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