]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/event.c
dont refocus the focused window, that just causes so many problems (with alt-tab :)
[mikachu/openbox.git] / openbox / event.c
1 #include "openbox.h"
2 #include "dock.h"
3 #include "client.h"
4 #include "xerror.h"
5 #include "prop.h"
6 #include "config.h"
7 #include "screen.h"
8 #include "frame.h"
9 #include "menu.h"
10 #include "framerender.h"
11 #include "focus.h"
12 #include "moveresize.h"
13 #include "stacking.h"
14 #include "extensions.h"
15 #include "timer.h"
16 #include "dispatch.h"
17 #include "event.h"
18
19 #include <X11/Xlib.h>
20 #include <X11/keysym.h>
21 #include <X11/Xatom.h>
22 #include <glib.h>
23
24 #ifdef USE_LIBSN
25 #  include <libsn/sn.h>
26 #endif
27
28 #ifdef HAVE_SYS_SELECT_H
29 #  include <sys/select.h>
30 #endif
31
32 static void event_process(XEvent *e);
33 static void event_handle_root(XEvent *e);
34 static void event_handle_dock(Dock *s, XEvent *e);
35 static void event_handle_dockapp(DockApp *app, XEvent *e);
36 static void event_handle_client(Client *c, XEvent *e);
37 static void event_handle_menu(Menu *menu, Client *c, XEvent *e);
38
39 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
40                             (e)->xfocus.detail == NotifyAncestor || \
41                             (e)->xfocus.detail > NotifyNonlinearVirtual)
42 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
43                              (e)->xfocus.detail == NotifyInferior || \
44                              (e)->xfocus.detail == NotifyAncestor || \
45                              (e)->xfocus.detail > NotifyNonlinearVirtual)
46
47 Time event_lasttime = 0;
48
49 /*! The value of the mask for the NumLock modifier */
50 unsigned int NumLockMask;
51 /*! The value of the mask for the ScrollLock modifier */
52 unsigned int ScrollLockMask;
53 /*! The key codes for the modifier keys */
54 static XModifierKeymap *modmap;
55 /*! Table of the constant modifier masks */
56 static const int mask_table[] = {
57     ShiftMask, LockMask, ControlMask, Mod1Mask,
58     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
59 };
60 static int mask_table_size;
61
62 static fd_set selset, allset;
63 static int max_fd, x_fd;
64 static GData *fd_handler_list;
65
66 void fd_event_handle();
67
68 void event_startup()
69 {
70     mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
71      
72     /* get lock masks that are defined by the display (not constant) */
73     modmap = XGetModifierMapping(ob_display);
74     g_assert(modmap);
75     if (modmap && modmap->max_keypermod > 0) {
76         size_t cnt;
77         const size_t size = mask_table_size * modmap->max_keypermod;
78         /* get the values of the keyboard lock modifiers
79            Note: Caps lock is not retrieved the same way as Scroll and Num
80            lock since it doesn't need to be. */
81         const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
82         const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
83                                                      XK_Scroll_Lock);
84           
85         for (cnt = 0; cnt < size; ++cnt) {
86             if (! modmap->modifiermap[cnt]) continue;
87                
88             if (num_lock == modmap->modifiermap[cnt])
89                 NumLockMask = mask_table[cnt / modmap->max_keypermod];
90             if (scroll_lock == modmap->modifiermap[cnt])
91                 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
92         }
93     }
94
95     FD_ZERO(&allset);
96     max_fd = x_fd = ConnectionNumber(ob_display);
97     FD_SET(x_fd, &allset);
98     g_datalist_init(&fd_handler_list);
99 }
100
101 void event_shutdown()
102 {
103     XFreeModifiermap(modmap);
104     g_datalist_clear(&fd_handler_list);
105 }
106
107 void event_loop()
108 {
109     XEvent e;
110     struct timeval *wait;
111     gboolean had_event = FALSE;
112
113     while (TRUE) {
114         /*
115           There are slightly different event retrieval semantics here for
116           local (or high bandwidth) versus remote (or low bandwidth)
117           connections to the display/Xserver.
118         */
119         if (ob_remote) {
120             if (!XPending(ob_display))
121                 break;
122         } else {
123             /*
124               This XSync allows for far more compression of events, which
125               makes things like Motion events perform far far better. Since
126               it also means network traffic for every event instead of every
127               X events (where X is the number retrieved at a time), it
128               probably should not be used for setups where Openbox is
129               running on a remote/low bandwidth display/Xserver.
130             */
131             XSync(ob_display, FALSE);
132             if (!XEventsQueued(ob_display, QueuedAlready))
133                 break;
134         }
135         XNextEvent(ob_display, &e);
136
137 #ifdef USE_LIBSN
138         sn_display_process_event(ob_sn_display, &e);
139 #endif
140
141         event_process(&e);
142         had_event = TRUE;
143     }
144
145     if (!had_event) {
146         timer_dispatch((GTimeVal**)&wait);
147         selset = allset;
148         select(max_fd + 1, &selset, NULL, NULL, wait);
149
150         /* handle the X events as soon as possible? */
151         if (FD_ISSET(x_fd, &selset))
152             return;
153
154         fd_event_handle();
155     }
156 }
157
158 static Window event_get_window(XEvent *e)
159 {
160     Window window;
161
162     /* pick a window */
163     switch (e->type) {
164     case MapRequest:
165         window = e->xmap.window;
166         break;
167     case UnmapNotify:
168         window = e->xunmap.window;
169         break;
170     case DestroyNotify:
171         window = e->xdestroywindow.window;
172         break;
173     case ConfigureRequest:
174         window = e->xconfigurerequest.window;
175         break;
176     case ConfigureNotify:
177         window = e->xconfigure.window;
178         break;
179     default:
180 #ifdef XKB
181         if (extensions_xkb && e->type == extensions_xkb_event_basep) {
182             switch (((XkbAnyEvent*)&e)->xkb_type) {
183             case XkbBellNotify:
184                 window = ((XkbBellNotifyEvent*)&e)->window;
185             default:
186                 window = None;
187             }
188         } else
189 #endif
190             window = e->xany.window;
191     }
192     return window;
193 }
194
195 static void event_set_lasttime(XEvent *e)
196 {
197     /* grab the lasttime and hack up the state */
198     switch (e->type) {
199     case ButtonPress:
200     case ButtonRelease:
201         event_lasttime = e->xbutton.time;
202         break;
203     case KeyPress:
204         event_lasttime = e->xkey.time;
205         break;
206     case KeyRelease:
207         event_lasttime = e->xkey.time;
208         break;
209     case MotionNotify:
210         event_lasttime = e->xmotion.time;
211         break;
212     case PropertyNotify:
213         event_lasttime = e->xproperty.time;
214         break;
215     case EnterNotify:
216     case LeaveNotify:
217         event_lasttime = e->xcrossing.time;
218         break;
219     default:
220         event_lasttime = CurrentTime;
221         break;
222     }
223 }
224
225 #define STRIP_MODS(s) \
226         s &= ~(LockMask | NumLockMask | ScrollLockMask), \
227         /* kill off the Button1Mask etc, only want the modifiers */ \
228         s &= (ControlMask | ShiftMask | Mod1Mask | \
229               Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
230
231 static void event_hack_mods(XEvent *e)
232 {
233     KeyCode *kp;
234     int i, k;
235
236     switch (e->type) {
237     case ButtonPress:
238     case ButtonRelease:
239         STRIP_MODS(e->xbutton.state);
240         break;
241     case KeyPress:
242         STRIP_MODS(e->xkey.state);
243         break;
244     case KeyRelease:
245         STRIP_MODS(e->xkey.state);
246         /* remove from the state the mask of the modifier being released, if
247            it is a modifier key being released (this is a little ugly..) */
248         kp = modmap->modifiermap;
249         for (i = 0; i < mask_table_size; ++i) {
250             for (k = 0; k < modmap->max_keypermod; ++k) {
251                 if (*kp == e->xkey.keycode) { /* found the keycode */
252                     /* remove the mask for it */
253                     e->xkey.state &= ~mask_table[i];
254                     /* cause the first loop to break; */
255                     i = mask_table_size;
256                     break; /* get outta here! */
257                 }
258                 ++kp;
259             }
260         }
261         break;
262     case MotionNotify:
263         STRIP_MODS(e->xmotion.state);
264         /* compress events */
265         {
266             XEvent ce;
267             while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
268                                           e->type, &ce)) {
269                 e->xmotion.x_root = ce.xmotion.x_root;
270                 e->xmotion.y_root = ce.xmotion.y_root;
271             }
272         }
273         break;
274     }
275 }
276
277 static gboolean event_ignore(XEvent *e, Client *client)
278 {
279     switch(e->type) {
280     case FocusIn:
281         /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
282            because of RevertToPointerRoot. If the focus ends up reverting to
283            pointer root on a workspace change, then the FocusIn event that we
284            want will be of type NotifyAncestor. This situation does not occur
285            for FocusOut, so it is safely ignored there.
286         */
287         if (INVALID_FOCUSIN(e) ||
288             client == NULL) {
289 #ifdef DEBUG_FOCUS
290         g_message("FocusIn on %lx mode %d detail %d IGNORED", e->xfocus.window,
291                   e->xfocus.mode, e->xfocus.detail);
292 #endif
293             /* says a client was not found for the event (or a valid FocusIn
294                event was not found.
295             */
296             e->xfocus.window = None;
297             return TRUE;
298         }
299
300 #ifdef DEBUG_FOCUS
301         g_message("FocusIn on %lx mode %d detail %d", e->xfocus.window,
302                   e->xfocus.mode, e->xfocus.detail);
303 #endif
304         break;
305     case FocusOut:
306         if (INVALID_FOCUSOUT(e)) {
307 #ifdef DEBUG_FOCUS
308         g_message("FocusOut on %lx mode %d detail %d IGNORED",
309                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
310 #endif
311             return TRUE;
312         }
313
314 #ifdef DEBUG_FOCUS
315         g_message("FocusOut on %lx mode %d detail %d",
316                   e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
317 #endif
318
319         {
320             XEvent fe;
321             gboolean fallback = TRUE;
322
323             while (TRUE) {
324                 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
325                                             e->xfocus.window,&fe))
326                     if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
327                         break;
328                 if (fe.type == FocusOut) {
329 #ifdef DEBUG_FOCUS
330                     g_message("found pending FocusOut");
331 #endif
332                     if (!INVALID_FOCUSOUT(&fe)) {
333                         /* if there is a VALID FocusOut still coming, don't
334                            fallback focus yet, we'll deal with it then */
335                         XPutBackEvent(ob_display, &fe);
336                         fallback = FALSE;
337                         break;
338                     }
339                 } else {
340 #ifdef DEBUG_FOCUS
341                     g_message("found pending FocusIn");
342 #endif
343                     /* is the focused window getting a FocusOut/In back to
344                        itself? */
345                     if (fe.xfocus.window == e->xfocus.window &&
346                         !event_ignore(&fe, client)) {
347 #ifdef DEBUG_FOCUS
348                         g_message("focused window got an Out/In back to "
349                                   "itself IGNORED both");
350 #endif
351                         return TRUE;
352                     }
353
354                     /* once all the FocusOut's have been dealt with, if there
355                        is a FocusIn still left and it is valid, then use it */
356                     event_process(&fe);
357                     /* secret magic way of event_process telling us that no
358                        client was found for the FocusIn event. ^_^ */
359                     if (fe.xfocus.window != None) {
360                         fallback = FALSE;
361                         break;
362                     }
363                 }
364             }
365             if (fallback) {
366 #ifdef DEBUG_FOCUS
367                 g_message("no valid FocusIn and no FocusOut events found, "
368                           "falling back");
369 #endif
370                 focus_fallback(Fallback_NoFocus);
371             }
372         }
373         break;
374     case EnterNotify:
375     case LeaveNotify:
376         /* NotifyUngrab occurs when a mouse button is released and the event is
377            caused, like when lowering a window */
378         /* NotifyVirtual occurs when ungrabbing the pointer */
379         if (e->xcrossing.mode == NotifyGrab ||
380             e->xcrossing.detail == NotifyInferior ||
381             (e->xcrossing.mode == NotifyUngrab &&
382              e->xcrossing.detail == NotifyVirtual)) {
383 #ifdef DEBUG_FOCUS
384             g_message("%sNotify mode %d detail %d on %lx IGNORED",
385                       (e->type == EnterNotify ? "Enter" : "Leave"),
386                       e->xcrossing.mode,
387                       e->xcrossing.detail, client?client->window:0);
388 #endif
389             return TRUE;
390         }
391 #ifdef DEBUG_FOCUS
392         g_message("%sNotify mode %d detail %d on %lx",
393                   (e->type == EnterNotify ? "Enter" : "Leave"),
394                   e->xcrossing.mode,
395                   e->xcrossing.detail, client?client->window:0);
396 #endif
397         break;
398     }
399     return FALSE;
400 }
401
402 static void event_process(XEvent *e)
403 {
404     Window window;
405     Client *client = NULL;
406     Dock *dock = NULL;
407     DockApp *dockapp = NULL;
408     Menu *menu = NULL;
409     ObWindow *obwin = NULL;
410
411     window = event_get_window(e);
412     if ((obwin = g_hash_table_lookup(window_map, &window))) {
413         switch (obwin->type) {
414         case Window_Dock:
415             dock = WINDOW_AS_DOCK(obwin);
416             break;
417         case Window_DockApp:
418             dockapp = WINDOW_AS_DOCKAPP(obwin);
419             break;
420         case Window_Menu:
421             menu = WINDOW_AS_MENU(obwin);
422             break;
423         case Window_Client:
424             client = WINDOW_AS_CLIENT(obwin);
425             break;
426         case Window_Internal:
427             /* not to be used for events */
428             g_assert_not_reached();
429             break;
430         }
431     }
432
433     event_set_lasttime(e);
434     event_hack_mods(e);
435     if (event_ignore(e, client))
436         return;
437
438     /* deal with it in the kernel */
439     if (menu) {
440         event_handle_menu(menu, client, e);
441         return;
442     } else if (client)
443         event_handle_client(client, e);
444     else if (dockapp)
445         event_handle_dockapp(dockapp, e);
446     else if (dock)
447         event_handle_dock(dock, e);
448     else if (window == ob_root)
449         event_handle_root(e);
450     else if (e->type == MapRequest)
451         client_manage(window);
452     else if (e->type == ConfigureRequest) {
453         /* unhandled configure requests must be used to configure the
454            window directly */
455         XWindowChanges xwc;
456                
457         xwc.x = e->xconfigurerequest.x;
458         xwc.y = e->xconfigurerequest.y;
459         xwc.width = e->xconfigurerequest.width;
460         xwc.height = e->xconfigurerequest.height;
461         xwc.border_width = e->xconfigurerequest.border_width;
462         xwc.sibling = e->xconfigurerequest.above;
463         xwc.stack_mode = e->xconfigurerequest.detail;
464        
465         /* we are not to be held responsible if someone sends us an
466            invalid request! */
467         xerror_set_ignore(TRUE);
468         XConfigureWindow(ob_display, window,
469                          e->xconfigurerequest.value_mask, &xwc);
470         xerror_set_ignore(FALSE);
471     }
472
473     if (moveresize_in_progress)
474         if (e->type == MotionNotify || e->type == ButtonRelease ||
475             e->type == ButtonPress ||
476             e->type == KeyPress || e->type == KeyRelease) {
477             moveresize_event(e);
478
479             return; /* no dispatch! */
480             
481         }
482
483     /* user input (action-bound) events */
484     /*
485     if (e->type == ButtonPress || e->type == ButtonRelease ||
486         e->type == MotionNotify)
487         mouse_event(e, client);
488     else if (e->type == KeyPress || e->type == KeyRelease)
489         ;
490     */
491
492     /* dispatch the event to registered handlers */
493     dispatch_x(e, client);
494 }
495
496 static void event_handle_root(XEvent *e)
497 {
498     Atom msgtype;
499      
500     switch(e->type) {
501     case ClientMessage:
502         if (e->xclient.format != 32) break;
503
504         msgtype = e->xclient.message_type;
505         if (msgtype == prop_atoms.net_current_desktop) {
506             unsigned int d = e->xclient.data.l[0];
507             if (d < screen_num_desktops)
508                 screen_set_desktop(d);
509         } else if (msgtype == prop_atoms.net_number_of_desktops) {
510             unsigned int d = e->xclient.data.l[0];
511             if (d > 0)
512                 screen_set_num_desktops(d);
513         } else if (msgtype == prop_atoms.net_showing_desktop) {
514             screen_show_desktop(e->xclient.data.l[0] != 0);
515         }
516         break;
517     case PropertyNotify:
518         if (e->xproperty.atom == prop_atoms.net_desktop_names)
519             screen_update_desktop_names();
520         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
521             screen_update_layout();
522         break;
523     case ConfigureNotify:
524 #ifdef XRANDR
525         XRRUpdateConfiguration(e);
526 #endif
527         if (e->xconfigure.width != screen_physical_size.width ||
528             e->xconfigure.height != screen_physical_size.height)
529             screen_resize(e->xconfigure.width, e->xconfigure.height);
530         break;
531     default:
532         ;
533 #ifdef VIDMODE
534         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
535             g_message("VIDMODE EVENT");
536         }
537 #endif
538     }
539 }
540
541 static void event_handle_client(Client *client, XEvent *e)
542 {
543     XEvent ce;
544     Atom msgtype;
545     int i=0;
546      
547     switch (e->type) {
548     case ButtonPress:
549     case ButtonRelease:
550         /* Wheel buttons don't draw because they are an instant click, so it
551            is a waste of resources to go drawing it. */
552         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
553             switch (frame_context(client, e->xbutton.window)) {
554             case Context_Maximize:
555                 client->frame->max_press = (e->type == ButtonPress);
556                 framerender_frame(client->frame);
557                 break;
558             case Context_Close:
559                 client->frame->close_press = (e->type == ButtonPress);
560                 framerender_frame(client->frame);
561                 break;
562             case Context_Iconify:
563                 client->frame->iconify_press = (e->type == ButtonPress);
564                 framerender_frame(client->frame);
565                 break;
566             case Context_AllDesktops:
567                 client->frame->desk_press = (e->type == ButtonPress);
568                 framerender_frame(client->frame);
569                 break; 
570             case Context_Shade:
571                 client->frame->shade_press = (e->type == ButtonPress);
572                 framerender_frame(client->frame);
573                 break;
574             default:
575                 /* nothing changes with clicks for any other contexts */
576                 break;
577             }
578         }
579         break;
580     case FocusIn:
581 #ifdef DEBUG_FOCUS
582         g_message("FocusIn on client for %lx", client->window);
583 #endif
584         if (client != focus_client) {
585             focus_set_client(client);
586             frame_adjust_focus(client->frame, TRUE);
587         }
588         break;
589     case FocusOut:
590 #ifdef DEBUG_FOCUS
591         g_message("FocusOut on client for %lx", client->window);
592 #endif
593         /* are we a fullscreen window or a transient of one? (checks layer)
594            if we are then we need to be iconified since we are losing focus
595          */
596         if (client->layer == Layer_Fullscreen && !client->iconic &&
597             !client_search_focus_tree_full(client))
598             /* iconify fullscreen windows when they and their transients
599                aren't focused */
600             client_iconify(client, TRUE, TRUE);
601         frame_adjust_focus(client->frame, FALSE);
602         break;
603     case EnterNotify:
604         if (client_normal(client)) {
605             if (ob_state == State_Starting) {
606                 /* move it to the top of the focus order */
607                 guint desktop = client->desktop;
608                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
609                 focus_order[desktop] = g_list_remove(focus_order[desktop],
610                                                      client);
611                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
612                                                       client);
613             } else if (config_focus_follow) {
614 #ifdef DEBUG_FOCUS
615                 g_message("EnterNotify on %lx, focusing window",
616                           client->window);
617 #endif
618                 client_focus(client);
619             }
620         }
621         break;
622     case ConfigureRequest:
623         /* compress these */
624         while (XCheckTypedWindowEvent(ob_display, client->window,
625                                       ConfigureRequest, &ce)) {
626             ++i;
627             /* XXX if this causes bad things.. we can compress config req's
628                with the same mask. */
629             e->xconfigurerequest.value_mask |=
630                 ce.xconfigurerequest.value_mask;
631             if (ce.xconfigurerequest.value_mask & CWX)
632                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
633             if (ce.xconfigurerequest.value_mask & CWY)
634                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
635             if (ce.xconfigurerequest.value_mask & CWWidth)
636                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
637             if (ce.xconfigurerequest.value_mask & CWHeight)
638                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
639             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
640                 e->xconfigurerequest.border_width =
641                     ce.xconfigurerequest.border_width;
642             if (ce.xconfigurerequest.value_mask & CWStackMode)
643                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
644         }
645
646         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
647         if (client->iconic || client->shaded) return;
648
649         if (e->xconfigurerequest.value_mask & CWBorderWidth)
650             client->border_width = e->xconfigurerequest.border_width;
651
652         /* resize, then move, as specified in the EWMH section 7.7 */
653         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
654                                                CWX | CWY)) {
655             int x, y, w, h;
656             Corner corner;
657                
658             x = (e->xconfigurerequest.value_mask & CWX) ?
659                 e->xconfigurerequest.x : client->area.x;
660             y = (e->xconfigurerequest.value_mask & CWY) ?
661                 e->xconfigurerequest.y : client->area.y;
662             w = (e->xconfigurerequest.value_mask & CWWidth) ?
663                 e->xconfigurerequest.width : client->area.width;
664             h = (e->xconfigurerequest.value_mask & CWHeight) ?
665                 e->xconfigurerequest.height : client->area.height;
666                
667             switch (client->gravity) {
668             case NorthEastGravity:
669             case EastGravity:
670                 corner = Corner_TopRight;
671                 break;
672             case SouthWestGravity:
673             case SouthGravity:
674                 corner = Corner_BottomLeft;
675                 break;
676             case SouthEastGravity:
677                 corner = Corner_BottomRight;
678                 break;
679             default:     /* NorthWest, Static, etc */
680                 corner = Corner_TopLeft;
681             }
682
683             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
684         }
685
686         if (e->xconfigurerequest.value_mask & CWStackMode) {
687             switch (e->xconfigurerequest.detail) {
688             case Below:
689             case BottomIf:
690                 stacking_lower(CLIENT_AS_WINDOW(client));
691                 break;
692
693             case Above:
694             case TopIf:
695             default:
696                 stacking_raise(CLIENT_AS_WINDOW(client));
697                 break;
698             }
699         }
700         break;
701     case UnmapNotify:
702         if (client->ignore_unmaps) {
703             client->ignore_unmaps--;
704             break;
705         }
706         client_unmanage(client);
707         break;
708     case DestroyNotify:
709         client_unmanage(client);
710         break;
711     case ReparentNotify:
712         /* this is when the client is first taken captive in the frame */
713         if (e->xreparent.parent == client->frame->plate) break;
714
715         /*
716           This event is quite rare and is usually handled in unmapHandler.
717           However, if the window is unmapped when the reparent event occurs,
718           the window manager never sees it because an unmap event is not sent
719           to an already unmapped window.
720         */
721
722         /* we don't want the reparent event, put it back on the stack for the
723            X server to deal with after we unmanage the window */
724         XPutBackEvent(ob_display, e);
725      
726         client_unmanage(client);
727         break;
728     case MapRequest:
729         g_message("MapRequest for 0x%lx", client->window);
730         if (!client->iconic) break; /* this normally doesn't happen, but if it
731                                        does, we don't want it! */
732         if (screen_showing_desktop)
733             screen_show_desktop(FALSE);
734         client_iconify(client, FALSE, TRUE);
735         if (!client->frame->visible)
736             /* if its not visible still, then don't mess with it */
737             break;
738         if (client->shaded)
739             client_shade(client, FALSE);
740         client_focus(client);
741         stacking_raise(CLIENT_AS_WINDOW(client));
742         break;
743     case ClientMessage:
744         /* validate cuz we query stuff off the client here */
745         if (!client_validate(client)) break;
746   
747         if (e->xclient.format != 32) return;
748
749         msgtype = e->xclient.message_type;
750         if (msgtype == prop_atoms.wm_change_state) {
751             /* compress changes into a single change */
752             while (XCheckTypedWindowEvent(ob_display, e->type,
753                                           client->window, &ce)) {
754                 /* XXX: it would be nice to compress ALL messages of a
755                    type, not just messages in a row without other
756                    message types between. */
757                 if (ce.xclient.message_type != msgtype) {
758                     XPutBackEvent(ob_display, &ce);
759                     break;
760                 }
761                 e->xclient = ce.xclient;
762             }
763             client_set_wm_state(client, e->xclient.data.l[0]);
764         } else if (msgtype == prop_atoms.net_wm_desktop) {
765             /* compress changes into a single change */
766             while (XCheckTypedWindowEvent(ob_display, e->type,
767                                           client->window, &ce)) {
768                 /* XXX: it would be nice to compress ALL messages of a
769                    type, not just messages in a row without other
770                    message types between. */
771                 if (ce.xclient.message_type != msgtype) {
772                     XPutBackEvent(ob_display, &ce);
773                     break;
774                 }
775                 e->xclient = ce.xclient;
776             }
777             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
778                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
779                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
780                                    FALSE);
781         } else if (msgtype == prop_atoms.net_wm_state) {
782             /* can't compress these */
783             g_message("net_wm_state %s %ld %ld for 0x%lx",
784                       (e->xclient.data.l[0] == 0 ? "Remove" :
785                        e->xclient.data.l[0] == 1 ? "Add" :
786                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
787                       e->xclient.data.l[1], e->xclient.data.l[2],
788                       client->window);
789             client_set_state(client, e->xclient.data.l[0],
790                              e->xclient.data.l[1], e->xclient.data.l[2]);
791         } else if (msgtype == prop_atoms.net_close_window) {
792             g_message("net_close_window for 0x%lx", client->window);
793             client_close(client);
794         } else if (msgtype == prop_atoms.net_active_window) {
795             g_message("net_active_window for 0x%lx", client->window);
796             client_activate(client);
797         } else if (msgtype == prop_atoms.net_wm_moveresize) {
798             g_message("net_wm_moveresize for 0x%lx", client->window);
799             if ((Atom)e->xclient.data.l[2] ==
800                 prop_atoms.net_wm_moveresize_size_topleft ||
801                 (Atom)e->xclient.data.l[2] ==
802                 prop_atoms.net_wm_moveresize_size_top ||
803                 (Atom)e->xclient.data.l[2] ==
804                 prop_atoms.net_wm_moveresize_size_topright ||
805                 (Atom)e->xclient.data.l[2] ==
806                 prop_atoms.net_wm_moveresize_size_right ||
807                 (Atom)e->xclient.data.l[2] ==
808                 prop_atoms.net_wm_moveresize_size_right ||
809                 (Atom)e->xclient.data.l[2] ==
810                 prop_atoms.net_wm_moveresize_size_bottomright ||
811                 (Atom)e->xclient.data.l[2] ==
812                 prop_atoms.net_wm_moveresize_size_bottom ||
813                 (Atom)e->xclient.data.l[2] ==
814                 prop_atoms.net_wm_moveresize_size_bottomleft ||
815                 (Atom)e->xclient.data.l[2] ==
816                 prop_atoms.net_wm_moveresize_size_left ||
817                 (Atom)e->xclient.data.l[2] ==
818                 prop_atoms.net_wm_moveresize_move ||
819                 (Atom)e->xclient.data.l[2] ==
820                 prop_atoms.net_wm_moveresize_size_keyboard ||
821                 (Atom)e->xclient.data.l[2] ==
822                 prop_atoms.net_wm_moveresize_move_keyboard) {
823
824                 moveresize_start(client, e->xclient.data.l[0],
825                                  e->xclient.data.l[1], e->xclient.data.l[3],
826                                  e->xclient.data.l[2]);
827             }
828         } else if (msgtype == prop_atoms.net_moveresize_window) {
829             int oldg = client->gravity;
830             int tmpg, x, y, w, h;
831
832             if (e->xclient.data.l[0] & 0xff)
833                 tmpg = e->xclient.data.l[0] & 0xff;
834             else
835                 tmpg = oldg;
836
837             if (e->xclient.data.l[0] & 1 << 8)
838                 x = e->xclient.data.l[1];
839             else
840                 x = client->area.x;
841             if (e->xclient.data.l[0] & 1 << 9)
842                 y = e->xclient.data.l[2];
843             else
844                 y = client->area.y;
845             if (e->xclient.data.l[0] & 1 << 10)
846                 w = e->xclient.data.l[3];
847             else
848                 w = client->area.y;
849             if (e->xclient.data.l[0] & 1 << 11)
850                 h = e->xclient.data.l[4];
851             else
852                 h = client->area.y;
853             client->gravity = tmpg;
854             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
855             client->gravity = oldg;
856         }
857         break;
858     case PropertyNotify:
859         /* validate cuz we query stuff off the client here */
860         if (!client_validate(client)) break;
861   
862         /* compress changes to a single property into a single change */
863         while (XCheckTypedWindowEvent(ob_display, e->type,
864                                       client->window, &ce)) {
865             /* XXX: it would be nice to compress ALL changes to a property,
866                not just changes in a row without other props between. */
867             if (ce.xproperty.atom != e->xproperty.atom) {
868                 XPutBackEvent(ob_display, &ce);
869                 break;
870             }
871         }
872
873         msgtype = e->xproperty.atom;
874         if (msgtype == XA_WM_NORMAL_HINTS) {
875             client_update_normal_hints(client);
876             /* normal hints can make a window non-resizable */
877             client_setup_decor_and_functions(client);
878         }
879         else if (msgtype == XA_WM_HINTS)
880             client_update_wmhints(client);
881         else if (msgtype == XA_WM_TRANSIENT_FOR) {
882             client_update_transient_for(client);
883             client_get_type(client);
884             /* type may have changed, so update the layer */
885             client_calc_layer(client);
886             client_setup_decor_and_functions(client);
887         }
888         else if (msgtype == prop_atoms.net_wm_name ||
889                  msgtype == prop_atoms.wm_name ||
890                  msgtype == prop_atoms.net_wm_icon_name ||
891                  msgtype == prop_atoms.wm_icon_name)
892             client_update_title(client);
893         else if (msgtype == prop_atoms.wm_class)
894             client_update_class(client);
895         else if (msgtype == prop_atoms.wm_protocols) {
896             client_update_protocols(client);
897             client_setup_decor_and_functions(client);
898         }
899         else if (msgtype == prop_atoms.net_wm_strut)
900             client_update_strut(client);
901         else if (msgtype == prop_atoms.net_wm_icon ||
902                  msgtype == prop_atoms.kwm_win_icon)
903             client_update_icons(client);
904     default:
905         ;
906 #ifdef SHAPE
907         if (extensions_shape && e->type == extensions_shape_event_basep) {
908             client->shaped = ((XShapeEvent*)e)->shaped;
909             frame_adjust_shape(client->frame);
910         }
911 #endif
912     }
913 }
914
915 static void event_handle_menu(Menu *menu, Client *client, XEvent *e)
916 {
917     MenuEntry *entry;
918
919     g_message("EVENT %d", e->type);
920     switch (e->type) {
921     case ButtonPress:
922         g_message("BUTTON PRESS");
923         if (e->xbutton.button == 3)
924             menu_hide(menu);
925         else if (e->xbutton.button == 1) {
926             entry = menu_find_entry(menu, e->xbutton.window);
927             if (!entry)
928                 stacking_raise(MENU_AS_WINDOW(menu));
929         }
930         break;
931     case ButtonRelease:
932         g_message("BUTTON RELEASED");
933         if (!menu->shown) break;
934
935 /*        grab_pointer_window(FALSE, None, menu->frame);*/
936
937         if (e->xbutton.button == 1) {
938             entry = menu_find_entry(menu, e->xbutton.window);
939             if (entry) {
940                 int junk;
941                 Window wjunk;
942                 guint ujunk, b, w, h;
943                 XGetGeometry(ob_display, e->xbutton.window,
944                              &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
945                 if (e->xbutton.x >= (signed)-b &&
946                     e->xbutton.y >= (signed)-b &&
947                     e->xbutton.x < (signed)(w+b) &&
948                     e->xbutton.y < (signed)(h+b)) {
949                     menu_entry_fire(entry);
950                 }
951             }
952         }
953         
954         break;
955     case EnterNotify:
956     case LeaveNotify:
957         g_message("enter/leave");
958         entry = menu_find_entry(menu, e->xcrossing.window);
959         if (entry) {
960             if (menu->mouseover)
961                 menu->mouseover(entry, e->type == EnterNotify);
962             else
963                 menu_control_mouseover(entry, e->type == EnterNotify);
964             
965             menu_entry_render(entry);
966         }
967         break;
968     }
969 }
970
971 void event_add_fd_handler(event_fd_handler *h) {
972   g_datalist_id_set_data(&fd_handler_list, h->fd, h);
973   FD_SET(h->fd, &allset);
974   max_fd = MAX(max_fd, h->fd);
975 }
976
977 void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
978 {
979   *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
980 }
981
982 void event_remove_fd(int n)
983 {
984   int tmpmax = 0;
985   FD_CLR(n, &allset);
986   g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
987   g_datalist_foreach(&fd_handler_list, find_max_fd_foreach, (gpointer)&tmpmax);
988   max_fd = MAX(x_fd, tmpmax);
989 }
990
991 void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
992 {
993     if (FD_ISSET( (int)n, &selset)) {
994         event_fd_handler *h = (event_fd_handler *)data;
995         g_assert(h->fd == (int)n);
996         h->handler(h->fd, h->data);
997     }
998 }
999
1000 void fd_event_handle()
1001 {
1002     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1003 }
1004
1005 static void event_handle_dock(Dock *s, XEvent *e)
1006 {
1007     switch (e->type) {
1008     case ButtonPress:
1009         stacking_raise(DOCK_AS_WINDOW(s));
1010         break;
1011     case EnterNotify:
1012         dock_hide(FALSE);
1013         break;
1014     case LeaveNotify:
1015         dock_hide(TRUE);
1016         break;
1017     }
1018 }
1019
1020 static void event_handle_dockapp(DockApp *app, XEvent *e)
1021 {
1022     switch (e->type) {
1023     case MotionNotify:
1024         dock_app_drag(app, &e->xmotion);
1025         break;
1026     case UnmapNotify:
1027         if (app->ignore_unmaps) {
1028             app->ignore_unmaps--;
1029             break;
1030         }
1031         dock_remove(app, TRUE);
1032         break;
1033     case DestroyNotify:
1034         dock_remove(app, FALSE);
1035         break;
1036     case ReparentNotify:
1037         dock_remove(app, FALSE);
1038         break;
1039     case ConfigureNotify:
1040         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1041         break;
1042     }
1043 }