]> icculus.org git repositories - dana/openbox.git/blob - openbox/event.c
add debug messages saying if an extension is missing on the server
[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-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), 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                 /* leaveinferior events can happen when the mouse goes onto the
765                    window's border and then into the window before the delay
766                    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             {
815                 ob_debug_type(OB_DEBUG_FOCUS,
816                               "%sNotify mode %d detail %d on %lx IGNORED\n",
817                               (e->type == EnterNotify ? "Enter" : "Leave"),
818                               e->xcrossing.mode,
819                               e->xcrossing.detail, client?client->window:0);
820             } else {
821                 ob_debug_type(OB_DEBUG_FOCUS,
822                               "%sNotify mode %d detail %d on %lx, "
823                               "focusing window: %d\n",
824                               (e->type == EnterNotify ? "Enter" : "Leave"),
825                               e->xcrossing.mode,
826                               e->xcrossing.detail, (client?client->window:0),
827                               !nofocus);
828                 if (!nofocus && config_focus_follow)
829                     event_enter_client(client);
830             }
831             break;
832         default:
833             break;
834         }
835         break;
836     }
837     case ConfigureRequest:
838         /* compress these */
839         while (XCheckTypedWindowEvent(ob_display, client->window,
840                                       ConfigureRequest, &ce)) {
841             ++i;
842             /* XXX if this causes bad things.. we can compress config req's
843                with the same mask. */
844             e->xconfigurerequest.value_mask |=
845                 ce.xconfigurerequest.value_mask;
846             if (ce.xconfigurerequest.value_mask & CWX)
847                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
848             if (ce.xconfigurerequest.value_mask & CWY)
849                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
850             if (ce.xconfigurerequest.value_mask & CWWidth)
851                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
852             if (ce.xconfigurerequest.value_mask & CWHeight)
853                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
854             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
855                 e->xconfigurerequest.border_width =
856                     ce.xconfigurerequest.border_width;
857             if (ce.xconfigurerequest.value_mask & CWStackMode)
858                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
859         }
860
861         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
862         if (client->iconic || client->shaded) return;
863
864         /* resize, then move, as specified in the EWMH section 7.7 */
865         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
866                                                CWX | CWY |
867                                                CWBorderWidth)) {
868             gint x, y, w, h;
869             ObCorner corner;
870
871             if (e->xconfigurerequest.value_mask & CWBorderWidth)
872                 client->border_width = e->xconfigurerequest.border_width;
873
874             x = (e->xconfigurerequest.value_mask & CWX) ?
875                 e->xconfigurerequest.x : client->area.x;
876             y = (e->xconfigurerequest.value_mask & CWY) ?
877                 e->xconfigurerequest.y : client->area.y;
878             w = (e->xconfigurerequest.value_mask & CWWidth) ?
879                 e->xconfigurerequest.width : client->area.width;
880             h = (e->xconfigurerequest.value_mask & CWHeight) ?
881                 e->xconfigurerequest.height : client->area.height;
882
883             {
884                 gint newx = x;
885                 gint newy = y;
886                 gint fw = w +
887                      client->frame->size.left + client->frame->size.right;
888                 gint fh = h +
889                      client->frame->size.top + client->frame->size.bottom;
890                 /* make this rude for size-only changes but not for position
891                    changes.. */
892                 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
893                                    (e->xconfigurerequest.value_mask & CWY));
894
895                 client_find_onscreen(client, &newx, &newy, fw, fh,
896                                      !moving);
897                 if (e->xconfigurerequest.value_mask & CWX)
898                     x = newx;
899                 if (e->xconfigurerequest.value_mask & CWY)
900                     y = newy;
901             }
902
903             switch (client->gravity) {
904             case NorthEastGravity:
905             case EastGravity:
906                 corner = OB_CORNER_TOPRIGHT;
907                 break;
908             case SouthWestGravity:
909             case SouthGravity:
910                 corner = OB_CORNER_BOTTOMLEFT;
911                 break;
912             case SouthEastGravity:
913                 corner = OB_CORNER_BOTTOMRIGHT;
914                 break;
915             default:     /* NorthWest, Static, etc */
916                 corner = OB_CORNER_TOPLEFT;
917             }
918
919             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
920                                   TRUE);
921         }
922
923         if (e->xconfigurerequest.value_mask & CWStackMode) {
924             switch (e->xconfigurerequest.detail) {
925             case Below:
926             case BottomIf:
927                 /* Apps are so rude. And this is totally disconnected from
928                    activation/focus. Bleh. */
929                 /*client_lower(client);*/
930                 break;
931
932             case Above:
933             case TopIf:
934             default:
935                 /* Apps are so rude. And this is totally disconnected from
936                    activation/focus. Bleh. */
937                 /*client_raise(client);*/
938                 break;
939             }
940         }
941         break;
942     case UnmapNotify:
943         if (client->ignore_unmaps) {
944             client->ignore_unmaps--;
945             break;
946         }
947         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
948                  "ignores left %d\n",
949                  client->window, e->xunmap.event, e->xunmap.from_configure,
950                  client->ignore_unmaps);
951         client_unmanage(client);
952         break;
953     case DestroyNotify:
954         ob_debug("DestroyNotify for window 0x%x\n", client->window);
955         client_unmanage(client);
956         break;
957     case ReparentNotify:
958         /* this is when the client is first taken captive in the frame */
959         if (e->xreparent.parent == client->frame->plate) break;
960
961         /*
962           This event is quite rare and is usually handled in unmapHandler.
963           However, if the window is unmapped when the reparent event occurs,
964           the window manager never sees it because an unmap event is not sent
965           to an already unmapped window.
966         */
967
968         /* we don't want the reparent event, put it back on the stack for the
969            X server to deal with after we unmanage the window */
970         XPutBackEvent(ob_display, e);
971      
972         ob_debug("ReparentNotify for window 0x%x\n", client->window);
973         client_unmanage(client);
974         break;
975     case MapRequest:
976         ob_debug("MapRequest for 0x%lx\n", client->window);
977         if (!client->iconic) break; /* this normally doesn't happen, but if it
978                                        does, we don't want it!
979                                        it can happen now when the window is on
980                                        another desktop, but we still don't
981                                        want it! */
982         client_activate(client, FALSE, TRUE);
983         break;
984     case ClientMessage:
985         /* validate cuz we query stuff off the client here */
986         if (!client_validate(client)) break;
987
988         if (e->xclient.format != 32) return;
989
990         msgtype = e->xclient.message_type;
991         if (msgtype == prop_atoms.wm_change_state) {
992             /* compress changes into a single change */
993             while (XCheckTypedWindowEvent(ob_display, client->window,
994                                           e->type, &ce)) {
995                 /* XXX: it would be nice to compress ALL messages of a
996                    type, not just messages in a row without other
997                    message types between. */
998                 if (ce.xclient.message_type != msgtype) {
999                     XPutBackEvent(ob_display, &ce);
1000                     break;
1001                 }
1002                 e->xclient = ce.xclient;
1003             }
1004             client_set_wm_state(client, e->xclient.data.l[0]);
1005         } else if (msgtype == prop_atoms.net_wm_desktop) {
1006             /* compress changes into a single change */
1007             while (XCheckTypedWindowEvent(ob_display, client->window,
1008                                           e->type, &ce)) {
1009                 /* XXX: it would be nice to compress ALL messages of a
1010                    type, not just messages in a row without other
1011                    message types between. */
1012                 if (ce.xclient.message_type != msgtype) {
1013                     XPutBackEvent(ob_display, &ce);
1014                     break;
1015                 }
1016                 e->xclient = ce.xclient;
1017             }
1018             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1019                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1020                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1021                                    FALSE);
1022         } else if (msgtype == prop_atoms.net_wm_state) {
1023             /* can't compress these */
1024             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1025                      (e->xclient.data.l[0] == 0 ? "Remove" :
1026                       e->xclient.data.l[0] == 1 ? "Add" :
1027                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1028                      e->xclient.data.l[1], e->xclient.data.l[2],
1029                      client->window);
1030             client_set_state(client, e->xclient.data.l[0],
1031                              e->xclient.data.l[1], e->xclient.data.l[2]);
1032         } else if (msgtype == prop_atoms.net_close_window) {
1033             ob_debug("net_close_window for 0x%lx\n", client->window);
1034             client_close(client);
1035         } else if (msgtype == prop_atoms.net_active_window) {
1036             ob_debug("net_active_window for 0x%lx source=%s\n",
1037                      client->window,
1038                      (e->xclient.data.l[0] == 0 ? "unknown" :
1039                       (e->xclient.data.l[0] == 1 ? "application" :
1040                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1041             /* XXX make use of data.l[2] ! */
1042             event_curtime = e->xclient.data.l[1];
1043             client_activate(client, FALSE,
1044                             (e->xclient.data.l[0] == 0 ||
1045                              e->xclient.data.l[0] == 2));
1046         } else if (msgtype == prop_atoms.net_wm_moveresize) {
1047             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1048                      client->window, e->xclient.data.l[2]);
1049             if ((Atom)e->xclient.data.l[2] ==
1050                 prop_atoms.net_wm_moveresize_size_topleft ||
1051                 (Atom)e->xclient.data.l[2] ==
1052                 prop_atoms.net_wm_moveresize_size_top ||
1053                 (Atom)e->xclient.data.l[2] ==
1054                 prop_atoms.net_wm_moveresize_size_topright ||
1055                 (Atom)e->xclient.data.l[2] ==
1056                 prop_atoms.net_wm_moveresize_size_right ||
1057                 (Atom)e->xclient.data.l[2] ==
1058                 prop_atoms.net_wm_moveresize_size_right ||
1059                 (Atom)e->xclient.data.l[2] ==
1060                 prop_atoms.net_wm_moveresize_size_bottomright ||
1061                 (Atom)e->xclient.data.l[2] ==
1062                 prop_atoms.net_wm_moveresize_size_bottom ||
1063                 (Atom)e->xclient.data.l[2] ==
1064                 prop_atoms.net_wm_moveresize_size_bottomleft ||
1065                 (Atom)e->xclient.data.l[2] ==
1066                 prop_atoms.net_wm_moveresize_size_left ||
1067                 (Atom)e->xclient.data.l[2] ==
1068                 prop_atoms.net_wm_moveresize_move ||
1069                 (Atom)e->xclient.data.l[2] ==
1070                 prop_atoms.net_wm_moveresize_size_keyboard ||
1071                 (Atom)e->xclient.data.l[2] ==
1072                 prop_atoms.net_wm_moveresize_move_keyboard) {
1073
1074                 moveresize_start(client, e->xclient.data.l[0],
1075                                  e->xclient.data.l[1], e->xclient.data.l[3],
1076                                  e->xclient.data.l[2]);
1077             }
1078             else if ((Atom)e->xclient.data.l[2] ==
1079                      prop_atoms.net_wm_moveresize_cancel)
1080                 moveresize_end(TRUE);
1081         } else if (msgtype == prop_atoms.net_moveresize_window) {
1082             gint oldg = client->gravity;
1083             gint tmpg, x, y, w, h;
1084
1085             if (e->xclient.data.l[0] & 0xff)
1086                 tmpg = e->xclient.data.l[0] & 0xff;
1087             else
1088                 tmpg = oldg;
1089
1090             if (e->xclient.data.l[0] & 1 << 8)
1091                 x = e->xclient.data.l[1];
1092             else
1093                 x = client->area.x;
1094             if (e->xclient.data.l[0] & 1 << 9)
1095                 y = e->xclient.data.l[2];
1096             else
1097                 y = client->area.y;
1098             if (e->xclient.data.l[0] & 1 << 10)
1099                 w = e->xclient.data.l[3];
1100             else
1101                 w = client->area.width;
1102             if (e->xclient.data.l[0] & 1 << 11)
1103                 h = e->xclient.data.l[4];
1104             else
1105                 h = client->area.height;
1106             client->gravity = tmpg;
1107
1108             {
1109                 gint newx = x;
1110                 gint newy = y;
1111                 gint fw = w +
1112                      client->frame->size.left + client->frame->size.right;
1113                 gint fh = h +
1114                      client->frame->size.top + client->frame->size.bottom;
1115                 client_find_onscreen(client, &newx, &newy, fw, fh,
1116                                      client_normal(client));
1117                 if (e->xclient.data.l[0] & 1 << 8)
1118                     x = newx;
1119                 if (e->xclient.data.l[0] & 1 << 9)
1120                     y = newy;
1121             }
1122
1123             client_configure(client, OB_CORNER_TOPLEFT,
1124                              x, y, w, h, FALSE, TRUE);
1125
1126             client->gravity = oldg;
1127         }
1128         break;
1129     case PropertyNotify:
1130         /* validate cuz we query stuff off the client here */
1131         if (!client_validate(client)) break;
1132   
1133         /* compress changes to a single property into a single change */
1134         while (XCheckTypedWindowEvent(ob_display, client->window,
1135                                       e->type, &ce)) {
1136             Atom a, b;
1137
1138             /* XXX: it would be nice to compress ALL changes to a property,
1139                not just changes in a row without other props between. */
1140
1141             a = ce.xproperty.atom;
1142             b = e->xproperty.atom;
1143
1144             if (a == b)
1145                 continue;
1146             if ((a == prop_atoms.net_wm_name ||
1147                  a == prop_atoms.wm_name ||
1148                  a == prop_atoms.net_wm_icon_name ||
1149                  a == prop_atoms.wm_icon_name)
1150                 &&
1151                 (b == prop_atoms.net_wm_name ||
1152                  b == prop_atoms.wm_name ||
1153                  b == prop_atoms.net_wm_icon_name ||
1154                  b == prop_atoms.wm_icon_name)) {
1155                 continue;
1156             }
1157             if (a == prop_atoms.net_wm_icon &&
1158                 b == prop_atoms.net_wm_icon)
1159                 continue;
1160
1161             XPutBackEvent(ob_display, &ce);
1162             break;
1163         }
1164
1165         msgtype = e->xproperty.atom;
1166         if (msgtype == XA_WM_NORMAL_HINTS) {
1167             client_update_normal_hints(client);
1168             /* normal hints can make a window non-resizable */
1169             client_setup_decor_and_functions(client);
1170         } else if (msgtype == XA_WM_HINTS) {
1171             client_update_wmhints(client);
1172         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1173             client_update_transient_for(client);
1174             client_get_type(client);
1175             /* type may have changed, so update the layer */
1176             client_calc_layer(client);
1177             client_setup_decor_and_functions(client);
1178         } else if (msgtype == prop_atoms.net_wm_name ||
1179                    msgtype == prop_atoms.wm_name ||
1180                    msgtype == prop_atoms.net_wm_icon_name ||
1181                    msgtype == prop_atoms.wm_icon_name) {
1182             client_update_title(client);
1183         } else if (msgtype == prop_atoms.wm_class) {
1184             client_update_class(client);
1185         } else if (msgtype == prop_atoms.wm_protocols) {
1186             client_update_protocols(client);
1187             client_setup_decor_and_functions(client);
1188         }
1189         else if (msgtype == prop_atoms.net_wm_strut) {
1190             client_update_strut(client);
1191         }
1192         else if (msgtype == prop_atoms.net_wm_icon) {
1193             client_update_icons(client);
1194         }
1195         else if (msgtype == prop_atoms.net_wm_user_time) {
1196             client_update_user_time(client);
1197         }
1198 #ifdef SYNC
1199         else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1200             client_update_sync_request_counter(client);
1201         }
1202 #endif
1203         else if (msgtype == prop_atoms.sm_client_id) {
1204             client_update_sm_client_id(client);
1205         }
1206     case ColormapNotify:
1207         client_update_colormap(client, e->xcolormap.colormap);
1208         break;
1209     default:
1210         ;
1211 #ifdef SHAPE
1212         if (extensions_shape && e->type == extensions_shape_event_basep) {
1213             client->shaped = ((XShapeEvent*)e)->shaped;
1214             frame_adjust_shape(client->frame);
1215         }
1216 #endif
1217     }
1218 }
1219
1220 static void event_handle_dock(ObDock *s, XEvent *e)
1221 {
1222     switch (e->type) {
1223     case ButtonPress:
1224         if (e->xbutton.button == 1)
1225             stacking_raise(DOCK_AS_WINDOW(s));
1226         else if (e->xbutton.button == 2)
1227             stacking_lower(DOCK_AS_WINDOW(s));
1228         break;
1229     case EnterNotify:
1230         dock_hide(FALSE);
1231         break;
1232     case LeaveNotify:
1233         dock_hide(TRUE);
1234         break;
1235     }
1236 }
1237
1238 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1239 {
1240     switch (e->type) {
1241     case MotionNotify:
1242         dock_app_drag(app, &e->xmotion);
1243         break;
1244     case UnmapNotify:
1245         if (app->ignore_unmaps) {
1246             app->ignore_unmaps--;
1247             break;
1248         }
1249         dock_remove(app, TRUE);
1250         break;
1251     case DestroyNotify:
1252         dock_remove(app, FALSE);
1253         break;
1254     case ReparentNotify:
1255         dock_remove(app, FALSE);
1256         break;
1257     case ConfigureNotify:
1258         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1259         break;
1260     }
1261 }
1262
1263 ObMenuFrame* find_active_menu()
1264 {
1265     GList *it;
1266     ObMenuFrame *ret = NULL;
1267
1268     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1269         ret = it->data;
1270         if (ret->selected)
1271             break;
1272         ret = NULL;
1273     }
1274     return ret;
1275 }
1276
1277 ObMenuFrame* find_active_or_last_menu()
1278 {
1279     ObMenuFrame *ret = NULL;
1280
1281     ret = find_active_menu();
1282     if (!ret && menu_frame_visible)
1283         ret = menu_frame_visible->data;
1284     return ret;
1285 }
1286
1287 static void event_handle_menu(XEvent *ev)
1288 {
1289     ObMenuFrame *f;
1290     ObMenuEntryFrame *e;
1291
1292     switch (ev->type) {
1293     case ButtonRelease:
1294         if (menu_can_hide) {
1295             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1296                                             ev->xbutton.y_root)))
1297                 menu_entry_frame_execute(e, ev->xbutton.state,
1298                                          ev->xbutton.time);
1299             else
1300                 menu_frame_hide_all();
1301         }
1302         break;
1303     case EnterNotify:
1304         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1305             if (e->ignore_enters)
1306                 --e->ignore_enters;
1307             else
1308                 menu_frame_select(e->frame, e);
1309         }
1310         break;
1311     case LeaveNotify:
1312         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1313             (f = find_active_menu()) && f->selected == e &&
1314             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1315         {
1316             menu_frame_select(e->frame, NULL);
1317         }
1318     case MotionNotify:   
1319         if ((e = menu_entry_frame_under(ev->xmotion.x_root,   
1320                                         ev->xmotion.y_root)))
1321             menu_frame_select(e->frame, e);   
1322         break;
1323     case KeyPress:
1324         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1325             menu_frame_hide_all();
1326         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1327             ObMenuFrame *f;
1328             if ((f = find_active_menu()))
1329                 menu_entry_frame_execute(f->selected, ev->xkey.state,
1330                                          ev->xkey.time);
1331         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1332             ObMenuFrame *f;
1333             if ((f = find_active_or_last_menu()) && f->parent)
1334                 menu_frame_select(f, NULL);
1335         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1336             ObMenuFrame *f;
1337             if ((f = find_active_or_last_menu()) && f->child)
1338                 menu_frame_select_next(f->child);
1339         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1340             ObMenuFrame *f;
1341             if ((f = find_active_or_last_menu()))
1342                 menu_frame_select_previous(f);
1343         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1344             ObMenuFrame *f;
1345             if ((f = find_active_or_last_menu()))
1346                 menu_frame_select_next(f);
1347         }
1348         break;
1349     }
1350 }
1351
1352 static gboolean menu_hide_delay_func(gpointer data)
1353 {
1354     menu_can_hide = TRUE;
1355     return FALSE; /* no repeat */
1356 }
1357
1358 static void focus_delay_dest(gpointer data)
1359 {
1360     g_free(data);
1361 }
1362
1363 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1364 {
1365     const ObFocusDelayData *f1 = d1;
1366     return f1->client == d2;
1367 }
1368
1369 static gboolean focus_delay_func(gpointer data)
1370 {
1371     ObFocusDelayData *d = data;
1372     Time old = event_curtime;
1373
1374     event_curtime = d->time;
1375     if (focus_client != d->client) {
1376         if (client_focus(d->client) && config_focus_raise)
1377             client_raise(d->client);
1378     }
1379     event_curtime = old;
1380     return FALSE; /* no repeat */
1381 }
1382
1383 static void focus_delay_client_dest(ObClient *client, gpointer data)
1384 {
1385     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1386                                      client, FALSE);
1387 }
1388
1389 void event_halt_focus_delay()
1390 {
1391     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1392 }
1393
1394 void event_ignore_queued_enters()
1395 {
1396     GSList *saved = NULL, *it;
1397     XEvent *e;
1398                 
1399     XSync(ob_display, FALSE);
1400
1401     /* count the events */
1402     while (TRUE) {
1403         e = g_new(XEvent, 1);
1404         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1405             ObWindow *win;
1406             
1407             win = g_hash_table_lookup(window_map, &e->xany.window);
1408             if (win && WINDOW_IS_CLIENT(win))
1409                 ++ignore_enter_focus;
1410             
1411             saved = g_slist_append(saved, e);
1412         } else {
1413             g_free(e);
1414             break;
1415         }
1416     }
1417     /* put the events back */
1418     for (it = saved; it; it = g_slist_next(it)) {
1419         XPutBackEvent(ob_display, it->data);
1420         g_free(it->data);
1421     }
1422     g_slist_free(saved);
1423 }
1424
1425 gboolean event_time_after(Time t1, Time t2)
1426 {
1427     g_assert(t1 != CurrentTime);
1428     g_assert(t2 != CurrentTime);
1429
1430     /*
1431       Timestamp values wrap around (after about 49.7 days). The server, given
1432       its current time is represented by timestamp T, always interprets
1433       timestamps from clients by treating half of the timestamp space as being
1434       later in time than T.
1435       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1436     */
1437
1438     /* TIME_HALF is half of the number space of a Time type variable */
1439 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1440
1441     if (t2 >= TIME_HALF)
1442         /* t2 is in the second half so t1 might wrap around and be smaller than
1443            t2 */
1444         return t1 >= t2 || t1 < (t2 + TIME_HALF);
1445     else
1446         /* t2 is in the first half so t1 has to come after it */
1447         return t1 >= t2 && t1 < (t2 + TIME_HALF);
1448 }