dont be rude to non-normal windows with keeping them onscreen
[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,
809                                      client_noral(self));
810                 if (e->xconfigurerequest.value_mask & CWX)
811                     x = newx;
812                 if (e->xconfigurerequest.value_mask & CWY)
813                     y = newy;
814             }
815                
816             switch (client->gravity) {
817             case NorthEastGravity:
818             case EastGravity:
819                 corner = OB_CORNER_TOPRIGHT;
820                 break;
821             case SouthWestGravity:
822             case SouthGravity:
823                 corner = OB_CORNER_BOTTOMLEFT;
824                 break;
825             case SouthEastGravity:
826                 corner = OB_CORNER_BOTTOMRIGHT;
827                 break;
828             default:     /* NorthWest, Static, etc */
829                 corner = OB_CORNER_TOPLEFT;
830             }
831
832             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
833                                   TRUE);
834         }
835
836         if (e->xconfigurerequest.value_mask & CWStackMode) {
837             switch (e->xconfigurerequest.detail) {
838             case Below:
839             case BottomIf:
840                 stacking_lower(CLIENT_AS_WINDOW(client));
841                 break;
842
843             case Above:
844             case TopIf:
845             default:
846                 stacking_raise(CLIENT_AS_WINDOW(client));
847                 break;
848             }
849         }
850         break;
851     case UnmapNotify:
852         if (client->ignore_unmaps) {
853             client->ignore_unmaps--;
854             break;
855         }
856         client_unmanage(client);
857         break;
858     case DestroyNotify:
859         client_unmanage(client);
860         break;
861     case ReparentNotify:
862         /* this is when the client is first taken captive in the frame */
863         if (e->xreparent.parent == client->frame->plate) break;
864
865         /*
866           This event is quite rare and is usually handled in unmapHandler.
867           However, if the window is unmapped when the reparent event occurs,
868           the window manager never sees it because an unmap event is not sent
869           to an already unmapped window.
870         */
871
872         /* we don't want the reparent event, put it back on the stack for the
873            X server to deal with after we unmanage the window */
874         XPutBackEvent(ob_display, e);
875      
876         client_unmanage(client);
877         break;
878     case MapRequest:
879         ob_debug("MapRequest for 0x%lx\n", client->window);
880         if (!client->iconic) break; /* this normally doesn't happen, but if it
881                                        does, we don't want it! */
882         if (screen_showing_desktop)
883             screen_show_desktop(FALSE);
884         client_iconify(client, FALSE, TRUE);
885         if (!client->frame->visible)
886             /* if its not visible still, then don't mess with it */
887             break;
888         if (client->shaded)
889             client_shade(client, FALSE);
890         client_focus(client);
891         stacking_raise(CLIENT_AS_WINDOW(client));
892         break;
893     case ClientMessage:
894         /* validate cuz we query stuff off the client here */
895         if (!client_validate(client)) break;
896   
897         if (e->xclient.format != 32) return;
898
899         msgtype = e->xclient.message_type;
900         if (msgtype == prop_atoms.wm_change_state) {
901             /* compress changes into a single change */
902             while (XCheckTypedWindowEvent(ob_display, e->type,
903                                           client->window, &ce)) {
904                 /* XXX: it would be nice to compress ALL messages of a
905                    type, not just messages in a row without other
906                    message types between. */
907                 if (ce.xclient.message_type != msgtype) {
908                     XPutBackEvent(ob_display, &ce);
909                     break;
910                 }
911                 e->xclient = ce.xclient;
912             }
913             client_set_wm_state(client, e->xclient.data.l[0]);
914         } else if (msgtype == prop_atoms.net_wm_desktop) {
915             /* compress changes into a single change */
916             while (XCheckTypedWindowEvent(ob_display, e->type,
917                                           client->window, &ce)) {
918                 /* XXX: it would be nice to compress ALL messages of a
919                    type, not just messages in a row without other
920                    message types between. */
921                 if (ce.xclient.message_type != msgtype) {
922                     XPutBackEvent(ob_display, &ce);
923                     break;
924                 }
925                 e->xclient = ce.xclient;
926             }
927             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
928                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
929                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
930                                    FALSE);
931         } else if (msgtype == prop_atoms.net_wm_state) {
932             /* can't compress these */
933             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
934                      (e->xclient.data.l[0] == 0 ? "Remove" :
935                       e->xclient.data.l[0] == 1 ? "Add" :
936                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
937                      e->xclient.data.l[1], e->xclient.data.l[2],
938                      client->window);
939             client_set_state(client, e->xclient.data.l[0],
940                              e->xclient.data.l[1], e->xclient.data.l[2]);
941         } else if (msgtype == prop_atoms.net_close_window) {
942             ob_debug("net_close_window for 0x%lx\n", client->window);
943             client_close(client);
944         } else if (msgtype == prop_atoms.net_active_window) {
945             ob_debug("net_active_window for 0x%lx\n", client->window);
946             client_activate(client, FALSE);
947         } else if (msgtype == prop_atoms.net_wm_moveresize) {
948             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
949             if ((Atom)e->xclient.data.l[2] ==
950                 prop_atoms.net_wm_moveresize_size_topleft ||
951                 (Atom)e->xclient.data.l[2] ==
952                 prop_atoms.net_wm_moveresize_size_top ||
953                 (Atom)e->xclient.data.l[2] ==
954                 prop_atoms.net_wm_moveresize_size_topright ||
955                 (Atom)e->xclient.data.l[2] ==
956                 prop_atoms.net_wm_moveresize_size_right ||
957                 (Atom)e->xclient.data.l[2] ==
958                 prop_atoms.net_wm_moveresize_size_right ||
959                 (Atom)e->xclient.data.l[2] ==
960                 prop_atoms.net_wm_moveresize_size_bottomright ||
961                 (Atom)e->xclient.data.l[2] ==
962                 prop_atoms.net_wm_moveresize_size_bottom ||
963                 (Atom)e->xclient.data.l[2] ==
964                 prop_atoms.net_wm_moveresize_size_bottomleft ||
965                 (Atom)e->xclient.data.l[2] ==
966                 prop_atoms.net_wm_moveresize_size_left ||
967                 (Atom)e->xclient.data.l[2] ==
968                 prop_atoms.net_wm_moveresize_move ||
969                 (Atom)e->xclient.data.l[2] ==
970                 prop_atoms.net_wm_moveresize_size_keyboard ||
971                 (Atom)e->xclient.data.l[2] ==
972                 prop_atoms.net_wm_moveresize_move_keyboard) {
973
974                 moveresize_start(client, e->xclient.data.l[0],
975                                  e->xclient.data.l[1], e->xclient.data.l[3],
976                                  e->xclient.data.l[2]);
977             }
978         } else if (msgtype == prop_atoms.net_moveresize_window) {
979             int oldg = client->gravity;
980             int tmpg, x, y, w, h;
981
982             if (e->xclient.data.l[0] & 0xff)
983                 tmpg = e->xclient.data.l[0] & 0xff;
984             else
985                 tmpg = oldg;
986
987             if (e->xclient.data.l[0] & 1 << 8)
988                 x = e->xclient.data.l[1];
989             else
990                 x = client->area.x;
991             if (e->xclient.data.l[0] & 1 << 9)
992                 y = e->xclient.data.l[2];
993             else
994                 y = client->area.y;
995             if (e->xclient.data.l[0] & 1 << 10)
996                 w = e->xclient.data.l[3];
997             else
998                 w = client->area.width;
999             if (e->xclient.data.l[0] & 1 << 11)
1000                 h = e->xclient.data.l[4];
1001             else
1002                 h = client->area.height;
1003             client->gravity = tmpg;
1004
1005             {
1006                 int newx = x;
1007                 int newy = y;
1008                 int fw = w +
1009                     client->frame->size.left + client->frame->size.right;
1010                 int fh = h +
1011                     client->frame->size.top + client->frame->size.bottom;
1012                 client_find_onscreen(client, &newx, &newy, fw, fh,
1013                                      client_normal(self));
1014                 if (e->xclient.data.l[0] & 1 << 8)
1015                     x = newx;
1016                 if (e->xclient.data.l[0] & 1 << 9)
1017                     y = newy;
1018             }
1019                
1020             client_configure(client, OB_CORNER_TOPLEFT,
1021                              x, y, w, h, FALSE, TRUE);
1022
1023             client->gravity = oldg;
1024         }
1025         break;
1026     case PropertyNotify:
1027         /* validate cuz we query stuff off the client here */
1028         if (!client_validate(client)) break;
1029   
1030         /* compress changes to a single property into a single change */
1031         while (XCheckTypedWindowEvent(ob_display, e->type,
1032                                       client->window, &ce)) {
1033             /* XXX: it would be nice to compress ALL changes to a property,
1034                not just changes in a row without other props between. */
1035             if (ce.xproperty.atom != e->xproperty.atom) {
1036                 XPutBackEvent(ob_display, &ce);
1037                 break;
1038             }
1039         }
1040
1041         msgtype = e->xproperty.atom;
1042         if (msgtype == XA_WM_NORMAL_HINTS) {
1043             client_update_normal_hints(client);
1044             /* normal hints can make a window non-resizable */
1045             client_setup_decor_and_functions(client);
1046         }
1047         else if (msgtype == XA_WM_HINTS)
1048             client_update_wmhints(client);
1049         else if (msgtype == XA_WM_TRANSIENT_FOR) {
1050             client_update_transient_for(client);
1051             client_get_type(client);
1052             /* type may have changed, so update the layer */
1053             client_calc_layer(client);
1054             client_setup_decor_and_functions(client);
1055         }
1056         else if (msgtype == prop_atoms.net_wm_name ||
1057                  msgtype == prop_atoms.wm_name ||
1058                  msgtype == prop_atoms.net_wm_icon_name ||
1059                  msgtype == prop_atoms.wm_icon_name)
1060             client_update_title(client);
1061         else if (msgtype == prop_atoms.wm_class)
1062             client_update_class(client);
1063         else if (msgtype == prop_atoms.wm_protocols) {
1064             client_update_protocols(client);
1065             client_setup_decor_and_functions(client);
1066         }
1067         else if (msgtype == prop_atoms.net_wm_strut) {
1068             client_update_strut(client);
1069         }
1070         else if (msgtype == prop_atoms.net_wm_icon ||
1071                  msgtype == prop_atoms.kwm_win_icon)
1072             client_update_icons(client);
1073     default:
1074         ;
1075 #ifdef SHAPE
1076         if (extensions_shape && e->type == extensions_shape_event_basep) {
1077             client->shaped = ((XShapeEvent*)e)->shaped;
1078             frame_adjust_shape(client->frame);
1079         }
1080 #endif
1081     }
1082 }
1083
1084 static void event_handle_menu(ObClient *client, XEvent *e)
1085 {
1086     ObMenuEntry *entry;
1087     ObMenu *top;
1088     GList *it = NULL;
1089
1090     top = g_list_nth_data(menu_visible, 0);
1091
1092     ob_debug("EVENT %d\n", e->type);
1093     switch (e->type) {
1094     case KeyPress:
1095         menu_control_keyboard_nav(e->xkey.keycode);
1096         break;
1097     case ButtonPress:
1098         ob_debug("BUTTON PRESS\n");
1099
1100         break;
1101     case ButtonRelease:
1102         ob_debug("BUTTON RELEASED\n");
1103
1104         for (it = menu_visible; it; it = g_list_next(it)) {
1105             ObMenu *m = it->data;
1106             if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
1107                 e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
1108                 e->xbutton.x_root < m->location.x + m->size.width +
1109                 ob_rr_theme->bwidth &&
1110                 e->xbutton.y_root < m->location.y + m->size.height +
1111                 ob_rr_theme->bwidth) {
1112                 if ((entry = menu_find_entry_by_pos(it->data,
1113                                                     e->xbutton.x_root -
1114                                                     m->location.x,
1115                                                     e->xbutton.y_root -
1116                                                     m->location.y))) {
1117                     m->selected(entry, e->xbutton.button,
1118                                 e->xbutton.x_root,
1119                                 e->xbutton.y_root);
1120                     break;
1121                 }
1122                 break;
1123             }
1124         }
1125
1126         /* will call the menu_hide() for each submenu as well */
1127         if (!it)
1128             menu_control_keyboard_nav(ob_keycode(OB_KEY_ESCAPE));
1129         
1130         break;
1131     case MotionNotify:
1132         ob_debug("motion\n");
1133         for (it = menu_visible; it; it = g_list_next(it)) {
1134             ObMenu *m = it->data;
1135             if ((entry = menu_find_entry_by_pos(it->data,
1136                                                 e->xmotion.x_root -
1137                                                 m->location.x,
1138                                                 e->xmotion.y_root -
1139                                                 m->location.y))) {
1140                 if (m->over && m->over->data != entry)
1141                     m->mouseover(m->over->data, FALSE);
1142
1143                 m->mouseover(entry, TRUE);
1144                 break;
1145             }
1146         }
1147
1148         break;
1149     }
1150 }
1151
1152 void event_add_fd_handler(event_fd_handler *h) {
1153     g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1154     FD_SET(h->fd, &allset);
1155     max_fd = MAX(max_fd, h->fd);
1156 }
1157
1158 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1159 {
1160     *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1161 }
1162
1163 static void find_max_fd()
1164
1165     int tmpmax = -1;
1166     g_datalist_foreach(&fd_handler_list, find_max_fd_foreach,
1167                        (gpointer)&tmpmax);
1168     max_fd = MAX(x_fd, tmpmax);
1169 #ifdef USE_SM
1170     max_fd = MAX(ice_fd, max_fd);
1171 #endif
1172 }
1173
1174 void event_remove_fd(int n)
1175 {
1176     FD_CLR(n, &allset);
1177     g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1178     find_max_fd();
1179 }
1180
1181 static void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1182 {
1183     if (FD_ISSET( (int)n, &selset)) {
1184         event_fd_handler *h = (event_fd_handler *)data;
1185         g_assert(h->fd == (int)n);
1186         h->handler(h->fd, h->data);
1187     }
1188 }
1189
1190 static void fd_event_handle()
1191 {
1192     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1193 }
1194
1195 static void event_handle_dock(ObDock *s, XEvent *e)
1196 {
1197     switch (e->type) {
1198     case ButtonPress:
1199         stacking_raise(DOCK_AS_WINDOW(s));
1200         break;
1201     case EnterNotify:
1202         dock_hide(FALSE);
1203         break;
1204     case LeaveNotify:
1205         dock_hide(TRUE);
1206         break;
1207     }
1208 }
1209
1210 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1211 {
1212     switch (e->type) {
1213     case MotionNotify:
1214         dock_app_drag(app, &e->xmotion);
1215         break;
1216     case UnmapNotify:
1217         if (app->ignore_unmaps) {
1218             app->ignore_unmaps--;
1219             break;
1220         }
1221         dock_remove(app, TRUE);
1222         break;
1223     case DestroyNotify:
1224         dock_remove(app, FALSE);
1225         break;
1226     case ReparentNotify:
1227         dock_remove(app, FALSE);
1228         break;
1229     case ConfigureNotify:
1230         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1231         break;
1232     }
1233 }