]> icculus.org git repositories - dana/openbox.git/blob - openbox/event.c
save the stacking order of the windows, and sort the saved session data based on...
[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 "keyboard.h"
12 #include "mouse.h"
13 #include "framerender.h"
14 #include "focus.h"
15 #include "moveresize.h"
16 #include "stacking.h"
17 #include "extensions.h"
18 #include "timer.h"
19 #include "dispatch.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_dock(ObDock *s, XEvent *e);
45 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
46 static void event_handle_client(ObClient *c, XEvent *e);
47 static void event_handle_menu(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         /* compress events */
302         {
303             XEvent ce;
304             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
305                                           e->type, &ce)) {
306                 e->xmotion.x_root = ce.xmotion.x_root;
307                 e->xmotion.y_root = ce.xmotion.y_root;
308             }
309         }
310         break;
311     }
312 }
313
314 static gboolean event_ignore(XEvent *e, ObClient *client)
315 {
316     switch(e->type) {
317     case FocusIn:
318         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
319            because of RevertToPointerRoot. If the focus ends up reverting to
320            pointer root on a workspace change, then the FocusIn event that we
321            want will be of type NotifyAncestor. This situation does not occur
322            for FocusOut, so it is safely ignored there.
323         */
324         if (INVALID_FOCUSIN(e) ||
325             client == NULL) {
326 #ifdef DEBUG_FOCUS
327         ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
328                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
329 #endif
330             /* says a client was not found for the event (or a valid FocusIn
331                event was not found.
332             */
333             e->xfocus.window = None;
334             return TRUE;
335         }
336
337 #ifdef DEBUG_FOCUS
338         ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
339                  e->xfocus.mode, e->xfocus.detail);
340 #endif
341         break;
342     case FocusOut:
343         if (INVALID_FOCUSOUT(e)) {
344 #ifdef DEBUG_FOCUS
345         ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
346                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
347 #endif
348             return TRUE;
349         }
350
351 #ifdef DEBUG_FOCUS
352         ob_debug("FocusOut on %lx mode %d detail %d\n",
353                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
354 #endif
355
356         {
357             XEvent fe;
358             gboolean fallback = TRUE;
359
360             while (TRUE) {
361                 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
362                                             e->xfocus.window,&fe))
363                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
364                         break;
365                 if (fe.type == FocusOut) {
366 #ifdef DEBUG_FOCUS
367                     ob_debug("found pending FocusOut");
368 #endif
369                     if (!INVALID_FOCUSOUT(&fe)) {
370                         /* if there is a VALID FocusOut still coming, don't
371                            fallback focus yet, we'll deal with it then */
372                         XPutBackEvent(ob_display, &fe);
373                         fallback = FALSE;
374                         break;
375                     }
376                 } else {
377 #ifdef DEBUG_FOCUS
378                     ob_debug("found pending FocusIn");
379 #endif
380                     /* is the focused window getting a FocusOut/In back to
381                        itself?
382                     */
383                     if (fe.xfocus.window == e->xfocus.window &&
384                         !event_ignore(&fe, client)) {
385                         /*
386                           if focus_client is not set, then we can't do
387                           this. we need the FocusIn. This happens in the
388                           case when the set_focus_client(NULL) in the
389                           focus_fallback function fires and then
390                           focus_fallback picks the currently focused
391                           window (such as on a SendToDesktop-esque action.
392                         */
393                         if (focus_client) {
394 #ifdef DEBUG_FOCUS
395                             ob_debug("focused window got an Out/In back to "
396                                      "itself IGNORED both");
397 #endif
398                             return TRUE;
399                         } else {
400                             event_process(&fe);
401 #ifdef DEBUG_FOCUS
402                             ob_debug("focused window got an Out/In back to "
403                                      "itself but focus_client was null "
404                                      "IGNORED just the Out");
405 #endif
406                             return TRUE;
407                         }
408                     }
409
410                     /* once all the FocusOut's have been dealt with, if there
411                        is a FocusIn still left and it is valid, then use it */
412                     event_process(&fe);
413                     /* secret magic way of event_process telling us that no
414                        client was found for the FocusIn event. ^_^ */
415                     if (fe.xfocus.window != None) {
416                         fallback = FALSE;
417                         break;
418                     }
419                 }
420             }
421             if (fallback) {
422 #ifdef DEBUG_FOCUS
423                 ob_debug("no valid FocusIn and no FocusOut events found, "
424                          "falling back");
425 #endif
426                 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
427             }
428         }
429         break;
430     case EnterNotify:
431     case LeaveNotify:
432         /* NotifyUngrab occurs when a mouse button is released and the event is
433            caused, like when lowering a window */
434         /* NotifyVirtual occurs when ungrabbing the pointer */
435         if (e->xcrossing.mode == NotifyGrab ||
436             e->xcrossing.detail == NotifyInferior ||
437             (e->xcrossing.mode == NotifyUngrab &&
438              e->xcrossing.detail == NotifyVirtual)) {
439 #ifdef DEBUG_FOCUS
440             ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
441                      (e->type == EnterNotify ? "Enter" : "Leave"),
442                      e->xcrossing.mode,
443                      e->xcrossing.detail, client?client->window:0);
444 #endif
445             return TRUE;
446         }
447 #ifdef DEBUG_FOCUS
448         ob_debug("%sNotify mode %d detail %d on %lx",
449                  (e->type == EnterNotify ? "Enter" : "Leave"),
450                  e->xcrossing.mode,
451                  e->xcrossing.detail, client?client->window:0);
452 #endif
453         break;
454     }
455     return FALSE;
456 }
457
458 static void event_process(XEvent *e)
459 {
460     Window window;
461     ObClient *client = NULL;
462     ObDock *dock = NULL;
463     ObDockApp *dockapp = NULL;
464     ObMenu *menu = NULL;
465     ObWindow *obwin = NULL;
466
467     window = event_get_window(e);
468     if ((obwin = g_hash_table_lookup(window_map, &window))) {
469         switch (obwin->type) {
470         case Window_Dock:
471             dock = WINDOW_AS_DOCK(obwin);
472             break;
473         case Window_DockApp:
474             dockapp = WINDOW_AS_DOCKAPP(obwin);
475             break;
476         case Window_Menu:
477             menu = WINDOW_AS_MENU(obwin);
478             break;
479         case Window_Client:
480             client = WINDOW_AS_CLIENT(obwin);
481             break;
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_visible)
527         if (e->type == MotionNotify || e->type == ButtonRelease ||
528             e->type == ButtonPress ||
529             e->type == KeyPress || e->type == KeyRelease) {
530             event_handle_menu(client, 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
545     /* user input (action-bound) events */
546     if (e->type == ButtonPress || e->type == ButtonRelease ||
547         e->type == MotionNotify || e->type == KeyPress ||
548         e->type == KeyRelease)
549     {
550         ObFrameContext context;
551
552         context = frame_context(client, e->xany.window);
553
554         if (!keyboard_process_interactive_grab(e, &client, &context)) {
555
556             if (e->type == ButtonPress || e->type == ButtonRelease ||
557                 e->type == MotionNotify)
558                 mouse_event(client, context, e);
559             else if (e->type == KeyPress)
560                 keyboard_event(client, e);
561         }
562     }
563
564     /* dispatch the event to registered handlers */
565     dispatch_x(e, client);
566 }
567
568 static void event_handle_root(XEvent *e)
569 {
570     Atom msgtype;
571      
572     switch(e->type) {
573     case SelectionClear:
574         ob_debug("Another WM has requested to replace us. Exiting.\n");
575         ob_exit();
576         break;
577
578     case ClientMessage:
579         if (e->xclient.format != 32) break;
580
581         msgtype = e->xclient.message_type;
582         if (msgtype == prop_atoms.net_current_desktop) {
583             unsigned int d = e->xclient.data.l[0];
584             if (d < screen_num_desktops)
585                 screen_set_desktop(d);
586         } else if (msgtype == prop_atoms.net_number_of_desktops) {
587             unsigned int d = e->xclient.data.l[0];
588             if (d > 0)
589                 screen_set_num_desktops(d);
590         } else if (msgtype == prop_atoms.net_showing_desktop) {
591             screen_show_desktop(e->xclient.data.l[0] != 0);
592         }
593         break;
594     case PropertyNotify:
595         if (e->xproperty.atom == prop_atoms.net_desktop_names)
596             screen_update_desktop_names();
597         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
598             screen_update_layout();
599         break;
600     case ConfigureNotify:
601 #ifdef XRANDR
602         XRRUpdateConfiguration(e);
603 #endif
604         screen_resize();
605         break;
606     default:
607         ;
608 #ifdef VIDMODE
609         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
610             ob_debug("VIDMODE EVENT\n");
611         }
612 #endif
613     }
614 }
615
616 static void event_handle_client(ObClient *client, XEvent *e)
617 {
618     XEvent ce;
619     Atom msgtype;
620     int i=0;
621     ObFrameContext con;
622      
623     switch (e->type) {
624     case VisibilityNotify:
625         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
626         break;
627     case ButtonPress:
628     case ButtonRelease:
629         /* Wheel buttons don't draw because they are an instant click, so it
630            is a waste of resources to go drawing it. */
631         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
632             switch (frame_context(client, e->xbutton.window)) {
633             case OB_FRAME_CONTEXT_MAXIMIZE:
634                 client->frame->max_press = (e->type == ButtonPress);
635                 framerender_frame(client->frame);
636                 break;
637             case OB_FRAME_CONTEXT_CLOSE:
638                 client->frame->close_press = (e->type == ButtonPress);
639                 framerender_frame(client->frame);
640                 break;
641             case OB_FRAME_CONTEXT_ICONIFY:
642                 client->frame->iconify_press = (e->type == ButtonPress);
643                 framerender_frame(client->frame);
644                 break;
645             case OB_FRAME_CONTEXT_ALLDESKTOPS:
646                 client->frame->desk_press = (e->type == ButtonPress);
647                 framerender_frame(client->frame);
648                 break; 
649             case OB_FRAME_CONTEXT_SHADE:
650                 client->frame->shade_press = (e->type == ButtonPress);
651                 framerender_frame(client->frame);
652                 break;
653             default:
654                 /* nothing changes with clicks for any other contexts */
655                 break;
656             }
657         }
658         break;
659     case FocusIn:
660 #ifdef DEBUG_FOCUS
661         ob_debug("FocusIn on client for %lx\n", client->window);
662 #endif
663         if (client != focus_client) {
664             focus_set_client(client);
665             frame_adjust_focus(client->frame, TRUE);
666         }
667         break;
668     case FocusOut:
669 #ifdef DEBUG_FOCUS
670         ob_debug("FocusOut on client for %lx\n", client->window);
671 #endif
672         /* are we a fullscreen window or a transient of one? (checks layer)
673            if we are then we need to be iconified since we are losing focus
674          */
675         if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
676             !client_search_focus_tree_full(client))
677             /* iconify fullscreen windows when they and their transients
678                aren't focused */
679             client_iconify(client, TRUE, TRUE);
680         frame_adjust_focus(client->frame, FALSE);
681         break;
682     case LeaveNotify:
683         con = frame_context(client, e->xcrossing.window);
684         switch (con) {
685         case OB_FRAME_CONTEXT_MAXIMIZE:
686             client->frame->max_hover = FALSE;
687             frame_adjust_state(client->frame);
688             break;
689         case OB_FRAME_CONTEXT_ALLDESKTOPS:
690             client->frame->desk_hover = FALSE;
691             frame_adjust_state(client->frame);
692             break;
693         case OB_FRAME_CONTEXT_SHADE:
694             client->frame->shade_hover = FALSE;
695             frame_adjust_state(client->frame);
696             break;
697         case OB_FRAME_CONTEXT_ICONIFY:
698             client->frame->iconify_hover = FALSE;
699             frame_adjust_state(client->frame);
700             break;
701         case OB_FRAME_CONTEXT_CLOSE:
702             client->frame->close_hover = FALSE;
703             frame_adjust_state(client->frame);
704             break;
705         default:
706             break;
707         }
708         break;
709     case EnterNotify:
710         con = frame_context(client, e->xcrossing.window);
711         switch (con) {
712         case OB_FRAME_CONTEXT_MAXIMIZE:
713             client->frame->max_hover = TRUE;
714             frame_adjust_state(client->frame);
715             break;
716         case OB_FRAME_CONTEXT_ALLDESKTOPS:
717             client->frame->desk_hover = TRUE;
718             frame_adjust_state(client->frame);
719             break;
720         case OB_FRAME_CONTEXT_SHADE:
721             client->frame->shade_hover = TRUE;
722             frame_adjust_state(client->frame);
723             break;
724         case OB_FRAME_CONTEXT_ICONIFY:
725             client->frame->iconify_hover = TRUE;
726             frame_adjust_state(client->frame);
727             break;
728         case OB_FRAME_CONTEXT_CLOSE:
729             client->frame->close_hover = TRUE;
730             frame_adjust_state(client->frame);
731             break;
732         case OB_FRAME_CONTEXT_FRAME:
733             if (client_normal(client)) {
734                 if (ob_state() == OB_STATE_STARTING) {
735                     /* move it to the top of the focus order */
736                     guint desktop = client->desktop;
737                     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
738                     focus_order[desktop] = g_list_remove(focus_order[desktop],
739                                                          client);
740                     focus_order[desktop] = g_list_prepend(focus_order[desktop],
741                                                           client);
742                 } else if (config_focus_follow) {
743 #ifdef DEBUG_FOCUS
744                     ob_debug("EnterNotify on %lx, focusing window\n",
745                              client->window);
746 #endif
747                     client_focus(client);
748                 }
749             }
750             break;
751         default:
752             break;
753         }
754         break;
755     case ConfigureRequest:
756         /* compress these */
757         while (XCheckTypedWindowEvent(ob_display, client->window,
758                                       ConfigureRequest, &ce)) {
759             ++i;
760             /* XXX if this causes bad things.. we can compress config req's
761                with the same mask. */
762             e->xconfigurerequest.value_mask |=
763                 ce.xconfigurerequest.value_mask;
764             if (ce.xconfigurerequest.value_mask & CWX)
765                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
766             if (ce.xconfigurerequest.value_mask & CWY)
767                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
768             if (ce.xconfigurerequest.value_mask & CWWidth)
769                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
770             if (ce.xconfigurerequest.value_mask & CWHeight)
771                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
772             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
773                 e->xconfigurerequest.border_width =
774                     ce.xconfigurerequest.border_width;
775             if (ce.xconfigurerequest.value_mask & CWStackMode)
776                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
777         }
778
779         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
780         if (client->iconic || client->shaded) return;
781
782         /* resize, then move, as specified in the EWMH section 7.7 */
783         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
784                                                CWX | CWY |
785                                                CWBorderWidth)) {
786             int x, y, w, h;
787             ObCorner corner;
788
789             if (e->xconfigurerequest.value_mask & CWBorderWidth)
790                 client->border_width = e->xconfigurerequest.border_width;
791
792             x = (e->xconfigurerequest.value_mask & CWX) ?
793                 e->xconfigurerequest.x : client->area.x;
794             y = (e->xconfigurerequest.value_mask & CWY) ?
795                 e->xconfigurerequest.y : client->area.y;
796             w = (e->xconfigurerequest.value_mask & CWWidth) ?
797                 e->xconfigurerequest.width : client->area.width;
798             h = (e->xconfigurerequest.value_mask & CWHeight) ?
799                 e->xconfigurerequest.height : client->area.height;
800
801             {
802                 int newx = x;
803                 int newy = y;
804                 int fw = w +
805                     client->frame->size.left + client->frame->size.right;
806                 int fh = h +
807                     client->frame->size.top + client->frame->size.bottom;
808                 client_find_onscreen(client, &newx, &newy, fw, fh, TRUE);
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, TRUE);
1012                 if (e->xclient.data.l[0] & 1 << 8)
1013                     x = newx;
1014                 if (e->xclient.data.l[0] & 1 << 9)
1015                     y = newy;
1016             }
1017                
1018             client_configure(client, OB_CORNER_TOPLEFT,
1019                              x, y, w, h, FALSE, TRUE);
1020
1021             client->gravity = oldg;
1022         }
1023         break;
1024     case PropertyNotify:
1025         /* validate cuz we query stuff off the client here */
1026         if (!client_validate(client)) break;
1027   
1028         /* compress changes to a single property into a single change */
1029         while (XCheckTypedWindowEvent(ob_display, e->type,
1030                                       client->window, &ce)) {
1031             /* XXX: it would be nice to compress ALL changes to a property,
1032                not just changes in a row without other props between. */
1033             if (ce.xproperty.atom != e->xproperty.atom) {
1034                 XPutBackEvent(ob_display, &ce);
1035                 break;
1036             }
1037         }
1038
1039         msgtype = e->xproperty.atom;
1040         if (msgtype == XA_WM_NORMAL_HINTS) {
1041             client_update_normal_hints(client);
1042             /* normal hints can make a window non-resizable */
1043             client_setup_decor_and_functions(client);
1044         }
1045         else if (msgtype == XA_WM_HINTS)
1046             client_update_wmhints(client);
1047         else if (msgtype == XA_WM_TRANSIENT_FOR) {
1048             client_update_transient_for(client);
1049             client_get_type(client);
1050             /* type may have changed, so update the layer */
1051             client_calc_layer(client);
1052             client_setup_decor_and_functions(client);
1053         }
1054         else if (msgtype == prop_atoms.net_wm_name ||
1055                  msgtype == prop_atoms.wm_name ||
1056                  msgtype == prop_atoms.net_wm_icon_name ||
1057                  msgtype == prop_atoms.wm_icon_name)
1058             client_update_title(client);
1059         else if (msgtype == prop_atoms.wm_class)
1060             client_update_class(client);
1061         else if (msgtype == prop_atoms.wm_protocols) {
1062             client_update_protocols(client);
1063             client_setup_decor_and_functions(client);
1064         }
1065         else if (msgtype == prop_atoms.net_wm_strut) {
1066             client_update_strut(client);
1067         }
1068         else if (msgtype == prop_atoms.net_wm_icon ||
1069                  msgtype == prop_atoms.kwm_win_icon)
1070             client_update_icons(client);
1071     default:
1072         ;
1073 #ifdef SHAPE
1074         if (extensions_shape && e->type == extensions_shape_event_basep) {
1075             client->shaped = ((XShapeEvent*)e)->shaped;
1076             frame_adjust_shape(client->frame);
1077         }
1078 #endif
1079     }
1080 }
1081
1082 static void event_handle_menu(ObClient *client, XEvent *e)
1083 {
1084     ObMenuEntry *entry;
1085     ObMenu *top;
1086     GList *it = NULL;
1087
1088     top = g_list_nth_data(menu_visible, 0);
1089
1090     ob_debug("EVENT %d\n", e->type);
1091     switch (e->type) {
1092     case KeyPress:
1093         menu_control_keyboard_nav(e->xkey.keycode);
1094         break;
1095     case ButtonPress:
1096         ob_debug("BUTTON PRESS\n");
1097
1098         break;
1099     case ButtonRelease:
1100         ob_debug("BUTTON RELEASED\n");
1101
1102         for (it = menu_visible; it; it = g_list_next(it)) {
1103             ObMenu *m = it->data;
1104             if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
1105                 e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
1106                 e->xbutton.x_root < m->location.x + m->size.width +
1107                 ob_rr_theme->bwidth &&
1108                 e->xbutton.y_root < m->location.y + m->size.height +
1109                 ob_rr_theme->bwidth) {
1110                 if ((entry = menu_find_entry_by_pos(it->data,
1111                                                     e->xbutton.x_root -
1112                                                     m->location.x,
1113                                                     e->xbutton.y_root -
1114                                                     m->location.y))) {
1115                     m->selected(entry, e->xbutton.button,
1116                                 e->xbutton.x_root,
1117                                 e->xbutton.y_root);
1118                     break;
1119                 }
1120                 break;
1121             }
1122         }
1123
1124         /* will call the menu_hide() for each submenu as well */
1125         if (!it)
1126             menu_control_keyboard_nav(ob_keycode(OB_KEY_ESCAPE));
1127         
1128         break;
1129     case MotionNotify:
1130         ob_debug("motion\n");
1131         for (it = menu_visible; it; it = g_list_next(it)) {
1132             ObMenu *m = it->data;
1133             if ((entry = menu_find_entry_by_pos(it->data,
1134                                                 e->xmotion.x_root -
1135                                                 m->location.x,
1136                                                 e->xmotion.y_root -
1137                                                 m->location.y))) {
1138                 if (m->over && m->over->data != entry)
1139                     m->mouseover(m->over->data, FALSE);
1140
1141                 m->mouseover(entry, TRUE);
1142                 break;
1143             }
1144         }
1145
1146         break;
1147     }
1148 }
1149
1150 void event_add_fd_handler(event_fd_handler *h) {
1151     g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1152     FD_SET(h->fd, &allset);
1153     max_fd = MAX(max_fd, h->fd);
1154 }
1155
1156 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1157 {
1158     *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1159 }
1160
1161 static void find_max_fd()
1162
1163     int tmpmax = -1;
1164     g_datalist_foreach(&fd_handler_list, find_max_fd_foreach,
1165                        (gpointer)&tmpmax);
1166     max_fd = MAX(x_fd, tmpmax);
1167 #ifdef USE_SM
1168     max_fd = MAX(ice_fd, max_fd);
1169 #endif
1170 }
1171
1172 void event_remove_fd(int n)
1173 {
1174     FD_CLR(n, &allset);
1175     g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1176     find_max_fd();
1177 }
1178
1179 static void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1180 {
1181     if (FD_ISSET( (int)n, &selset)) {
1182         event_fd_handler *h = (event_fd_handler *)data;
1183         g_assert(h->fd == (int)n);
1184         h->handler(h->fd, h->data);
1185     }
1186 }
1187
1188 static void fd_event_handle()
1189 {
1190     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1191 }
1192
1193 static void event_handle_dock(ObDock *s, XEvent *e)
1194 {
1195     switch (e->type) {
1196     case ButtonPress:
1197         stacking_raise(DOCK_AS_WINDOW(s));
1198         break;
1199     case EnterNotify:
1200         dock_hide(FALSE);
1201         break;
1202     case LeaveNotify:
1203         dock_hide(TRUE);
1204         break;
1205     }
1206 }
1207
1208 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1209 {
1210     switch (e->type) {
1211     case MotionNotify:
1212         dock_app_drag(app, &e->xmotion);
1213         break;
1214     case UnmapNotify:
1215         if (app->ignore_unmaps) {
1216             app->ignore_unmaps--;
1217             break;
1218         }
1219         dock_remove(app, TRUE);
1220         break;
1221     case DestroyNotify:
1222         dock_remove(app, FALSE);
1223         break;
1224     case ReparentNotify:
1225         dock_remove(app, FALSE);
1226         break;
1227     case ConfigureNotify:
1228         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1229         break;
1230     }
1231 }