4 space indents
[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         if (e->xconfigure.width != screen_physical_size.width ||
599             e->xconfigure.height != screen_physical_size.height)
600             screen_resize(e->xconfigure.width, e->xconfigure.height);
601         break;
602     default:
603         ;
604 #ifdef VIDMODE
605         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
606             g_message("VIDMODE EVENT");
607         }
608 #endif
609     }
610 }
611
612 static void event_handle_client(Client *client, XEvent *e)
613 {
614     XEvent ce;
615     Atom msgtype;
616     int i=0;
617      
618     switch (e->type) {
619     case ButtonPress:
620     case ButtonRelease:
621         /* Wheel buttons don't draw because they are an instant click, so it
622            is a waste of resources to go drawing it. */
623         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
624             switch (frame_context(client, e->xbutton.window)) {
625             case Context_Maximize:
626                 client->frame->max_press = (e->type == ButtonPress);
627                 framerender_frame(client->frame);
628                 break;
629             case Context_Close:
630                 client->frame->close_press = (e->type == ButtonPress);
631                 framerender_frame(client->frame);
632                 break;
633             case Context_Iconify:
634                 client->frame->iconify_press = (e->type == ButtonPress);
635                 framerender_frame(client->frame);
636                 break;
637             case Context_AllDesktops:
638                 client->frame->desk_press = (e->type == ButtonPress);
639                 framerender_frame(client->frame);
640                 break; 
641             case Context_Shade:
642                 client->frame->shade_press = (e->type == ButtonPress);
643                 framerender_frame(client->frame);
644                 break;
645             default:
646                 /* nothing changes with clicks for any other contexts */
647                 break;
648             }
649         }
650         break;
651     case FocusIn:
652 #ifdef DEBUG_FOCUS
653         g_message("FocusIn on client for %lx", client->window);
654 #endif
655         if (client != focus_client) {
656             focus_set_client(client);
657             frame_adjust_focus(client->frame, TRUE);
658         }
659         break;
660     case FocusOut:
661 #ifdef DEBUG_FOCUS
662         g_message("FocusOut on client for %lx", client->window);
663 #endif
664         /* are we a fullscreen window or a transient of one? (checks layer)
665            if we are then we need to be iconified since we are losing focus
666          */
667         if (client->layer == Layer_Fullscreen && !client->iconic &&
668             !client_search_focus_tree_full(client))
669             /* iconify fullscreen windows when they and their transients
670                aren't focused */
671             client_iconify(client, TRUE, TRUE);
672         frame_adjust_focus(client->frame, FALSE);
673         break;
674     case EnterNotify:
675         if (client_normal(client)) {
676             if (ob_state == State_Starting) {
677                 /* move it to the top of the focus order */
678                 guint desktop = client->desktop;
679                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
680                 focus_order[desktop] = g_list_remove(focus_order[desktop],
681                                                      client);
682                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
683                                                       client);
684             } else if (config_focus_follow) {
685 #ifdef DEBUG_FOCUS
686                 g_message("EnterNotify on %lx, focusing window",
687                           client->window);
688 #endif
689                 client_focus(client);
690             }
691         }
692         break;
693     case ConfigureRequest:
694         /* compress these */
695         while (XCheckTypedWindowEvent(ob_display, client->window,
696                                       ConfigureRequest, &ce)) {
697             ++i;
698             /* XXX if this causes bad things.. we can compress config req's
699                with the same mask. */
700             e->xconfigurerequest.value_mask |=
701                 ce.xconfigurerequest.value_mask;
702             if (ce.xconfigurerequest.value_mask & CWX)
703                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
704             if (ce.xconfigurerequest.value_mask & CWY)
705                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
706             if (ce.xconfigurerequest.value_mask & CWWidth)
707                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
708             if (ce.xconfigurerequest.value_mask & CWHeight)
709                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
710             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
711                 e->xconfigurerequest.border_width =
712                     ce.xconfigurerequest.border_width;
713             if (ce.xconfigurerequest.value_mask & CWStackMode)
714                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
715         }
716
717         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
718         if (client->iconic || client->shaded) return;
719
720         if (e->xconfigurerequest.value_mask & CWBorderWidth)
721             client->border_width = e->xconfigurerequest.border_width;
722
723         /* resize, then move, as specified in the EWMH section 7.7 */
724         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
725                                                CWX | CWY)) {
726             int x, y, w, h;
727             Corner corner;
728                
729             x = (e->xconfigurerequest.value_mask & CWX) ?
730                 e->xconfigurerequest.x : client->area.x;
731             y = (e->xconfigurerequest.value_mask & CWY) ?
732                 e->xconfigurerequest.y : client->area.y;
733             w = (e->xconfigurerequest.value_mask & CWWidth) ?
734                 e->xconfigurerequest.width : client->area.width;
735             h = (e->xconfigurerequest.value_mask & CWHeight) ?
736                 e->xconfigurerequest.height : client->area.height;
737                
738             switch (client->gravity) {
739             case NorthEastGravity:
740             case EastGravity:
741                 corner = Corner_TopRight;
742                 break;
743             case SouthWestGravity:
744             case SouthGravity:
745                 corner = Corner_BottomLeft;
746                 break;
747             case SouthEastGravity:
748                 corner = Corner_BottomRight;
749                 break;
750             default:     /* NorthWest, Static, etc */
751                 corner = Corner_TopLeft;
752             }
753
754             client_configure(client, corner, x, y, w, h, FALSE, TRUE);
755         }
756
757         if (e->xconfigurerequest.value_mask & CWStackMode) {
758             switch (e->xconfigurerequest.detail) {
759             case Below:
760             case BottomIf:
761                 stacking_lower(CLIENT_AS_WINDOW(client));
762                 break;
763
764             case Above:
765             case TopIf:
766             default:
767                 stacking_raise(CLIENT_AS_WINDOW(client));
768                 break;
769             }
770         }
771         break;
772     case UnmapNotify:
773         if (client->ignore_unmaps) {
774             client->ignore_unmaps--;
775             break;
776         }
777         client_unmanage(client);
778         break;
779     case DestroyNotify:
780         client_unmanage(client);
781         break;
782     case ReparentNotify:
783         /* this is when the client is first taken captive in the frame */
784         if (e->xreparent.parent == client->frame->plate) break;
785
786         /*
787           This event is quite rare and is usually handled in unmapHandler.
788           However, if the window is unmapped when the reparent event occurs,
789           the window manager never sees it because an unmap event is not sent
790           to an already unmapped window.
791         */
792
793         /* we don't want the reparent event, put it back on the stack for the
794            X server to deal with after we unmanage the window */
795         XPutBackEvent(ob_display, e);
796      
797         client_unmanage(client);
798         break;
799     case MapRequest:
800         g_message("MapRequest for 0x%lx", client->window);
801         if (!client->iconic) break; /* this normally doesn't happen, but if it
802                                        does, we don't want it! */
803         if (screen_showing_desktop)
804             screen_show_desktop(FALSE);
805         client_iconify(client, FALSE, TRUE);
806         if (!client->frame->visible)
807             /* if its not visible still, then don't mess with it */
808             break;
809         if (client->shaded)
810             client_shade(client, FALSE);
811         client_focus(client);
812         stacking_raise(CLIENT_AS_WINDOW(client));
813         break;
814     case ClientMessage:
815         /* validate cuz we query stuff off the client here */
816         if (!client_validate(client)) break;
817   
818         if (e->xclient.format != 32) return;
819
820         msgtype = e->xclient.message_type;
821         if (msgtype == prop_atoms.wm_change_state) {
822             /* compress changes into a single change */
823             while (XCheckTypedWindowEvent(ob_display, e->type,
824                                           client->window, &ce)) {
825                 /* XXX: it would be nice to compress ALL messages of a
826                    type, not just messages in a row without other
827                    message types between. */
828                 if (ce.xclient.message_type != msgtype) {
829                     XPutBackEvent(ob_display, &ce);
830                     break;
831                 }
832                 e->xclient = ce.xclient;
833             }
834             client_set_wm_state(client, e->xclient.data.l[0]);
835         } else if (msgtype == prop_atoms.net_wm_desktop) {
836             /* compress changes into a single change */
837             while (XCheckTypedWindowEvent(ob_display, e->type,
838                                           client->window, &ce)) {
839                 /* XXX: it would be nice to compress ALL messages of a
840                    type, not just messages in a row without other
841                    message types between. */
842                 if (ce.xclient.message_type != msgtype) {
843                     XPutBackEvent(ob_display, &ce);
844                     break;
845                 }
846                 e->xclient = ce.xclient;
847             }
848             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
849                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
850                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
851                                    FALSE);
852         } else if (msgtype == prop_atoms.net_wm_state) {
853             /* can't compress these */
854             g_message("net_wm_state %s %ld %ld for 0x%lx",
855                       (e->xclient.data.l[0] == 0 ? "Remove" :
856                        e->xclient.data.l[0] == 1 ? "Add" :
857                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
858                       e->xclient.data.l[1], e->xclient.data.l[2],
859                       client->window);
860             client_set_state(client, e->xclient.data.l[0],
861                              e->xclient.data.l[1], e->xclient.data.l[2]);
862         } else if (msgtype == prop_atoms.net_close_window) {
863             g_message("net_close_window for 0x%lx", client->window);
864             client_close(client);
865         } else if (msgtype == prop_atoms.net_active_window) {
866             g_message("net_active_window for 0x%lx", client->window);
867             client_activate(client);
868         } else if (msgtype == prop_atoms.net_wm_moveresize) {
869             g_message("net_wm_moveresize for 0x%lx", client->window);
870             if ((Atom)e->xclient.data.l[2] ==
871                 prop_atoms.net_wm_moveresize_size_topleft ||
872                 (Atom)e->xclient.data.l[2] ==
873                 prop_atoms.net_wm_moveresize_size_top ||
874                 (Atom)e->xclient.data.l[2] ==
875                 prop_atoms.net_wm_moveresize_size_topright ||
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_right ||
880                 (Atom)e->xclient.data.l[2] ==
881                 prop_atoms.net_wm_moveresize_size_bottomright ||
882                 (Atom)e->xclient.data.l[2] ==
883                 prop_atoms.net_wm_moveresize_size_bottom ||
884                 (Atom)e->xclient.data.l[2] ==
885                 prop_atoms.net_wm_moveresize_size_bottomleft ||
886                 (Atom)e->xclient.data.l[2] ==
887                 prop_atoms.net_wm_moveresize_size_left ||
888                 (Atom)e->xclient.data.l[2] ==
889                 prop_atoms.net_wm_moveresize_move ||
890                 (Atom)e->xclient.data.l[2] ==
891                 prop_atoms.net_wm_moveresize_size_keyboard ||
892                 (Atom)e->xclient.data.l[2] ==
893                 prop_atoms.net_wm_moveresize_move_keyboard) {
894
895                 moveresize_start(client, e->xclient.data.l[0],
896                                  e->xclient.data.l[1], e->xclient.data.l[3],
897                                  e->xclient.data.l[2]);
898             }
899         } else if (msgtype == prop_atoms.net_moveresize_window) {
900             int oldg = client->gravity;
901             int tmpg, x, y, w, h;
902
903             if (e->xclient.data.l[0] & 0xff)
904                 tmpg = e->xclient.data.l[0] & 0xff;
905             else
906                 tmpg = oldg;
907
908             if (e->xclient.data.l[0] & 1 << 8)
909                 x = e->xclient.data.l[1];
910             else
911                 x = client->area.x;
912             if (e->xclient.data.l[0] & 1 << 9)
913                 y = e->xclient.data.l[2];
914             else
915                 y = client->area.y;
916             if (e->xclient.data.l[0] & 1 << 10)
917                 w = e->xclient.data.l[3];
918             else
919                 w = client->area.y;
920             if (e->xclient.data.l[0] & 1 << 11)
921                 h = e->xclient.data.l[4];
922             else
923                 h = client->area.y;
924             client->gravity = tmpg;
925             client_configure(client, Corner_TopLeft, x, y, w, h, FALSE, TRUE);
926             client->gravity = oldg;
927         }
928         break;
929     case PropertyNotify:
930         /* validate cuz we query stuff off the client here */
931         if (!client_validate(client)) break;
932   
933         /* compress changes to a single property into a single change */
934         while (XCheckTypedWindowEvent(ob_display, e->type,
935                                       client->window, &ce)) {
936             /* XXX: it would be nice to compress ALL changes to a property,
937                not just changes in a row without other props between. */
938             if (ce.xproperty.atom != e->xproperty.atom) {
939                 XPutBackEvent(ob_display, &ce);
940                 break;
941             }
942         }
943
944         msgtype = e->xproperty.atom;
945         if (msgtype == XA_WM_NORMAL_HINTS) {
946             client_update_normal_hints(client);
947             /* normal hints can make a window non-resizable */
948             client_setup_decor_and_functions(client);
949         }
950         else if (msgtype == XA_WM_HINTS)
951             client_update_wmhints(client);
952         else if (msgtype == XA_WM_TRANSIENT_FOR) {
953             client_update_transient_for(client);
954             client_get_type(client);
955             /* type may have changed, so update the layer */
956             client_calc_layer(client);
957             client_setup_decor_and_functions(client);
958         }
959         else if (msgtype == prop_atoms.net_wm_name ||
960                  msgtype == prop_atoms.wm_name ||
961                  msgtype == prop_atoms.net_wm_icon_name ||
962                  msgtype == prop_atoms.wm_icon_name)
963             client_update_title(client);
964         else if (msgtype == prop_atoms.wm_class)
965             client_update_class(client);
966         else if (msgtype == prop_atoms.wm_protocols) {
967             client_update_protocols(client);
968             client_setup_decor_and_functions(client);
969         }
970         else if (msgtype == prop_atoms.net_wm_strut)
971             client_update_strut(client);
972         else if (msgtype == prop_atoms.net_wm_icon ||
973                  msgtype == prop_atoms.kwm_win_icon)
974             client_update_icons(client);
975     default:
976         ;
977 #ifdef SHAPE
978         if (extensions_shape && e->type == extensions_shape_event_basep) {
979             client->shaped = ((XShapeEvent*)e)->shaped;
980             frame_adjust_shape(client->frame);
981         }
982 #endif
983     }
984 }
985
986 static void event_handle_menu(Client *client, XEvent *e)
987 {
988     static MenuEntry *over = NULL;
989     MenuEntry *entry;
990     Menu *top;
991     GSList *it;
992
993     top = g_slist_nth_data(menu_visible, 0);
994
995     g_message("EVENT %d", e->type);
996     switch (e->type) {
997     case KeyPress:
998         if (over) {
999             if (over->parent->mouseover)
1000                 over->parent->mouseover(over, FALSE);
1001             else
1002                 menu_control_mouseover(over, FALSE);
1003             menu_entry_render(over);
1004             over = NULL;
1005         }
1006 /*
1007         if (top->hide)
1008             top->hide(top);
1009         else
1010 */
1011             menu_hide(top);
1012         break;
1013     case ButtonPress:
1014         if (e->xbutton.button > 3) break;
1015
1016         g_message("BUTTON PRESS");
1017         break;
1018     case ButtonRelease:
1019         if (e->xbutton.button > 3) break;
1020
1021         g_message("BUTTON RELEASED");
1022
1023         for (it = menu_visible; it; it = g_slist_next(it)) {
1024             Menu *m = it->data;
1025             if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
1026                 e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
1027                 e->xbutton.x_root < m->location.x + m->size.width +
1028                 ob_rr_theme->bwidth &&
1029                 e->xbutton.y_root < m->location.y + m->size.height +
1030                 ob_rr_theme->bwidth) {
1031                 if ((entry = menu_find_entry_by_pos(it->data,
1032                                                     e->xbutton.x_root -
1033                                                     m->location.x,
1034                                                     e->xbutton.y_root -
1035                                                     m->location.y))) {
1036                     if (over) {
1037                         if (over->parent->mouseover)
1038                             over->parent->mouseover(over, FALSE);
1039                         else
1040                             menu_control_mouseover(over, FALSE); 
1041                         menu_entry_render(over);
1042                         over = NULL;
1043                     }
1044                     /* this hides the menu */
1045                     menu_entry_fire(entry);
1046                 }
1047                 break;
1048             }
1049         }
1050         if (!it) {
1051             if (over) {
1052                 if (over->parent->mouseover)
1053                     over->parent->mouseover(over, FALSE);
1054                 else
1055                     menu_control_mouseover(over, FALSE); 
1056                 menu_entry_render(over);
1057                 over = NULL;
1058             }
1059 /*
1060             if (top->hide)
1061                 top->hide(top);
1062             else
1063 */
1064                 menu_hide(top);
1065         }
1066         
1067         break;
1068     case MotionNotify:
1069         g_message("motion");
1070         for (it = menu_visible; it; it = g_slist_next(it)) {
1071             Menu *m = it->data;
1072             if ((entry = menu_find_entry_by_pos(it->data,
1073                                                 e->xmotion.x_root -
1074                                                 m->location.x,
1075                                                 e->xmotion.y_root -
1076                                                 m->location.y))) {
1077                 if (over && entry != over) {
1078                     if (over->parent->mouseover)
1079                         over->parent->mouseover(over, FALSE);
1080                     else
1081                         menu_control_mouseover(over, FALSE);
1082                     menu_entry_render(over);
1083                 }
1084
1085                 over = entry;
1086                 if (over->parent->mouseover)
1087                     over->parent->mouseover(over, TRUE);
1088                 else
1089                     menu_control_mouseover(over, TRUE);
1090                 menu_entry_render(over);
1091                 break;
1092             }
1093         }
1094         if (!it && over) {
1095             if (over->parent->mouseover)
1096                 over->parent->mouseover(over, FALSE);
1097             else
1098                 menu_control_mouseover(over, FALSE);
1099             menu_entry_render(over);
1100             over = NULL;
1101         }
1102         break;
1103     }
1104 }
1105
1106 void event_add_fd_handler(event_fd_handler *h) {
1107     g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1108     FD_SET(h->fd, &allset);
1109     max_fd = MAX(max_fd, h->fd);
1110 }
1111
1112 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1113 {
1114     *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1115 }
1116
1117 static void find_max_fd()
1118
1119     int tmpmax = -1;
1120     g_datalist_foreach(&fd_handler_list, find_max_fd_foreach,
1121                        (gpointer)&tmpmax);
1122     max_fd = MAX(x_fd, tmpmax);
1123 #ifdef USE_SM
1124     max_fd = MAX(ice_fd, tmpmax);
1125 #endif
1126 }
1127
1128 void event_remove_fd(int n)
1129 {
1130     FD_CLR(n, &allset);
1131     g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1132     find_max_fd();
1133 }
1134
1135 static void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1136 {
1137     if (FD_ISSET( (int)n, &selset)) {
1138         event_fd_handler *h = (event_fd_handler *)data;
1139         g_assert(h->fd == (int)n);
1140         h->handler(h->fd, h->data);
1141     }
1142 }
1143
1144 static void fd_event_handle()
1145 {
1146     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1147 }
1148
1149 static void event_handle_dock(Dock *s, XEvent *e)
1150 {
1151     switch (e->type) {
1152     case ButtonPress:
1153         stacking_raise(DOCK_AS_WINDOW(s));
1154         break;
1155     case EnterNotify:
1156         dock_hide(FALSE);
1157         break;
1158     case LeaveNotify:
1159         dock_hide(TRUE);
1160         break;
1161     }
1162 }
1163
1164 static void event_handle_dockapp(DockApp *app, XEvent *e)
1165 {
1166     switch (e->type) {
1167     case MotionNotify:
1168         dock_app_drag(app, &e->xmotion);
1169         break;
1170     case UnmapNotify:
1171         if (app->ignore_unmaps) {
1172             app->ignore_unmaps--;
1173             break;
1174         }
1175         dock_remove(app, TRUE);
1176         break;
1177     case DestroyNotify:
1178         dock_remove(app, FALSE);
1179         break;
1180     case ReparentNotify:
1181         dock_remove(app, FALSE);
1182         break;
1183     case ConfigureNotify:
1184         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1185         break;
1186     }
1187 }