]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
handle enter/leave events in the menu
[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) 2006        Mikael Magnusson
5    Copyright (c) 2003        Ben Jansens
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    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "event.h"
21 #include "debug.h"
22 #include "window.h"
23 #include "openbox.h"
24 #include "dock.h"
25 #include "client.h"
26 #include "xerror.h"
27 #include "prop.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "frame.h"
31 #include "menu.h"
32 #include "menuframe.h"
33 #include "keyboard.h"
34 #include "mouse.h"
35 #include "mainloop.h"
36 #include "framerender.h"
37 #include "focus.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "extensions.h"
42
43 #include <X11/Xlib.h>
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
46 #include <glib.h>
47
48 #ifdef HAVE_SYS_SELECT_H
49 #  include <sys/select.h>
50 #endif
51 #ifdef HAVE_SIGNAL_H
52 #  include <signal.h>
53 #endif
54 #ifdef XKB
55 #  include <X11/XKBlib.h>
56 #endif
57
58 #ifdef USE_SM
59 #include <X11/ICE/ICElib.h>
60 #endif
61
62 typedef struct
63 {
64     gboolean ignored;
65 } ObEventData;
66
67 static void event_process(const XEvent *e, gpointer data);
68 static void event_client_dest(ObClient *client, gpointer data);
69 static void event_handle_root(XEvent *e);
70 static void event_handle_menu(XEvent *e);
71 static void event_handle_dock(ObDock *s, XEvent *e);
72 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
73 static void event_handle_client(ObClient *c, XEvent *e);
74 static void event_handle_group(ObGroup *g, XEvent *e);
75
76 static gboolean focus_delay_func(gpointer data);
77 static void focus_delay_client_dest(ObClient *client, gpointer data);
78
79 static gboolean menu_hide_delay_func(gpointer data);
80
81 /* The time for the current event being processed */
82 Time event_curtime = CurrentTime;
83
84 /*! The value of the mask for the NumLock modifier */
85 guint NumLockMask;
86 /*! The value of the mask for the ScrollLock modifier */
87 guint ScrollLockMask;
88 /*! The key codes for the modifier keys */
89 static XModifierKeymap *modmap;
90 /*! Table of the constant modifier masks */
91 static const gint mask_table[] = {
92     ShiftMask, LockMask, ControlMask, Mod1Mask,
93     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
94 };
95 static gint mask_table_size;
96
97 static guint ignore_enter_focus = 0;
98
99 static gboolean menu_can_hide;
100
101 #ifdef USE_SM
102 static void ice_handler(gint fd, gpointer conn)
103 {
104     Bool b;
105     IceProcessMessages(conn, NULL, &b);
106 }
107
108 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
109                       IcePointer *watch_data)
110 {
111     static gint fd = -1;
112
113     if (opening) {
114         fd = IceConnectionNumber(conn);
115         ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
116     } else {
117         ob_main_loop_fd_remove(ob_main_loop, fd);
118         fd = -1;
119     }
120 }
121 #endif
122
123 void event_startup(gboolean reconfig)
124 {
125     if (reconfig) return;
126
127     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
128      
129     /* get lock masks that are defined by the display (not constant) */
130     modmap = XGetModifierMapping(ob_display);
131     g_assert(modmap);
132     if (modmap && modmap->max_keypermod > 0) {
133         size_t cnt;
134         const size_t size = mask_table_size * modmap->max_keypermod;
135         /* get the values of the keyboard lock modifiers
136            Note: Caps lock is not retrieved the same way as Scroll and Num
137            lock since it doesn't need to be. */
138         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
139         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
140                                                      XK_Scroll_Lock);
141
142         for (cnt = 0; cnt < size; ++cnt) {
143             if (! modmap->modifiermap[cnt]) continue;
144
145             if (num_lock == modmap->modifiermap[cnt])
146                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
147             if (scroll_lock == modmap->modifiermap[cnt])
148                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
149         }
150     }
151
152     ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
153
154 #ifdef USE_SM
155     IceAddConnectionWatch(ice_watch, NULL);
156 #endif
157
158     client_add_destructor(focus_delay_client_dest, NULL);
159     client_add_destructor(event_client_dest, NULL);
160 }
161
162 void event_shutdown(gboolean reconfig)
163 {
164     if (reconfig) return;
165
166 #ifdef USE_SM
167     IceRemoveConnectionWatch(ice_watch, NULL);
168 #endif
169
170     client_remove_destructor(focus_delay_client_dest);
171     client_remove_destructor(event_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_curtime(XEvent *e)
216 {
217     Time t = CurrentTime;
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     event_curtime = t;
248 }
249
250 #define STRIP_MODS(s) \
251         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
252         /* kill off the Button1Mask etc, only want the modifiers */ \
253         s &= (ControlMask | ShiftMask | Mod1Mask | \
254               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
255
256 static void event_hack_mods(XEvent *e)
257 {
258 #ifdef XKB
259     XkbStateRec xkb_state;
260 #endif
261     KeyCode *kp;
262     gint i, k;
263
264     switch (e->type) {
265     case ButtonPress:
266     case ButtonRelease:
267         STRIP_MODS(e->xbutton.state);
268         break;
269     case KeyPress:
270         STRIP_MODS(e->xkey.state);
271         break;
272     case KeyRelease:
273         STRIP_MODS(e->xkey.state);
274         /* remove from the state the mask of the modifier being released, if
275            it is a modifier key being released (this is a little ugly..) */
276 #ifdef XKB
277         if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
278             e->xkey.state = xkb_state.compat_state;
279             break;
280         }
281 #endif
282         kp = modmap->modifiermap;
283         for (i = 0; i < mask_table_size; ++i) {
284             for (k = 0; k < modmap->max_keypermod; ++k) {
285                 if (*kp == e->xkey.keycode) { /* found the keycode */
286                     /* remove the mask for it */
287                     e->xkey.state &= ~mask_table[i];
288                     /* cause the first loop to break; */
289                     i = mask_table_size;
290                     break; /* get outta here! */
291                 }
292                 ++kp;
293             }
294         }
295         break;
296     case MotionNotify:
297         STRIP_MODS(e->xmotion.state);
298         /* compress events */
299         {
300             XEvent ce;
301             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
302                                           e->type, &ce)) {
303                 e->xmotion.x_root = ce.xmotion.x_root;
304                 e->xmotion.y_root = ce.xmotion.y_root;
305             }
306         }
307         break;
308     }
309 }
310
311 static gboolean wanted_focusevent(XEvent *e)
312 {
313     gint mode = e->xfocus.mode;
314     gint detail = e->xfocus.detail;
315     Window win = e->xany.window;
316
317     if (e->type == FocusIn) {
318
319         /* These are ones we never want.. */
320
321         /* This means focus was given by a keyboard/mouse grab. */
322         if (mode == NotifyGrab)
323             return FALSE;
324         /* This means focus was given back from a keyboard/mouse grab. */
325         if (mode == NotifyUngrab)
326             return FALSE;
327
328         /* These are the ones we want.. */
329
330         if (win == RootWindow(ob_display, ob_screen)) {
331             /* This means focus reverted off of a client */
332             if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
333                 detail == NotifyInferior)
334                 return TRUE;
335             else
336                 return FALSE;
337         }
338
339         /* This means focus moved from the root window to a client */
340         if (detail == NotifyVirtual)
341             return TRUE;
342         /* This means focus moved from one client to another */
343         if (detail == NotifyNonlinearVirtual)
344             return TRUE;
345
346         /* This means focus reverted off of a client */
347         if (detail == NotifyInferior)
348             return TRUE;
349
350         /* Otherwise.. */
351         return FALSE;
352     } else {
353         g_assert(e->type == FocusOut);
354
355
356         /* These are ones we never want.. */
357
358         /* This means focus was taken by a keyboard/mouse grab. */
359         if (mode == NotifyGrab)
360             return FALSE;
361
362         /* Focus left the root window revertedto state */
363         if (win == RootWindow(ob_display, ob_screen))
364             return FALSE;
365
366         /* These are the ones we want.. */
367
368         /* This means focus moved from a client to the root window */
369         if (detail == NotifyVirtual)
370             return TRUE;
371         /* This means focus moved from one client to another */
372         if (detail == NotifyNonlinearVirtual)
373             return TRUE;
374
375         /* Otherwise.. */
376         return FALSE;
377     }
378 }
379
380 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
381 {
382     return e->type == FocusIn && wanted_focusevent(e);
383 }
384
385 static gboolean event_ignore(XEvent *e, ObClient *client)
386 {
387     switch(e->type) {
388     case EnterNotify:
389     case LeaveNotify:
390         if (e->xcrossing.detail == NotifyInferior)
391             return TRUE;
392         break;
393     case FocusIn:
394     case FocusOut:
395         /* I don't think this should ever happen with our event masks, but
396            if it does, we don't want it. */
397         if (client == NULL)
398             return TRUE;
399         if (!wanted_focusevent(e))
400             return TRUE;
401         break;
402     }
403     return FALSE;
404 }
405
406 static void event_process(const XEvent *ec, gpointer data)
407 {
408     Window window;
409     ObGroup *group = NULL;
410     ObClient *client = NULL;
411     ObDock *dock = NULL;
412     ObDockApp *dockapp = NULL;
413     ObWindow *obwin = NULL;
414     XEvent ee, *e;
415     ObEventData *ed = data;
416
417     /* make a copy we can mangle */
418     ee = *ec;
419     e = &ee;
420
421     window = event_get_window(e);
422     if (!(e->type == PropertyNotify &&
423           (group = g_hash_table_lookup(group_map, &window))))
424         if ((obwin = g_hash_table_lookup(window_map, &window))) {
425             switch (obwin->type) {
426             case Window_Dock:
427                 dock = WINDOW_AS_DOCK(obwin);
428                 break;
429             case Window_DockApp:
430                 dockapp = WINDOW_AS_DOCKAPP(obwin);
431                 break;
432             case Window_Client:
433                 client = WINDOW_AS_CLIENT(obwin);
434                 break;
435             case Window_Menu:
436             case Window_Internal:
437                 /* not to be used for events */
438                 g_assert_not_reached();
439                 break;
440             }
441         }
442
443     if (e->type == FocusIn || e->type == FocusOut) {
444         gint mode = e->xfocus.mode;
445         gint detail = e->xfocus.detail;
446         Window window = e->xfocus.window;
447         if (detail == NotifyVirtual) {
448             ob_debug_type(OB_DEBUG_FOCUS,
449                           "FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
450                           (e->type == FocusIn ? "IN" : "OUT"), window);
451         }
452
453         else if (detail == NotifyNonlinearVirtual) {
454             ob_debug_type(OB_DEBUG_FOCUS,
455                           "FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
456                           (e->type == FocusIn ? "IN" : "OUT"), window);
457         }
458
459         else
460             ob_debug_type(OB_DEBUG_FOCUS,
461                           "UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
462                           (e->type == FocusIn ? "IN" : "OUT"),
463                           detail, mode, window);
464     }
465
466     event_set_curtime(e);
467     event_hack_mods(e);
468     if (event_ignore(e, client)) {
469         if (ed)
470             ed->ignored = TRUE;
471         return;
472     } else if (ed)
473             ed->ignored = FALSE;
474
475     /* deal with it in the kernel */
476     if (group)
477         event_handle_group(group, e);
478     else if (client)
479         event_handle_client(client, e);
480     else if (dockapp)
481         event_handle_dockapp(dockapp, e);
482     else if (dock)
483         event_handle_dock(dock, e);
484     else if (window == RootWindow(ob_display, ob_screen))
485         event_handle_root(e);
486     else if (e->type == MapRequest)
487         client_manage(window);
488     else if (e->type == ConfigureRequest) {
489         /* unhandled configure requests must be used to configure the
490            window directly */
491         XWindowChanges xwc;
492
493         xwc.x = e->xconfigurerequest.x;
494         xwc.y = e->xconfigurerequest.y;
495         xwc.width = e->xconfigurerequest.width;
496         xwc.height = e->xconfigurerequest.height;
497         xwc.border_width = e->xconfigurerequest.border_width;
498         xwc.sibling = e->xconfigurerequest.above;
499         xwc.stack_mode = e->xconfigurerequest.detail;
500        
501         /* we are not to be held responsible if someone sends us an
502            invalid request! */
503         xerror_set_ignore(TRUE);
504         XConfigureWindow(ob_display, window,
505                          e->xconfigurerequest.value_mask, &xwc);
506         xerror_set_ignore(FALSE);
507     }
508
509     /* crossing events for menu */
510     if (e->type == EnterNotify || e->type == LeaveNotify)
511         if (menu_frame_visible)
512             event_handle_menu(e);
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                                          config_menu_hide_delay * 1000,
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                     keyboard_event((focus_cycle_target ? focus_cycle_target :
542                                     (focus_hilite ? focus_hilite : client)),
543                                    e);
544                 }
545             }
546         }
547     }
548     /* if something happens and it's not from an XEvent, then we don't know
549        the time */
550     event_curtime = CurrentTime;
551 }
552
553 static void event_handle_root(XEvent *e)
554 {
555     Atom msgtype;
556      
557     switch(e->type) {
558     case SelectionClear:
559         ob_debug("Another WM has requested to replace us. Exiting.\n");
560         ob_exit_replace();
561         break;
562
563     case ClientMessage:
564         if (e->xclient.format != 32) break;
565
566         msgtype = e->xclient.message_type;
567         if (msgtype == prop_atoms.net_current_desktop) {
568             guint d = e->xclient.data.l[0];
569             if (d < screen_num_desktops) {
570                 event_curtime = e->xclient.data.l[1];
571                 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
572                 screen_set_desktop(d);
573             }
574         } else if (msgtype == prop_atoms.net_number_of_desktops) {
575             guint d = e->xclient.data.l[0];
576             if (d > 0)
577                 screen_set_num_desktops(d);
578         } else if (msgtype == prop_atoms.net_showing_desktop) {
579             screen_show_desktop(e->xclient.data.l[0] != 0);
580         } else if (msgtype == prop_atoms.ob_control) {
581             if (e->xclient.data.l[0] == 1)
582                 ob_reconfigure();
583             else if (e->xclient.data.l[0] == 2)
584                 ob_restart();
585         }
586         break;
587     case PropertyNotify:
588         if (e->xproperty.atom == prop_atoms.net_desktop_names)
589             screen_update_desktop_names();
590         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
591             screen_update_layout();
592         break;
593     case ConfigureNotify:
594 #ifdef XRANDR
595         XRRUpdateConfiguration(e);
596 #endif
597         screen_resize();
598         break;
599     default:
600         ;
601     }
602 }
603
604 static void event_handle_group(ObGroup *group, XEvent *e)
605 {
606     GSList *it;
607
608     g_assert(e->type == PropertyNotify);
609
610     for (it = group->members; it; it = g_slist_next(it))
611         event_handle_client(it->data, e);
612 }
613
614 void event_enter_client(ObClient *client)
615 {
616     g_assert(config_focus_follow);
617
618     if (client_normal(client) && client_can_focus(client)) {
619         if (config_focus_delay) {
620             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
621             ob_main_loop_timeout_add(ob_main_loop,
622                                      config_focus_delay,
623                                      focus_delay_func,
624                                      client, NULL);
625         } else
626             focus_delay_func(client);
627     }
628 }
629
630 static void event_handle_client(ObClient *client, XEvent *e)
631 {
632     XEvent ce;
633     Atom msgtype;
634     gint i=0;
635     ObFrameContext con;
636      
637     switch (e->type) {
638     case VisibilityNotify:
639         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
640         break;
641     case ButtonPress:
642     case ButtonRelease:
643         /* Wheel buttons don't draw because they are an instant click, so it
644            is a waste of resources to go drawing it. */
645         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
646             con = frame_context(client, e->xbutton.window);
647             con = mouse_button_frame_context(con, e->xbutton.button);
648             switch (con) {
649             case OB_FRAME_CONTEXT_MAXIMIZE:
650                 client->frame->max_press = (e->type == ButtonPress);
651                 framerender_frame(client->frame);
652                 break;
653             case OB_FRAME_CONTEXT_CLOSE:
654                 client->frame->close_press = (e->type == ButtonPress);
655                 framerender_frame(client->frame);
656                 break;
657             case OB_FRAME_CONTEXT_ICONIFY:
658                 client->frame->iconify_press = (e->type == ButtonPress);
659                 framerender_frame(client->frame);
660                 break;
661             case OB_FRAME_CONTEXT_ALLDESKTOPS:
662                 client->frame->desk_press = (e->type == ButtonPress);
663                 framerender_frame(client->frame);
664                 break; 
665             case OB_FRAME_CONTEXT_SHADE:
666                 client->frame->shade_press = (e->type == ButtonPress);
667                 framerender_frame(client->frame);
668                 break;
669             default:
670                 /* nothing changes with clicks for any other contexts */
671                 break;
672             }
673         }
674         break;
675     case FocusIn:
676         if (client != focus_client) {
677             focus_set_client(client);
678             frame_adjust_focus(client->frame, TRUE);
679             client_calc_layer(client);
680         }
681         break;
682     case FocusOut:
683         /* Look for the followup FocusIn */
684         if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
685             /* There is no FocusIn, this means focus went to a window that
686                is not being managed, or a window on another screen. */
687             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
688         } else if (ce.xany.window == e->xany.window) {
689             /* If focus didn't actually move anywhere, there is nothing to do*/
690             break;
691         } else if (ce.xfocus.detail == NotifyPointerRoot ||
692                  ce.xfocus.detail == NotifyDetailNone) {
693             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
694             /* Focus has been reverted to the root window or nothing, so fall
695                back to something other than the window which just had it. */
696             focus_fallback(FALSE);
697         } else if (ce.xfocus.detail == NotifyInferior) {
698             ob_debug_type(OB_DEBUG_FOCUS, "Focus went to parent\n");
699             /* Focus has been reverted to parent, which is our frame window,
700                or the root window, so fall back to something other than the
701                window which had it. */
702             focus_fallback(FALSE);
703         } else {
704             /* Focus did move, so process the FocusIn event */
705             ObEventData ed = { .ignored = FALSE };
706             event_process(&ce, &ed);
707             if (ed.ignored) {
708                 /* The FocusIn was ignored, this means it was on a window
709                    that isn't a client. */
710                 ob_debug_type(OB_DEBUG_FOCUS,
711                               "Focus went to an unmanaged window 0x%x !\n",
712                               ce.xfocus.window);
713                 focus_fallback(TRUE);
714             }
715         }
716
717         /* This client is no longer focused, so show that */
718         focus_hilite = NULL;
719         frame_adjust_focus(client->frame, FALSE);
720         client_calc_layer(client);
721         break;
722     case LeaveNotify:
723         con = frame_context(client, e->xcrossing.window);
724         switch (con) {
725         case OB_FRAME_CONTEXT_MAXIMIZE:
726             client->frame->max_hover = FALSE;
727             frame_adjust_state(client->frame);
728             break;
729         case OB_FRAME_CONTEXT_ALLDESKTOPS:
730             client->frame->desk_hover = FALSE;
731             frame_adjust_state(client->frame);
732             break;
733         case OB_FRAME_CONTEXT_SHADE:
734             client->frame->shade_hover = FALSE;
735             frame_adjust_state(client->frame);
736             break;
737         case OB_FRAME_CONTEXT_ICONIFY:
738             client->frame->iconify_hover = FALSE;
739             frame_adjust_state(client->frame);
740             break;
741         case OB_FRAME_CONTEXT_CLOSE:
742             client->frame->close_hover = FALSE;
743             frame_adjust_state(client->frame);
744             break;
745         case OB_FRAME_CONTEXT_FRAME:
746             if (config_focus_follow && config_focus_delay)
747                 ob_main_loop_timeout_remove_data(ob_main_loop,
748                                                  focus_delay_func,
749                                                  client, TRUE);
750             break;
751         default:
752             break;
753         }
754         break;
755     case EnterNotify:
756     {
757         gboolean nofocus = FALSE;
758
759         if (ignore_enter_focus) {
760             ignore_enter_focus--;
761             nofocus = TRUE;
762         }
763
764         con = frame_context(client, e->xcrossing.window);
765         switch (con) {
766         case OB_FRAME_CONTEXT_MAXIMIZE:
767             client->frame->max_hover = TRUE;
768             frame_adjust_state(client->frame);
769             break;
770         case OB_FRAME_CONTEXT_ALLDESKTOPS:
771             client->frame->desk_hover = TRUE;
772             frame_adjust_state(client->frame);
773             break;
774         case OB_FRAME_CONTEXT_SHADE:
775             client->frame->shade_hover = TRUE;
776             frame_adjust_state(client->frame);
777             break;
778         case OB_FRAME_CONTEXT_ICONIFY:
779             client->frame->iconify_hover = TRUE;
780             frame_adjust_state(client->frame);
781             break;
782         case OB_FRAME_CONTEXT_CLOSE:
783             client->frame->close_hover = TRUE;
784             frame_adjust_state(client->frame);
785             break;
786         case OB_FRAME_CONTEXT_FRAME:
787             if (e->xcrossing.mode == NotifyGrab ||
788                 e->xcrossing.mode == NotifyUngrab)
789             {
790                 ob_debug_type(OB_DEBUG_FOCUS,
791                               "%sNotify mode %d detail %d on %lx IGNORED\n",
792                               (e->type == EnterNotify ? "Enter" : "Leave"),
793                               e->xcrossing.mode,
794                               e->xcrossing.detail, client?client->window:0);
795             } else {
796                 ob_debug_type(OB_DEBUG_FOCUS,
797                               "%sNotify mode %d detail %d on %lx, "
798                               "focusing window: %d\n",
799                               (e->type == EnterNotify ? "Enter" : "Leave"),
800                               e->xcrossing.mode,
801                               e->xcrossing.detail, (client?client->window:0),
802                               !nofocus);
803                 if (!nofocus && config_focus_follow)
804                     event_enter_client(client);
805             }
806             break;
807         default:
808             break;
809         }
810         break;
811     }
812     case ConfigureRequest:
813         /* compress these */
814         while (XCheckTypedWindowEvent(ob_display, client->window,
815                                       ConfigureRequest, &ce)) {
816             ++i;
817             /* XXX if this causes bad things.. we can compress config req's
818                with the same mask. */
819             e->xconfigurerequest.value_mask |=
820                 ce.xconfigurerequest.value_mask;
821             if (ce.xconfigurerequest.value_mask & CWX)
822                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
823             if (ce.xconfigurerequest.value_mask & CWY)
824                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
825             if (ce.xconfigurerequest.value_mask & CWWidth)
826                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
827             if (ce.xconfigurerequest.value_mask & CWHeight)
828                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
829             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
830                 e->xconfigurerequest.border_width =
831                     ce.xconfigurerequest.border_width;
832             if (ce.xconfigurerequest.value_mask & CWStackMode)
833                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
834         }
835
836         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
837         if (client->iconic || client->shaded) return;
838
839         /* resize, then move, as specified in the EWMH section 7.7 */
840         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
841                                                CWX | CWY |
842                                                CWBorderWidth)) {
843             gint x, y, w, h;
844             ObCorner corner;
845
846             if (e->xconfigurerequest.value_mask & CWBorderWidth)
847                 client->border_width = e->xconfigurerequest.border_width;
848
849             x = (e->xconfigurerequest.value_mask & CWX) ?
850                 e->xconfigurerequest.x : client->area.x;
851             y = (e->xconfigurerequest.value_mask & CWY) ?
852                 e->xconfigurerequest.y : client->area.y;
853             w = (e->xconfigurerequest.value_mask & CWWidth) ?
854                 e->xconfigurerequest.width : client->area.width;
855             h = (e->xconfigurerequest.value_mask & CWHeight) ?
856                 e->xconfigurerequest.height : client->area.height;
857
858             {
859                 gint newx = x;
860                 gint newy = y;
861                 gint fw = w +
862                      client->frame->size.left + client->frame->size.right;
863                 gint fh = h +
864                      client->frame->size.top + client->frame->size.bottom;
865                 /* make this rude for size-only changes but not for position
866                    changes.. */
867                 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
868                                    (e->xconfigurerequest.value_mask & CWY));
869
870                 client_find_onscreen(client, &newx, &newy, fw, fh,
871                                      !moving);
872                 if (e->xconfigurerequest.value_mask & CWX)
873                     x = newx;
874                 if (e->xconfigurerequest.value_mask & CWY)
875                     y = newy;
876             }
877
878             switch (client->gravity) {
879             case NorthEastGravity:
880             case EastGravity:
881                 corner = OB_CORNER_TOPRIGHT;
882                 break;
883             case SouthWestGravity:
884             case SouthGravity:
885                 corner = OB_CORNER_BOTTOMLEFT;
886                 break;
887             case SouthEastGravity:
888                 corner = OB_CORNER_BOTTOMRIGHT;
889                 break;
890             default:     /* NorthWest, Static, etc */
891                 corner = OB_CORNER_TOPLEFT;
892             }
893
894             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
895                                   TRUE);
896         }
897
898         if (e->xconfigurerequest.value_mask & CWStackMode) {
899             switch (e->xconfigurerequest.detail) {
900             case Below:
901             case BottomIf:
902                 /* Apps are so rude. And this is totally disconnected from
903                    activation/focus. Bleh. */
904                 /*client_lower(client);*/
905                 break;
906
907             case Above:
908             case TopIf:
909             default:
910                 /* Apps are so rude. And this is totally disconnected from
911                    activation/focus. Bleh. */
912                 /*client_raise(client);*/
913                 break;
914             }
915         }
916         break;
917     case UnmapNotify:
918         if (client->ignore_unmaps) {
919             client->ignore_unmaps--;
920             break;
921         }
922         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
923                  "ignores left %d\n",
924                  client->window, e->xunmap.event, e->xunmap.from_configure,
925                  client->ignore_unmaps);
926         client_unmanage(client);
927         break;
928     case DestroyNotify:
929         ob_debug("DestroyNotify for window 0x%x\n", client->window);
930         client_unmanage(client);
931         break;
932     case ReparentNotify:
933         /* this is when the client is first taken captive in the frame */
934         if (e->xreparent.parent == client->frame->plate) break;
935
936         /*
937           This event is quite rare and is usually handled in unmapHandler.
938           However, if the window is unmapped when the reparent event occurs,
939           the window manager never sees it because an unmap event is not sent
940           to an already unmapped window.
941         */
942
943         /* we don't want the reparent event, put it back on the stack for the
944            X server to deal with after we unmanage the window */
945         XPutBackEvent(ob_display, e);
946      
947         ob_debug("ReparentNotify for window 0x%x\n", client->window);
948         client_unmanage(client);
949         break;
950     case MapRequest:
951         ob_debug("MapRequest for 0x%lx\n", client->window);
952         if (!client->iconic) break; /* this normally doesn't happen, but if it
953                                        does, we don't want it!
954                                        it can happen now when the window is on
955                                        another desktop, but we still don't
956                                        want it! */
957         client_activate(client, FALSE, TRUE);
958         break;
959     case ClientMessage:
960         /* validate cuz we query stuff off the client here */
961         if (!client_validate(client)) break;
962
963         if (e->xclient.format != 32) return;
964
965         msgtype = e->xclient.message_type;
966         if (msgtype == prop_atoms.wm_change_state) {
967             /* compress changes into a single change */
968             while (XCheckTypedWindowEvent(ob_display, client->window,
969                                           e->type, &ce)) {
970                 /* XXX: it would be nice to compress ALL messages of a
971                    type, not just messages in a row without other
972                    message types between. */
973                 if (ce.xclient.message_type != msgtype) {
974                     XPutBackEvent(ob_display, &ce);
975                     break;
976                 }
977                 e->xclient = ce.xclient;
978             }
979             client_set_wm_state(client, e->xclient.data.l[0]);
980         } else if (msgtype == prop_atoms.net_wm_desktop) {
981             /* compress changes into a single change */
982             while (XCheckTypedWindowEvent(ob_display, client->window,
983                                           e->type, &ce)) {
984                 /* XXX: it would be nice to compress ALL messages of a
985                    type, not just messages in a row without other
986                    message types between. */
987                 if (ce.xclient.message_type != msgtype) {
988                     XPutBackEvent(ob_display, &ce);
989                     break;
990                 }
991                 e->xclient = ce.xclient;
992             }
993             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
994                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
995                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
996                                    FALSE);
997         } else if (msgtype == prop_atoms.net_wm_state) {
998             /* can't compress these */
999             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1000                      (e->xclient.data.l[0] == 0 ? "Remove" :
1001                       e->xclient.data.l[0] == 1 ? "Add" :
1002                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1003                      e->xclient.data.l[1], e->xclient.data.l[2],
1004                      client->window);
1005             client_set_state(client, e->xclient.data.l[0],
1006                              e->xclient.data.l[1], e->xclient.data.l[2]);
1007         } else if (msgtype == prop_atoms.net_close_window) {
1008             ob_debug("net_close_window for 0x%lx\n", client->window);
1009             client_close(client);
1010         } else if (msgtype == prop_atoms.net_active_window) {
1011             ob_debug("net_active_window for 0x%lx source=%s\n",
1012                      client->window,
1013                      (e->xclient.data.l[0] == 0 ? "unknown" :
1014                       (e->xclient.data.l[0] == 1 ? "application" :
1015                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1016             /* XXX make use of data.l[2] ! */
1017             event_curtime = e->xclient.data.l[1];
1018             client_activate(client, FALSE,
1019                             (e->xclient.data.l[0] == 0 ||
1020                              e->xclient.data.l[0] == 2));
1021         } else if (msgtype == prop_atoms.net_wm_moveresize) {
1022             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1023                      client->window, e->xclient.data.l[2]);
1024             if ((Atom)e->xclient.data.l[2] ==
1025                 prop_atoms.net_wm_moveresize_size_topleft ||
1026                 (Atom)e->xclient.data.l[2] ==
1027                 prop_atoms.net_wm_moveresize_size_top ||
1028                 (Atom)e->xclient.data.l[2] ==
1029                 prop_atoms.net_wm_moveresize_size_topright ||
1030                 (Atom)e->xclient.data.l[2] ==
1031                 prop_atoms.net_wm_moveresize_size_right ||
1032                 (Atom)e->xclient.data.l[2] ==
1033                 prop_atoms.net_wm_moveresize_size_right ||
1034                 (Atom)e->xclient.data.l[2] ==
1035                 prop_atoms.net_wm_moveresize_size_bottomright ||
1036                 (Atom)e->xclient.data.l[2] ==
1037                 prop_atoms.net_wm_moveresize_size_bottom ||
1038                 (Atom)e->xclient.data.l[2] ==
1039                 prop_atoms.net_wm_moveresize_size_bottomleft ||
1040                 (Atom)e->xclient.data.l[2] ==
1041                 prop_atoms.net_wm_moveresize_size_left ||
1042                 (Atom)e->xclient.data.l[2] ==
1043                 prop_atoms.net_wm_moveresize_move ||
1044                 (Atom)e->xclient.data.l[2] ==
1045                 prop_atoms.net_wm_moveresize_size_keyboard ||
1046                 (Atom)e->xclient.data.l[2] ==
1047                 prop_atoms.net_wm_moveresize_move_keyboard) {
1048
1049                 moveresize_start(client, e->xclient.data.l[0],
1050                                  e->xclient.data.l[1], e->xclient.data.l[3],
1051                                  e->xclient.data.l[2]);
1052             }
1053             else if ((Atom)e->xclient.data.l[2] ==
1054                      prop_atoms.net_wm_moveresize_cancel)
1055                 moveresize_end(TRUE);
1056         } else if (msgtype == prop_atoms.net_moveresize_window) {
1057             gint oldg = client->gravity;
1058             gint tmpg, x, y, w, h;
1059
1060             if (e->xclient.data.l[0] & 0xff)
1061                 tmpg = e->xclient.data.l[0] & 0xff;
1062             else
1063                 tmpg = oldg;
1064
1065             if (e->xclient.data.l[0] & 1 << 8)
1066                 x = e->xclient.data.l[1];
1067             else
1068                 x = client->area.x;
1069             if (e->xclient.data.l[0] & 1 << 9)
1070                 y = e->xclient.data.l[2];
1071             else
1072                 y = client->area.y;
1073             if (e->xclient.data.l[0] & 1 << 10)
1074                 w = e->xclient.data.l[3];
1075             else
1076                 w = client->area.width;
1077             if (e->xclient.data.l[0] & 1 << 11)
1078                 h = e->xclient.data.l[4];
1079             else
1080                 h = client->area.height;
1081             client->gravity = tmpg;
1082
1083             {
1084                 gint newx = x;
1085                 gint newy = y;
1086                 gint fw = w +
1087                      client->frame->size.left + client->frame->size.right;
1088                 gint fh = h +
1089                      client->frame->size.top + client->frame->size.bottom;
1090                 client_find_onscreen(client, &newx, &newy, fw, fh,
1091                                      client_normal(client));
1092                 if (e->xclient.data.l[0] & 1 << 8)
1093                     x = newx;
1094                 if (e->xclient.data.l[0] & 1 << 9)
1095                     y = newy;
1096             }
1097
1098             client_configure(client, OB_CORNER_TOPLEFT,
1099                              x, y, w, h, FALSE, TRUE);
1100
1101             client->gravity = oldg;
1102         }
1103         break;
1104     case PropertyNotify:
1105         /* validate cuz we query stuff off the client here */
1106         if (!client_validate(client)) break;
1107   
1108         /* compress changes to a single property into a single change */
1109         while (XCheckTypedWindowEvent(ob_display, client->window,
1110                                       e->type, &ce)) {
1111             Atom a, b;
1112
1113             /* XXX: it would be nice to compress ALL changes to a property,
1114                not just changes in a row without other props between. */
1115
1116             a = ce.xproperty.atom;
1117             b = e->xproperty.atom;
1118
1119             if (a == b)
1120                 continue;
1121             if ((a == prop_atoms.net_wm_name ||
1122                  a == prop_atoms.wm_name ||
1123                  a == prop_atoms.net_wm_icon_name ||
1124                  a == prop_atoms.wm_icon_name)
1125                 &&
1126                 (b == prop_atoms.net_wm_name ||
1127                  b == prop_atoms.wm_name ||
1128                  b == prop_atoms.net_wm_icon_name ||
1129                  b == prop_atoms.wm_icon_name)) {
1130                 continue;
1131             }
1132             if (a == prop_atoms.net_wm_icon &&
1133                 b == prop_atoms.net_wm_icon)
1134                 continue;
1135
1136             XPutBackEvent(ob_display, &ce);
1137             break;
1138         }
1139
1140         msgtype = e->xproperty.atom;
1141         if (msgtype == XA_WM_NORMAL_HINTS) {
1142             client_update_normal_hints(client);
1143             /* normal hints can make a window non-resizable */
1144             client_setup_decor_and_functions(client);
1145         } else if (msgtype == XA_WM_HINTS) {
1146             client_update_wmhints(client);
1147         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1148             client_update_transient_for(client);
1149             client_get_type(client);
1150             /* type may have changed, so update the layer */
1151             client_calc_layer(client);
1152             client_setup_decor_and_functions(client);
1153         } else if (msgtype == prop_atoms.net_wm_name ||
1154                    msgtype == prop_atoms.wm_name ||
1155                    msgtype == prop_atoms.net_wm_icon_name ||
1156                    msgtype == prop_atoms.wm_icon_name) {
1157             client_update_title(client);
1158         } else if (msgtype == prop_atoms.wm_class) {
1159             client_update_class(client);
1160         } else if (msgtype == prop_atoms.wm_protocols) {
1161             client_update_protocols(client);
1162             client_setup_decor_and_functions(client);
1163         }
1164         else if (msgtype == prop_atoms.net_wm_strut) {
1165             client_update_strut(client);
1166         }
1167         else if (msgtype == prop_atoms.net_wm_icon) {
1168             client_update_icons(client);
1169         }
1170         else if (msgtype == prop_atoms.net_wm_user_time) {
1171             client_update_user_time(client);
1172         }
1173         else if (msgtype == prop_atoms.sm_client_id) {
1174             client_update_sm_client_id(client);
1175         }
1176     default:
1177         ;
1178 #ifdef SHAPE
1179         if (extensions_shape && e->type == extensions_shape_event_basep) {
1180             client->shaped = ((XShapeEvent*)e)->shaped;
1181             frame_adjust_shape(client->frame);
1182         }
1183 #endif
1184     }
1185 }
1186
1187 static void event_handle_dock(ObDock *s, XEvent *e)
1188 {
1189     switch (e->type) {
1190     case ButtonPress:
1191         if (e->xbutton.button == 1)
1192             stacking_raise(DOCK_AS_WINDOW(s));
1193         else if (e->xbutton.button == 2)
1194             stacking_lower(DOCK_AS_WINDOW(s));
1195         break;
1196     case EnterNotify:
1197         dock_hide(FALSE);
1198         break;
1199     case LeaveNotify:
1200         dock_hide(TRUE);
1201         break;
1202     }
1203 }
1204
1205 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1206 {
1207     switch (e->type) {
1208     case MotionNotify:
1209         dock_app_drag(app, &e->xmotion);
1210         break;
1211     case UnmapNotify:
1212         if (app->ignore_unmaps) {
1213             app->ignore_unmaps--;
1214             break;
1215         }
1216         dock_remove(app, TRUE);
1217         break;
1218     case DestroyNotify:
1219         dock_remove(app, FALSE);
1220         break;
1221     case ReparentNotify:
1222         dock_remove(app, FALSE);
1223         break;
1224     case ConfigureNotify:
1225         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1226         break;
1227     }
1228 }
1229
1230 ObMenuFrame* find_active_menu()
1231 {
1232     GList *it;
1233     ObMenuFrame *ret = NULL;
1234
1235     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1236         ret = it->data;
1237         if (ret->selected)
1238             break;
1239         ret = NULL;
1240     }
1241     return ret;
1242 }
1243
1244 ObMenuFrame* find_active_or_last_menu()
1245 {
1246     ObMenuFrame *ret = NULL;
1247
1248     ret = find_active_menu();
1249     if (!ret && menu_frame_visible)
1250         ret = menu_frame_visible->data;
1251     return ret;
1252 }
1253
1254 static void event_handle_menu(XEvent *ev)
1255 {
1256     ObMenuFrame *f;
1257     ObMenuEntryFrame *e;
1258
1259     switch (ev->type) {
1260     case ButtonRelease:
1261         if (menu_can_hide) {
1262             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1263                                             ev->xbutton.y_root)))
1264                 menu_entry_frame_execute(e, ev->xbutton.state,
1265                                          ev->xbutton.time);
1266             else
1267                 menu_frame_hide_all();
1268         }
1269         break;
1270     case EnterNotify:
1271         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1272             if (e->ignore_enters)
1273                 --e->ignore_enters;
1274             else
1275                 menu_frame_select(e->frame, e);
1276         }
1277         break;
1278     case LeaveNotify:
1279         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1280             if (e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1281                 menu_frame_select(e->frame, NULL);
1282         }
1283     case MotionNotify:
1284         if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1285                                         ev->xmotion.y_root))) {
1286             /* XXX menu_frame_entry_move_on_screen(f); */
1287             menu_frame_select(e->frame, e);
1288         }
1289         break;
1290     case KeyPress:
1291         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1292             menu_frame_hide_all();
1293         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1294             ObMenuFrame *f;
1295             if ((f = find_active_menu()))
1296                 menu_entry_frame_execute(f->selected, ev->xkey.state,
1297                                          ev->xkey.time);
1298         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1299             ObMenuFrame *f;
1300             if ((f = find_active_or_last_menu()) && f->parent)
1301                 menu_frame_select(f, NULL);
1302         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1303             ObMenuFrame *f;
1304             if ((f = find_active_or_last_menu()) && f->child)
1305                 menu_frame_select_next(f->child);
1306         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1307             ObMenuFrame *f;
1308             if ((f = find_active_or_last_menu()))
1309                 menu_frame_select_previous(f);
1310         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1311             ObMenuFrame *f;
1312             if ((f = find_active_or_last_menu()))
1313                 menu_frame_select_next(f);
1314         }
1315         break;
1316     }
1317 }
1318
1319 static gboolean menu_hide_delay_func(gpointer data)
1320 {
1321     menu_can_hide = TRUE;
1322     return FALSE; /* no repeat */
1323 }
1324
1325 static gboolean focus_delay_func(gpointer data)
1326 {
1327     ObClient *c = data;
1328
1329     if (focus_client != c) {
1330         if (client_focus(c) && config_focus_raise)
1331             client_raise(c);
1332     }
1333     return FALSE; /* no repeat */
1334 }
1335
1336 static void focus_delay_client_dest(ObClient *client, gpointer data)
1337 {
1338     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1339                                      client, TRUE);
1340 }
1341
1342 static void event_client_dest(ObClient *client, gpointer data)
1343 {
1344     if (client == focus_hilite)
1345         focus_hilite = NULL;
1346 }
1347
1348 void event_halt_focus_delay()
1349 {
1350     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1351 }
1352
1353 void event_ignore_queued_enters()
1354 {
1355     GSList *saved = NULL, *it;
1356     XEvent *e;
1357                 
1358     XSync(ob_display, FALSE);
1359
1360     /* count the events */
1361     while (TRUE) {
1362         e = g_new(XEvent, 1);
1363         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1364             ObWindow *win;
1365             
1366             win = g_hash_table_lookup(window_map, &e->xany.window);
1367             if (win && WINDOW_IS_CLIENT(win))
1368                 ++ignore_enter_focus;
1369             
1370             saved = g_slist_append(saved, e);
1371         } else {
1372             g_free(e);
1373             break;
1374         }
1375     }
1376     /* put the events back */
1377     for (it = saved; it; it = g_slist_next(it)) {
1378         XPutBackEvent(ob_display, it->data);
1379         g_free(it->data);
1380     }
1381     g_slist_free(saved);
1382 }
1383
1384 gboolean event_time_after(Time t1, Time t2)
1385 {
1386     g_assert(t1 != CurrentTime);
1387     g_assert(t2 != CurrentTime);
1388
1389     /*
1390       Timestamp values wrap around (after about 49.7 days). The server, given
1391       its current time is represented by timestamp T, always interprets
1392       timestamps from clients by treating half of the timestamp space as being
1393       later in time than T.
1394       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1395     */
1396
1397     /* TIME_HALF is half of the number space of a Time type variable */
1398 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1399
1400     if (t2 >= TIME_HALF)
1401         /* t2 is in the second half so t1 might wrap around and be smaller than
1402            t2 */
1403         return t1 >= t2 || t1 < (t2 + TIME_HALF);
1404     else
1405         /* t2 is in the first half so t1 has to come after it */
1406         return t1 >= t2 && t1 < (t2 + TIME_HALF);
1407 }