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