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