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