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