X-Git-Url: https://repo.or.cz/w/wmaker-crm.git/blobdiff_plain/59fc927dc9f183802621138534fa6eaafe5593ba..688a56e8ab67b56550e2874d9d7423f0d435bfd9:/src/event.c diff --git a/src/event.c b/src/event.c dissimilarity index 88% index 3afe07a9..cc42506a 100644 --- a/src/event.c +++ b/src/event.c @@ -1,1856 +1,1741 @@ -/* event.c- event loop and handling - * - * Window Maker window manager - * - * Copyright (c) 1997-2003 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. - */ - -#include -#include "wconfig.h" - - -#include -#include -#include -#include - - - -#include -#include -#ifdef SHAPE -# include -#endif -#ifdef XDND -#include "xdnd.h" -#endif - -#ifdef KEEP_XKB_LOCK_STATUS -#include -#endif /* KEEP_XKB_LOCK_STATUS */ - -#include "WindowMaker.h" -#include "window.h" -#include "actions.h" -#include "client.h" -#include "funcs.h" -#include "keybind.h" -#include "application.h" -#include "stacking.h" -#include "defaults.h" -#include "workspace.h" -#include "dock.h" -#include "framewin.h" -#include "properties.h" -#include "balloon.h" -#include "xinerama.h" - -#ifdef NETWM_HINTS -# include "wmspec.h" -#endif - -/******** Global Variables **********/ -extern XContext wWinContext; -extern XContext wVEdgeContext; - -extern Cursor wCursor[WCUR_LAST]; - -extern WShortKey wKeyBindings[WKBD_LAST]; -extern int wScreenCount; -extern Time LastTimestamp; -extern Time LastFocusChange; - -extern WPreferences wPreferences; - -#define MOD_MASK wPreferences.modifier_mask - -extern Atom _XA_WM_COLORMAP_NOTIFY; - -extern Atom _XA_WM_CHANGE_STATE; -extern Atom _XA_WM_DELETE_WINDOW; -extern Atom _XA_GNUSTEP_WM_ATTR; -extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW; -extern Atom _XA_GNUSTEP_TITLEBAR_STATE; -extern Atom _XA_WINDOWMAKER_WM_FUNCTION; -extern Atom _XA_WINDOWMAKER_COMMAND; - - -#ifdef SHAPE -extern Bool wShapeSupported; -extern int wShapeEventBase; -#endif - -#ifdef KEEP_XKB_LOCK_STATUS -extern int wXkbEventBase; -#endif - -/* special flags */ -/*extern char WDelayedActionSet;*/ - - -/************ Local stuff ***********/ - -static void saveTimestamp(XEvent *event); -static void handleColormapNotify(); -static void handleMapNotify(); -static void handleUnmapNotify(); -static void handleButtonPress(); -static void handleExpose(); -static void handleDestroyNotify(); -static void handleConfigureRequest(); -static void handleMapRequest(); -static void handlePropertyNotify(); -static void handleEnterNotify(); -static void handleLeaveNotify(); -static void handleExtensions(); -static void handleClientMessage(); -static void handleKeyPress(); -static void handleFocusIn(); -static void handleMotionNotify(); -static void handleVisibilityNotify(); - - -#ifdef SHAPE -static void handleShapeNotify(); -#endif - -/* called from the signal handler */ -void NotifyDeadProcess(pid_t pid, unsigned char status); - -/* real dead process handler */ -static void handleDeadProcess(void *foo); - - -typedef struct DeadProcesses { - pid_t pid; - unsigned char exit_status; -} DeadProcesses; - -/* stack of dead processes */ -static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES]; -static int deadProcessPtr=0; - - -typedef struct DeathHandler { - WDeathHandler *callback; - pid_t pid; - void *client_data; -} DeathHandler; - -static WMArray *deathHandlers=NULL; - - - -WMagicNumber -wAddDeathHandler(pid_t pid, WDeathHandler *callback, void *cdata) -{ - DeathHandler *handler; - - handler = malloc(sizeof(DeathHandler)); - if (!handler) - return 0; - - handler->pid = pid; - handler->callback = callback; - handler->client_data = cdata; - - if (!deathHandlers) - deathHandlers = WMCreateArrayWithDestructor(8, wfree); - - WMAddToArray(deathHandlers, handler); - - return handler; -} - - - -void -wDeleteDeathHandler(WMagicNumber id) -{ - DeathHandler *handler=(DeathHandler*)id; - - if (!handler || !deathHandlers) - return; - - /* array destructor will call wfree(handler) */ - WMRemoveFromArray(deathHandlers, handler); -} - - -void -DispatchEvent(XEvent *event) -{ - if (deathHandlers) - handleDeadProcess(NULL); - - if (WCHECK_STATE(WSTATE_NEED_EXIT)) { - WCHANGE_STATE(WSTATE_EXITING); - /* received SIGTERM */ - /* - * WMHandleEvent() can't be called from anything - * executed inside here, or we can get in a infinite - * recursive loop. - */ - Shutdown(WSExitMode); - - } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) { - WCHANGE_STATE(WSTATE_RESTARTING); - - Shutdown(WSRestartPreparationMode); - /* received SIGHUP */ - Restart(NULL, True); - } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) { - WCHANGE_STATE(WSTATE_NORMAL); - wDefaultsCheckDomains(); - } - - /* for the case that all that is wanted to be dispatched is - * the stuff above */ - if (!event) - return; - - saveTimestamp(event); - switch (event->type) { - case MapRequest: - handleMapRequest(event); - break; - - case KeyPress: - handleKeyPress(event); - break; - - case MotionNotify: - handleMotionNotify(event); - break; - - case ConfigureRequest: - handleConfigureRequest(event); - break; - - case DestroyNotify: - handleDestroyNotify(event); - break; - - case MapNotify: - handleMapNotify(event); - break; - - case UnmapNotify: - handleUnmapNotify(event); - break; - - case ButtonPress: - handleButtonPress(event); - break; - - case Expose: - handleExpose(event); - break; - - case PropertyNotify: - handlePropertyNotify(event); - break; - - case EnterNotify: - handleEnterNotify(event); - break; - - case LeaveNotify: - handleLeaveNotify(event); - break; - - case ClientMessage: - handleClientMessage(event); - break; - - case ColormapNotify: - handleColormapNotify(event); - break; - - case MappingNotify: - if (event->xmapping.request == MappingKeyboard - || event->xmapping.request == MappingModifier) - XRefreshKeyboardMapping(&event->xmapping); - break; - - case FocusIn: - handleFocusIn(event); - break; - - case VisibilityNotify: - handleVisibilityNotify(event); - break; - default: - handleExtensions(event); - break; - } -} - -/* - *---------------------------------------------------------------------- - * inotifyHandleEvents- - * Check for inotify events - * - * Returns: - * After reading events for the given file descriptor (fd) and - * watch descriptor (wd) - * - * Side effects: - * Calls wDefaultsCheckDomains if config database is updated - *---------------------------------------------------------------------- - */ - -#define BUFF_SIZE ((sizeof(struct inotify_event) + 16)*512) -void inotifyHandleEvents (int fd, int wd) -{ - extern void wDefaultsCheckDomains(); - ssize_t eventQLength, i = 0; - char buff[BUFF_SIZE] = {0}; - /* Check config only once per read of the event queue */ - int oneShotFlag = 0; - - /* - * Read off the queued events - * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should - * not occur; the block is on Xevents, but a config file change will normally - * occur as a result of an Xevent - so the event queue should never have more than - * a few entries before a read(). - */ - eventQLength = read(fd, buff, BUFF_SIZE); - - /* check what events occured */ - /* Should really check wd here too, but for now we only have one watch! */ - while (i < eventQLength) { - struct inotify_event *pevent = (struct inotify_event *)&buff[i]; - - /* - * see inotify.h for event types. - */ - if (pevent->mask & IN_DELETE_SELF) { - wwarning(_("the defaults database has been deleted!" - " Restart Window Maker to create the database" - " with the default settings")); - close(fd); - } - if (pevent->mask & IN_UNMOUNT) { - wwarning(_("the unit containing the defaults database has" - " been unmounted. Setting --static mode." - " Any changes will not be saved.")); - close(fd); - wPreferences.flags.noupdates=1; - } - if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) { - fprintf(stdout,"wmaker: reading config files in defaults database.\n"); - wDefaultsCheckDomains(); - } - - /* move to next event in the buffer */ - i += sizeof(struct inotify_event) + pevent->len; - } -} - -/* - *---------------------------------------------------------------------- - * EventLoop- - * Processes X and internal events indefinitely. - * - * Returns: - * Never returns - * - * Side effects: - * The LastTimestamp global variable is updated. - * Calls inotifyGetEvents if defaults database changes. - *---------------------------------------------------------------------- - */ -void -EventLoop() -{ - XEvent event; - extern int inotifyFD; - extern int inotifyWD; - struct timeval time; - fd_set rfds; - int retVal=0; - - if (inotifyFD < 0 || inotifyWD < 0) - retVal = -1; - - for(;;) { - - WMNextEvent(dpy, &event); /* Blocks here */ - WMHandleEvent(&event); - - if (retVal != -1 ) { - time.tv_sec = 0; - time.tv_usec = 0; - FD_ZERO (&rfds); - FD_SET (inotifyFD, &rfds); - - /* check for available read data from inotify - don't block! */ - retVal = select (inotifyFD + 1, &rfds, NULL, NULL, &time); - - if (retVal < 0) { /* an error has occured */ - wwarning(_("select failed. The inotify instance will be closed." - " Changes to the defaults database will require" - " a restart to take effect.")); - close(inotifyFD); - continue; - } - if (FD_ISSET (inotifyFD, &rfds)) - inotifyHandleEvents(inotifyFD,inotifyWD); - } - } -} - -/* - *---------------------------------------------------------------------- - * ProcessPendingEvents -- - * Processes the events that are currently pending (at the time - * this function is called) in the display's queue. - * - * Returns: - * After the pending events that were present at the function call - * are processed. - * - * Side effects: - * Many -- whatever handling events may involve. - * - *---------------------------------------------------------------------- - */ -void -ProcessPendingEvents() -{ - XEvent event; - int count; - - XSync(dpy, False); - - /* Take a snapshot of the event count in the queue */ - count = XPending(dpy); - - while (count>0 && XPending(dpy)) { - WMNextEvent(dpy, &event); - WMHandleEvent(&event); - count--; - } -} - - -Bool -IsDoubleClick(WScreen *scr, XEvent *event) -{ - if ((scr->last_click_time>0) && - (event->xbutton.time-scr->last_click_time<=wPreferences.dblclick_time) - && (event->xbutton.button == scr->last_click_button) - && (event->xbutton.window == scr->last_click_window)) { - - scr->flags.next_click_is_not_double = 1; - scr->last_click_time = 0; - scr->last_click_window = event->xbutton.window; - - return True; - } - return False; -} - - -void -NotifyDeadProcess(pid_t pid, unsigned char status) -{ - if (deadProcessPtr>=MAX_DEAD_PROCESSES-1) { - wwarning("stack overflow: too many dead processes"); - return; - } - /* stack the process to be handled later, - * as this is called from the signal handler */ - deadProcesses[deadProcessPtr].pid = pid; - deadProcesses[deadProcessPtr].exit_status = status; - deadProcessPtr++; -} - - -static void -handleDeadProcess(void *foo) -{ - DeathHandler *tmp; - int i; - - for (i=0; i0) { - deadProcessPtr--; - - for (i = WMGetArrayItemCount(deathHandlers)-1; i >= 0; i--) { - tmp = WMGetFromArray(deathHandlers, i); - if (!tmp) - continue; - - if (tmp->pid == deadProcesses[deadProcessPtr].pid) { - (*tmp->callback)(tmp->pid, - deadProcesses[deadProcessPtr].exit_status, - tmp->client_data); - wDeleteDeathHandler(tmp); - } - } - } -} - - -static void -saveTimestamp(XEvent *event) -{ - /* - * Never save CurrentTime as LastTimestamp because CurrentTime - * it's not a real timestamp (it's the 0L constant) - */ - - switch (event->type) { - case ButtonRelease: - case ButtonPress: - LastTimestamp = event->xbutton.time; - break; - case KeyPress: - case KeyRelease: - LastTimestamp = event->xkey.time; - break; - case MotionNotify: - LastTimestamp = event->xmotion.time; - break; - case PropertyNotify: - LastTimestamp = event->xproperty.time; - break; - case EnterNotify: - case LeaveNotify: - LastTimestamp = event->xcrossing.time; - break; - case SelectionClear: - LastTimestamp = event->xselectionclear.time; - break; - case SelectionRequest: - LastTimestamp = event->xselectionrequest.time; - break; - case SelectionNotify: - LastTimestamp = event->xselection.time; -#ifdef XDND - wXDNDProcessSelection(event); -#endif - break; - } -} - - -static int -matchWindow(void *item, void *cdata) -{ - return (((WFakeGroupLeader*)item)->origLeader == (Window)cdata); -} - - -static void -handleExtensions(XEvent *event) -{ -#ifdef KEEP_XKB_LOCK_STATUS - XkbEvent *xkbevent; - xkbevent = (XkbEvent *)event; -#endif /*KEEP_XKB_LOCK_STATUS*/ -#ifdef SHAPE - if (wShapeSupported && event->type == (wShapeEventBase+ShapeNotify)) { - handleShapeNotify(event); - } -#endif -#ifdef KEEP_XKB_LOCK_STATUS - if (wPreferences.modelock && (xkbevent->type == wXkbEventBase)){ - handleXkbIndicatorStateNotify(event); - } -#endif /*KEEP_XKB_LOCK_STATUS*/ -} - - -static void -handleMapRequest(XEvent *ev) -{ - WWindow *wwin; - WScreen *scr = NULL; - Window window = ev->xmaprequest.window; - -#ifdef DEBUG - printf("got map request for %x\n", (unsigned)window); -#endif - if ((wwin = wWindowFor(window))) { - if (wwin->flags.shaded) { - wUnshadeWindow(wwin); - } - /* deiconify window */ - if (wwin->flags.miniaturized) { - wDeiconifyWindow(wwin); - } else if (wwin->flags.hidden) { - WApplication *wapp = wApplicationOf(wwin->main_window); - /* go to the last workspace that the user worked on the app */ - if (wapp) { - wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace); - } - wUnhideApplication(wapp, False, False); - } - return; - } - - scr = wScreenForRootWindow(ev->xmaprequest.parent); - - wwin = wManageWindow(scr, window); - - /* - * This is to let the Dock know that the application it launched - * has already been mapped (eg: it has finished launching). - * It is not necessary for normally docked apps, but is needed for - * apps that were forcedly docked (like with dockit). - */ - if (scr->last_dock) { - if (wwin && wwin->main_window!=None && wwin->main_window!=window) - wDockTrackWindowLaunch(scr->last_dock, wwin->main_window); - else - wDockTrackWindowLaunch(scr->last_dock, window); - } - - if (wwin) { - wClientSetState(wwin, NormalState, None); - if (wwin->flags.maximized) { - wMaximizeWindow(wwin, wwin->flags.maximized); - } - if (wwin->flags.shaded) { - wwin->flags.shaded = 0; - wwin->flags.skip_next_animation = 1; - wShadeWindow(wwin); - } - if (wwin->flags.miniaturized) { - wwin->flags.miniaturized = 0; - wwin->flags.skip_next_animation = 1; - wIconifyWindow(wwin); - } - if (wwin->flags.fullscreen) { - wwin->flags.fullscreen = 0; - wFullscreenWindow(wwin); - } - if (wwin->flags.hidden) { - WApplication *wapp = wApplicationOf(wwin->main_window); - - wwin->flags.hidden = 0; - wwin->flags.skip_next_animation = 1; - if (wapp) { - wHideApplication(wapp); - } - } - } -} - - -static void -handleDestroyNotify(XEvent *event) -{ - WWindow *wwin; - WApplication *app; - Window window = event->xdestroywindow.window; - WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event); - int widx; - -#ifdef DEBUG - printf("got destroy notify\n"); -#endif - wwin = wWindowFor(window); - if (wwin) { - wUnmanageWindow(wwin, False, True); - } - - if (scr != NULL) { - while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, - (void*)window)) != WANotFound) { - WFakeGroupLeader *fPtr; - - fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx); - if (fPtr->retainCount > 0) { - fPtr->retainCount--; - if (fPtr->retainCount==0 && fPtr->leader!=None) { - XDestroyWindow(dpy, fPtr->leader); - fPtr->leader = None; - XFlush(dpy); - } - } - fPtr->origLeader = None; - } - } - - app = wApplicationOf(window); - if (app) { - if (window == app->main_window) { - app->refcount = 0; - wwin = app->main_window_desc->screen_ptr->focused_window; - while (wwin) { - if (wwin->main_window == window) { - wwin->main_window = None; - } - wwin = wwin->prev; - } - } - wApplicationDestroy(app); - } -} - - - -static void -handleExpose(XEvent *event) -{ - WObjDescriptor *desc; - XEvent ev; - -#ifdef DEBUG - printf("got expose\n"); -#endif - while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)); - - if (XFindContext(dpy, event->xexpose.window, wWinContext, - (XPointer *)&desc)==XCNOENT) { - return; - } - - if (desc->handle_expose) { - (*desc->handle_expose)(desc, event); - } -} - -static void -executeButtonAction(WScreen *scr, XEvent *event, int action) -{ - switch(action) { - case WA_SELECT_WINDOWS: - wUnselectWindows(scr); - wSelectWindows(scr, event); - break; - case WA_OPEN_APPMENU: - OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False); - /* ugly hack */ - if (scr->root_menu) { - if (scr->root_menu->brother->flags.mapped) - event->xbutton.window = scr->root_menu->brother->frame->core->window; - else - event->xbutton.window = scr->root_menu->frame->core->window; - } - break; - case WA_OPEN_WINLISTMENU: - OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False); - if (scr->switch_menu) { - if (scr->switch_menu->brother->flags.mapped) - event->xbutton.window = scr->switch_menu->brother->frame->core->window; - else - event->xbutton.window = scr->switch_menu->frame->core->window; - } - break; - default: - break; - } -} - - -/* bindable */ -static void -handleButtonPress(XEvent *event) -{ - WObjDescriptor *desc; - WScreen *scr; - -#ifdef DEBUG - printf("got button press\n"); -#endif - scr = wScreenForRootWindow(event->xbutton.root); - -#ifdef BALLOON_TEXT - wBalloonHide(scr); -#endif - - -#ifndef LITE - if (event->xbutton.window==scr->root_win) { - if (event->xbutton.button==Button1 && - wPreferences.mouse_button1!=WA_NONE) { - executeButtonAction(scr, event, wPreferences.mouse_button1); - } else if (event->xbutton.button==Button2 && - wPreferences.mouse_button2!=WA_NONE) { - executeButtonAction(scr, event, wPreferences.mouse_button2); - } else if (event->xbutton.button==Button3 && - wPreferences.mouse_button3!=WA_NONE) { - executeButtonAction(scr, event, wPreferences.mouse_button3); - } else if (event->xbutton.button==Button4 && - wPreferences.mouse_wheel!=WA_NONE) { - wWorkspaceRelativeChange(scr, 1); - } else if (event->xbutton.button==Button5 && - wPreferences.mouse_wheel!=WA_NONE) { - wWorkspaceRelativeChange(scr, -1); - } - } -#endif /* !LITE */ - - desc = NULL; - if (XFindContext(dpy, event->xbutton.subwindow, wWinContext, - (XPointer *)&desc)==XCNOENT) { - if (XFindContext(dpy, event->xbutton.window, wWinContext, - (XPointer *)&desc)==XCNOENT) { - return; - } - } - - if (desc->parent_type == WCLASS_WINDOW) { - XSync(dpy, 0); - - if (event->xbutton.state & MOD_MASK) { - XAllowEvents(dpy, AsyncPointer, CurrentTime); - } - - /* if (wPreferences.focus_mode == WKF_CLICK) {*/ - if (wPreferences.ignore_focus_click) { - XAllowEvents(dpy, AsyncPointer, CurrentTime); - } - XAllowEvents(dpy, ReplayPointer, CurrentTime); - /* }*/ - XSync(dpy, 0); - } else if (desc->parent_type == WCLASS_APPICON - || desc->parent_type == WCLASS_MINIWINDOW - || desc->parent_type == WCLASS_DOCK_ICON) { - if (event->xbutton.state & MOD_MASK) { - XSync(dpy, 0); - XAllowEvents(dpy, AsyncPointer, CurrentTime); - XSync(dpy, 0); - } - } - - if (desc->handle_mousedown!=NULL) { - (*desc->handle_mousedown)(desc, event); - } - - /* save double-click information */ - if (scr->flags.next_click_is_not_double) { - scr->flags.next_click_is_not_double = 0; - } else { - scr->last_click_time = event->xbutton.time; - scr->last_click_button = event->xbutton.button; - scr->last_click_window = event->xbutton.window; - } -} - - -static void -handleMapNotify(XEvent *event) -{ - WWindow *wwin; -#ifdef DEBUG - printf("got map\n"); -#endif - wwin = wWindowFor(event->xmap.event); - if (wwin && wwin->client_win == event->xmap.event) { - if (wwin->flags.miniaturized) { - wDeiconifyWindow(wwin); - } else { - XGrabServer(dpy); - wWindowMap(wwin); - wClientSetState(wwin, NormalState, None); - XUngrabServer(dpy); - } - } -} - - -static void -handleUnmapNotify(XEvent *event) -{ - WWindow *wwin; - XEvent ev; - Bool withdraw = False; -#ifdef DEBUG - printf("got unmap\n"); -#endif - /* only process windows with StructureNotify selected - * (ignore SubstructureNotify) */ - wwin = wWindowFor(event->xunmap.window); - if (!wwin) - return; - - /* whether the event is a Withdrawal request */ - if (event->xunmap.event == wwin->screen_ptr->root_win - && event->xunmap.send_event) - withdraw = True; - - if (wwin->client_win != event->xunmap.event && !withdraw) - return; - - if (!wwin->flags.mapped && !withdraw - && wwin->frame->workspace == wwin->screen_ptr->current_workspace - && !wwin->flags.miniaturized && !wwin->flags.hidden) - return; - - XGrabServer(dpy); - XUnmapWindow(dpy, wwin->frame->core->window); - wwin->flags.mapped = 0; - XSync(dpy, 0); - /* check if the window was destroyed */ - if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify,&ev)) { - DispatchEvent(&ev); - } else { - Bool reparented = False; - - if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev)) - reparented = True; - - /* withdraw window */ - wwin->flags.mapped = 0; - if (!reparented) - wClientSetState(wwin, WithdrawnState, None); - - /* if the window was reparented, do not reparent it back to the - * root window */ - wUnmanageWindow(wwin, !reparented, False); - } - XUngrabServer(dpy); -} - - -static void -handleConfigureRequest(XEvent *event) -{ - WWindow *wwin; -#ifdef DEBUG - printf("got configure request\n"); -#endif - if (!(wwin=wWindowFor(event->xconfigurerequest.window))) { - /* - * Configure request for unmapped window - */ - wClientConfigure(NULL, &(event->xconfigurerequest)); - } else { - wClientConfigure(wwin, &(event->xconfigurerequest)); - } -} - - -static void -handlePropertyNotify(XEvent *event) -{ - WWindow *wwin; - WApplication *wapp; - Window jr; - int ji; - unsigned int ju; - WScreen *scr; -#ifdef DEBUG - printf("got property notify\n"); -#endif - - wwin = wWindowFor(event->xproperty.window); - if (wwin) { - if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, - &ju, &ju, &ju, &ju)) { - return; - } - wClientCheckProperty(wwin, &event->xproperty); - } - wapp = wApplicationOf(event->xproperty.window); - if (wapp) { - wClientCheckProperty(wapp->main_window_desc, &event->xproperty); - } - - scr = wScreenForWindow(event->xproperty.window); -} - - -static void -handleClientMessage(XEvent *event) -{ - WWindow *wwin; - WObjDescriptor *desc; -#ifdef DEBUG - printf("got client message\n"); -#endif - /* handle transition from Normal to Iconic state */ - if (event->xclient.message_type == _XA_WM_CHANGE_STATE - && event->xclient.format == 32 - && event->xclient.data.l[0] == IconicState) { - - wwin = wWindowFor(event->xclient.window); - if (!wwin) return; - if (!wwin->flags.miniaturized) - wIconifyWindow(wwin); - } else if (event->xclient.message_type == _XA_WM_COLORMAP_NOTIFY - && event->xclient.format == 32) { - WScreen *scr = wScreenSearchForRootWindow(event->xclient.window); - - if (!scr) - return; - - if (event->xclient.data.l[1] == 1) { /* starting */ - wColormapAllowClientInstallation(scr, True); - } else { /* stopping */ - wColormapAllowClientInstallation(scr, False); - } - } else if (event->xclient.message_type == _XA_WINDOWMAKER_COMMAND) { - - wDefaultsCheckDomains(); - - } else if (event->xclient.message_type == _XA_WINDOWMAKER_WM_FUNCTION) { - WApplication *wapp; - int done=0; - wapp = wApplicationOf(event->xclient.window); - if (wapp) { - switch (event->xclient.data.l[0]) { - case WMFHideOtherApplications: - wHideOtherApplications(wapp->main_window_desc); - done = 1; - break; - - case WMFHideApplication: - wHideApplication(wapp); - done = 1; - break; - } - } - if (!done) { - wwin = wWindowFor(event->xclient.window); - if (wwin) { - switch (event->xclient.data.l[0]) { - case WMFHideOtherApplications: - wHideOtherApplications(wwin); - break; - - case WMFHideApplication: - wHideApplication(wApplicationOf(wwin->main_window)); - break; - } - } - } - } else if (event->xclient.message_type == _XA_GNUSTEP_WM_ATTR) { - wwin = wWindowFor(event->xclient.window); - if (!wwin) return; - switch (event->xclient.data.l[0]) { - case GSWindowLevelAttr: - { - int level = (int)event->xclient.data.l[1]; - - if (WINDOW_LEVEL(wwin) != level) { - ChangeStackingLevel(wwin->frame->core, level); - } - } - break; - } - } else if (event->xclient.message_type == _XA_GNUSTEP_TITLEBAR_STATE) { - wwin = wWindowFor(event->xclient.window); - if (!wwin) return; - switch (event->xclient.data.l[0]) { - case WMTitleBarNormal: - wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED); - break; - case WMTitleBarMain: - wFrameWindowChangeState(wwin->frame, WS_PFOCUSED); - break; - case WMTitleBarKey: - wFrameWindowChangeState(wwin->frame, WS_FOCUSED); - break; - } -#ifdef NETWM_HINTS - } else if (wNETWMProcessClientMessage(&event->xclient)) { - /* do nothing */ -#endif -#ifdef XDND - } else if (wXDNDProcessClientMessage(&event->xclient)) { - /* do nothing */ -#endif /* XDND */ - } else { - /* - * Non-standard thing, but needed by OffiX DND. - * For when the icon frame gets a ClientMessage - * that should have gone to the icon_window. - */ - if (XFindContext(dpy, event->xbutton.window, wWinContext, - (XPointer *)&desc)!=XCNOENT) { - struct WIcon *icon=NULL; - - if (desc->parent_type == WCLASS_MINIWINDOW) { - icon = (WIcon*)desc->parent; - } else if (desc->parent_type == WCLASS_DOCK_ICON - || desc->parent_type == WCLASS_APPICON) { - icon = ((WAppIcon*)desc->parent)->icon; - } - if (icon && (wwin=icon->owner)) { - if (wwin->client_win!=event->xclient.window) { - event->xclient.window = wwin->client_win; - XSendEvent(dpy, wwin->client_win, False, NoEventMask, - event); - } - } - } - } -} - - -static void -raiseWindow(WScreen *scr) -{ - WWindow *wwin; - - scr->autoRaiseTimer = NULL; - - wwin = wWindowFor(scr->autoRaiseWindow); - if (!wwin) - return; - - if (!wwin->flags.destroyed && wwin->flags.focused) { - wRaiseFrame(wwin->frame->core); - /* this is needed or a race condition will occur */ - XSync(dpy, False); - } -} - - -static void -handleEnterNotify(XEvent *event) -{ - WWindow *wwin; - WObjDescriptor *desc = NULL; -#ifdef VIRTUAL_DESKTOP - void (*vdHandler)(XEvent * event); -#endif - XEvent ev; - WScreen *scr = wScreenForRootWindow(event->xcrossing.root); -#ifdef DEBUG - printf("got enter notify\n"); -#endif - -#ifdef VIRTUAL_DESKTOP - if (XFindContext(dpy, event->xcrossing.window, wVEdgeContext, - (XPointer *)&vdHandler)!=XCNOENT) { - (*vdHandler)(event); - } -#endif - - if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, - &ev)) { - /* already left the window... */ - saveTimestamp(&ev); - if (ev.xcrossing.mode==event->xcrossing.mode - && ev.xcrossing.detail==event->xcrossing.detail) { - return; - } - } - - if (XFindContext(dpy, event->xcrossing.window, wWinContext, - (XPointer *)&desc)!=XCNOENT) { - if(desc->handle_enternotify) - (*desc->handle_enternotify)(desc, event); - } - - /* enter to window */ - wwin = wWindowFor(event->xcrossing.window); - if (!wwin) { - if (wPreferences.colormap_mode==WCM_POINTER) { - wColormapInstallForWindow(scr, NULL); - } - if (scr->autoRaiseTimer - && event->xcrossing.root==event->xcrossing.window) { - WMDeleteTimerHandler(scr->autoRaiseTimer); - scr->autoRaiseTimer = NULL; - } - } else { - /* set auto raise timer even if in focus-follows-mouse mode - * and the event is for the frame window, even if the window - * has focus already. useful if you move the pointer from a focused - * window to the root window and back pretty fast - * - * set focus if in focus-follows-mouse mode and the event - * is for the frame window and window doesn't have focus yet */ - if (wPreferences.focus_mode==WKF_SLOPPY - && wwin->frame->core->window==event->xcrossing.window - && !scr->flags.doing_alt_tab) { - - if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable)) - wSetFocusTo(scr, wwin); - - if (scr->autoRaiseTimer) - WMDeleteTimerHandler(scr->autoRaiseTimer); - scr->autoRaiseTimer = NULL; - - if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) { - scr->autoRaiseWindow = wwin->frame->core->window; - scr->autoRaiseTimer - = WMAddTimerHandler(wPreferences.raise_delay, - (WMCallback*)raiseWindow, scr); - } - } - /* Install colormap for window, if the colormap installation mode - * is colormap_follows_mouse */ - if (wPreferences.colormap_mode==WCM_POINTER) { - if (wwin->client_win==event->xcrossing.window) - wColormapInstallForWindow(scr, wwin); - else - wColormapInstallForWindow(scr, NULL); - } - } - - /* a little kluge to hide the clip balloon */ - if (!wPreferences.flags.noclip && scr->flags.clip_balloon_mapped) { - if (!desc) { - XUnmapWindow(dpy, scr->clip_balloon); - scr->flags.clip_balloon_mapped = 0; - } else { - if (desc->parent_type!=WCLASS_DOCK_ICON - || scr->clip_icon != desc->parent) { - XUnmapWindow(dpy, scr->clip_balloon); - scr->flags.clip_balloon_mapped = 0; - } - } - } - - if (event->xcrossing.window == event->xcrossing.root - && event->xcrossing.detail == NotifyNormal - && event->xcrossing.detail != NotifyInferior - && wPreferences.focus_mode != WKF_CLICK) { - - wSetFocusTo(scr, scr->focused_window); - } - -#ifdef BALLOON_TEXT - wBalloonEnteredObject(scr, desc); -#endif -} - - -static void -handleLeaveNotify(XEvent *event) -{ - WObjDescriptor *desc = NULL; - - if (XFindContext(dpy, event->xcrossing.window, wWinContext, - (XPointer *)&desc)!=XCNOENT) { - if(desc->handle_leavenotify) - (*desc->handle_leavenotify)(desc, event); - } -} - - -#ifdef SHAPE -static void -handleShapeNotify(XEvent *event) -{ - XShapeEvent *shev = (XShapeEvent*)event; - WWindow *wwin; - XEvent ev; -#ifdef DEBUG - printf("got shape notify\n"); -#endif - while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev)) { - XShapeEvent *sev = (XShapeEvent*)&ev; - - if (sev->kind == ShapeBounding) { - if (sev->shaped == shev->shaped) { - *shev = *sev; - } else { - XPutBackEvent(dpy, &ev); - break; - } - } - } - - wwin = wWindowFor(shev->window); - if (!wwin || shev->kind != ShapeBounding) - return; - - if (!shev->shaped && wwin->flags.shaped) { - - wwin->flags.shaped = 0; - wWindowClearShape(wwin); - - } else if (shev->shaped) { - - wwin->flags.shaped = 1; - wWindowSetShape(wwin); - } -} -#endif /* SHAPE */ - -#ifdef KEEP_XKB_LOCK_STATUS -/* please help ]d if you know what to do */ -handleXkbIndicatorStateNotify(XEvent *event) -{ - WWindow *wwin; - WScreen *scr; - XkbStateRec staterec; - int i; - - for (i=0; ifocused_window; - if (wwin && wwin->flags.focused) { - XkbGetState(dpy,XkbUseCoreKbd,&staterec); - if (wwin->frame->languagemode != staterec.group) { - wwin->frame->last_languagemode = wwin->frame->languagemode; - wwin->frame->languagemode = staterec.group; - } -#ifdef XKB_BUTTON_HINT - if (wwin->frame->titlebar) { - wFrameWindowPaint(wwin->frame); - } -#endif - } - } -} -#endif /*KEEP_XKB_LOCK_STATUS*/ - -static void -handleColormapNotify(XEvent *event) -{ - WWindow *wwin; - WScreen *scr; - Bool reinstall = False; - - wwin = wWindowFor(event->xcolormap.window); - if (!wwin) - return; - - scr = wwin->screen_ptr; - - do { - if (wwin) { - if (event->xcolormap.new) { - XWindowAttributes attr; - - XGetWindowAttributes(dpy, wwin->client_win, &attr); - - if (wwin == scr->cmap_window && wwin->cmap_window_no == 0) - scr->current_colormap = attr.colormap; - - reinstall = True; - } else if (event->xcolormap.state == ColormapUninstalled && - scr->current_colormap == event->xcolormap.colormap) { - - /* some bastard app (like XV) removed our colormap */ - /* - * can't enforce or things like xscreensaver wont work - * reinstall = True; - */ - } else if (event->xcolormap.state == ColormapInstalled && - scr->current_colormap == event->xcolormap.colormap) { - - /* someone has put our colormap back */ - reinstall = False; - } - } - } while (XCheckTypedEvent(dpy, ColormapNotify, event) - && ((wwin = wWindowFor(event->xcolormap.window)) || 1)); - - if (reinstall && scr->current_colormap!=None) { - if (!scr->flags.colormap_stuff_blocked) - XInstallColormap(dpy, scr->current_colormap); - } -} - - - -static void -handleFocusIn(XEvent *event) -{ - WWindow *wwin; - - /* - * For applications that like stealing the focus. - */ - while (XCheckTypedEvent(dpy, FocusIn, event)); - saveTimestamp(event); - if (event->xfocus.mode == NotifyUngrab - || event->xfocus.mode == NotifyGrab - || event->xfocus.detail > NotifyNonlinearVirtual) { - return; - } - - wwin = wWindowFor(event->xfocus.window); - if (wwin && !wwin->flags.focused) { - if (wwin->flags.mapped) - wSetFocusTo(wwin->screen_ptr, wwin); - else - wSetFocusTo(wwin->screen_ptr, NULL); - } else if (!wwin) { - WScreen *scr = wScreenForWindow(event->xfocus.window); - if (scr) - wSetFocusTo(scr, NULL); - } -} - - -static WWindow* -windowUnderPointer(WScreen *scr) -{ - unsigned int mask; - int foo; - Window bar, win; - - if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, - &mask)) - return wWindowFor(win); - return NULL; -} - - -static int CheckFullScreenWindowFocused(WScreen *scr) -{ - if (scr->focused_window && scr->focused_window->flags.fullscreen) - return 1; - else - return 0; -} - - -static void -handleKeyPress(XEvent *event) -{ - WScreen *scr = wScreenForRootWindow(event->xkey.root); - WWindow *wwin = scr->focused_window; - short i, widx; - int modifiers; - int command = -1; -#ifdef KEEP_XKB_LOCK_STATUS - XkbStateRec staterec; -#endif /*KEEP_XKB_LOCK_STATUS*/ - - /* ignore CapsLock */ - modifiers = event->xkey.state & ValidModMask; - - for (i=0; ixkey.keycode - && (/*wKeyBindings[i].modifier==0 - ||*/ wKeyBindings[i].modifier==modifiers)) { - command = i; - break; - } - } - - - if (command < 0) { -#ifdef LITE - { -#if 0 - } -#endif -#else - if (!wRootMenuPerformShortcut(event)) { -#endif - static int dontLoop = 0; - - if (dontLoop > 10) { - wwarning("problem with key event processing code"); - return; - } - dontLoop++; - /* if the focused window is an internal window, try redispatching - * the event to the managed window, as it can be a WINGs window */ - if (wwin && wwin->flags.internal_window - && wwin->client_leader!=None) { - /* client_leader contains the WINGs toplevel */ - event->xany.window = wwin->client_leader; - WMHandleEvent(event); - } - dontLoop--; - } - return; - } - -#define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded)) -#define ISFOCUSED(w) ((w) && (w)->flags.focused) - - switch (command) { -#ifndef LITE - case WKBD_ROOTMENU: - /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True);*/ - if (!CheckFullScreenWindowFocused(scr)) { - WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr)); - OpenRootMenu(scr, rect.pos.x + rect.size.width/2, rect.pos.y + rect.size.height/2, True); - } - break; - case WKBD_WINDOWLIST: - if (!CheckFullScreenWindowFocused(scr)) { - WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr)); - OpenSwitchMenu(scr, rect.pos.x + rect.size.width/2, rect.pos.y + rect.size.height/2, True); - } - break; -#endif /* !LITE */ - case WKBD_WINDOWMENU: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) - OpenWindowMenu(wwin, wwin->frame_x, - wwin->frame_y+wwin->frame->top_width, True); - break; - case WKBD_MINIATURIZE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) - && !WFLAGP(wwin, no_miniaturizable)) { - CloseWindowMenu(scr); - - if (wwin->protocols.MINIATURIZE_WINDOW) - wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, - event->xbutton.time); - else { - wIconifyWindow(wwin); - } - } - break; - case WKBD_HIDE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { - WApplication *wapp = wApplicationOf(wwin->main_window); - CloseWindowMenu(scr); - - if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) { - wHideApplication(wapp); - } - } - break; - case WKBD_HIDE_OTHERS: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { - CloseWindowMenu(scr); - - wHideOtherApplications(wwin); - } - break; - case WKBD_MAXIMIZE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { - int newdir = (MAX_VERTICAL|MAX_HORIZONTAL); - - CloseWindowMenu(scr); - - if (wwin->flags.maximized == newdir) { - wUnmaximizeWindow(wwin); - } else { - wMaximizeWindow(wwin, newdir|MAX_KEYBOARD); - } - } - break; - case WKBD_VMAXIMIZE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { - int newdir = (MAX_VERTICAL ^ wwin->flags.maximized); - - CloseWindowMenu(scr); - - if (newdir) { - wMaximizeWindow(wwin, newdir|MAX_KEYBOARD); - } else { - wUnmaximizeWindow(wwin); - } - } - break; - case WKBD_HMAXIMIZE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { - int newdir = (MAX_HORIZONTAL ^ wwin->flags.maximized); - - CloseWindowMenu(scr); - - if (newdir) { - wMaximizeWindow(wwin, newdir|MAX_KEYBOARD); - } else { - wUnmaximizeWindow(wwin); - } - } - break; - case WKBD_RAISE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { - CloseWindowMenu(scr); - - wRaiseFrame(wwin->frame->core); - } - break; - case WKBD_LOWER: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { - CloseWindowMenu(scr); - - wLowerFrame(wwin->frame->core); - } - break; - case WKBD_RAISELOWER: - /* raise or lower the window under the pointer, not the - * focused one - */ - wwin = windowUnderPointer(scr); - if (wwin) - wRaiseLowerFrame(wwin->frame->core); - break; - case WKBD_SHADE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) { - if (wwin->flags.shaded) - wUnshadeWindow(wwin); - else - wShadeWindow(wwin); - } - break; - case WKBD_MOVERESIZE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && - (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) { - CloseWindowMenu(scr); - - wKeyboardMoveResizeWindow(wwin); - } - break; - case WKBD_CLOSE: - if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) { - CloseWindowMenu(scr); - if (wwin->protocols.DELETE_WINDOW) - wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, - event->xkey.time); - } - break; - case WKBD_SELECT: - if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { - wSelectWindow(wwin, !wwin->flags.selected); - } - break; - case WKBD_FOCUSNEXT: - StartWindozeCycle(wwin, event, True); - break; - - case WKBD_FOCUSPREV: - StartWindozeCycle(wwin, event, False); - break; - - case WKBD_WORKSPACE1 ... WKBD_WORKSPACE10: - widx = command - WKBD_WORKSPACE1; - i = (scr->current_workspace / 10) * 10 + widx; - if (wPreferences.ws_advance || i < scr->workspace_count) - wWorkspaceChange(scr, i); - break; - - case WKBD_NEXTWORKSPACE: - wWorkspaceRelativeChange(scr, 1); - break; - case WKBD_PREVWORKSPACE: - wWorkspaceRelativeChange(scr, -1); - break; - case WKBD_WINDOW1: - case WKBD_WINDOW2: - case WKBD_WINDOW3: - case WKBD_WINDOW4: - case WKBD_WINDOW5: - case WKBD_WINDOW6: - case WKBD_WINDOW7: - case WKBD_WINDOW8: - case WKBD_WINDOW9: - case WKBD_WINDOW10: - - widx = command-WKBD_WINDOW1; - - if (scr->shortcutWindows[widx]) { - WMArray *list = scr->shortcutWindows[widx]; - int cw; - int count = WMGetArrayItemCount(list); - WWindow *twin; - WMArrayIterator iter; - WWindow *wwin; - - wUnselectWindows(scr); - cw = scr->current_workspace; - - WM_ETARETI_ARRAY(list, wwin, iter) { - if (count > 1) - wWindowChangeWorkspace(wwin, cw); - - wMakeWindowVisible(wwin); - - if (count > 1) - wSelectWindow(wwin, True); - } - - /* rotate the order of windows, to create a cycling effect */ - twin = WMGetFromArray(list, 0); - WMDeleteFromArray(list, 0); - WMAddToArray(list, twin); - - } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) { - if (scr->shortcutWindows[widx]) { - WMFreeArray(scr->shortcutWindows[widx]); - scr->shortcutWindows[widx] = NULL; - } - - if (wwin->flags.selected && scr->selected_windows) { - scr->shortcutWindows[widx] = - WMDuplicateArray(scr->selected_windows); - /*WMRemoveFromArray(scr->shortcutWindows[index], wwin); - WMInsertInArray(scr->shortcutWindows[index], 0, wwin);*/ - } else { - scr->shortcutWindows[widx] = WMCreateArray(4); - WMAddToArray(scr->shortcutWindows[widx], wwin); - } - - wSelectWindow(wwin, !wwin->flags.selected); - XFlush(dpy); - wusleep(3000); - wSelectWindow(wwin, !wwin->flags.selected); - XFlush(dpy); - - } else if (scr->selected_windows - && WMGetArrayItemCount(scr->selected_windows)) { - - if (wwin->flags.selected && scr->selected_windows) { - if (scr->shortcutWindows[widx]) { - WMFreeArray(scr->shortcutWindows[widx]); - } - scr->shortcutWindows[widx] = - WMDuplicateArray(scr->selected_windows); - } - } - - break; - - case WKBD_SWITCH_SCREEN: - if (wScreenCount > 1) { - WScreen *scr2; - int i; - - /* find index of this screen */ - for (i = 0; i < wScreenCount; i++) { - if (wScreenWithNumber(i) == scr) - break; - } - i++; - if (i >= wScreenCount) { - i = 0; - } - scr2 = wScreenWithNumber(i); - - if (scr2) { - XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0, - scr2->scr_width/2, scr2->scr_height/2); - } - } - break; - - case WKBD_NEXTWSLAYER: - case WKBD_PREVWSLAYER: - { - int row, column; - - row = scr->current_workspace/10; - column = scr->current_workspace%10; - - if (command==WKBD_NEXTWSLAYER) { - if ((row+1)*10 < scr->workspace_count) - wWorkspaceChange(scr, column+(row+1)*10); - } else { - if (row > 0) - wWorkspaceChange(scr, column+(row-1)*10); - } - } - break; - case WKBD_CLIPLOWER: - if (!wPreferences.flags.noclip) - wDockLower(scr->workspaces[scr->current_workspace]->clip); - break; - case WKBD_CLIPRAISE: - if (!wPreferences.flags.noclip) - wDockRaise(scr->workspaces[scr->current_workspace]->clip); - break; - case WKBD_CLIPRAISELOWER: - if (!wPreferences.flags.noclip) - wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip); - break; -#ifdef KEEP_XKB_LOCK_STATUS - case WKBD_TOGGLE: - if(wPreferences.modelock) { - /*toggle*/ - wwin = scr->focused_window; - - if (wwin && wwin->flags.mapped - && wwin->frame->workspace == wwin->screen_ptr->current_workspace - && !wwin->flags.miniaturized && !wwin->flags.hidden) { - XkbGetState(dpy,XkbUseCoreKbd,&staterec); - - wwin->frame->languagemode = wwin->frame->last_languagemode; - wwin->frame->last_languagemode = staterec.group; - XkbLockGroup(dpy,XkbUseCoreKbd, wwin->frame->languagemode); - - } - } - break; -#endif /* KEEP_XKB_LOCK_STATUS */ -#ifdef VIRTUAL_DESKTOP - case WKBD_VDESK_LEFT: - wWorkspaceKeyboardMoveDesktop(scr, VEC_LEFT); - break; - - case WKBD_VDESK_RIGHT: - wWorkspaceKeyboardMoveDesktop(scr, VEC_RIGHT); - break; - - case WKBD_VDESK_UP: - wWorkspaceKeyboardMoveDesktop(scr, VEC_UP); - break; - - case WKBD_VDESK_DOWN: - wWorkspaceKeyboardMoveDesktop(scr, VEC_DOWN); - break; -#endif - - } -} - - -static void -handleMotionNotify(XEvent *event) -{ - WScreen *scr = wScreenForRootWindow(event->xmotion.root); - - if (wPreferences.scrollable_menus) { - WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root); - WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p)); - - if (scr->flags.jump_back_pending || - p.x <= (rect.pos.x + 1) || - p.x >= (rect.pos.x + rect.size.width - 2) || - p.y <= (rect.pos.y + 1) || - p.y >= (rect.pos.y + rect.size.height - 2)) { - WMenu *menu; -#ifdef DEBUG - printf("pointer at screen edge\n"); -#endif - menu = wMenuUnderPointer(scr); - if (menu!=NULL) - wMenuScroll(menu, event); - } - } -} - - -static void -handleVisibilityNotify(XEvent *event) -{ - WWindow *wwin; - - wwin = wWindowFor(event->xvisibility.window); - if (!wwin) - return; - wwin->flags.obscured = - (event->xvisibility.state == VisibilityFullyObscured); -} - - +/* event.c- event loop and handling + * + * Window Maker window manager + * + * Copyright (c) 1997-2003 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. + */ + +#include +#include "wconfig.h" + +#include +#include +#include +#include + +#include +#include +#ifdef SHAPE +# include +#endif +#ifdef XDND +#include "xdnd.h" +#endif + +#ifdef KEEP_XKB_LOCK_STATUS +#include +#endif /* KEEP_XKB_LOCK_STATUS */ + +#include "WindowMaker.h" +#include "window.h" +#include "actions.h" +#include "client.h" +#include "funcs.h" +#include "keybind.h" +#include "application.h" +#include "stacking.h" +#include "defaults.h" +#include "workspace.h" +#include "dock.h" +#include "framewin.h" +#include "properties.h" +#include "balloon.h" +#include "xinerama.h" + +#ifdef NETWM_HINTS +# include "wmspec.h" +#endif + +/******** Global Variables **********/ +extern XContext wWinContext; +extern XContext wVEdgeContext; + +extern Cursor wCursor[WCUR_LAST]; + +extern WShortKey wKeyBindings[WKBD_LAST]; +extern int wScreenCount; +extern Time LastTimestamp; +extern Time LastFocusChange; + +extern WPreferences wPreferences; + +#define MOD_MASK wPreferences.modifier_mask + +extern Atom _XA_WM_COLORMAP_NOTIFY; + +extern Atom _XA_WM_CHANGE_STATE; +extern Atom _XA_WM_DELETE_WINDOW; +extern Atom _XA_GNUSTEP_WM_ATTR; +extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW; +extern Atom _XA_GNUSTEP_TITLEBAR_STATE; +extern Atom _XA_WINDOWMAKER_WM_FUNCTION; +extern Atom _XA_WINDOWMAKER_COMMAND; + +#ifdef SHAPE +extern Bool wShapeSupported; +extern int wShapeEventBase; +#endif + +#ifdef KEEP_XKB_LOCK_STATUS +extern int wXkbEventBase; +#endif + +/* special flags */ +/*extern char WDelayedActionSet;*/ + +/************ Local stuff ***********/ + +static void saveTimestamp(XEvent * event); +static void handleColormapNotify(); +static void handleMapNotify(); +static void handleUnmapNotify(); +static void handleButtonPress(); +static void handleExpose(); +static void handleDestroyNotify(); +static void handleConfigureRequest(); +static void handleMapRequest(); +static void handlePropertyNotify(); +static void handleEnterNotify(); +static void handleLeaveNotify(); +static void handleExtensions(); +static void handleClientMessage(); +static void handleKeyPress(); +static void handleFocusIn(); +static void handleMotionNotify(); +static void handleVisibilityNotify(); + +#ifdef SHAPE +static void handleShapeNotify(); +#endif + +/* called from the signal handler */ +void NotifyDeadProcess(pid_t pid, unsigned char status); + +/* real dead process handler */ +static void handleDeadProcess(void *foo); + +typedef struct DeadProcesses { + pid_t pid; + unsigned char exit_status; +} DeadProcesses; + +/* stack of dead processes */ +static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES]; +static int deadProcessPtr = 0; + +typedef struct DeathHandler { + WDeathHandler *callback; + pid_t pid; + void *client_data; +} DeathHandler; + +static WMArray *deathHandlers = NULL; + +WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata) +{ + DeathHandler *handler; + + handler = malloc(sizeof(DeathHandler)); + if (!handler) + return 0; + + handler->pid = pid; + handler->callback = callback; + handler->client_data = cdata; + + if (!deathHandlers) + deathHandlers = WMCreateArrayWithDestructor(8, wfree); + + WMAddToArray(deathHandlers, handler); + + return handler; +} + +void wDeleteDeathHandler(WMagicNumber id) +{ + DeathHandler *handler = (DeathHandler *) id; + + if (!handler || !deathHandlers) + return; + + /* array destructor will call wfree(handler) */ + WMRemoveFromArray(deathHandlers, handler); +} + +void DispatchEvent(XEvent * event) +{ + if (deathHandlers) + handleDeadProcess(NULL); + + if (WCHECK_STATE(WSTATE_NEED_EXIT)) { + WCHANGE_STATE(WSTATE_EXITING); + /* received SIGTERM */ + /* + * WMHandleEvent() can't be called from anything + * executed inside here, or we can get in a infinite + * recursive loop. + */ + Shutdown(WSExitMode); + + } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) { + WCHANGE_STATE(WSTATE_RESTARTING); + + Shutdown(WSRestartPreparationMode); + /* received SIGHUP */ + Restart(NULL, True); + } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) { + WCHANGE_STATE(WSTATE_NORMAL); + wDefaultsCheckDomains(); + } + + /* for the case that all that is wanted to be dispatched is + * the stuff above */ + if (!event) + return; + + saveTimestamp(event); + switch (event->type) { + case MapRequest: + handleMapRequest(event); + break; + + case KeyPress: + handleKeyPress(event); + break; + + case MotionNotify: + handleMotionNotify(event); + break; + + case ConfigureRequest: + handleConfigureRequest(event); + break; + + case DestroyNotify: + handleDestroyNotify(event); + break; + + case MapNotify: + handleMapNotify(event); + break; + + case UnmapNotify: + handleUnmapNotify(event); + break; + + case ButtonPress: + handleButtonPress(event); + break; + + case Expose: + handleExpose(event); + break; + + case PropertyNotify: + handlePropertyNotify(event); + break; + + case EnterNotify: + handleEnterNotify(event); + break; + + case LeaveNotify: + handleLeaveNotify(event); + break; + + case ClientMessage: + handleClientMessage(event); + break; + + case ColormapNotify: + handleColormapNotify(event); + break; + + case MappingNotify: + if (event->xmapping.request == MappingKeyboard || event->xmapping.request == MappingModifier) + XRefreshKeyboardMapping(&event->xmapping); + break; + + case FocusIn: + handleFocusIn(event); + break; + + case VisibilityNotify: + handleVisibilityNotify(event); + break; + default: + handleExtensions(event); + break; + } +} + +/* + *---------------------------------------------------------------------- + * inotifyHandleEvents- + * Check for inotify events + * + * Returns: + * After reading events for the given file descriptor (fd) and + * watch descriptor (wd) + * + * Side effects: + * Calls wDefaultsCheckDomains if config database is updated + *---------------------------------------------------------------------- + */ + +#define BUFF_SIZE ((sizeof(struct inotify_event) + 16)*512) +void inotifyHandleEvents(int fd, int wd) +{ + extern void wDefaultsCheckDomains(); + ssize_t eventQLength, i = 0; + char buff[BUFF_SIZE] = { 0 }; + /* Check config only once per read of the event queue */ + int oneShotFlag = 0; + + /* + * Read off the queued events + * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should + * not occur; the block is on Xevents, but a config file change will normally + * occur as a result of an Xevent - so the event queue should never have more than + * a few entries before a read(). + */ + eventQLength = read(fd, buff, BUFF_SIZE); + + /* check what events occured */ + /* Should really check wd here too, but for now we only have one watch! */ + while (i < eventQLength) { + struct inotify_event *pevent = (struct inotify_event *)&buff[i]; + + /* + * see inotify.h for event types. + */ + if (pevent->mask & IN_DELETE_SELF) { + wwarning(_("the defaults database has been deleted!" + " Restart Window Maker to create the database" " with the default settings")); + close(fd); + } + if (pevent->mask & IN_UNMOUNT) { + wwarning(_("the unit containing the defaults database has" + " been unmounted. Setting --static mode." " Any changes will not be saved.")); + close(fd); + wPreferences.flags.noupdates = 1; + } + if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) { + fprintf(stdout, "wmaker: reading config files in defaults database.\n"); + wDefaultsCheckDomains(); + } + + /* move to next event in the buffer */ + i += sizeof(struct inotify_event) + pevent->len; + } +} + +/* + *---------------------------------------------------------------------- + * EventLoop- + * Processes X and internal events indefinitely. + * + * Returns: + * Never returns + * + * Side effects: + * The LastTimestamp global variable is updated. + * Calls inotifyGetEvents if defaults database changes. + *---------------------------------------------------------------------- + */ +void EventLoop() +{ + XEvent event; + extern int inotifyFD; + extern int inotifyWD; + struct timeval time; + fd_set rfds; + int retVal = 0; + + if (inotifyFD < 0 || inotifyWD < 0) + retVal = -1; + + for (;;) { + + WMNextEvent(dpy, &event); /* Blocks here */ + WMHandleEvent(&event); + + if (retVal != -1) { + time.tv_sec = 0; + time.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(inotifyFD, &rfds); + + /* check for available read data from inotify - don't block! */ + retVal = select(inotifyFD + 1, &rfds, NULL, NULL, &time); + + if (retVal < 0) { /* an error has occured */ + wwarning(_("select failed. The inotify instance will be closed." + " Changes to the defaults database will require" + " a restart to take effect.")); + close(inotifyFD); + continue; + } + if (FD_ISSET(inotifyFD, &rfds)) + inotifyHandleEvents(inotifyFD, inotifyWD); + } + } +} + +/* + *---------------------------------------------------------------------- + * ProcessPendingEvents -- + * Processes the events that are currently pending (at the time + * this function is called) in the display's queue. + * + * Returns: + * After the pending events that were present at the function call + * are processed. + * + * Side effects: + * Many -- whatever handling events may involve. + * + *---------------------------------------------------------------------- + */ +void ProcessPendingEvents() +{ + XEvent event; + int count; + + XSync(dpy, False); + + /* Take a snapshot of the event count in the queue */ + count = XPending(dpy); + + while (count > 0 && XPending(dpy)) { + WMNextEvent(dpy, &event); + WMHandleEvent(&event); + count--; + } +} + +Bool IsDoubleClick(WScreen * scr, XEvent * event) +{ + if ((scr->last_click_time > 0) && + (event->xbutton.time - scr->last_click_time <= wPreferences.dblclick_time) + && (event->xbutton.button == scr->last_click_button) + && (event->xbutton.window == scr->last_click_window)) { + + scr->flags.next_click_is_not_double = 1; + scr->last_click_time = 0; + scr->last_click_window = event->xbutton.window; + + return True; + } + return False; +} + +void NotifyDeadProcess(pid_t pid, unsigned char status) +{ + if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) { + wwarning("stack overflow: too many dead processes"); + return; + } + /* stack the process to be handled later, + * as this is called from the signal handler */ + deadProcesses[deadProcessPtr].pid = pid; + deadProcesses[deadProcessPtr].exit_status = status; + deadProcessPtr++; +} + +static void handleDeadProcess(void *foo) +{ + DeathHandler *tmp; + int i; + + for (i = 0; i < deadProcessPtr; i++) { + wWindowDeleteSavedStatesForPID(deadProcesses[i].pid); + } + + if (!deathHandlers) { + deadProcessPtr = 0; + return; + } + + /* get the pids on the queue and call handlers */ + while (deadProcessPtr > 0) { + deadProcessPtr--; + + for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) { + tmp = WMGetFromArray(deathHandlers, i); + if (!tmp) + continue; + + if (tmp->pid == deadProcesses[deadProcessPtr].pid) { + (*tmp->callback) (tmp->pid, + deadProcesses[deadProcessPtr].exit_status, tmp->client_data); + wDeleteDeathHandler(tmp); + } + } + } +} + +static void saveTimestamp(XEvent * event) +{ + /* + * Never save CurrentTime as LastTimestamp because CurrentTime + * it's not a real timestamp (it's the 0L constant) + */ + + switch (event->type) { + case ButtonRelease: + case ButtonPress: + LastTimestamp = event->xbutton.time; + break; + case KeyPress: + case KeyRelease: + LastTimestamp = event->xkey.time; + break; + case MotionNotify: + LastTimestamp = event->xmotion.time; + break; + case PropertyNotify: + LastTimestamp = event->xproperty.time; + break; + case EnterNotify: + case LeaveNotify: + LastTimestamp = event->xcrossing.time; + break; + case SelectionClear: + LastTimestamp = event->xselectionclear.time; + break; + case SelectionRequest: + LastTimestamp = event->xselectionrequest.time; + break; + case SelectionNotify: + LastTimestamp = event->xselection.time; +#ifdef XDND + wXDNDProcessSelection(event); +#endif + break; + } +} + +static int matchWindow(void *item, void *cdata) +{ + return (((WFakeGroupLeader *) item)->origLeader == (Window) cdata); +} + +static void handleExtensions(XEvent * event) +{ +#ifdef KEEP_XKB_LOCK_STATUS + XkbEvent *xkbevent; + xkbevent = (XkbEvent *) event; +#endif /*KEEP_XKB_LOCK_STATUS */ +#ifdef SHAPE + if (wShapeSupported && event->type == (wShapeEventBase + ShapeNotify)) { + handleShapeNotify(event); + } +#endif +#ifdef KEEP_XKB_LOCK_STATUS + if (wPreferences.modelock && (xkbevent->type == wXkbEventBase)) { + handleXkbIndicatorStateNotify(event); + } +#endif /*KEEP_XKB_LOCK_STATUS */ +} + +static void handleMapRequest(XEvent * ev) +{ + WWindow *wwin; + WScreen *scr = NULL; + Window window = ev->xmaprequest.window; + +#ifdef DEBUG + printf("got map request for %x\n", (unsigned)window); +#endif + if ((wwin = wWindowFor(window))) { + if (wwin->flags.shaded) { + wUnshadeWindow(wwin); + } + /* deiconify window */ + if (wwin->flags.miniaturized) { + wDeiconifyWindow(wwin); + } else if (wwin->flags.hidden) { + WApplication *wapp = wApplicationOf(wwin->main_window); + /* go to the last workspace that the user worked on the app */ + if (wapp) { + wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace); + } + wUnhideApplication(wapp, False, False); + } + return; + } + + scr = wScreenForRootWindow(ev->xmaprequest.parent); + + wwin = wManageWindow(scr, window); + + /* + * This is to let the Dock know that the application it launched + * has already been mapped (eg: it has finished launching). + * It is not necessary for normally docked apps, but is needed for + * apps that were forcedly docked (like with dockit). + */ + if (scr->last_dock) { + if (wwin && wwin->main_window != None && wwin->main_window != window) + wDockTrackWindowLaunch(scr->last_dock, wwin->main_window); + else + wDockTrackWindowLaunch(scr->last_dock, window); + } + + if (wwin) { + wClientSetState(wwin, NormalState, None); + if (wwin->flags.maximized) { + wMaximizeWindow(wwin, wwin->flags.maximized); + } + if (wwin->flags.shaded) { + wwin->flags.shaded = 0; + wwin->flags.skip_next_animation = 1; + wShadeWindow(wwin); + } + if (wwin->flags.miniaturized) { + wwin->flags.miniaturized = 0; + wwin->flags.skip_next_animation = 1; + wIconifyWindow(wwin); + } + if (wwin->flags.fullscreen) { + wwin->flags.fullscreen = 0; + wFullscreenWindow(wwin); + } + if (wwin->flags.hidden) { + WApplication *wapp = wApplicationOf(wwin->main_window); + + wwin->flags.hidden = 0; + wwin->flags.skip_next_animation = 1; + if (wapp) { + wHideApplication(wapp); + } + } + } +} + +static void handleDestroyNotify(XEvent * event) +{ + WWindow *wwin; + WApplication *app; + Window window = event->xdestroywindow.window; + WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event); + int widx; + +#ifdef DEBUG + printf("got destroy notify\n"); +#endif + wwin = wWindowFor(window); + if (wwin) { + wUnmanageWindow(wwin, False, True); + } + + if (scr != NULL) { + while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) { + WFakeGroupLeader *fPtr; + + fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx); + if (fPtr->retainCount > 0) { + fPtr->retainCount--; + if (fPtr->retainCount == 0 && fPtr->leader != None) { + XDestroyWindow(dpy, fPtr->leader); + fPtr->leader = None; + XFlush(dpy); + } + } + fPtr->origLeader = None; + } + } + + app = wApplicationOf(window); + if (app) { + if (window == app->main_window) { + app->refcount = 0; + wwin = app->main_window_desc->screen_ptr->focused_window; + while (wwin) { + if (wwin->main_window == window) { + wwin->main_window = None; + } + wwin = wwin->prev; + } + } + wApplicationDestroy(app); + } +} + +static void handleExpose(XEvent * event) +{ + WObjDescriptor *desc; + XEvent ev; + +#ifdef DEBUG + printf("got expose\n"); +#endif + while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)) ; + + if (XFindContext(dpy, event->xexpose.window, wWinContext, (XPointer *) & desc) == XCNOENT) { + return; + } + + if (desc->handle_expose) { + (*desc->handle_expose) (desc, event); + } +} + +static void executeButtonAction(WScreen * scr, XEvent * event, int action) +{ + switch (action) { + case WA_SELECT_WINDOWS: + wUnselectWindows(scr); + wSelectWindows(scr, event); + break; + case WA_OPEN_APPMENU: + OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False); + /* ugly hack */ + if (scr->root_menu) { + if (scr->root_menu->brother->flags.mapped) + event->xbutton.window = scr->root_menu->brother->frame->core->window; + else + event->xbutton.window = scr->root_menu->frame->core->window; + } + break; + case WA_OPEN_WINLISTMENU: + OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False); + if (scr->switch_menu) { + if (scr->switch_menu->brother->flags.mapped) + event->xbutton.window = scr->switch_menu->brother->frame->core->window; + else + event->xbutton.window = scr->switch_menu->frame->core->window; + } + break; + default: + break; + } +} + +/* bindable */ +static void handleButtonPress(XEvent * event) +{ + WObjDescriptor *desc; + WScreen *scr; + +#ifdef DEBUG + printf("got button press\n"); +#endif + scr = wScreenForRootWindow(event->xbutton.root); + +#ifdef BALLOON_TEXT + wBalloonHide(scr); +#endif + +#ifndef LITE + if (event->xbutton.window == scr->root_win) { + if (event->xbutton.button == Button1 && wPreferences.mouse_button1 != WA_NONE) { + executeButtonAction(scr, event, wPreferences.mouse_button1); + } else if (event->xbutton.button == Button2 && wPreferences.mouse_button2 != WA_NONE) { + executeButtonAction(scr, event, wPreferences.mouse_button2); + } else if (event->xbutton.button == Button3 && wPreferences.mouse_button3 != WA_NONE) { + executeButtonAction(scr, event, wPreferences.mouse_button3); + } else if (event->xbutton.button == Button4 && wPreferences.mouse_wheel != WA_NONE) { + wWorkspaceRelativeChange(scr, 1); + } else if (event->xbutton.button == Button5 && wPreferences.mouse_wheel != WA_NONE) { + wWorkspaceRelativeChange(scr, -1); + } + } +#endif /* !LITE */ + + desc = NULL; + if (XFindContext(dpy, event->xbutton.subwindow, wWinContext, (XPointer *) & desc) == XCNOENT) { + if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) == XCNOENT) { + return; + } + } + + if (desc->parent_type == WCLASS_WINDOW) { + XSync(dpy, 0); + + if (event->xbutton.state & MOD_MASK) { + XAllowEvents(dpy, AsyncPointer, CurrentTime); + } + + /* if (wPreferences.focus_mode == WKF_CLICK) { */ + if (wPreferences.ignore_focus_click) { + XAllowEvents(dpy, AsyncPointer, CurrentTime); + } + XAllowEvents(dpy, ReplayPointer, CurrentTime); + /* } */ + XSync(dpy, 0); + } else if (desc->parent_type == WCLASS_APPICON + || desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) { + if (event->xbutton.state & MOD_MASK) { + XSync(dpy, 0); + XAllowEvents(dpy, AsyncPointer, CurrentTime); + XSync(dpy, 0); + } + } + + if (desc->handle_mousedown != NULL) { + (*desc->handle_mousedown) (desc, event); + } + + /* save double-click information */ + if (scr->flags.next_click_is_not_double) { + scr->flags.next_click_is_not_double = 0; + } else { + scr->last_click_time = event->xbutton.time; + scr->last_click_button = event->xbutton.button; + scr->last_click_window = event->xbutton.window; + } +} + +static void handleMapNotify(XEvent * event) +{ + WWindow *wwin; +#ifdef DEBUG + printf("got map\n"); +#endif + wwin = wWindowFor(event->xmap.event); + if (wwin && wwin->client_win == event->xmap.event) { + if (wwin->flags.miniaturized) { + wDeiconifyWindow(wwin); + } else { + XGrabServer(dpy); + wWindowMap(wwin); + wClientSetState(wwin, NormalState, None); + XUngrabServer(dpy); + } + } +} + +static void handleUnmapNotify(XEvent * event) +{ + WWindow *wwin; + XEvent ev; + Bool withdraw = False; +#ifdef DEBUG + printf("got unmap\n"); +#endif + /* only process windows with StructureNotify selected + * (ignore SubstructureNotify) */ + wwin = wWindowFor(event->xunmap.window); + if (!wwin) + return; + + /* whether the event is a Withdrawal request */ + if (event->xunmap.event == wwin->screen_ptr->root_win && event->xunmap.send_event) + withdraw = True; + + if (wwin->client_win != event->xunmap.event && !withdraw) + return; + + if (!wwin->flags.mapped && !withdraw + && wwin->frame->workspace == wwin->screen_ptr->current_workspace + && !wwin->flags.miniaturized && !wwin->flags.hidden) + return; + + XGrabServer(dpy); + XUnmapWindow(dpy, wwin->frame->core->window); + wwin->flags.mapped = 0; + XSync(dpy, 0); + /* check if the window was destroyed */ + if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify, &ev)) { + DispatchEvent(&ev); + } else { + Bool reparented = False; + + if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev)) + reparented = True; + + /* withdraw window */ + wwin->flags.mapped = 0; + if (!reparented) + wClientSetState(wwin, WithdrawnState, None); + + /* if the window was reparented, do not reparent it back to the + * root window */ + wUnmanageWindow(wwin, !reparented, False); + } + XUngrabServer(dpy); +} + +static void handleConfigureRequest(XEvent * event) +{ + WWindow *wwin; +#ifdef DEBUG + printf("got configure request\n"); +#endif + if (!(wwin = wWindowFor(event->xconfigurerequest.window))) { + /* + * Configure request for unmapped window + */ + wClientConfigure(NULL, &(event->xconfigurerequest)); + } else { + wClientConfigure(wwin, &(event->xconfigurerequest)); + } +} + +static void handlePropertyNotify(XEvent * event) +{ + WWindow *wwin; + WApplication *wapp; + Window jr; + int ji; + unsigned int ju; + WScreen *scr; +#ifdef DEBUG + printf("got property notify\n"); +#endif + + wwin = wWindowFor(event->xproperty.window); + if (wwin) { + if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, &ju, &ju, &ju, &ju)) { + return; + } + wClientCheckProperty(wwin, &event->xproperty); + } + wapp = wApplicationOf(event->xproperty.window); + if (wapp) { + wClientCheckProperty(wapp->main_window_desc, &event->xproperty); + } + + scr = wScreenForWindow(event->xproperty.window); +} + +static void handleClientMessage(XEvent * event) +{ + WWindow *wwin; + WObjDescriptor *desc; +#ifdef DEBUG + printf("got client message\n"); +#endif + /* handle transition from Normal to Iconic state */ + if (event->xclient.message_type == _XA_WM_CHANGE_STATE + && event->xclient.format == 32 && event->xclient.data.l[0] == IconicState) { + + wwin = wWindowFor(event->xclient.window); + if (!wwin) + return; + if (!wwin->flags.miniaturized) + wIconifyWindow(wwin); + } else if (event->xclient.message_type == _XA_WM_COLORMAP_NOTIFY && event->xclient.format == 32) { + WScreen *scr = wScreenSearchForRootWindow(event->xclient.window); + + if (!scr) + return; + + if (event->xclient.data.l[1] == 1) { /* starting */ + wColormapAllowClientInstallation(scr, True); + } else { /* stopping */ + wColormapAllowClientInstallation(scr, False); + } + } else if (event->xclient.message_type == _XA_WINDOWMAKER_COMMAND) { + + wDefaultsCheckDomains(); + + } else if (event->xclient.message_type == _XA_WINDOWMAKER_WM_FUNCTION) { + WApplication *wapp; + int done = 0; + wapp = wApplicationOf(event->xclient.window); + if (wapp) { + switch (event->xclient.data.l[0]) { + case WMFHideOtherApplications: + wHideOtherApplications(wapp->main_window_desc); + done = 1; + break; + + case WMFHideApplication: + wHideApplication(wapp); + done = 1; + break; + } + } + if (!done) { + wwin = wWindowFor(event->xclient.window); + if (wwin) { + switch (event->xclient.data.l[0]) { + case WMFHideOtherApplications: + wHideOtherApplications(wwin); + break; + + case WMFHideApplication: + wHideApplication(wApplicationOf(wwin->main_window)); + break; + } + } + } + } else if (event->xclient.message_type == _XA_GNUSTEP_WM_ATTR) { + wwin = wWindowFor(event->xclient.window); + if (!wwin) + return; + switch (event->xclient.data.l[0]) { + case GSWindowLevelAttr: + { + int level = (int)event->xclient.data.l[1]; + + if (WINDOW_LEVEL(wwin) != level) { + ChangeStackingLevel(wwin->frame->core, level); + } + } + break; + } + } else if (event->xclient.message_type == _XA_GNUSTEP_TITLEBAR_STATE) { + wwin = wWindowFor(event->xclient.window); + if (!wwin) + return; + switch (event->xclient.data.l[0]) { + case WMTitleBarNormal: + wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED); + break; + case WMTitleBarMain: + wFrameWindowChangeState(wwin->frame, WS_PFOCUSED); + break; + case WMTitleBarKey: + wFrameWindowChangeState(wwin->frame, WS_FOCUSED); + break; + } +#ifdef NETWM_HINTS + } else if (wNETWMProcessClientMessage(&event->xclient)) { + /* do nothing */ +#endif +#ifdef XDND + } else if (wXDNDProcessClientMessage(&event->xclient)) { + /* do nothing */ +#endif /* XDND */ + } else { + /* + * Non-standard thing, but needed by OffiX DND. + * For when the icon frame gets a ClientMessage + * that should have gone to the icon_window. + */ + if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) != XCNOENT) { + struct WIcon *icon = NULL; + + if (desc->parent_type == WCLASS_MINIWINDOW) { + icon = (WIcon *) desc->parent; + } else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) { + icon = ((WAppIcon *) desc->parent)->icon; + } + if (icon && (wwin = icon->owner)) { + if (wwin->client_win != event->xclient.window) { + event->xclient.window = wwin->client_win; + XSendEvent(dpy, wwin->client_win, False, NoEventMask, event); + } + } + } + } +} + +static void raiseWindow(WScreen * scr) +{ + WWindow *wwin; + + scr->autoRaiseTimer = NULL; + + wwin = wWindowFor(scr->autoRaiseWindow); + if (!wwin) + return; + + if (!wwin->flags.destroyed && wwin->flags.focused) { + wRaiseFrame(wwin->frame->core); + /* this is needed or a race condition will occur */ + XSync(dpy, False); + } +} + +static void handleEnterNotify(XEvent * event) +{ + WWindow *wwin; + WObjDescriptor *desc = NULL; +#ifdef VIRTUAL_DESKTOP + void (*vdHandler) (XEvent * event); +#endif + XEvent ev; + WScreen *scr = wScreenForRootWindow(event->xcrossing.root); +#ifdef DEBUG + printf("got enter notify\n"); +#endif + +#ifdef VIRTUAL_DESKTOP + if (XFindContext(dpy, event->xcrossing.window, wVEdgeContext, (XPointer *) & vdHandler) != XCNOENT) { + (*vdHandler) (event); + } +#endif + + if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, &ev)) { + /* already left the window... */ + saveTimestamp(&ev); + if (ev.xcrossing.mode == event->xcrossing.mode && ev.xcrossing.detail == event->xcrossing.detail) { + return; + } + } + + if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) { + if (desc->handle_enternotify) + (*desc->handle_enternotify) (desc, event); + } + + /* enter to window */ + wwin = wWindowFor(event->xcrossing.window); + if (!wwin) { + if (wPreferences.colormap_mode == WCM_POINTER) { + wColormapInstallForWindow(scr, NULL); + } + if (scr->autoRaiseTimer && event->xcrossing.root == event->xcrossing.window) { + WMDeleteTimerHandler(scr->autoRaiseTimer); + scr->autoRaiseTimer = NULL; + } + } else { + /* set auto raise timer even if in focus-follows-mouse mode + * and the event is for the frame window, even if the window + * has focus already. useful if you move the pointer from a focused + * window to the root window and back pretty fast + * + * set focus if in focus-follows-mouse mode and the event + * is for the frame window and window doesn't have focus yet */ + if (wPreferences.focus_mode == WKF_SLOPPY + && wwin->frame->core->window == event->xcrossing.window && !scr->flags.doing_alt_tab) { + + if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable)) + wSetFocusTo(scr, wwin); + + if (scr->autoRaiseTimer) + WMDeleteTimerHandler(scr->autoRaiseTimer); + scr->autoRaiseTimer = NULL; + + if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) { + scr->autoRaiseWindow = wwin->frame->core->window; + scr->autoRaiseTimer + = WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr); + } + } + /* Install colormap for window, if the colormap installation mode + * is colormap_follows_mouse */ + if (wPreferences.colormap_mode == WCM_POINTER) { + if (wwin->client_win == event->xcrossing.window) + wColormapInstallForWindow(scr, wwin); + else + wColormapInstallForWindow(scr, NULL); + } + } + + /* a little kluge to hide the clip balloon */ + if (!wPreferences.flags.noclip && scr->flags.clip_balloon_mapped) { + if (!desc) { + XUnmapWindow(dpy, scr->clip_balloon); + scr->flags.clip_balloon_mapped = 0; + } else { + if (desc->parent_type != WCLASS_DOCK_ICON || scr->clip_icon != desc->parent) { + XUnmapWindow(dpy, scr->clip_balloon); + scr->flags.clip_balloon_mapped = 0; + } + } + } + + if (event->xcrossing.window == event->xcrossing.root + && event->xcrossing.detail == NotifyNormal + && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) { + + wSetFocusTo(scr, scr->focused_window); + } +#ifdef BALLOON_TEXT + wBalloonEnteredObject(scr, desc); +#endif +} + +static void handleLeaveNotify(XEvent * event) +{ + WObjDescriptor *desc = NULL; + + if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) { + if (desc->handle_leavenotify) + (*desc->handle_leavenotify) (desc, event); + } +} + +#ifdef SHAPE +static void handleShapeNotify(XEvent * event) +{ + XShapeEvent *shev = (XShapeEvent *) event; + WWindow *wwin; + XEvent ev; +#ifdef DEBUG + printf("got shape notify\n"); +#endif + while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev)) { + XShapeEvent *sev = (XShapeEvent *) & ev; + + if (sev->kind == ShapeBounding) { + if (sev->shaped == shev->shaped) { + *shev = *sev; + } else { + XPutBackEvent(dpy, &ev); + break; + } + } + } + + wwin = wWindowFor(shev->window); + if (!wwin || shev->kind != ShapeBounding) + return; + + if (!shev->shaped && wwin->flags.shaped) { + + wwin->flags.shaped = 0; + wWindowClearShape(wwin); + + } else if (shev->shaped) { + + wwin->flags.shaped = 1; + wWindowSetShape(wwin); + } +} +#endif /* SHAPE */ + +#ifdef KEEP_XKB_LOCK_STATUS +/* please help ]d if you know what to do */ +handleXkbIndicatorStateNotify(XEvent * event) +{ + WWindow *wwin; + WScreen *scr; + XkbStateRec staterec; + int i; + + for (i = 0; i < wScreenCount; i++) { + scr = wScreenWithNumber(i); + wwin = scr->focused_window; + if (wwin && wwin->flags.focused) { + XkbGetState(dpy, XkbUseCoreKbd, &staterec); + if (wwin->frame->languagemode != staterec.group) { + wwin->frame->last_languagemode = wwin->frame->languagemode; + wwin->frame->languagemode = staterec.group; + } +#ifdef XKB_BUTTON_HINT + if (wwin->frame->titlebar) { + wFrameWindowPaint(wwin->frame); + } +#endif + } + } +} +#endif /*KEEP_XKB_LOCK_STATUS */ + +static void handleColormapNotify(XEvent * event) +{ + WWindow *wwin; + WScreen *scr; + Bool reinstall = False; + + wwin = wWindowFor(event->xcolormap.window); + if (!wwin) + return; + + scr = wwin->screen_ptr; + + do { + if (wwin) { + if (event->xcolormap.new) { + XWindowAttributes attr; + + XGetWindowAttributes(dpy, wwin->client_win, &attr); + + if (wwin == scr->cmap_window && wwin->cmap_window_no == 0) + scr->current_colormap = attr.colormap; + + reinstall = True; + } else if (event->xcolormap.state == ColormapUninstalled && + scr->current_colormap == event->xcolormap.colormap) { + + /* some bastard app (like XV) removed our colormap */ + /* + * can't enforce or things like xscreensaver wont work + * reinstall = True; + */ + } else if (event->xcolormap.state == ColormapInstalled && + scr->current_colormap == event->xcolormap.colormap) { + + /* someone has put our colormap back */ + reinstall = False; + } + } + } while (XCheckTypedEvent(dpy, ColormapNotify, event) + && ((wwin = wWindowFor(event->xcolormap.window)) || 1)); + + if (reinstall && scr->current_colormap != None) { + if (!scr->flags.colormap_stuff_blocked) + XInstallColormap(dpy, scr->current_colormap); + } +} + +static void handleFocusIn(XEvent * event) +{ + WWindow *wwin; + + /* + * For applications that like stealing the focus. + */ + while (XCheckTypedEvent(dpy, FocusIn, event)) ; + saveTimestamp(event); + if (event->xfocus.mode == NotifyUngrab + || event->xfocus.mode == NotifyGrab || event->xfocus.detail > NotifyNonlinearVirtual) { + return; + } + + wwin = wWindowFor(event->xfocus.window); + if (wwin && !wwin->flags.focused) { + if (wwin->flags.mapped) + wSetFocusTo(wwin->screen_ptr, wwin); + else + wSetFocusTo(wwin->screen_ptr, NULL); + } else if (!wwin) { + WScreen *scr = wScreenForWindow(event->xfocus.window); + if (scr) + wSetFocusTo(scr, NULL); + } +} + +static WWindow *windowUnderPointer(WScreen * scr) +{ + unsigned int mask; + int foo; + Window bar, win; + + if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) + return wWindowFor(win); + return NULL; +} + +static int CheckFullScreenWindowFocused(WScreen * scr) +{ + if (scr->focused_window && scr->focused_window->flags.fullscreen) + return 1; + else + return 0; +} + +static void handleKeyPress(XEvent * event) +{ + WScreen *scr = wScreenForRootWindow(event->xkey.root); + WWindow *wwin = scr->focused_window; + short i, widx; + int modifiers; + int command = -1; +#ifdef KEEP_XKB_LOCK_STATUS + XkbStateRec staterec; +#endif /*KEEP_XKB_LOCK_STATUS */ + + /* ignore CapsLock */ + modifiers = event->xkey.state & ValidModMask; + + for (i = 0; i < WKBD_LAST; i++) { + if (wKeyBindings[i].keycode == 0) + continue; + + if (wKeyBindings[i].keycode == event->xkey.keycode && ( /*wKeyBindings[i].modifier==0 + || */ wKeyBindings[i].modifier == + modifiers)) { + command = i; + break; + } + } + + if (command < 0) { +#ifdef LITE + { +#if 0 + } +#endif +#else + if (!wRootMenuPerformShortcut(event)) { +#endif + static int dontLoop = 0; + + if (dontLoop > 10) { + wwarning("problem with key event processing code"); + return; + } + dontLoop++; + /* if the focused window is an internal window, try redispatching + * the event to the managed window, as it can be a WINGs window */ + if (wwin && wwin->flags.internal_window && wwin->client_leader != None) { + /* client_leader contains the WINGs toplevel */ + event->xany.window = wwin->client_leader; + WMHandleEvent(event); + } + dontLoop--; + } + return; + } +#define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded)) +#define ISFOCUSED(w) ((w) && (w)->flags.focused) + + switch (command) { +#ifndef LITE + case WKBD_ROOTMENU: + /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True); */ + if (!CheckFullScreenWindowFocused(scr)) { + WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr)); + OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2, + True); + } + break; + case WKBD_WINDOWLIST: + if (!CheckFullScreenWindowFocused(scr)) { + WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr)); + OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2, + True); + } + break; +#endif /* !LITE */ + case WKBD_WINDOWMENU: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) + OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True); + break; + case WKBD_MINIATURIZE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) + && !WFLAGP(wwin, no_miniaturizable)) { + CloseWindowMenu(scr); + + if (wwin->protocols.MINIATURIZE_WINDOW) + wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, event->xbutton.time); + else { + wIconifyWindow(wwin); + } + } + break; + case WKBD_HIDE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { + WApplication *wapp = wApplicationOf(wwin->main_window); + CloseWindowMenu(scr); + + if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) { + wHideApplication(wapp); + } + } + break; + case WKBD_HIDE_OTHERS: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { + CloseWindowMenu(scr); + + wHideOtherApplications(wwin); + } + break; + case WKBD_MAXIMIZE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { + int newdir = (MAX_VERTICAL | MAX_HORIZONTAL); + + CloseWindowMenu(scr); + + if (wwin->flags.maximized == newdir) { + wUnmaximizeWindow(wwin); + } else { + wMaximizeWindow(wwin, newdir | MAX_KEYBOARD); + } + } + break; + case WKBD_VMAXIMIZE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { + int newdir = (MAX_VERTICAL ^ wwin->flags.maximized); + + CloseWindowMenu(scr); + + if (newdir) { + wMaximizeWindow(wwin, newdir | MAX_KEYBOARD); + } else { + wUnmaximizeWindow(wwin); + } + } + break; + case WKBD_HMAXIMIZE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) { + int newdir = (MAX_HORIZONTAL ^ wwin->flags.maximized); + + CloseWindowMenu(scr); + + if (newdir) { + wMaximizeWindow(wwin, newdir | MAX_KEYBOARD); + } else { + wUnmaximizeWindow(wwin); + } + } + break; + case WKBD_RAISE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { + CloseWindowMenu(scr); + + wRaiseFrame(wwin->frame->core); + } + break; + case WKBD_LOWER: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { + CloseWindowMenu(scr); + + wLowerFrame(wwin->frame->core); + } + break; + case WKBD_RAISELOWER: + /* raise or lower the window under the pointer, not the + * focused one + */ + wwin = windowUnderPointer(scr); + if (wwin) + wRaiseLowerFrame(wwin->frame->core); + break; + case WKBD_SHADE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) { + if (wwin->flags.shaded) + wUnshadeWindow(wwin); + else + wShadeWindow(wwin); + } + break; + case WKBD_MOVERESIZE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) { + CloseWindowMenu(scr); + + wKeyboardMoveResizeWindow(wwin); + } + break; + case WKBD_CLOSE: + if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) { + CloseWindowMenu(scr); + if (wwin->protocols.DELETE_WINDOW) + wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, event->xkey.time); + } + break; + case WKBD_SELECT: + if (ISMAPPED(wwin) && ISFOCUSED(wwin)) { + wSelectWindow(wwin, !wwin->flags.selected); + } + break; + case WKBD_FOCUSNEXT: + StartWindozeCycle(wwin, event, True); + break; + + case WKBD_FOCUSPREV: + StartWindozeCycle(wwin, event, False); + break; + + case WKBD_WORKSPACE1...WKBD_WORKSPACE10: + widx = command - WKBD_WORKSPACE1; + i = (scr->current_workspace / 10) * 10 + widx; + if (wPreferences.ws_advance || i < scr->workspace_count) + wWorkspaceChange(scr, i); + break; + + case WKBD_NEXTWORKSPACE: + wWorkspaceRelativeChange(scr, 1); + break; + case WKBD_PREVWORKSPACE: + wWorkspaceRelativeChange(scr, -1); + break; + case WKBD_WINDOW1: + case WKBD_WINDOW2: + case WKBD_WINDOW3: + case WKBD_WINDOW4: + case WKBD_WINDOW5: + case WKBD_WINDOW6: + case WKBD_WINDOW7: + case WKBD_WINDOW8: + case WKBD_WINDOW9: + case WKBD_WINDOW10: + + widx = command - WKBD_WINDOW1; + + if (scr->shortcutWindows[widx]) { + WMArray *list = scr->shortcutWindows[widx]; + int cw; + int count = WMGetArrayItemCount(list); + WWindow *twin; + WMArrayIterator iter; + WWindow *wwin; + + wUnselectWindows(scr); + cw = scr->current_workspace; + + WM_ETARETI_ARRAY(list, wwin, iter) { + if (count > 1) + wWindowChangeWorkspace(wwin, cw); + + wMakeWindowVisible(wwin); + + if (count > 1) + wSelectWindow(wwin, True); + } + + /* rotate the order of windows, to create a cycling effect */ + twin = WMGetFromArray(list, 0); + WMDeleteFromArray(list, 0); + WMAddToArray(list, twin); + + } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) { + if (scr->shortcutWindows[widx]) { + WMFreeArray(scr->shortcutWindows[widx]); + scr->shortcutWindows[widx] = NULL; + } + + if (wwin->flags.selected && scr->selected_windows) { + scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows); + /*WMRemoveFromArray(scr->shortcutWindows[index], wwin); + WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */ + } else { + scr->shortcutWindows[widx] = WMCreateArray(4); + WMAddToArray(scr->shortcutWindows[widx], wwin); + } + + wSelectWindow(wwin, !wwin->flags.selected); + XFlush(dpy); + wusleep(3000); + wSelectWindow(wwin, !wwin->flags.selected); + XFlush(dpy); + + } else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) { + + if (wwin->flags.selected && scr->selected_windows) { + if (scr->shortcutWindows[widx]) { + WMFreeArray(scr->shortcutWindows[widx]); + } + scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows); + } + } + + break; + + case WKBD_SWITCH_SCREEN: + if (wScreenCount > 1) { + WScreen *scr2; + int i; + + /* find index of this screen */ + for (i = 0; i < wScreenCount; i++) { + if (wScreenWithNumber(i) == scr) + break; + } + i++; + if (i >= wScreenCount) { + i = 0; + } + scr2 = wScreenWithNumber(i); + + if (scr2) { + XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0, + scr2->scr_width / 2, scr2->scr_height / 2); + } + } + break; + + case WKBD_NEXTWSLAYER: + case WKBD_PREVWSLAYER: + { + int row, column; + + row = scr->current_workspace / 10; + column = scr->current_workspace % 10; + + if (command == WKBD_NEXTWSLAYER) { + if ((row + 1) * 10 < scr->workspace_count) + wWorkspaceChange(scr, column + (row + 1) * 10); + } else { + if (row > 0) + wWorkspaceChange(scr, column + (row - 1) * 10); + } + } + break; + case WKBD_CLIPLOWER: + if (!wPreferences.flags.noclip) + wDockLower(scr->workspaces[scr->current_workspace]->clip); + break; + case WKBD_CLIPRAISE: + if (!wPreferences.flags.noclip) + wDockRaise(scr->workspaces[scr->current_workspace]->clip); + break; + case WKBD_CLIPRAISELOWER: + if (!wPreferences.flags.noclip) + wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip); + break; +#ifdef KEEP_XKB_LOCK_STATUS + case WKBD_TOGGLE: + if (wPreferences.modelock) { + /*toggle */ + wwin = scr->focused_window; + + if (wwin && wwin->flags.mapped + && wwin->frame->workspace == wwin->screen_ptr->current_workspace + && !wwin->flags.miniaturized && !wwin->flags.hidden) { + XkbGetState(dpy, XkbUseCoreKbd, &staterec); + + wwin->frame->languagemode = wwin->frame->last_languagemode; + wwin->frame->last_languagemode = staterec.group; + XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode); + + } + } + break; +#endif /* KEEP_XKB_LOCK_STATUS */ +#ifdef VIRTUAL_DESKTOP + case WKBD_VDESK_LEFT: + wWorkspaceKeyboardMoveDesktop(scr, VEC_LEFT); + break; + + case WKBD_VDESK_RIGHT: + wWorkspaceKeyboardMoveDesktop(scr, VEC_RIGHT); + break; + + case WKBD_VDESK_UP: + wWorkspaceKeyboardMoveDesktop(scr, VEC_UP); + break; + + case WKBD_VDESK_DOWN: + wWorkspaceKeyboardMoveDesktop(scr, VEC_DOWN); + break; +#endif + + } +} + +static void handleMotionNotify(XEvent * event) +{ + WScreen *scr = wScreenForRootWindow(event->xmotion.root); + + if (wPreferences.scrollable_menus) { + WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root); + WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p)); + + if (scr->flags.jump_back_pending || + p.x <= (rect.pos.x + 1) || + p.x >= (rect.pos.x + rect.size.width - 2) || + p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) { + WMenu *menu; +#ifdef DEBUG + printf("pointer at screen edge\n"); +#endif + menu = wMenuUnderPointer(scr); + if (menu != NULL) + wMenuScroll(menu, event); + } + } +} + +static void handleVisibilityNotify(XEvent * event) +{ + WWindow *wwin; + + wwin = wWindowFor(event->xvisibility.window); + if (!wwin) + return; + wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured); +}