]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
animate iconify/reestore. yeah.
[mikachu/openbox.git] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    event.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "event.h"
21 #include "debug.h"
22 #include "window.h"
23 #include "openbox.h"
24 #include "dock.h"
25 #include "client.h"
26 #include "xerror.h"
27 #include "prop.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "frame.h"
31 #include "menu.h"
32 #include "menuframe.h"
33 #include "keyboard.h"
34 #include "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 the keyboard interactive action uses the event then dont
550                use it for bindings. likewise is moveresize uses the event. */
551             if (!keyboard_process_interactive_grab(e, &client) &&
552                 !(moveresize_in_progress && moveresize_event(e)))
553             {
554                 if (moveresize_in_progress)
555                     /* make further actions work on the client being
556                        moved/resized */
557                     client = moveresize_client;
558
559
560                 menu_can_hide = FALSE;
561                 ob_main_loop_timeout_add(ob_main_loop,
562                                          config_menu_hide_delay * 1000,
563                                          menu_hide_delay_func,
564                                          NULL, g_direct_equal, NULL);
565
566                 if (e->type == ButtonPress || e->type == ButtonRelease ||
567                     e->type == MotionNotify) {
568                     mouse_event(client, e);
569                 } else if (e->type == KeyPress) {
570                     keyboard_event((focus_cycle_target ? focus_cycle_target :
571                                     (client ? client : focus_client)), e);
572                 }
573             }
574         }
575     }
576     /* if something happens and it's not from an XEvent, then we don't know
577        the time */
578     event_curtime = CurrentTime;
579 }
580
581 static void event_handle_root(XEvent *e)
582 {
583     Atom msgtype;
584      
585     switch(e->type) {
586     case SelectionClear:
587         ob_debug("Another WM has requested to replace us. Exiting.\n");
588         ob_exit_replace();
589         break;
590
591     case ClientMessage:
592         if (e->xclient.format != 32) break;
593
594         msgtype = e->xclient.message_type;
595         if (msgtype == prop_atoms.net_current_desktop) {
596             guint d = e->xclient.data.l[0];
597             if (d < screen_num_desktops) {
598                 event_curtime = e->xclient.data.l[1];
599                 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
600                 screen_set_desktop(d);
601             }
602         } else if (msgtype == prop_atoms.net_number_of_desktops) {
603             guint d = e->xclient.data.l[0];
604             if (d > 0)
605                 screen_set_num_desktops(d);
606         } else if (msgtype == prop_atoms.net_showing_desktop) {
607             screen_show_desktop(e->xclient.data.l[0] != 0, TRUE);
608         } else if (msgtype == prop_atoms.ob_control) {
609             if (e->xclient.data.l[0] == 1)
610                 ob_reconfigure();
611             else if (e->xclient.data.l[0] == 2)
612                 ob_restart();
613         }
614         break;
615     case PropertyNotify:
616         if (e->xproperty.atom == prop_atoms.net_desktop_names)
617             screen_update_desktop_names();
618         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
619             screen_update_layout();
620         break;
621     case ConfigureNotify:
622 #ifdef XRANDR
623         XRRUpdateConfiguration(e);
624 #endif
625         screen_resize();
626         break;
627     default:
628         ;
629     }
630 }
631
632 static void event_handle_group(ObGroup *group, XEvent *e)
633 {
634     GSList *it;
635
636     g_assert(e->type == PropertyNotify);
637
638     for (it = group->members; it; it = g_slist_next(it))
639         event_handle_client(it->data, e);
640 }
641
642 void event_enter_client(ObClient *client)
643 {
644     g_assert(config_focus_follow);
645
646     if (client_normal(client) && client_can_focus(client)) {
647         if (config_focus_delay) {
648             ObFocusDelayData *data;
649
650             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
651
652             data = g_new(ObFocusDelayData, 1);
653             data->client = client;
654             data->time = event_curtime;
655
656             ob_main_loop_timeout_add(ob_main_loop,
657                                      config_focus_delay,
658                                      focus_delay_func,
659                                      data, focus_delay_cmp, focus_delay_dest);
660         } else {
661             ObFocusDelayData data;
662             data.client = client;
663             data.time = event_curtime;
664             focus_delay_func(&data);
665         }
666     }
667 }
668
669 static void event_handle_client(ObClient *client, XEvent *e)
670 {
671     XEvent ce;
672     Atom msgtype;
673     gint i=0;
674     ObFrameContext con;
675      
676     switch (e->type) {
677     case ButtonPress:
678     case ButtonRelease:
679         /* Wheel buttons don't draw because they are an instant click, so it
680            is a waste of resources to go drawing it. */
681         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
682             con = frame_context(client, e->xbutton.window);
683             con = mouse_button_frame_context(con, e->xbutton.button);
684             switch (con) {
685             case OB_FRAME_CONTEXT_MAXIMIZE:
686                 client->frame->max_press = (e->type == ButtonPress);
687                 framerender_frame(client->frame);
688                 break;
689             case OB_FRAME_CONTEXT_CLOSE:
690                 client->frame->close_press = (e->type == ButtonPress);
691                 framerender_frame(client->frame);
692                 break;
693             case OB_FRAME_CONTEXT_ICONIFY:
694                 client->frame->iconify_press = (e->type == ButtonPress);
695                 framerender_frame(client->frame);
696                 break;
697             case OB_FRAME_CONTEXT_ALLDESKTOPS:
698                 client->frame->desk_press = (e->type == ButtonPress);
699                 framerender_frame(client->frame);
700                 break; 
701             case OB_FRAME_CONTEXT_SHADE:
702                 client->frame->shade_press = (e->type == ButtonPress);
703                 framerender_frame(client->frame);
704                 break;
705             default:
706                 /* nothing changes with clicks for any other contexts */
707                 break;
708             }
709         }
710         break;
711     case LeaveNotify:
712         con = frame_context(client, e->xcrossing.window);
713         switch (con) {
714         case OB_FRAME_CONTEXT_MAXIMIZE:
715             client->frame->max_hover = FALSE;
716             frame_adjust_state(client->frame);
717             break;
718         case OB_FRAME_CONTEXT_ALLDESKTOPS:
719             client->frame->desk_hover = FALSE;
720             frame_adjust_state(client->frame);
721             break;
722         case OB_FRAME_CONTEXT_SHADE:
723             client->frame->shade_hover = FALSE;
724             frame_adjust_state(client->frame);
725             break;
726         case OB_FRAME_CONTEXT_ICONIFY:
727             client->frame->iconify_hover = FALSE;
728             frame_adjust_state(client->frame);
729             break;
730         case OB_FRAME_CONTEXT_CLOSE:
731             client->frame->close_hover = FALSE;
732             frame_adjust_state(client->frame);
733             break;
734         case OB_FRAME_CONTEXT_FRAME:
735             ob_debug_type(OB_DEBUG_FOCUS,
736                           "%sNotify mode %d detail %d on %lx\n",
737                           (e->type == EnterNotify ? "Enter" : "Leave"),
738                           e->xcrossing.mode,
739                           e->xcrossing.detail, (client?client->window:0));
740             if (keyboard_interactively_grabbed())
741                 break;
742             if (config_focus_follow && config_focus_delay &&
743                 /* leave inferior events can happen when the mouse goes onto
744                    the window's border and then into the window before the
745                    delay is up */
746                 e->xcrossing.detail != NotifyInferior)
747             {
748                 ob_main_loop_timeout_remove_data(ob_main_loop,
749                                                  focus_delay_func,
750                                                  client, FALSE);
751             }
752             break;
753         default:
754             break;
755         }
756         break;
757     case EnterNotify:
758     {
759         gboolean nofocus = FALSE;
760
761         if (ignore_enter_focus) {
762             ignore_enter_focus--;
763             nofocus = TRUE;
764         }
765
766         con = frame_context(client, e->xcrossing.window);
767         switch (con) {
768         case OB_FRAME_CONTEXT_MAXIMIZE:
769             client->frame->max_hover = TRUE;
770             frame_adjust_state(client->frame);
771             break;
772         case OB_FRAME_CONTEXT_ALLDESKTOPS:
773             client->frame->desk_hover = TRUE;
774             frame_adjust_state(client->frame);
775             break;
776         case OB_FRAME_CONTEXT_SHADE:
777             client->frame->shade_hover = TRUE;
778             frame_adjust_state(client->frame);
779             break;
780         case OB_FRAME_CONTEXT_ICONIFY:
781             client->frame->iconify_hover = TRUE;
782             frame_adjust_state(client->frame);
783             break;
784         case OB_FRAME_CONTEXT_CLOSE:
785             client->frame->close_hover = TRUE;
786             frame_adjust_state(client->frame);
787             break;
788         case OB_FRAME_CONTEXT_FRAME:
789             if (keyboard_interactively_grabbed())
790                 break;
791             if (e->xcrossing.mode == NotifyGrab ||
792                 e->xcrossing.mode == NotifyUngrab ||
793                 /*ignore enters when we're already in the window */
794                 e->xcrossing.detail == NotifyInferior)
795             {
796                 ob_debug_type(OB_DEBUG_FOCUS,
797                               "%sNotify mode %d detail %d on %lx IGNORED\n",
798                               (e->type == EnterNotify ? "Enter" : "Leave"),
799                               e->xcrossing.mode,
800                               e->xcrossing.detail, client?client->window:0);
801             } else {
802                 ob_debug_type(OB_DEBUG_FOCUS,
803                               "%sNotify mode %d detail %d on %lx, "
804                               "focusing window: %d\n",
805                               (e->type == EnterNotify ? "Enter" : "Leave"),
806                               e->xcrossing.mode,
807                               e->xcrossing.detail, (client?client->window:0),
808                               !nofocus);
809                 if (!nofocus && config_focus_follow)
810                     event_enter_client(client);
811             }
812             break;
813         default:
814             break;
815         }
816         break;
817     }
818     case ConfigureRequest:
819         /* compress these */
820         while (XCheckTypedWindowEvent(ob_display, client->window,
821                                       ConfigureRequest, &ce)) {
822             ++i;
823             /* XXX if this causes bad things.. we can compress config req's
824                with the same mask. */
825             e->xconfigurerequest.value_mask |=
826                 ce.xconfigurerequest.value_mask;
827             if (ce.xconfigurerequest.value_mask & CWX)
828                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
829             if (ce.xconfigurerequest.value_mask & CWY)
830                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
831             if (ce.xconfigurerequest.value_mask & CWWidth)
832                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
833             if (ce.xconfigurerequest.value_mask & CWHeight)
834                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
835             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
836                 e->xconfigurerequest.border_width =
837                     ce.xconfigurerequest.border_width;
838             if (ce.xconfigurerequest.value_mask & CWStackMode)
839                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
840         }
841
842         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
843         if (client->iconic || client->shaded) return;
844
845         /* resize, then move, as specified in the EWMH section 7.7 */
846         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
847                                                CWX | CWY |
848                                                CWBorderWidth)) {
849             gint x, y, w, h;
850
851             if (e->xconfigurerequest.value_mask & CWBorderWidth)
852                 client->border_width = e->xconfigurerequest.border_width;
853
854             x = (e->xconfigurerequest.value_mask & CWX) ?
855                 e->xconfigurerequest.x : client->area.x;
856             y = (e->xconfigurerequest.value_mask & CWY) ?
857                 e->xconfigurerequest.y : client->area.y;
858             w = (e->xconfigurerequest.value_mask & CWWidth) ?
859                 e->xconfigurerequest.width : client->area.width;
860             h = (e->xconfigurerequest.value_mask & CWHeight) ?
861                 e->xconfigurerequest.height : client->area.height;
862
863             ob_debug("ConfigureRequest x %d %d y %d %d\n",
864                      e->xconfigurerequest.value_mask & CWX, x,
865                      e->xconfigurerequest.value_mask & CWY, y);
866
867             client_find_onscreen(client, &x, &y, w, h, FALSE);
868             client_configure_full(client, x, y, w, h, FALSE, TRUE, TRUE);
869         }
870
871         if (e->xconfigurerequest.value_mask & CWStackMode) {
872             switch (e->xconfigurerequest.detail) {
873             case Below:
874             case BottomIf:
875                 /* Apps are so rude. And this is totally disconnected from
876                    activation/focus. Bleh. */
877                 /*client_lower(client);*/
878                 break;
879
880             case Above:
881             case TopIf:
882             default:
883                 /* Apps are so rude. And this is totally disconnected from
884                    activation/focus. Bleh. */
885                 /*client_raise(client);*/
886                 break;
887             }
888         }
889         break;
890     case UnmapNotify:
891         if (client->ignore_unmaps) {
892             client->ignore_unmaps--;
893             break;
894         }
895         ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
896                  "ignores left %d\n",
897                  client->window, e->xunmap.event, e->xunmap.from_configure,
898                  client->ignore_unmaps);
899         client_unmanage(client);
900         break;
901     case DestroyNotify:
902         ob_debug("DestroyNotify for window 0x%x\n", client->window);
903         client_unmanage(client);
904         break;
905     case ReparentNotify:
906         /* this is when the client is first taken captive in the frame */
907         if (e->xreparent.parent == client->frame->plate) break;
908
909         /*
910           This event is quite rare and is usually handled in unmapHandler.
911           However, if the window is unmapped when the reparent event occurs,
912           the window manager never sees it because an unmap event is not sent
913           to an already unmapped window.
914         */
915
916         /* we don't want the reparent event, put it back on the stack for the
917            X server to deal with after we unmanage the window */
918         XPutBackEvent(ob_display, e);
919      
920         ob_debug("ReparentNotify for window 0x%x\n", client->window);
921         client_unmanage(client);
922         break;
923     case MapRequest:
924         ob_debug("MapRequest for 0x%lx\n", client->window);
925         if (!client->iconic) break; /* this normally doesn't happen, but if it
926                                        does, we don't want it!
927                                        it can happen now when the window is on
928                                        another desktop, but we still don't
929                                        want it! */
930         client_activate(client, FALSE, TRUE);
931         break;
932     case ClientMessage:
933         /* validate cuz we query stuff off the client here */
934         if (!client_validate(client)) break;
935
936         if (e->xclient.format != 32) return;
937
938         msgtype = e->xclient.message_type;
939         if (msgtype == prop_atoms.wm_change_state) {
940             /* compress changes into a single change */
941             while (XCheckTypedWindowEvent(ob_display, client->window,
942                                           e->type, &ce)) {
943                 /* XXX: it would be nice to compress ALL messages of a
944                    type, not just messages in a row without other
945                    message types between. */
946                 if (ce.xclient.message_type != msgtype) {
947                     XPutBackEvent(ob_display, &ce);
948                     break;
949                 }
950                 e->xclient = ce.xclient;
951             }
952             client_set_wm_state(client, e->xclient.data.l[0]);
953         } else if (msgtype == prop_atoms.net_wm_desktop) {
954             /* compress changes into a single change */
955             while (XCheckTypedWindowEvent(ob_display, client->window,
956                                           e->type, &ce)) {
957                 /* XXX: it would be nice to compress ALL messages of a
958                    type, not just messages in a row without other
959                    message types between. */
960                 if (ce.xclient.message_type != msgtype) {
961                     XPutBackEvent(ob_display, &ce);
962                     break;
963                 }
964                 e->xclient = ce.xclient;
965             }
966             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
967                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
968                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
969                                    FALSE);
970         } else if (msgtype == prop_atoms.net_wm_state) {
971             /* can't compress these */
972             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
973                      (e->xclient.data.l[0] == 0 ? "Remove" :
974                       e->xclient.data.l[0] == 1 ? "Add" :
975                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
976                      e->xclient.data.l[1], e->xclient.data.l[2],
977                      client->window);
978             client_set_state(client, e->xclient.data.l[0],
979                              e->xclient.data.l[1], e->xclient.data.l[2]);
980         } else if (msgtype == prop_atoms.net_close_window) {
981             ob_debug("net_close_window for 0x%lx\n", client->window);
982             client_close(client);
983         } else if (msgtype == prop_atoms.net_active_window) {
984             ob_debug("net_active_window for 0x%lx source=%s\n",
985                      client->window,
986                      (e->xclient.data.l[0] == 0 ? "unknown" :
987                       (e->xclient.data.l[0] == 1 ? "application" :
988                        (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
989             /* XXX make use of data.l[2] ! */
990             event_curtime = e->xclient.data.l[1];
991             client_activate(client, FALSE,
992                             (e->xclient.data.l[0] == 0 ||
993                              e->xclient.data.l[0] == 2));
994         } else if (msgtype == prop_atoms.net_wm_moveresize) {
995             ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
996                      client->window, e->xclient.data.l[2]);
997             if ((Atom)e->xclient.data.l[2] ==
998                 prop_atoms.net_wm_moveresize_size_topleft ||
999                 (Atom)e->xclient.data.l[2] ==
1000                 prop_atoms.net_wm_moveresize_size_top ||
1001                 (Atom)e->xclient.data.l[2] ==
1002                 prop_atoms.net_wm_moveresize_size_topright ||
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_right ||
1007                 (Atom)e->xclient.data.l[2] ==
1008                 prop_atoms.net_wm_moveresize_size_bottomright ||
1009                 (Atom)e->xclient.data.l[2] ==
1010                 prop_atoms.net_wm_moveresize_size_bottom ||
1011                 (Atom)e->xclient.data.l[2] ==
1012                 prop_atoms.net_wm_moveresize_size_bottomleft ||
1013                 (Atom)e->xclient.data.l[2] ==
1014                 prop_atoms.net_wm_moveresize_size_left ||
1015                 (Atom)e->xclient.data.l[2] ==
1016                 prop_atoms.net_wm_moveresize_move ||
1017                 (Atom)e->xclient.data.l[2] ==
1018                 prop_atoms.net_wm_moveresize_size_keyboard ||
1019                 (Atom)e->xclient.data.l[2] ==
1020                 prop_atoms.net_wm_moveresize_move_keyboard) {
1021
1022                 moveresize_start(client, e->xclient.data.l[0],
1023                                  e->xclient.data.l[1], e->xclient.data.l[3],
1024                                  e->xclient.data.l[2]);
1025             }
1026             else if ((Atom)e->xclient.data.l[2] ==
1027                      prop_atoms.net_wm_moveresize_cancel)
1028                 moveresize_end(TRUE);
1029         } else if (msgtype == prop_atoms.net_moveresize_window) {
1030             gint grav, x, y, w, h;
1031
1032             if (e->xclient.data.l[0] & 0xff)
1033                 grav = e->xclient.data.l[0] & 0xff;
1034             else 
1035                 grav = client->gravity;
1036
1037             if (e->xclient.data.l[0] & 1 << 8)
1038                 x = e->xclient.data.l[1];
1039             else
1040                 x = client->area.x;
1041             if (e->xclient.data.l[0] & 1 << 9)
1042                 y = e->xclient.data.l[2];
1043             else
1044                 y = client->area.y;
1045             if (e->xclient.data.l[0] & 1 << 10)
1046                 w = e->xclient.data.l[3];
1047             else
1048                 w = client->area.width;
1049             if (e->xclient.data.l[0] & 1 << 11)
1050                 h = e->xclient.data.l[4];
1051             else
1052                 h = client->area.height;
1053
1054             ob_debug("MOVERESIZE x %d %d y %d %d\n",
1055                      e->xclient.data.l[0] & 1 << 8, x,
1056                      e->xclient.data.l[0] & 1 << 9, y);
1057             client_convert_gravity(client, grav, &x, &y, w, h);
1058             client_find_onscreen(client, &x, &y, w, h, FALSE);
1059             client_configure(client, x, y, w, h, FALSE, TRUE);
1060         }
1061         break;
1062     case PropertyNotify:
1063         /* validate cuz we query stuff off the client here */
1064         if (!client_validate(client)) break;
1065   
1066         /* compress changes to a single property into a single change */
1067         while (XCheckTypedWindowEvent(ob_display, client->window,
1068                                       e->type, &ce)) {
1069             Atom a, b;
1070
1071             /* XXX: it would be nice to compress ALL changes to a property,
1072                not just changes in a row without other props between. */
1073
1074             a = ce.xproperty.atom;
1075             b = e->xproperty.atom;
1076
1077             if (a == b)
1078                 continue;
1079             if ((a == prop_atoms.net_wm_name ||
1080                  a == prop_atoms.wm_name ||
1081                  a == prop_atoms.net_wm_icon_name ||
1082                  a == prop_atoms.wm_icon_name)
1083                 &&
1084                 (b == prop_atoms.net_wm_name ||
1085                  b == prop_atoms.wm_name ||
1086                  b == prop_atoms.net_wm_icon_name ||
1087                  b == prop_atoms.wm_icon_name)) {
1088                 continue;
1089             }
1090             if (a == prop_atoms.net_wm_icon &&
1091                 b == prop_atoms.net_wm_icon)
1092                 continue;
1093
1094             XPutBackEvent(ob_display, &ce);
1095             break;
1096         }
1097
1098         msgtype = e->xproperty.atom;
1099         if (msgtype == XA_WM_NORMAL_HINTS) {
1100             client_update_normal_hints(client);
1101             /* normal hints can make a window non-resizable */
1102             client_setup_decor_and_functions(client);
1103         } else if (msgtype == XA_WM_HINTS) {
1104             client_update_wmhints(client);
1105         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1106             client_update_transient_for(client);
1107             client_get_type(client);
1108             /* type may have changed, so update the layer */
1109             client_calc_layer(client);
1110             client_setup_decor_and_functions(client);
1111         } else if (msgtype == prop_atoms.net_wm_name ||
1112                    msgtype == prop_atoms.wm_name ||
1113                    msgtype == prop_atoms.net_wm_icon_name ||
1114                    msgtype == prop_atoms.wm_icon_name) {
1115             client_update_title(client);
1116         } else if (msgtype == prop_atoms.wm_class) {
1117             client_update_class(client);
1118         } else if (msgtype == prop_atoms.wm_protocols) {
1119             client_update_protocols(client);
1120             client_setup_decor_and_functions(client);
1121         }
1122         else if (msgtype == prop_atoms.net_wm_strut) {
1123             client_update_strut(client);
1124         }
1125         else if (msgtype == prop_atoms.net_wm_icon) {
1126             client_update_icons(client);
1127         }
1128         else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1129             client_update_icon_geometry(client);
1130         }
1131         else if (msgtype == prop_atoms.net_wm_user_time) {
1132             client_update_user_time(client);
1133         }
1134 #ifdef SYNC
1135         else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1136             client_update_sync_request_counter(client);
1137         }
1138 #endif
1139         else if (msgtype == prop_atoms.sm_client_id) {
1140             client_update_sm_client_id(client);
1141         }
1142     case ColormapNotify:
1143         client_update_colormap(client, e->xcolormap.colormap);
1144         break;
1145     default:
1146         ;
1147 #ifdef SHAPE
1148         if (extensions_shape && e->type == extensions_shape_event_basep) {
1149             client->shaped = ((XShapeEvent*)e)->shaped;
1150             frame_adjust_shape(client->frame);
1151         }
1152 #endif
1153     }
1154 }
1155
1156 static void event_handle_dock(ObDock *s, XEvent *e)
1157 {
1158     switch (e->type) {
1159     case ButtonPress:
1160         if (e->xbutton.button == 1)
1161             stacking_raise(DOCK_AS_WINDOW(s));
1162         else if (e->xbutton.button == 2)
1163             stacking_lower(DOCK_AS_WINDOW(s));
1164         break;
1165     case EnterNotify:
1166         dock_hide(FALSE);
1167         break;
1168     case LeaveNotify:
1169         dock_hide(TRUE);
1170         break;
1171     }
1172 }
1173
1174 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1175 {
1176     switch (e->type) {
1177     case MotionNotify:
1178         dock_app_drag(app, &e->xmotion);
1179         break;
1180     case UnmapNotify:
1181         if (app->ignore_unmaps) {
1182             app->ignore_unmaps--;
1183             break;
1184         }
1185         dock_remove(app, TRUE);
1186         break;
1187     case DestroyNotify:
1188         dock_remove(app, FALSE);
1189         break;
1190     case ReparentNotify:
1191         dock_remove(app, FALSE);
1192         break;
1193     case ConfigureNotify:
1194         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1195         break;
1196     }
1197 }
1198
1199 static ObMenuFrame* find_active_menu()
1200 {
1201     GList *it;
1202     ObMenuFrame *ret = NULL;
1203
1204     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1205         ret = it->data;
1206         if (ret->selected)
1207             break;
1208         ret = NULL;
1209     }
1210     return ret;
1211 }
1212
1213 static ObMenuFrame* find_active_or_last_menu()
1214 {
1215     ObMenuFrame *ret = NULL;
1216
1217     ret = find_active_menu();
1218     if (!ret && menu_frame_visible)
1219         ret = menu_frame_visible->data;
1220     return ret;
1221 }
1222
1223 static gboolean event_handle_menu_keyboard(XEvent *ev)
1224 {
1225     guint keycode, state;
1226     gunichar unikey;
1227     ObMenuFrame *frame;
1228     gboolean ret = TRUE;
1229
1230     keycode = ev->xkey.keycode;
1231     state = ev->xkey.state;
1232     unikey = translate_unichar(keycode);
1233
1234     frame = find_active_or_last_menu();
1235     if (frame == NULL)
1236         ret = FALSE;
1237
1238     else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
1239         /* Escape closes the active menu */
1240         menu_frame_hide(frame);
1241     }
1242
1243     else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1244                                                       state == ControlMask))
1245     {
1246         /* Enter runs the active item or goes into the submenu.
1247            Control-Enter runs it without closing the menu. */
1248         if (frame->child)
1249             menu_frame_select_next(frame->child);
1250         else
1251             menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1252     }
1253
1254     else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1255         /* Left goes to the parent menu */
1256         menu_frame_select(frame, NULL, TRUE);
1257     }
1258
1259     else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1260         /* Right goes to the selected submenu */
1261         if (frame->child) menu_frame_select_next(frame->child);
1262     }
1263
1264     else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1265         menu_frame_select_previous(frame);
1266     }
1267
1268     else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1269         menu_frame_select_next(frame);
1270     }
1271
1272     /* keyboard accelerator shortcuts. */
1273     else if (ev->xkey.state == 0 &&
1274              /* was it a valid key? */
1275              unikey != 0 &&
1276              /* don't bother if the menu is empty. */
1277              frame->entries)
1278     {
1279         GList *start;
1280         GList *it;
1281         ObMenuEntryFrame *found = NULL;
1282         guint num_found = 0;
1283
1284         /* start after the selected one */
1285         start = frame->entries;
1286         if (frame->selected) {
1287             for (it = start; frame->selected != it->data; it = g_list_next(it))
1288                 g_assert(it != NULL); /* nothing was selected? */
1289             /* next with wraparound */
1290             start = g_list_next(it);
1291             if (start == NULL) start = frame->entries;
1292         }
1293
1294         it = start;
1295         do {
1296             ObMenuEntryFrame *e = it->data;
1297             gunichar entrykey = 0;
1298
1299             if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1300                 e->entry->data.normal.enabled)
1301                 entrykey = e->entry->data.normal.shortcut;
1302             else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1303                 entrykey = e->entry->data.submenu.submenu->shortcut;
1304
1305             if (unikey == entrykey) {
1306                 if (found == NULL) found = e;
1307                 ++num_found;
1308             }
1309
1310             /* next with wraparound */
1311             it = g_list_next(it);
1312             if (it == NULL) it = frame->entries;
1313         } while (it != start);
1314
1315         if (found) {
1316             if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1317                 num_found == 1)
1318             {
1319                 menu_frame_select(frame, found, TRUE);
1320                 usleep(50000);
1321                 menu_entry_frame_execute(found, state, ev->xkey.time);
1322             } else {
1323                 menu_frame_select(frame, found, TRUE);
1324                 if (num_found == 1)
1325                     menu_frame_select_next(frame->child);
1326             }
1327         } else
1328             ret = FALSE;
1329     }
1330     else
1331         ret = FALSE;
1332
1333     return ret;
1334 }
1335
1336 static gboolean event_handle_menu(XEvent *ev)
1337 {
1338     ObMenuFrame *f;
1339     ObMenuEntryFrame *e;
1340     gboolean ret = TRUE;
1341
1342     switch (ev->type) {
1343     case ButtonRelease:
1344         if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
1345             && menu_can_hide)
1346         {
1347             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1348                                             ev->xbutton.y_root)))
1349                 menu_entry_frame_execute(e, ev->xbutton.state,
1350                                          ev->xbutton.time);
1351             else
1352                 menu_frame_hide_all();
1353         }
1354         break;
1355     case EnterNotify:
1356         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1357             if (e->ignore_enters)
1358                 --e->ignore_enters;
1359             else
1360                 menu_frame_select(e->frame, e, FALSE);
1361         }
1362         break;
1363     case LeaveNotify:
1364         if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1365             (f = find_active_menu()) && f->selected == e &&
1366             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1367         {
1368             menu_frame_select(e->frame, NULL, FALSE);
1369         }
1370     case MotionNotify:   
1371         if ((e = menu_entry_frame_under(ev->xmotion.x_root,   
1372                                         ev->xmotion.y_root)))
1373             menu_frame_select(e->frame, e, FALSE);
1374         break;
1375     case KeyPress:
1376         ret = event_handle_menu_keyboard(ev);
1377         break;
1378     }
1379     return ret;
1380 }
1381
1382 static gboolean menu_hide_delay_func(gpointer data)
1383 {
1384     menu_can_hide = TRUE;
1385     return FALSE; /* no repeat */
1386 }
1387
1388 static void focus_delay_dest(gpointer data)
1389 {
1390     g_free(data);
1391 }
1392
1393 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1394 {
1395     const ObFocusDelayData *f1 = d1;
1396     return f1->client == d2;
1397 }
1398
1399 static gboolean focus_delay_func(gpointer data)
1400 {
1401     ObFocusDelayData *d = data;
1402     Time old = event_curtime;
1403
1404     event_curtime = d->time;
1405     if (focus_client != d->client) {
1406         if (client_focus(d->client) && config_focus_raise)
1407             client_raise(d->client);
1408     }
1409     event_curtime = old;
1410     return FALSE; /* no repeat */
1411 }
1412
1413 static void focus_delay_client_dest(ObClient *client, gpointer data)
1414 {
1415     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1416                                      client, FALSE);
1417 }
1418
1419 void event_halt_focus_delay()
1420 {
1421     ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1422 }
1423
1424 void event_ignore_queued_enters()
1425 {
1426     GSList *saved = NULL, *it;
1427     XEvent *e;
1428                 
1429     XSync(ob_display, FALSE);
1430
1431     /* count the events */
1432     while (TRUE) {
1433         e = g_new(XEvent, 1);
1434         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1435             ObWindow *win;
1436             
1437             win = g_hash_table_lookup(window_map, &e->xany.window);
1438             if (win && WINDOW_IS_CLIENT(win))
1439                 ++ignore_enter_focus;
1440             
1441             saved = g_slist_append(saved, e);
1442         } else {
1443             g_free(e);
1444             break;
1445         }
1446     }
1447     /* put the events back */
1448     for (it = saved; it; it = g_slist_next(it)) {
1449         XPutBackEvent(ob_display, it->data);
1450         g_free(it->data);
1451     }
1452     g_slist_free(saved);
1453 }
1454
1455 gboolean event_time_after(Time t1, Time t2)
1456 {
1457     g_assert(t1 != CurrentTime);
1458     g_assert(t2 != CurrentTime);
1459
1460     /*
1461       Timestamp values wrap around (after about 49.7 days). The server, given
1462       its current time is represented by timestamp T, always interprets
1463       timestamps from clients by treating half of the timestamp space as being
1464       later in time than T.
1465       - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1466     */
1467
1468     /* TIME_HALF is half of the number space of a Time type variable */
1469 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1470
1471     if (t2 >= TIME_HALF)
1472         /* t2 is in the second half so t1 might wrap around and be smaller than
1473            t2 */
1474         return t1 >= t2 || t1 < (t2 + TIME_HALF);
1475     else
1476         /* t2 is in the first half so t1 has to come after it */
1477         return t1 >= t2 && t1 < (t2 + TIME_HALF);
1478 }