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