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