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