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