]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
use the current size not the start size for moving
[mikachu/openbox.git] / openbox / event.c
1 #include "debug.h"
2 #include "openbox.h"
3 #include "dock.h"
4 #include "client.h"
5 #include "xerror.h"
6 #include "prop.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "frame.h"
10 #include "menu.h"
11 #include "menuframe.h"
12 #include "keyboard.h"
13 #include "mouse.h"
14 #include "mainloop.h"
15 #include "framerender.h"
16 #include "focus.h"
17 #include "moveresize.h"
18 #include "stacking.h"
19 #include "extensions.h"
20 #include "event.h"
21
22 #include <X11/Xlib.h>
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
25 #include <glib.h>
26
27 #ifdef HAVE_SYS_SELECT_H
28 #  include <sys/select.h>
29 #endif
30 #ifdef HAVE_SIGNAL_H
31 #  include <signal.h>
32 #endif
33
34 #ifdef USE_SM
35 #include <X11/ICE/ICElib.h>
36 #endif
37
38 static void event_process(const XEvent *e, gpointer data);
39 static void event_handle_root(XEvent *e);
40 static void event_handle_menu(XEvent *e);
41 static void event_handle_dock(ObDock *s, XEvent *e);
42 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
43 static void event_handle_client(ObClient *c, XEvent *e);
44
45 static gboolean focus_delay_func(gpointer data);
46 static void focus_delay_client_dest(gpointer data);
47
48 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
49                             (e)->xfocus.detail == NotifyAncestor || \
50                             (e)->xfocus.detail > NotifyNonlinearVirtual)
51 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
52                              (e)->xfocus.detail == NotifyInferior || \
53                              (e)->xfocus.detail == NotifyAncestor || \
54                              (e)->xfocus.detail > NotifyNonlinearVirtual)
55
56 Time event_lasttime = 0;
57
58 /*! The value of the mask for the NumLock modifier */
59 unsigned int NumLockMask;
60 /*! The value of the mask for the ScrollLock modifier */
61 unsigned int ScrollLockMask;
62 /*! The key codes for the modifier keys */
63 static XModifierKeymap *modmap;
64 /*! Table of the constant modifier masks */
65 static const int mask_table[] = {
66     ShiftMask, LockMask, ControlMask, Mod1Mask,
67     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
68 };
69 static int mask_table_size;
70
71 static ObClient *focus_delay_client;
72
73 #ifdef USE_SM
74 static void ice_handler(int fd, gpointer conn)
75 {
76     Bool b;
77     IceProcessMessages(conn, NULL, &b);
78 }
79
80 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
81                       IcePointer *watch_data)
82 {
83     static gint fd = -1;
84
85     if (opening) {
86         fd = IceConnectionNumber(conn);
87         ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
88     } else {
89         ob_main_loop_fd_remove(ob_main_loop, fd);
90         fd = -1;
91     }
92 }
93 #endif
94
95 void event_startup(gboolean reconfig)
96 {
97     if (reconfig) return;
98
99     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
100      
101     /* get lock masks that are defined by the display (not constant) */
102     modmap = XGetModifierMapping(ob_display);
103     g_assert(modmap);
104     if (modmap && modmap->max_keypermod > 0) {
105         size_t cnt;
106         const size_t size = mask_table_size * modmap->max_keypermod;
107         /* get the values of the keyboard lock modifiers
108            Note: Caps lock is not retrieved the same way as Scroll and Num
109            lock since it doesn't need to be. */
110         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
111         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
112                                                      XK_Scroll_Lock);
113           
114         for (cnt = 0; cnt < size; ++cnt) {
115             if (! modmap->modifiermap[cnt]) continue;
116                
117             if (num_lock == modmap->modifiermap[cnt])
118                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
119             if (scroll_lock == modmap->modifiermap[cnt])
120                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
121         }
122     }
123
124     ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
125
126 #ifdef USE_SM
127     IceAddConnectionWatch(ice_watch, NULL);
128 #endif
129
130     client_add_destructor(focus_delay_client_dest);
131 }
132
133 void event_shutdown(gboolean reconfig)
134 {
135     if (reconfig) return;
136
137     client_remove_destructor(focus_delay_client_dest);
138     XFreeModifiermap(modmap);
139 }
140
141 static Window event_get_window(XEvent *e)
142 {
143     Window window;
144
145     /* pick a window */
146     switch (e->type) {
147     case SelectionClear:
148         window = RootWindow(ob_display, ob_screen);
149         break;
150     case MapRequest:
151         window = e->xmap.window;
152         break;
153     case UnmapNotify:
154         window = e->xunmap.window;
155         break;
156     case DestroyNotify:
157         window = e->xdestroywindow.window;
158         break;
159     case ConfigureRequest:
160         window = e->xconfigurerequest.window;
161         break;
162     case ConfigureNotify:
163         window = e->xconfigure.window;
164         break;
165     default:
166 #ifdef XKB
167         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
168             switch (((XkbAnyEvent*)e)->xkb_type) {
169             case XkbBellNotify:
170                 window = ((XkbBellNotifyEvent*)e)->window;
171             default:
172                 window = None;
173             }
174         } else
175 #endif
176             window = e->xany.window;
177     }
178     return window;
179 }
180
181 static void event_set_lasttime(XEvent *e)
182 {
183     Time t = 0;
184
185     /* grab the lasttime and hack up the state */
186     switch (e->type) {
187     case ButtonPress:
188     case ButtonRelease:
189         t = e->xbutton.time;
190         break;
191     case KeyPress:
192         t = e->xkey.time;
193         break;
194     case KeyRelease:
195         t = e->xkey.time;
196         break;
197     case MotionNotify:
198         t = e->xmotion.time;
199         break;
200     case PropertyNotify:
201         t = e->xproperty.time;
202         break;
203     case EnterNotify:
204     case LeaveNotify:
205         t = e->xcrossing.time;
206         break;
207     default:
208         /* if more event types are anticipated, get their timestamp
209            explicitly */
210         break;
211     }
212
213     if (t > event_lasttime)
214         event_lasttime = t;
215 }
216
217 #define STRIP_MODS(s) \
218         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
219         /* kill off the Button1Mask etc, only want the modifiers */ \
220         s &= (ControlMask | ShiftMask | Mod1Mask | \
221               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
222
223 static void event_hack_mods(XEvent *e)
224 {
225     KeyCode *kp;
226     int i, k;
227
228     switch (e->type) {
229     case ButtonPress:
230     case ButtonRelease:
231         STRIP_MODS(e->xbutton.state);
232         break;
233     case KeyPress:
234         STRIP_MODS(e->xkey.state);
235         break;
236     case KeyRelease:
237         STRIP_MODS(e->xkey.state);
238         /* remove from the state the mask of the modifier being released, if
239            it is a modifier key being released (this is a little ugly..) */
240         kp = modmap->modifiermap;
241         for (i = 0; i < mask_table_size; ++i) {
242             for (k = 0; k < modmap->max_keypermod; ++k) {
243                 if (*kp == e->xkey.keycode) { /* found the keycode */
244                     /* remove the mask for it */
245                     e->xkey.state &= ~mask_table[i];
246                     /* cause the first loop to break; */
247                     i = mask_table_size;
248                     break; /* get outta here! */
249                 }
250                 ++kp;
251             }
252         }
253         break;
254     case MotionNotify:
255         STRIP_MODS(e->xmotion.state);
256         /* compress events */
257         {
258             XEvent ce;
259             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
260                                           e->type, &ce)) {
261                 e->xmotion.x_root = ce.xmotion.x_root;
262                 e->xmotion.y_root = ce.xmotion.y_root;
263             }
264         }
265         break;
266     }
267 }
268
269 static gboolean event_ignore(XEvent *e, ObClient *client)
270 {
271     switch(e->type) {
272     case FocusIn:
273         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
274            because of RevertToPointerRoot. If the focus ends up reverting to
275            pointer root on a workspace change, then the FocusIn event that we
276            want will be of type NotifyAncestor. This situation does not occur
277            for FocusOut, so it is safely ignored there.
278         */
279         if (INVALID_FOCUSIN(e) ||
280             client == NULL) {
281 #ifdef DEBUG_FOCUS
282         ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
283                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
284 #endif
285             /* says a client was not found for the event (or a valid FocusIn
286                event was not found.
287             */
288             e->xfocus.window = None;
289             return TRUE;
290         }
291
292 #ifdef DEBUG_FOCUS
293         ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
294                  e->xfocus.mode, e->xfocus.detail);
295 #endif
296         break;
297     case FocusOut:
298         if (INVALID_FOCUSOUT(e)) {
299 #ifdef DEBUG_FOCUS
300         ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
301                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
302 #endif
303             return TRUE;
304         }
305
306 #ifdef DEBUG_FOCUS
307         ob_debug("FocusOut on %lx mode %d detail %d\n",
308                  e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
309 #endif
310
311         {
312             XEvent fe;
313             gboolean fallback = TRUE;
314
315             while (TRUE) {
316                 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
317                                             FocusOut, &fe))
318                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
319                         break;
320                 if (fe.type == FocusOut) {
321 #ifdef DEBUG_FOCUS
322                     ob_debug("found pending FocusOut\n");
323 #endif
324                     if (!INVALID_FOCUSOUT(&fe)) {
325                         /* if there is a VALID FocusOut still coming, don't
326                            fallback focus yet, we'll deal with it then */
327                         XPutBackEvent(ob_display, &fe);
328                         fallback = FALSE;
329                         break;
330                     }
331                 } else {
332 #ifdef DEBUG_FOCUS
333                     ob_debug("found pending FocusIn\n");
334 #endif
335                     /* is the focused window getting a FocusOut/In back to
336                        itself?
337                     */
338                     if (fe.xfocus.window == e->xfocus.window &&
339                         !event_ignore(&fe, client)) {
340                         /*
341                           if focus_client is not set, then we can't do
342                           this. we need the FocusIn. This happens in the
343                           case when the set_focus_client(NULL) in the
344                           focus_fallback function fires and then
345                           focus_fallback picks the currently focused
346                           window (such as on a SendToDesktop-esque action.
347                         */
348                         if (focus_client) {
349 #ifdef DEBUG_FOCUS
350                             ob_debug("focused window got an Out/In back to "
351                                      "itself IGNORED both\n");
352 #endif
353                             return TRUE;
354                         } else {
355                             event_process(&fe, NULL);
356 #ifdef DEBUG_FOCUS
357                             ob_debug("focused window got an Out/In back to "
358                                      "itself but focus_client was null "
359                                      "IGNORED just the Out\n");
360 #endif
361                             return TRUE;
362                         }
363                     }
364
365                     /* once all the FocusOut's have been dealt with, if there
366                        is a FocusIn still left and it is valid, then use it */
367                     event_process(&fe, NULL);
368                     /* secret magic way of event_process telling us that no
369                        client was found for the FocusIn event. ^_^ */
370                     if (fe.xfocus.window != None) {
371                         fallback = FALSE;
372                         break;
373                     }
374                 }
375             }
376             if (fallback) {
377 #ifdef DEBUG_FOCUS
378                 ob_debug("no valid FocusIn and no FocusOut events found, "
379                          "falling back\n");
380 #endif
381                 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
382             }
383         }
384         break;
385     case EnterNotify:
386     case LeaveNotify:
387         /* NotifyUngrab occurs when a mouse button is released and the event is
388            caused, like when lowering a window */
389         /* NotifyVirtual occurs when ungrabbing the pointer */
390         if (e->xcrossing.mode == NotifyGrab ||
391             e->xcrossing.detail == NotifyInferior ||
392             (e->xcrossing.mode == NotifyUngrab &&
393              e->xcrossing.detail == NotifyVirtual)) {
394 #ifdef DEBUG_FOCUS
395             ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
396                      (e->type == EnterNotify ? "Enter" : "Leave"),
397                      e->xcrossing.mode,
398                      e->xcrossing.detail, client?client->window:0);
399 #endif
400             return TRUE;
401         }
402 #ifdef DEBUG_FOCUS
403         ob_debug("%sNotify mode %d detail %d on %lx\n",
404                  (e->type == EnterNotify ? "Enter" : "Leave"),
405                  e->xcrossing.mode,
406                  e->xcrossing.detail, client?client->window:0);
407 #endif
408         break;
409     }
410     return FALSE;
411 }
412
413 static void event_process(const XEvent *ec, gpointer data)
414 {
415     Window window;
416     ObClient *client = NULL;
417     ObDock *dock = NULL;
418     ObDockApp *dockapp = NULL;
419     ObWindow *obwin = NULL;
420     XEvent ee, *e;
421
422     /* make a copy we can mangle */
423     ee = *ec;
424     e = &ee;
425
426     window = event_get_window(e);
427     if ((obwin = g_hash_table_lookup(window_map, &window))) {
428         switch (obwin->type) {
429         case Window_Dock:
430             dock = WINDOW_AS_DOCK(obwin);
431             break;
432         case Window_DockApp:
433             dockapp = WINDOW_AS_DOCKAPP(obwin);
434             break;
435         case Window_Client:
436             client = WINDOW_AS_CLIENT(obwin);
437             break;
438         case Window_Menu:
439         case Window_Internal:
440             /* not to be used for events */
441             g_assert_not_reached();
442             break;
443         }
444     }
445
446     event_set_lasttime(e);
447     event_hack_mods(e);
448     if (event_ignore(e, client))
449         return;
450
451     /* deal with it in the kernel */
452     if (client)
453         event_handle_client(client, e);
454     else if (dockapp)
455         event_handle_dockapp(dockapp, e);
456     else if (dock)
457         event_handle_dock(dock, e);
458     else if (window == RootWindow(ob_display, ob_screen))
459         event_handle_root(e);
460     else if (e->type == MapRequest)
461         client_manage(window);
462     else if (e->type == ConfigureRequest) {
463         /* unhandled configure requests must be used to configure the
464            window directly */
465         XWindowChanges xwc;
466                
467         xwc.x = e->xconfigurerequest.x;
468         xwc.y = e->xconfigurerequest.y;
469         xwc.width = e->xconfigurerequest.width;
470         xwc.height = e->xconfigurerequest.height;
471         xwc.border_width = e->xconfigurerequest.border_width;
472         xwc.sibling = e->xconfigurerequest.above;
473         xwc.stack_mode = e->xconfigurerequest.detail;
474        
475         /* we are not to be held responsible if someone sends us an
476            invalid request! */
477         xerror_set_ignore(TRUE);
478         XConfigureWindow(ob_display, window,
479                          e->xconfigurerequest.value_mask, &xwc);
480         xerror_set_ignore(FALSE);
481     }
482
483     /* user input (action-bound) events */
484     if (e->type == ButtonPress || e->type == ButtonRelease ||
485         e->type == MotionNotify || e->type == KeyPress ||
486         e->type == KeyRelease)
487     {
488         if (menu_frame_visible)
489             event_handle_menu(e);
490         else {
491             if (!keyboard_process_interactive_grab(e, &client)) {
492                 if (moveresize_in_progress)
493                     moveresize_event(e);
494
495                 if (e->type == ButtonPress || e->type == ButtonRelease ||
496                     e->type == MotionNotify)
497                     mouse_event(client, e);
498                 else if (e->type == KeyPress)
499                     /* when in the middle of a focus cycling action, this
500                        causes the window which appears to be focused to be
501                        the one on which the actions will be executed */
502                     keyboard_event((focus_cycle_target ?
503                                     focus_cycle_target :
504                                     (client ? client : focus_client)), e);
505             }
506         }
507     }
508 }
509
510 static void event_handle_root(XEvent *e)
511 {
512     Atom msgtype;
513      
514     switch(e->type) {
515     case SelectionClear:
516         ob_debug("Another WM has requested to replace us. Exiting.\n");
517         ob_exit();
518         break;
519
520     case ClientMessage:
521         if (e->xclient.format != 32) break;
522
523         msgtype = e->xclient.message_type;
524         if (msgtype == prop_atoms.net_current_desktop) {
525             unsigned int d = e->xclient.data.l[0];
526             if (d < screen_num_desktops)
527                 screen_set_desktop(d);
528         } else if (msgtype == prop_atoms.net_number_of_desktops) {
529             unsigned int d = e->xclient.data.l[0];
530             if (d > 0)
531                 screen_set_num_desktops(d);
532         } else if (msgtype == prop_atoms.net_showing_desktop) {
533             screen_show_desktop(e->xclient.data.l[0] != 0);
534         }
535         break;
536     case PropertyNotify:
537         if (e->xproperty.atom == prop_atoms.net_desktop_names)
538             screen_update_desktop_names();
539         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
540             screen_update_layout();
541         break;
542     case ConfigureNotify:
543 #ifdef XRANDR
544         XRRUpdateConfiguration(e);
545 #endif
546         screen_resize();
547         break;
548     default:
549         ;
550 #ifdef VIDMODE
551         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
552             ob_debug("VIDMODE EVENT\n");
553         }
554 #endif
555     }
556 }
557
558 static void event_handle_client(ObClient *client, XEvent *e)
559 {
560     XEvent ce;
561     Atom msgtype;
562     int i=0;
563     ObFrameContext con;
564      
565     switch (e->type) {
566     case VisibilityNotify:
567         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
568         break;
569     case ButtonPress:
570     case ButtonRelease:
571         /* Wheel buttons don't draw because they are an instant click, so it
572            is a waste of resources to go drawing it. */
573         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
574             switch (frame_context(client, e->xbutton.window)) {
575             case OB_FRAME_CONTEXT_MAXIMIZE:
576                 client->frame->max_press = (e->type == ButtonPress);
577                 framerender_frame(client->frame);
578                 break;
579             case OB_FRAME_CONTEXT_CLOSE:
580                 client->frame->close_press = (e->type == ButtonPress);
581                 framerender_frame(client->frame);
582                 break;
583             case OB_FRAME_CONTEXT_ICONIFY:
584                 client->frame->iconify_press = (e->type == ButtonPress);
585                 framerender_frame(client->frame);
586                 break;
587             case OB_FRAME_CONTEXT_ALLDESKTOPS:
588                 client->frame->desk_press = (e->type == ButtonPress);
589                 framerender_frame(client->frame);
590                 break; 
591             case OB_FRAME_CONTEXT_SHADE:
592                 client->frame->shade_press = (e->type == ButtonPress);
593                 framerender_frame(client->frame);
594                 break;
595             default:
596                 /* nothing changes with clicks for any other contexts */
597                 break;
598             }
599         }
600         break;
601     case FocusIn:
602 #ifdef DEBUG_FOCUS
603         ob_debug("FocusIn on client for %lx\n", client->window);
604 #endif
605         if (client != focus_client) {
606             focus_set_client(client);
607             frame_adjust_focus(client->frame, TRUE);
608         }
609         break;
610     case FocusOut:
611 #ifdef DEBUG_FOCUS
612         ob_debug("FocusOut on client for %lx\n", client->window);
613 #endif
614         /* are we a fullscreen window or a transient of one? (checks layer)
615            if we are then we need to be iconified since we are losing focus
616          */
617         if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
618             !client_search_focus_tree_full(client))
619             /* iconify fullscreen windows when they and their transients
620                aren't focused */
621             client_iconify(client, TRUE, TRUE);
622         frame_adjust_focus(client->frame, FALSE);
623         break;
624     case LeaveNotify:
625         con = frame_context(client, e->xcrossing.window);
626         switch (con) {
627         case OB_FRAME_CONTEXT_MAXIMIZE:
628             client->frame->max_hover = FALSE;
629             frame_adjust_state(client->frame);
630             break;
631         case OB_FRAME_CONTEXT_ALLDESKTOPS:
632             client->frame->desk_hover = FALSE;
633             frame_adjust_state(client->frame);
634             break;
635         case OB_FRAME_CONTEXT_SHADE:
636             client->frame->shade_hover = FALSE;
637             frame_adjust_state(client->frame);
638             break;
639         case OB_FRAME_CONTEXT_ICONIFY:
640             client->frame->iconify_hover = FALSE;
641             frame_adjust_state(client->frame);
642             break;
643         case OB_FRAME_CONTEXT_CLOSE:
644             client->frame->close_hover = FALSE;
645             frame_adjust_state(client->frame);
646             break;
647         case OB_FRAME_CONTEXT_FRAME:
648             /* XXX if doing a 'reconfigure' make sure you kill this timer,
649                maybe all timers.. */
650             if (config_focus_delay && client == focus_delay_client) {
651                 ob_main_loop_timeout_remove_data(ob_main_loop,
652                                                  focus_delay_func,
653                                                  focus_delay_client);
654                 focus_delay_client = NULL;
655             }
656         default:
657             break;
658         }
659         break;
660     case EnterNotify:
661         con = frame_context(client, e->xcrossing.window);
662         switch (con) {
663         case OB_FRAME_CONTEXT_MAXIMIZE:
664             client->frame->max_hover = TRUE;
665             frame_adjust_state(client->frame);
666             break;
667         case OB_FRAME_CONTEXT_ALLDESKTOPS:
668             client->frame->desk_hover = TRUE;
669             frame_adjust_state(client->frame);
670             break;
671         case OB_FRAME_CONTEXT_SHADE:
672             client->frame->shade_hover = TRUE;
673             frame_adjust_state(client->frame);
674             break;
675         case OB_FRAME_CONTEXT_ICONIFY:
676             client->frame->iconify_hover = TRUE;
677             frame_adjust_state(client->frame);
678             break;
679         case OB_FRAME_CONTEXT_CLOSE:
680             client->frame->close_hover = TRUE;
681             frame_adjust_state(client->frame);
682             break;
683         case OB_FRAME_CONTEXT_FRAME:
684             if (client_normal(client)) {
685                 if (ob_state() == OB_STATE_STARTING) {
686                     /* move it to the top of the focus order */
687                     guint desktop = client->desktop;
688                     if (desktop == DESKTOP_ALL) desktop = screen_desktop;
689                     focus_order[desktop] = g_list_remove(focus_order[desktop],
690                                                          client);
691                     focus_order[desktop] = g_list_prepend(focus_order[desktop],
692                                                           client);
693                 } else if (config_focus_follow) {
694 #ifdef DEBUG_FOCUS
695                     ob_debug("EnterNotify on %lx, focusing window\n",
696                              client->window);
697 #endif
698                     if (config_focus_delay) {
699                         ob_main_loop_timeout_add(ob_main_loop,
700                                                  config_focus_delay,
701                                                  focus_delay_func,
702                                                  client, NULL);
703                         focus_delay_client = client;
704                     } else
705                         client_focus(client);
706                 }
707             }
708             break;
709         default:
710             break;
711         }
712         break;
713     case ConfigureRequest:
714         /* compress these */
715         while (XCheckTypedWindowEvent(ob_display, client->window,
716                                       ConfigureRequest, &ce)) {
717             ++i;
718             /* XXX if this causes bad things.. we can compress config req's
719                with the same mask. */
720             e->xconfigurerequest.value_mask |=
721                 ce.xconfigurerequest.value_mask;
722             if (ce.xconfigurerequest.value_mask & CWX)
723                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
724             if (ce.xconfigurerequest.value_mask & CWY)
725                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
726             if (ce.xconfigurerequest.value_mask & CWWidth)
727                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
728             if (ce.xconfigurerequest.value_mask & CWHeight)
729                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
730             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
731                 e->xconfigurerequest.border_width =
732                     ce.xconfigurerequest.border_width;
733             if (ce.xconfigurerequest.value_mask & CWStackMode)
734                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
735         }
736
737         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
738         if (client->iconic || client->shaded) return;
739
740         /* resize, then move, as specified in the EWMH section 7.7 */
741         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
742                                                CWX | CWY |
743                                                CWBorderWidth)) {
744             int x, y, w, h;
745             ObCorner corner;
746
747             if (e->xconfigurerequest.value_mask & CWBorderWidth)
748                 client->border_width = e->xconfigurerequest.border_width;
749
750             x = (e->xconfigurerequest.value_mask & CWX) ?
751                 e->xconfigurerequest.x : client->area.x;
752             y = (e->xconfigurerequest.value_mask & CWY) ?
753                 e->xconfigurerequest.y : client->area.y;
754             w = (e->xconfigurerequest.value_mask & CWWidth) ?
755                 e->xconfigurerequest.width : client->area.width;
756             h = (e->xconfigurerequest.value_mask & CWHeight) ?
757                 e->xconfigurerequest.height : client->area.height;
758
759             {
760                 int newx = x;
761                 int newy = y;
762                 int fw = w +
763                     client->frame->size.left + client->frame->size.right;
764                 int fh = h +
765                     client->frame->size.top + client->frame->size.bottom;
766                 client_find_onscreen(client, &newx, &newy, fw, fh,
767                                      client_normal(client));
768                 if (e->xconfigurerequest.value_mask & CWX)
769                     x = newx;
770                 if (e->xconfigurerequest.value_mask & CWY)
771                     y = newy;
772             }
773                
774             switch (client->gravity) {
775             case NorthEastGravity:
776             case EastGravity:
777                 corner = OB_CORNER_TOPRIGHT;
778                 break;
779             case SouthWestGravity:
780             case SouthGravity:
781                 corner = OB_CORNER_BOTTOMLEFT;
782                 break;
783             case SouthEastGravity:
784                 corner = OB_CORNER_BOTTOMRIGHT;
785                 break;
786             default:     /* NorthWest, Static, etc */
787                 corner = OB_CORNER_TOPLEFT;
788             }
789
790             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
791                                   TRUE);
792         }
793
794         if (e->xconfigurerequest.value_mask & CWStackMode) {
795             switch (e->xconfigurerequest.detail) {
796             case Below:
797             case BottomIf:
798                 stacking_lower(CLIENT_AS_WINDOW(client));
799                 break;
800
801             case Above:
802             case TopIf:
803             default:
804                 stacking_raise(CLIENT_AS_WINDOW(client));
805                 break;
806             }
807         }
808         break;
809     case UnmapNotify:
810         if (client->ignore_unmaps) {
811             client->ignore_unmaps--;
812             break;
813         }
814         client_unmanage(client);
815         break;
816     case DestroyNotify:
817         client_unmanage(client);
818         break;
819     case ReparentNotify:
820         /* this is when the client is first taken captive in the frame */
821         if (e->xreparent.parent == client->frame->plate) break;
822
823         /*
824           This event is quite rare and is usually handled in unmapHandler.
825           However, if the window is unmapped when the reparent event occurs,
826           the window manager never sees it because an unmap event is not sent
827           to an already unmapped window.
828         */
829
830         /* we don't want the reparent event, put it back on the stack for the
831            X server to deal with after we unmanage the window */
832         XPutBackEvent(ob_display, e);
833      
834         client_unmanage(client);
835         break;
836     case MapRequest:
837         ob_debug("MapRequest for 0x%lx\n", client->window);
838         if (!client->iconic) break; /* this normally doesn't happen, but if it
839                                        does, we don't want it! */
840         if (screen_showing_desktop)
841             screen_show_desktop(FALSE);
842         client_iconify(client, FALSE, TRUE);
843         if (!client->frame->visible)
844             /* if its not visible still, then don't mess with it */
845             break;
846         if (client->shaded)
847             client_shade(client, FALSE);
848         client_focus(client);
849         stacking_raise(CLIENT_AS_WINDOW(client));
850         break;
851     case ClientMessage:
852         /* validate cuz we query stuff off the client here */
853         if (!client_validate(client)) break;
854   
855         if (e->xclient.format != 32) return;
856
857         msgtype = e->xclient.message_type;
858         if (msgtype == prop_atoms.wm_change_state) {
859             /* compress changes into a single change */
860             while (XCheckTypedWindowEvent(ob_display, client->window,
861                                           e->type, &ce)) {
862                 /* XXX: it would be nice to compress ALL messages of a
863                    type, not just messages in a row without other
864                    message types between. */
865                 if (ce.xclient.message_type != msgtype) {
866                     XPutBackEvent(ob_display, &ce);
867                     break;
868                 }
869                 e->xclient = ce.xclient;
870             }
871             client_set_wm_state(client, e->xclient.data.l[0]);
872         } else if (msgtype == prop_atoms.net_wm_desktop) {
873             /* compress changes into a single change */
874             while (XCheckTypedWindowEvent(ob_display, client->window,
875                                           e->type, &ce)) {
876                 /* XXX: it would be nice to compress ALL messages of a
877                    type, not just messages in a row without other
878                    message types between. */
879                 if (ce.xclient.message_type != msgtype) {
880                     XPutBackEvent(ob_display, &ce);
881                     break;
882                 }
883                 e->xclient = ce.xclient;
884             }
885             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
886                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
887                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
888                                    FALSE);
889         } else if (msgtype == prop_atoms.net_wm_state) {
890             /* can't compress these */
891             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
892                      (e->xclient.data.l[0] == 0 ? "Remove" :
893                       e->xclient.data.l[0] == 1 ? "Add" :
894                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
895                      e->xclient.data.l[1], e->xclient.data.l[2],
896                      client->window);
897             client_set_state(client, e->xclient.data.l[0],
898                              e->xclient.data.l[1], e->xclient.data.l[2]);
899         } else if (msgtype == prop_atoms.net_close_window) {
900             ob_debug("net_close_window for 0x%lx\n", client->window);
901             client_close(client);
902         } else if (msgtype == prop_atoms.net_active_window) {
903             ob_debug("net_active_window for 0x%lx\n", client->window);
904             client_activate(client, FALSE);
905         } else if (msgtype == prop_atoms.net_wm_moveresize) {
906             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
907             if ((Atom)e->xclient.data.l[2] ==
908                 prop_atoms.net_wm_moveresize_size_topleft ||
909                 (Atom)e->xclient.data.l[2] ==
910                 prop_atoms.net_wm_moveresize_size_top ||
911                 (Atom)e->xclient.data.l[2] ==
912                 prop_atoms.net_wm_moveresize_size_topright ||
913                 (Atom)e->xclient.data.l[2] ==
914                 prop_atoms.net_wm_moveresize_size_right ||
915                 (Atom)e->xclient.data.l[2] ==
916                 prop_atoms.net_wm_moveresize_size_right ||
917                 (Atom)e->xclient.data.l[2] ==
918                 prop_atoms.net_wm_moveresize_size_bottomright ||
919                 (Atom)e->xclient.data.l[2] ==
920                 prop_atoms.net_wm_moveresize_size_bottom ||
921                 (Atom)e->xclient.data.l[2] ==
922                 prop_atoms.net_wm_moveresize_size_bottomleft ||
923                 (Atom)e->xclient.data.l[2] ==
924                 prop_atoms.net_wm_moveresize_size_left ||
925                 (Atom)e->xclient.data.l[2] ==
926                 prop_atoms.net_wm_moveresize_move ||
927                 (Atom)e->xclient.data.l[2] ==
928                 prop_atoms.net_wm_moveresize_size_keyboard ||
929                 (Atom)e->xclient.data.l[2] ==
930                 prop_atoms.net_wm_moveresize_move_keyboard) {
931
932                 moveresize_start(client, e->xclient.data.l[0],
933                                  e->xclient.data.l[1], e->xclient.data.l[3],
934                                  e->xclient.data.l[2]);
935             }
936         } else if (msgtype == prop_atoms.net_moveresize_window) {
937             int oldg = client->gravity;
938             int tmpg, x, y, w, h;
939
940             if (e->xclient.data.l[0] & 0xff)
941                 tmpg = e->xclient.data.l[0] & 0xff;
942             else
943                 tmpg = oldg;
944
945             if (e->xclient.data.l[0] & 1 << 8)
946                 x = e->xclient.data.l[1];
947             else
948                 x = client->area.x;
949             if (e->xclient.data.l[0] & 1 << 9)
950                 y = e->xclient.data.l[2];
951             else
952                 y = client->area.y;
953             if (e->xclient.data.l[0] & 1 << 10)
954                 w = e->xclient.data.l[3];
955             else
956                 w = client->area.width;
957             if (e->xclient.data.l[0] & 1 << 11)
958                 h = e->xclient.data.l[4];
959             else
960                 h = client->area.height;
961             client->gravity = tmpg;
962
963             {
964                 int newx = x;
965                 int newy = y;
966                 int fw = w +
967                     client->frame->size.left + client->frame->size.right;
968                 int fh = h +
969                     client->frame->size.top + client->frame->size.bottom;
970                 client_find_onscreen(client, &newx, &newy, fw, fh,
971                                      client_normal(client));
972                 if (e->xclient.data.l[0] & 1 << 8)
973                     x = newx;
974                 if (e->xclient.data.l[0] & 1 << 9)
975                     y = newy;
976             }
977                
978             client_configure(client, OB_CORNER_TOPLEFT,
979                              x, y, w, h, FALSE, TRUE);
980
981             client->gravity = oldg;
982         }
983         break;
984     case PropertyNotify:
985         /* validate cuz we query stuff off the client here */
986         if (!client_validate(client)) break;
987   
988         /* compress changes to a single property into a single change */
989         while (XCheckTypedWindowEvent(ob_display, client->window,
990                                       e->type, &ce)) {
991             Atom a, b;
992
993             /* XXX: it would be nice to compress ALL changes to a property,
994                not just changes in a row without other props between. */
995
996             a = ce.xproperty.atom;
997             b = e->xproperty.atom;
998
999             if (a == b)
1000                 continue;
1001             if ((a == prop_atoms.net_wm_name ||
1002                  a == prop_atoms.wm_name ||
1003                  a == prop_atoms.net_wm_icon_name ||
1004                  a == prop_atoms.wm_icon_name)
1005                 &&
1006                 (b == prop_atoms.net_wm_name ||
1007                  b == prop_atoms.wm_name ||
1008                  b == prop_atoms.net_wm_icon_name ||
1009                  b == prop_atoms.wm_icon_name)) {
1010                 continue;
1011             }
1012             if ((a == prop_atoms.net_wm_icon ||
1013                  a == prop_atoms.kwm_win_icon)
1014                 &&
1015                 (b == prop_atoms.net_wm_icon ||
1016                  b == prop_atoms.kwm_win_icon))
1017                 continue;
1018
1019             XPutBackEvent(ob_display, &ce);
1020             break;
1021         }
1022
1023         msgtype = e->xproperty.atom;
1024         if (msgtype == XA_WM_NORMAL_HINTS) {
1025             client_update_normal_hints(client);
1026             /* normal hints can make a window non-resizable */
1027             client_setup_decor_and_functions(client);
1028         } else if (msgtype == XA_WM_HINTS) {
1029             client_update_wmhints(client);
1030         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1031             client_update_transient_for(client);
1032             client_get_type(client);
1033             /* type may have changed, so update the layer */
1034             client_calc_layer(client);
1035             client_setup_decor_and_functions(client);
1036         } else if (msgtype == prop_atoms.net_wm_name ||
1037                    msgtype == prop_atoms.wm_name ||
1038                    msgtype == prop_atoms.net_wm_icon_name ||
1039                    msgtype == prop_atoms.wm_icon_name) {
1040             client_update_title(client);
1041         } else if (msgtype == prop_atoms.wm_class) {
1042             client_update_class(client);
1043         } else if (msgtype == prop_atoms.wm_protocols) {
1044             client_update_protocols(client);
1045             client_setup_decor_and_functions(client);
1046         }
1047         else if (msgtype == prop_atoms.net_wm_strut) {
1048             client_update_strut(client);
1049         }
1050         else if (msgtype == prop_atoms.net_wm_icon ||
1051                  msgtype == prop_atoms.kwm_win_icon) {
1052             client_update_icons(client);
1053         }
1054     default:
1055         ;
1056 #ifdef SHAPE
1057         if (extensions_shape && e->type == extensions_shape_event_basep) {
1058             client->shaped = ((XShapeEvent*)e)->shaped;
1059             frame_adjust_shape(client->frame);
1060         }
1061 #endif
1062     }
1063 }
1064
1065 static void event_handle_dock(ObDock *s, XEvent *e)
1066 {
1067     switch (e->type) {
1068     case ButtonPress:
1069         stacking_raise(DOCK_AS_WINDOW(s));
1070         break;
1071     case EnterNotify:
1072         dock_hide(FALSE);
1073         break;
1074     case LeaveNotify:
1075         dock_hide(TRUE);
1076         break;
1077     }
1078 }
1079
1080 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1081 {
1082     switch (e->type) {
1083     case MotionNotify:
1084         dock_app_drag(app, &e->xmotion);
1085         break;
1086     case UnmapNotify:
1087         if (app->ignore_unmaps) {
1088             app->ignore_unmaps--;
1089             break;
1090         }
1091         dock_remove(app, TRUE);
1092         break;
1093     case DestroyNotify:
1094         dock_remove(app, FALSE);
1095         break;
1096     case ReparentNotify:
1097         dock_remove(app, FALSE);
1098         break;
1099     case ConfigureNotify:
1100         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1101         break;
1102     }
1103 }
1104
1105 ObMenuFrame* find_active_menu()
1106 {
1107     GList *it;
1108     ObMenuFrame *f;
1109
1110     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1111         f = it->data;
1112         if (f->selected)
1113             break;
1114     }
1115     return it ? it->data : NULL;
1116 }
1117
1118 static void event_handle_menu(XEvent *ev)
1119 {
1120     ObMenuFrame *f;
1121     ObMenuEntryFrame *e;
1122
1123     switch (ev->type) {
1124     case ButtonRelease:
1125         if (!(f = menu_frame_under(ev->xbutton.x_root,
1126                                    ev->xbutton.y_root)))
1127             menu_frame_hide_all();
1128         else {
1129             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1130                                             ev->xbutton.y_root)))
1131                 menu_entry_frame_execute(e, ev->xbutton.state);
1132         }
1133         break;
1134     case MotionNotify:
1135         if ((f = menu_frame_under(ev->xmotion.x_root,
1136                                   ev->xmotion.y_root))) {
1137             menu_frame_move_on_screen(f);
1138             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1139                                             ev->xmotion.y_root)))
1140                 menu_frame_select(f, e);
1141         }
1142         break;
1143     case KeyPress:
1144         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1145             menu_frame_hide_all();
1146         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1147             ObMenuFrame *f;
1148             if ((f = find_active_menu()))
1149                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1150         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1151             ObMenuFrame *f;
1152             if ((f = find_active_menu()) && f->parent)
1153                 menu_frame_select(f, NULL);
1154         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1155             ObMenuFrame *f;
1156             if ((f = find_active_menu()) && f->child)
1157                 menu_frame_select_next(f->child);
1158         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1159             ObMenuFrame *f;
1160             if ((f = find_active_menu()))
1161                 menu_frame_select_previous(f);
1162         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1163             ObMenuFrame *f;
1164             if ((f = find_active_menu()))
1165                 menu_frame_select_next(f);
1166         }
1167         break;
1168     }
1169 }
1170
1171 static gboolean focus_delay_func(gpointer data)
1172 {
1173     client_focus(focus_delay_client);
1174     return FALSE; /* no repeat */
1175 }
1176
1177 static void focus_delay_client_dest(gpointer data)
1178 {
1179     ObClient *c = data;
1180     if (c == focus_delay_client) {
1181         ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1182                                          focus_delay_client);
1183         focus_delay_client = NULL;
1184     }
1185 }