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