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