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