]> icculus.org git repositories - dana/openbox.git/blob - openbox/event.c
use focus_hilite to ensure that when a wnidow thats not focused is hilighted, the...
[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     /* sometimes focus_hilite can be on an unfocused window, this make sure
331        it loses its focus hilite when focus moves */
332     if (focus_hilite &&
333         (focus_in && focus_hilite != focus_in) &&
334         (focus_out && focus_hilite != focus_out))
335     {
336         frame_adjust_focus(focus_hilite->frame, FALSE);
337     }
338
339     if (focus_in) {
340         if (focus_in != focus_client) {
341             focus_set_client(focus_in);
342             frame_adjust_focus(focus_in->frame, TRUE);
343             client_calc_layer(focus_in);
344         }
345     } 
346     if (focus_out) {
347         if (focus_out == focus_client)
348             focus_set_client(NULL);
349         frame_adjust_focus(focus_out->frame, FALSE);
350         client_calc_layer(focus_out);
351     }
352
353     focus_hilite = focus_in;
354
355     if (focus_client != last) {
356         if (!focus_client)
357             focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
358         last = focus_client;
359     }
360
361     focus_in = focus_out = NULL;
362 }
363
364 static void event_process(const XEvent *ec, gpointer data)
365 {
366     Window window;
367     ObGroup *group = NULL;
368     ObClient *client = NULL;
369     ObDock *dock = NULL;
370     ObDockApp *dockapp = NULL;
371     ObWindow *obwin = NULL;
372     XEvent ee, *e;
373
374     /* make a copy we can mangle */
375     ee = *ec;
376     e = &ee;
377
378     window = event_get_window(e);
379     if (!(e->type == PropertyNotify &&
380           (group = g_hash_table_lookup(group_map, &window))))
381         if ((obwin = g_hash_table_lookup(window_map, &window))) {
382             switch (obwin->type) {
383             case Window_Dock:
384                 dock = WINDOW_AS_DOCK(obwin);
385                 break;
386             case Window_DockApp:
387                 dockapp = WINDOW_AS_DOCKAPP(obwin);
388                 break;
389             case Window_Client:
390                 client = WINDOW_AS_CLIENT(obwin);
391                 break;
392             case Window_Menu:
393             case Window_Internal:
394                 /* not to be used for events */
395                 g_assert_not_reached();
396                 break;
397             }
398         }
399
400     event_set_lasttime(e);
401     event_hack_mods(e);
402     if (event_ignore(e, client))
403         return;
404
405     /* deal with it in the kernel */
406     if (group)
407         event_handle_group(group, e);
408     else if (client)
409         event_handle_client(client, e);
410     else if (dockapp)
411         event_handle_dockapp(dockapp, e);
412     else if (dock)
413         event_handle_dock(dock, e);
414     else if (window == RootWindow(ob_display, ob_screen))
415         event_handle_root(e);
416     else if (e->type == MapRequest)
417         client_manage(window);
418     else if (e->type == ConfigureRequest) {
419         /* unhandled configure requests must be used to configure the
420            window directly */
421         XWindowChanges xwc;
422                
423         xwc.x = e->xconfigurerequest.x;
424         xwc.y = e->xconfigurerequest.y;
425         xwc.width = e->xconfigurerequest.width;
426         xwc.height = e->xconfigurerequest.height;
427         xwc.border_width = e->xconfigurerequest.border_width;
428         xwc.sibling = e->xconfigurerequest.above;
429         xwc.stack_mode = e->xconfigurerequest.detail;
430        
431         /* we are not to be held responsible if someone sends us an
432            invalid request! */
433         xerror_set_ignore(TRUE);
434         XConfigureWindow(ob_display, window,
435                          e->xconfigurerequest.value_mask, &xwc);
436         xerror_set_ignore(FALSE);
437     }
438
439     /* user input (action-bound) events */
440     if (e->type == ButtonPress || e->type == ButtonRelease ||
441         e->type == MotionNotify || e->type == KeyPress ||
442         e->type == KeyRelease)
443     {
444         if (menu_frame_visible)
445             event_handle_menu(e);
446         else {
447             if (!keyboard_process_interactive_grab(e, &client)) {
448                 if (moveresize_in_progress) {
449                     moveresize_event(e);
450
451                     /* make further actions work on the client being
452                        moved/resized */
453                     client = moveresize_client;
454                 }
455
456                 menu_can_hide = FALSE;
457                 ob_main_loop_timeout_add(ob_main_loop,
458                                          G_USEC_PER_SEC / 4,
459                                          menu_hide_delay_func,
460                                          NULL, NULL);
461
462                 if (e->type == ButtonPress || e->type == ButtonRelease ||
463                     e->type == MotionNotify)
464                     mouse_event(client, e);
465                 else if (e->type == KeyPress)
466                     /* when in the middle of a focus cycling action, this
467                        causes the window which appears to be focused to be
468                        the one on which the actions will be executed */
469                     keyboard_event((focus_cycle_target ?
470                                     focus_cycle_target : client), e);
471             }
472         }
473     }
474 }
475
476 static void event_handle_root(XEvent *e)
477 {
478     Atom msgtype;
479      
480     switch(e->type) {
481     case SelectionClear:
482         ob_debug("Another WM has requested to replace us. Exiting.\n");
483         ob_exit(0);
484         break;
485
486     case ClientMessage:
487         if (e->xclient.format != 32) break;
488
489         msgtype = e->xclient.message_type;
490         if (msgtype == prop_atoms.net_current_desktop) {
491             unsigned int d = e->xclient.data.l[0];
492             if (d < screen_num_desktops)
493                 screen_set_desktop(d);
494         } else if (msgtype == prop_atoms.net_number_of_desktops) {
495             unsigned int d = e->xclient.data.l[0];
496             if (d > 0)
497                 screen_set_num_desktops(d);
498         } else if (msgtype == prop_atoms.net_showing_desktop) {
499             screen_show_desktop(e->xclient.data.l[0] != 0);
500         }
501         break;
502     case PropertyNotify:
503         if (e->xproperty.atom == prop_atoms.net_desktop_names)
504             screen_update_desktop_names();
505         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
506             screen_update_layout();
507         break;
508     case ConfigureNotify:
509 #ifdef XRANDR
510         XRRUpdateConfiguration(e);
511 #endif
512         screen_resize();
513         break;
514     default:
515         ;
516 #ifdef VIDMODE
517         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
518             ob_debug("VIDMODE EVENT\n");
519         }
520 #endif
521     }
522 }
523
524 static void event_handle_group(ObGroup *group, XEvent *e)
525 {
526     GSList *it;
527
528     g_assert(e->type == PropertyNotify);
529
530     for (it = group->members; it; it = g_slist_next(it))
531         event_handle_client(it->data, e);
532 }
533
534 void event_enter_client(ObClient *client)
535 {
536     g_assert(config_focus_follow);
537
538     if (client_normal(client) && client_can_focus(client)) {
539         if (config_focus_delay) {
540             ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
541             ob_main_loop_timeout_add(ob_main_loop,
542                                      config_focus_delay,
543                                      focus_delay_func,
544                                      client, NULL);
545         } else
546             focus_delay_func(client);
547     }
548 }
549
550 static void event_handle_client(ObClient *client, XEvent *e)
551 {
552     XEvent ce;
553     Atom msgtype;
554     int i=0;
555     ObFrameContext con;
556      
557     switch (e->type) {
558     case VisibilityNotify:
559         client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
560         break;
561     case ButtonPress:
562     case ButtonRelease:
563         /* Wheel buttons don't draw because they are an instant click, so it
564            is a waste of resources to go drawing it. */
565         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
566             con = frame_context(client, e->xbutton.window);
567             con = mouse_button_frame_context(con, e->xbutton.button);
568             switch (con) {
569             case OB_FRAME_CONTEXT_MAXIMIZE:
570                 client->frame->max_press = (e->type == ButtonPress);
571                 framerender_frame(client->frame);
572                 break;
573             case OB_FRAME_CONTEXT_CLOSE:
574                 client->frame->close_press = (e->type == ButtonPress);
575                 framerender_frame(client->frame);
576                 break;
577             case OB_FRAME_CONTEXT_ICONIFY:
578                 client->frame->iconify_press = (e->type == ButtonPress);
579                 framerender_frame(client->frame);
580                 break;
581             case OB_FRAME_CONTEXT_ALLDESKTOPS:
582                 client->frame->desk_press = (e->type == ButtonPress);
583                 framerender_frame(client->frame);
584                 break; 
585             case OB_FRAME_CONTEXT_SHADE:
586                 client->frame->shade_press = (e->type == ButtonPress);
587                 framerender_frame(client->frame);
588                 break;
589             default:
590                 /* nothing changes with clicks for any other contexts */
591                 break;
592             }
593         }
594         break;
595     case FocusIn:
596 #ifdef DEBUG_FOCUS
597         ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
598                  e->xfocus.window, client->window,
599                  e->xfocus.mode, e->xfocus.detail);
600 #endif
601         focus_in = client;
602         if (focus_out == client)
603             focus_out = NULL;
604         break;
605     case FocusOut:
606 #ifdef DEBUG_FOCUS
607         ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
608                  e->xfocus.window, client->window,
609                  e->xfocus.mode, e->xfocus.detail);
610 #endif
611         focus_out = client;
612         if (focus_in == client)
613             focus_in = NULL;
614         break;
615     case LeaveNotify:
616         con = frame_context(client, e->xcrossing.window);
617         switch (con) {
618         case OB_FRAME_CONTEXT_MAXIMIZE:
619             client->frame->max_hover = FALSE;
620             frame_adjust_state(client->frame);
621             break;
622         case OB_FRAME_CONTEXT_ALLDESKTOPS:
623             client->frame->desk_hover = FALSE;
624             frame_adjust_state(client->frame);
625             break;
626         case OB_FRAME_CONTEXT_SHADE:
627             client->frame->shade_hover = FALSE;
628             frame_adjust_state(client->frame);
629             break;
630         case OB_FRAME_CONTEXT_ICONIFY:
631             client->frame->iconify_hover = FALSE;
632             frame_adjust_state(client->frame);
633             break;
634         case OB_FRAME_CONTEXT_CLOSE:
635             client->frame->close_hover = FALSE;
636             frame_adjust_state(client->frame);
637             break;
638         case OB_FRAME_CONTEXT_FRAME:
639             /*
640               if (config_focus_follow && config_focus_delay)
641               ob_main_loop_timeout_remove_data(ob_main_loop,
642               focus_delay_func,
643               client);
644             */
645             break;
646         default:
647             break;
648         }
649         break;
650     case EnterNotify:
651     {
652         gboolean nofocus = FALSE;
653
654         if (ignore_enter_focus) {
655             ignore_enter_focus--;
656             nofocus = TRUE;
657         }
658
659         con = frame_context(client, e->xcrossing.window);
660         switch (con) {
661         case OB_FRAME_CONTEXT_MAXIMIZE:
662             client->frame->max_hover = TRUE;
663             frame_adjust_state(client->frame);
664             break;
665         case OB_FRAME_CONTEXT_ALLDESKTOPS:
666             client->frame->desk_hover = TRUE;
667             frame_adjust_state(client->frame);
668             break;
669         case OB_FRAME_CONTEXT_SHADE:
670             client->frame->shade_hover = TRUE;
671             frame_adjust_state(client->frame);
672             break;
673         case OB_FRAME_CONTEXT_ICONIFY:
674             client->frame->iconify_hover = TRUE;
675             frame_adjust_state(client->frame);
676             break;
677         case OB_FRAME_CONTEXT_CLOSE:
678             client->frame->close_hover = TRUE;
679             frame_adjust_state(client->frame);
680             break;
681         case OB_FRAME_CONTEXT_FRAME:
682             if (e->xcrossing.mode == NotifyGrab ||
683                 e->xcrossing.mode == NotifyUngrab)
684             {
685 #ifdef DEBUG_FOCUS
686                 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
687                          (e->type == EnterNotify ? "Enter" : "Leave"),
688                          e->xcrossing.mode,
689                          e->xcrossing.detail, client?client->window:0);
690 #endif
691             } else {
692 #ifdef DEBUG_FOCUS
693                 ob_debug("%sNotify mode %d detail %d on %lx, "
694                          "focusing window: %d\n",
695                          (e->type == EnterNotify ? "Enter" : "Leave"),
696                          e->xcrossing.mode,
697                          e->xcrossing.detail, (client?client->window:0),
698                          !nofocus);
699 #endif
700                 if (!nofocus && config_focus_follow)
701                     event_enter_client(client);
702             }
703             break;
704         default:
705             break;
706         }
707         break;
708     }
709     case ConfigureRequest:
710         /* compress these */
711         while (XCheckTypedWindowEvent(ob_display, client->window,
712                                       ConfigureRequest, &ce)) {
713             ++i;
714             /* XXX if this causes bad things.. we can compress config req's
715                with the same mask. */
716             e->xconfigurerequest.value_mask |=
717                 ce.xconfigurerequest.value_mask;
718             if (ce.xconfigurerequest.value_mask & CWX)
719                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
720             if (ce.xconfigurerequest.value_mask & CWY)
721                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
722             if (ce.xconfigurerequest.value_mask & CWWidth)
723                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
724             if (ce.xconfigurerequest.value_mask & CWHeight)
725                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
726             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
727                 e->xconfigurerequest.border_width =
728                     ce.xconfigurerequest.border_width;
729             if (ce.xconfigurerequest.value_mask & CWStackMode)
730                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
731         }
732
733         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
734         if (client->iconic || client->shaded) return;
735
736         /* resize, then move, as specified in the EWMH section 7.7 */
737         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
738                                                CWX | CWY |
739                                                CWBorderWidth)) {
740             int x, y, w, h;
741             ObCorner corner;
742
743             if (e->xconfigurerequest.value_mask & CWBorderWidth)
744                 client->border_width = e->xconfigurerequest.border_width;
745
746             x = (e->xconfigurerequest.value_mask & CWX) ?
747                 e->xconfigurerequest.x : client->area.x;
748             y = (e->xconfigurerequest.value_mask & CWY) ?
749                 e->xconfigurerequest.y : client->area.y;
750             w = (e->xconfigurerequest.value_mask & CWWidth) ?
751                 e->xconfigurerequest.width : client->area.width;
752             h = (e->xconfigurerequest.value_mask & CWHeight) ?
753                 e->xconfigurerequest.height : client->area.height;
754
755             {
756                 int newx = x;
757                 int newy = y;
758                 int fw = w +
759                     client->frame->size.left + client->frame->size.right;
760                 int fh = h +
761                     client->frame->size.top + client->frame->size.bottom;
762                 client_find_onscreen(client, &newx, &newy, fw, fh,
763                                      client_normal(client));
764                 if (e->xconfigurerequest.value_mask & CWX)
765                     x = newx;
766                 if (e->xconfigurerequest.value_mask & CWY)
767                     y = newy;
768             }
769                
770             switch (client->gravity) {
771             case NorthEastGravity:
772             case EastGravity:
773                 corner = OB_CORNER_TOPRIGHT;
774                 break;
775             case SouthWestGravity:
776             case SouthGravity:
777                 corner = OB_CORNER_BOTTOMLEFT;
778                 break;
779             case SouthEastGravity:
780                 corner = OB_CORNER_BOTTOMRIGHT;
781                 break;
782             default:     /* NorthWest, Static, etc */
783                 corner = OB_CORNER_TOPLEFT;
784             }
785
786             client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
787                                   TRUE);
788         }
789
790         if (e->xconfigurerequest.value_mask & CWStackMode) {
791             switch (e->xconfigurerequest.detail) {
792             case Below:
793             case BottomIf:
794                 client_lower(client);
795                 break;
796
797             case Above:
798             case TopIf:
799             default:
800                 client_raise(client);
801                 break;
802             }
803         }
804         break;
805     case UnmapNotify:
806         if (client->ignore_unmaps) {
807             client->ignore_unmaps--;
808             break;
809         }
810         client_unmanage(client);
811         break;
812     case DestroyNotify:
813         client_unmanage(client);
814         break;
815     case ReparentNotify:
816         /* this is when the client is first taken captive in the frame */
817         if (e->xreparent.parent == client->frame->plate) break;
818
819         /*
820           This event is quite rare and is usually handled in unmapHandler.
821           However, if the window is unmapped when the reparent event occurs,
822           the window manager never sees it because an unmap event is not sent
823           to an already unmapped window.
824         */
825
826         /* we don't want the reparent event, put it back on the stack for the
827            X server to deal with after we unmanage the window */
828         XPutBackEvent(ob_display, e);
829      
830         client_unmanage(client);
831         break;
832     case MapRequest:
833         ob_debug("MapRequest for 0x%lx\n", client->window);
834         if (!client->iconic) break; /* this normally doesn't happen, but if it
835                                        does, we don't want it! */
836         client_activate(client, FALSE);
837         break;
838     case ClientMessage:
839         /* validate cuz we query stuff off the client here */
840         if (!client_validate(client)) break;
841
842         if (e->xclient.format != 32) return;
843
844         msgtype = e->xclient.message_type;
845         if (msgtype == prop_atoms.wm_change_state) {
846             /* compress changes into a single change */
847             while (XCheckTypedWindowEvent(ob_display, client->window,
848                                           e->type, &ce)) {
849                 /* XXX: it would be nice to compress ALL messages of a
850                    type, not just messages in a row without other
851                    message types between. */
852                 if (ce.xclient.message_type != msgtype) {
853                     XPutBackEvent(ob_display, &ce);
854                     break;
855                 }
856                 e->xclient = ce.xclient;
857             }
858             client_set_wm_state(client, e->xclient.data.l[0]);
859         } else if (msgtype == prop_atoms.net_wm_desktop) {
860             /* compress changes into a single change */
861             while (XCheckTypedWindowEvent(ob_display, client->window,
862                                           e->type, &ce)) {
863                 /* XXX: it would be nice to compress ALL messages of a
864                    type, not just messages in a row without other
865                    message types between. */
866                 if (ce.xclient.message_type != msgtype) {
867                     XPutBackEvent(ob_display, &ce);
868                     break;
869                 }
870                 e->xclient = ce.xclient;
871             }
872             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
873                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
874                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
875                                    FALSE);
876         } else if (msgtype == prop_atoms.net_wm_state) {
877             /* can't compress these */
878             ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
879                      (e->xclient.data.l[0] == 0 ? "Remove" :
880                       e->xclient.data.l[0] == 1 ? "Add" :
881                       e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
882                      e->xclient.data.l[1], e->xclient.data.l[2],
883                      client->window);
884             client_set_state(client, e->xclient.data.l[0],
885                              e->xclient.data.l[1], e->xclient.data.l[2]);
886         } else if (msgtype == prop_atoms.net_close_window) {
887             ob_debug("net_close_window for 0x%lx\n", client->window);
888             client_close(client);
889         } else if (msgtype == prop_atoms.net_active_window) {
890             ob_debug("net_active_window for 0x%lx\n", client->window);
891             client_activate(client, FALSE);
892         } else if (msgtype == prop_atoms.net_wm_moveresize) {
893             ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
894             if ((Atom)e->xclient.data.l[2] ==
895                 prop_atoms.net_wm_moveresize_size_topleft ||
896                 (Atom)e->xclient.data.l[2] ==
897                 prop_atoms.net_wm_moveresize_size_top ||
898                 (Atom)e->xclient.data.l[2] ==
899                 prop_atoms.net_wm_moveresize_size_topright ||
900                 (Atom)e->xclient.data.l[2] ==
901                 prop_atoms.net_wm_moveresize_size_right ||
902                 (Atom)e->xclient.data.l[2] ==
903                 prop_atoms.net_wm_moveresize_size_right ||
904                 (Atom)e->xclient.data.l[2] ==
905                 prop_atoms.net_wm_moveresize_size_bottomright ||
906                 (Atom)e->xclient.data.l[2] ==
907                 prop_atoms.net_wm_moveresize_size_bottom ||
908                 (Atom)e->xclient.data.l[2] ==
909                 prop_atoms.net_wm_moveresize_size_bottomleft ||
910                 (Atom)e->xclient.data.l[2] ==
911                 prop_atoms.net_wm_moveresize_size_left ||
912                 (Atom)e->xclient.data.l[2] ==
913                 prop_atoms.net_wm_moveresize_move ||
914                 (Atom)e->xclient.data.l[2] ==
915                 prop_atoms.net_wm_moveresize_size_keyboard ||
916                 (Atom)e->xclient.data.l[2] ==
917                 prop_atoms.net_wm_moveresize_move_keyboard) {
918
919                 moveresize_start(client, e->xclient.data.l[0],
920                                  e->xclient.data.l[1], e->xclient.data.l[3],
921                                  e->xclient.data.l[2]);
922             }
923         } else if (msgtype == prop_atoms.net_moveresize_window) {
924             int oldg = client->gravity;
925             int tmpg, x, y, w, h;
926
927             if (e->xclient.data.l[0] & 0xff)
928                 tmpg = e->xclient.data.l[0] & 0xff;
929             else
930                 tmpg = oldg;
931
932             if (e->xclient.data.l[0] & 1 << 8)
933                 x = e->xclient.data.l[1];
934             else
935                 x = client->area.x;
936             if (e->xclient.data.l[0] & 1 << 9)
937                 y = e->xclient.data.l[2];
938             else
939                 y = client->area.y;
940             if (e->xclient.data.l[0] & 1 << 10)
941                 w = e->xclient.data.l[3];
942             else
943                 w = client->area.width;
944             if (e->xclient.data.l[0] & 1 << 11)
945                 h = e->xclient.data.l[4];
946             else
947                 h = client->area.height;
948             client->gravity = tmpg;
949
950             {
951                 int newx = x;
952                 int newy = y;
953                 int fw = w +
954                     client->frame->size.left + client->frame->size.right;
955                 int fh = h +
956                     client->frame->size.top + client->frame->size.bottom;
957                 client_find_onscreen(client, &newx, &newy, fw, fh,
958                                      client_normal(client));
959                 if (e->xclient.data.l[0] & 1 << 8)
960                     x = newx;
961                 if (e->xclient.data.l[0] & 1 << 9)
962                     y = newy;
963             }
964                
965             client_configure(client, OB_CORNER_TOPLEFT,
966                              x, y, w, h, FALSE, TRUE);
967
968             client->gravity = oldg;
969         }
970         break;
971     case PropertyNotify:
972         /* validate cuz we query stuff off the client here */
973         if (!client_validate(client)) break;
974   
975         /* compress changes to a single property into a single change */
976         while (XCheckTypedWindowEvent(ob_display, client->window,
977                                       e->type, &ce)) {
978             Atom a, b;
979
980             /* XXX: it would be nice to compress ALL changes to a property,
981                not just changes in a row without other props between. */
982
983             a = ce.xproperty.atom;
984             b = e->xproperty.atom;
985
986             if (a == b)
987                 continue;
988             if ((a == prop_atoms.net_wm_name ||
989                  a == prop_atoms.wm_name ||
990                  a == prop_atoms.net_wm_icon_name ||
991                  a == prop_atoms.wm_icon_name)
992                 &&
993                 (b == prop_atoms.net_wm_name ||
994                  b == prop_atoms.wm_name ||
995                  b == prop_atoms.net_wm_icon_name ||
996                  b == prop_atoms.wm_icon_name)) {
997                 continue;
998             }
999             if ((a == prop_atoms.net_wm_icon ||
1000                  a == prop_atoms.kwm_win_icon)
1001                 &&
1002                 (b == prop_atoms.net_wm_icon ||
1003                  b == prop_atoms.kwm_win_icon))
1004                 continue;
1005
1006             XPutBackEvent(ob_display, &ce);
1007             break;
1008         }
1009
1010         msgtype = e->xproperty.atom;
1011         if (msgtype == XA_WM_NORMAL_HINTS) {
1012             client_update_normal_hints(client);
1013             /* normal hints can make a window non-resizable */
1014             client_setup_decor_and_functions(client);
1015         } else if (msgtype == XA_WM_HINTS) {
1016             client_update_wmhints(client);
1017         } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1018             client_update_transient_for(client);
1019             client_get_type(client);
1020             /* type may have changed, so update the layer */
1021             client_calc_layer(client);
1022             client_setup_decor_and_functions(client);
1023         } else if (msgtype == prop_atoms.net_wm_name ||
1024                    msgtype == prop_atoms.wm_name ||
1025                    msgtype == prop_atoms.net_wm_icon_name ||
1026                    msgtype == prop_atoms.wm_icon_name) {
1027             client_update_title(client);
1028         } else if (msgtype == prop_atoms.wm_class) {
1029             client_update_class(client);
1030         } else if (msgtype == prop_atoms.wm_protocols) {
1031             client_update_protocols(client);
1032             client_setup_decor_and_functions(client);
1033         }
1034         else if (msgtype == prop_atoms.net_wm_strut) {
1035             client_update_strut(client);
1036         }
1037         else if (msgtype == prop_atoms.net_wm_icon ||
1038                  msgtype == prop_atoms.kwm_win_icon) {
1039             client_update_icons(client);
1040         }
1041         else if (msgtype == prop_atoms.sm_client_id) {
1042             client_update_sm_client_id(client);
1043         }
1044     default:
1045         ;
1046 #ifdef SHAPE
1047         if (extensions_shape && e->type == extensions_shape_event_basep) {
1048             client->shaped = ((XShapeEvent*)e)->shaped;
1049             frame_adjust_shape(client->frame);
1050         }
1051 #endif
1052     }
1053 }
1054
1055 static void event_handle_dock(ObDock *s, XEvent *e)
1056 {
1057     switch (e->type) {
1058     case ButtonPress:
1059         stacking_raise(DOCK_AS_WINDOW(s));
1060         break;
1061     case EnterNotify:
1062         dock_hide(FALSE);
1063         break;
1064     case LeaveNotify:
1065         dock_hide(TRUE);
1066         break;
1067     }
1068 }
1069
1070 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1071 {
1072     switch (e->type) {
1073     case MotionNotify:
1074         dock_app_drag(app, &e->xmotion);
1075         break;
1076     case UnmapNotify:
1077         if (app->ignore_unmaps) {
1078             app->ignore_unmaps--;
1079             break;
1080         }
1081         dock_remove(app, TRUE);
1082         break;
1083     case DestroyNotify:
1084         dock_remove(app, FALSE);
1085         break;
1086     case ReparentNotify:
1087         dock_remove(app, FALSE);
1088         break;
1089     case ConfigureNotify:
1090         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1091         break;
1092     }
1093 }
1094
1095 ObMenuFrame* find_active_menu()
1096 {
1097     GList *it;
1098     ObMenuFrame *ret = NULL;
1099
1100     for (it = menu_frame_visible; it; it = g_list_next(it)) {
1101         ret = it->data;
1102         if (ret->selected)
1103             break;
1104         ret = NULL;
1105     }
1106     return ret;
1107 }
1108
1109 ObMenuFrame* find_active_or_last_menu()
1110 {
1111     ObMenuFrame *ret = NULL;
1112
1113     ret = find_active_menu();
1114     if (!ret && menu_frame_visible)
1115         ret = menu_frame_visible->data;
1116     return ret;
1117 }
1118
1119 static void event_handle_menu(XEvent *ev)
1120 {
1121     ObMenuFrame *f;
1122     ObMenuEntryFrame *e;
1123
1124     switch (ev->type) {
1125     case ButtonRelease:
1126         if (menu_can_hide) {
1127             if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1128                                             ev->xbutton.y_root)))
1129                 menu_entry_frame_execute(e, ev->xbutton.state);
1130             else
1131                 menu_frame_hide_all();
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         {
1143             ObMenuFrame *a;
1144
1145             a = find_active_menu();
1146             if (a && a != f &&
1147                 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1148             {
1149                 menu_frame_select(a, NULL);
1150             }
1151         }
1152         break;
1153     case KeyPress:
1154         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1155             menu_frame_hide_all();
1156         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1157             ObMenuFrame *f;
1158             if ((f = find_active_menu()))
1159                 menu_entry_frame_execute(f->selected, ev->xkey.state);
1160         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1161             ObMenuFrame *f;
1162             if ((f = find_active_or_last_menu()) && f->parent)
1163                 menu_frame_select(f, NULL);
1164         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1165             ObMenuFrame *f;
1166             if ((f = find_active_or_last_menu()) && f->child)
1167                 menu_frame_select_next(f->child);
1168         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1169             ObMenuFrame *f;
1170             if ((f = find_active_or_last_menu()))
1171                 menu_frame_select_previous(f);
1172         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1173             ObMenuFrame *f;
1174             if ((f = find_active_or_last_menu()))
1175                 menu_frame_select_next(f);
1176         }
1177         break;
1178     }
1179 }
1180
1181 static gboolean menu_hide_delay_func(gpointer data)
1182 {
1183     menu_can_hide = TRUE;
1184     return FALSE; /* no repeat */
1185 }
1186
1187 static gboolean focus_delay_func(gpointer data)
1188 {
1189     ObClient *c = data;
1190
1191     if (focus_client != c) {
1192         client_focus(c);
1193         if (config_focus_raise)
1194             client_raise(c);
1195     }
1196     return FALSE; /* no repeat */
1197 }
1198
1199 static void focus_delay_client_dest(ObClient *client, gpointer data)
1200 {
1201     ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1202 }
1203
1204 void event_ignore_queued_enters()
1205 {
1206     GSList *saved = NULL, *it;
1207     XEvent *e;
1208                 
1209     XSync(ob_display, FALSE);
1210
1211     /* count the events */
1212     while (TRUE) {
1213         e = g_new(XEvent, 1);
1214         if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1215             ObWindow *win;
1216             
1217             win = g_hash_table_lookup(window_map, &e->xany.window);
1218             if (win && WINDOW_IS_CLIENT(win))
1219                 ++ignore_enter_focus;
1220             
1221             saved = g_slist_append(saved, e);
1222         } else {
1223             g_free(e);
1224             break;
1225         }
1226     }
1227     /* put the events back */
1228     for (it = saved; it; it = g_slist_next(it)) {
1229         XPutBackEvent(ob_display, it->data);
1230         g_free(it->data);
1231     }
1232     g_slist_free(saved);
1233 }