ultra-keyboard-controlled-menus
[dana/openbox.git] / openbox / event.c
1 #include "debug.h"
2 #include "openbox.h"
3 #include "dock.h"
4 #include "client.h"
5 #include "xerror.h"
6 #include "prop.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "frame.h"
10 #include "menu.h"
11 #include "menuframe.h"
12 #include "keyboard.h"
13 #include "mouse.h"
14 #include "framerender.h"
15 #include "focus.h"
16 #include "moveresize.h"
17 #include "stacking.h"
18 #include "extensions.h"
19 #include "timer.h"
20 #include "event.h"
21
22 #include <X11/Xlib.h>
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
25 #include <glib.h>
26
27 #ifdef USE_LIBSN
28 #  include <libsn/sn.h>
29 #endif
30
31 #ifdef HAVE_SYS_SELECT_H
32 #  include <sys/select.h>
33 #endif
34 #ifdef HAVE_SIGNAL_H
35 #  include <signal.h>
36 #endif
37
38 #ifdef USE_SM
39 #include <X11/ICE/ICElib.h>
40 #endif
41
42 static void event_process(XEvent *e);
43 static void event_handle_root(XEvent *e);
44 static void event_handle_menu(XEvent *e);
45 static void event_handle_dock(ObDock *s, XEvent *e);
46 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
47 static void event_handle_client(ObClient *c, XEvent *e);
48 static void fd_event_handle();
49 #ifdef USE_SM
50 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
51                       IcePointer *watch_data);
52 #endif
53 static void find_max_fd();
54
55 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
56                             (e)->xfocus.detail == NotifyAncestor || \
57                             (e)->xfocus.detail > NotifyNonlinearVirtual)
58 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
59                              (e)->xfocus.detail == NotifyInferior || \
60                              (e)->xfocus.detail == NotifyAncestor || \
61                              (e)->xfocus.detail > NotifyNonlinearVirtual)
62
63 Time event_lasttime = 0;
64
65 /*! The value of the mask for the NumLock modifier */
66 unsigned int NumLockMask;
67 /*! The value of the mask for the ScrollLock modifier */
68 unsigned int ScrollLockMask;
69 /*! The key codes for the modifier keys */
70 static XModifierKeymap *modmap;
71 /*! Table of the constant modifier masks */
72 static const int mask_table[] = {
73     ShiftMask, LockMask, ControlMask, Mod1Mask,
74     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
75 };
76 static int mask_table_size;
77
78 static fd_set selset, allset;
79 #ifdef USE_SM
80 static IceConn ice_conn;
81 static int ice_fd;
82 #endif
83 static int max_fd, x_fd;
84 static GData *fd_handler_list;
85
86
87 #ifdef USE_SM
88 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
89                       IcePointer *watch_data)
90 {
91     if (opening) {
92         g_assert (ice_fd < 0);
93         ice_conn = conn;
94         ice_fd = IceConnectionNumber(conn);
95         FD_SET(ice_fd, &allset);
96     } else {
97         FD_CLR(ice_fd, &allset);
98         ice_fd = -1;
99     }
100     find_max_fd();
101 }
102 #endif
103
104 void event_startup()
105 {
106     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
107      
108     /* get lock masks that are defined by the display (not constant) */
109     modmap = XGetModifierMapping(ob_display);
110     g_assert(modmap);
111     if (modmap && modmap->max_keypermod > 0) {
112         size_t cnt;
113         const size_t size = mask_table_size * modmap->max_keypermod;
114         /* get the values of the keyboard lock modifiers
115            Note: Caps lock is not retrieved the same way as Scroll and Num
116            lock since it doesn't need to be. */
117         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
118         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
119                                                      XK_Scroll_Lock);
120           
121         for (cnt = 0; cnt < size; ++cnt) {
122             if (! modmap->modifiermap[cnt]) continue;
123                
124             if (num_lock == modmap->modifiermap[cnt])
125                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
126             if (scroll_lock == modmap->modifiermap[cnt])
127                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
128         }
129     }
130
131     FD_ZERO(&allset);
132     max_fd = x_fd = ConnectionNumber(ob_display);
133     FD_SET(x_fd, &allset);
134
135 #ifdef USE_SM
136     ice_fd = -1;
137     IceAddConnectionWatch(ice_watch, NULL);
138 #endif
139
140     g_datalist_init(&fd_handler_list);
141 }
142
143 void event_shutdown()
144 {
145     XFreeModifiermap(modmap);
146     g_datalist_clear(&fd_handler_list);
147 }
148
149 void event_loop()
150 {
151     XEvent e;
152     struct timeval *wait;
153     gboolean had_event = FALSE;
154
155     while (XPending(ob_display)) {
156         XNextEvent(ob_display, &e);
157
158 #ifdef USE_LIBSN
159         sn_display_process_event(ob_sn_display, &e);
160 #endif
161
162         event_process(&e);
163         had_event = TRUE;
164     }
165
166     if (!had_event) {
167         timer_dispatch((GTimeVal**)&wait);
168         selset = allset;
169         select(max_fd + 1, &selset, NULL, NULL, wait);
170
171         /* handle the X events as soon as possible? */
172         if (FD_ISSET(x_fd, &selset))
173             return;
174
175 #ifdef USE_SM
176         if (ice_fd >= 0 && FD_ISSET(ice_fd, &selset)) {
177             Bool b;
178             IceProcessMessages(ice_conn, NULL, &b);
179         }
180 #endif
181
182         fd_event_handle();
183     }
184 }
185
186 static Window event_get_window(XEvent *e)
187 {
188     Window window;
189
190     /* pick a window */
191     switch (e->type) {
192     case SelectionClear:
193         window = RootWindow(ob_display, ob_screen);
194         break;
195     case MapRequest:
196         window = e->xmap.window;
197         break;
198     case UnmapNotify:
199         window = e->xunmap.window;
200         break;
201     case DestroyNotify:
202         window = e->xdestroywindow.window;
203         break;
204     case ConfigureRequest:
205         window = e->xconfigurerequest.window;
206         break;
207     case ConfigureNotify:
208         window = e->xconfigure.window;
209         break;
210     default:
211 #ifdef XKB
212         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
213             switch (((XkbAnyEvent*)e)->xkb_type) {
214             case XkbBellNotify:
215                 window = ((XkbBellNotifyEvent*)e)->window;
216             default:
217                 window = None;
218             }
219         } else
220 #endif
221             window = e->xany.window;
222     }
223     return window;
224 }
225
226 static void event_set_lasttime(XEvent *e)
227 {
228     Time t = 0;
229
230     /* grab the lasttime and hack up the state */
231     switch (e->type) {
232     case ButtonPress:
233     case ButtonRelease:
234         t = e->xbutton.time;
235         break;
236     case KeyPress:
237         t = e->xkey.time;
238         break;
239     case KeyRelease:
240         t = e->xkey.time;
241         break;
242     case MotionNotify:
243         t = e->xmotion.time;
244         break;
245     case PropertyNotify:
246         t = e->xproperty.time;
247         break;
248     case EnterNotify:
249     case LeaveNotify:
250         t = e->xcrossing.time;
251         break;
252     default:
253         /* if more event types are anticipated, get their timestamp
254            explicitly */
255         break;
256     }
257
258     if (t > event_lasttime)
259         event_lasttime = t;
260 }
261
262 #define STRIP_MODS(s) \
263         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
264         /* kill off the Button1Mask etc, only want the modifiers */ \
265         s &= (ControlMask | ShiftMask | Mod1Mask | \
266               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
267
268 static void event_hack_mods(XEvent *e)
269 {
270     KeyCode *kp;
271     int i, k;
272
273     switch (e->type) {
274     case ButtonPress:
275     case ButtonRelease:
276         STRIP_MODS(e->xbutton.state);
277         break;
278     case KeyPress:
279         STRIP_MODS(e->xkey.state);
280         break;
281     case KeyRelease:
282         STRIP_MODS(e->xkey.state);
283         /* remove from the state the mask of the modifier being released, if
284            it is a modifier key being released (this is a little ugly..) */
285         kp = modmap->modifiermap;
286         for (i = 0; i < mask_table_size; ++i) {
287             for (k = 0; k < modmap->max_keypermod; ++k) {
288                 if (*kp == e->xkey.keycode) { /* found the keycode */
289                     /* remove the mask for it */
290                     e->xkey.state &= ~mask_table[i];
291                     /* cause the first loop to break; */
292                     i = mask_table_size;
293                     break; /* get outta here! */
294                 }
295                 ++kp;
296             }
297         }
298         break;
299     case MotionNotify:
300         STRIP_MODS(e->xmotion.state);
301 #if 0
302         /* compress events */
303         {
304             XEvent ce;
305             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
306                                           e->type, &ce)) {
307                 e->xmotion.x_root = ce.xmotion.x_root;
308                 e->xmotion.y_root = ce.xmotion.y_root;
309             }
310         }
311 #endif
312         break;
313     }
314 }
315
316 static gboolean event_ignore(XEvent *e, ObClient *client)
317 {
318     switch(e->type) {
319     case FocusIn:
320         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
321            because of RevertToPointerRoot. If the focus ends up reverting to
322            pointer root on a workspace change, then the FocusIn event that we
323            want will be of type NotifyAncestor. This situation does not occur
324            for FocusOut, so it is safely ignored there.
325         */
326         if (INVALID_FOCUSIN(e) ||
327             client == NULL) {
328 #ifdef DEBUG_FOCUS
329         ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
330                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
331 #endif
332             /* says a client was not found for the event (or a valid FocusIn
333                event was not found.
334             */
335             e->xfocus.window = None;
336             return TRUE;
337         }
338
339 #ifdef DEBUG_FOCUS
340         ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
341                  e->xfocus.mode, e->xfocus.detail);
342 #endif
343         break;
344     case FocusOut:
345         if (INVALID_FOCUSOUT(e)) {
346 #ifdef DEBUG_FOCUS
347         ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
348                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
349 #endif
350             return TRUE;
351         }
352
353 #ifdef DEBUG_FOCUS
354         ob_debug("FocusOut on %lx mode %d detail %d\n",
355                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
356 #endif
357
358         {
359             XEvent fe;
360             gboolean fallback = TRUE;
361
362             while (TRUE) {
363                 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
364                                             e->xfocus.window,&fe))
365                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
366                         break;
367                 if (fe.type == FocusOut) {
368 #ifdef DEBUG_FOCUS
369                     ob_debug("found pending FocusOut");
370 #endif
371                     if (!INVALID_FOCUSOUT(&fe)) {
372                         /* if there is a VALID FocusOut still coming, don't
373                            fallback focus yet, we'll deal with it then */
374                         XPutBackEvent(ob_display, &fe);
375                         fallback = FALSE;
376                         break;
377                     }
378                 } else {
379 #ifdef DEBUG_FOCUS
380                     ob_debug("found pending FocusIn");
381 #endif
382                     /* is the focused window getting a FocusOut/In back to
383                        itself?
384                     */
385                     if (fe.xfocus.window == e->xfocus.window &&
386                         !event_ignore(&fe, client)) {
387                         /*
388                           if focus_client is not set, then we can't do
389                           this. we need the FocusIn. This happens in the
390                           case when the set_focus_client(NULL) in the
391                           focus_fallback function fires and then
392                           focus_fallback picks the currently focused
393                           window (such as on a SendToDesktop-esque action.
394                         */
395                         if (focus_client) {
396 #ifdef DEBUG_FOCUS
397                             ob_debug("focused window got an Out/In back to "
398                                      "itself IGNORED both");
399 #endif
400                             return TRUE;
401                         } else {
402                             event_process(&fe);
403 #ifdef DEBUG_FOCUS
404                             ob_debug("focused window got an Out/In back to "
405                                      "itself but focus_client was null "
406                                      "IGNORED just the Out");
407 #endif
408                             return TRUE;
409                         }
410                     }
411
412                     /* once all the FocusOut's have been dealt with, if there
413                        is a FocusIn still left and it is valid, then use it */
414                     event_process(&fe);
415                     /* secret magic way of event_process telling us that no
416                        client was found for the FocusIn event. ^_^ */
417                     if (fe.xfocus.window != None) {
418                         fallback = FALSE;
419                         break;
420                     }
421                 }
422             }
423             if (fallback) {
424 #ifdef DEBUG_FOCUS
425                 ob_debug("no valid FocusIn and no FocusOut events found, "
426                          "falling back");
427 #endif
428                 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
429             }
430         }
431         break;
432     case EnterNotify:
433     case LeaveNotify:
434         /* NotifyUngrab occurs when a mouse button is released and the event is
435            caused, like when lowering a window */
436         /* NotifyVirtual occurs when ungrabbing the pointer */
437         if (e->xcrossing.mode == NotifyGrab ||
438             e->xcrossing.detail == NotifyInferior ||
439             (e->xcrossing.mode == NotifyUngrab &&
440              e->xcrossing.detail == NotifyVirtual)) {
441 #ifdef DEBUG_FOCUS
442             ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
443                      (e->type == EnterNotify ? "Enter" : "Leave"),
444                      e->xcrossing.mode,
445                      e->xcrossing.detail, client?client->window:0);
446 #endif
447             return TRUE;
448         }
449 #ifdef DEBUG_FOCUS
450         ob_debug("%sNotify mode %d detail %d on %lx",
451                  (e->type == EnterNotify ? "Enter" : "Leave"),
452                  e->xcrossing.mode,
453                  e->xcrossing.detail, client?client->window:0);
454 #endif
455         break;
456     }
457     return FALSE;
458 }
459
460 static void event_process(XEvent *e)
461 {
462     Window window;
463     ObClient *client = NULL;
464     ObDock *dock = NULL;
465     ObDockApp *dockapp = NULL;
466     ObWindow *obwin = NULL;
467
468     window = event_get_window(e);
469     if ((obwin = g_hash_table_lookup(window_map, &window))) {
470         switch (obwin->type) {
471         case Window_Dock:
472             dock = WINDOW_AS_DOCK(obwin);
473             break;
474         case Window_DockApp:
475             dockapp = WINDOW_AS_DOCKAPP(obwin);
476             break;
477         case Window_Client:
478             client = WINDOW_AS_CLIENT(obwin);
479             break;
480         case Window_Menu:
481         case Window_Internal:
482             /* not to be used for events */
483             g_assert_not_reached();
484             break;
485         }
486     }
487
488     event_set_lasttime(e);
489     event_hack_mods(e);
490     if (event_ignore(e, client))
491         return;
492
493     /* deal with it in the kernel */
494     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
525     /* user input (action-bound) events */
526     if (e->type == ButtonPress || e->type == ButtonRelease ||
527         e->type == MotionNotify || e->type == KeyPress ||
528         e->type == KeyRelease)
529     {
530         if (menu_frame_visible)
531             event_handle_menu(e);
532         else if (moveresize_in_progress)
533             moveresize_event(e);
534         else {
535             ObFrameContext context;
536
537             context = frame_context(client, e->xany.window);
538
539             if (!keyboard_process_interactive_grab(e, &client, &context)) {
540                 if (e->type == ButtonPress || e->type == ButtonRelease ||
541                     e->type == MotionNotify)
542                     mouse_event(client, context, e);
543                 else if (e->type == KeyPress)
544                     keyboard_event(client, e);
545             }
546         }
547     }
548 }
549
550 static void event_handle_root(XEvent *e)
551 {
552     Atom msgtype;
553      
554     switch(e->type) {
555     case SelectionClear:
556         ob_debug("Another WM has requested to replace us. Exiting.\n");
557         ob_exit();
558         break;
559
560     case ClientMessage:
561         if (e->xclient.format != 32) break;
562
563         msgtype = e->xclient.message_type;
564         if (msgtype == prop_atoms.net_current_desktop) {
565             unsigned int d = e->xclient.data.l[0];
566             if (d < screen_num_desktops)
567                 screen_set_desktop(d);
568         } else if (msgtype == prop_atoms.net_number_of_desktops) {
569             unsigned int d = e->xclient.data.l[0];
570             if (d > 0)
571                 screen_set_num_desktops(d);
572         } else if (msgtype == prop_atoms.net_showing_desktop) {
573             screen_show_desktop(e->xclient.data.l[0] != 0);
574         }
575         break;
576     case PropertyNotify:
577         if (e->xproperty.atom == prop_atoms.net_desktop_names)
578             screen_update_desktop_names();
579         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
580             screen_update_layout();
581         break;
582     case ConfigureNotify:
583 #ifdef XRANDR
584         XRRUpdateConfiguration(e);
585 #endif
586         screen_resize();
587         break;
588     default:
589         ;
590 #ifdef VIDMODE
591         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
592             ob_debug("VIDMODE EVENT\n");
593         }
594 #endif
595     }
596 }
597
598 static void event_handle_client(ObClient *client, XEvent *e)
599 {
600     XEvent ce;
601     Atom msgtype;
602     int i=0;
603     ObFrameContext con;
604      
605     switch (e->type) {
606     case VisibilityNotify:
607         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
608         break;
609     case ButtonPress:
610     case ButtonRelease:
611         /* Wheel buttons don't draw because they are an instant click, so it
612            is a waste of resources to go drawing it. */
613         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
614             switch (frame_context(client, e->xbutton.window)) {
615             case OB_FRAME_CONTEXT_MAXIMIZE:
616                 client->frame->max_press = (e->type == ButtonPress);
617                 framerender_frame(client->frame);
618                 break;
619             case OB_FRAME_CONTEXT_CLOSE:
620                 client->frame->close_press = (e->type == ButtonPress);
621                 framerender_frame(client->frame);
622                 break;
623             case OB_FRAME_CONTEXT_ICONIFY:
624                 client->frame->iconify_press = (e->type == ButtonPress);
625                 framerender_frame(client->frame);
626                 break;
627             case OB_FRAME_CONTEXT_ALLDESKTOPS:
628                 client->frame->desk_press = (e->type == ButtonPress);
629                 framerender_frame(client->frame);
630                 break; 
631             case OB_FRAME_CONTEXT_SHADE:
632                 client->frame->shade_press = (e->type == ButtonPress);
633                 framerender_frame(client->frame);
634                 break;
635             default:
636                 /* nothing changes with clicks for any other contexts */
637                 break;
638             }
639         }
640         break;
641     case FocusIn:
642 #ifdef DEBUG_FOCUS
643         ob_debug("FocusIn on client for %lx\n", client->window);
644 #endif
645         if (client != focus_client) {
646             focus_set_client(client);
647             frame_adjust_focus(client->frame, TRUE);
648         }
649         break;
650     case FocusOut:
651 #ifdef DEBUG_FOCUS
652         ob_debug("FocusOut on client for %lx\n", client->window);
653 #endif
654         /* are we a fullscreen window or a transient of one? (checks layer)
655            if we are then we need to be iconified since we are losing focus
656          */
657         if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
658             !client_search_focus_tree_full(client))
659             /* iconify fullscreen windows when they and their transients
660                aren't focused */
661             client_iconify(client, TRUE, TRUE);
662         frame_adjust_focus(client->frame, FALSE);
663         break;
664     case LeaveNotify:
665         con = frame_context(client, e->xcrossing.window);
666         switch (con) {
667         case OB_FRAME_CONTEXT_MAXIMIZE:
668             client->frame->max_hover = FALSE;
669             frame_adjust_state(client->frame);
670             break;
671         case OB_FRAME_CONTEXT_ALLDESKTOPS:
672             client->frame->desk_hover = FALSE;
673             frame_adjust_state(client->frame);
674             break;
675         case OB_FRAME_CONTEXT_SHADE:
676             client->frame->shade_hover = FALSE;
677             frame_adjust_state(client->frame);
678             break;
679         case OB_FRAME_CONTEXT_ICONIFY:
680             client->frame->iconify_hover = FALSE;
681             frame_adjust_state(client->frame);
682             break;
683         case OB_FRAME_CONTEXT_CLOSE:
684             client->frame->close_hover = FALSE;
685             frame_adjust_state(client->frame);
686             break;
687         default:
688             break;
689         }
690         break;
691     case EnterNotify:
692         con = frame_context(client, e->xcrossing.window);
693         switch (con) {
694         case OB_FRAME_CONTEXT_MAXIMIZE:
695             client->frame->max_hover = TRUE;
696             frame_adjust_state(client->frame);
697             break;
698         case OB_FRAME_CONTEXT_ALLDESKTOPS:
699             client->frame->desk_hover = TRUE;
700             frame_adjust_state(client->frame);
701             break;
702         case OB_FRAME_CONTEXT_SHADE:
703             client->frame->shade_hover = TRUE;
704             frame_adjust_state(client->frame);
705             break;
706         case OB_FRAME_CONTEXT_ICONIFY:
707             client->frame->iconify_hover = TRUE;
708             frame_adjust_state(client->frame);
709             break;
710         case OB_FRAME_CONTEXT_CLOSE:
711             client->frame->close_hover = TRUE;
712             frame_adjust_state(client->frame);
713             break;
714         case OB_FRAME_CONTEXT_FRAME:
715             if (client_normal(client)) {
716                 if (ob_state() == OB_STATE_STARTING) {
717                     /* move it to the top of the focus order */
718                     guint desktop = client->desktop;
719                     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
720                     focus_order[desktop] = g_list_remove(focus_order[desktop],
721                                                          client);
722                     focus_order[desktop] = g_list_prepend(focus_order[desktop],
723                                                           client);
724                 } else if (config_focus_follow) {
725 #ifdef DEBUG_FOCUS
726                     ob_debug("EnterNotify on %lx, focusing window\n",
727                              client->window);
728 #endif
729                     client_focus(client);
730                 }
731             }
732             break;
733         default:
734             break;
735         }
736         break;
737     case ConfigureRequest:
738         /* compress these */
739         while (XCheckTypedWindowEvent(ob_display, client->window,
740                                       ConfigureRequest, &ce)) {
741             ++i;
742             /* XXX if this causes bad things.. we can compress config req's
743                with the same mask. */
744             e->xconfigurerequest.value_mask |=
745                 ce.xconfigurerequest.value_mask;
746             if (ce.xconfigurerequest.value_mask & CWX)
747                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
748             if (ce.xconfigurerequest.value_mask & CWY)
749                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
750             if (ce.xconfigurerequest.value_mask & CWWidth)
751                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
752             if (ce.xconfigurerequest.value_mask & CWHeight)
753                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
754             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
755                 e->xconfigurerequest.border_width =
756                     ce.xconfigurerequest.border_width;
757             if (ce.xconfigurerequest.value_mask & CWStackMode)
758                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
759         }
760
761         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
762         if (client->iconic || client->shaded) return;
763
764         /* resize, then move, as specified in the EWMH section 7.7 */
765         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
766                                                CWX | CWY |
767                                                CWBorderWidth)) {
768             int x, y, w, h;
769             ObCorner corner;
770
771             if (e->xconfigurerequest.value_mask & CWBorderWidth)
772                 client->border_width = e->xconfigurerequest.border_width;
773
774             x = (e->xconfigurerequest.value_mask & CWX) ?
775                 e->xconfigurerequest.x : client->area.x;
776             y = (e->xconfigurerequest.value_mask & CWY) ?
777                 e->xconfigurerequest.y : client->area.y;
778             w = (e->xconfigurerequest.value_mask & CWWidth) ?
779                 e->xconfigurerequest.width : client->area.width;
780             h = (e->xconfigurerequest.value_mask & CWHeight) ?
781                 e->xconfigurerequest.height : client->area.height;
782
783             {
784                 int newx = x;
785                 int newy = y;
786                 int fw = w +
787                     client->frame->size.left + client->frame->size.right;
788                 int fh = h +
789                     client->frame->size.top + client->frame->size.bottom;
790                 client_find_onscreen(client, &newx, &newy, fw, fh,
791                                      client_normal(client));
792                 if (e->xconfigurerequest.value_mask & CWX)
793                     x = newx;
794                 if (e->xconfigurerequest.value_mask & CWY)
795                     y = newy;
796             }
797                
798             switch (client->gravity) {
799             case NorthEastGravity:
800             case EastGravity:
801                 corner = OB_CORNER_TOPRIGHT;
802                 break;
803             case SouthWestGravity:
804             case SouthGravity:
805                 corner = OB_CORNER_BOTTOMLEFT;
806                 break;
807             case SouthEastGravity:
808                 corner = OB_CORNER_BOTTOMRIGHT;
809                 break;
810             default:     /* NorthWest, Static, etc */
811                 corner = OB_CORNER_TOPLEFT;
812             }
813
814             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
815                                   TRUE);
816         }
817
818         if (e->xconfigurerequest.value_mask & CWStackMode) {
819             switch (e->xconfigurerequest.detail) {
820             case Below:
821             case BottomIf:
822                 stacking_lower(CLIENT_AS_WINDOW(client));
823                 break;
824
825             case Above:
826             case TopIf:
827             default:
828                 stacking_raise(CLIENT_AS_WINDOW(client));
829                 break;
830             }
831         }
832         break;
833     case UnmapNotify:
834         if (client->ignore_unmaps) {
835             client->ignore_unmaps--;
836             break;
837         }
838         client_unmanage(client);
839         break;
840     case DestroyNotify:
841         client_unmanage(client);
842         break;
843     case ReparentNotify:
844         /* this is when the client is first taken captive in the frame */
845         if (e->xreparent.parent == client->frame->plate) break;
846
847         /*
848           This event is quite rare and is usually handled in unmapHandler.
849           However, if the window is unmapped when the reparent event occurs,
850           the window manager never sees it because an unmap event is not sent
851           to an already unmapped window.
852         */
853
854         /* we don't want the reparent event, put it back on the stack for the
855            X server to deal with after we unmanage the window */
856         XPutBackEvent(ob_display, e);
857      
858         client_unmanage(client);
859         break;
860     case MapRequest:
861         ob_debug("MapRequest for 0x%lx\n", client->window);
862         if (!client->iconic) break; /* this normally doesn't happen, but if it
863                                        does, we don't want it! */
864         if (screen_showing_desktop)
865             screen_show_desktop(FALSE);
866         client_iconify(client, FALSE, TRUE);
867         if (!client->frame->visible)
868             /* if its not visible still, then don't mess with it */
869             break;
870         if (client->shaded)
871             client_shade(client, FALSE);
872         client_focus(client);
873         stacking_raise(CLIENT_AS_WINDOW(client));
874         break;
875     case ClientMessage:
876         /* validate cuz we query stuff off the client here */
877         if (!client_validate(client)) break;
878   
879         if (e->xclient.format != 32) return;
880
881         msgtype = e->xclient.message_type;
882         if (msgtype == prop_atoms.wm_change_state) {
883             /* compress changes into a single change */
884             while (XCheckTypedWindowEvent(ob_display, e->type,
885                                           client->window, &ce)) {
886                 /* XXX: it would be nice to compress ALL messages of a
887                    type, not just messages in a row without other
888                    message types between. */
889                 if (ce.xclient.message_type != msgtype) {
890                     XPutBackEvent(ob_display, &ce);
891                     break;
892                 }
893                 e->xclient = ce.xclient;
894             }
895             client_set_wm_state(client, e->xclient.data.l[0]);
896         } else if (msgtype == prop_atoms.net_wm_desktop) {
897             /* compress changes into a single change */
898             while (XCheckTypedWindowEvent(ob_display, e->type,
899                                           client->window, &ce)) {
900                 /* XXX: it would be nice to compress ALL messages of a
901                    type, not just messages in a row without other
902                    message types between. */
903                 if (ce.xclient.message_type != msgtype) {
904                     XPutBackEvent(ob_display, &ce);
905                     break;
906                 }
907                 e->xclient = ce.xclient;
908             }
909             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
910                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
911                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
912                                    FALSE);
913         } else if (msgtype == prop_atoms.net_wm_state) {
914             /* can't compress these */
915             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
916                      (e->xclient.data.l[0] == 0 ? "Remove" :
917                       e->xclient.data.l[0] == 1 ? "Add" :
918                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
919                      e->xclient.data.l[1], e->xclient.data.l[2],
920                      client->window);
921             client_set_state(client, e->xclient.data.l[0],
922                              e->xclient.data.l[1], e->xclient.data.l[2]);
923         } else if (msgtype == prop_atoms.net_close_window) {
924             ob_debug("net_close_window for 0x%lx\n", client->window);
925             client_close(client);
926         } else if (msgtype == prop_atoms.net_active_window) {
927             ob_debug("net_active_window for 0x%lx\n", client->window);
928             client_activate(client, FALSE);
929         } else if (msgtype == prop_atoms.net_wm_moveresize) {
930             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
931             if ((Atom)e->xclient.data.l[2] ==
932                 prop_atoms.net_wm_moveresize_size_topleft ||
933                 (Atom)e->xclient.data.l[2] ==
934                 prop_atoms.net_wm_moveresize_size_top ||
935                 (Atom)e->xclient.data.l[2] ==
936                 prop_atoms.net_wm_moveresize_size_topright ||
937                 (Atom)e->xclient.data.l[2] ==
938                 prop_atoms.net_wm_moveresize_size_right ||
939                 (Atom)e->xclient.data.l[2] ==
940                 prop_atoms.net_wm_moveresize_size_right ||
941                 (Atom)e->xclient.data.l[2] ==
942                 prop_atoms.net_wm_moveresize_size_bottomright ||
943                 (Atom)e->xclient.data.l[2] ==
944                 prop_atoms.net_wm_moveresize_size_bottom ||
945                 (Atom)e->xclient.data.l[2] ==
946                 prop_atoms.net_wm_moveresize_size_bottomleft ||
947                 (Atom)e->xclient.data.l[2] ==
948                 prop_atoms.net_wm_moveresize_size_left ||
949                 (Atom)e->xclient.data.l[2] ==
950                 prop_atoms.net_wm_moveresize_move ||
951                 (Atom)e->xclient.data.l[2] ==
952                 prop_atoms.net_wm_moveresize_size_keyboard ||
953                 (Atom)e->xclient.data.l[2] ==
954                 prop_atoms.net_wm_moveresize_move_keyboard) {
955
956                 moveresize_start(client, e->xclient.data.l[0],
957                                  e->xclient.data.l[1], e->xclient.data.l[3],
958                                  e->xclient.data.l[2]);
959             }
960         } else if (msgtype == prop_atoms.net_moveresize_window) {
961             int oldg = client->gravity;
962             int tmpg, x, y, w, h;
963
964             if (e->xclient.data.l[0] & 0xff)
965                 tmpg = e->xclient.data.l[0] & 0xff;
966             else
967                 tmpg = oldg;
968
969             if (e->xclient.data.l[0] & 1 << 8)
970                 x = e->xclient.data.l[1];
971             else
972                 x = client->area.x;
973             if (e->xclient.data.l[0] & 1 << 9)
974                 y = e->xclient.data.l[2];
975             else
976                 y = client->area.y;
977             if (e->xclient.data.l[0] & 1 << 10)
978                 w = e->xclient.data.l[3];
979             else
980                 w = client->area.width;
981             if (e->xclient.data.l[0] & 1 << 11)
982                 h = e->xclient.data.l[4];
983             else
984                 h = client->area.height;
985             client->gravity = tmpg;
986
987             {
988                 int newx = x;
989                 int newy = y;
990                 int fw = w +
991                     client->frame->size.left + client->frame->size.right;
992                 int fh = h +
993                     client->frame->size.top + client->frame->size.bottom;
994                 client_find_onscreen(client, &newx, &newy, fw, fh,
995                                      client_normal(client));
996                 if (e->xclient.data.l[0] & 1 << 8)
997                     x = newx;
998                 if (e->xclient.data.l[0] & 1 << 9)
999                     y = newy;
1000             }
1001                
1002             client_configure(client, OB_CORNER_TOPLEFT,
1003                              x, y, w, h, FALSE, TRUE);
1004
1005             client->gravity = oldg;
1006         }
1007         break;
1008     case PropertyNotify:
1009         /* validate cuz we query stuff off the client here */
1010         if (!client_validate(client)) break;
1011   
1012         /* compress changes to a single property into a single change */
1013         while (XCheckTypedWindowEvent(ob_display, e->type,
1014                                       client->window, &ce)) {
1015             /* XXX: it would be nice to compress ALL changes to a property,
1016                not just changes in a row without other props between. */
1017             if (ce.xproperty.atom != e->xproperty.atom) {
1018                 XPutBackEvent(ob_display, &ce);
1019                 break;
1020             }
1021         }
1022
1023         msgtype = e->xproperty.atom;
1024         if (msgtype == XA_WM_NORMAL_HINTS) {
1025             client_update_normal_hints(client);
1026             /* normal hints can make a window non-resizable */
1027             client_setup_decor_and_functions(client);
1028         }
1029         else if (msgtype == XA_WM_HINTS)
1030             client_update_wmhints(client);
1031         else if (msgtype == XA_WM_TRANSIENT_FOR) {
1032             client_update_transient_for(client);
1033             client_get_type(client);
1034             /* type may have changed, so update the layer */
1035             client_calc_layer(client);
1036             client_setup_decor_and_functions(client);
1037         }
1038         else if (msgtype == prop_atoms.net_wm_name ||
1039                  msgtype == prop_atoms.wm_name ||
1040                  msgtype == prop_atoms.net_wm_icon_name ||
1041                  msgtype == prop_atoms.wm_icon_name)
1042             client_update_title(client);
1043         else if (msgtype == prop_atoms.wm_class)
1044             client_update_class(client);
1045         else if (msgtype == prop_atoms.wm_protocols) {
1046             client_update_protocols(client);
1047             client_setup_decor_and_functions(client);
1048         }
1049         else if (msgtype == prop_atoms.net_wm_strut) {
1050             client_update_strut(client);
1051         }
1052         else if (msgtype == prop_atoms.net_wm_icon ||
1053                  msgtype == prop_atoms.kwm_win_icon)
1054             client_update_icons(client);
1055     default:
1056         ;
1057 #ifdef SHAPE
1058         if (extensions_shape && e->type == extensions_shape_event_basep) {
1059             client->shaped = ((XShapeEvent*)e)->shaped;
1060             frame_adjust_shape(client->frame);
1061         }
1062 #endif
1063     }
1064 }
1065
1066 void event_add_fd_handler(event_fd_handler *h) {
1067     g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1068     FD_SET(h->fd, &allset);
1069     max_fd = MAX(max_fd, h->fd);
1070 }
1071
1072 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1073 {
1074     *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1075 }
1076
1077 static void find_max_fd()
1078
1079     int tmpmax = -1;
1080     g_datalist_foreach(&fd_handler_list, find_max_fd_foreach,
1081                        (gpointer)&tmpmax);
1082     max_fd = MAX(x_fd, tmpmax);
1083 #ifdef USE_SM
1084     max_fd = MAX(ice_fd, max_fd);
1085 #endif
1086 }
1087
1088 void event_remove_fd(gint n)
1089 {
1090     FD_CLR(n, &allset);
1091     g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1092     find_max_fd();
1093 }
1094
1095 static void fd_event_handle_foreach(GQuark n,
1096                                     gpointer data, gpointer user_data)
1097 {
1098     if (FD_ISSET( (int)n, &selset)) {
1099         event_fd_handler *h = (event_fd_handler *)data;
1100         g_assert(h->fd == (int)n);
1101         h->handler(h->fd, h->data);
1102     }
1103 }
1104
1105 static void fd_event_handle()
1106 {
1107     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1108 }
1109
1110 static void event_handle_dock(ObDock *s, XEvent *e)
1111 {
1112     switch (e->type) {
1113     case ButtonPress:
1114         stacking_raise(DOCK_AS_WINDOW(s));
1115         break;
1116     case EnterNotify:
1117         dock_hide(FALSE);
1118         break;
1119     case LeaveNotify:
1120         dock_hide(TRUE);
1121         break;
1122     }
1123 }
1124
1125 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1126 {
1127     switch (e->type) {
1128     case MotionNotify:
1129         dock_app_drag(app, &e->xmotion);
1130         break;
1131     case UnmapNotify:
1132         if (app->ignore_unmaps) {
1133             app->ignore_unmaps--;
1134             break;
1135         }
1136         dock_remove(app, TRUE);
1137         break;
1138     case DestroyNotify:
1139         dock_remove(app, FALSE);
1140         break;
1141     case ReparentNotify:
1142         dock_remove(app, FALSE);
1143         break;
1144     case ConfigureNotify:
1145         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1146         break;
1147     }
1148 }
1149
1150 ObMenuFrame* find_active_menu()
1151 {
1152     GList *it;
1153     ObMenuFrame *f;
1154
1155     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1156         f = it->data;
1157         if (f->selected)
1158             break;
1159     }
1160     return it ? it->data : NULL;
1161 }
1162
1163 static void event_handle_menu(XEvent *ev)
1164 {
1165     ObMenuFrame *f;
1166     ObMenuEntryFrame *e;
1167
1168     switch (ev->type) {
1169     case ButtonRelease:
1170         if (!(f = menu_frame_under(ev->xbutton.x_root,
1171                                    ev->xbutton.y_root)))
1172             menu_frame_hide_all();
1173         else {
1174             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1175                                             ev->xbutton.y_root)))
1176                 menu_entry_frame_execute(e,
1177                                          !(ev->xbutton.state & ControlMask));
1178         }
1179         break;
1180     case MotionNotify:
1181         if ((f = menu_frame_under(ev->xmotion.x_root,
1182                                   ev->xmotion.y_root))) {
1183             menu_frame_move_on_screen(f);
1184             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1185                                             ev->xmotion.y_root)))
1186                 menu_frame_select(f, e);
1187         }
1188         break;
1189     case KeyPress:
1190         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1191             menu_frame_hide_all();
1192         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1193             ObMenuFrame *f;
1194             if ((f = find_active_menu()))
1195                 menu_entry_frame_execute(f->selected,
1196                                          !(ev->xkey.state & ControlMask));
1197         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1198             ObMenuFrame *f;
1199             if ((f = find_active_menu()) && f->parent)
1200                 menu_frame_select(f, NULL);
1201         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1202             ObMenuFrame *f;
1203             if ((f = find_active_menu()) && f->child && f->child->entries)
1204                 menu_frame_select(f->child, f->child->entries->data);
1205         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1206             ObMenuFrame *f;
1207             if ((f = find_active_menu()))
1208                 menu_frame_select_previous(f);
1209         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1210             ObMenuFrame *f;
1211             if ((f = find_active_menu()))
1212                 menu_frame_select_next(f);
1213         }
1214     }
1215 }