]> 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         } else
535             focus_delay_func(client);
536     }
537 }
538
539 static void event_handle_client(ObClient *client, XEvent *e)
540 {
541     XEvent ce;
542     Atom msgtype;
543     int i=0;
544     ObFrameContext con;
545      
546     switch (e->type) {
547     case VisibilityNotify:
548         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
549         break;
550     case ButtonPress:
551     case ButtonRelease:
552         /* Wheel buttons don't draw because they are an instant click, so it
553            is a waste of resources to go drawing it. */
554         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
555             con = frame_context(client, e->xbutton.window);
556             con = mouse_button_frame_context(con, e->xbutton.button);
557             switch (con) {
558             case OB_FRAME_CONTEXT_MAXIMIZE:
559                 client->frame->max_press = (e->type == ButtonPress);
560                 framerender_frame(client->frame);
561                 break;
562             case OB_FRAME_CONTEXT_CLOSE:
563                 client->frame->close_press = (e->type == ButtonPress);
564                 framerender_frame(client->frame);
565                 break;
566             case OB_FRAME_CONTEXT_ICONIFY:
567                 client->frame->iconify_press = (e->type == ButtonPress);
568                 framerender_frame(client->frame);
569                 break;
570             case OB_FRAME_CONTEXT_ALLDESKTOPS:
571                 client->frame->desk_press = (e->type == ButtonPress);
572                 framerender_frame(client->frame);
573                 break; 
574             case OB_FRAME_CONTEXT_SHADE:
575                 client->frame->shade_press = (e->type == ButtonPress);
576                 framerender_frame(client->frame);
577                 break;
578             default:
579                 /* nothing changes with clicks for any other contexts */
580                 break;
581             }
582         }
583         break;
584     case FocusIn:
585 #ifdef DEBUG_FOCUS
586         ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
587                  e->xfocus.window, client->window, e->xfocus.mode, e->xfocus.detail);
588 #endif
589         focus_in = client;
590         if (focus_out == client)
591             focus_out = NULL;
592         break;
593     case FocusOut:
594 #ifdef DEBUG_FOCUS
595         ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
596                  e->xfocus.window, client->window, e->xfocus.mode, e->xfocus.detail);
597 #endif
598         if (focus_in == client)
599             focus_in = NULL;
600         if (client == focus_client)
601             focus_out = client;
602         break;
603     case LeaveNotify:
604         con = frame_context(client, e->xcrossing.window);
605         switch (con) {
606         case OB_FRAME_CONTEXT_MAXIMIZE:
607             client->frame->max_hover = FALSE;
608             frame_adjust_state(client->frame);
609             break;
610         case OB_FRAME_CONTEXT_ALLDESKTOPS:
611             client->frame->desk_hover = FALSE;
612             frame_adjust_state(client->frame);
613             break;
614         case OB_FRAME_CONTEXT_SHADE:
615             client->frame->shade_hover = FALSE;
616             frame_adjust_state(client->frame);
617             break;
618         case OB_FRAME_CONTEXT_ICONIFY:
619             client->frame->iconify_hover = FALSE;
620             frame_adjust_state(client->frame);
621             break;
622         case OB_FRAME_CONTEXT_CLOSE:
623             client->frame->close_hover = FALSE;
624             frame_adjust_state(client->frame);
625             break;
626         case OB_FRAME_CONTEXT_FRAME:
627             /*
628             if (config_focus_follow && config_focus_delay)
629                 ob_main_loop_timeout_remove_data(ob_main_loop,
630                                                  focus_delay_func,
631                                                  client);
632             */
633             break;
634         default:
635             break;
636         }
637         break;
638     case EnterNotify:
639     {
640         gboolean nofocus = FALSE;
641
642         if (ignore_enter_focus) {
643             ignore_enter_focus--;
644             nofocus = TRUE;
645         }
646
647         con = frame_context(client, e->xcrossing.window);
648         switch (con) {
649         case OB_FRAME_CONTEXT_MAXIMIZE:
650             client->frame->max_hover = TRUE;
651             frame_adjust_state(client->frame);
652             break;
653         case OB_FRAME_CONTEXT_ALLDESKTOPS:
654             client->frame->desk_hover = TRUE;
655             frame_adjust_state(client->frame);
656             break;
657         case OB_FRAME_CONTEXT_SHADE:
658             client->frame->shade_hover = TRUE;
659             frame_adjust_state(client->frame);
660             break;
661         case OB_FRAME_CONTEXT_ICONIFY:
662             client->frame->iconify_hover = TRUE;
663             frame_adjust_state(client->frame);
664             break;
665         case OB_FRAME_CONTEXT_CLOSE:
666             client->frame->close_hover = TRUE;
667             frame_adjust_state(client->frame);
668             break;
669         case OB_FRAME_CONTEXT_FRAME:
670             if (e->xcrossing.mode == NotifyGrab ||
671                 e->xcrossing.mode == NotifyUngrab)
672             {
673 #ifdef DEBUG_FOCUS
674                 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
675                          (e->type == EnterNotify ? "Enter" : "Leave"),
676                          e->xcrossing.mode,
677                          e->xcrossing.detail, client?client->window:0);
678 #endif
679             } else {
680 #ifdef DEBUG_FOCUS
681                 ob_debug("%sNotify mode %d detail %d on %lx, "
682                          "focusing window: %d\n",
683                          (e->type == EnterNotify ? "Enter" : "Leave"),
684                          e->xcrossing.mode,
685                          e->xcrossing.detail, (client?client->window:0),
686                          !nofocus);
687 #endif
688                 if (!nofocus && config_focus_follow)
689                     event_enter_client(client);
690             }
691             break;
692         default:
693             break;
694         }
695         break;
696     }
697     case ConfigureRequest:
698         /* compress these */
699         while (XCheckTypedWindowEvent(ob_display, client->window,
700                                       ConfigureRequest, &ce)) {
701             ++i;
702             /* XXX if this causes bad things.. we can compress config req's
703                with the same mask. */
704             e->xconfigurerequest.value_mask |=
705                 ce.xconfigurerequest.value_mask;
706             if (ce.xconfigurerequest.value_mask & CWX)
707                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
708             if (ce.xconfigurerequest.value_mask & CWY)
709                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
710             if (ce.xconfigurerequest.value_mask & CWWidth)
711                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
712             if (ce.xconfigurerequest.value_mask & CWHeight)
713                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
714             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
715                 e->xconfigurerequest.border_width =
716                     ce.xconfigurerequest.border_width;
717             if (ce.xconfigurerequest.value_mask & CWStackMode)
718                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
719         }
720
721         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
722         if (client->iconic || client->shaded) return;
723
724         /* resize, then move, as specified in the EWMH section 7.7 */
725         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
726                                                CWX | CWY |
727                                                CWBorderWidth)) {
728             int x, y, w, h;
729             ObCorner corner;
730
731             if (e->xconfigurerequest.value_mask & CWBorderWidth)
732                 client->border_width = e->xconfigurerequest.border_width;
733
734             x = (e->xconfigurerequest.value_mask & CWX) ?
735                 e->xconfigurerequest.x : client->area.x;
736             y = (e->xconfigurerequest.value_mask & CWY) ?
737                 e->xconfigurerequest.y : client->area.y;
738             w = (e->xconfigurerequest.value_mask & CWWidth) ?
739                 e->xconfigurerequest.width : client->area.width;
740             h = (e->xconfigurerequest.value_mask & CWHeight) ?
741                 e->xconfigurerequest.height : client->area.height;
742
743             {
744                 int newx = x;
745                 int newy = y;
746                 int fw = w +
747                     client->frame->size.left + client->frame->size.right;
748                 int fh = h +
749                     client->frame->size.top + client->frame->size.bottom;
750                 client_find_onscreen(client, &newx, &newy, fw, fh,
751                                      client_normal(client));
752                 if (e->xconfigurerequest.value_mask & CWX)
753                     x = newx;
754                 if (e->xconfigurerequest.value_mask & CWY)
755                     y = newy;
756             }
757                
758             switch (client->gravity) {
759             case NorthEastGravity:
760             case EastGravity:
761                 corner = OB_CORNER_TOPRIGHT;
762                 break;
763             case SouthWestGravity:
764             case SouthGravity:
765                 corner = OB_CORNER_BOTTOMLEFT;
766                 break;
767             case SouthEastGravity:
768                 corner = OB_CORNER_BOTTOMRIGHT;
769                 break;
770             default:     /* NorthWest, Static, etc */
771                 corner = OB_CORNER_TOPLEFT;
772             }
773
774             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
775                                   TRUE);
776         }
777
778         if (e->xconfigurerequest.value_mask & CWStackMode) {
779             switch (e->xconfigurerequest.detail) {
780             case Below:
781             case BottomIf:
782             client_lower(client);
783             break;
784
785             case Above:
786             case TopIf:
787             default:
788             client_raise(client);
789             break;
790             }
791         }
792         break;
793     case UnmapNotify:
794         if (client->ignore_unmaps) {
795             client->ignore_unmaps--;
796             break;
797         }
798         client_unmanage(client);
799         break;
800     case DestroyNotify:
801         client_unmanage(client);
802         break;
803     case ReparentNotify:
804         /* this is when the client is first taken captive in the frame */
805         if (e->xreparent.parent == client->frame->plate) break;
806
807         /*
808           This event is quite rare and is usually handled in unmapHandler.
809           However, if the window is unmapped when the reparent event occurs,
810           the window manager never sees it because an unmap event is not sent
811           to an already unmapped window.
812         */
813
814         /* we don't want the reparent event, put it back on the stack for the
815            X server to deal with after we unmanage the window */
816         XPutBackEvent(ob_display, e);
817      
818         client_unmanage(client);
819         break;
820     case MapRequest:
821         ob_debug("MapRequest for 0x%lx\n", client->window);
822         if (!client->iconic) break; /* this normally doesn't happen, but if it
823                                        does, we don't want it! */
824         client_activate(client, FALSE);
825         break;
826     case ClientMessage:
827         /* validate cuz we query stuff off the client here */
828         if (!client_validate(client)) break;
829
830         if (e->xclient.format != 32) return;
831
832         msgtype = e->xclient.message_type;
833         if (msgtype == prop_atoms.wm_change_state) {
834             /* compress changes into a single change */
835             while (XCheckTypedWindowEvent(ob_display, client->window,
836                                           e->type, &ce)) {
837                 /* XXX: it would be nice to compress ALL messages of a
838                    type, not just messages in a row without other
839                    message types between. */
840                 if (ce.xclient.message_type != msgtype) {
841                     XPutBackEvent(ob_display, &ce);
842                     break;
843                 }
844                 e->xclient = ce.xclient;
845             }
846             client_set_wm_state(client, e->xclient.data.l[0]);
847         } else if (msgtype == prop_atoms.net_wm_desktop) {
848             /* compress changes into a single change */
849             while (XCheckTypedWindowEvent(ob_display, client->window,
850                                           e->type, &ce)) {
851                 /* XXX: it would be nice to compress ALL messages of a
852                    type, not just messages in a row without other
853                    message types between. */
854                 if (ce.xclient.message_type != msgtype) {
855                     XPutBackEvent(ob_display, &ce);
856                     break;
857                 }
858                 e->xclient = ce.xclient;
859             }
860             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
861                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
862                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
863                                    FALSE);
864         } else if (msgtype == prop_atoms.net_wm_state) {
865             /* can't compress these */
866             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
867                      (e->xclient.data.l[0] == 0 ? "Remove" :
868                       e->xclient.data.l[0] == 1 ? "Add" :
869                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
870                      e->xclient.data.l[1], e->xclient.data.l[2],
871                      client->window);
872             client_set_state(client, e->xclient.data.l[0],
873                              e->xclient.data.l[1], e->xclient.data.l[2]);
874         } else if (msgtype == prop_atoms.net_close_window) {
875             ob_debug("net_close_window for 0x%lx\n", client->window);
876             client_close(client);
877         } else if (msgtype == prop_atoms.net_active_window) {
878             ob_debug("net_active_window for 0x%lx\n", client->window);
879             client_activate(client, FALSE);
880         } else if (msgtype == prop_atoms.net_wm_moveresize) {
881             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
882             if ((Atom)e->xclient.data.l[2] ==
883                 prop_atoms.net_wm_moveresize_size_topleft ||
884                 (Atom)e->xclient.data.l[2] ==
885                 prop_atoms.net_wm_moveresize_size_top ||
886                 (Atom)e->xclient.data.l[2] ==
887                 prop_atoms.net_wm_moveresize_size_topright ||
888                 (Atom)e->xclient.data.l[2] ==
889                 prop_atoms.net_wm_moveresize_size_right ||
890                 (Atom)e->xclient.data.l[2] ==
891                 prop_atoms.net_wm_moveresize_size_right ||
892                 (Atom)e->xclient.data.l[2] ==
893                 prop_atoms.net_wm_moveresize_size_bottomright ||
894                 (Atom)e->xclient.data.l[2] ==
895                 prop_atoms.net_wm_moveresize_size_bottom ||
896                 (Atom)e->xclient.data.l[2] ==
897                 prop_atoms.net_wm_moveresize_size_bottomleft ||
898                 (Atom)e->xclient.data.l[2] ==
899                 prop_atoms.net_wm_moveresize_size_left ||
900                 (Atom)e->xclient.data.l[2] ==
901                 prop_atoms.net_wm_moveresize_move ||
902                 (Atom)e->xclient.data.l[2] ==
903                 prop_atoms.net_wm_moveresize_size_keyboard ||
904                 (Atom)e->xclient.data.l[2] ==
905                 prop_atoms.net_wm_moveresize_move_keyboard) {
906
907                 moveresize_start(client, e->xclient.data.l[0],
908                                  e->xclient.data.l[1], e->xclient.data.l[3],
909                                  e->xclient.data.l[2]);
910             }
911         } else if (msgtype == prop_atoms.net_moveresize_window) {
912             int oldg = client->gravity;
913             int tmpg, x, y, w, h;
914
915             if (e->xclient.data.l[0] & 0xff)
916                 tmpg = e->xclient.data.l[0] & 0xff;
917             else
918                 tmpg = oldg;
919
920             if (e->xclient.data.l[0] & 1 << 8)
921                 x = e->xclient.data.l[1];
922             else
923                 x = client->area.x;
924             if (e->xclient.data.l[0] & 1 << 9)
925                 y = e->xclient.data.l[2];
926             else
927                 y = client->area.y;
928             if (e->xclient.data.l[0] & 1 << 10)
929                 w = e->xclient.data.l[3];
930             else
931                 w = client->area.width;
932             if (e->xclient.data.l[0] & 1 << 11)
933                 h = e->xclient.data.l[4];
934             else
935                 h = client->area.height;
936             client->gravity = tmpg;
937
938             {
939                 int newx = x;
940                 int newy = y;
941                 int fw = w +
942                     client->frame->size.left + client->frame->size.right;
943                 int fh = h +
944                     client->frame->size.top + client->frame->size.bottom;
945                 client_find_onscreen(client, &newx, &newy, fw, fh,
946                                      client_normal(client));
947                 if (e->xclient.data.l[0] & 1 << 8)
948                     x = newx;
949                 if (e->xclient.data.l[0] & 1 << 9)
950                     y = newy;
951             }
952                
953             client_configure(client, OB_CORNER_TOPLEFT,
954                              x, y, w, h, FALSE, TRUE);
955
956             client->gravity = oldg;
957         }
958         break;
959     case PropertyNotify:
960         /* validate cuz we query stuff off the client here */
961         if (!client_validate(client)) break;
962   
963         /* compress changes to a single property into a single change */
964         while (XCheckTypedWindowEvent(ob_display, client->window,
965                                       e->type, &ce)) {
966             Atom a, b;
967
968             /* XXX: it would be nice to compress ALL changes to a property,
969                not just changes in a row without other props between. */
970
971             a = ce.xproperty.atom;
972             b = e->xproperty.atom;
973
974             if (a == b)
975                 continue;
976             if ((a == prop_atoms.net_wm_name ||
977                  a == prop_atoms.wm_name ||
978                  a == prop_atoms.net_wm_icon_name ||
979                  a == prop_atoms.wm_icon_name)
980                 &&
981                 (b == prop_atoms.net_wm_name ||
982                  b == prop_atoms.wm_name ||
983                  b == prop_atoms.net_wm_icon_name ||
984                  b == prop_atoms.wm_icon_name)) {
985                 continue;
986             }
987             if ((a == prop_atoms.net_wm_icon ||
988                  a == prop_atoms.kwm_win_icon)
989                 &&
990                 (b == prop_atoms.net_wm_icon ||
991                  b == prop_atoms.kwm_win_icon))
992                 continue;
993
994             XPutBackEvent(ob_display, &ce);
995             break;
996         }
997
998         msgtype = e->xproperty.atom;
999         if (msgtype == XA_WM_NORMAL_HINTS) {
1000             client_update_normal_hints(client);
1001             /* normal hints can make a window non-resizable */
1002             client_setup_decor_and_functions(client);
1003         } else if (msgtype == XA_WM_HINTS) {
1004             client_update_wmhints(client);
1005         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1006             client_update_transient_for(client);
1007             client_get_type(client);
1008             /* type may have changed, so update the layer */
1009             client_calc_layer(client);
1010             client_setup_decor_and_functions(client);
1011         } else if (msgtype == prop_atoms.net_wm_name ||
1012                    msgtype == prop_atoms.wm_name ||
1013                    msgtype == prop_atoms.net_wm_icon_name ||
1014                    msgtype == prop_atoms.wm_icon_name) {
1015             client_update_title(client);
1016         } else if (msgtype == prop_atoms.wm_class) {
1017             client_update_class(client);
1018         } else if (msgtype == prop_atoms.wm_protocols) {
1019             client_update_protocols(client);
1020             client_setup_decor_and_functions(client);
1021         }
1022         else if (msgtype == prop_atoms.net_wm_strut) {
1023             client_update_strut(client);
1024         }
1025         else if (msgtype == prop_atoms.net_wm_icon ||
1026                  msgtype == prop_atoms.kwm_win_icon) {
1027             client_update_icons(client);
1028         }
1029         else if (msgtype == prop_atoms.sm_client_id) {
1030             client_update_sm_client_id(client);
1031         }
1032     default:
1033         ;
1034 #ifdef SHAPE
1035         if (extensions_shape && e->type == extensions_shape_event_basep) {
1036             client->shaped = ((XShapeEvent*)e)->shaped;
1037             frame_adjust_shape(client->frame);
1038         }
1039 #endif
1040     }
1041 }
1042
1043 static void event_handle_dock(ObDock *s, XEvent *e)
1044 {
1045     switch (e->type) {
1046     case ButtonPress:
1047         stacking_raise(DOCK_AS_WINDOW(s));
1048         break;
1049     case EnterNotify:
1050         dock_hide(FALSE);
1051         break;
1052     case LeaveNotify:
1053         dock_hide(TRUE);
1054         break;
1055     }
1056 }
1057
1058 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1059 {
1060     switch (e->type) {
1061     case MotionNotify:
1062         dock_app_drag(app, &e->xmotion);
1063         break;
1064     case UnmapNotify:
1065         if (app->ignore_unmaps) {
1066             app->ignore_unmaps--;
1067             break;
1068         }
1069         dock_remove(app, TRUE);
1070         break;
1071     case DestroyNotify:
1072         dock_remove(app, FALSE);
1073         break;
1074     case ReparentNotify:
1075         dock_remove(app, FALSE);
1076         break;
1077     case ConfigureNotify:
1078         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1079         break;
1080     }
1081 }
1082
1083 ObMenuFrame* find_active_menu()
1084 {
1085     GList *it;
1086     ObMenuFrame *ret = NULL;
1087
1088     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1089         ret = it->data;
1090         if (ret->selected)
1091             break;
1092         ret = NULL;
1093     }
1094     return ret;
1095 }
1096
1097 ObMenuFrame* find_active_or_last_menu()
1098 {
1099     ObMenuFrame *ret = NULL;
1100
1101     ret = find_active_menu();
1102     if (!ret && menu_frame_visible)
1103         ret = menu_frame_visible->data;
1104     return ret;
1105 }
1106
1107 static void event_handle_menu(XEvent *ev)
1108 {
1109     ObMenuFrame *f;
1110     ObMenuEntryFrame *e;
1111
1112     switch (ev->type) {
1113     case ButtonRelease:
1114         if (menu_can_hide) {
1115             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1116                                             ev->xbutton.y_root)))
1117                 menu_entry_frame_execute(e, ev->xbutton.state);
1118             else
1119                 menu_frame_hide_all();
1120         }
1121         break;
1122     case MotionNotify:
1123         if ((f = menu_frame_under(ev->xmotion.x_root,
1124                                   ev->xmotion.y_root))) {
1125             menu_frame_move_on_screen(f);
1126             if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1127                                             ev->xmotion.y_root)))
1128                 menu_frame_select(f, e);
1129         }
1130         {
1131             ObMenuFrame *a;
1132
1133             a = find_active_menu();
1134             if (a && a != f &&
1135                 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1136             {
1137                 menu_frame_select(a, NULL);
1138             }
1139         }
1140         break;
1141     case KeyPress:
1142         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1143             menu_frame_hide_all();
1144         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1145             ObMenuFrame *f;
1146             if ((f = find_active_menu()))
1147                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1148         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1149             ObMenuFrame *f;
1150             if ((f = find_active_or_last_menu()) && f->parent)
1151                 menu_frame_select(f, NULL);
1152         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1153             ObMenuFrame *f;
1154             if ((f = find_active_or_last_menu()) && f->child)
1155                 menu_frame_select_next(f->child);
1156         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1157             ObMenuFrame *f;
1158             if ((f = find_active_or_last_menu()))
1159                 menu_frame_select_previous(f);
1160         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1161             ObMenuFrame *f;
1162             if ((f = find_active_or_last_menu()))
1163                 menu_frame_select_next(f);
1164         }
1165         break;
1166     }
1167 }
1168
1169 static gboolean menu_hide_delay_func(gpointer data)
1170 {
1171     menu_can_hide = TRUE;
1172     return FALSE; /* no repeat */
1173 }
1174
1175 static gboolean focus_delay_func(gpointer data)
1176 {
1177     ObClient *c = data;
1178
1179     if (focus_client != c) {
1180         client_focus(c);
1181         if (config_focus_raise)
1182             client_raise(c);
1183     }
1184     return FALSE; /* no repeat */
1185 }
1186
1187 static void focus_delay_client_dest(ObClient *client, gpointer data)
1188 {
1189     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1190 }
1191
1192 void event_ignore_queued_enters()
1193 {
1194     GSList *saved = NULL, *it;
1195     XEvent *e;
1196                 
1197     XSync(ob_display, FALSE);
1198
1199     /* count the events */
1200     while (TRUE) {
1201         e = g_new(XEvent, 1);
1202         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1203             ObWindow *win;
1204             
1205             win = g_hash_table_lookup(window_map, &e->xany.window);
1206             if (win && WINDOW_IS_CLIENT(win))
1207                 ++ignore_enter_focus;
1208             
1209             saved = g_slist_append(saved, e);
1210         } else {
1211             g_free(e);
1212             break;
1213         }
1214     }
1215     /* put the events back */
1216     for (it = saved; it; it = g_slist_next(it)) {
1217         XPutBackEvent(ob_display, it->data);
1218         g_free(it->data);
1219     }
1220     g_slist_free(saved);
1221 }