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