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