ignore ancestor FocusIn events. i get them sometimes when hitting alt-tab and that...
[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                     if (fe.xfocus.window == e->xfocus.window &&
346                         !event_ignore(&fe, client)) {
347 #ifdef DEBUG_FOCUS
348                         g_message("focused window got an Out/In back to "
349                                   "itself IGNORED both");
350 #endif
351                         return TRUE;
352                     }
353
354                     /* once all the FocusOut's have been dealt with, if there
355                        is a FocusIn still left and it is valid, then use it */
356                     event_process(&fe);
357                     /* secret magic way of event_process telling us that no
358                        client was found for the FocusIn event. ^_^ */
359                     if (fe.xfocus.window != None) {
360                         fallback = FALSE;
361                         break;
362                     }
363                 }
364             }
365             if (fallback) {
366 #ifdef DEBUG_FOCUS
367                 g_message("no valid FocusIn and no FocusOut events found, "
368                           "falling back");
369 #endif
370                 focus_fallback(Fallback_NoFocus);
371             }
372         }
373         break;
374     case EnterNotify:
375     case LeaveNotify:
376         /* NotifyUngrab occurs when a mouse button is released and the event is
377            caused, like when lowering a window */
378         /* NotifyVirtual occurs when ungrabbing the pointer */
379         if (e->xcrossing.mode == NotifyGrab ||
380             e->xcrossing.detail == NotifyInferior ||
381             (e->xcrossing.mode == NotifyUngrab &&
382              e->xcrossing.detail == NotifyVirtual)) {
383 #ifdef DEBUG_FOCUS
384             g_message("%sNotify mode %d detail %d on %lx IGNORED",
385                       (e->type == EnterNotify ? "Enter" : "Leave"),
386                       e->xcrossing.mode,
387                       e->xcrossing.detail, client?client->window:0);
388 #endif
389             return TRUE;
390         }
391 #ifdef DEBUG_FOCUS
392         g_message("%sNotify mode %d detail %d on %lx",
393                   (e->type == EnterNotify ? "Enter" : "Leave"),
394                   e->xcrossing.mode,
395                   e->xcrossing.detail, client?client->window:0);
396 #endif
397         break;
398     }
399     return FALSE;
400 }
401
402 static void event_process(XEvent *e)
403 {
404     Window window;
405     Client *client = NULL;
406     Dock *dock = NULL;
407     DockApp *dockapp = NULL;
408     Menu *menu = NULL;
409     ObWindow *obwin = NULL;
410
411     window = event_get_window(e);
412     if ((obwin = g_hash_table_lookup(window_map, &window))) {
413         switch (obwin->type) {
414         case Window_Dock:
415             dock = WINDOW_AS_DOCK(obwin);
416             break;
417         case Window_DockApp:
418             dockapp = WINDOW_AS_DOCKAPP(obwin);
419             break;
420         case Window_Menu:
421             menu = WINDOW_AS_MENU(obwin);
422             break;
423         case Window_Client:
424             client = WINDOW_AS_CLIENT(obwin);
425             break;
426         case Window_Internal:
427             /* not to be used for events */
428             g_assert_not_reached();
429             break;
430         }
431     }
432
433     event_set_lasttime(e);
434     event_hack_mods(e);
435     if (event_ignore(e, client))
436         return;
437
438     /* deal with it in the kernel */
439     if (menu) {
440         event_handle_menu(menu, client, e);
441         return;
442     } else if (client)
443         event_handle_client(client, e);
444     else if (dockapp)
445         event_handle_dockapp(dockapp, e);
446     else if (dock)
447         event_handle_dock(dock, e);
448     else if (window == ob_root)
449         event_handle_root(e);
450     else if (e->type == MapRequest)
451         client_manage(window);
452     else if (e->type == ConfigureRequest) {
453         /* unhandled configure requests must be used to configure the
454            window directly */
455         XWindowChanges xwc;
456                
457         xwc.x = e->xconfigurerequest.x;
458         xwc.y = e->xconfigurerequest.y;
459         xwc.width = e->xconfigurerequest.width;
460         xwc.height = e->xconfigurerequest.height;
461         xwc.border_width = e->xconfigurerequest.border_width;
462         xwc.sibling = e->xconfigurerequest.above;
463         xwc.stack_mode = e->xconfigurerequest.detail;
464        
465         /* we are not to be held responsible if someone sends us an
466            invalid request! */
467         xerror_set_ignore(TRUE);
468         XConfigureWindow(ob_display, window,
469                          e->xconfigurerequest.value_mask, &xwc);
470         xerror_set_ignore(FALSE);
471     }
472
473     if (moveresize_in_progress)
474         if (e->type == MotionNotify || e->type == ButtonRelease ||
475             e->type == ButtonPress ||
476             e->type == KeyPress || e->type == KeyRelease) {
477             moveresize_event(e);
478
479             return; /* no dispatch! */
480             
481         }
482
483     /* user input (action-bound) events */
484     /*
485     if (e->type == ButtonPress || e->type == ButtonRelease ||
486         e->type == MotionNotify)
487         mouse_event(e, client);
488     else if (e->type == KeyPress || e->type == KeyRelease)
489         ;
490     */
491
492     /* dispatch the event to registered handlers */
493     dispatch_x(e, client);
494 }
495
496 static void event_handle_root(XEvent *e)
497 {
498     Atom msgtype;
499      
500     switch(e->type) {
501     case ClientMessage:
502         if (e->xclient.format != 32) break;
503
504         msgtype = e->xclient.message_type;
505         if (msgtype == prop_atoms.net_current_desktop) {
506             unsigned int d = e->xclient.data.l[0];
507             if (d < screen_num_desktops)
508                 screen_set_desktop(d);
509         } else if (msgtype == prop_atoms.net_number_of_desktops) {
510             unsigned int d = e->xclient.data.l[0];
511             if (d > 0)
512                 screen_set_num_desktops(d);
513         } else if (msgtype == prop_atoms.net_showing_desktop) {
514             screen_show_desktop(e->xclient.data.l[0] != 0);
515         }
516         break;
517     case PropertyNotify:
518         if (e->xproperty.atom == prop_atoms.net_desktop_names)
519             screen_update_desktop_names();
520         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
521             screen_update_layout();
522         break;
523     case ConfigureNotify:
524 #ifdef XRANDR
525         XRRUpdateConfiguration(e);
526 #endif
527         if (e->xconfigure.width != screen_physical_size.width ||
528             e->xconfigure.height != screen_physical_size.height)
529             screen_resize(e->xconfigure.width, e->xconfigure.height);
530         break;
531     default:
532         ;
533 #ifdef VIDMODE
534         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
535             g_message("VIDMODE EVENT");
536         }
537 #endif
538     }
539 }
540
541 static void event_handle_client(Client *client, XEvent *e)
542 {
543     XEvent ce;
544     Atom msgtype;
545     int i=0;
546      
547     switch (e->type) {
548     case ButtonPress:
549     case ButtonRelease:
550         /* Wheel buttons don't draw because they are an instant click, so it
551            is a waste of resources to go drawing it. */
552         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
553             switch (frame_context(client, e->xbutton.window)) {
554             case Context_Maximize:
555                 client->frame->max_press = (e->type == ButtonPress);
556                 framerender_frame(client->frame);
557                 break;
558             case Context_Close:
559                 client->frame->close_press = (e->type == ButtonPress);
560                 framerender_frame(client->frame);
561                 break;
562             case Context_Iconify:
563                 client->frame->iconify_press = (e->type == ButtonPress);
564                 framerender_frame(client->frame);
565                 break;
566             case Context_AllDesktops:
567                 client->frame->desk_press = (e->type == ButtonPress);
568                 framerender_frame(client->frame);
569                 break; 
570             case Context_Shade:
571                 client->frame->shade_press = (e->type == ButtonPress);
572                 framerender_frame(client->frame);
573                 break;
574             default:
575                 /* nothing changes with clicks for any other contexts */
576                 break;
577             }
578         }
579         break;
580     case FocusIn:
581 #ifdef DEBUG_FOCUS
582         g_message("FocusIn on client for %lx", client->window);
583 #endif
584         focus_set_client(client);
585         frame_adjust_focus(client->frame, TRUE);
586         break;
587     case FocusOut:
588 #ifdef DEBUG_FOCUS
589         g_message("FocusOut on client for %lx", client->window);
590 #endif
591         /* are we a fullscreen window or a transient of one? (checks layer)
592            if we are then we need to be iconified since we are losing focus
593          */
594         if (client->layer == Layer_Fullscreen && !client->iconic &&
595             !client_search_focus_tree_full(client))
596             /* iconify fullscreen windows when they and their transients
597                aren't focused */
598             client_iconify(client, TRUE, TRUE);
599         frame_adjust_focus(client->frame, FALSE);
600         break;
601     case EnterNotify:
602         if (client_normal(client)) {
603             if (ob_state == State_Starting) {
604                 /* move it to the top of the focus order */
605                 guint desktop = client->desktop;
606                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
607                 focus_order[desktop] = g_list_remove(focus_order[desktop],
608                                                      client);
609                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
610                                                       client);
611             } else if (config_focus_follow) {
612 #ifdef DEBUG_FOCUS
613                 g_message("EnterNotify on %lx, focusing window",
614                           client->window);
615 #endif
616                 client_focus(client);
617             }
618         }
619         break;
620     case ConfigureRequest:
621         /* compress these */
622         while (XCheckTypedWindowEvent(ob_display, client->window,
623                                       ConfigureRequest, &ce)) {
624             ++i;
625             /* XXX if this causes bad things.. we can compress config req's
626                with the same mask. */
627             e->xconfigurerequest.value_mask |=
628                 ce.xconfigurerequest.value_mask;
629             if (ce.xconfigurerequest.value_mask & CWX)
630                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
631             if (ce.xconfigurerequest.value_mask & CWY)
632                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
633             if (ce.xconfigurerequest.value_mask & CWWidth)
634                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
635             if (ce.xconfigurerequest.value_mask & CWHeight)
636                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
637             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
638                 e->xconfigurerequest.border_width =
639                     ce.xconfigurerequest.border_width;
640             if (ce.xconfigurerequest.value_mask & CWStackMode)
641                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
642         }
643
644         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
645         if (client->iconic || client->shaded) return;
646
647         if (e->xconfigurerequest.value_mask & CWBorderWidth)
648             client->border_width = e->xconfigurerequest.border_width;
649
650         /* resize, then move, as specified in the EWMH section 7.7 */
651         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
652                                                CWX | CWY)) {
653             int x, y, w, h;
654             Corner corner;
655                
656             x = (e->xconfigurerequest.value_mask & CWX) ?
657                 e->xconfigurerequest.x : client->area.x;
658             y = (e->xconfigurerequest.value_mask & CWY) ?
659                 e->xconfigurerequest.y : client->area.y;
660             w = (e->xconfigurerequest.value_mask & CWWidth) ?
661                 e->xconfigurerequest.width : client->area.width;
662             h = (e->xconfigurerequest.value_mask & CWHeight) ?
663                 e->xconfigurerequest.height : client->area.height;
664                
665             switch (client->gravity) {
666             case NorthEastGravity:
667             case EastGravity:
668                 corner = Corner_TopRight;
669                 break;
670             case SouthWestGravity:
671             case SouthGravity:
672                 corner = Corner_BottomLeft;
673                 break;
674             case SouthEastGravity:
675                 corner = Corner_BottomRight;
676                 break;
677             default:     /* NorthWest, Static, etc */
678                 corner = Corner_TopLeft;
679             }
680
681             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
682         }
683
684         if (e->xconfigurerequest.value_mask & CWStackMode) {
685             switch (e->xconfigurerequest.detail) {
686             case Below:
687             case BottomIf:
688                 stacking_lower(CLIENT_AS_WINDOW(client));
689                 break;
690
691             case Above:
692             case TopIf:
693             default:
694                 stacking_raise(CLIENT_AS_WINDOW(client));
695                 break;
696             }
697         }
698         break;
699     case UnmapNotify:
700         if (client->ignore_unmaps) {
701             client->ignore_unmaps--;
702             break;
703         }
704         client_unmanage(client);
705         break;
706     case DestroyNotify:
707         client_unmanage(client);
708         break;
709     case ReparentNotify:
710         /* this is when the client is first taken captive in the frame */
711         if (e->xreparent.parent == client->frame->plate) break;
712
713         /*
714           This event is quite rare and is usually handled in unmapHandler.
715           However, if the window is unmapped when the reparent event occurs,
716           the window manager never sees it because an unmap event is not sent
717           to an already unmapped window.
718         */
719
720         /* we don't want the reparent event, put it back on the stack for the
721            X server to deal with after we unmanage the window */
722         XPutBackEvent(ob_display, e);
723      
724         client_unmanage(client);
725         break;
726     case MapRequest:
727         g_message("MapRequest for 0x%lx", client->window);
728         if (!client->iconic) break; /* this normally doesn't happen, but if it
729                                        does, we don't want it! */
730         if (screen_showing_desktop)
731             screen_show_desktop(FALSE);
732         client_iconify(client, FALSE, TRUE);
733         if (!client->frame->visible)
734             /* if its not visible still, then don't mess with it */
735             break;
736         if (client->shaded)
737             client_shade(client, FALSE);
738         client_focus(client);
739         stacking_raise(CLIENT_AS_WINDOW(client));
740         break;
741     case ClientMessage:
742         /* validate cuz we query stuff off the client here */
743         if (!client_validate(client)) break;
744   
745         if (e->xclient.format != 32) return;
746
747         msgtype = e->xclient.message_type;
748         if (msgtype == prop_atoms.wm_change_state) {
749             /* compress changes into a single change */
750             while (XCheckTypedWindowEvent(ob_display, e->type,
751                                           client->window, &ce)) {
752                 /* XXX: it would be nice to compress ALL messages of a
753                    type, not just messages in a row without other
754                    message types between. */
755                 if (ce.xclient.message_type != msgtype) {
756                     XPutBackEvent(ob_display, &ce);
757                     break;
758                 }
759                 e->xclient = ce.xclient;
760             }
761             client_set_wm_state(client, e->xclient.data.l[0]);
762         } else if (msgtype == prop_atoms.net_wm_desktop) {
763             /* compress changes into a single change */
764             while (XCheckTypedWindowEvent(ob_display, e->type,
765                                           client->window, &ce)) {
766                 /* XXX: it would be nice to compress ALL messages of a
767                    type, not just messages in a row without other
768                    message types between. */
769                 if (ce.xclient.message_type != msgtype) {
770                     XPutBackEvent(ob_display, &ce);
771                     break;
772                 }
773                 e->xclient = ce.xclient;
774             }
775             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
776                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
777                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
778                                    FALSE);
779         } else if (msgtype == prop_atoms.net_wm_state) {
780             /* can't compress these */
781             g_message("net_wm_state %s %ld %ld for 0x%lx",
782                       (e->xclient.data.l[0] == 0 ? "Remove" :
783                        e->xclient.data.l[0] == 1 ? "Add" :
784                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
785                       e->xclient.data.l[1], e->xclient.data.l[2],
786                       client->window);
787             client_set_state(client, e->xclient.data.l[0],
788                              e->xclient.data.l[1], e->xclient.data.l[2]);
789         } else if (msgtype == prop_atoms.net_close_window) {
790             g_message("net_close_window for 0x%lx", client->window);
791             client_close(client);
792         } else if (msgtype == prop_atoms.net_active_window) {
793             g_message("net_active_window for 0x%lx", client->window);
794             client_activate(client);
795         } else if (msgtype == prop_atoms.net_wm_moveresize) {
796             g_message("net_wm_moveresize for 0x%lx", client->window);
797             if ((Atom)e->xclient.data.l[2] ==
798                 prop_atoms.net_wm_moveresize_size_topleft ||
799                 (Atom)e->xclient.data.l[2] ==
800                 prop_atoms.net_wm_moveresize_size_top ||
801                 (Atom)e->xclient.data.l[2] ==
802                 prop_atoms.net_wm_moveresize_size_topright ||
803                 (Atom)e->xclient.data.l[2] ==
804                 prop_atoms.net_wm_moveresize_size_right ||
805                 (Atom)e->xclient.data.l[2] ==
806                 prop_atoms.net_wm_moveresize_size_right ||
807                 (Atom)e->xclient.data.l[2] ==
808                 prop_atoms.net_wm_moveresize_size_bottomright ||
809                 (Atom)e->xclient.data.l[2] ==
810                 prop_atoms.net_wm_moveresize_size_bottom ||
811                 (Atom)e->xclient.data.l[2] ==
812                 prop_atoms.net_wm_moveresize_size_bottomleft ||
813                 (Atom)e->xclient.data.l[2] ==
814                 prop_atoms.net_wm_moveresize_size_left ||
815                 (Atom)e->xclient.data.l[2] ==
816                 prop_atoms.net_wm_moveresize_move ||
817                 (Atom)e->xclient.data.l[2] ==
818                 prop_atoms.net_wm_moveresize_size_keyboard ||
819                 (Atom)e->xclient.data.l[2] ==
820                 prop_atoms.net_wm_moveresize_move_keyboard) {
821
822                 moveresize_start(client, e->xclient.data.l[0],
823                                  e->xclient.data.l[1], e->xclient.data.l[3],
824                                  e->xclient.data.l[2]);
825             }
826         } else if (msgtype == prop_atoms.net_moveresize_window) {
827             int oldg = client->gravity;
828             int tmpg, x, y, w, h;
829
830             if (e->xclient.data.l[0] & 0xff)
831                 tmpg = e->xclient.data.l[0] & 0xff;
832             else
833                 tmpg = oldg;
834
835             if (e->xclient.data.l[0] & 1 << 8)
836                 x = e->xclient.data.l[1];
837             else
838                 x = client->area.x;
839             if (e->xclient.data.l[0] & 1 << 9)
840                 y = e->xclient.data.l[2];
841             else
842                 y = client->area.y;
843             if (e->xclient.data.l[0] & 1 << 10)
844                 w = e->xclient.data.l[3];
845             else
846                 w = client->area.y;
847             if (e->xclient.data.l[0] & 1 << 11)
848                 h = e->xclient.data.l[4];
849             else
850                 h = client->area.y;
851             client->gravity = tmpg;
852             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
853             client->gravity = oldg;
854         }
855         break;
856     case PropertyNotify:
857         /* validate cuz we query stuff off the client here */
858         if (!client_validate(client)) break;
859   
860         /* compress changes to a single property into a single change */
861         while (XCheckTypedWindowEvent(ob_display, e->type,
862                                       client->window, &ce)) {
863             /* XXX: it would be nice to compress ALL changes to a property,
864                not just changes in a row without other props between. */
865             if (ce.xproperty.atom != e->xproperty.atom) {
866                 XPutBackEvent(ob_display, &ce);
867                 break;
868             }
869         }
870
871         msgtype = e->xproperty.atom;
872         if (msgtype == XA_WM_NORMAL_HINTS) {
873             client_update_normal_hints(client);
874             /* normal hints can make a window non-resizable */
875             client_setup_decor_and_functions(client);
876         }
877         else if (msgtype == XA_WM_HINTS)
878             client_update_wmhints(client);
879         else if (msgtype == XA_WM_TRANSIENT_FOR) {
880             client_update_transient_for(client);
881             client_get_type(client);
882             /* type may have changed, so update the layer */
883             client_calc_layer(client);
884             client_setup_decor_and_functions(client);
885         }
886         else if (msgtype == prop_atoms.net_wm_name ||
887                  msgtype == prop_atoms.wm_name ||
888                  msgtype == prop_atoms.net_wm_icon_name ||
889                  msgtype == prop_atoms.wm_icon_name)
890             client_update_title(client);
891         else if (msgtype == prop_atoms.wm_class)
892             client_update_class(client);
893         else if (msgtype == prop_atoms.wm_protocols) {
894             client_update_protocols(client);
895             client_setup_decor_and_functions(client);
896         }
897         else if (msgtype == prop_atoms.net_wm_strut)
898             client_update_strut(client);
899         else if (msgtype == prop_atoms.net_wm_icon ||
900                  msgtype == prop_atoms.kwm_win_icon)
901             client_update_icons(client);
902     default:
903         ;
904 #ifdef SHAPE
905         if (extensions_shape && e->type == extensions_shape_event_basep) {
906             client->shaped = ((XShapeEvent*)e)->shaped;
907             frame_adjust_shape(client->frame);
908         }
909 #endif
910     }
911 }
912
913 static void event_handle_menu(Menu *menu, Client *client, XEvent *e)
914 {
915     MenuEntry *entry;
916
917     g_message("EVENT %d", e->type);
918     switch (e->type) {
919     case ButtonPress:
920         g_message("BUTTON PRESS");
921         if (e->xbutton.button == 3)
922             menu_hide(menu);
923         else if (e->xbutton.button == 1) {
924             entry = menu_find_entry(menu, e->xbutton.window);
925             if (!entry)
926                 stacking_raise(MENU_AS_WINDOW(menu));
927         }
928         break;
929     case ButtonRelease:
930         g_message("BUTTON RELEASED");
931         if (!menu->shown) break;
932
933 /*        grab_pointer_window(FALSE, None, menu->frame);*/
934
935         if (e->xbutton.button == 1) {
936             entry = menu_find_entry(menu, e->xbutton.window);
937             if (entry) {
938                 int junk;
939                 Window wjunk;
940                 guint ujunk, b, w, h;
941                 XGetGeometry(ob_display, e->xbutton.window,
942                              &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
943                 if (e->xbutton.x >= (signed)-b &&
944                     e->xbutton.y >= (signed)-b &&
945                     e->xbutton.x < (signed)(w+b) &&
946                     e->xbutton.y < (signed)(h+b)) {
947                     menu_entry_fire(entry);
948                 }
949             }
950         }
951         
952         break;
953     case EnterNotify:
954     case LeaveNotify:
955         g_message("enter/leave");
956         entry = menu_find_entry(menu, e->xcrossing.window);
957         if (entry) {
958             if (menu->mouseover)
959                 menu->mouseover(entry, e->type == EnterNotify);
960             else
961                 menu_control_mouseover(entry, e->type == EnterNotify);
962             
963             menu_entry_render(entry);
964         }
965         break;
966     }
967 }
968
969 void event_add_fd_handler(event_fd_handler *h) {
970   g_datalist_id_set_data(&fd_handler_list, h->fd, h);
971   FD_SET(h->fd, &allset);
972   max_fd = MAX(max_fd, h->fd);
973 }
974
975 void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
976 {
977   *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
978 }
979
980 void event_remove_fd(int n)
981 {
982   int tmpmax = 0;
983   FD_CLR(n, &allset);
984   g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
985   g_datalist_foreach(&fd_handler_list, find_max_fd_foreach, (gpointer)&tmpmax);
986   max_fd = MAX(x_fd, tmpmax);
987 }
988
989 void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
990 {
991     if (FD_ISSET( (int)n, &selset)) {
992         event_fd_handler *h = (event_fd_handler *)data;
993         g_assert(h->fd == (int)n);
994         h->handler(h->fd, h->data);
995     }
996 }
997
998 void fd_event_handle()
999 {
1000     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1001 }
1002
1003 static void event_handle_dock(Dock *s, XEvent *e)
1004 {
1005     switch (e->type) {
1006     case ButtonPress:
1007         stacking_raise(DOCK_AS_WINDOW(s));
1008         break;
1009     case EnterNotify:
1010         dock_hide(FALSE);
1011         break;
1012     case LeaveNotify:
1013         dock_hide(TRUE);
1014         break;
1015     }
1016 }
1017
1018 static void event_handle_dockapp(DockApp *app, XEvent *e)
1019 {
1020     switch (e->type) {
1021     case MotionNotify:
1022         dock_app_drag(app, &e->xmotion);
1023         break;
1024     case UnmapNotify:
1025         if (app->ignore_unmaps) {
1026             app->ignore_unmaps--;
1027             break;
1028         }
1029         dock_remove(app, TRUE);
1030         break;
1031     case DestroyNotify:
1032         dock_remove(app, FALSE);
1033         break;
1034     case ReparentNotify:
1035         dock_remove(app, FALSE);
1036         break;
1037     case ConfigureNotify:
1038         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1039         break;
1040     }
1041 }