nm cant do it.. damn
[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 (!XCheckTypedEvent(ob_display, FocusOut, &fe))
295                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
296                         break;
297                 if (fe.type == FocusOut) {
298 #ifdef DEBUG_FOCUS
299                     g_message("found pending FocusOut");
300 #endif
301                     if (!INVALID_FOCUSOUT(&fe)) {
302                         /* if there is a VALID FocusOut still coming, don't
303                            fallback focus yet, we'll deal with it then */
304                         XPutBackEvent(ob_display, &fe);
305                         fallback = FALSE;
306                         break;
307                     }
308                 } else {
309 #ifdef DEBUG_FOCUS
310                     g_message("found pending FocusIn");
311 #endif
312                     /* once all the FocusOut's have been dealt with, if there
313                        is a FocusIn still left and it is valid, then use it */
314                     event_process(&fe);
315                     /* secret magic way of event_process telling us that no
316                        client was found for the FocusIn event. ^_^ */
317                     if (fe.xfocus.window != None) {
318                         fallback = FALSE;
319                         break;
320                     }
321                 }
322             }
323             if (fallback) {
324 #ifdef DEBUG_FOCUS
325                 g_message("no valid FocusIn and no FocusOut events found, "
326                           "falling back");
327 #endif
328                 focus_fallback(Fallback_NoFocus);
329             }
330         }
331         break;
332     case EnterNotify:
333     case LeaveNotify:
334         /* NotifyUngrab occurs when a mouse button is released and the event is
335            caused, like when lowering a window */
336         /* NotifyVirtual occurs when ungrabbing the pointer */
337         if (e->xcrossing.mode == NotifyGrab ||
338             e->xcrossing.detail == NotifyInferior ||
339             (e->xcrossing.mode == NotifyUngrab &&
340              e->xcrossing.detail == NotifyVirtual)) {
341 #ifndef DEBUG_FOCUS
342             g_message("%sNotify mode %d detail %d on %lx IGNORED",
343                       (e->type == EnterNotify ? "Enter" : "Leave"),
344                       e->xcrossing.mode,
345                       e->xcrossing.detail, client?client->window:0);
346 #endif
347             return TRUE;
348         }
349 #ifndef DEBUG_FOCUS
350         g_message("%sNotify mode %d detail %d on %lx",
351                   (e->type == EnterNotify ? "Enter" : "Leave"),
352                   e->xcrossing.mode,
353                   e->xcrossing.detail, client?client->window:0);
354 #endif
355         break;
356     }
357     return FALSE;
358 }
359
360 static void event_process(XEvent *e)
361 {
362     Window window;
363     Client *client;
364     Menu *menu = NULL;
365
366     window = event_get_window(e);
367     if (!(client = g_hash_table_lookup(client_map, &window)))
368         menu = g_hash_table_lookup(menu_map, &window);
369     event_set_lasttime(e);
370     event_hack_mods(e);
371     if (event_ignore(e, client))
372         return;
373
374     /* deal with it in the kernel */
375     if (menu) {
376         event_handle_menu(menu, e);
377         return;
378     } else if (client)
379         event_handle_client(client, e);
380     else if (window == ob_root)
381         event_handle_root(e);
382     else if (e->type == MapRequest)
383         client_manage(window);
384     else if (e->type == ConfigureRequest) {
385         /* unhandled configure requests must be used to configure the
386            window directly */
387         XWindowChanges xwc;
388                
389         xwc.x = e->xconfigurerequest.x;
390         xwc.y = e->xconfigurerequest.y;
391         xwc.width = e->xconfigurerequest.width;
392         xwc.height = e->xconfigurerequest.height;
393         xwc.border_width = e->xconfigurerequest.border_width;
394         xwc.sibling = e->xconfigurerequest.above;
395         xwc.stack_mode = e->xconfigurerequest.detail;
396        
397         /* we are not to be held responsible if someone sends us an
398            invalid request! */
399         xerror_set_ignore(TRUE);
400         XConfigureWindow(ob_display, window,
401                          e->xconfigurerequest.value_mask, &xwc);
402         xerror_set_ignore(FALSE);
403     }
404
405     if (moveresize_in_progress)
406         if (e->type == MotionNotify || e->type == ButtonRelease ||
407             e->type == ButtonPress ||
408             e->type == KeyPress || e->type == KeyRelease) {
409             moveresize_event(e);
410             return; /* no dispatch! */
411         }
412
413     /* user input (action-bound) events */
414     /*
415     if (e->type == ButtonPress || e->type == ButtonRelease ||
416         e->type == MotionNotify)
417         mouse_event(e, client);
418     else if (e->type == KeyPress || e->type == KeyRelease)
419         ;
420     */
421
422     /* dispatch the event to registered handlers */
423     dispatch_x(e, client);
424 }
425
426 static void event_handle_root(XEvent *e)
427 {
428     Atom msgtype;
429      
430     switch(e->type) {
431     case ClientMessage:
432         if (e->xclient.format != 32) break;
433
434         msgtype = e->xclient.message_type;
435         if (msgtype == prop_atoms.net_current_desktop) {
436             unsigned int d = e->xclient.data.l[0];
437             if (d < screen_num_desktops)
438                 screen_set_desktop(d);
439         } else if (msgtype == prop_atoms.net_number_of_desktops) {
440             unsigned int d = e->xclient.data.l[0];
441             if (d > 0)
442                 screen_set_num_desktops(d);
443         } else if (msgtype == prop_atoms.net_showing_desktop) {
444             screen_show_desktop(e->xclient.data.l[0] != 0);
445         }
446         break;
447     case PropertyNotify:
448         if (e->xproperty.atom == prop_atoms.net_desktop_names)
449             screen_update_desktop_names();
450         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
451             screen_update_layout();
452         break;
453     }
454 }
455
456 static void event_handle_client(Client *client, XEvent *e)
457 {
458     XEvent ce;
459     Atom msgtype;
460     int i=0;
461      
462     switch (e->type) {
463     case ButtonPress:
464     case ButtonRelease:
465         switch (frame_context(client, e->xbutton.window)) {
466         case Context_Maximize:
467             client->frame->max_press = (e->type == ButtonPress);
468             framerender_frame(client->frame);
469             break;
470         case Context_Close:
471             client->frame->close_press = (e->type == ButtonPress);
472             framerender_frame(client->frame);
473             break;
474         case Context_Iconify:
475             client->frame->iconify_press = (e->type == ButtonPress);
476             framerender_frame(client->frame);
477             break;
478         case Context_AllDesktops:
479             client->frame->desk_press = (e->type == ButtonPress);
480             framerender_frame(client->frame);
481             break; 
482         case Context_Shade:
483             client->frame->shade_press = (e->type == ButtonPress);
484             framerender_frame(client->frame);
485             break;
486         default:
487             /* nothing changes with clicks for any other contexts */
488             break;
489         }
490         break;
491     case FocusIn:
492         focus_set_client(client);
493     case FocusOut:
494 #ifdef DEBUG_FOCUS
495         g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
496                   client->window);
497 #endif
498         /* focus state can affect the stacking layer */
499         client_calc_layer(client);
500         frame_adjust_focus(client->frame);
501         break;
502     case EnterNotify:
503         if (client_normal(client)) {
504             if (ob_state == State_Starting) {
505                 /* move it to the top of the focus order */
506                 guint desktop = client->desktop;
507                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
508                 focus_order[desktop] = g_list_remove(focus_order[desktop],
509                                                      client);
510                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
511                                                       client);
512             } else if (config_focus_follow) {
513 #ifdef DEBUG_FOCUS
514                 g_message("EnterNotify on %lx, focusing window",
515                           client->window);
516 #endif
517                 client_focus(client);
518             }
519         }
520         break;
521     case ConfigureRequest:
522         /* compress these */
523         while (XCheckTypedWindowEvent(ob_display, client->window,
524                                       ConfigureRequest, &ce)) {
525             ++i;
526             /* XXX if this causes bad things.. we can compress config req's
527                with the same mask. */
528             e->xconfigurerequest.value_mask |=
529                 ce.xconfigurerequest.value_mask;
530             if (ce.xconfigurerequest.value_mask & CWX)
531                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
532             if (ce.xconfigurerequest.value_mask & CWY)
533                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
534             if (ce.xconfigurerequest.value_mask & CWWidth)
535                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
536             if (ce.xconfigurerequest.value_mask & CWHeight)
537                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
538             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
539                 e->xconfigurerequest.border_width =
540                     ce.xconfigurerequest.border_width;
541             if (ce.xconfigurerequest.value_mask & CWStackMode)
542                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
543         }
544
545         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
546         if (client->iconic || client->shaded) return;
547
548         if (e->xconfigurerequest.value_mask & CWBorderWidth)
549             client->border_width = e->xconfigurerequest.border_width;
550
551         /* resize, then move, as specified in the EWMH section 7.7 */
552         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
553                                                CWX | CWY)) {
554             int x, y, w, h;
555             Corner corner;
556                
557             x = (e->xconfigurerequest.value_mask & CWX) ?
558                 e->xconfigurerequest.x : client->area.x;
559             y = (e->xconfigurerequest.value_mask & CWY) ?
560                 e->xconfigurerequest.y : client->area.y;
561             w = (e->xconfigurerequest.value_mask & CWWidth) ?
562                 e->xconfigurerequest.width : client->area.width;
563             h = (e->xconfigurerequest.value_mask & CWHeight) ?
564                 e->xconfigurerequest.height : client->area.height;
565                
566             switch (client->gravity) {
567             case NorthEastGravity:
568             case EastGravity:
569                 corner = Corner_TopRight;
570                 break;
571             case SouthWestGravity:
572             case SouthGravity:
573                 corner = Corner_BottomLeft;
574                 break;
575             case SouthEastGravity:
576                 corner = Corner_BottomRight;
577                 break;
578             default:     /* NorthWest, Static, etc */
579                 corner = Corner_TopLeft;
580             }
581
582             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
583         }
584
585         if (e->xconfigurerequest.value_mask & CWStackMode) {
586             switch (e->xconfigurerequest.detail) {
587             case Below:
588             case BottomIf:
589                 stacking_lower(client);
590                 break;
591
592             case Above:
593             case TopIf:
594             default:
595                 stacking_raise(client);
596                 break;
597             }
598         }
599         break;
600     case UnmapNotify:
601         if (client->ignore_unmaps) {
602             client->ignore_unmaps--;
603             break;
604         }
605         client_unmanage(client);
606         break;
607     case DestroyNotify:
608         client_unmanage(client);
609         break;
610     case ReparentNotify:
611         /* this is when the client is first taken captive in the frame */
612         if (e->xreparent.parent == client->frame->plate) break;
613
614         /*
615           This event is quite rare and is usually handled in unmapHandler.
616           However, if the window is unmapped when the reparent event occurs,
617           the window manager never sees it because an unmap event is not sent
618           to an already unmapped window.
619         */
620
621         /* we don't want the reparent event, put it back on the stack for the
622            X server to deal with after we unmanage the window */
623         XPutBackEvent(ob_display, e);
624      
625         client_unmanage(client);
626         break;
627     case MapRequest:
628         g_message("MapRequest for 0x%lx", client->window);
629         if (!client->iconic) break; /* this normally doesn't happen, but if it
630                                        does, we don't want it! */
631         if (screen_showing_desktop)
632             screen_show_desktop(FALSE);
633         client_iconify(client, FALSE, TRUE);
634         if (!client->frame->visible)
635             /* if its not visible still, then don't mess with it */
636             break;
637         if (client->shaded)
638             client_shade(client, FALSE);
639         client_focus(client);
640         stacking_raise(client);
641         break;
642     case ClientMessage:
643         /* validate cuz we query stuff off the client here */
644         if (!client_validate(client)) break;
645   
646         if (e->xclient.format != 32) return;
647
648         msgtype = e->xclient.message_type;
649         if (msgtype == prop_atoms.wm_change_state) {
650             /* compress changes into a single change */
651             while (XCheckTypedWindowEvent(ob_display, e->type,
652                                           client->window, &ce)) {
653                 /* XXX: it would be nice to compress ALL messages of a
654                    type, not just messages in a row without other
655                    message types between. */
656                 if (ce.xclient.message_type != msgtype) {
657                     XPutBackEvent(ob_display, &ce);
658                     break;
659                 }
660                 e->xclient = ce.xclient;
661             }
662             client_set_wm_state(client, e->xclient.data.l[0]);
663         } else if (msgtype == prop_atoms.net_wm_desktop) {
664             /* compress changes into a single change */
665             while (XCheckTypedWindowEvent(ob_display, e->type,
666                                           client->window, &ce)) {
667                 /* XXX: it would be nice to compress ALL messages of a
668                    type, not just messages in a row without other
669                    message types between. */
670                 if (ce.xclient.message_type != msgtype) {
671                     XPutBackEvent(ob_display, &ce);
672                     break;
673                 }
674                 e->xclient = ce.xclient;
675             }
676             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
677                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
678                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
679                                    FALSE);
680         } else if (msgtype == prop_atoms.net_wm_state) {
681             /* can't compress these */
682             g_message("net_wm_state %s %ld %ld for 0x%lx",
683                       (e->xclient.data.l[0] == 0 ? "Remove" :
684                        e->xclient.data.l[0] == 1 ? "Add" :
685                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
686                       e->xclient.data.l[1], e->xclient.data.l[2],
687                       client->window);
688             client_set_state(client, e->xclient.data.l[0],
689                              e->xclient.data.l[1], e->xclient.data.l[2]);
690         } else if (msgtype == prop_atoms.net_close_window) {
691             g_message("net_close_window for 0x%lx", client->window);
692             client_close(client);
693         } else if (msgtype == prop_atoms.net_active_window) {
694             g_message("net_active_window for 0x%lx", client->window);
695             if (screen_showing_desktop)
696                 screen_show_desktop(FALSE);
697             if (client->iconic)
698                 client_iconify(client, FALSE, TRUE);
699             else if (!client->frame->visible)
700                 /* if its not visible for other reasons, then don't mess
701                    with it */
702                 break;
703             if (client->shaded)
704                 client_shade(client, FALSE);
705             client_focus(client);
706             stacking_raise(client);
707         } else if (msgtype == prop_atoms.net_wm_moveresize) {
708             g_message("net_wm_moveresize for 0x%lx", client->window);
709             if ((Atom)e->xclient.data.l[2] ==
710                 prop_atoms.net_wm_moveresize_size_topleft ||
711                 (Atom)e->xclient.data.l[2] ==
712                 prop_atoms.net_wm_moveresize_size_top ||
713                 (Atom)e->xclient.data.l[2] ==
714                 prop_atoms.net_wm_moveresize_size_topright ||
715                 (Atom)e->xclient.data.l[2] ==
716                 prop_atoms.net_wm_moveresize_size_right ||
717                 (Atom)e->xclient.data.l[2] ==
718                 prop_atoms.net_wm_moveresize_size_right ||
719                 (Atom)e->xclient.data.l[2] ==
720                 prop_atoms.net_wm_moveresize_size_bottomright ||
721                 (Atom)e->xclient.data.l[2] ==
722                 prop_atoms.net_wm_moveresize_size_bottom ||
723                 (Atom)e->xclient.data.l[2] ==
724                 prop_atoms.net_wm_moveresize_size_bottomleft ||
725                 (Atom)e->xclient.data.l[2] ==
726                 prop_atoms.net_wm_moveresize_size_left ||
727                 (Atom)e->xclient.data.l[2] ==
728                 prop_atoms.net_wm_moveresize_move ||
729                 (Atom)e->xclient.data.l[2] ==
730                 prop_atoms.net_wm_moveresize_size_keyboard ||
731                 (Atom)e->xclient.data.l[2] ==
732                 prop_atoms.net_wm_moveresize_move_keyboard) {
733
734                 moveresize_start(client, e->xclient.data.l[0],
735                                  e->xclient.data.l[1], e->xclient.data.l[3],
736                                  e->xclient.data.l[2]);
737             }
738         } else if (msgtype == prop_atoms.net_moveresize_window) {
739             int oldg = client->gravity;
740             int tmpg, x, y, w, h;
741
742             if (e->xclient.data.l[0] & 0xff)
743                 tmpg = e->xclient.data.l[0] & 0xff;
744             else
745                 tmpg = oldg;
746
747             if (e->xclient.data.l[0] & 1 << 8)
748                 x = e->xclient.data.l[1];
749             else
750                 x = client->area.x;
751             if (e->xclient.data.l[0] & 1 << 9)
752                 y = e->xclient.data.l[2];
753             else
754                 y = client->area.y;
755             if (e->xclient.data.l[0] & 1 << 10)
756                 w = e->xclient.data.l[3];
757             else
758                 w = client->area.y;
759             if (e->xclient.data.l[0] & 1 << 11)
760                 h = e->xclient.data.l[4];
761             else
762                 h = client->area.y;
763             client->gravity = tmpg;
764             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
765             client->gravity = oldg;
766         }
767         break;
768     case PropertyNotify:
769         /* validate cuz we query stuff off the client here */
770         if (!client_validate(client)) break;
771   
772         /* compress changes to a single property into a single change */
773         while (XCheckTypedWindowEvent(ob_display, e->type,
774                                       client->window, &ce)) {
775             /* XXX: it would be nice to compress ALL changes to a property,
776                not just changes in a row without other props between. */
777             if (ce.xproperty.atom != e->xproperty.atom) {
778                 XPutBackEvent(ob_display, &ce);
779                 break;
780             }
781         }
782
783         msgtype = e->xproperty.atom;
784         if (msgtype == XA_WM_NORMAL_HINTS) {
785             client_update_normal_hints(client);
786             /* normal hints can make a window non-resizable */
787             client_setup_decor_and_functions(client);
788         }
789         else if (msgtype == XA_WM_HINTS)
790             client_update_wmhints(client);
791         else if (msgtype == XA_WM_TRANSIENT_FOR) {
792             client_update_transient_for(client);
793             client_get_type(client);
794             /* type may have changed, so update the layer */
795             client_calc_layer(client);
796             client_setup_decor_and_functions(client);
797         }
798         else if (msgtype == prop_atoms.net_wm_name ||
799                  msgtype == prop_atoms.wm_name)
800             client_update_title(client);
801         else if (msgtype == prop_atoms.net_wm_icon_name ||
802                  msgtype == prop_atoms.wm_icon_name)
803             client_update_icon_title(client);
804         else if (msgtype == prop_atoms.wm_class)
805             client_update_class(client);
806         else if (msgtype == prop_atoms.wm_protocols) {
807             client_update_protocols(client);
808             client_setup_decor_and_functions(client);
809         }
810         else if (msgtype == prop_atoms.net_wm_strut)
811             client_update_strut(client);
812         else if (msgtype == prop_atoms.net_wm_icon)
813             client_update_icons(client);
814         else if (msgtype == prop_atoms.kwm_win_icon)
815             client_update_kwm_icon(client);
816     default:
817         ;
818 #ifdef SHAPE
819         if (extensions_shape && e->type == extensions_shape_event_basep) {
820             client->shaped = ((XShapeEvent*)e)->shaped;
821             frame_adjust_shape(client->frame);
822         }
823 #endif
824     }
825 }
826
827 static void event_handle_menu(Menu *menu, XEvent *e)
828 {
829     MenuEntry *entry;
830
831     g_message("EVENT %d", e->type);
832     switch (e->type) {
833     case ButtonPress:
834         if (e->xbutton.button == 3)
835             menu_hide(menu);
836         break;
837     case ButtonRelease:
838         if (!menu->shown) break;
839
840 /*        grab_pointer_window(FALSE, None, menu->frame);*/
841
842         entry = menu_find_entry(menu, e->xbutton.window);
843         if (entry) {
844             int junk;
845             Window wjunk;
846             guint ujunk, b, w, h;
847             XGetGeometry(ob_display, e->xbutton.window,
848                          &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
849             if (e->xbutton.x >= (signed)-b &&
850                 e->xbutton.y >= (signed)-b &&
851                 e->xbutton.x < (signed)(w+b) &&
852                 e->xbutton.y < (signed)(h+b)) {
853                 menu_entry_fire(entry);
854             }
855         }
856         break;
857     case EnterNotify:
858     case LeaveNotify:
859         g_message("enter/leave");
860         entry = menu_find_entry(menu, e->xcrossing.window);
861         if (entry) {
862             entry->hilite = e->type == EnterNotify;
863             menu_entry_render(entry);
864         }
865         break;
866     }
867 }