/* event.c- event loop and handling * * Window Maker window manager * * Copyright (c) 1997, 1998 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 "wconfig.h" #include #include #include #include #include #include #ifdef SHAPE #include #endif #ifdef XDE_DND #include "xde.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" #ifdef GNOME_STUFF # include "gnome.h" #endif #ifdef KWM_HINTS # include "kwm.h" #endif /******** Global Variables **********/ extern XContext wWinContext; 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_MINIATURIZE_WINDOW; extern Atom _XA_WINDOWMAKER_WM_FUNCTION; #ifdef OFFIX_DND extern Atom _XA_DND_PROTOCOL; #endif #ifdef SHAPE extern Bool wShapeSupported; extern int wShapeEventBase; #endif /* special flags */ extern char WDelayedActionSet; /************ Local stuff ***********/ static void saveTimestamp(XEvent *event); static void handleColormapNotify(); static void handleMapNotify(), handleUnmapNotify(); static void handleButtonPress(), 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; struct DeathHandler *next; void *client_data; } DeathHandler; static DeathHandler *deathHandler=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; handler->next = deathHandler; deathHandler = handler; return handler; } void wDeleteDeathHandler(WMagicNumber id) { DeathHandler *tmp, *handler=(DeathHandler*)id; if (!handler || !deathHandler) return; tmp = deathHandler; if (tmp==handler) { deathHandler = handler->next; free(handler); } else { while (tmp->next) { if (tmp->next==handler) { tmp->next=handler->next; free(handler); break; } tmp = tmp->next; } } } void DispatchEvent(XEvent *event) { if (deathHandler) 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); } /* 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; } } /* *---------------------------------------------------------------------- * EventLoop- * Processes X and internal events indefinitely. * * Returns: * Never returns * * Side effects: * The LastTimestamp global variable is updated. *---------------------------------------------------------------------- */ void EventLoop() { XEvent event; for(;;) { WMNextEvent(dpy, &event); WMHandleEvent(&event); } } 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--; tmp = deathHandler; while (tmp) { DeathHandler *t; t = tmp->next; if (tmp->pid == deadProcesses[deadProcessPtr].pid) { (*tmp->callback)(tmp->pid, deadProcesses[deadProcessPtr].exit_status, tmp->client_data); wDeleteDeathHandler(tmp); } tmp = t; } } } static void saveTimestamp(XEvent *event) { LastTimestamp = CurrentTime; 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; break; } } static void handleExtensions(XEvent *event) { #ifdef SHAPE if (wShapeSupported && event->type == (wShapeEventBase+ShapeNotify)) { handleShapeNotify(event); } #endif } 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 */ #ifndef REDUCE_APPICONS /* This severely breaks REDUCE_APPICONS. last_workspace is a neat * concept but it needs to be reworked to handle REDUCE_APPICONS -cls */ if (wapp) { wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace); } #endif 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.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; #ifdef DEBUG puts("got destroy notify"); #endif wwin = wWindowFor(window); if (wwin) { wUnmanageWindow(wwin, False, True); } 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); } #ifdef KWM_HINTS wKWMCheckDestroy(&event->xdestroywindow); #endif } static void handleExpose(XEvent *event) { WObjDescriptor *desc; XEvent ev; #ifdef DEBUG puts("got expose"); #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); } } /* bindable */ static void handleButtonPress(XEvent *event) { WObjDescriptor *desc; WScreen *scr; #ifdef DEBUG puts("got button press"); #endif scr = wScreenForRootWindow(event->xbutton.root); #ifdef BALLOON_TEXT wBalloonHide(scr); #endif #ifndef LITE if (event->xbutton.window==scr->root_win) { #ifdef GNOME_STUFF if (wGNOMEProxyizeButtonEvent(scr, event)) return; #endif if (event->xbutton.button==wPreferences.menu_button) { 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; } } else if (event->xbutton.button==wPreferences.windowl_button) { 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; } } else if (event->xbutton.button==wPreferences.select_button) { wUnselectWindows(scr); wSelectWindows(scr, event); } #ifdef MOUSE_WS_SWITCH else if (event->xbutton.button==Button5) { wWorkspaceRelativeChange(scr, -1); } else if (event->xbutton.button==Button4) { wWorkspaceRelativeChange(scr, 1); } #endif /* MOUSE_WS_SWITCH */ } #endif /* !LITE */ if (XFindContext(dpy, event->xbutton.subwindow, wWinContext, (XPointer *)&desc)==XCNOENT) { if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *)&desc)==XCNOENT) { return; } } if (desc->handle_mousedown!=NULL) { (*desc->handle_mousedown)(desc, event); } 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); } } /* 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 puts("got map"); #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 puts("got unmap"); #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 puts("got configure request"); #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 puts("got property notify"); #endif if ((wwin=wWindowFor(event->xproperty.window))) { 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); if (scr && scr->root_win == event->xproperty.window) { #ifdef KWM_HINTS wKWMCheckRootHintChange(scr, &event->xproperty); #endif } } static void handleClientMessage(XEvent *event) { WWindow *wwin; WObjDescriptor *desc; #ifdef DEBUG puts("got client message"); #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_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; } } } #ifdef GNOME_STUFF } else if (wGNOMEProcessClientMessage(&event->xclient)) { /* do nothing */ #endif /* GNOME_STUFF */ #ifdef KWM_HINTS } else if (wKWMProcessClientMessage(&event->xclient)) { /* do nothing */ #endif /* KWM_HINTS */ #ifdef XDE_DND } else if (wXDEProcessClientMessage(&event->xclient)) { /* do nothing */ #endif /* XDE_DND */ #ifdef OFFIX_DND } else if (event->xclient.message_type==_XA_DND_PROTOCOL) { WScreen *scr = wScreenForWindow(event->xclient.window); if (scr && wDockReceiveDNDDrop(scr,event)) goto redirect_message; #endif /* OFFIX_DND */ } else { #ifdef OFFIX_DND redirect_message: #endif /* * 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) { 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; XEvent ev; WScreen *scr = wScreenForRootWindow(event->xcrossing.root); #ifdef DEBUG puts("got enter notify"); #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.focus_mode==WKF_POINTER && event->xcrossing.window==event->xcrossing.root) { wSetFocusTo(scr, NULL); } if (wPreferences.colormap_mode==WKF_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_POINTER || wPreferences.focus_mode==WKF_SLOPPY) && wwin->frame->core->window==event->xcrossing.window && !scr->flags.doing_alt_tab) { if (!wwin->flags.focused) 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==WKF_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); } if (event->xcrossing.window == event->xcrossing.root && event->xcrossing.mode == NotifyNormal && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) { WScreen *scr = wScreenForRootWindow(event->xcrossing.root); wSetFocusTo(scr, NULL); } } #ifdef SHAPE static void handleShapeNotify(XEvent *event) { XShapeEvent *shev = (XShapeEvent*)event; WWindow *wwin; XEvent ev; #ifdef DEBUG puts("got shape notify"); #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 */ 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; } #ifdef WEENDOZE_CYCLE static WWindow* nextToFocusAfter(WWindow *wwin) { WWindow *tmp = wwin->next; while (tmp) { if (wWindowCanReceiveFocus(tmp) && !WFLAGP(tmp, skip_window_list)) { return tmp; } tmp = tmp->next; } tmp = wwin; /* start over from the beginning of the list */ while (tmp->prev) tmp = tmp->prev; while (tmp && tmp != wwin) { if (wWindowCanReceiveFocus(tmp) && !WFLAGP(tmp, skip_window_list)) { return tmp; } tmp = tmp->next; } return wwin; } static WWindow* nextToFocusBefore(WWindow *wwin) { WWindow *tmp = wwin->prev; while (tmp) { if (wWindowCanReceiveFocus(tmp) && !WFLAGP(tmp, skip_window_list)) { return tmp; } tmp = tmp->prev; } /* start over from the beginning of the list */ tmp = wwin; while (tmp->next) tmp = tmp->next; while (tmp && tmp != wwin) { if (wWindowCanReceiveFocus(tmp) && !WFLAGP(tmp, skip_window_list)) { return tmp; } tmp = tmp->prev; } return wwin; } static void doWindozeCycle(WWindow *wwin, XEvent *event, Bool next) { WScreen *scr = wScreenForRootWindow(event->xkey.root); Bool done = False; WWindow *newFocused; WWindow *oldFocused; int modifiers; XModifierKeymap *keymap; if (!wwin) return; puts("IN"); keymap = XGetModifierMapping(dpy); XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime); if (next) { newFocused = nextToFocusAfter(wwin); } else { newFocused = nextToFocusBefore(wwin); } scr->flags.doing_alt_tab = 1; wWindowFocus(newFocused, scr->focused_window); oldFocused = newFocused; OpenSwitchMenu(scr, scr->scr_width/2, scr->scr_height/2, False); while (!done) { XEvent ev; WMMaskEvent(dpy,KeyPressMask|KeyReleaseMask|ExposureMask, &ev); /* WMNextEvent(dpy, &ev);*/ if (ev.type != KeyRelease && ev.type != KeyPress) { WMHandleEvent(&ev); continue; } puts("EV"); /* ignore CapsLock */ modifiers = ev.xkey.state & ValidModMask; if (ev.type == KeyPress && wKeyBindings[WKBD_FOCUSNEXT].keycode == ev.xkey.keycode && wKeyBindings[WKBD_FOCUSNEXT].modifier == modifiers) { UpdateSwitchMenu(scr, newFocused, ACTION_CHANGE_STATE); newFocused = nextToFocusAfter(newFocused); wWindowFocus(newFocused, oldFocused); oldFocused = newFocused; UpdateSwitchMenu(scr, newFocused, ACTION_CHANGE_STATE); } else if (ev.type == KeyPress && wKeyBindings[WKBD_FOCUSPREV].keycode == ev.xkey.keycode && wKeyBindings[WKBD_FOCUSPREV].modifier == modifiers) { UpdateSwitchMenu(scr, newFocused, ACTION_CHANGE_STATE); newFocused = nextToFocusBefore(newFocused); wWindowFocus(newFocused, oldFocused); oldFocused = newFocused; UpdateSwitchMenu(scr, newFocused, ACTION_CHANGE_STATE); } if (ev.type == KeyRelease) { int i; for (i = 0; i <= 8 * keymap->max_keypermod; i++) { if (keymap->modifiermap[i] == ev.xkey.keycode && wKeyBindings[WKBD_FOCUSNEXT].modifier & 1<<(i/keymap->max_keypermod)) { done = True; break; } } } } puts("OUT"); XFree(keymap); XUngrabKeyboard(dpy, CurrentTime); wSetFocusTo(scr, newFocused); scr->flags.doing_alt_tab = 0; OpenSwitchMenu(scr, scr->scr_width/2, scr->scr_height/2, False); } #endif /* WEENDOZE_CYCLE */ static void handleKeyPress(XEvent *event) { WScreen *scr = wScreenForRootWindow(event->xkey.root); WWindow *wwin = scr->focused_window; int i; 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); break; case WKBD_WINDOWLIST: OpenSwitchMenu(scr, event->xkey.x_root, event->xkey.y_root, 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_MAXIMIZE: if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_resizable)) { CloseWindowMenu(scr); if (wwin->flags.maximized) { wUnmaximizeWindow(wwin); } else { wMaximizeWindow(wwin, MAX_VERTICAL|MAX_HORIZONTAL); } } break; case WKBD_VMAXIMIZE: if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_resizable)) { CloseWindowMenu(scr); if (wwin->flags.maximized) { wUnmaximizeWindow(wwin); } else { wMaximizeWindow(wwin, MAX_VERTICAL); } } 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)) { 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: #ifdef WEENDOZE_CYCLE if (wPreferences.windoze_cycling) { doWindozeCycle(wwin, event, True); } else #endif /* WEENDOZE_CYCLE */ { wwin = NextFocusWindow(scr); if (wwin != NULL) { wSetFocusTo(scr, wwin); if (wPreferences.circ_raise) wRaiseFrame(wwin->frame->core); } } break; case WKBD_FOCUSPREV: #ifdef WEENDOZE_CYCLE if (wPreferences.windoze_cycling) { doWindozeCycle(wwin, event, False); } else #endif /* WEENDOZE_CYCLE */ { wwin = PrevFocusWindow(scr); if (wwin != NULL) { wSetFocusTo(scr, wwin); if (wPreferences.circ_raise) wRaiseFrame(wwin->frame->core); } } break; #if (defined(__STDC__) && !defined(UNIXCPP)) || defined(ANSICPP) #define GOTOWORKS(wk) case WKBD_WORKSPACE##wk:\ i = (scr->current_workspace/10)*10 + wk - 1;\ if (wPreferences.ws_advance || iworkspace_count)\ wWorkspaceChange(scr, i);\ break #else #define GOTOWORKS(wk) case WKBD_WORKSPACE/**/wk:\ i = (scr->current_workspace/10)*10 + wk - 1;\ if (wPreferences.ws_advance || iworkspace_count)\ wWorkspaceChange(scr, i);\ break #endif GOTOWORKS(1); GOTOWORKS(2); GOTOWORKS(3); GOTOWORKS(4); GOTOWORKS(5); GOTOWORKS(6); GOTOWORKS(7); GOTOWORKS(8); GOTOWORKS(9); GOTOWORKS(10); #undef GOTOWORKS 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: #ifdef EXTEND_WINDOWSHORTCUT case WKBD_WINDOW5: case WKBD_WINDOW6: case WKBD_WINDOW7: case WKBD_WINDOW8: case WKBD_WINDOW9: case WKBD_WINDOW10: #endif if (scr->shortcutWindow[command-WKBD_WINDOW1]) { wMakeWindowVisible(scr->shortcutWindow[command-WKBD_WINDOW1]); } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) { scr->shortcutWindow[command-WKBD_WINDOW1] = wwin; wSelectWindow(wwin, !wwin->flags.selected); XFlush(dpy); wusleep(3000); wSelectWindow(wwin, !wwin->flags.selected); XFlush(dpy); } 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){ XkbGetState(dpy,XkbUseCoreKbd,&staterec); /*toggle*/ XkbLockGroup(dpy,XkbUseCoreKbd, wwin->languagemode=staterec.compat_state&32?0:1); } break; #endif /* KEEP_XKB_LOCK_STATUS */ } } static void handleMotionNotify(XEvent *event) { WMenu *menu; WScreen *scr = wScreenForRootWindow(event->xmotion.root); WWindow *wwin; if (wPreferences.scrollable_menus) { if (event->xmotion.x_root <= 1 || event->xmotion.x_root >= (scr->scr_width - 2) || event->xmotion.y_root <= 1 || event->xmotion.y_root >= (scr->scr_height - 2)) { #ifdef DEBUG puts("pointer at screen edge"); #endif menu = wMenuUnderPointer(scr); if (menu!=NULL) wMenuScroll(menu, event); } } #if 0 if (event->xmotion.subwindow == None) return; if (scr->scrolledFMaximize != None) { WWindow *twin; twin = wWindowFor(scr->scrolledFMaximize); if (twin && twin->frame_y ==) { } scr->scrolledFMaximize = NULL; } else { /* scroll full maximized window */ if (event->xmotion.y_root < 1 || event->xmotion.y_root > scr->scr_height - 1) { wwin = wWindowFor(event->xmotion.subwindow); if (wwin && (wwin->flags.maximized & MAX_VERTICAL) && WFLAGP(wwin, full_maximize) && event->xmotion.x_root >= wwin->frame_x && event->xmotion.x_root <= wwin->frame_x + wwin->frame->core->width) { if (!WFLAGP(wwin, no_titlebar) && wwin->frame_y <= - wwin->frame->top_width) { wWindowMove(wwin, wwin->frame_x, 0); wwin->flags.dragged_while_fmaximized = 0; } else if (!WFLAGP(wwin, no_resizebar) && wwin->frame_y + wwin->frame->core->height >= scr->scr_height + wwin->frame->bottom_width) { int y = scr->scr_height + wwin->frame->bottom_width; y = scr->scr_height - wwin->frame_y - wwin->frame->core->height; wWindowMove(wwin, wwin->frame_x, y); wwin->flags.dragged_while_fmaximized = 0; } } } #endif } static void handleVisibilityNotify(XEvent *event) { WWindow *wwin; wwin = wWindowFor(event->xvisibility.window); if (!wwin) return; wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured); }