Change to the linux kernel coding style
[wmaker-crm.git] / src / event.c
1 /* event.c- event loop and handling
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22
23 #include <sys/inotify.h>
24 #include "wconfig.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #ifdef SHAPE
34 # include <X11/extensions/shape.h>
35 #endif
36 #ifdef XDND
37 #include "xdnd.h"
38 #endif
39
40 #ifdef KEEP_XKB_LOCK_STATUS
41 #include <X11/XKBlib.h>
42 #endif                          /* KEEP_XKB_LOCK_STATUS */
43
44 #include "WindowMaker.h"
45 #include "window.h"
46 #include "actions.h"
47 #include "client.h"
48 #include "funcs.h"
49 #include "keybind.h"
50 #include "application.h"
51 #include "stacking.h"
52 #include "defaults.h"
53 #include "workspace.h"
54 #include "dock.h"
55 #include "framewin.h"
56 #include "properties.h"
57 #include "balloon.h"
58 #include "xinerama.h"
59
60 #ifdef NETWM_HINTS
61 # include "wmspec.h"
62 #endif
63
64 /******** Global Variables **********/
65 extern XContext wWinContext;
66 extern XContext wVEdgeContext;
67
68 extern Cursor wCursor[WCUR_LAST];
69
70 extern WShortKey wKeyBindings[WKBD_LAST];
71 extern int wScreenCount;
72 extern Time LastTimestamp;
73 extern Time LastFocusChange;
74
75 extern WPreferences wPreferences;
76
77 #define MOD_MASK wPreferences.modifier_mask
78
79 extern Atom _XA_WM_COLORMAP_NOTIFY;
80
81 extern Atom _XA_WM_CHANGE_STATE;
82 extern Atom _XA_WM_DELETE_WINDOW;
83 extern Atom _XA_GNUSTEP_WM_ATTR;
84 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW;
85 extern Atom _XA_GNUSTEP_TITLEBAR_STATE;
86 extern Atom _XA_WINDOWMAKER_WM_FUNCTION;
87 extern Atom _XA_WINDOWMAKER_COMMAND;
88
89 #ifdef SHAPE
90 extern Bool wShapeSupported;
91 extern int wShapeEventBase;
92 #endif
93
94 #ifdef KEEP_XKB_LOCK_STATUS
95 extern int wXkbEventBase;
96 #endif
97
98 /* special flags */
99 /*extern char WDelayedActionSet;*/
100
101 /************ Local stuff ***********/
102
103 static void saveTimestamp(XEvent * event);
104 static void handleColormapNotify();
105 static void handleMapNotify();
106 static void handleUnmapNotify();
107 static void handleButtonPress();
108 static void handleExpose();
109 static void handleDestroyNotify();
110 static void handleConfigureRequest();
111 static void handleMapRequest();
112 static void handlePropertyNotify();
113 static void handleEnterNotify();
114 static void handleLeaveNotify();
115 static void handleExtensions();
116 static void handleClientMessage();
117 static void handleKeyPress();
118 static void handleFocusIn();
119 static void handleMotionNotify();
120 static void handleVisibilityNotify();
121
122 #ifdef SHAPE
123 static void handleShapeNotify();
124 #endif
125
126 /* called from the signal handler */
127 void NotifyDeadProcess(pid_t pid, unsigned char status);
128
129 /* real dead process handler */
130 static void handleDeadProcess(void *foo);
131
132 typedef struct DeadProcesses {
133         pid_t pid;
134         unsigned char exit_status;
135 } DeadProcesses;
136
137 /* stack of dead processes */
138 static DeadProcesses deadProcesses[MAX_DEAD_PROCESSES];
139 static int deadProcessPtr = 0;
140
141 typedef struct DeathHandler {
142         WDeathHandler *callback;
143         pid_t pid;
144         void *client_data;
145 } DeathHandler;
146
147 static WMArray *deathHandlers = NULL;
148
149 WMagicNumber wAddDeathHandler(pid_t pid, WDeathHandler * callback, void *cdata)
150 {
151         DeathHandler *handler;
152
153         handler = malloc(sizeof(DeathHandler));
154         if (!handler)
155                 return 0;
156
157         handler->pid = pid;
158         handler->callback = callback;
159         handler->client_data = cdata;
160
161         if (!deathHandlers)
162                 deathHandlers = WMCreateArrayWithDestructor(8, wfree);
163
164         WMAddToArray(deathHandlers, handler);
165
166         return handler;
167 }
168
169 void wDeleteDeathHandler(WMagicNumber id)
170 {
171         DeathHandler *handler = (DeathHandler *) id;
172
173         if (!handler || !deathHandlers)
174                 return;
175
176         /* array destructor will call wfree(handler) */
177         WMRemoveFromArray(deathHandlers, handler);
178 }
179
180 void DispatchEvent(XEvent * event)
181 {
182         if (deathHandlers)
183                 handleDeadProcess(NULL);
184
185         if (WCHECK_STATE(WSTATE_NEED_EXIT)) {
186                 WCHANGE_STATE(WSTATE_EXITING);
187                 /* received SIGTERM */
188                 /*
189                  * WMHandleEvent() can't be called from anything
190                  * executed inside here, or we can get in a infinite
191                  * recursive loop.
192                  */
193                 Shutdown(WSExitMode);
194
195         } else if (WCHECK_STATE(WSTATE_NEED_RESTART)) {
196                 WCHANGE_STATE(WSTATE_RESTARTING);
197
198                 Shutdown(WSRestartPreparationMode);
199                 /* received SIGHUP */
200                 Restart(NULL, True);
201         } else if (WCHECK_STATE(WSTATE_NEED_REREAD)) {
202                 WCHANGE_STATE(WSTATE_NORMAL);
203                 wDefaultsCheckDomains();
204         }
205
206         /* for the case that all that is wanted to be dispatched is
207          * the stuff above */
208         if (!event)
209                 return;
210
211         saveTimestamp(event);
212         switch (event->type) {
213         case MapRequest:
214                 handleMapRequest(event);
215                 break;
216
217         case KeyPress:
218                 handleKeyPress(event);
219                 break;
220
221         case MotionNotify:
222                 handleMotionNotify(event);
223                 break;
224
225         case ConfigureRequest:
226                 handleConfigureRequest(event);
227                 break;
228
229         case DestroyNotify:
230                 handleDestroyNotify(event);
231                 break;
232
233         case MapNotify:
234                 handleMapNotify(event);
235                 break;
236
237         case UnmapNotify:
238                 handleUnmapNotify(event);
239                 break;
240
241         case ButtonPress:
242                 handleButtonPress(event);
243                 break;
244
245         case Expose:
246                 handleExpose(event);
247                 break;
248
249         case PropertyNotify:
250                 handlePropertyNotify(event);
251                 break;
252
253         case EnterNotify:
254                 handleEnterNotify(event);
255                 break;
256
257         case LeaveNotify:
258                 handleLeaveNotify(event);
259                 break;
260
261         case ClientMessage:
262                 handleClientMessage(event);
263                 break;
264
265         case ColormapNotify:
266                 handleColormapNotify(event);
267                 break;
268
269         case MappingNotify:
270                 if (event->xmapping.request == MappingKeyboard || event->xmapping.request == MappingModifier)
271                         XRefreshKeyboardMapping(&event->xmapping);
272                 break;
273
274         case FocusIn:
275                 handleFocusIn(event);
276                 break;
277
278         case VisibilityNotify:
279                 handleVisibilityNotify(event);
280                 break;
281         default:
282                 handleExtensions(event);
283                 break;
284         }
285 }
286
287 /*
288  *----------------------------------------------------------------------
289  * inotifyHandleEvents-
290  *      Check for inotify events
291  *
292  * Returns:
293  *      After reading events for the given file descriptor (fd) and
294  *     watch descriptor (wd)
295  *
296  * Side effects:
297  *      Calls wDefaultsCheckDomains if config database is updated
298  *----------------------------------------------------------------------
299  */
300
301 #define BUFF_SIZE ((sizeof(struct inotify_event) + 16)*512)
302 void inotifyHandleEvents(int fd, int wd)
303 {
304         extern void wDefaultsCheckDomains();
305         ssize_t eventQLength, i = 0;
306         char buff[BUFF_SIZE] = { 0 };
307         /* Check config only once per read of the event queue */
308         int oneShotFlag = 0;
309
310         /*
311          * Read off the queued events
312          * queue overflow is not checked (IN_Q_OVERFLOW). In practise this should
313          * not occur; the block is on Xevents, but a config file change will normally
314          * occur as a result of an Xevent - so the event queue should never have more than
315          * a few entries before a read().
316          */
317         eventQLength = read(fd, buff, BUFF_SIZE);
318
319         /* check what events occured */
320         /* Should really check wd here too, but for now we only have one watch! */
321         while (i < eventQLength) {
322                 struct inotify_event *pevent = (struct inotify_event *)&buff[i];
323
324                 /*
325                  * see inotify.h for event types.
326                  */
327                 if (pevent->mask & IN_DELETE_SELF) {
328                         wwarning(_("the defaults database has been deleted!"
329                                    " Restart Window Maker to create the database" " with the default settings"));
330                         close(fd);
331                 }
332                 if (pevent->mask & IN_UNMOUNT) {
333                         wwarning(_("the unit containing the defaults database has"
334                                    " been unmounted. Setting --static mode." " Any changes will not be saved."));
335                         close(fd);
336                         wPreferences.flags.noupdates = 1;
337                 }
338                 if ((pevent->mask & IN_MODIFY) && oneShotFlag == 0) {
339                         fprintf(stdout, "wmaker: reading config files in defaults database.\n");
340                         wDefaultsCheckDomains();
341                 }
342
343                 /* move to next event in the buffer */
344                 i += sizeof(struct inotify_event) + pevent->len;
345         }
346 }
347
348 /*
349  *----------------------------------------------------------------------
350  * EventLoop-
351  *      Processes X and internal events indefinitely.
352  *
353  * Returns:
354  *      Never returns
355  *
356  * Side effects:
357  *      The LastTimestamp global variable is updated.
358  *      Calls inotifyGetEvents if defaults database changes.
359  *----------------------------------------------------------------------
360  */
361 void EventLoop()
362 {
363         XEvent event;
364         extern int inotifyFD;
365         extern int inotifyWD;
366         struct timeval time;
367         fd_set rfds;
368         int retVal = 0;
369
370         if (inotifyFD < 0 || inotifyWD < 0)
371                 retVal = -1;
372
373         for (;;) {
374
375                 WMNextEvent(dpy, &event);       /* Blocks here */
376                 WMHandleEvent(&event);
377
378                 if (retVal != -1) {
379                         time.tv_sec = 0;
380                         time.tv_usec = 0;
381                         FD_ZERO(&rfds);
382                         FD_SET(inotifyFD, &rfds);
383
384                         /* check for available read data from inotify - don't block! */
385                         retVal = select(inotifyFD + 1, &rfds, NULL, NULL, &time);
386
387                         if (retVal < 0) {       /* an error has occured */
388                                 wwarning(_("select failed. The inotify instance will be closed."
389                                            " Changes to the defaults database will require"
390                                            " a restart to take effect."));
391                                 close(inotifyFD);
392                                 continue;
393                         }
394                         if (FD_ISSET(inotifyFD, &rfds))
395                                 inotifyHandleEvents(inotifyFD, inotifyWD);
396                 }
397         }
398 }
399
400 /*
401  *----------------------------------------------------------------------
402  * ProcessPendingEvents --
403  *      Processes the events that are currently pending (at the time
404  *      this function is called) in the display's queue.
405  *
406  * Returns:
407  *      After the pending events that were present at the function call
408  *      are processed.
409  *
410  * Side effects:
411  *      Many -- whatever handling events may involve.
412  *
413  *----------------------------------------------------------------------
414  */
415 void ProcessPendingEvents()
416 {
417         XEvent event;
418         int count;
419
420         XSync(dpy, False);
421
422         /* Take a snapshot of the event count in the queue */
423         count = XPending(dpy);
424
425         while (count > 0 && XPending(dpy)) {
426                 WMNextEvent(dpy, &event);
427                 WMHandleEvent(&event);
428                 count--;
429         }
430 }
431
432 Bool IsDoubleClick(WScreen * scr, XEvent * event)
433 {
434         if ((scr->last_click_time > 0) &&
435             (event->xbutton.time - scr->last_click_time <= wPreferences.dblclick_time)
436             && (event->xbutton.button == scr->last_click_button)
437             && (event->xbutton.window == scr->last_click_window)) {
438
439                 scr->flags.next_click_is_not_double = 1;
440                 scr->last_click_time = 0;
441                 scr->last_click_window = event->xbutton.window;
442
443                 return True;
444         }
445         return False;
446 }
447
448 void NotifyDeadProcess(pid_t pid, unsigned char status)
449 {
450         if (deadProcessPtr >= MAX_DEAD_PROCESSES - 1) {
451                 wwarning("stack overflow: too many dead processes");
452                 return;
453         }
454         /* stack the process to be handled later,
455          * as this is called from the signal handler */
456         deadProcesses[deadProcessPtr].pid = pid;
457         deadProcesses[deadProcessPtr].exit_status = status;
458         deadProcessPtr++;
459 }
460
461 static void handleDeadProcess(void *foo)
462 {
463         DeathHandler *tmp;
464         int i;
465
466         for (i = 0; i < deadProcessPtr; i++) {
467                 wWindowDeleteSavedStatesForPID(deadProcesses[i].pid);
468         }
469
470         if (!deathHandlers) {
471                 deadProcessPtr = 0;
472                 return;
473         }
474
475         /* get the pids on the queue and call handlers */
476         while (deadProcessPtr > 0) {
477                 deadProcessPtr--;
478
479                 for (i = WMGetArrayItemCount(deathHandlers) - 1; i >= 0; i--) {
480                         tmp = WMGetFromArray(deathHandlers, i);
481                         if (!tmp)
482                                 continue;
483
484                         if (tmp->pid == deadProcesses[deadProcessPtr].pid) {
485                                 (*tmp->callback) (tmp->pid,
486                                                   deadProcesses[deadProcessPtr].exit_status, tmp->client_data);
487                                 wDeleteDeathHandler(tmp);
488                         }
489                 }
490         }
491 }
492
493 static void saveTimestamp(XEvent * event)
494 {
495         /*
496          * Never save CurrentTime as LastTimestamp because CurrentTime
497          * it's not a real timestamp (it's the 0L constant)
498          */
499
500         switch (event->type) {
501         case ButtonRelease:
502         case ButtonPress:
503                 LastTimestamp = event->xbutton.time;
504                 break;
505         case KeyPress:
506         case KeyRelease:
507                 LastTimestamp = event->xkey.time;
508                 break;
509         case MotionNotify:
510                 LastTimestamp = event->xmotion.time;
511                 break;
512         case PropertyNotify:
513                 LastTimestamp = event->xproperty.time;
514                 break;
515         case EnterNotify:
516         case LeaveNotify:
517                 LastTimestamp = event->xcrossing.time;
518                 break;
519         case SelectionClear:
520                 LastTimestamp = event->xselectionclear.time;
521                 break;
522         case SelectionRequest:
523                 LastTimestamp = event->xselectionrequest.time;
524                 break;
525         case SelectionNotify:
526                 LastTimestamp = event->xselection.time;
527 #ifdef XDND
528                 wXDNDProcessSelection(event);
529 #endif
530                 break;
531         }
532 }
533
534 static int matchWindow(void *item, void *cdata)
535 {
536         return (((WFakeGroupLeader *) item)->origLeader == (Window) cdata);
537 }
538
539 static void handleExtensions(XEvent * event)
540 {
541 #ifdef KEEP_XKB_LOCK_STATUS
542         XkbEvent *xkbevent;
543         xkbevent = (XkbEvent *) event;
544 #endif                          /*KEEP_XKB_LOCK_STATUS */
545 #ifdef SHAPE
546         if (wShapeSupported && event->type == (wShapeEventBase + ShapeNotify)) {
547                 handleShapeNotify(event);
548         }
549 #endif
550 #ifdef KEEP_XKB_LOCK_STATUS
551         if (wPreferences.modelock && (xkbevent->type == wXkbEventBase)) {
552                 handleXkbIndicatorStateNotify(event);
553         }
554 #endif                          /*KEEP_XKB_LOCK_STATUS */
555 }
556
557 static void handleMapRequest(XEvent * ev)
558 {
559         WWindow *wwin;
560         WScreen *scr = NULL;
561         Window window = ev->xmaprequest.window;
562
563 #ifdef DEBUG
564         printf("got map request for %x\n", (unsigned)window);
565 #endif
566         if ((wwin = wWindowFor(window))) {
567                 if (wwin->flags.shaded) {
568                         wUnshadeWindow(wwin);
569                 }
570                 /* deiconify window */
571                 if (wwin->flags.miniaturized) {
572                         wDeiconifyWindow(wwin);
573                 } else if (wwin->flags.hidden) {
574                         WApplication *wapp = wApplicationOf(wwin->main_window);
575                         /* go to the last workspace that the user worked on the app */
576                         if (wapp) {
577                                 wWorkspaceChange(wwin->screen_ptr, wapp->last_workspace);
578                         }
579                         wUnhideApplication(wapp, False, False);
580                 }
581                 return;
582         }
583
584         scr = wScreenForRootWindow(ev->xmaprequest.parent);
585
586         wwin = wManageWindow(scr, window);
587
588         /*
589          * This is to let the Dock know that the application it launched
590          * has already been mapped (eg: it has finished launching).
591          * It is not necessary for normally docked apps, but is needed for
592          * apps that were forcedly docked (like with dockit).
593          */
594         if (scr->last_dock) {
595                 if (wwin && wwin->main_window != None && wwin->main_window != window)
596                         wDockTrackWindowLaunch(scr->last_dock, wwin->main_window);
597                 else
598                         wDockTrackWindowLaunch(scr->last_dock, window);
599         }
600
601         if (wwin) {
602                 wClientSetState(wwin, NormalState, None);
603                 if (wwin->flags.maximized) {
604                         wMaximizeWindow(wwin, wwin->flags.maximized);
605                 }
606                 if (wwin->flags.shaded) {
607                         wwin->flags.shaded = 0;
608                         wwin->flags.skip_next_animation = 1;
609                         wShadeWindow(wwin);
610                 }
611                 if (wwin->flags.miniaturized) {
612                         wwin->flags.miniaturized = 0;
613                         wwin->flags.skip_next_animation = 1;
614                         wIconifyWindow(wwin);
615                 }
616                 if (wwin->flags.fullscreen) {
617                         wwin->flags.fullscreen = 0;
618                         wFullscreenWindow(wwin);
619                 }
620                 if (wwin->flags.hidden) {
621                         WApplication *wapp = wApplicationOf(wwin->main_window);
622
623                         wwin->flags.hidden = 0;
624                         wwin->flags.skip_next_animation = 1;
625                         if (wapp) {
626                                 wHideApplication(wapp);
627                         }
628                 }
629         }
630 }
631
632 static void handleDestroyNotify(XEvent * event)
633 {
634         WWindow *wwin;
635         WApplication *app;
636         Window window = event->xdestroywindow.window;
637         WScreen *scr = wScreenForRootWindow(event->xdestroywindow.event);
638         int widx;
639
640 #ifdef DEBUG
641         printf("got destroy notify\n");
642 #endif
643         wwin = wWindowFor(window);
644         if (wwin) {
645                 wUnmanageWindow(wwin, False, True);
646         }
647
648         if (scr != NULL) {
649                 while ((widx = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)window)) != WANotFound) {
650                         WFakeGroupLeader *fPtr;
651
652                         fPtr = WMGetFromArray(scr->fakeGroupLeaders, widx);
653                         if (fPtr->retainCount > 0) {
654                                 fPtr->retainCount--;
655                                 if (fPtr->retainCount == 0 && fPtr->leader != None) {
656                                         XDestroyWindow(dpy, fPtr->leader);
657                                         fPtr->leader = None;
658                                         XFlush(dpy);
659                                 }
660                         }
661                         fPtr->origLeader = None;
662                 }
663         }
664
665         app = wApplicationOf(window);
666         if (app) {
667                 if (window == app->main_window) {
668                         app->refcount = 0;
669                         wwin = app->main_window_desc->screen_ptr->focused_window;
670                         while (wwin) {
671                                 if (wwin->main_window == window) {
672                                         wwin->main_window = None;
673                                 }
674                                 wwin = wwin->prev;
675                         }
676                 }
677                 wApplicationDestroy(app);
678         }
679 }
680
681 static void handleExpose(XEvent * event)
682 {
683         WObjDescriptor *desc;
684         XEvent ev;
685
686 #ifdef DEBUG
687         printf("got expose\n");
688 #endif
689         while (XCheckTypedWindowEvent(dpy, event->xexpose.window, Expose, &ev)) ;
690
691         if (XFindContext(dpy, event->xexpose.window, wWinContext, (XPointer *) & desc) == XCNOENT) {
692                 return;
693         }
694
695         if (desc->handle_expose) {
696                 (*desc->handle_expose) (desc, event);
697         }
698 }
699
700 static void executeButtonAction(WScreen * scr, XEvent * event, int action)
701 {
702         switch (action) {
703         case WA_SELECT_WINDOWS:
704                 wUnselectWindows(scr);
705                 wSelectWindows(scr, event);
706                 break;
707         case WA_OPEN_APPMENU:
708                 OpenRootMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
709                 /* ugly hack */
710                 if (scr->root_menu) {
711                         if (scr->root_menu->brother->flags.mapped)
712                                 event->xbutton.window = scr->root_menu->brother->frame->core->window;
713                         else
714                                 event->xbutton.window = scr->root_menu->frame->core->window;
715                 }
716                 break;
717         case WA_OPEN_WINLISTMENU:
718                 OpenSwitchMenu(scr, event->xbutton.x_root, event->xbutton.y_root, False);
719                 if (scr->switch_menu) {
720                         if (scr->switch_menu->brother->flags.mapped)
721                                 event->xbutton.window = scr->switch_menu->brother->frame->core->window;
722                         else
723                                 event->xbutton.window = scr->switch_menu->frame->core->window;
724                 }
725                 break;
726         default:
727                 break;
728         }
729 }
730
731 /* bindable */
732 static void handleButtonPress(XEvent * event)
733 {
734         WObjDescriptor *desc;
735         WScreen *scr;
736
737 #ifdef DEBUG
738         printf("got button press\n");
739 #endif
740         scr = wScreenForRootWindow(event->xbutton.root);
741
742 #ifdef BALLOON_TEXT
743         wBalloonHide(scr);
744 #endif
745
746 #ifndef LITE
747         if (event->xbutton.window == scr->root_win) {
748                 if (event->xbutton.button == Button1 && wPreferences.mouse_button1 != WA_NONE) {
749                         executeButtonAction(scr, event, wPreferences.mouse_button1);
750                 } else if (event->xbutton.button == Button2 && wPreferences.mouse_button2 != WA_NONE) {
751                         executeButtonAction(scr, event, wPreferences.mouse_button2);
752                 } else if (event->xbutton.button == Button3 && wPreferences.mouse_button3 != WA_NONE) {
753                         executeButtonAction(scr, event, wPreferences.mouse_button3);
754                 } else if (event->xbutton.button == Button4 && wPreferences.mouse_wheel != WA_NONE) {
755                         wWorkspaceRelativeChange(scr, 1);
756                 } else if (event->xbutton.button == Button5 && wPreferences.mouse_wheel != WA_NONE) {
757                         wWorkspaceRelativeChange(scr, -1);
758                 }
759         }
760 #endif                          /* !LITE */
761
762         desc = NULL;
763         if (XFindContext(dpy, event->xbutton.subwindow, wWinContext, (XPointer *) & desc) == XCNOENT) {
764                 if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) == XCNOENT) {
765                         return;
766                 }
767         }
768
769         if (desc->parent_type == WCLASS_WINDOW) {
770                 XSync(dpy, 0);
771
772                 if (event->xbutton.state & MOD_MASK) {
773                         XAllowEvents(dpy, AsyncPointer, CurrentTime);
774                 }
775
776                 /*      if (wPreferences.focus_mode == WKF_CLICK) { */
777                 if (wPreferences.ignore_focus_click) {
778                         XAllowEvents(dpy, AsyncPointer, CurrentTime);
779                 }
780                 XAllowEvents(dpy, ReplayPointer, CurrentTime);
781                 /*      } */
782                 XSync(dpy, 0);
783         } else if (desc->parent_type == WCLASS_APPICON
784                    || desc->parent_type == WCLASS_MINIWINDOW || desc->parent_type == WCLASS_DOCK_ICON) {
785                 if (event->xbutton.state & MOD_MASK) {
786                         XSync(dpy, 0);
787                         XAllowEvents(dpy, AsyncPointer, CurrentTime);
788                         XSync(dpy, 0);
789                 }
790         }
791
792         if (desc->handle_mousedown != NULL) {
793                 (*desc->handle_mousedown) (desc, event);
794         }
795
796         /* save double-click information */
797         if (scr->flags.next_click_is_not_double) {
798                 scr->flags.next_click_is_not_double = 0;
799         } else {
800                 scr->last_click_time = event->xbutton.time;
801                 scr->last_click_button = event->xbutton.button;
802                 scr->last_click_window = event->xbutton.window;
803         }
804 }
805
806 static void handleMapNotify(XEvent * event)
807 {
808         WWindow *wwin;
809 #ifdef DEBUG
810         printf("got map\n");
811 #endif
812         wwin = wWindowFor(event->xmap.event);
813         if (wwin && wwin->client_win == event->xmap.event) {
814                 if (wwin->flags.miniaturized) {
815                         wDeiconifyWindow(wwin);
816                 } else {
817                         XGrabServer(dpy);
818                         wWindowMap(wwin);
819                         wClientSetState(wwin, NormalState, None);
820                         XUngrabServer(dpy);
821                 }
822         }
823 }
824
825 static void handleUnmapNotify(XEvent * event)
826 {
827         WWindow *wwin;
828         XEvent ev;
829         Bool withdraw = False;
830 #ifdef DEBUG
831         printf("got unmap\n");
832 #endif
833         /* only process windows with StructureNotify selected
834          * (ignore SubstructureNotify) */
835         wwin = wWindowFor(event->xunmap.window);
836         if (!wwin)
837                 return;
838
839         /* whether the event is a Withdrawal request */
840         if (event->xunmap.event == wwin->screen_ptr->root_win && event->xunmap.send_event)
841                 withdraw = True;
842
843         if (wwin->client_win != event->xunmap.event && !withdraw)
844                 return;
845
846         if (!wwin->flags.mapped && !withdraw
847             && wwin->frame->workspace == wwin->screen_ptr->current_workspace
848             && !wwin->flags.miniaturized && !wwin->flags.hidden)
849                 return;
850
851         XGrabServer(dpy);
852         XUnmapWindow(dpy, wwin->frame->core->window);
853         wwin->flags.mapped = 0;
854         XSync(dpy, 0);
855         /* check if the window was destroyed */
856         if (XCheckTypedWindowEvent(dpy, wwin->client_win, DestroyNotify, &ev)) {
857                 DispatchEvent(&ev);
858         } else {
859                 Bool reparented = False;
860
861                 if (XCheckTypedWindowEvent(dpy, wwin->client_win, ReparentNotify, &ev))
862                         reparented = True;
863
864                 /* withdraw window */
865                 wwin->flags.mapped = 0;
866                 if (!reparented)
867                         wClientSetState(wwin, WithdrawnState, None);
868
869                 /* if the window was reparented, do not reparent it back to the
870                  * root window */
871                 wUnmanageWindow(wwin, !reparented, False);
872         }
873         XUngrabServer(dpy);
874 }
875
876 static void handleConfigureRequest(XEvent * event)
877 {
878         WWindow *wwin;
879 #ifdef DEBUG
880         printf("got configure request\n");
881 #endif
882         if (!(wwin = wWindowFor(event->xconfigurerequest.window))) {
883                 /*
884                  * Configure request for unmapped window
885                  */
886                 wClientConfigure(NULL, &(event->xconfigurerequest));
887         } else {
888                 wClientConfigure(wwin, &(event->xconfigurerequest));
889         }
890 }
891
892 static void handlePropertyNotify(XEvent * event)
893 {
894         WWindow *wwin;
895         WApplication *wapp;
896         Window jr;
897         int ji;
898         unsigned int ju;
899         WScreen *scr;
900 #ifdef DEBUG
901         printf("got property notify\n");
902 #endif
903
904         wwin = wWindowFor(event->xproperty.window);
905         if (wwin) {
906                 if (!XGetGeometry(dpy, wwin->client_win, &jr, &ji, &ji, &ju, &ju, &ju, &ju)) {
907                         return;
908                 }
909                 wClientCheckProperty(wwin, &event->xproperty);
910         }
911         wapp = wApplicationOf(event->xproperty.window);
912         if (wapp) {
913                 wClientCheckProperty(wapp->main_window_desc, &event->xproperty);
914         }
915
916         scr = wScreenForWindow(event->xproperty.window);
917 }
918
919 static void handleClientMessage(XEvent * event)
920 {
921         WWindow *wwin;
922         WObjDescriptor *desc;
923 #ifdef DEBUG
924         printf("got client message\n");
925 #endif
926         /* handle transition from Normal to Iconic state */
927         if (event->xclient.message_type == _XA_WM_CHANGE_STATE
928             && event->xclient.format == 32 && event->xclient.data.l[0] == IconicState) {
929
930                 wwin = wWindowFor(event->xclient.window);
931                 if (!wwin)
932                         return;
933                 if (!wwin->flags.miniaturized)
934                         wIconifyWindow(wwin);
935         } else if (event->xclient.message_type == _XA_WM_COLORMAP_NOTIFY && event->xclient.format == 32) {
936                 WScreen *scr = wScreenSearchForRootWindow(event->xclient.window);
937
938                 if (!scr)
939                         return;
940
941                 if (event->xclient.data.l[1] == 1) {    /* starting */
942                         wColormapAllowClientInstallation(scr, True);
943                 } else {        /* stopping */
944                         wColormapAllowClientInstallation(scr, False);
945                 }
946         } else if (event->xclient.message_type == _XA_WINDOWMAKER_COMMAND) {
947
948                 wDefaultsCheckDomains();
949
950         } else if (event->xclient.message_type == _XA_WINDOWMAKER_WM_FUNCTION) {
951                 WApplication *wapp;
952                 int done = 0;
953                 wapp = wApplicationOf(event->xclient.window);
954                 if (wapp) {
955                         switch (event->xclient.data.l[0]) {
956                         case WMFHideOtherApplications:
957                                 wHideOtherApplications(wapp->main_window_desc);
958                                 done = 1;
959                                 break;
960
961                         case WMFHideApplication:
962                                 wHideApplication(wapp);
963                                 done = 1;
964                                 break;
965                         }
966                 }
967                 if (!done) {
968                         wwin = wWindowFor(event->xclient.window);
969                         if (wwin) {
970                                 switch (event->xclient.data.l[0]) {
971                                 case WMFHideOtherApplications:
972                                         wHideOtherApplications(wwin);
973                                         break;
974
975                                 case WMFHideApplication:
976                                         wHideApplication(wApplicationOf(wwin->main_window));
977                                         break;
978                                 }
979                         }
980                 }
981         } else if (event->xclient.message_type == _XA_GNUSTEP_WM_ATTR) {
982                 wwin = wWindowFor(event->xclient.window);
983                 if (!wwin)
984                         return;
985                 switch (event->xclient.data.l[0]) {
986                 case GSWindowLevelAttr:
987                         {
988                                 int level = (int)event->xclient.data.l[1];
989
990                                 if (WINDOW_LEVEL(wwin) != level) {
991                                         ChangeStackingLevel(wwin->frame->core, level);
992                                 }
993                         }
994                         break;
995                 }
996         } else if (event->xclient.message_type == _XA_GNUSTEP_TITLEBAR_STATE) {
997                 wwin = wWindowFor(event->xclient.window);
998                 if (!wwin)
999                         return;
1000                 switch (event->xclient.data.l[0]) {
1001                 case WMTitleBarNormal:
1002                         wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1003                         break;
1004                 case WMTitleBarMain:
1005                         wFrameWindowChangeState(wwin->frame, WS_PFOCUSED);
1006                         break;
1007                 case WMTitleBarKey:
1008                         wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1009                         break;
1010                 }
1011 #ifdef NETWM_HINTS
1012         } else if (wNETWMProcessClientMessage(&event->xclient)) {
1013                 /* do nothing */
1014 #endif
1015 #ifdef XDND
1016         } else if (wXDNDProcessClientMessage(&event->xclient)) {
1017                 /* do nothing */
1018 #endif                          /* XDND */
1019         } else {
1020                 /*
1021                  * Non-standard thing, but needed by OffiX DND.
1022                  * For when the icon frame gets a ClientMessage
1023                  * that should have gone to the icon_window.
1024                  */
1025                 if (XFindContext(dpy, event->xbutton.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1026                         struct WIcon *icon = NULL;
1027
1028                         if (desc->parent_type == WCLASS_MINIWINDOW) {
1029                                 icon = (WIcon *) desc->parent;
1030                         } else if (desc->parent_type == WCLASS_DOCK_ICON || desc->parent_type == WCLASS_APPICON) {
1031                                 icon = ((WAppIcon *) desc->parent)->icon;
1032                         }
1033                         if (icon && (wwin = icon->owner)) {
1034                                 if (wwin->client_win != event->xclient.window) {
1035                                         event->xclient.window = wwin->client_win;
1036                                         XSendEvent(dpy, wwin->client_win, False, NoEventMask, event);
1037                                 }
1038                         }
1039                 }
1040         }
1041 }
1042
1043 static void raiseWindow(WScreen * scr)
1044 {
1045         WWindow *wwin;
1046
1047         scr->autoRaiseTimer = NULL;
1048
1049         wwin = wWindowFor(scr->autoRaiseWindow);
1050         if (!wwin)
1051                 return;
1052
1053         if (!wwin->flags.destroyed && wwin->flags.focused) {
1054                 wRaiseFrame(wwin->frame->core);
1055                 /* this is needed or a race condition will occur */
1056                 XSync(dpy, False);
1057         }
1058 }
1059
1060 static void handleEnterNotify(XEvent * event)
1061 {
1062         WWindow *wwin;
1063         WObjDescriptor *desc = NULL;
1064 #ifdef VIRTUAL_DESKTOP
1065         void (*vdHandler) (XEvent * event);
1066 #endif
1067         XEvent ev;
1068         WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
1069 #ifdef DEBUG
1070         printf("got enter notify\n");
1071 #endif
1072
1073 #ifdef VIRTUAL_DESKTOP
1074         if (XFindContext(dpy, event->xcrossing.window, wVEdgeContext, (XPointer *) & vdHandler) != XCNOENT) {
1075                 (*vdHandler) (event);
1076         }
1077 #endif
1078
1079         if (XCheckTypedWindowEvent(dpy, event->xcrossing.window, LeaveNotify, &ev)) {
1080                 /* already left the window... */
1081                 saveTimestamp(&ev);
1082                 if (ev.xcrossing.mode == event->xcrossing.mode && ev.xcrossing.detail == event->xcrossing.detail) {
1083                         return;
1084                 }
1085         }
1086
1087         if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1088                 if (desc->handle_enternotify)
1089                         (*desc->handle_enternotify) (desc, event);
1090         }
1091
1092         /* enter to window */
1093         wwin = wWindowFor(event->xcrossing.window);
1094         if (!wwin) {
1095                 if (wPreferences.colormap_mode == WCM_POINTER) {
1096                         wColormapInstallForWindow(scr, NULL);
1097                 }
1098                 if (scr->autoRaiseTimer && event->xcrossing.root == event->xcrossing.window) {
1099                         WMDeleteTimerHandler(scr->autoRaiseTimer);
1100                         scr->autoRaiseTimer = NULL;
1101                 }
1102         } else {
1103                 /* set auto raise timer even if in focus-follows-mouse mode
1104                  * and the event is for the frame window, even if the window
1105                  * has focus already.  useful if you move the pointer from a focused
1106                  * window to the root window and back pretty fast
1107                  *
1108                  * set focus if in focus-follows-mouse mode and the event
1109                  * is for the frame window and window doesn't have focus yet */
1110                 if (wPreferences.focus_mode == WKF_SLOPPY
1111                     && wwin->frame->core->window == event->xcrossing.window && !scr->flags.doing_alt_tab) {
1112
1113                         if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable))
1114                                 wSetFocusTo(scr, wwin);
1115
1116                         if (scr->autoRaiseTimer)
1117                                 WMDeleteTimerHandler(scr->autoRaiseTimer);
1118                         scr->autoRaiseTimer = NULL;
1119
1120                         if (wPreferences.raise_delay && !WFLAGP(wwin, no_focusable)) {
1121                                 scr->autoRaiseWindow = wwin->frame->core->window;
1122                                 scr->autoRaiseTimer
1123                                     = WMAddTimerHandler(wPreferences.raise_delay, (WMCallback *) raiseWindow, scr);
1124                         }
1125                 }
1126                 /* Install colormap for window, if the colormap installation mode
1127                  * is colormap_follows_mouse */
1128                 if (wPreferences.colormap_mode == WCM_POINTER) {
1129                         if (wwin->client_win == event->xcrossing.window)
1130                                 wColormapInstallForWindow(scr, wwin);
1131                         else
1132                                 wColormapInstallForWindow(scr, NULL);
1133                 }
1134         }
1135
1136         /* a little kluge to hide the clip balloon */
1137         if (!wPreferences.flags.noclip && scr->flags.clip_balloon_mapped) {
1138                 if (!desc) {
1139                         XUnmapWindow(dpy, scr->clip_balloon);
1140                         scr->flags.clip_balloon_mapped = 0;
1141                 } else {
1142                         if (desc->parent_type != WCLASS_DOCK_ICON || scr->clip_icon != desc->parent) {
1143                                 XUnmapWindow(dpy, scr->clip_balloon);
1144                                 scr->flags.clip_balloon_mapped = 0;
1145                         }
1146                 }
1147         }
1148
1149         if (event->xcrossing.window == event->xcrossing.root
1150             && event->xcrossing.detail == NotifyNormal
1151             && event->xcrossing.detail != NotifyInferior && wPreferences.focus_mode != WKF_CLICK) {
1152
1153                 wSetFocusTo(scr, scr->focused_window);
1154         }
1155 #ifdef BALLOON_TEXT
1156         wBalloonEnteredObject(scr, desc);
1157 #endif
1158 }
1159
1160 static void handleLeaveNotify(XEvent * event)
1161 {
1162         WObjDescriptor *desc = NULL;
1163
1164         if (XFindContext(dpy, event->xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT) {
1165                 if (desc->handle_leavenotify)
1166                         (*desc->handle_leavenotify) (desc, event);
1167         }
1168 }
1169
1170 #ifdef SHAPE
1171 static void handleShapeNotify(XEvent * event)
1172 {
1173         XShapeEvent *shev = (XShapeEvent *) event;
1174         WWindow *wwin;
1175         XEvent ev;
1176 #ifdef DEBUG
1177         printf("got shape notify\n");
1178 #endif
1179         while (XCheckTypedWindowEvent(dpy, shev->window, event->type, &ev)) {
1180                 XShapeEvent *sev = (XShapeEvent *) & ev;
1181
1182                 if (sev->kind == ShapeBounding) {
1183                         if (sev->shaped == shev->shaped) {
1184                                 *shev = *sev;
1185                         } else {
1186                                 XPutBackEvent(dpy, &ev);
1187                                 break;
1188                         }
1189                 }
1190         }
1191
1192         wwin = wWindowFor(shev->window);
1193         if (!wwin || shev->kind != ShapeBounding)
1194                 return;
1195
1196         if (!shev->shaped && wwin->flags.shaped) {
1197
1198                 wwin->flags.shaped = 0;
1199                 wWindowClearShape(wwin);
1200
1201         } else if (shev->shaped) {
1202
1203                 wwin->flags.shaped = 1;
1204                 wWindowSetShape(wwin);
1205         }
1206 }
1207 #endif                          /* SHAPE */
1208
1209 #ifdef KEEP_XKB_LOCK_STATUS
1210 /* please help ]d if you know what to do */
1211 handleXkbIndicatorStateNotify(XEvent * event)
1212 {
1213         WWindow *wwin;
1214         WScreen *scr;
1215         XkbStateRec staterec;
1216         int i;
1217
1218         for (i = 0; i < wScreenCount; i++) {
1219                 scr = wScreenWithNumber(i);
1220                 wwin = scr->focused_window;
1221                 if (wwin && wwin->flags.focused) {
1222                         XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1223                         if (wwin->frame->languagemode != staterec.group) {
1224                                 wwin->frame->last_languagemode = wwin->frame->languagemode;
1225                                 wwin->frame->languagemode = staterec.group;
1226                         }
1227 #ifdef XKB_BUTTON_HINT
1228                         if (wwin->frame->titlebar) {
1229                                 wFrameWindowPaint(wwin->frame);
1230                         }
1231 #endif
1232                 }
1233         }
1234 }
1235 #endif                          /*KEEP_XKB_LOCK_STATUS */
1236
1237 static void handleColormapNotify(XEvent * event)
1238 {
1239         WWindow *wwin;
1240         WScreen *scr;
1241         Bool reinstall = False;
1242
1243         wwin = wWindowFor(event->xcolormap.window);
1244         if (!wwin)
1245                 return;
1246
1247         scr = wwin->screen_ptr;
1248
1249         do {
1250                 if (wwin) {
1251                         if (event->xcolormap.new) {
1252                                 XWindowAttributes attr;
1253
1254                                 XGetWindowAttributes(dpy, wwin->client_win, &attr);
1255
1256                                 if (wwin == scr->cmap_window && wwin->cmap_window_no == 0)
1257                                         scr->current_colormap = attr.colormap;
1258
1259                                 reinstall = True;
1260                         } else if (event->xcolormap.state == ColormapUninstalled &&
1261                                    scr->current_colormap == event->xcolormap.colormap) {
1262
1263                                 /* some bastard app (like XV) removed our colormap */
1264                                 /*
1265                                  * can't enforce or things like xscreensaver wont work
1266                                  * reinstall = True;
1267                                  */
1268                         } else if (event->xcolormap.state == ColormapInstalled &&
1269                                    scr->current_colormap == event->xcolormap.colormap) {
1270
1271                                 /* someone has put our colormap back */
1272                                 reinstall = False;
1273                         }
1274                 }
1275         } while (XCheckTypedEvent(dpy, ColormapNotify, event)
1276                  && ((wwin = wWindowFor(event->xcolormap.window)) || 1));
1277
1278         if (reinstall && scr->current_colormap != None) {
1279                 if (!scr->flags.colormap_stuff_blocked)
1280                         XInstallColormap(dpy, scr->current_colormap);
1281         }
1282 }
1283
1284 static void handleFocusIn(XEvent * event)
1285 {
1286         WWindow *wwin;
1287
1288         /*
1289          * For applications that like stealing the focus.
1290          */
1291         while (XCheckTypedEvent(dpy, FocusIn, event)) ;
1292         saveTimestamp(event);
1293         if (event->xfocus.mode == NotifyUngrab
1294             || event->xfocus.mode == NotifyGrab || event->xfocus.detail > NotifyNonlinearVirtual) {
1295                 return;
1296         }
1297
1298         wwin = wWindowFor(event->xfocus.window);
1299         if (wwin && !wwin->flags.focused) {
1300                 if (wwin->flags.mapped)
1301                         wSetFocusTo(wwin->screen_ptr, wwin);
1302                 else
1303                         wSetFocusTo(wwin->screen_ptr, NULL);
1304         } else if (!wwin) {
1305                 WScreen *scr = wScreenForWindow(event->xfocus.window);
1306                 if (scr)
1307                         wSetFocusTo(scr, NULL);
1308         }
1309 }
1310
1311 static WWindow *windowUnderPointer(WScreen * scr)
1312 {
1313         unsigned int mask;
1314         int foo;
1315         Window bar, win;
1316
1317         if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1318                 return wWindowFor(win);
1319         return NULL;
1320 }
1321
1322 static int CheckFullScreenWindowFocused(WScreen * scr)
1323 {
1324         if (scr->focused_window && scr->focused_window->flags.fullscreen)
1325                 return 1;
1326         else
1327                 return 0;
1328 }
1329
1330 static void handleKeyPress(XEvent * event)
1331 {
1332         WScreen *scr = wScreenForRootWindow(event->xkey.root);
1333         WWindow *wwin = scr->focused_window;
1334         short i, widx;
1335         int modifiers;
1336         int command = -1;
1337 #ifdef KEEP_XKB_LOCK_STATUS
1338         XkbStateRec staterec;
1339 #endif                          /*KEEP_XKB_LOCK_STATUS */
1340
1341         /* ignore CapsLock */
1342         modifiers = event->xkey.state & ValidModMask;
1343
1344         for (i = 0; i < WKBD_LAST; i++) {
1345                 if (wKeyBindings[i].keycode == 0)
1346                         continue;
1347
1348                 if (wKeyBindings[i].keycode == event->xkey.keycode && ( /*wKeyBindings[i].modifier==0
1349                                                                            || */ wKeyBindings[i].modifier ==
1350                                                                               modifiers)) {
1351                         command = i;
1352                         break;
1353                 }
1354         }
1355
1356         if (command < 0) {
1357 #ifdef LITE
1358                 {
1359 #if 0
1360                 }
1361 #endif
1362 #else
1363                 if (!wRootMenuPerformShortcut(event)) {
1364 #endif
1365                         static int dontLoop = 0;
1366
1367                         if (dontLoop > 10) {
1368                                 wwarning("problem with key event processing code");
1369                                 return;
1370                         }
1371                         dontLoop++;
1372                         /* if the focused window is an internal window, try redispatching
1373                          * the event to the managed window, as it can be a WINGs window */
1374                         if (wwin && wwin->flags.internal_window && wwin->client_leader != None) {
1375                                 /* client_leader contains the WINGs toplevel */
1376                                 event->xany.window = wwin->client_leader;
1377                                 WMHandleEvent(event);
1378                         }
1379                         dontLoop--;
1380                 }
1381                 return;
1382         }
1383 #define ISMAPPED(w) ((w) && !(w)->flags.miniaturized && ((w)->flags.mapped || (w)->flags.shaded))
1384 #define ISFOCUSED(w) ((w) && (w)->flags.focused)
1385
1386         switch (command) {
1387 #ifndef LITE
1388         case WKBD_ROOTMENU:
1389                 /*OpenRootMenu(scr, event->xkey.x_root, event->xkey.y_root, True); */
1390                 if (!CheckFullScreenWindowFocused(scr)) {
1391                         WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1392                         OpenRootMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1393                                      True);
1394                 }
1395                 break;
1396         case WKBD_WINDOWLIST:
1397                 if (!CheckFullScreenWindowFocused(scr)) {
1398                         WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1399                         OpenSwitchMenu(scr, rect.pos.x + rect.size.width / 2, rect.pos.y + rect.size.height / 2,
1400                                        True);
1401                 }
1402                 break;
1403 #endif                          /* !LITE */
1404         case WKBD_WINDOWMENU:
1405                 if (ISMAPPED(wwin) && ISFOCUSED(wwin))
1406                         OpenWindowMenu(wwin, wwin->frame_x, wwin->frame_y + wwin->frame->top_width, True);
1407                 break;
1408         case WKBD_MINIATURIZE:
1409                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)
1410                     && !WFLAGP(wwin, no_miniaturizable)) {
1411                         CloseWindowMenu(scr);
1412
1413                         if (wwin->protocols.MINIATURIZE_WINDOW)
1414                                 wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, event->xbutton.time);
1415                         else {
1416                                 wIconifyWindow(wwin);
1417                         }
1418                 }
1419                 break;
1420         case WKBD_HIDE:
1421                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1422                         WApplication *wapp = wApplicationOf(wwin->main_window);
1423                         CloseWindowMenu(scr);
1424
1425                         if (wapp && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1426                                 wHideApplication(wapp);
1427                         }
1428                 }
1429                 break;
1430         case WKBD_HIDE_OTHERS:
1431                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1432                         CloseWindowMenu(scr);
1433
1434                         wHideOtherApplications(wwin);
1435                 }
1436                 break;
1437         case WKBD_MAXIMIZE:
1438                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1439                         int newdir = (MAX_VERTICAL | MAX_HORIZONTAL);
1440
1441                         CloseWindowMenu(scr);
1442
1443                         if (wwin->flags.maximized == newdir) {
1444                                 wUnmaximizeWindow(wwin);
1445                         } else {
1446                                 wMaximizeWindow(wwin, newdir | MAX_KEYBOARD);
1447                         }
1448                 }
1449                 break;
1450         case WKBD_VMAXIMIZE:
1451                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1452                         int newdir = (MAX_VERTICAL ^ wwin->flags.maximized);
1453
1454                         CloseWindowMenu(scr);
1455
1456                         if (newdir) {
1457                                 wMaximizeWindow(wwin, newdir | MAX_KEYBOARD);
1458                         } else {
1459                                 wUnmaximizeWindow(wwin);
1460                         }
1461                 }
1462                 break;
1463         case WKBD_HMAXIMIZE:
1464                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && IS_RESIZABLE(wwin)) {
1465                         int newdir = (MAX_HORIZONTAL ^ wwin->flags.maximized);
1466
1467                         CloseWindowMenu(scr);
1468
1469                         if (newdir) {
1470                                 wMaximizeWindow(wwin, newdir | MAX_KEYBOARD);
1471                         } else {
1472                                 wUnmaximizeWindow(wwin);
1473                         }
1474                 }
1475                 break;
1476         case WKBD_RAISE:
1477                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1478                         CloseWindowMenu(scr);
1479
1480                         wRaiseFrame(wwin->frame->core);
1481                 }
1482                 break;
1483         case WKBD_LOWER:
1484                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1485                         CloseWindowMenu(scr);
1486
1487                         wLowerFrame(wwin->frame->core);
1488                 }
1489                 break;
1490         case WKBD_RAISELOWER:
1491                 /* raise or lower the window under the pointer, not the
1492                  * focused one
1493                  */
1494                 wwin = windowUnderPointer(scr);
1495                 if (wwin)
1496                         wRaiseLowerFrame(wwin->frame->core);
1497                 break;
1498         case WKBD_SHADE:
1499                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_shadeable)) {
1500                         if (wwin->flags.shaded)
1501                                 wUnshadeWindow(wwin);
1502                         else
1503                                 wShadeWindow(wwin);
1504                 }
1505                 break;
1506         case WKBD_MOVERESIZE:
1507                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && (IS_RESIZABLE(wwin) || IS_MOVABLE(wwin))) {
1508                         CloseWindowMenu(scr);
1509
1510                         wKeyboardMoveResizeWindow(wwin);
1511                 }
1512                 break;
1513         case WKBD_CLOSE:
1514                 if (ISMAPPED(wwin) && ISFOCUSED(wwin) && !WFLAGP(wwin, no_closable)) {
1515                         CloseWindowMenu(scr);
1516                         if (wwin->protocols.DELETE_WINDOW)
1517                                 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, event->xkey.time);
1518                 }
1519                 break;
1520         case WKBD_SELECT:
1521                 if (ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1522                         wSelectWindow(wwin, !wwin->flags.selected);
1523                 }
1524                 break;
1525         case WKBD_FOCUSNEXT:
1526                 StartWindozeCycle(wwin, event, True);
1527                 break;
1528
1529         case WKBD_FOCUSPREV:
1530                 StartWindozeCycle(wwin, event, False);
1531                 break;
1532
1533         case WKBD_WORKSPACE1...WKBD_WORKSPACE10:
1534                 widx = command - WKBD_WORKSPACE1;
1535                 i = (scr->current_workspace / 10) * 10 + widx;
1536                 if (wPreferences.ws_advance || i < scr->workspace_count)
1537                         wWorkspaceChange(scr, i);
1538                 break;
1539
1540         case WKBD_NEXTWORKSPACE:
1541                 wWorkspaceRelativeChange(scr, 1);
1542                 break;
1543         case WKBD_PREVWORKSPACE:
1544                 wWorkspaceRelativeChange(scr, -1);
1545                 break;
1546         case WKBD_WINDOW1:
1547         case WKBD_WINDOW2:
1548         case WKBD_WINDOW3:
1549         case WKBD_WINDOW4:
1550         case WKBD_WINDOW5:
1551         case WKBD_WINDOW6:
1552         case WKBD_WINDOW7:
1553         case WKBD_WINDOW8:
1554         case WKBD_WINDOW9:
1555         case WKBD_WINDOW10:
1556
1557                 widx = command - WKBD_WINDOW1;
1558
1559                 if (scr->shortcutWindows[widx]) {
1560                         WMArray *list = scr->shortcutWindows[widx];
1561                         int cw;
1562                         int count = WMGetArrayItemCount(list);
1563                         WWindow *twin;
1564                         WMArrayIterator iter;
1565                         WWindow *wwin;
1566
1567                         wUnselectWindows(scr);
1568                         cw = scr->current_workspace;
1569
1570                         WM_ETARETI_ARRAY(list, wwin, iter) {
1571                                 if (count > 1)
1572                                         wWindowChangeWorkspace(wwin, cw);
1573
1574                                 wMakeWindowVisible(wwin);
1575
1576                                 if (count > 1)
1577                                         wSelectWindow(wwin, True);
1578                         }
1579
1580                         /* rotate the order of windows, to create a cycling effect */
1581                         twin = WMGetFromArray(list, 0);
1582                         WMDeleteFromArray(list, 0);
1583                         WMAddToArray(list, twin);
1584
1585                 } else if (wwin && ISMAPPED(wwin) && ISFOCUSED(wwin)) {
1586                         if (scr->shortcutWindows[widx]) {
1587                                 WMFreeArray(scr->shortcutWindows[widx]);
1588                                 scr->shortcutWindows[widx] = NULL;
1589                         }
1590
1591                         if (wwin->flags.selected && scr->selected_windows) {
1592                                 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1593                                 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
1594                                    WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
1595                         } else {
1596                                 scr->shortcutWindows[widx] = WMCreateArray(4);
1597                                 WMAddToArray(scr->shortcutWindows[widx], wwin);
1598                         }
1599
1600                         wSelectWindow(wwin, !wwin->flags.selected);
1601                         XFlush(dpy);
1602                         wusleep(3000);
1603                         wSelectWindow(wwin, !wwin->flags.selected);
1604                         XFlush(dpy);
1605
1606                 } else if (scr->selected_windows && WMGetArrayItemCount(scr->selected_windows)) {
1607
1608                         if (wwin->flags.selected && scr->selected_windows) {
1609                                 if (scr->shortcutWindows[widx]) {
1610                                         WMFreeArray(scr->shortcutWindows[widx]);
1611                                 }
1612                                 scr->shortcutWindows[widx] = WMDuplicateArray(scr->selected_windows);
1613                         }
1614                 }
1615
1616                 break;
1617
1618         case WKBD_SWITCH_SCREEN:
1619                 if (wScreenCount > 1) {
1620                         WScreen *scr2;
1621                         int i;
1622
1623                         /* find index of this screen */
1624                         for (i = 0; i < wScreenCount; i++) {
1625                                 if (wScreenWithNumber(i) == scr)
1626                                         break;
1627                         }
1628                         i++;
1629                         if (i >= wScreenCount) {
1630                                 i = 0;
1631                         }
1632                         scr2 = wScreenWithNumber(i);
1633
1634                         if (scr2) {
1635                                 XWarpPointer(dpy, scr->root_win, scr2->root_win, 0, 0, 0, 0,
1636                                              scr2->scr_width / 2, scr2->scr_height / 2);
1637                         }
1638                 }
1639                 break;
1640
1641         case WKBD_NEXTWSLAYER:
1642         case WKBD_PREVWSLAYER:
1643                 {
1644                         int row, column;
1645
1646                         row = scr->current_workspace / 10;
1647                         column = scr->current_workspace % 10;
1648
1649                         if (command == WKBD_NEXTWSLAYER) {
1650                                 if ((row + 1) * 10 < scr->workspace_count)
1651                                         wWorkspaceChange(scr, column + (row + 1) * 10);
1652                         } else {
1653                                 if (row > 0)
1654                                         wWorkspaceChange(scr, column + (row - 1) * 10);
1655                         }
1656                 }
1657                 break;
1658         case WKBD_CLIPLOWER:
1659                 if (!wPreferences.flags.noclip)
1660                         wDockLower(scr->workspaces[scr->current_workspace]->clip);
1661                 break;
1662         case WKBD_CLIPRAISE:
1663                 if (!wPreferences.flags.noclip)
1664                         wDockRaise(scr->workspaces[scr->current_workspace]->clip);
1665                 break;
1666         case WKBD_CLIPRAISELOWER:
1667                 if (!wPreferences.flags.noclip)
1668                         wDockRaiseLower(scr->workspaces[scr->current_workspace]->clip);
1669                 break;
1670 #ifdef KEEP_XKB_LOCK_STATUS
1671         case WKBD_TOGGLE:
1672                 if (wPreferences.modelock) {
1673                         /*toggle */
1674                         wwin = scr->focused_window;
1675
1676                         if (wwin && wwin->flags.mapped
1677                             && wwin->frame->workspace == wwin->screen_ptr->current_workspace
1678                             && !wwin->flags.miniaturized && !wwin->flags.hidden) {
1679                                 XkbGetState(dpy, XkbUseCoreKbd, &staterec);
1680
1681                                 wwin->frame->languagemode = wwin->frame->last_languagemode;
1682                                 wwin->frame->last_languagemode = staterec.group;
1683                                 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1684
1685                         }
1686                 }
1687                 break;
1688 #endif                          /* KEEP_XKB_LOCK_STATUS */
1689 #ifdef VIRTUAL_DESKTOP
1690         case WKBD_VDESK_LEFT:
1691                 wWorkspaceKeyboardMoveDesktop(scr, VEC_LEFT);
1692                 break;
1693
1694         case WKBD_VDESK_RIGHT:
1695                 wWorkspaceKeyboardMoveDesktop(scr, VEC_RIGHT);
1696                 break;
1697
1698         case WKBD_VDESK_UP:
1699                 wWorkspaceKeyboardMoveDesktop(scr, VEC_UP);
1700                 break;
1701
1702         case WKBD_VDESK_DOWN:
1703                 wWorkspaceKeyboardMoveDesktop(scr, VEC_DOWN);
1704                 break;
1705 #endif
1706
1707         }
1708 }
1709
1710 static void handleMotionNotify(XEvent * event)
1711 {
1712         WScreen *scr = wScreenForRootWindow(event->xmotion.root);
1713
1714         if (wPreferences.scrollable_menus) {
1715                 WMPoint p = wmkpoint(event->xmotion.x_root, event->xmotion.y_root);
1716                 WMRect rect = wGetRectForHead(scr, wGetHeadForPoint(scr, p));
1717
1718                 if (scr->flags.jump_back_pending ||
1719                     p.x <= (rect.pos.x + 1) ||
1720                     p.x >= (rect.pos.x + rect.size.width - 2) ||
1721                     p.y <= (rect.pos.y + 1) || p.y >= (rect.pos.y + rect.size.height - 2)) {
1722                         WMenu *menu;
1723 #ifdef DEBUG
1724                         printf("pointer at screen edge\n");
1725 #endif
1726                         menu = wMenuUnderPointer(scr);
1727                         if (menu != NULL)
1728                                 wMenuScroll(menu, event);
1729                 }
1730         }
1731 }
1732
1733 static void handleVisibilityNotify(XEvent * event)
1734 {
1735         WWindow *wwin;
1736
1737         wwin = wWindowFor(event->xvisibility.window);
1738         if (!wwin)
1739                 return;
1740         wwin->flags.obscured = (event->xvisibility.state == VisibilityFullyObscured);
1741 }