]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
dont iconify fullscreen windows, just recalc stacking orders when focus changes
[mikachu/openbox.git] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    event.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "event.h"
20 #include "debug.h"
21 #include "window.h"
22 #include "openbox.h"
23 #include "dock.h"
24 #include "client.h"
25 #include "xerror.h"
26 #include "prop.h"
27 #include "config.h"
28 #include "screen.h"
29 #include "frame.h"
30 #include "menu.h"
31 #include "menuframe.h"
32 #include "keyboard.h"
33 #include "mouse.h"
34 #include "mainloop.h"
35 #include "framerender.h"
36 #include "focus.h"
37 #include "moveresize.h"
38 #include "group.h"
39 #include "stacking.h"
40 #include "extensions.h"
41
42 #include <X11/Xlib.h>
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
45 #include <glib.h>
46
47 #ifdef HAVE_SYS_SELECT_H
48 #  include <sys/select.h>
49 #endif
50 #ifdef HAVE_SIGNAL_H
51 #  include <signal.h>
52 #endif
53
54 #ifdef USE_SM
55 #include <X11/ICE/ICElib.h>
56 #endif
57
58 typedef struct
59 {
60     gboolean ignored;
61 } ObEventData;
62
63 static void event_process(const XEvent *e, gpointer data);
64 static void event_handle_root(XEvent *e);
65 static void event_handle_menu(XEvent *e);
66 static void event_handle_dock(ObDock *s, XEvent *e);
67 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
68 static void event_handle_client(ObClient *c, XEvent *e);
69 static void event_handle_group(ObGroup *g, XEvent *e);
70
71 static gboolean focus_delay_func(gpointer data);
72 static void focus_delay_client_dest(gpointer data);
73
74 static gboolean menu_hide_delay_func(gpointer data);
75
76 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
77                             (e)->xfocus.detail == NotifyAncestor || \
78                             (e)->xfocus.detail > NotifyNonlinearVirtual)
79 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
80                              (e)->xfocus.detail == NotifyInferior || \
81                              (e)->xfocus.detail == NotifyAncestor || \
82                              (e)->xfocus.detail > NotifyNonlinearVirtual)
83
84 Time event_lasttime = 0;
85
86 /*! The value of the mask for the NumLock modifier */
87 unsigned int NumLockMask;
88 /*! The value of the mask for the ScrollLock modifier */
89 unsigned int ScrollLockMask;
90 /*! The key codes for the modifier keys */
91 static XModifierKeymap *modmap;
92 /*! Table of the constant modifier masks */
93 static const int mask_table[] = {
94     ShiftMask, LockMask, ControlMask, Mod1Mask,
95     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
96 };
97 static int mask_table_size;
98
99 static guint ignore_enter_focus = 0;
100
101 static gboolean menu_can_hide;
102
103 #ifdef USE_SM
104 static void ice_handler(int fd, gpointer conn)
105 {
106     Bool b;
107     IceProcessMessages(conn, NULL, &b);
108 }
109
110 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
111                       IcePointer *watch_data)
112 {
113     static gint fd = -1;
114
115     if (opening) {
116         fd = IceConnectionNumber(conn);
117         ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
118     } else {
119         ob_main_loop_fd_remove(ob_main_loop, fd);
120         fd = -1;
121     }
122 }
123 #endif
124
125 void event_startup(gboolean reconfig)
126 {
127     if (reconfig) return;
128
129     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
130      
131     /* get lock masks that are defined by the display (not constant) */
132     modmap = XGetModifierMapping(ob_display);
133     g_assert(modmap);
134     if (modmap && modmap->max_keypermod > 0) {
135         size_t cnt;
136         const size_t size = mask_table_size * modmap->max_keypermod;
137         /* get the values of the keyboard lock modifiers
138            Note: Caps lock is not retrieved the same way as Scroll and Num
139            lock since it doesn't need to be. */
140         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
141         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
142                                                      XK_Scroll_Lock);
143           
144         for (cnt = 0; cnt < size; ++cnt) {
145             if (! modmap->modifiermap[cnt]) continue;
146                
147             if (num_lock == modmap->modifiermap[cnt])
148                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
149             if (scroll_lock == modmap->modifiermap[cnt])
150                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
151         }
152     }
153
154     ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
155
156 #ifdef USE_SM
157     IceAddConnectionWatch(ice_watch, NULL);
158 #endif
159
160     client_add_destructor(focus_delay_client_dest);
161 }
162
163 void event_shutdown(gboolean reconfig)
164 {
165     if (reconfig) return;
166
167 #ifdef USE_SM
168     IceRemoveConnectionWatch(ice_watch, NULL);
169 #endif
170
171     client_remove_destructor(focus_delay_client_dest);
172     XFreeModifiermap(modmap);
173 }
174
175 static Window event_get_window(XEvent *e)
176 {
177     Window window;
178
179     /* pick a window */
180     switch (e->type) {
181     case SelectionClear:
182         window = RootWindow(ob_display, ob_screen);
183         break;
184     case MapRequest:
185         window = e->xmap.window;
186         break;
187     case UnmapNotify:
188         window = e->xunmap.window;
189         break;
190     case DestroyNotify:
191         window = e->xdestroywindow.window;
192         break;
193     case ConfigureRequest:
194         window = e->xconfigurerequest.window;
195         break;
196     case ConfigureNotify:
197         window = e->xconfigure.window;
198         break;
199     default:
200 #ifdef XKB
201         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
202             switch (((XkbAnyEvent*)e)->xkb_type) {
203             case XkbBellNotify:
204                 window = ((XkbBellNotifyEvent*)e)->window;
205             default:
206                 window = None;
207             }
208         } else
209 #endif
210             window = e->xany.window;
211     }
212     return window;
213 }
214
215 static void event_set_lasttime(XEvent *e)
216 {
217     Time t = 0;
218
219     /* grab the lasttime and hack up the state */
220     switch (e->type) {
221     case ButtonPress:
222     case ButtonRelease:
223         t = e->xbutton.time;
224         break;
225     case KeyPress:
226         t = e->xkey.time;
227         break;
228     case KeyRelease:
229         t = e->xkey.time;
230         break;
231     case MotionNotify:
232         t = e->xmotion.time;
233         break;
234     case PropertyNotify:
235         t = e->xproperty.time;
236         break;
237     case EnterNotify:
238     case LeaveNotify:
239         t = e->xcrossing.time;
240         break;
241     default:
242         /* if more event types are anticipated, get their timestamp
243            explicitly */
244         break;
245     }
246
247     if (t > event_lasttime)
248         event_lasttime = t;
249 }
250
251 #define STRIP_MODS(s) \
252         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
253         /* kill off the Button1Mask etc, only want the modifiers */ \
254         s &= (ControlMask | ShiftMask | Mod1Mask | \
255               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
256
257 static void event_hack_mods(XEvent *e)
258 {
259     KeyCode *kp;
260     int i, k;
261
262     switch (e->type) {
263     case ButtonPress:
264     case ButtonRelease:
265         STRIP_MODS(e->xbutton.state);
266         break;
267     case KeyPress:
268         STRIP_MODS(e->xkey.state);
269         break;
270     case KeyRelease:
271         STRIP_MODS(e->xkey.state);
272         /* remove from the state the mask of the modifier being released, if
273            it is a modifier key being released (this is a little ugly..) */
274         kp = modmap->modifiermap;
275         for (i = 0; i < mask_table_size; ++i) {
276             for (k = 0; k < modmap->max_keypermod; ++k) {
277                 if (*kp == e->xkey.keycode) { /* found the keycode */
278                     /* remove the mask for it */
279                     e->xkey.state &= ~mask_table[i];
280                     /* cause the first loop to break; */
281                     i = mask_table_size;
282                     break; /* get outta here! */
283                 }
284                 ++kp;
285             }
286         }
287         break;
288     case MotionNotify:
289         STRIP_MODS(e->xmotion.state);
290         /* compress events */
291         {
292             XEvent ce;
293             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
294                                           e->type, &ce)) {
295                 e->xmotion.x_root = ce.xmotion.x_root;
296                 e->xmotion.y_root = ce.xmotion.y_root;
297             }
298         }
299         break;
300     }
301 }
302
303 static gboolean event_ignore(XEvent *e, ObClient *client)
304 {
305     switch(e->type) {
306     case EnterNotify:
307     case LeaveNotify:
308         if (e->xcrossing.detail == NotifyInferior)
309             return TRUE;
310         break;
311     case FocusIn:
312         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
313            because of RevertToPointerRoot. If the focus ends up reverting to
314            pointer root on a workspace change, then the FocusIn event that we
315            want will be of type NotifyAncestor. This situation does not occur
316            for FocusOut, so it is safely ignored there.
317         */
318         if (INVALID_FOCUSIN(e) ||
319             client == NULL) {
320 #ifdef DEBUG_FOCUS
321             ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
322                      e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
323 #endif
324             /* says a client was not found for the event (or a valid FocusIn
325                event was not found.
326             */
327             e->xfocus.window = None;
328             return TRUE;
329         }
330
331 #ifdef DEBUG_FOCUS
332         ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
333                  e->xfocus.mode, e->xfocus.detail);
334 #endif
335         break;
336     case FocusOut:
337         if (INVALID_FOCUSOUT(e)) {
338 #ifdef DEBUG_FOCUS
339         ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
340                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
341 #endif
342             return TRUE;
343         }
344
345 #ifdef DEBUG_FOCUS
346         ob_debug("FocusOut on %lx mode %d detail %d\n",
347                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
348 #endif
349
350         {
351             XEvent fe;
352             gboolean fallback = TRUE;
353
354             while (TRUE) {
355                 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
356                                             FocusOut, &fe))
357                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
358                         break;
359                 if (fe.type == FocusOut) {
360 #ifdef DEBUG_FOCUS
361                     ob_debug("found pending FocusOut\n");
362 #endif
363                     if (!INVALID_FOCUSOUT(&fe)) {
364                         /* if there is a VALID FocusOut still coming, don't
365                            fallback focus yet, we'll deal with it then */
366                         XPutBackEvent(ob_display, &fe);
367                         fallback = FALSE;
368                         break;
369                     }
370                 } else {
371 #ifdef DEBUG_FOCUS
372                     ob_debug("found pending FocusIn\n");
373 #endif
374                     /* is the focused window getting a FocusOut/In back to
375                        itself?
376                     */
377                     if (fe.xfocus.window == e->xfocus.window &&
378                         !event_ignore(&fe, client)) {
379                         /*
380                           if focus_client is not set, then we can't do
381                           this. we need the FocusIn. This happens in the
382                           case when the set_focus_client(NULL) in the
383                           focus_fallback function fires and then
384                           focus_fallback picks the currently focused
385                           window (such as on a SendToDesktop-esque action.
386                         */
387                         if (focus_client) {
388 #ifdef DEBUG_FOCUS
389                             ob_debug("focused window got an Out/In back to "
390                                      "itself IGNORED both\n");
391 #endif
392                             return TRUE;
393                         } else {
394                             event_process(&fe, NULL);
395 #ifdef DEBUG_FOCUS
396                             ob_debug("focused window got an Out/In back to "
397                                      "itself but focus_client was null "
398                                      "IGNORED just the Out\n");
399 #endif
400                             return TRUE;
401                         }
402                     }
403
404                     {
405                         ObEventData d;
406
407                         /* once all the FocusOut's have been dealt with, if
408                            there is a FocusIn still left and it is valid, then
409                            use it */
410                         event_process(&fe, &d);
411                         if (!d.ignored) {
412 #ifdef DEBUG_FOCUS
413                             ob_debug("FocusIn was OK, so don't fallback\n");
414 #endif
415                             fallback = FALSE;
416                             break;
417                         }
418                     }
419                 }
420             }
421             if (fallback) {
422 #ifdef DEBUG_FOCUS
423                 ob_debug("no valid FocusIn and no FocusOut events found, "
424                          "falling back\n");
425 #endif
426                 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
427             }
428         }
429         break;
430     }
431     return FALSE;
432 }
433
434 static void event_process(const XEvent *ec, gpointer data)
435 {
436     Window window;
437     ObGroup *group = NULL;
438     ObClient *client = NULL;
439     ObDock *dock = NULL;
440     ObDockApp *dockapp = NULL;
441     ObWindow *obwin = NULL;
442     XEvent ee, *e;
443     ObEventData *ed = data;
444
445     /* make a copy we can mangle */
446     ee = *ec;
447     e = &ee;
448
449     window = event_get_window(e);
450     if (!(e->type == PropertyNotify &&
451           (group = g_hash_table_lookup(group_map, &window))))
452         if ((obwin = g_hash_table_lookup(window_map, &window))) {
453             switch (obwin->type) {
454             case Window_Dock:
455                 dock = WINDOW_AS_DOCK(obwin);
456                 break;
457             case Window_DockApp:
458                 dockapp = WINDOW_AS_DOCKAPP(obwin);
459                 break;
460             case Window_Client:
461                 client = WINDOW_AS_CLIENT(obwin);
462                 break;
463             case Window_Menu:
464             case Window_Internal:
465                 /* not to be used for events */
466                 g_assert_not_reached();
467                 break;
468             }
469         }
470
471     event_set_lasttime(e);
472     event_hack_mods(e);
473     if (event_ignore(e, client)) {
474         if (ed)
475             ed->ignored = TRUE;
476         return;
477     } else if (ed)
478             ed->ignored = FALSE;
479
480     /* deal with it in the kernel */
481     if (group)
482         event_handle_group(group, e);
483     else if (client)
484         event_handle_client(client, e);
485     else if (dockapp)
486         event_handle_dockapp(dockapp, e);
487     else if (dock)
488         event_handle_dock(dock, e);
489     else if (window == RootWindow(ob_display, ob_screen))
490         event_handle_root(e);
491     else if (e->type == MapRequest)
492         client_manage(window);
493     else if (e->type == ConfigureRequest) {
494         /* unhandled configure requests must be used to configure the
495            window directly */
496         XWindowChanges xwc;
497                
498         xwc.x = e->xconfigurerequest.x;
499         xwc.y = e->xconfigurerequest.y;
500         xwc.width = e->xconfigurerequest.width;
501         xwc.height = e->xconfigurerequest.height;
502         xwc.border_width = e->xconfigurerequest.border_width;
503         xwc.sibling = e->xconfigurerequest.above;
504         xwc.stack_mode = e->xconfigurerequest.detail;
505        
506         /* we are not to be held responsible if someone sends us an
507            invalid request! */
508         xerror_set_ignore(TRUE);
509         XConfigureWindow(ob_display, window,
510                          e->xconfigurerequest.value_mask, &xwc);
511         xerror_set_ignore(FALSE);
512     }
513
514     /* user input (action-bound) events */
515     if (e->type == ButtonPress || e->type == ButtonRelease ||
516         e->type == MotionNotify || e->type == KeyPress ||
517         e->type == KeyRelease)
518     {
519         if (menu_frame_visible)
520             event_handle_menu(e);
521         else {
522             if (!keyboard_process_interactive_grab(e, &client)) {
523                 if (moveresize_in_progress) {
524                     moveresize_event(e);
525
526                     /* make further actions work on the client being
527                        moved/resized */
528                     client = moveresize_client;
529                 }
530
531                 menu_can_hide = FALSE;
532                 ob_main_loop_timeout_add(ob_main_loop,
533                                          G_USEC_PER_SEC / 4,
534                                          menu_hide_delay_func,
535                                          NULL, NULL);
536
537                 if (e->type == ButtonPress || e->type == ButtonRelease ||
538                     e->type == MotionNotify)
539                     mouse_event(client, e);
540                 else if (e->type == KeyPress)
541                     /* when in the middle of a focus cycling action, this
542                        causes the window which appears to be focused to be
543                        the one on which the actions will be executed */
544                     keyboard_event((focus_cycle_target ?
545                                     focus_cycle_target : client), e);
546             }
547         }
548     }
549 }
550
551 static void event_handle_root(XEvent *e)
552 {
553     Atom msgtype;
554      
555     switch(e->type) {
556     case SelectionClear:
557         ob_debug("Another WM has requested to replace us. Exiting.\n");
558         ob_exit(0);
559         break;
560
561     case ClientMessage:
562         if (e->xclient.format != 32) break;
563
564         msgtype = e->xclient.message_type;
565         if (msgtype == prop_atoms.net_current_desktop) {
566             unsigned int d = e->xclient.data.l[0];
567             if (d < screen_num_desktops)
568                 screen_set_desktop(d);
569         } else if (msgtype == prop_atoms.net_number_of_desktops) {
570             unsigned int d = e->xclient.data.l[0];
571             if (d > 0)
572                 screen_set_num_desktops(d);
573         } else if (msgtype == prop_atoms.net_showing_desktop) {
574             screen_show_desktop(e->xclient.data.l[0] != 0);
575         }
576         break;
577     case PropertyNotify:
578         if (e->xproperty.atom == prop_atoms.net_desktop_names)
579             screen_update_desktop_names();
580         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
581             screen_update_layout();
582         break;
583     case ConfigureNotify:
584 #ifdef XRANDR
585         XRRUpdateConfiguration(e);
586 #endif
587         screen_resize();
588         break;
589     default:
590         ;
591 #ifdef VIDMODE
592         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
593             ob_debug("VIDMODE EVENT\n");
594         }
595 #endif
596     }
597 }
598
599 static void event_handle_group(ObGroup *group, XEvent *e)
600 {
601     GSList *it;
602
603     g_assert(e->type == PropertyNotify);
604
605     for (it = group->members; it; it = g_slist_next(it))
606         event_handle_client(it->data, e);
607 }
608
609 void event_enter_client(ObClient *client)
610 {
611     g_assert(config_focus_follow);
612
613     if (client_normal(client) && client_can_focus(client)) {
614         if (config_focus_delay) {
615             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
616             ob_main_loop_timeout_add(ob_main_loop,
617                                      config_focus_delay,
618                                      focus_delay_func,
619                                      client, NULL);
620         } else
621             focus_delay_func(client);
622     }
623 }
624
625 static void event_handle_client(ObClient *client, XEvent *e)
626 {
627     XEvent ce;
628     Atom msgtype;
629     int i=0;
630     ObFrameContext con;
631      
632     switch (e->type) {
633     case VisibilityNotify:
634         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
635         break;
636     case ButtonPress:
637     case ButtonRelease:
638         /* Wheel buttons don't draw because they are an instant click, so it
639            is a waste of resources to go drawing it. */
640         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
641             con = frame_context(client, e->xbutton.window);
642             con = mouse_button_frame_context(con, e->xbutton.button);
643             switch (con) {
644             case OB_FRAME_CONTEXT_MAXIMIZE:
645                 client->frame->max_press = (e->type == ButtonPress);
646                 framerender_frame(client->frame);
647                 break;
648             case OB_FRAME_CONTEXT_CLOSE:
649                 client->frame->close_press = (e->type == ButtonPress);
650                 framerender_frame(client->frame);
651                 break;
652             case OB_FRAME_CONTEXT_ICONIFY:
653                 client->frame->iconify_press = (e->type == ButtonPress);
654                 framerender_frame(client->frame);
655                 break;
656             case OB_FRAME_CONTEXT_ALLDESKTOPS:
657                 client->frame->desk_press = (e->type == ButtonPress);
658                 framerender_frame(client->frame);
659                 break; 
660             case OB_FRAME_CONTEXT_SHADE:
661                 client->frame->shade_press = (e->type == ButtonPress);
662                 framerender_frame(client->frame);
663                 break;
664             default:
665                 /* nothing changes with clicks for any other contexts */
666                 break;
667             }
668         }
669         break;
670     case FocusIn:
671 #ifdef DEBUG_FOCUS
672         ob_debug("FocusIn on client for %lx\n", client->window);
673 #endif
674         if (client != focus_client) {
675             focus_set_client(client);
676             frame_adjust_focus(client->frame, TRUE);
677             client_calc_layer(client);
678         }
679         break;
680     case FocusOut:
681 #ifdef DEBUG_FOCUS
682         ob_debug("FocusOut on client for %lx\n", client->window);
683 #endif
684         frame_adjust_focus(client->frame, FALSE);
685         client_calc_layer(client);
686         break;
687     case LeaveNotify:
688         con = frame_context(client, e->xcrossing.window);
689         switch (con) {
690         case OB_FRAME_CONTEXT_MAXIMIZE:
691             client->frame->max_hover = FALSE;
692             frame_adjust_state(client->frame);
693             break;
694         case OB_FRAME_CONTEXT_ALLDESKTOPS:
695             client->frame->desk_hover = FALSE;
696             frame_adjust_state(client->frame);
697             break;
698         case OB_FRAME_CONTEXT_SHADE:
699             client->frame->shade_hover = FALSE;
700             frame_adjust_state(client->frame);
701             break;
702         case OB_FRAME_CONTEXT_ICONIFY:
703             client->frame->iconify_hover = FALSE;
704             frame_adjust_state(client->frame);
705             break;
706         case OB_FRAME_CONTEXT_CLOSE:
707             client->frame->close_hover = FALSE;
708             frame_adjust_state(client->frame);
709             break;
710         case OB_FRAME_CONTEXT_FRAME:
711             /*
712             if (config_focus_follow && config_focus_delay)
713                 ob_main_loop_timeout_remove_data(ob_main_loop,
714                                                  focus_delay_func,
715                                                  client);
716             */
717             break;
718         default:
719             break;
720         }
721         break;
722     case EnterNotify:
723     {
724         gboolean nofocus = FALSE;
725
726         if (ignore_enter_focus) {
727             ignore_enter_focus--;
728             nofocus = TRUE;
729         }
730
731         con = frame_context(client, e->xcrossing.window);
732         switch (con) {
733         case OB_FRAME_CONTEXT_MAXIMIZE:
734             client->frame->max_hover = TRUE;
735             frame_adjust_state(client->frame);
736             break;
737         case OB_FRAME_CONTEXT_ALLDESKTOPS:
738             client->frame->desk_hover = TRUE;
739             frame_adjust_state(client->frame);
740             break;
741         case OB_FRAME_CONTEXT_SHADE:
742             client->frame->shade_hover = TRUE;
743             frame_adjust_state(client->frame);
744             break;
745         case OB_FRAME_CONTEXT_ICONIFY:
746             client->frame->iconify_hover = TRUE;
747             frame_adjust_state(client->frame);
748             break;
749         case OB_FRAME_CONTEXT_CLOSE:
750             client->frame->close_hover = TRUE;
751             frame_adjust_state(client->frame);
752             break;
753         case OB_FRAME_CONTEXT_FRAME:
754             if (e->xcrossing.mode == NotifyGrab ||
755                 e->xcrossing.mode == NotifyUngrab)
756             {
757 #ifdef DEBUG_FOCUS
758                 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
759                          (e->type == EnterNotify ? "Enter" : "Leave"),
760                          e->xcrossing.mode,
761                          e->xcrossing.detail, client?client->window:0);
762 #endif
763             } else {
764 #ifdef DEBUG_FOCUS
765                 ob_debug("%sNotify mode %d detail %d on %lx, "
766                          "focusing window\n",
767                          (e->type == EnterNotify ? "Enter" : "Leave"),
768                          e->xcrossing.mode,
769                          e->xcrossing.detail, client?client->window:0);
770 #endif
771                 if (!nofocus && config_focus_follow)
772                     event_enter_client(client);
773             }
774             break;
775         default:
776             break;
777         }
778         break;
779     }
780     case ConfigureRequest:
781         /* compress these */
782         while (XCheckTypedWindowEvent(ob_display, client->window,
783                                       ConfigureRequest, &ce)) {
784             ++i;
785             /* XXX if this causes bad things.. we can compress config req's
786                with the same mask. */
787             e->xconfigurerequest.value_mask |=
788                 ce.xconfigurerequest.value_mask;
789             if (ce.xconfigurerequest.value_mask & CWX)
790                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
791             if (ce.xconfigurerequest.value_mask & CWY)
792                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
793             if (ce.xconfigurerequest.value_mask & CWWidth)
794                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
795             if (ce.xconfigurerequest.value_mask & CWHeight)
796                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
797             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
798                 e->xconfigurerequest.border_width =
799                     ce.xconfigurerequest.border_width;
800             if (ce.xconfigurerequest.value_mask & CWStackMode)
801                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
802         }
803
804         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
805         if (client->iconic || client->shaded) return;
806
807         /* resize, then move, as specified in the EWMH section 7.7 */
808         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
809                                                CWX | CWY |
810                                                CWBorderWidth)) {
811             int x, y, w, h;
812             ObCorner corner;
813
814             if (e->xconfigurerequest.value_mask & CWBorderWidth)
815                 client->border_width = e->xconfigurerequest.border_width;
816
817             x = (e->xconfigurerequest.value_mask & CWX) ?
818                 e->xconfigurerequest.x : client->area.x;
819             y = (e->xconfigurerequest.value_mask & CWY) ?
820                 e->xconfigurerequest.y : client->area.y;
821             w = (e->xconfigurerequest.value_mask & CWWidth) ?
822                 e->xconfigurerequest.width : client->area.width;
823             h = (e->xconfigurerequest.value_mask & CWHeight) ?
824                 e->xconfigurerequest.height : client->area.height;
825
826             {
827                 int newx = x;
828                 int newy = y;
829                 int fw = w +
830                     client->frame->size.left + client->frame->size.right;
831                 int fh = h +
832                     client->frame->size.top + client->frame->size.bottom;
833                 client_find_onscreen(client, &newx, &newy, fw, fh,
834                                      client_normal(client));
835                 if (e->xconfigurerequest.value_mask & CWX)
836                     x = newx;
837                 if (e->xconfigurerequest.value_mask & CWY)
838                     y = newy;
839             }
840                
841             switch (client->gravity) {
842             case NorthEastGravity:
843             case EastGravity:
844                 corner = OB_CORNER_TOPRIGHT;
845                 break;
846             case SouthWestGravity:
847             case SouthGravity:
848                 corner = OB_CORNER_BOTTOMLEFT;
849                 break;
850             case SouthEastGravity:
851                 corner = OB_CORNER_BOTTOMRIGHT;
852                 break;
853             default:     /* NorthWest, Static, etc */
854                 corner = OB_CORNER_TOPLEFT;
855             }
856
857             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
858                                   TRUE);
859         }
860
861         if (e->xconfigurerequest.value_mask & CWStackMode) {
862             switch (e->xconfigurerequest.detail) {
863             case Below:
864             case BottomIf:
865                 stacking_lower(CLIENT_AS_WINDOW(client));
866                 break;
867
868             case Above:
869             case TopIf:
870             default:
871                 stacking_raise(CLIENT_AS_WINDOW(client));
872                 break;
873             }
874         }
875         break;
876     case UnmapNotify:
877         if (client->ignore_unmaps) {
878             client->ignore_unmaps--;
879             break;
880         }
881         client_unmanage(client);
882         break;
883     case DestroyNotify:
884         client_unmanage(client);
885         break;
886     case ReparentNotify:
887         /* this is when the client is first taken captive in the frame */
888         if (e->xreparent.parent == client->frame->plate) break;
889
890         /*
891           This event is quite rare and is usually handled in unmapHandler.
892           However, if the window is unmapped when the reparent event occurs,
893           the window manager never sees it because an unmap event is not sent
894           to an already unmapped window.
895         */
896
897         /* we don't want the reparent event, put it back on the stack for the
898            X server to deal with after we unmanage the window */
899         XPutBackEvent(ob_display, e);
900      
901         client_unmanage(client);
902         break;
903     case MapRequest:
904         ob_debug("MapRequest for 0x%lx\n", client->window);
905         if (!client->iconic) break; /* this normally doesn't happen, but if it
906                                        does, we don't want it! */
907         if (screen_showing_desktop)
908             screen_show_desktop(FALSE);
909         client_iconify(client, FALSE, TRUE);
910         if (!client->frame->visible)
911             /* if its not visible still, then don't mess with it */
912             break;
913         if (client->shaded)
914             client_shade(client, FALSE);
915         client_focus(client);
916         stacking_raise(CLIENT_AS_WINDOW(client));
917         break;
918     case ClientMessage:
919         /* validate cuz we query stuff off the client here */
920         if (!client_validate(client)) break;
921   
922         if (e->xclient.format != 32) return;
923
924         msgtype = e->xclient.message_type;
925         if (msgtype == prop_atoms.wm_change_state) {
926             /* compress changes into a single change */
927             while (XCheckTypedWindowEvent(ob_display, client->window,
928                                           e->type, &ce)) {
929                 /* XXX: it would be nice to compress ALL messages of a
930                    type, not just messages in a row without other
931                    message types between. */
932                 if (ce.xclient.message_type != msgtype) {
933                     XPutBackEvent(ob_display, &ce);
934                     break;
935                 }
936                 e->xclient = ce.xclient;
937             }
938             client_set_wm_state(client, e->xclient.data.l[0]);
939         } else if (msgtype == prop_atoms.net_wm_desktop) {
940             /* compress changes into a single change */
941             while (XCheckTypedWindowEvent(ob_display, client->window,
942                                           e->type, &ce)) {
943                 /* XXX: it would be nice to compress ALL messages of a
944                    type, not just messages in a row without other
945                    message types between. */
946                 if (ce.xclient.message_type != msgtype) {
947                     XPutBackEvent(ob_display, &ce);
948                     break;
949                 }
950                 e->xclient = ce.xclient;
951             }
952             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
953                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
954                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
955                                    FALSE);
956         } else if (msgtype == prop_atoms.net_wm_state) {
957             /* can't compress these */
958             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
959                      (e->xclient.data.l[0] == 0 ? "Remove" :
960                       e->xclient.data.l[0] == 1 ? "Add" :
961                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
962                      e->xclient.data.l[1], e->xclient.data.l[2],
963                      client->window);
964             client_set_state(client, e->xclient.data.l[0],
965                              e->xclient.data.l[1], e->xclient.data.l[2]);
966         } else if (msgtype == prop_atoms.net_close_window) {
967             ob_debug("net_close_window for 0x%lx\n", client->window);
968             client_close(client);
969         } else if (msgtype == prop_atoms.net_active_window) {
970             ob_debug("net_active_window for 0x%lx\n", client->window);
971             client_activate(client, FALSE);
972         } else if (msgtype == prop_atoms.net_wm_moveresize) {
973             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
974             if ((Atom)e->xclient.data.l[2] ==
975                 prop_atoms.net_wm_moveresize_size_topleft ||
976                 (Atom)e->xclient.data.l[2] ==
977                 prop_atoms.net_wm_moveresize_size_top ||
978                 (Atom)e->xclient.data.l[2] ==
979                 prop_atoms.net_wm_moveresize_size_topright ||
980                 (Atom)e->xclient.data.l[2] ==
981                 prop_atoms.net_wm_moveresize_size_right ||
982                 (Atom)e->xclient.data.l[2] ==
983                 prop_atoms.net_wm_moveresize_size_right ||
984                 (Atom)e->xclient.data.l[2] ==
985                 prop_atoms.net_wm_moveresize_size_bottomright ||
986                 (Atom)e->xclient.data.l[2] ==
987                 prop_atoms.net_wm_moveresize_size_bottom ||
988                 (Atom)e->xclient.data.l[2] ==
989                 prop_atoms.net_wm_moveresize_size_bottomleft ||
990                 (Atom)e->xclient.data.l[2] ==
991                 prop_atoms.net_wm_moveresize_size_left ||
992                 (Atom)e->xclient.data.l[2] ==
993                 prop_atoms.net_wm_moveresize_move ||
994                 (Atom)e->xclient.data.l[2] ==
995                 prop_atoms.net_wm_moveresize_size_keyboard ||
996                 (Atom)e->xclient.data.l[2] ==
997                 prop_atoms.net_wm_moveresize_move_keyboard) {
998
999                 moveresize_start(client, e->xclient.data.l[0],
1000                                  e->xclient.data.l[1], e->xclient.data.l[3],
1001                                  e->xclient.data.l[2]);
1002             }
1003         } else if (msgtype == prop_atoms.net_moveresize_window) {
1004             int oldg = client->gravity;
1005             int tmpg, x, y, w, h;
1006
1007             if (e->xclient.data.l[0] & 0xff)
1008                 tmpg = e->xclient.data.l[0] & 0xff;
1009             else
1010                 tmpg = oldg;
1011
1012             if (e->xclient.data.l[0] & 1 << 8)
1013                 x = e->xclient.data.l[1];
1014             else
1015                 x = client->area.x;
1016             if (e->xclient.data.l[0] & 1 << 9)
1017                 y = e->xclient.data.l[2];
1018             else
1019                 y = client->area.y;
1020             if (e->xclient.data.l[0] & 1 << 10)
1021                 w = e->xclient.data.l[3];
1022             else
1023                 w = client->area.width;
1024             if (e->xclient.data.l[0] & 1 << 11)
1025                 h = e->xclient.data.l[4];
1026             else
1027                 h = client->area.height;
1028             client->gravity = tmpg;
1029
1030             {
1031                 int newx = x;
1032                 int newy = y;
1033                 int fw = w +
1034                     client->frame->size.left + client->frame->size.right;
1035                 int fh = h +
1036                     client->frame->size.top + client->frame->size.bottom;
1037                 client_find_onscreen(client, &newx, &newy, fw, fh,
1038                                      client_normal(client));
1039                 if (e->xclient.data.l[0] & 1 << 8)
1040                     x = newx;
1041                 if (e->xclient.data.l[0] & 1 << 9)
1042                     y = newy;
1043             }
1044                
1045             client_configure(client, OB_CORNER_TOPLEFT,
1046                              x, y, w, h, FALSE, TRUE);
1047
1048             client->gravity = oldg;
1049         }
1050         break;
1051     case PropertyNotify:
1052         /* validate cuz we query stuff off the client here */
1053         if (!client_validate(client)) break;
1054   
1055         /* compress changes to a single property into a single change */
1056         while (XCheckTypedWindowEvent(ob_display, client->window,
1057                                       e->type, &ce)) {
1058             Atom a, b;
1059
1060             /* XXX: it would be nice to compress ALL changes to a property,
1061                not just changes in a row without other props between. */
1062
1063             a = ce.xproperty.atom;
1064             b = e->xproperty.atom;
1065
1066             if (a == b)
1067                 continue;
1068             if ((a == prop_atoms.net_wm_name ||
1069                  a == prop_atoms.wm_name ||
1070                  a == prop_atoms.net_wm_icon_name ||
1071                  a == prop_atoms.wm_icon_name)
1072                 &&
1073                 (b == prop_atoms.net_wm_name ||
1074                  b == prop_atoms.wm_name ||
1075                  b == prop_atoms.net_wm_icon_name ||
1076                  b == prop_atoms.wm_icon_name)) {
1077                 continue;
1078             }
1079             if ((a == prop_atoms.net_wm_icon ||
1080                  a == prop_atoms.kwm_win_icon)
1081                 &&
1082                 (b == prop_atoms.net_wm_icon ||
1083                  b == prop_atoms.kwm_win_icon))
1084                 continue;
1085
1086             XPutBackEvent(ob_display, &ce);
1087             break;
1088         }
1089
1090         msgtype = e->xproperty.atom;
1091         if (msgtype == XA_WM_NORMAL_HINTS) {
1092             client_update_normal_hints(client);
1093             /* normal hints can make a window non-resizable */
1094             client_setup_decor_and_functions(client);
1095         } else if (msgtype == XA_WM_HINTS) {
1096             client_update_wmhints(client);
1097         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1098             client_update_transient_for(client);
1099             client_get_type(client);
1100             /* type may have changed, so update the layer */
1101             client_calc_layer(client);
1102             client_setup_decor_and_functions(client);
1103         } else if (msgtype == prop_atoms.net_wm_name ||
1104                    msgtype == prop_atoms.wm_name ||
1105                    msgtype == prop_atoms.net_wm_icon_name ||
1106                    msgtype == prop_atoms.wm_icon_name) {
1107             client_update_title(client);
1108         } else if (msgtype == prop_atoms.wm_class) {
1109             client_update_class(client);
1110         } else if (msgtype == prop_atoms.wm_protocols) {
1111             client_update_protocols(client);
1112             client_setup_decor_and_functions(client);
1113         }
1114         else if (msgtype == prop_atoms.net_wm_strut) {
1115             client_update_strut(client);
1116         }
1117         else if (msgtype == prop_atoms.net_wm_icon ||
1118                  msgtype == prop_atoms.kwm_win_icon) {
1119             client_update_icons(client);
1120         }
1121         else if (msgtype == prop_atoms.sm_client_id) {
1122             client_update_sm_client_id(client);
1123         }
1124     default:
1125         ;
1126 #ifdef SHAPE
1127         if (extensions_shape && e->type == extensions_shape_event_basep) {
1128             client->shaped = ((XShapeEvent*)e)->shaped;
1129             frame_adjust_shape(client->frame);
1130         }
1131 #endif
1132     }
1133 }
1134
1135 static void event_handle_dock(ObDock *s, XEvent *e)
1136 {
1137     switch (e->type) {
1138     case ButtonPress:
1139         stacking_raise(DOCK_AS_WINDOW(s));
1140         break;
1141     case EnterNotify:
1142         dock_hide(FALSE);
1143         break;
1144     case LeaveNotify:
1145         dock_hide(TRUE);
1146         break;
1147     }
1148 }
1149
1150 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1151 {
1152     switch (e->type) {
1153     case MotionNotify:
1154         dock_app_drag(app, &e->xmotion);
1155         break;
1156     case UnmapNotify:
1157         if (app->ignore_unmaps) {
1158             app->ignore_unmaps--;
1159             break;
1160         }
1161         dock_remove(app, TRUE);
1162         break;
1163     case DestroyNotify:
1164         dock_remove(app, FALSE);
1165         break;
1166     case ReparentNotify:
1167         dock_remove(app, FALSE);
1168         break;
1169     case ConfigureNotify:
1170         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1171         break;
1172     }
1173 }
1174
1175 ObMenuFrame* find_active_menu()
1176 {
1177     GList *it;
1178     ObMenuFrame *f;
1179
1180     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1181         f = it->data;
1182         if (f->selected)
1183             break;
1184     }
1185     return it ? it->data : NULL;
1186 }
1187
1188 static void event_handle_menu(XEvent *ev)
1189 {
1190     ObMenuFrame *f;
1191     ObMenuEntryFrame *e;
1192
1193     switch (ev->type) {
1194     case ButtonRelease:
1195         if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1196                                         ev->xbutton.y_root)))
1197             menu_entry_frame_execute(e, ev->xbutton.state);
1198         else if (menu_can_hide)
1199             menu_frame_hide_all();
1200         break;
1201     case MotionNotify:
1202         if ((f = menu_frame_under(ev->xmotion.x_root,
1203                                   ev->xmotion.y_root))) {
1204             menu_frame_move_on_screen(f);
1205             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1206                                             ev->xmotion.y_root)))
1207                 menu_frame_select(f, e);
1208         }
1209         {
1210             ObMenuFrame *a;
1211
1212             a = find_active_menu();
1213             if (a && a != f &&
1214                 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1215             {
1216                 menu_frame_select(a, NULL);
1217             }
1218         }
1219         break;
1220     case KeyPress:
1221         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1222             menu_frame_hide_all();
1223         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1224             ObMenuFrame *f;
1225             if ((f = find_active_menu()))
1226                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1227         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1228             ObMenuFrame *f;
1229             if ((f = find_active_menu()) && f->parent)
1230                 menu_frame_select(f, NULL);
1231         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1232             ObMenuFrame *f;
1233             if ((f = find_active_menu()) && f->child)
1234                 menu_frame_select_next(f->child);
1235         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1236             ObMenuFrame *f;
1237             if ((f = find_active_menu()))
1238                 menu_frame_select_previous(f);
1239         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1240             ObMenuFrame *f;
1241             if ((f = find_active_menu()))
1242                 menu_frame_select_next(f);
1243         }
1244         break;
1245     }
1246 }
1247
1248 static gboolean menu_hide_delay_func(gpointer data)
1249 {
1250     menu_can_hide = TRUE;
1251     return FALSE; /* no repeat */
1252 }
1253
1254 static gboolean focus_delay_func(gpointer data)
1255 {
1256     ObClient *c = data;
1257
1258     client_focus(c);
1259     if (config_focus_raise)
1260         stacking_raise(CLIENT_AS_WINDOW(c));
1261     return FALSE; /* no repeat */
1262 }
1263
1264 static void focus_delay_client_dest(gpointer data)
1265 {
1266     ObClient *c = data;
1267
1268     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, c);
1269 }
1270
1271 void event_ignore_queued_enters()
1272 {
1273     GSList *saved = NULL, *it;
1274     XEvent *e;
1275                 
1276     XSync(ob_display, FALSE);
1277
1278     /* count the events */
1279     while (TRUE) {
1280         e = g_new(XEvent, 1);
1281         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1282             saved = g_slist_append(saved, e);
1283             ++ignore_enter_focus;
1284         } else {
1285             g_free(e);
1286             break;
1287         }
1288     }
1289     /* put the events back */
1290     for (it = saved; it; it = g_slist_next(it)) {
1291         XPutBackEvent(ob_display, it->data);
1292         g_free(it->data);
1293     }
1294     g_slist_free(saved);
1295 }