/* kwm.c-- stuff for support for kwm hints * * Window Maker window manager * * Copyright (c) 1998, 1999 Alfredo K. Kojima * * 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. */ /* * * Supported stuff: * ================ * * kfm icon selection from krootbgwm * * kwm.h function/method Notes *---------------------------------------------------------------------------- * setUnsavedDataHint() currently, only gives visual clue that * there is unsaved data (broken X close button) * setSticky() * setIcon() std X thing... * setDecoration() * logout() * refreshScreen() * setWmCommand() std X thing * currentDesktop() * setKWMModule() * * isKWMInitialized() not trustworthy * activeWindow() dunno it's use, but since it's easy to * implement it won't hurt to support * switchToDesktop() * (set/get)WindowRegion() * (set)numberOfDesktops() KDE limits to 32, but wmaker is virtually * unlimited. May raise some incompatibility * in badly written KDE modules? * (set/get)DesktopName() * sendKWMCommand() also does the message relay thing * desktop() * geometryRestore() * isIconified() * isMaximized() * isSticky() * moveToDesktop() WARNING: evil mechanism * setGeometryRestore() WARNING: evil mechanism * setMaximize() woo hoo! wanna race? * setIconify() BAH!: why reinvent the f'ing wheel!? * its even broken!!! * move() std X thing * setGeometry() std X thing * close() std X thing * activate() * activateInternal() * raise() std X thing * lower() std X thing * prepareForSwallowing() std X thing * doNotManage() klugy thing... * getBlablablaString() * setKWMDockModule() maybe we can make the Dock behave as the KDE * dock, but must figure where to show the windows * setDockWindow() * * Unsupported stuff (superfluous/not-essential/nonsense): * ======================================================= * * darkenScreen() * setMiniIcon() * configureWm() * raiseSoundEvent() * registerSoundEvent() * unregisterSoundEvent() * title() * titleWithState() * geometry() kde lib code makes it unnecessary * * * Extensions to KDE: * ================== * * These are clientmessage-type messages specific to Window Maker: * wmaker:info - show info panel * wmaker:legal - show legal panel * wmaker:arrangeIcons - arrange icons * wmaker:showAll - show all windows * wmaker:hideOthers - hide others * wmaker:restart - restart wmaker * wmaker:exit - exit wmaker */ /* * TODO * different WORKAREA for each workspace */ #include "wconfig.h" #ifdef KWM_HINTS #include #include #include #include #include #include #include #include #include #include #include #include "WindowMaker.h" #include "screen.h" #include "wcore.h" #include "framewin.h" #include "window.h" #include "properties.h" #include "icon.h" #include "client.h" #include "funcs.h" #include "actions.h" #include "workspace.h" #include "dialog.h" #include "stacking.h" #include "kwm.h" /*#define DEBUG1 */ /******* Global ******/ extern Time LastFocusChange; extern Time LastTimestamp; extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW; extern Atom _XA_WM_DELETE_WINDOW; /** Local **/ static Atom _XA_KWM_COMMAND = 0; static Atom _XA_KWM_ACTIVATE_WINDOW = 0; static Atom _XA_KWM_ACTIVE_WINDOW = 0; static Atom _XA_KWM_DO_NOT_MANAGE = 0; static Atom _XA_KWM_DOCKWINDOW = 0; static Atom _XA_KWM_RUNNING = 0; static Atom _XA_KWM_MODULE = 0; static Atom _XA_KWM_MODULE_INIT = 0; static Atom _XA_KWM_MODULE_INITIALIZED = 0; static Atom _XA_KWM_MODULE_DESKTOP_CHANGE = 0; static Atom _XA_KWM_MODULE_DESKTOP_NAME_CHANGE = 0; static Atom _XA_KWM_MODULE_DESKTOP_NUMBER_CHANGE = 0; static Atom _XA_KWM_MODULE_WIN_ADD = 0; static Atom _XA_KWM_MODULE_WIN_REMOVE = 0; static Atom _XA_KWM_MODULE_WIN_CHANGE = 0; static Atom _XA_KWM_MODULE_WIN_RAISE = 0; static Atom _XA_KWM_MODULE_WIN_LOWER = 0; static Atom _XA_KWM_MODULE_WIN_ACTIVATE = 0; static Atom _XA_KWM_MODULE_WIN_ICON_CHANGE = 0; static Atom _XA_KWM_MODULE_DOCKWIN_ADD = 0; static Atom _XA_KWM_MODULE_DOCKWIN_REMOVE = 0; static Atom _XA_KWM_WIN_UNSAVED_DATA = 0; static Atom _XA_KWM_WIN_DECORATION = 0; static Atom _XA_KWM_WIN_DESKTOP = 0; static Atom _XA_KWM_WIN_GEOMETRY_RESTORE = 0; static Atom _XA_KWM_WIN_ICONIFIED = 0; static Atom _XA_KWM_WIN_MAXIMIZED = 0; static Atom _XA_KWM_WIN_STICKY = 0; static Atom _XA_KWM_WIN_ICON_GEOMETRY = 0; static Atom _XA_KWM_CURRENT_DESKTOP = 0; static Atom _XA_KWM_NUMBER_OF_DESKTOPS = 0; static Atom _XA_KWM_DESKTOP_NAME_[MAX_WORKSPACES]; static Atom _XA_KWM_WINDOW_REGION_[MAX_WORKSPACES]; /* list of window titles that must not be managed */ typedef struct KWMDoNotManageList { char title[20]; struct KWMDoNotManageList *next; } KWMDoNotManageList; static KWMDoNotManageList *KWMDoNotManageCrap = NULL; /* list of KWM modules */ typedef struct KWMModuleList { Window window; struct KWMModuleList *next; #ifdef DEBUG1 char *title; #endif } KWMModuleList; static KWMModuleList *KWMModules = NULL; static KWMModuleList *KWMDockWindows = NULL; /* window decoration types */ enum { KWMnoDecoration = 0, KWMnormalDecoration = 1, KWMtinyDecoration = 2, KWMnoFocus = 256, KWMstandaloneMenuBar = 512, KWMdesktopIcon = 1024, KWMstaysOnTop = 2048 }; static Bool getSimpleHint(Window win, Atom atom, long *retval) { long *data = 0; assert(atom!=0); data = (long*)PropGetCheckProperty(win, atom, atom, 32, 1, NULL); if (!data) return False; *retval = *data; XFree(data); return True; } static void setSimpleHint(Window win, Atom atom, long value) { assert(atom!=0); XChangeProperty(dpy, win, atom, atom, 32, PropModeReplace, (unsigned char*)&value, 1); } static void sendClientMessage(WScreen *scr, Window window, Atom atom, long value) { XEvent event; long mask = 0; assert(atom!=0); memset(&event, 0, sizeof(XEvent)); event.xclient.type = ClientMessage; event.xclient.message_type = atom; event.xclient.window = window; event.xclient.format = 32; event.xclient.data.l[0] = value; event.xclient.data.l[1] = LastTimestamp; if (scr && scr->root_win == window) mask = SubstructureRedirectMask; XSendEvent(dpy, window, False, mask, &event); } static void sendTextMessage(WScreen *scr, Window window, Atom atom, char *text) { XEvent event; long mask = 0; int i; assert(atom!=0); memset(&event, 0, sizeof(XEvent)); event.xclient.type = ClientMessage; event.xclient.message_type = atom; event.xclient.window = window; event.xclient.format = 8; for (i=0; i<20 && text[i]; i++) event.xclient.data.b[i] = text[i]; if (scr && scr->root_win == window) mask = SubstructureRedirectMask; XSendEvent(dpy, window, False, mask, &event); } static Bool getAreaHint(Window win, Atom atom, WArea *area) { long *data = 0; data = (long*)PropGetCheckProperty(win, atom, atom, 32, 4, NULL); if (!data) return False; area->x1 = data[0]; area->y1 = data[1]; area->x2 = data[2] + area->x1; area->y2 = data[3] + area->y1; XFree(data); return True; } static void setAreaHint(Window win, Atom atom, WArea area) { long value[4]; assert(atom!=0); value[0] = area.x1; value[1] = area.y1; value[2] = area.x2 - area.x1; value[3] = area.y2 - area.y1; XChangeProperty(dpy, win, atom, atom, 32, PropModeReplace, (unsigned char*)&value, 4); } static void addModule(WScreen *scr, Window window) { KWMModuleList *node; long val; WWindow *ptr; node = malloc(sizeof(KWMModuleList)); if (!node) { wwarning("out of memory while registering KDE module"); return; } node->next = KWMModules; node->window = window; KWMModules = node; sendClientMessage(scr, window, _XA_KWM_MODULE_INIT, 0); if (getSimpleHint(window, _XA_KWM_MODULE, &val) && val==2) { if (scr->kwm_dock != None) { setSimpleHint(window, _XA_KWM_MODULE, 1); } else { KWMModuleList *ptr; scr->kwm_dock = window; for (ptr = KWMDockWindows; ptr!=NULL; ptr = ptr->next) { sendClientMessage(scr, scr->kwm_dock, _XA_KWM_MODULE_DOCKWIN_ADD, ptr->window); } } } /* send list of windows */ for (ptr = scr->focused_window; ptr!=NULL; ptr = ptr->prev) { if (!ptr->flags.kwm_hidden_for_modules && !WFLAGP(ptr, skip_window_list)) { sendClientMessage(scr, window, _XA_KWM_MODULE_WIN_ADD, ptr->client_win); } } /* send window stacking order */ wKWMSendStacking(scr, window); /* send focused window */ if (scr->focused_window && scr->focused_window->flags.focused) { sendClientMessage(scr, window, _XA_KWM_MODULE_WIN_ACTIVATE, scr->focused_window->client_win); } /* tell who we are */ sendTextMessage(scr, window, _XA_KWM_COMMAND, "wm:wmaker"); sendClientMessage(scr, window, _XA_KWM_MODULE_INITIALIZED, 0); #ifdef DEBUG1 KWMModules->title = NULL; XFetchName(dpy, window, &KWMModules->title); printf("NEW MODULE %s\n", KWMModules->title); #endif } static void removeModule(WScreen *scr, Window window) { KWMModuleList *next; if (!KWMModules) { return; } if (KWMModules->window == window) { next = KWMModules->next; #ifdef DEBUG1 printf("REMOVING MODULE %s\n", KWMModules->title); if (KWMModules->title) XFree(KWMModules->title); #endif free(KWMModules); KWMModules = next; } else { KWMModuleList *ptr; ptr = KWMModules; while (ptr->next) { if (ptr->next->window == window) { next = ptr->next->next; #ifdef DEBUG1 printf("REMOVING MODULE %s\n", ptr->next->title); if (ptr->next->title) XFree(ptr->next->title); #endif free(ptr->next); ptr->next->next = next; break; } ptr->next = ptr->next->next; } } if (scr->kwm_dock == window) scr->kwm_dock = None; } static void addDockWindow(WScreen *scr, Window window) { KWMModuleList *ptr; for (ptr = KWMDockWindows; ptr != NULL; ptr = ptr->next) { if (ptr->window == window) break; } if (!ptr) { KWMModuleList *node; node = malloc(sizeof(KWMModuleList)); if (!node) { wwarning("out of memory while processing KDE dock window"); return; } node->next = KWMDockWindows; KWMDockWindows = node; node->window = window; XSelectInput(dpy, window, StructureNotifyMask); sendClientMessage(scr, scr->kwm_dock, _XA_KWM_MODULE_DOCKWIN_ADD, window); } } static void removeDockWindow(WScreen *scr, Window window) { if (!KWMDockWindows) return; if (KWMDockWindows->window == window) { KWMModuleList *next; sendClientMessage(scr, scr->kwm_dock, _XA_KWM_MODULE_DOCKWIN_REMOVE, window); next = KWMDockWindows->next; free(KWMDockWindows); KWMDockWindows = next; } else { KWMModuleList *ptr, *next; ptr = KWMDockWindows; while (ptr->next) { if (ptr->next->window == window) { sendClientMessage(scr, scr->kwm_dock, _XA_KWM_MODULE_DOCKWIN_REMOVE, window); next = ptr->next->next; free(ptr->next); ptr->next = next; return; } ptr = ptr->next; } } } static void sendToModules(WScreen *scr, Atom atom, WWindow *wwin, long data) { KWMModuleList *ptr; XEvent event; long mask; if (wwin) { if (wwin->flags.kwm_hidden_for_modules || WFLAGP(wwin, skip_window_list)) return; data = wwin->client_win; } #ifdef DEBUG1 printf("notifying %s\n",XGetAtomName(dpy, atom)); #endif memset(&event, 0, sizeof(XEvent)); event.xclient.type = ClientMessage; event.xclient.message_type = atom; event.xclient.format = 32; event.xclient.data.l[1] = LastTimestamp; mask = 0; if (scr && scr->root_win == data) mask = SubstructureRedirectMask; for (ptr = KWMModules; ptr!=NULL; ptr = ptr->next) { event.xclient.window = ptr->window; event.xclient.data.l[0] = data; XSendEvent(dpy, ptr->window, False, mask, &event); } } void wKWMInitStuff(WScreen *scr) { if (!_XA_KWM_WIN_STICKY) { _XA_KWM_WIN_UNSAVED_DATA = XInternAtom(dpy, "KWM_WIN_UNSAVED_DATA", False); _XA_KWM_WIN_DECORATION = XInternAtom(dpy, "KWM_WIN_DECORATION", False); _XA_KWM_WIN_DESKTOP = XInternAtom(dpy, "KWM_WIN_DESKTOP", False); _XA_KWM_WIN_GEOMETRY_RESTORE = XInternAtom(dpy, "KWM_WIN_GEOMETRY_RESTORE", False); _XA_KWM_WIN_STICKY = XInternAtom(dpy, "KWM_WIN_STICKY", False); _XA_KWM_WIN_ICONIFIED = XInternAtom(dpy, "KWM_WIN_ICONIFIED", False); _XA_KWM_WIN_MAXIMIZED = XInternAtom(dpy, "KWM_WIN_MAXIMIZED", False); _XA_KWM_WIN_ICON_GEOMETRY = XInternAtom(dpy, "KWM_WIN_ICON_GEOMETRY", False); _XA_KWM_COMMAND = XInternAtom(dpy, "KWM_COMMAND", False); _XA_KWM_ACTIVE_WINDOW = XInternAtom(dpy, "KWM_ACTIVE_WINDOW", False); _XA_KWM_ACTIVATE_WINDOW = XInternAtom(dpy, "KWM_ACTIVATE_WINDOW", False); _XA_KWM_DO_NOT_MANAGE = XInternAtom(dpy, "KWM_DO_NOT_MANAGE", False); _XA_KWM_CURRENT_DESKTOP = XInternAtom(dpy, "KWM_CURRENT_DESKTOP", False); _XA_KWM_NUMBER_OF_DESKTOPS = XInternAtom(dpy, "KWM_NUMBER_OF_DESKTOPS", False); _XA_KWM_DOCKWINDOW = XInternAtom(dpy, "KWM_DOCKWINDOW", False); _XA_KWM_RUNNING = XInternAtom(dpy, "KWM_RUNNING", False); _XA_KWM_MODULE = XInternAtom(dpy, "KWM_MODULE", False); _XA_KWM_MODULE_INIT = XInternAtom(dpy, "KWM_MODULE_INIT", False); _XA_KWM_MODULE_INITIALIZED = XInternAtom(dpy, "KWM_MODULE_INITIALIZED", False); /* dunno what these do, but Matthias' patch contains it... */ _XA_KWM_MODULE_DESKTOP_CHANGE = XInternAtom(dpy, "KWM_MODULE_DESKTOP_CHANGE", False); _XA_KWM_MODULE_DESKTOP_NAME_CHANGE = XInternAtom(dpy, "KWM_MODULE_DESKTOP_NAME_CHANGE", False); _XA_KWM_MODULE_DESKTOP_NUMBER_CHANGE = XInternAtom(dpy, "KWM_MODULE_DESKTOP_NUMBER_CHANGE", False); _XA_KWM_MODULE_WIN_ADD = XInternAtom(dpy, "KWM_MODULE_WIN_ADD", False); _XA_KWM_MODULE_WIN_REMOVE = XInternAtom(dpy, "KWM_MODULE_WIN_REMOVE", False); _XA_KWM_MODULE_WIN_CHANGE = XInternAtom(dpy, "KWM_MODULE_WIN_CHANGE", False); _XA_KWM_MODULE_WIN_RAISE = XInternAtom(dpy, "KWM_MODULE_WIN_RAISE", False); _XA_KWM_MODULE_WIN_LOWER = XInternAtom(dpy, "KWM_MODULE_WIN_LOWER", False); _XA_KWM_MODULE_WIN_ACTIVATE = XInternAtom(dpy, "KWM_MODULE_WIN_ACTIVATE", False); _XA_KWM_MODULE_WIN_ICON_CHANGE = XInternAtom(dpy, "KWM_MODULE_WIN_ICON_CHANGE", False); _XA_KWM_MODULE_DOCKWIN_ADD = XInternAtom(dpy, "KWM_MODULE_DOCKWIN_ADD", False); _XA_KWM_MODULE_DOCKWIN_REMOVE = XInternAtom(dpy, "KWM_MODULE_DOCKWIN_REMOVE", False); memset(_XA_KWM_WINDOW_REGION_, 0, sizeof(_XA_KWM_WINDOW_REGION_)); memset(_XA_KWM_DESKTOP_NAME_, 0, sizeof(_XA_KWM_DESKTOP_NAME_)); } #define SETSTR(hint, str) {\ static Atom a = 0; if (!a) a = XInternAtom(dpy, #hint, False);\ XChangeProperty(dpy, scr->root_win, a, XA_STRING, 8, PropModeReplace,\ (unsigned char*)str, strlen(str));} SETSTR(KWM_STRING_MAXIMIZE, _("Maximize")); SETSTR(KWM_STRING_UNMAXIMIZE, _("Unmaximize")); SETSTR(KWM_STRING_ICONIFY, _("Miniaturize")); SETSTR(KWM_STRING_UNICONIFY, _("Deminiaturize")); SETSTR(KWM_STRING_STICKY, _("Omnipresent")); SETSTR(KWM_STRING_UNSTICKY, _("Not Omnipresent")); SETSTR(KWM_STRING_STRING_MOVE, _("Move")); SETSTR(KWM_STRING_STRING_RESIZE, _("Resize")); SETSTR(KWM_STRING_CLOSE, _("Close")); SETSTR(KWM_STRING_TODESKTOP, _("Move To")); SETSTR(KWM_STRING_ONTOCURRENTDESKTOP, _("Bring Here")); #undef SETSTR } void wKWMSendStacking(WScreen *scr, Window module) { int i; WCoreWindow *core; for (i = 0; i < MAX_WINDOW_LEVELS; i++) { for (core = scr->stacking_list[i]; core != NULL; core = core->stacking->under) { WWindow *wwin; wwin = wWindowFor(core->window); if (wwin) sendClientMessage(scr, module, _XA_KWM_MODULE_WIN_RAISE, wwin->client_win); } } } void wKWMBroadcastStacking(WScreen *scr) { KWMModuleList *ptr = KWMModules; while (ptr) { wKWMSendStacking(scr, ptr->window); ptr = ptr->next; } } char* wKWMGetWorkspaceName(WScreen *scr, int workspace) { Atom type_ret; int fmt_ret; unsigned long nitems_ret; unsigned long bytes_after_ret; char *data = NULL, *tmp; char buffer[64]; assert(workspace >= 0 && workspace < MAX_WORKSPACES); if (_XA_KWM_DESKTOP_NAME_[workspace]==0) { sprintf(buffer, "KWM_DESKTOP_NAME_%d", workspace + 1); _XA_KWM_DESKTOP_NAME_[workspace] = XInternAtom(dpy, buffer, False); } /* What do these people have against correctly using property types? */ if (XGetWindowProperty(dpy, scr->root_win, _XA_KWM_DESKTOP_NAME_[workspace], 0, 128, False, XA_STRING, &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret, (unsigned char**)&data)!=Success || !data) return NULL; tmp = wstrdup(data); XFree(data); return tmp; } void wKWMSetInitializedHint(WScreen *scr) { setSimpleHint(scr->root_win, _XA_KWM_RUNNING, 1); } void wKWMShutdown(WScreen *scr, Bool closeModules) { KWMModuleList *ptr; XDeleteProperty(dpy, scr->root_win, _XA_KWM_RUNNING); if (closeModules) { for (ptr = KWMModules; ptr != NULL; ptr = ptr->next) { XKillClient(dpy, ptr->window); } } } void wKWMCheckClientHints(WWindow *wwin, int *layer, int *workspace) { long val; if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_UNSAVED_DATA, &val) && val) { wwin->client_flags.broken_close = 1; } if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_DECORATION, &val)) { if (val & KWMnoFocus) { wwin->client_flags.no_focusable = 1; } switch (val & ~KWMnoFocus) { case KWMnoDecoration: wwin->client_flags.no_titlebar = 1; wwin->client_flags.no_resizebar = 1; break; case KWMtinyDecoration: wwin->client_flags.no_resizebar = 1; break; case KWMstandaloneMenuBar: wwin->client_flags.no_titlebar = 1; wwin->client_flags.no_resizebar = 1; wwin->client_flags.no_resizable = 1; wwin->client_flags.skip_window_list = 1; wwin->client_flags.no_hide_others = 1; wwin->flags.kwm_menubar = 1; *layer = WMMainMenuLevel; break; case KWMdesktopIcon: *layer = WMDesktopLevel; break; case KWMstaysOnTop: *layer = WMFloatingLevel; break; case KWMnormalDecoration: default: break; } } if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_DESKTOP, &val)) { *workspace = val - 1; } } void wKWMCheckClientInitialState(WWindow *wwin) { long val; WArea area; if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_STICKY, &val) && val) { wwin->client_flags.omnipresent = 1; } if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_ICONIFIED, &val) && val) { wwin->flags.miniaturized = 1; } if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_MAXIMIZED, &val) && val) { wwin->flags.maximized = MAX_VERTICAL|MAX_HORIZONTAL; } if (getAreaHint(wwin->client_win, _XA_KWM_WIN_GEOMETRY_RESTORE, &area) && (wwin->old_geometry.x != area.x1 || wwin->old_geometry.y != area.y1 || wwin->old_geometry.width != area.x2 - area.x1 || wwin->old_geometry.height != area.y2 - area.y1)) { wwin->old_geometry.x = area.x1; wwin->old_geometry.y = area.y1; wwin->old_geometry.width = area.x2 - area.x1; wwin->old_geometry.height = area.y2 - area.y1; } } Bool wKWMCheckClientHintChange(WWindow *wwin, XPropertyEvent *event) { Bool processed = True; Bool flag; long value; if (!wwin->frame) { return False; } if (event->atom == _XA_KWM_WIN_UNSAVED_DATA) { #ifdef DEBUG1 printf("got KDE unsaved data change\n"); #endif flag = getSimpleHint(wwin->client_win, _XA_KWM_WIN_UNSAVED_DATA, &value) && value; if (flag != wwin->client_flags.broken_close) { wwin->client_flags.broken_close = flag; if (wwin->frame) wWindowUpdateButtonImages(wwin); } } else if (event->atom == _XA_KWM_WIN_STICKY) { #ifdef DEBUG1 printf("got KDE sticky change\n"); #endif flag = !getSimpleHint(wwin->client_win, _XA_KWM_WIN_STICKY, &value) || value; if (flag != wwin->client_flags.omnipresent) { wwin->client_flags.omnipresent = flag; UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE); } } else if (event->atom == _XA_KWM_WIN_MAXIMIZED) { #ifdef DEBUG1 printf("got KDE maximize change\n"); #endif flag = !getSimpleHint(wwin->client_win, _XA_KWM_WIN_MAXIMIZED, &value) || value; if (flag != (wwin->flags.maximized!=0)) { if (flag) wMaximizeWindow(wwin, flag*(MAX_VERTICAL|MAX_HORIZONTAL)); else wUnmaximizeWindow(wwin); } } else if (event->atom == _XA_KWM_WIN_ICONIFIED) { #ifdef DEBUG1 printf("got KDE iconify change\n"); #endif flag = !getSimpleHint(wwin->client_win, _XA_KWM_WIN_ICONIFIED, &value) || value; if (flag != wwin->flags.miniaturized) { if (flag) wIconifyWindow(wwin); else wDeiconifyWindow(wwin); } } else if (event->atom == _XA_KWM_WIN_DECORATION) { Bool refresh = False; int oldnofocus; #ifdef DEBUG1 printf("got KDE decoration change\n"); #endif oldnofocus = wwin->client_flags.no_focusable; if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_DECORATION, &value)) { wwin->client_flags.no_focusable = (value & KWMnoFocus)!=0; switch (value & ~KWMnoFocus) { case KWMnoDecoration: if (!WFLAGP(wwin, no_titlebar) || !WFLAGP(wwin, no_resizebar)) refresh = True; wwin->client_flags.no_titlebar = 1; wwin->client_flags.no_resizebar = 1; break; case KWMtinyDecoration: if (WFLAGP(wwin, no_titlebar) || !WFLAGP(wwin, no_resizebar)) refresh = True; wwin->client_flags.no_titlebar = 0; wwin->client_flags.no_resizebar = 1; break; case KWMnormalDecoration: default: if (WFLAGP(wwin, no_titlebar) || WFLAGP(wwin, no_resizebar)) refresh = True; wwin->client_flags.no_titlebar = 0; wwin->client_flags.no_resizebar = 0; break; } } else { if (WFLAGP(wwin, no_titlebar) || WFLAGP(wwin, no_resizebar)) refresh = True; wwin->client_flags.no_focusable = (value & KWMnoFocus)!=0; wwin->client_flags.no_titlebar = 0; wwin->client_flags.no_resizebar = 0; } if (refresh) wWindowConfigureBorders(wwin); if (wwin->client_flags.no_focusable && !oldnofocus) { sendToModules(wwin->screen_ptr, _XA_KWM_MODULE_WIN_REMOVE, wwin, 0); wwin->flags.kwm_hidden_for_modules = 1; } else if (!wwin->client_flags.no_focusable && oldnofocus) { if (wwin->flags.kwm_hidden_for_modules) { sendToModules(wwin->screen_ptr, _XA_KWM_MODULE_WIN_ADD, wwin, 0); wwin->flags.kwm_hidden_for_modules = 0; } } } else if (event->atom == _XA_KWM_WIN_DESKTOP && wwin->frame) { #ifdef DEBUG1 printf("got KDE workspace change for %s\n", wwin->frame->title); #endif if (getSimpleHint(wwin->client_win, _XA_KWM_WIN_DESKTOP, &value) && value-1 != wwin->frame->workspace) { wWindowChangeWorkspace(wwin, value-1); } } else if (event->atom == _XA_KWM_WIN_GEOMETRY_RESTORE) { WArea area; #ifdef DEBUG1 printf("got KDE geometry restore change\n"); #endif if (getAreaHint(wwin->client_win, _XA_KWM_WIN_GEOMETRY_RESTORE, &area) && (wwin->old_geometry.x != area.x1 || wwin->old_geometry.y != area.y1 || wwin->old_geometry.width != area.x2 - area.x1 || wwin->old_geometry.height != area.y2 - area.y1)) { wwin->old_geometry.x = area.x1; wwin->old_geometry.y = area.y1; wwin->old_geometry.width = area.x2 - area.x1; wwin->old_geometry.height = area.y2 - area.y1; } } else { processed = False; } return processed; } static Bool performWindowCommand(WScreen *scr, char *command) { WWindow *wwin = NULL; wwin = scr->focused_window; if (!wwin || !wwin->flags.focused || !wwin->flags.mapped) { wwin = NULL; } CloseWindowMenu(scr); if (strcmp(command, "winMove")==0 || strcmp(command, "winResize")==0) { if (wwin) wKeyboardMoveResizeWindow(wwin); } else if (strcmp(command, "winMaximize")==0) { if (wwin) wMaximizeWindow(wwin, MAX_VERTICAL|MAX_HORIZONTAL); } else if (strcmp(command, "winRestore")==0) { if (wwin && wwin->flags.maximized) wUnmaximizeWindow(wwin); } else if (strcmp(command, "winIconify")==0) { if (wwin && !WFLAGP(wwin, no_miniaturizable)) { if (wwin->protocols.MINIATURIZE_WINDOW) wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, LastTimestamp); else { wIconifyWindow(wwin); } } } else if (strcmp(command, "winClose")==0) { if (wwin && !WFLAGP(wwin, no_closable)) { if (wwin->protocols.DELETE_WINDOW) wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp); } } else if (strcmp(command, "winSticky")==0) { if (wwin) { wwin->client_flags.omnipresent ^= 1; UpdateSwitchMenu(scr, wwin, ACTION_CHANGE_WORKSPACE); } } else if (strcmp(command, "winShade")==0) { if (wwin && !WFLAGP(wwin, no_shadeable)) { if (wwin->flags.shaded) wUnshadeWindow(wwin); else wShadeWindow(wwin); } } else if (strcmp(command, "winOperation")==0) { if (wwin) OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y+wwin->frame->top_width, True); } else { return False; } return True; } static void performCommand(WScreen *scr, char *command, XClientMessageEvent *event) { assert(scr != NULL); if (strcmp(command, "commandLine")==0 || strcmp(command, "execute")==0) { char *cmd; cmd = ExpandOptions(scr, _("%a(Run Command,Type the command to run:)")); if (cmd) { ExecuteShellCommand(scr, cmd); free(cmd); } } else if (strcmp(command, "logout")==0) { Shutdown(WSLogoutMode); } else if (strcmp(command, "refreshScreen")==0) { wRefreshDesktop(scr); } else if (strncmp(command, "go:", 3)==0) { Shutdown(WSRestartPreparationMode); Restart(&command[3], False); Restart(NULL, True); } else if (strcmp(command, "desktop+1")==0) { wWorkspaceRelativeChange(scr, 1); } else if (strcmp(command, "desktop-1")==0) { wWorkspaceRelativeChange(scr, -1); } else if (strcmp(command, "desktop+2")==0) { wWorkspaceRelativeChange(scr, 2); } else if (strcmp(command, "desktop-2")==0) { wWorkspaceRelativeChange(scr, -2); } else if (strcmp(command, "desktop%%2")==0) { if (scr->current_workspace % 2 == 1) wWorkspaceRelativeChange(scr, 1); else wWorkspaceRelativeChange(scr, -1); } else if (strncmp(command, "desktop", 7)==0) { int ws; ws = atoi(&command[7]); wWorkspaceChange(scr, ws); /* wmaker specific stuff */ } else if (strcmp(command, "wmaker:info")==0) { wShowInfoPanel(scr); } else if (strcmp(command, "wmaker:legal")==0) { wShowLegalPanel(scr); } else if (strcmp(command, "wmaker:arrangeIcons")==0) { wArrangeIcons(scr, True); } else if (strcmp(command, "wmaker:showAll")==0) { wShowAllWindows(scr); } else if (strcmp(command, "wmaker:hideOthers")==0) { wHideOtherApplications(scr->focused_window); } else if (strcmp(command, "wmaker:restart")==0) { Shutdown(WSRestartPreparationMode); Restart(NULL, True); } else if (strcmp(command, "wmaker:exit")==0) { Shutdown(WSExitMode); #ifdef UNSUPPORTED_STUFF } else if (strcmp(command, "moduleRaised")==0) { /* useless */ } else if (strcmp(command, "deskUnclutter")==0) { } else if (strcmp(command, "deskCascade")==0) { } else if (strcmp(command, "configure")==0) { } else if (strcmp(command, "taskManager")==0) { } else if (strcmp(command, "darkenScreen")==0) { /* breaks consistency */ #endif } else if (!performWindowCommand(scr, command)) { KWMModuleList *module; long mask = 0; XEvent ev; /* do message relay thing */ ev.xclient = *event; for (module = KWMModules; module != NULL; module = module->next) { ev.xclient.window = module->window; if (module->window == scr->root_win) mask = SubstructureRedirectMask; else mask = 0; XSendEvent(dpy, module->window, False, mask, &ev); } } } Bool wKWMProcessClientMessage(XClientMessageEvent *event) { Bool processed = True; WScreen *scr; #ifdef DEBUG1 printf("CLIENT MESS %s\n", XGetAtomName(dpy, event->message_type)); #endif if (event->message_type == _XA_KWM_COMMAND && event->format==8) { char buffer[24]; int i; scr = wScreenForRootWindow(event->window); for (i=0; i<20; i++) { buffer[i] = event->data.b[i]; } buffer[i] = 0; #ifdef DEBUG1 printf("got KDE command %s\n", buffer); #endif performCommand(scr, buffer, event); } else if (event->message_type == _XA_KWM_ACTIVATE_WINDOW) { WWindow *wwin; #ifdef DEBUG1 printf("got KDE activate internal\n"); #endif wwin = wWindowFor(event->data.l[0]); if (wwin) wSetFocusTo(wwin->screen_ptr, wwin); } else if (event->message_type == _XA_KWM_DO_NOT_MANAGE && event->format == 8) { KWMDoNotManageList *node; int i; #ifdef DEBUG1 printf("got KDE dont manage\n"); #endif node = malloc(sizeof(KWMDoNotManageList)); if (!node) { wwarning("out of memory processing KWM_DO_NOT_MANAGE message"); } for (i=0; i<20 && event->data.b[i]; i++) node->title[i] = event->data.b[i]; node->title[i] = 0; node->next = KWMDoNotManageCrap; KWMDoNotManageCrap = node; } else if (event->message_type == _XA_KWM_MODULE) { long val; Window modwin = event->data.l[0]; scr = wScreenForRootWindow(event->window); if (getSimpleHint(modwin, _XA_KWM_MODULE, &val) && val) { #ifdef DEBUG1 puts("got KDE module startup"); #endif addModule(scr, modwin); } else { #ifdef DEBUG1 puts("got KDE module finish"); #endif removeModule(scr, modwin); } } else { processed = False; } return processed; } void wKWMCheckModule(WScreen *scr, Window window) { long val; if (getSimpleHint(window, _XA_KWM_MODULE, &val) && val) { #ifdef DEBUG1 puts("got KDE module startup"); #endif addModule(scr, window); } } Bool wKWMCheckRootHintChange(WScreen *scr, XPropertyEvent *event) { Bool processed = True; long value; if (event->atom == _XA_KWM_CURRENT_DESKTOP) { if (getSimpleHint(scr->root_win, _XA_KWM_CURRENT_DESKTOP, &value)) { #ifdef DEBUG1 printf("got KDE workspace switch to %li\n", value); #endif if (value-1 != scr->current_workspace) { wWorkspaceChange(scr, value-1); } } } else if (event->atom == _XA_KWM_NUMBER_OF_DESKTOPS) { #ifdef DEBUG1 printf("got KDE workspace number change\n"); #endif if (getSimpleHint(scr->root_win, _XA_KWM_NUMBER_OF_DESKTOPS, &value)) { /* increasing is easy... */ if (value > scr->workspace_count) { scr->flags.kwm_syncing_count = 1; wWorkspaceMake(scr, value - scr->workspace_count); scr->flags.kwm_syncing_count = 0; } else if (value < scr->workspace_count) { int i; Bool rebuild = False; scr->flags.kwm_syncing_count = 1; /* decrease all we can do */ for (i = scr->workspace_count; i >= value; i--) { if (!wWorkspaceDelete(scr, i)) { rebuild = True; break; } } scr->flags.kwm_syncing_count = 0; /* someone destroyed a workspace that can't be destroyed. * Reset the hints to reflect our internal state. */ if (rebuild) { wKWMUpdateWorkspaceCountHint(scr); } } } } else { int i; processed = False; for (i = 0; i < MAX_WORKSPACES && i < scr->workspace_count; i++) { if (event->atom == _XA_KWM_DESKTOP_NAME_[i]) { char *name; name = wKWMGetWorkspaceName(scr, i); #ifdef DEBUG1 printf("got KDE workspace name change to %s\n", name); #endif if (name && strncmp(name, scr->workspaces[i]->name, MAX_WORKSPACENAME_WIDTH)!=0) { scr->flags.kwm_syncing_name = 1; wWorkspaceRename(scr, i, name); scr->flags.kwm_syncing_name = 0; } if (name) XFree(name); processed = True; break; } else if (event->atom == _XA_KWM_WINDOW_REGION_[i]) { WArea area; if (getAreaHint(scr->root_win, event->atom, &area)) { if (scr->totalUsableArea.x1 != area.x1 || scr->totalUsableArea.y1 != area.y1 || scr->totalUsableArea.x2 != area.x2 || scr->totalUsableArea.y2 != area.y2) { wScreenUpdateUsableArea(scr); } } processed = True; break; } } } return processed; } Bool wKWMManageableClient(WScreen *scr, Window win, char *title) { KWMDoNotManageList *ptr, *next; long val; if (getSimpleHint(win, _XA_KWM_DOCKWINDOW, &val) && val) { addDockWindow(scr, win); return False; } ptr = KWMDoNotManageCrap; /* * TODO: support for glob patterns or regexes */ if (ptr && strncmp(ptr->title, title, strlen(ptr->title))==0) { next = ptr->next; free(ptr); KWMDoNotManageCrap = next; #ifdef DEBUG1 printf("window %s not managed per KDE request\n", title); #endif sendToModules(scr, _XA_KWM_MODULE_WIN_ADD, NULL, win); sendToModules(scr, _XA_KWM_MODULE_WIN_REMOVE, NULL, win); return False; } else if (ptr) { while (ptr->next) { if (strncmp(ptr->next->title, title, strlen(ptr->next->title))==0) { #ifdef DEBUG1 printf("window %s not managed per KDE request\n", title); #endif next = ptr->next->next; free(ptr->next); ptr->next = next; sendToModules(scr, _XA_KWM_MODULE_WIN_ADD, NULL, win); sendToModules(scr, _XA_KWM_MODULE_WIN_REMOVE, NULL, win); return False; } ptr = ptr->next; } } return True; } void wKWMUpdateCurrentWorkspaceHint(WScreen *scr) { setSimpleHint(scr->root_win, _XA_KWM_CURRENT_DESKTOP, scr->current_workspace+1); sendToModules(scr, _XA_KWM_MODULE_DESKTOP_CHANGE, NULL, scr->current_workspace+1); } void wKWMUpdateActiveWindowHint(WScreen *scr) { long val; WWindow *wwin, *tmp; if (!scr->focused_window || !scr->focused_window->flags.focused) val = None; else { val = (long)(scr->focused_window->client_win); /* raise the menubar thing */ wwin = scr->focused_window; tmp = wwin->prev; while (tmp) { if (tmp->flags.kwm_menubar && tmp->transient_for == wwin->client_win) { wRaiseFrame(tmp->frame->core); break; } tmp = tmp->prev; } } XChangeProperty(dpy, scr->root_win, _XA_KWM_ACTIVE_WINDOW, _XA_KWM_ACTIVE_WINDOW, 32, PropModeReplace, (unsigned char*)&val, 1); XFlush(dpy); } void wKWMUpdateWorkspaceCountHint(WScreen *scr) { if (scr->flags.kwm_syncing_count) return; setSimpleHint(scr->root_win, _XA_KWM_NUMBER_OF_DESKTOPS, scr->workspace_count); sendToModules(scr, _XA_KWM_MODULE_DESKTOP_NUMBER_CHANGE, NULL, scr->workspace_count); } void wKWMCheckDestroy(XDestroyWindowEvent *event) { WScreen *scr; if (event->event == event->window) { return; } scr = wScreenSearchForRootWindow(event->event); if (!scr) { return; } removeModule(scr, event->window); removeDockWindow(scr, event->window); } void wKWMUpdateWorkspaceNameHint(WScreen *scr, int workspace) { char buffer[64]; assert(workspace >= 0 && workspace < MAX_WORKSPACES); if (_XA_KWM_DESKTOP_NAME_[workspace]==0) { sprintf(buffer, "KWM_DESKTOP_NAME_%d", workspace + 1); _XA_KWM_DESKTOP_NAME_[workspace] = XInternAtom(dpy, buffer, False); } XChangeProperty(dpy, scr->root_win, _XA_KWM_DESKTOP_NAME_[workspace], XA_STRING, 8, PropModeReplace, (unsigned char*)scr->workspaces[workspace]->name, strlen(scr->workspaces[workspace]->name)+1); sendToModules(scr, _XA_KWM_MODULE_DESKTOP_NAME_CHANGE, NULL, workspace+1); } void wKWMUpdateClientWorkspace(WWindow *wwin) { #ifdef DEBUG1 printf("updating workspace of %s to %i\n", wwin->frame->title, wwin->frame->workspace+1); #endif setSimpleHint(wwin->client_win, _XA_KWM_WIN_DESKTOP, wwin->frame->workspace+1); } void wKWMUpdateClientGeometryRestore(WWindow *wwin) { WArea rect; rect.x1 = wwin->old_geometry.x; rect.y1 = wwin->old_geometry.y; rect.x2 = wwin->old_geometry.x + wwin->old_geometry.width; rect.y2 = wwin->old_geometry.y + wwin->old_geometry.height; setAreaHint(wwin->client_win, _XA_KWM_WIN_GEOMETRY_RESTORE, rect); } void wKWMUpdateClientStateHint(WWindow *wwin, WKWMStateFlag flags) { if (flags & KWMIconifiedFlag) { setSimpleHint(wwin->client_win, _XA_KWM_WIN_ICONIFIED, wwin->flags.miniaturized /*|| wwin->flags.shaded || wwin->flags.hidden*/); } if (flags & KWMStickyFlag) { setSimpleHint(wwin->client_win, _XA_KWM_WIN_STICKY, IS_OMNIPRESENT(wwin)); } if (flags & KWMMaximizedFlag) { setSimpleHint(wwin->client_win, _XA_KWM_WIN_MAXIMIZED, wwin->flags.maximized!=0); } } Bool wKWMGetUsableArea(WScreen *scr, WArea *area) { char buffer[64]; if (_XA_KWM_WINDOW_REGION_[0]==0) { sprintf(buffer, "KWM_WINDOW_REGION_%d", 1); _XA_KWM_WINDOW_REGION_[0] = XInternAtom(dpy, buffer, False); } return getAreaHint(scr->root_win, _XA_KWM_WINDOW_REGION_[0], area); } Bool wKWMGetIconGeometry(WWindow *wwin, WArea *area) { return getAreaHint(wwin->client_win, _XA_KWM_WIN_ICON_GEOMETRY, area); } #ifdef not_used void wKWMSetUsableAreaHint(WScreen *scr, int workspace) { /* if we set this after making changes of our own to the area, * the next time the area changes, we won't know what should * be the new final area. This protocol isn't worth a shit :/ */ /* * According to Matthias Ettrich: * Indeed, there's no protocol to deal with the area yet in case several * clients want to influence it. It is sufficent, though, if it is clear * that one process is responsable for the area. For KDE this is kpanel, but * I see that there might be a conflict with the docking area of windowmaker * itself. * */ #ifdef notdef char buffer[64]; assert(workspace >= 0 && workspace < MAX_WORKSPACES); if (_XA_KWM_WINDOW_REGION_[workspace]==0) { sprintf(buffer, "KWM_WINDOW_REGION_%d", workspace+1); _XA_KWM_WINDOW_REGION_[workspace] = XInternAtom(dpy, buffer, False); } setAreaHint(scr->root_win, _XA_KWM_WINDOW_REGION_[workspace], scr->totalUsableArea); #endif } #endif /* not_used */ void wKWMSendEventMessage(WWindow *wwin, WKWMEventMessage message) { Atom msg; if (wwin && (wwin->flags.internal_window || wwin->flags.kwm_hidden_for_modules || !wwin->flags.kwm_managed || WFLAGP(wwin, skip_window_list))) return; switch (message) { case WKWMAddWindow: msg = _XA_KWM_MODULE_WIN_ADD; break; case WKWMRemoveWindow: msg = _XA_KWM_MODULE_WIN_REMOVE; break; case WKWMFocusWindow: msg = _XA_KWM_MODULE_WIN_ACTIVATE; break; case WKWMRaiseWindow: msg = _XA_KWM_MODULE_WIN_RAISE; break; case WKWMLowerWindow: msg = _XA_KWM_MODULE_WIN_LOWER; break; case WKWMChangedClient: msg = _XA_KWM_MODULE_WIN_CHANGE; break; case WKWMIconChange: msg = _XA_KWM_MODULE_WIN_ICON_CHANGE; break; default: return; } sendToModules(wwin ? wwin->screen_ptr : NULL, msg, wwin, 0); } static void writeSocket(int sock, char *data) { char buffer[128]; sprintf(buffer, "%i ", strlen(data)); write(sock, buffer, strlen(buffer)); write(sock, data, strlen(data)); } static int connectKFM(WScreen *scr) { char *path; char buffer[128]; char *ptr; FILE *f; int pid; int sock = 0; struct sockaddr_un addr; path = wstrappend(wgethomedir(), "/.kde/share/apps/kfm/pid"); strcpy(buffer, getenv("DISPLAY")); ptr = strchr(buffer, ':'); if (ptr) *ptr = '_'; ptr = strrchr(buffer, '.'); if (ptr) *ptr = 0; { char b[32]; sprintf(b, ".%i", scr->screen); strcat(buffer, b); } ptr = path; path = wstrappend(ptr, buffer); free(ptr); /* pid file */ f = fopen(path, "r"); free(path); if (!f) return -1; buffer[0] = 0; fgets(buffer, 123, f); pid = atoi(buffer); if (pid <= 0) return -1; if (kill(pid, 0) != 0) return -1; buffer[0] = 0; fscanf(f, "%s", buffer); fclose(f); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) return -1; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, buffer); if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { close(sock); return -1; } path = wstrappend(wgethomedir(), "/.kde/share/apps/kfm/magic"); f = fopen(path, "r"); if (!f) { return -1; } ptr = fgets(buffer, 123, f); fclose(f); if (!ptr) { return -1; } puts(buffer); ptr = wstrappend("auth", buffer); writeSocket(sock, ptr); free(ptr); return sock; } void wKWMSelectRootRegion(WScreen *scr, int x, int y, int w, int h, Bool control) { #if 0 char buffer[128]; int sock; puts("CONNECTING"); sock = connectKFM(scr); if (sock < 0) return; puts("SENDING DATA"); sprintf(buffer, "selectRootIcons %i %i %i %i %c", x, y, w, h, control); writeSocket(sock, buffer); close(sock); #endif } #endif /* KWM_HINTS */