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