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