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