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