/* -*-c-*- */ /* Copyright (C) 2001 Olivier Chapuis */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * An implementation of the Extended Window Manager Hints specification * http://freedesktop.org/Standards/wm-spec/ * * Not Implemented (draft 1.3 - 2004-05-01): * * _NET_DESKTOP_LAYOUT (not useful for us) * _NET_VIRTUAL_ROOTS (not useful at all for us) * _NET_SHOWING_DESKTOP should be implemented * _NET_REQUEST_FRAME_EXTENTS may be implemented * * Some _NET_WINDOW_TYPE are simply ignored: * _NET_WM_WINDOW_TYPE_SPLASH * _NET_WM_WINDOW_TYPE_UTILITIES * _KDE_NET_WM_WINDOW_TYPE_OVERRIDE (deprecated) * * _NET_WM_STATE_DEMANDS_ATTENTION state * * _NET_WM_STRUT_PARTIAL should be implemented * * _NET_WM_USER_TIME can be used to decide if we give the focus to a newly * mapped window * * The kill huge process protocol: _NET_WM_PID and _NET_WM_PING * * _NET_WM_HANDLED_ICONS (what to do? Nothing?) * * "Source indication in requests" is not used but can be useful with some * "ignore styles" * * Problems: * - _NET_WM_WINDOW_TYPE_TOOLBAR is interpreted in a different way * in GNOME (the spec) and in KDE 2/3.0 (~ simple dock?). * */ #include "config.h" #include #include #include "libs/fvwmlib.h" #include "libs/FScreen.h" #include "fvwm.h" #include "execcontext.h" #include "functions.h" #include "commands.h" #include "misc.h" #include "screen.h" #include "update.h" #include "stack.h" #include "style.h" #include "externs.h" #include "decorations.h" #include "ewmh.h" #include "ewmh_intern.h" #include "geometry.h" #include "window_flags.h" typedef struct kst_item { Window w; struct kst_item *next; } KstItem; Atom XA_UTF8_STRING = None; KstItem *ewmh_KstWinList = NULL; /* * Default */ ewmhInfo ewmhc = { 4, /* NumberOfDesktops */ 0, /* MaxDesktops limit the number of desktops*/ 0, /* CurrentNumberOfDesktops */ False, /* NeedsToCheckDesk */ {0, 0, 0, 0}, /* BaseStrut */ }; /* * The ewmh atoms lists */ #define ENTRY(name, type, func) {name, None, type, func} /* WARNING: lists must be in "name" alphabetic order! */ /* EWMH_ATOM_LIST_CLIENT_ROOT * net atoms that can be send (Client Message) by a client and do not * need a window to operate */ ewmh_atom ewmh_atom_client_root[] = { ENTRY("_NET_CURRENT_DESKTOP", XA_CARDINAL, ewmh_CurrentDesktop), ENTRY("_NET_DESKTOP_GEOMETRY", XA_CARDINAL, ewmh_DesktopGeometry), ENTRY("_NET_DESKTOP_NAMES", None, None), ENTRY("_NET_DESKTOP_VIEWPORT", XA_CARDINAL, ewmh_DesktopViewPort), ENTRY("_NET_NUMBER_OF_DESKTOPS", XA_CARDINAL, ewmh_NumberOfDesktops), {NULL,0,0,0} }; /* EWMH_ATOM_LIST_CLIENT_WIN * net atoms that can be send (Client Message) by a client and do need * window to operate */ ewmh_atom ewmh_atom_client_win[] = { ENTRY("_NET_ACTIVE_WINDOW", XA_WINDOW, ewmh_ActiveWindow), ENTRY("_NET_CLOSE_WINDOW", XA_WINDOW, ewmh_CloseWindow), ENTRY("_NET_MOVERESIZE_WINDOW", XA_WINDOW, ewmh_MoveResizeWindow), ENTRY("_NET_RESTACK_WINDOW", XA_WINDOW, ewmh_RestackWindow), ENTRY("_NET_WM_DESKTOP", XA_CARDINAL, ewmh_WMDesktop), ENTRY("_NET_WM_MOVERESIZE", XA_WINDOW, ewmh_MoveResize), ENTRY("_NET_WM_STATE", XA_ATOM, ewmh_WMState), {NULL,0,0,0} }; /* EWMH_ATOM_LIST_WM_STATE * the different wm state, a client can ask to add, remove or toggle * via a _NET_WM_STATE Client message. fvwm must maintain these states * too */ ewmh_atom ewmh_atom_wm_state[] = { ENTRY( "_NET_WM_STATE_ABOVE", XA_ATOM, ewmh_WMStateStaysOnTop), ENTRY( "_NET_WM_STATE_BELOW", XA_ATOM, ewmh_WMStateStaysOnBottom), ENTRY( "_NET_WM_STATE_FULLSCREEN", XA_ATOM, ewmh_WMStateFullScreen), ENTRY("_NET_WM_STATE_HIDDEN", XA_ATOM, ewmh_WMStateHidden), ENTRY( "_NET_WM_STATE_MAXIMIZED_HORIZ", XA_ATOM, ewmh_WMStateMaxHoriz), ENTRY( "_NET_WM_STATE_MAXIMIZED_HORZ", XA_ATOM, ewmh_WMStateMaxHoriz), ENTRY("_NET_WM_STATE_MAXIMIZED_VERT", XA_ATOM, ewmh_WMStateMaxVert), ENTRY("_NET_WM_STATE_MODAL", XA_ATOM, ewmh_WMStateModal), ENTRY("_NET_WM_STATE_SHADED", XA_ATOM, ewmh_WMStateShaded), ENTRY( "_NET_WM_STATE_SKIP_PAGER", XA_ATOM, ewmh_WMStateSkipPager), ENTRY( "_NET_WM_STATE_SKIP_TASKBAR", XA_ATOM, ewmh_WMStateSkipTaskBar), ENTRY( "_NET_WM_STATE_STAYS_ON_TOP", XA_ATOM, ewmh_WMStateStaysOnTop), ENTRY("_NET_WM_STATE_STICKY", XA_ATOM, ewmh_WMStateSticky), {NULL,0,0,0} }; #define EWMH_NUMBER_OF_STATE sizeof(ewmh_atom_wm_state)/sizeof(ewmh_atom) - 1 /* EWMH ATOM_LIST_ALLOWED_ACTIONS: atom for _NET_WM_ALLOWED_ACTIONS */ ewmh_atom ewmh_atom_allowed_actions[] = { ENTRY("_NET_WM_ACTION_CHANGE_DESKTOP", XA_ATOM, ewmh_AllowsYes), ENTRY("_NET_WM_ACTION_CLOSE", XA_ATOM, ewmh_AllowsClose), ENTRY("_NET_WM_ACTION_FULLSCREEN", XA_ATOM, ewmh_AllowsFullScreen), ENTRY("_NET_WM_ACTION_MAXIMIZE_HORZ", XA_ATOM, ewmh_AllowsMaximize), ENTRY("_NET_WM_ACTION_MAXIMIZE_VERT", XA_ATOM, ewmh_AllowsMaximize), ENTRY("_NET_WM_ACTION_MINIMIZE", XA_ATOM, ewmh_AllowsMinimize), ENTRY("_NET_WM_ACTION_MOVE", XA_ATOM, ewmh_AllowsMove), ENTRY("_NET_WM_ACTION_RESIZE", XA_ATOM, ewmh_AllowsResize), ENTRY("_NET_WM_ACTION_SHADE", XA_ATOM, ewmh_AllowsYes), ENTRY("_NET_WM_ACTION_STICK", XA_ATOM, ewmh_AllowsYes), {NULL,0,0,0} }; #define EWMH_NUMBER_OF_ALLOWED_ACTIONS \ sizeof(ewmh_atom_allowed_actions)/sizeof(ewmh_atom) - 1 /* EWMH ATOM_LIST_WINDOW_TYPE: the various window type */ ewmh_atom ewmh_atom_window_type[] = { ENTRY("_NET_WM_WINDOW_TYPE_DESKTOP", XA_ATOM, ewmh_HandleDesktop), ENTRY("_NET_WM_WINDOW_TYPE_DIALOG", XA_ATOM, ewmh_HandleDialog), ENTRY("_NET_WM_WINDOW_TYPE_DOCK", XA_ATOM, ewmh_HandleDock), ENTRY("_NET_WM_WINDOW_TYPE_MENU", XA_ATOM, ewmh_HandleMenu), ENTRY("_NET_WM_WINDOW_TYPE_NORMAL", XA_ATOM, ewmh_HandleNormal), ENTRY("_NET_WM_WINDOW_TYPE_TOOLBAR", XA_ATOM, ewmh_HandleToolBar), {NULL,0,0,0} }; /* EWMH ATOM_LIST_FIXED_PROPERTY * property that have a window at startup and which should not change */ ewmh_atom ewmh_atom_fixed_property[] = { ENTRY("_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", XA_WINDOW, None), ENTRY("_NET_WM_HANDLED_ICON", XA_ATOM, None), ENTRY("_NET_WM_PID", XA_ATOM, None), ENTRY("_NET_WM_WINDOW_TYPE", XA_ATOM, None), {NULL,0,0,0} }; /* EWMH ATOM_LIST_PROPERTY_NOTIFY * properties of a window which is updated by the window via a PropertyNotify * event */ ewmh_atom ewmh_atom_property_notify[] = { ENTRY("_NET_WM_ICON", XA_CARDINAL, ewmh_WMIcon), ENTRY("_NET_WM_ICON_GEOMETRY", XA_CARDINAL, ewmh_WMIconGeometry), ENTRY("_NET_WM_ICON_NAME", None, EWMH_WMIconName), ENTRY("_NET_WM_NAME", None, EWMH_WMName), ENTRY("_NET_WM_STRUT", XA_CARDINAL, ewmh_WMStrut), {NULL,0,0,0} }; /* EWMH_ATOM_LIST_FVWM_ROOT: root atom that should be maintained by fvwm only */ ewmh_atom ewmh_atom_fvwm_root[] = { ENTRY("_KDE_NET_SYSTEM_TRAY_WINDOWS", XA_WINDOW, None), ENTRY("_NET_CLIENT_LIST", XA_WINDOW, None), ENTRY("_NET_CLIENT_LIST_STACKING", XA_WINDOW, None), ENTRY("_NET_SUPPORTED", XA_ATOM, None), ENTRY("_NET_SUPPORTING_WM_CHECK", XA_WINDOW, None), ENTRY("_NET_VIRTUAL_ROOTS", XA_WINDOW, None), ENTRY("_NET_WORKAREA", XA_CARDINAL, None), {NULL,0,0,0} }; /* EWMH_ATOM_LIST_FVWM_WIN: window atom that should be maintained by fvwm * only */ ewmh_atom ewmh_atom_fvwm_win[] = { ENTRY("_KDE_NET_WM_FRAME_STRUT", XA_CARDINAL, None), ENTRY("_NET_FRAME_EXTENTS", XA_CARDINAL, None), ENTRY("_NET_WM_ALLOWED_ACTIONS", XA_ATOM, None), ENTRY("_NET_WM_ICON_VISIBLE_NAME", None, None), ENTRY("_NET_WM_VISIBLE_NAME", None, None), {NULL,0,0,0} }; #define NUMBER_OF_ATOM_LISTS 9 #define L_ENTRY(x,y) {x,y,sizeof(y)/sizeof(ewmh_atom)} ewmh_atom_list atom_list[] = { L_ENTRY(EWMH_ATOM_LIST_CLIENT_ROOT, ewmh_atom_client_root), L_ENTRY(EWMH_ATOM_LIST_CLIENT_WIN, ewmh_atom_client_win), L_ENTRY(EWMH_ATOM_LIST_WM_STATE, ewmh_atom_wm_state), L_ENTRY(EWMH_ATOM_LIST_ALLOWED_ACTIONS, ewmh_atom_allowed_actions), L_ENTRY(EWMH_ATOM_LIST_WINDOW_TYPE, ewmh_atom_window_type), L_ENTRY(EWMH_ATOM_LIST_FIXED_PROPERTY, ewmh_atom_fixed_property), L_ENTRY(EWMH_ATOM_LIST_PROPERTY_NOTIFY, ewmh_atom_property_notify), L_ENTRY(EWMH_ATOM_LIST_FVWM_ROOT, ewmh_atom_fvwm_root), L_ENTRY(EWMH_ATOM_LIST_FVWM_WIN, ewmh_atom_fvwm_win), L_ENTRY(EWMH_ATOM_LIST_END, NULL) }; /* * Atoms utilities */ static int compare(const void *a, const void *b) { return (strcmp((char *)a, ((ewmh_atom *)b)->name)); } static ewmh_atom *get_ewmh_atom_by_name( const char *atom_name, ewmh_atom_list_name list_name) { ewmh_atom *a = NULL; Bool done = 0; int i = 0; while(!done && atom_list[i].name != EWMH_ATOM_LIST_END) { if (atom_list[i].name == list_name || list_name == EWMH_ATOM_LIST_ALL) { a = (ewmh_atom *)bsearch( atom_name, atom_list[i].list, atom_list[i].size - 1, sizeof(ewmh_atom), compare); if (a != NULL || list_name != EWMH_ATOM_LIST_ALL) { done = 1; } } i++; } return a; } ewmh_atom *ewmh_GetEwmhAtomByAtom(Atom atom, ewmh_atom_list_name list_name) { Bool done = 0; int i = 0; while(!done && atom_list[i].name != EWMH_ATOM_LIST_END) { if (atom_list[i].name == list_name || list_name == EWMH_ATOM_LIST_ALL) { ewmh_atom *list = atom_list[i].list; while(list->name != NULL) { if (atom == list->atom) { return list; } list++; } if (atom_list[i].name == list_name) { return NULL; } } i++; } return NULL; } void ewmh_ChangeProperty( Window w, const char *atom_name, ewmh_atom_list_name list, unsigned char *data, int length) { ewmh_atom *a; int format = 32; if ((a = get_ewmh_atom_by_name(atom_name, list)) != NULL) { if (a->atom_type == XA_UTF8_STRING) { format = 8; } XChangeProperty( dpy, w, a->atom, a->atom_type , format, PropModeReplace, data, length); } return; } void ewmh_DeleteProperty( Window w, const char *atom_name, ewmh_atom_list_name list) { ewmh_atom *a; if ((a = get_ewmh_atom_by_name(atom_name, list)) != NULL) { XDeleteProperty(dpy, w, a->atom); } return; } static int atom_size(int format) { if (format == 32) { return sizeof(long); } else { return (format >> 3); } } static void *atom_get(Window win, Atom to_get, Atom type, int *size) { unsigned char *retval; Atom type_ret; unsigned long bytes_after, num_ret; long length; int format_ret; void *data; int ok; retval = NULL; length = 0x7fffffff; ok = XGetWindowProperty( dpy, win, to_get, 0L, length, False, type, &type_ret, &format_ret, &num_ret, &bytes_after, &retval); if ((ok == Success) && (retval) && (num_ret > 0) && (format_ret > 0)) { int asize; asize = atom_size(format_ret); data = safemalloc(num_ret * asize); if (format_ret == 32 && asize * 8 != format_ret) { int i; for (i = 0; i < num_ret; i++) { ((CARD32 *)data)[i] = ((long *)retval)[i]; } } else { if (data) { memcpy(data, retval, num_ret * asize); } } XFree(retval); *size = num_ret * (format_ret >> 3); return data; } if (retval) { XFree(retval); } return NULL; } void *ewmh_AtomGetByName( Window win, const char *atom_name, ewmh_atom_list_name list, int *size) { ewmh_atom *a; void *data = NULL; if ((a = get_ewmh_atom_by_name(atom_name, list)) != NULL) { data = atom_get(win, a->atom, a->atom_type, size); } return data; } /* * client_root: here the client is fvwm */ static int check_desk(void) { int d = -1; FvwmWindow *fw; for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { if (!IS_STICKY_ACROSS_DESKS(fw)) { d = max(d, fw->Desk); } } return d; } void EWMH_SetCurrentDesktop(void) { long val; val = Scr.CurrentDesk; if (val < 0 || (val >= ewmhc.MaxDesktops && ewmhc.MaxDesktops != 0)) { return; } if (val >= ewmhc.CurrentNumberOfDesktops || (ewmhc.NumberOfDesktops != ewmhc.CurrentNumberOfDesktops && val < ewmhc.CurrentNumberOfDesktops)) { EWMH_SetNumberOfDesktops(); } ewmh_ChangeProperty(Scr.Root,"_NET_CURRENT_DESKTOP", EWMH_ATOM_LIST_CLIENT_ROOT, (unsigned char *)&val, 1); return; } void EWMH_SetNumberOfDesktops(void) { long val; if (ewmhc.CurrentNumberOfDesktops < ewmhc.NumberOfDesktops) { ewmhc.CurrentNumberOfDesktops = ewmhc.NumberOfDesktops; } if (ewmhc.CurrentNumberOfDesktops > ewmhc.NumberOfDesktops || ewmhc.NeedsToCheckDesk) { int d = check_desk(); ewmhc.NeedsToCheckDesk = False; if (d >= ewmhc.MaxDesktops && ewmhc.MaxDesktops != 0) d = 0; ewmhc.CurrentNumberOfDesktops = max(ewmhc.NumberOfDesktops, d+1); } if (Scr.CurrentDesk >= ewmhc.CurrentNumberOfDesktops && (Scr.CurrentDesk < ewmhc.MaxDesktops || ewmhc.MaxDesktops == 0)) { ewmhc.CurrentNumberOfDesktops = Scr.CurrentDesk + 1; } val = (long)ewmhc.CurrentNumberOfDesktops; ewmh_ChangeProperty(Scr.Root, "_NET_NUMBER_OF_DESKTOPS", EWMH_ATOM_LIST_CLIENT_ROOT, (unsigned char *)&val, 1); ewmh_SetWorkArea(); return; } void EWMH_SetDesktopViewPort(void) { long val[256][2]; /* no more than 256 desktops */ int i = 0; while(i < ewmhc.NumberOfDesktops && i < 256) { val[i][0] = Scr.Vx; val[i][1] = Scr.Vy; i++; } ewmh_ChangeProperty( Scr.Root, "_NET_DESKTOP_VIEWPORT", EWMH_ATOM_LIST_CLIENT_ROOT, (unsigned char *)&val, i*2); return; } void EWMH_SetDesktopGeometry(void) { long val[2]; val[0] = Scr.VxMax + Scr.MyDisplayWidth; val[1] = Scr.VyMax + Scr.MyDisplayHeight; ewmh_ChangeProperty( Scr.Root,"_NET_DESKTOP_GEOMETRY", EWMH_ATOM_LIST_CLIENT_ROOT, (unsigned char *)&val, 2); return; } /* * client_win: here the client is fvwm */ void EWMH_SetActiveWindow(Window w) { ewmh_ChangeProperty( Scr.Root, "_NET_ACTIVE_WINDOW", EWMH_ATOM_LIST_CLIENT_WIN, (unsigned char *)&w, 1); return; } void EWMH_SetWMDesktop(FvwmWindow *fw) { long desk = fw->Desk; if (IS_STICKY_ACROSS_DESKS(fw)) { desk = (unsigned long)-1; } else if (desk >= ewmhc.CurrentNumberOfDesktops) { ewmhc.NeedsToCheckDesk = True; EWMH_SetNumberOfDesktops(); } ewmh_ChangeProperty( FW_W(fw), "_NET_WM_DESKTOP", EWMH_ATOM_LIST_CLIENT_WIN, (unsigned char *)&desk, 1); return; } /* * fvwm must maintain the _NET_WM_STATE */ void EWMH_SetWMState(FvwmWindow *fw, Bool do_restore) { Atom wm_state[EWMH_NUMBER_OF_STATE]; int i = 0; ewmh_atom *list = ewmh_atom_wm_state; while(list->name != NULL) { if (list->action(fw, NULL, NULL, do_restore)) { wm_state[i++] = list->atom; } list++; } if (i > 0) { ewmh_ChangeProperty( FW_W(fw), "_NET_WM_STATE", EWMH_ATOM_LIST_CLIENT_WIN, (unsigned char *)wm_state, i); } else { ewmh_DeleteProperty( FW_W(fw), "_NET_WM_STATE", EWMH_ATOM_LIST_CLIENT_WIN); } return; } /* * fvwm_root */ /*** kde system tray ***/ /* #define DEBUG_KST */ static void add_kst_item(Window w) { KstItem *t,**prev; t = ewmh_KstWinList; prev = &ewmh_KstWinList; while(t != NULL) { prev = &(t->next); t = t->next; } *prev = (KstItem *)safemalloc(sizeof(KstItem)); (*prev)->w = w; (*prev)->next = NULL; return; } static void delete_kst_item(Window w) { KstItem *t,**prev; t = ewmh_KstWinList; prev = &ewmh_KstWinList; while((t!= NULL)&&(t->w != w)) { prev = &(t->next); t = t->next; } if (t == NULL) { return; } if(prev != NULL) { *prev = t->next; } free(t); return; } static void set_kde_sys_tray(void) { Window *wins = NULL; KstItem *t; int i = 0, nbr = 0; t = ewmh_KstWinList; while(t != NULL) { nbr++; t = t->next; } if (nbr > 0) { wins = (Window *)safemalloc(sizeof(Window) * nbr); } t = ewmh_KstWinList; #ifdef DEBUG_KST fprintf(stderr,"ADD_TO_KST: "); #endif while (t != NULL) { #ifdef DEBUG_KST fprintf(stderr,"0x%lx ",t->w); #endif wins[i++] = t->w; t = t->next; } #ifdef DEBUG_KST fprintf(stderr,"\n"); #endif ewmh_ChangeProperty(Scr.Root,"_KDE_NET_SYSTEM_TRAY_WINDOWS", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)wins,i); if (wins != NULL) { free(wins); } return; } void ewmh_AddToKdeSysTray(FvwmWindow *fw) { int size = 0; Atom *val; KstItem *t; val = ewmh_AtomGetByName( FW_W(fw), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", EWMH_ATOM_LIST_FIXED_PROPERTY, &size); if (val == NULL) { return; } free(val); t = ewmh_KstWinList; while(t != NULL && t->w != FW_W(fw)) t = t->next; if (t != NULL) { return; /* already in the list */ } add_kst_item(FW_W(fw)); set_kde_sys_tray(); return; } #if 0 /* not used at present time */ void ewmh_FreeKdeSysTray(void) { KstItem *t; t = ewmh_KstWinList; while(t != NULL) { XSelectInput(dpy, t->w, NoEventMask); delete_kst_item(t->w); t = ewmh_KstWinList; } set_kde_sys_tray(); return; } #endif int EWMH_IsKdeSysTrayWindow(Window w) { KstItem *t; t = ewmh_KstWinList; while(t != NULL && t->w != w) { t = t->next; } if (t == NULL) { return 0; } #ifdef DEBUG_KST fprintf(stderr,"IsKdeSysTrayWindow: 0x%lx\n", w); #endif return 1; } void EWMH_ManageKdeSysTray(Window w, int type) { KstItem *t; t = ewmh_KstWinList; while(t != NULL && t->w != w) { t = t->next; } if (t == NULL) { return; } switch(type) { case UnmapNotify: #ifdef DEBUG_KST fprintf(stderr,"KST_UNMAP: 0x%lx\n", w); #endif XSelectInput(dpy, w, StructureNotifyMask); XFlush(dpy); break; case DestroyNotify: #ifdef DEBUG_KST fprintf(stderr,"KST_DESTROY: 0x%lx\n", w); #endif XSelectInput(dpy, t->w, NoEventMask); XFlush(dpy); delete_kst_item(w); set_kde_sys_tray(); break; case ReparentNotify: #ifdef DEBUG_KST fprintf(stderr,"KST_Reparent: 0x%lx\n", w); #endif XSelectInput(dpy, w, StructureNotifyMask); XFlush(dpy); break; default: #ifdef DEBUG_KST fprintf(stderr,"KST_NO: 0x%lx\n", w); #endif break; } return; } /**** Client lists ****/ void EWMH_SetClientList(void) { Window *wl = NULL; FvwmWindow *fw; int nbr = 0; int i = 0; for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { nbr++; } if (nbr != 0) { wl = (Window *)safemalloc(sizeof(Window) * nbr); for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { wl[i++] = FW_W(fw); } } ewmh_ChangeProperty( Scr.Root,"_NET_CLIENT_LIST", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)wl, nbr); if (wl != NULL) { free (wl); } return; } void EWMH_SetClientListStacking(void) { Window *wl = NULL; FvwmWindow *fw; int nbr = 0; int i = 0; for ( fw = Scr.FvwmRoot.stack_next; fw != &Scr.FvwmRoot; fw = fw->stack_next) { nbr++; } i = nbr-1; if (nbr != 0) { wl = (Window *)safemalloc(sizeof(Window) * nbr); for ( fw = Scr.FvwmRoot.stack_next; fw != &Scr.FvwmRoot; fw = fw->stack_next) { wl[i--] = FW_W(fw); } } ewmh_ChangeProperty( Scr.Root,"_NET_CLIENT_LIST_STACKING", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)wl, nbr); if (wl != NULL) { free (wl); } return; } /**** Working Area stuff ****/ /**** At present time we support only sticky windows with strut ****/ void ewmh_SetWorkArea(void) { long val[256][4]; /* no more than 256 desktops */ int i = 0; while(i < ewmhc.NumberOfDesktops && i < 256) { val[i][0] = Scr.Desktops->ewmh_working_area.x; val[i][1] = Scr.Desktops->ewmh_working_area.y; val[i][2] = Scr.Desktops->ewmh_working_area.width; val[i][3] = Scr.Desktops->ewmh_working_area.height; i++; } ewmh_ChangeProperty( Scr.Root, "_NET_WORKAREA", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)&val, i*4); return; } void ewmh_ComputeAndSetWorkArea(void) { int left = ewmhc.BaseStrut.left; int right = ewmhc.BaseStrut.right; int top = ewmhc.BaseStrut.top; int bottom = ewmhc.BaseStrut.bottom; int x,y,width,height; FvwmWindow *fw; for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { if ( DO_EWMH_IGNORE_STRUT_HINTS(fw) || !IS_STICKY_ACROSS_PAGES(fw)) { continue; } left = max(left, fw->strut.left); right = max(right, fw->strut.right); top = max(top, fw->strut.top); bottom = max(bottom, fw->strut.bottom); } x = left; y = top; width = Scr.MyDisplayWidth - (left + right); height = Scr.MyDisplayHeight - (top + bottom); if ( Scr.Desktops->ewmh_working_area.x != x || Scr.Desktops->ewmh_working_area.y != y || Scr.Desktops->ewmh_working_area.width != width || Scr.Desktops->ewmh_working_area.height != height) { Scr.Desktops->ewmh_working_area.x = x; Scr.Desktops->ewmh_working_area.y = y; Scr.Desktops->ewmh_working_area.width = width; Scr.Desktops->ewmh_working_area.height = height; ewmh_SetWorkArea(); } return; } void ewmh_HandleDynamicWorkArea(void) { int dyn_left = ewmhc.BaseStrut.left; int dyn_right = ewmhc.BaseStrut.right; int dyn_top = ewmhc.BaseStrut.top; int dyn_bottom = ewmhc.BaseStrut.bottom; int x,y,width,height; FvwmWindow *fw; for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { if ( DO_EWMH_IGNORE_STRUT_HINTS(fw) || !IS_STICKY_ACROSS_PAGES(fw)) { continue; } dyn_left = max(dyn_left, fw->dyn_strut.left); dyn_right = max(dyn_right, fw->dyn_strut.right); dyn_top = max(dyn_top, fw->dyn_strut.top); dyn_bottom = max(dyn_bottom, fw->dyn_strut.bottom); } x = dyn_left; y = dyn_top; width = Scr.MyDisplayWidth - (dyn_left + dyn_right); height = Scr.MyDisplayHeight - (dyn_top + dyn_bottom); if ( Scr.Desktops->ewmh_dyn_working_area.x != x || Scr.Desktops->ewmh_dyn_working_area.y != y || Scr.Desktops->ewmh_dyn_working_area.width != width || Scr.Desktops->ewmh_dyn_working_area.height != height) { Scr.Desktops->ewmh_dyn_working_area.x = x; Scr.Desktops->ewmh_dyn_working_area.y = y; Scr.Desktops->ewmh_dyn_working_area.width = width; Scr.Desktops->ewmh_dyn_working_area.height = height; /* here we may update the maximized window ...etc */ } return; } void EWMH_UpdateWorkArea(void) { ewmh_ComputeAndSetWorkArea(); ewmh_HandleDynamicWorkArea(); return; } void EWMH_GetWorkAreaIntersection( FvwmWindow *fw, int *x, int *y, int *w, int *h, int type) { int nx,ny,nw,nh; int area_x = Scr.Desktops->ewmh_working_area.x; int area_y = Scr.Desktops->ewmh_working_area.y; int area_w = Scr.Desktops->ewmh_working_area.width; int area_h = Scr.Desktops->ewmh_working_area.height; Bool is_dynamic = False; switch(type) { case EWMH_IGNORE_WORKING_AREA: return; case EWMH_USE_WORKING_AREA: break; case EWMH_USE_DYNAMIC_WORKING_AREA: is_dynamic = True; break; default: break; } if (is_dynamic) { area_x = Scr.Desktops->ewmh_dyn_working_area.x; area_y = Scr.Desktops->ewmh_dyn_working_area.y; area_w = Scr.Desktops->ewmh_dyn_working_area.width; area_h = Scr.Desktops->ewmh_dyn_working_area.height; } nx = max(*x, area_x); ny = max(*y, area_y); nw = min(*x + *w, area_x + area_w) - nx; nh = min(*y + *h, area_y + area_h) - ny; *x = nx; *y = ny; *w = nw; *h = nh; return; } static float get_intersection( int x11, int y11, int x12, int y12, int x21, int y21, int x22, int y22, Bool use_percent) { float ret = 0; int xl, xr, yt, yb; if (x11 < x22 && x12 > x21 && y11 < y22 && y12 > y21) { xl = max(x11, x21); xr = min(x12, x22); yt = max(y11, y21); yb = min(y12, y22); ret = (xr - xl) * (yb - yt); } if (use_percent && (x22 - x21) * (y22 - y21) != 0 && (x12 - x11) * (y12 - y11) != 0) { ret = 100 * max(ret / ((x22 - x21) * (y22 - y21)), ret / ((x12 - x11) * (y12 - y11))); } return ret; } static float ewmh_GetStrutIntersection( int x11, int y11, int x12, int y12, int left, int right, int top, int bottom, Bool use_percent) { float ret = 0; int x21, y21, x22, y22; /* left */ x21 = 0; y21 = 0; x22 = left; y22 = Scr.MyDisplayHeight; ret += get_intersection( x11, y11, x12, y12, x21, y21, x22, y22, use_percent); /* right */ x21 = Scr.MyDisplayWidth - right; y21 = 0; x22 = Scr.MyDisplayWidth; y22 = Scr.MyDisplayHeight; ret += get_intersection( x11, y11, x12, y12, x21, y21, x22, y22, use_percent); /* top */ x21 = 0; y21 = 0; x22 = Scr.MyDisplayWidth; y22 = top; ret += get_intersection( x11, y11, x12, y12, x21, y21, x22, y22, use_percent); /* bottom */ x21 = 0; y21 = Scr.MyDisplayHeight - bottom; x22 = Scr.MyDisplayWidth; y22 = Scr.MyDisplayHeight; ret += get_intersection( x11, y11, x12, y12, x21, y21, x22, y22, use_percent); return ret; } float EWMH_GetBaseStrutIntersection( int x11, int y11, int x12, int y12, Bool use_percent) { return ewmh_GetStrutIntersection( x11, y11, x12, y12, ewmhc.BaseStrut.left, ewmhc.BaseStrut.right, ewmhc.BaseStrut.top, ewmhc.BaseStrut.bottom, use_percent); } float EWMH_GetStrutIntersection( int x11, int y11, int x12, int y12, Bool use_percent) { int left, right, top, bottom; left = Scr.Desktops->ewmh_working_area.x; right = Scr.MyDisplayWidth - (Scr.Desktops->ewmh_working_area.x + Scr.Desktops->ewmh_working_area.width); top = Scr.Desktops->ewmh_working_area.y; bottom = Scr.MyDisplayHeight - (Scr.Desktops->ewmh_working_area.y + Scr.Desktops->ewmh_working_area.height); return ewmh_GetStrutIntersection( x11, y11, x12, y12, left, right, top, bottom, use_percent); } /* * fvwm_win */ void EWMH_SetFrameStrut(FvwmWindow *fw) { long val[4]; size_borders b; if (EWMH_IsKdeSysTrayWindow(FW_W(fw))) { /* Fixed position of tray window in kicker */ return; } get_window_borders(fw, &b); /* left */ val[0] = b.top_left.width; /* right */ val[1] = b.bottom_right.width; /* top */ val[2] = b.top_left.height; /* bottom */ val[3] = b.bottom_right.height; ewmh_ChangeProperty( FW_W(fw), "_KDE_NET_WM_FRAME_STRUT", EWMH_ATOM_LIST_FVWM_WIN, (unsigned char *)&val, 4); ewmh_ChangeProperty( FW_W(fw), "_NET_FRAME_EXTENTS", EWMH_ATOM_LIST_FVWM_WIN, (unsigned char *)&val, 4); return; } /* * allowed actions */ Bool ewmh_AllowsYes(EWMH_CMD_ARGS) { return True; } Bool ewmh_AllowsClose(EWMH_CMD_ARGS) { return is_function_allowed( F_CLOSE, NULL, fw, RQORIG_PROGRAM_US, False); } Bool ewmh_AllowsFullScreen(EWMH_CMD_ARGS) { if ( !is_function_allowed( F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False) || !is_function_allowed( F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False) || !is_function_allowed( F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True)) { return False; } return True; } Bool ewmh_AllowsMinimize(EWMH_CMD_ARGS) { return is_function_allowed(F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False); } Bool ewmh_AllowsMaximize(EWMH_CMD_ARGS) { return is_function_allowed(F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False); } Bool ewmh_AllowsMove(EWMH_CMD_ARGS) { return is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False); } Bool ewmh_AllowsResize(EWMH_CMD_ARGS) { return is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False); } void EWMH_SetAllowedActions(FvwmWindow *fw) { Atom wm_actions[EWMH_NUMBER_OF_ALLOWED_ACTIONS]; int i = 0; ewmh_atom *list = ewmh_atom_allowed_actions; while(list->name != NULL) { if (list->action(fw, NULL, NULL, 0)) wm_actions[i++] = list->atom; list++; } if (i > 0) { ewmh_ChangeProperty( FW_W(fw), "_NET_WM_ALLOWED_ACTIONS", EWMH_ATOM_LIST_FVWM_WIN, (unsigned char *)wm_actions, i); } else { ewmh_DeleteProperty( FW_W(fw), "_NET_WM_ALLOWED_ACTIONS", EWMH_ATOM_LIST_FVWM_WIN); } return; } /* * Window types */ int ewmh_HandleDesktop(EWMH_CMD_ARGS) { if (Scr.EwmhDesktop != NULL && FW_W(Scr.EwmhDesktop) != FW_W(fw)) { fvwm_msg( WARN,"ewmh_HandleDesktop", "A Desktop application (0x%lx) already runs! This" " can cause problems\n", FW_W(Scr.EwmhDesktop)); /* what to do ? */ } fw->ewmh_window_type = EWMH_WINDOW_TYPE_DESKTOP_ID; Scr.EwmhDesktop = fw; SSET_LAYER(*style, 0); style->flags.use_layer = 1; style->flag_mask.use_layer = 1; style->change_mask.use_layer = 1; S_SET_IS_STICKY_ACROSS_PAGES(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCC(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCC(*style), 1); S_SET_IS_FIXED(SCF(*style), 1); S_SET_IS_FIXED(SCM(*style), 1); S_SET_IS_FIXED(SCC(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCF(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCM(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCC(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCF(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCM(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCC(*style), 1); S_SET_IS_UNICONIFIABLE(SCF(*style), 1); S_SET_IS_UNICONIFIABLE(SCM(*style), 1); S_SET_IS_UNICONIFIABLE(SCC(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCF(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCM(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCC(*style), 1); /* No border */ SSET_BORDER_WIDTH(*style, 0); style->flags.has_border_width = 1; style->flag_mask.has_border_width = 1; style->change_mask.has_border_width = 1; SSET_HANDLE_WIDTH(*style, 0); style->flags.has_handle_width = 1; style->flag_mask.has_handle_width = 1; style->change_mask.has_handle_width = 1; /* no title */ style->flags.has_no_title = 1; style->flag_mask.has_no_title = 1; style->change_mask.has_no_title = 1; /* ClickToFocus, I do not think we should use NeverFocus */ FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCF(*style)), 1); FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCC(*style)), 1); FPS_GRAB_FOCUS(S_FOCUS_POLICY(SCF(*style)), 1); FPS_GRAB_FOCUS(S_FOCUS_POLICY(SCM(*style)), 1); FPS_GRAB_FOCUS(S_FOCUS_POLICY(SCC(*style)), 1); /* ClickToFocusPassesClick */ FPS_PASS_FOCUS_CLICK(S_FOCUS_POLICY(SCF(*style)), 1); FPS_PASS_FOCUS_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_PASS_FOCUS_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); FPS_PASS_RAISE_CLICK(S_FOCUS_POLICY(SCF(*style)), 1); FPS_PASS_RAISE_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_PASS_RAISE_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); /* not useful */ FPS_RAISE_FOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCF(*style)), 0); FPS_RAISE_FOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_RAISE_FOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); FPS_RAISE_UNFOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCF(*style)), 0); FPS_RAISE_UNFOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_RAISE_UNFOCUSED_CLIENT_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); FPS_RAISE_FOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCF(*style)), 0); FPS_RAISE_FOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_RAISE_FOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); FPS_RAISE_UNFOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCF(*style)), 0); FPS_RAISE_UNFOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCM(*style)), 1); FPS_RAISE_UNFOCUSED_DECOR_CLICK(S_FOCUS_POLICY(SCC(*style)), 1); return 1; } int ewmh_HandleDialog(EWMH_CMD_ARGS) { fw->ewmh_window_type = EWMH_WINDOW_TYPE_DIALOG_ID; return 0; } int ewmh_HandleDock(EWMH_CMD_ARGS) { fw->ewmh_window_type = EWMH_WINDOW_TYPE_DOCK_ID; S_SET_IS_STICKY_ACROSS_PAGES(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCC(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCC(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCF(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCM(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCC(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCF(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCM(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCC(*style), 1); S_SET_IS_UNICONIFIABLE(SCF(*style), 1); S_SET_IS_UNICONIFIABLE(SCM(*style), 1); S_SET_IS_UNICONIFIABLE(SCC(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCF(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCM(*style), 1); S_SET_IS_UNMAXIMIZABLE(SCC(*style), 1); if (fw->ewmh_hint_layer == -1) { fw->ewmh_hint_layer = Scr.TopLayer; if (DO_EWMH_USE_STACKING_HINTS(style)) { SSET_LAYER(*style, Scr.TopLayer); style->flags.use_layer = 1; style->flag_mask.use_layer = 1; style->change_mask.use_layer = 1; } else if (!style->change_mask.use_layer) { SSET_LAYER(*style, Scr.DefaultLayer); style->flags.use_layer = 1; style->flag_mask.use_layer = 1; style->change_mask.use_layer = 1; } } /* no title ? MWM hints should be used by the app but ... */ return 1; } int ewmh_HandleMenu(EWMH_CMD_ARGS) { fw->ewmh_window_type = EWMH_WINDOW_TYPE_MENU_ID; /* tear off menu */ S_SET_DO_WINDOW_LIST_SKIP(SCF(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCM(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCC(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCF(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCM(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCC(*style), 1); /* NeverFocus */ FPS_LENIENT(S_FOCUS_POLICY(SCF(*style)), 0); FPS_LENIENT(S_FOCUS_POLICY(SCM(*style)), 1); FPS_LENIENT(S_FOCUS_POLICY(SCC(*style)), 1); FPS_FOCUS_ENTER(S_FOCUS_POLICY(SCF(*style)), 0); FPS_UNFOCUS_LEAVE(S_FOCUS_POLICY(SCF(*style)), 0); FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCF(*style)), 0); FPS_FOCUS_CLICK_DECOR(S_FOCUS_POLICY(SCF(*style)), 0); FPS_FOCUS_CLICK_ICON(S_FOCUS_POLICY(SCF(*style)), 0); FPS_FOCUS_BY_FUNCTION(S_FOCUS_POLICY(SCF(*style)), 0); FPS_FOCUS_ENTER(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_ENTER(S_FOCUS_POLICY(SCC(*style)), 1); FPS_UNFOCUS_LEAVE(S_FOCUS_POLICY(SCM(*style)), 1); FPS_UNFOCUS_LEAVE(S_FOCUS_POLICY(SCC(*style)), 1); FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_CLICK_CLIENT(S_FOCUS_POLICY(SCC(*style)), 1); FPS_FOCUS_CLICK_DECOR(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_CLICK_DECOR(S_FOCUS_POLICY(SCC(*style)), 1); FPS_FOCUS_CLICK_ICON(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_CLICK_ICON(S_FOCUS_POLICY(SCC(*style)), 1); FPS_FOCUS_BY_FUNCTION(S_FOCUS_POLICY(SCM(*style)), 1); FPS_FOCUS_BY_FUNCTION(S_FOCUS_POLICY(SCC(*style)), 1); return 1; } int ewmh_HandleNormal(EWMH_CMD_ARGS) { fw->ewmh_window_type = EWMH_WINDOW_TYPE_NORMAL_ID; return 0; } int ewmh_HandleToolBar(EWMH_CMD_ARGS) { fw->ewmh_window_type = EWMH_WINDOW_TYPE_TOOLBAR_ID; /* this ok for KDE 2 (and 3??) but I do not think that a toolbar should be sticky */ S_SET_IS_STICKY_ACROSS_PAGES(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_PAGES(SCC(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCF(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCM(*style), 1); S_SET_IS_STICKY_ACROSS_DESKS(SCC(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCF(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCM(*style), 1); S_SET_DO_WINDOW_LIST_SKIP(SCC(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCF(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCM(*style), 1); S_SET_DO_CIRCULATE_SKIP(SCC(*style), 1); /* no title ? MWM hints should be used by the app but ... */ return 1; } void ewmh_HandleWindowType(FvwmWindow *fw, window_style *style) { Atom *val; ewmh_atom *list = ewmh_atom_window_type; int size = 0; int i = 0; Bool found = False; fw->ewmh_window_type = 0; if (DO_EWMH_IGNORE_WINDOW_TYPE(style)) { return; } val = ewmh_AtomGetByName( FW_W(fw), "_NET_WM_WINDOW_TYPE", EWMH_ATOM_LIST_FIXED_PROPERTY, &size); if (val == NULL) { return; } /* we support only one window type: the first that we support */ while(i < size && !found) { list = ewmh_atom_window_type; while(list->name != NULL && !found) { if (list->atom == val[i]) { list->action(fw, NULL, style, 0); found = True; } list++; } i++; } free(val); return; } /* * a workaround for ksmserver exit windows */ static int ksmserver_workarround(FvwmWindow *fw) { if (fw->name.name != NULL && fw->class.res_name != NULL && fw->icon_name.name != NULL && fw->class.res_class != NULL && strcmp(fw->name.name, "ksmserver") == 0 && strcmp(fw->class.res_class, "ksmserver") == 0 && strcmp(fw->icon_name.name, "ksmserver") == 0 && strcmp(fw->class.res_name, "unnamed") == 0) { int layer = 0; if (IS_TRANSIENT(fw)) { layer = Scr.TopLayer + 2; } else { layer = Scr.TopLayer + 1; } new_layer(fw, layer); return 1; } return 0; } /* * Window Initialisation / Destroy */ void EWMH_GetStyle(FvwmWindow *fw, window_style *style) { if (style->change_mask.use_layer) { fw->ewmh_normal_layer = SGET_LAYER(*style); } else if (fw->ewmh_normal_layer == 0) { fw->ewmh_normal_layer = Scr.DefaultLayer; } ewmh_WMState(fw, NULL, style, 0); ewmh_WMDesktop(fw, NULL, style, 0); /* the window type override the state hint */ ewmh_HandleWindowType(fw, style); return; } static void ewmh_check_wm_pid(FvwmWindow *fw) { int size = 0; Atom *val; fw->ewmh_window_type = 0; val = ewmh_AtomGetByName( FW_W(fw), "_NET_WM_PID", EWMH_ATOM_LIST_FIXED_PROPERTY, &size); if (val != NULL) { free(val); SET_HAS_EWMH_WM_PID(fw, 1); if (CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO) { SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV); SET_CR_MOTION_METHOD_DETECTED(fw, 1); } } return; } /* see also EWMH_WMName and EWMH_WMIconName in add_window */ void EWMH_WindowInit(FvwmWindow *fw) { /*EWMH_DLOG("Init window 0x%lx",FW_W(fw));*/ EWMH_SetWMState(fw, False); EWMH_SetWMDesktop(fw); EWMH_SetAllowedActions(fw); ewmh_WMStrut(fw, NULL, NULL, 0); ewmh_WMIconGeometry(fw, NULL, NULL, 0); ewmh_AddToKdeSysTray(fw); EWMH_SetFrameStrut(fw); if (IS_EWMH_DESKTOP(FW_W(fw))) { return; } if (ksmserver_workarround(fw)) { return; } ewmh_WMIcon(fw, NULL, NULL, 0); ewmh_check_wm_pid(fw); /*EWMH_DLOG("window 0x%lx initialised",FW_W(fw));*/ return; } /* unmap or reparent: restore state */ void EWMH_RestoreInitialStates(FvwmWindow *fw, int event_type) { EWMH_SetWMState(fw, True); if (HAS_EWMH_INIT_WM_DESKTOP(fw) == EWMH_STATE_HAS_HINT) { ewmh_ChangeProperty( FW_W(fw), "_NET_WM_DESKTOP", EWMH_ATOM_LIST_CLIENT_WIN, (unsigned char *)&(fw->ewmh_hint_desktop), 1); } else { ewmh_DeleteProperty( FW_W(fw), "_NET_WM_DESKTOP", EWMH_ATOM_LIST_CLIENT_WIN); } if (HAS_EWMH_WM_ICON_HINT(fw) == EWMH_FVWM_ICON) { EWMH_DeleteWmIcon(fw, True, True); } return; } /* a window are going to be destroyed (in the add_window.c destroy_window * sens) */ void EWMH_DestroyWindow(FvwmWindow *fw) { if (IS_EWMH_DESKTOP(FW_W(fw))) { Scr.EwmhDesktop = NULL; } if (fw->Desk >= ewmhc.NumberOfDesktops) { ewmhc.NeedsToCheckDesk = True; } return; } /* a window has been destroyed (unmap/reparent/destroy) */ void EWMH_WindowDestroyed(void) { EWMH_SetClientList(); EWMH_SetClientListStacking(); if (ewmhc.NeedsToCheckDesk) { EWMH_SetNumberOfDesktops(); } ewmh_ComputeAndSetWorkArea(); ewmh_HandleDynamicWorkArea(); return; } /* * Init Stuff */ static int set_all_atom_in_list(ewmh_atom *list) { int l = 0; while(list->name != NULL) { list->atom = XInternAtom(dpy,list->name,False); if (list->atom_type == None) { list->atom_type = XA_UTF8_STRING; } l++; list++; } return l; } static void set_net_supported(int l) { Atom *supported; int i, k = 0; supported = (Atom *)safemalloc(l*sizeof(Atom)); for(i=0; i < NUMBER_OF_ATOM_LISTS; i++) { ewmh_atom *list = atom_list[i].list; while(list->name != NULL) { supported[k++] = list->atom; list++; } } ewmh_ChangeProperty( Scr.Root, "_NET_SUPPORTED", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)supported, k); free(supported); return; } static void clean_up(void) { ewmh_ChangeProperty( Scr.Root,"_KDE_NET_SYSTEM_TRAY_WINDOWS", EWMH_ATOM_LIST_FVWM_ROOT, NULL, 0); return; } void EWMH_Init(void) { int i; int supported_count = 0; long val; XTextProperty text; unsigned char utf_name[4]; char *names[1]; XClassHint classhints; /* initialisation of all the atoms */ XA_UTF8_STRING = XInternAtom(dpy,"UTF8_STRING",False); for(i=0; i < NUMBER_OF_ATOM_LISTS; i++) { supported_count += set_all_atom_in_list(atom_list[i].list); } /* the laws that we respect */ set_net_supported(supported_count); /* use the Scr.NoFocusWin as the WM_CHECK window */ val = Scr.NoFocusWin; ewmh_ChangeProperty( Scr.Root, "_NET_SUPPORTING_WM_CHECK", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)&val, 1); ewmh_ChangeProperty( Scr.NoFocusWin, "_NET_SUPPORTING_WM_CHECK", EWMH_ATOM_LIST_FVWM_ROOT, (unsigned char *)&val, 1); names[0] = "fvwm"; classhints.res_name= "fvwm"; classhints.res_class= "FVWM"; XSetClassHint(dpy, Scr.NoFocusWin, &classhints); if (XStringListToTextProperty(names, 1, &text)) { XSetWMName(dpy, Scr.NoFocusWin, &text); XFree(text.value); } /* FVWM in UTF8 */ utf_name[0] = 0x46; utf_name[1] = 0x56; utf_name[2] = 0x57; utf_name[3] = 0x4D; ewmh_ChangeProperty( Scr.NoFocusWin, "_NET_WM_NAME", EWMH_ATOM_LIST_PROPERTY_NOTIFY, (unsigned char *)&utf_name, 4); clean_up(); EWMH_SetDesktopNames(); EWMH_SetCurrentDesktop(); EWMH_SetNumberOfDesktops(); EWMH_SetDesktopViewPort(); EWMH_SetDesktopGeometry(); EWMH_SetClientList(); EWMH_SetClientListStacking(); ewmh_ComputeAndSetWorkArea(); return; } /* * Exit Stuff */ void EWMH_ExitStuff(void) { FvwmWindow *fw; for (fw = Scr.FvwmRoot.next; fw != NULL; fw = fw->next) { EWMH_RestoreInitialStates(fw, 0); } return; } #ifdef EWMH_DEBUG void EWMH_DLOG(char *msg, ...) { va_list args; clock_t time_val, time_taken; static clock_t start_time = 0; static clock_t prev_time = 0; struct tms not_used_tms; char buffer[200]; /* oversized */ time_t mytime; struct tm *t_ptr; time(&mytime); t_ptr = localtime(&mytime); if (start_time == 0) { /* get clock ticks */ prev_time = start_time = (unsigned int)times(¬_used_tms); } /* get clock ticks */ time_val = (unsigned int)times(¬_used_tms); time_taken = time_val - prev_time; prev_time = time_val; sprintf( buffer, "%.2d:%.2d:%.2d %6ld", t_ptr->tm_hour, t_ptr->tm_min, t_ptr->tm_sec, time_taken); fprintf(stderr, "EWMH DEBUG: "); va_start(args,msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, "\n"); fprintf(stderr, " [time]: %s\n",buffer); return; } #endif void EWMH_fullscreen(FvwmWindow *fw) { fscreen_scr_arg fscr; rectangle scr_g; size_borders b; int page_x; int page_y; char cmd[128] = "\0"; /* maximize with ResizeMoveMaximize */ if ( !is_function_allowed( F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False) || !is_function_allowed( F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False) || !is_function_allowed( F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True)) { return; } if (IS_ICONIFIED(fw)) { execute_function_override_window( NULL, NULL, "Iconify off", 0, fw); } if (IS_SHADED(fw)) { int sas = fw->shade_anim_steps; fw->shade_anim_steps = 0; execute_function_override_window( NULL, NULL, "WindowShade off", 0, fw); fw->shade_anim_steps = sas; } SET_EWMH_FULLSCREEN(fw,True); apply_decor_change(fw); fscr.xypos.x = fw->g.frame.x + fw->g.frame.width / 2; fscr.xypos.y = fw->g.frame.y + fw->g.frame.height / 2; FScreenGetScrRect( &fscr, FSCREEN_XYPOS, &scr_g.x, &scr_g.y, &scr_g.width, &scr_g.height); get_window_borders(fw, &b); get_page_offset_check_visible(&page_x, &page_y, fw); sprintf( cmd, "ResizeMoveMaximize %dp %dp +%dp +%dp", scr_g.width, scr_g.height, scr_g.x - b.top_left.width + page_x, scr_g.y - b.top_left.height + page_y); if (DO_EWMH_USE_STACKING_HINTS(fw)) { int sl = fw->ewmh_normal_layer; new_layer(fw, Scr.TopLayer); if (sl == 0) { fw->ewmh_normal_layer = Scr.DefaultLayer; } else { fw->ewmh_normal_layer = sl; } } if (cmd[0] != 0) { SET_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw, 1); execute_function_override_window(NULL, NULL, cmd, 0, fw); SET_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw, 0); } return; }