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