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