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