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