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