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