]> icculus.org git repositories - dana/openbox.git/blob - openbox/event.c
add ability to set a texture to None
[dana/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                     */
346                     if (fe.xfocus.window == e->xfocus.window &&
347                         !event_ignore(&fe, client)) {
348                         /*
349                           if focus_client is not set, then we can't do
350                           this. we need the FocusIn. This happens in the
351                           case when the set_focus_client(NULL) in the
352                           focus_fallback function fires and then
353                           focus_fallback picks the currently focused
354                           window (such as on a SendToDesktop-esque action.
355                         */
356                         if (focus_client) {
357 #ifdef DEBUG_FOCUS
358                             g_message("focused window got an Out/In back to "
359                                       "itself IGNORED both");
360 #endif
361                             return TRUE;
362                         } else {
363                             event_process(&fe);
364 #ifdef DEBUG_FOCUS
365                             g_message("focused window got an Out/In back to "
366                                       "itself but focus_client was null "
367                                       "IGNORED just the Out");
368 #endif
369                             return TRUE;
370                         }
371                     }
372
373                     /* once all the FocusOut's have been dealt with, if there
374                        is a FocusIn still left and it is valid, then use it */
375                     event_process(&fe);
376                     /* secret magic way of event_process telling us that no
377                        client was found for the FocusIn event. ^_^ */
378                     if (fe.xfocus.window != None) {
379                         fallback = FALSE;
380                         break;
381                     }
382                 }
383             }
384             if (fallback) {
385 #ifdef DEBUG_FOCUS
386                 g_message("no valid FocusIn and no FocusOut events found, "
387                           "falling back");
388 #endif
389                 focus_fallback(Fallback_NoFocus);
390             }
391         }
392         break;
393     case EnterNotify:
394     case LeaveNotify:
395         /* NotifyUngrab occurs when a mouse button is released and the event is
396            caused, like when lowering a window */
397         /* NotifyVirtual occurs when ungrabbing the pointer */
398         if (e->xcrossing.mode == NotifyGrab ||
399             e->xcrossing.detail == NotifyInferior ||
400             (e->xcrossing.mode == NotifyUngrab &&
401              e->xcrossing.detail == NotifyVirtual)) {
402 #ifdef DEBUG_FOCUS
403             g_message("%sNotify mode %d detail %d on %lx IGNORED",
404                       (e->type == EnterNotify ? "Enter" : "Leave"),
405                       e->xcrossing.mode,
406                       e->xcrossing.detail, client?client->window:0);
407 #endif
408             return TRUE;
409         }
410 #ifdef DEBUG_FOCUS
411         g_message("%sNotify mode %d detail %d on %lx",
412                   (e->type == EnterNotify ? "Enter" : "Leave"),
413                   e->xcrossing.mode,
414                   e->xcrossing.detail, client?client->window:0);
415 #endif
416         break;
417     }
418     return FALSE;
419 }
420
421 static void event_process(XEvent *e)
422 {
423     Window window;
424     Client *client = NULL;
425     Dock *dock = NULL;
426     DockApp *dockapp = NULL;
427     Menu *menu = NULL;
428     ObWindow *obwin = NULL;
429
430     window = event_get_window(e);
431     if ((obwin = g_hash_table_lookup(window_map, &window))) {
432         switch (obwin->type) {
433         case Window_Dock:
434             dock = WINDOW_AS_DOCK(obwin);
435             break;
436         case Window_DockApp:
437             dockapp = WINDOW_AS_DOCKAPP(obwin);
438             break;
439         case Window_Menu:
440             menu = WINDOW_AS_MENU(obwin);
441             break;
442         case Window_Client:
443             client = WINDOW_AS_CLIENT(obwin);
444             break;
445         case Window_Decoration:
446             client = WINDOW_AS_DECORATION(obwin)->frame->client;
447             break;
448         case Window_Internal:
449             /* not to be used for events */
450             g_assert_not_reached();
451             break;
452         }
453     }
454
455     event_set_lasttime(e);
456     event_hack_mods(e);
457     if (event_ignore(e, client))
458         return;
459
460     /* deal with it in the kernel */
461     if (menu) {
462         event_handle_menu(menu, client, e);
463         return;
464     } else if (client)
465         event_handle_client(client, e);
466     else if (dockapp)
467         event_handle_dockapp(dockapp, e);
468     else if (dock)
469         event_handle_dock(dock, e);
470     else if (window == ob_root)
471         event_handle_root(e);
472     else if (e->type == MapRequest)
473         client_manage(window);
474     else if (e->type == ConfigureRequest) {
475         /* unhandled configure requests must be used to configure the
476            window directly */
477         XWindowChanges xwc;
478                
479         xwc.x = e->xconfigurerequest.x;
480         xwc.y = e->xconfigurerequest.y;
481         xwc.width = e->xconfigurerequest.width;
482         xwc.height = e->xconfigurerequest.height;
483         xwc.border_width = e->xconfigurerequest.border_width;
484         xwc.sibling = e->xconfigurerequest.above;
485         xwc.stack_mode = e->xconfigurerequest.detail;
486        
487         /* we are not to be held responsible if someone sends us an
488            invalid request! */
489         xerror_set_ignore(TRUE);
490         XConfigureWindow(ob_display, window,
491                          e->xconfigurerequest.value_mask, &xwc);
492         xerror_set_ignore(FALSE);
493     }
494
495     if (moveresize_in_progress)
496         if (e->type == MotionNotify || e->type == ButtonRelease ||
497             e->type == ButtonPress ||
498             e->type == KeyPress || e->type == KeyRelease) {
499             moveresize_event(e);
500
501             return; /* no dispatch! */
502             
503         }
504
505     /* user input (action-bound) events */
506     /*
507     if (e->type == ButtonPress || e->type == ButtonRelease ||
508         e->type == MotionNotify)
509         mouse_event(e, client);
510     else if (e->type == KeyPress || e->type == KeyRelease)
511         ;
512     */
513
514     /* dispatch the event to registered handlers */
515     dispatch_x(e, client);
516 }
517
518 static void event_handle_root(XEvent *e)
519 {
520     Atom msgtype;
521      
522     switch(e->type) {
523     case ClientMessage:
524         if (e->xclient.format != 32) break;
525
526         msgtype = e->xclient.message_type;
527         if (msgtype == prop_atoms.net_current_desktop) {
528             unsigned int d = e->xclient.data.l[0];
529             if (d < screen_num_desktops)
530                 screen_set_desktop(d);
531         } else if (msgtype == prop_atoms.net_number_of_desktops) {
532             unsigned int d = e->xclient.data.l[0];
533             if (d > 0)
534                 screen_set_num_desktops(d);
535         } else if (msgtype == prop_atoms.net_showing_desktop) {
536             screen_show_desktop(e->xclient.data.l[0] != 0);
537         }
538         break;
539     case PropertyNotify:
540         if (e->xproperty.atom == prop_atoms.net_desktop_names)
541             screen_update_desktop_names();
542         else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
543             screen_update_layout();
544         break;
545     case ConfigureNotify:
546 #ifdef XRANDR
547         XRRUpdateConfiguration(e);
548 #endif
549         if (e->xconfigure.width != screen_physical_size.width ||
550             e->xconfigure.height != screen_physical_size.height)
551             screen_resize(e->xconfigure.width, e->xconfigure.height);
552         break;
553     default:
554         ;
555 #ifdef VIDMODE
556         if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
557             g_message("VIDMODE EVENT");
558         }
559 #endif
560     }
561 }
562
563 static void event_handle_client(Client *client, XEvent *e)
564 {
565     XEvent ce;
566     Atom msgtype;
567     int i=0;
568      
569     switch (e->type) {
570     case ButtonPress:
571     case ButtonRelease:
572         /* Wheel buttons don't draw because they are an instant click, so it
573            is a waste of resources to go drawing it. */
574         if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
575             switch (frame_context(client, e->xbutton.window)) {
576             case Context_Maximize:
577                 client->frame->max_press = (e->type == ButtonPress);
578                 framerender_frame(client->frame);
579                 break;
580             case Context_Close:
581                 client->frame->close_press = (e->type == ButtonPress);
582                 framerender_frame(client->frame);
583                 break;
584             case Context_Iconify:
585                 client->frame->iconify_press = (e->type == ButtonPress);
586                 framerender_frame(client->frame);
587                 break;
588             case Context_AllDesktops:
589                 client->frame->desk_press = (e->type == ButtonPress);
590                 framerender_frame(client->frame);
591                 break; 
592             case Context_Shade:
593                 client->frame->shade_press = (e->type == ButtonPress);
594                 framerender_frame(client->frame);
595                 break;
596             default:
597                 /* nothing changes with clicks for any other contexts */
598                 break;
599             }
600         }
601         break;
602     case FocusIn:
603 #ifdef DEBUG_FOCUS
604         g_message("FocusIn on client for %lx", client->window);
605 #endif
606         if (client != focus_client) {
607             focus_set_client(client);
608             frame_adjust_focus(client->frame, TRUE);
609         }
610         break;
611     case FocusOut:
612 #ifdef DEBUG_FOCUS
613         g_message("FocusOut on client for %lx", client->window);
614 #endif
615         /* are we a fullscreen window or a transient of one? (checks layer)
616            if we are then we need to be iconified since we are losing focus
617          */
618         if (client->layer == Layer_Fullscreen && !client->iconic &&
619             !client_search_focus_tree_full(client))
620             /* iconify fullscreen windows when they and their transients
621                aren't focused */
622             client_iconify(client, TRUE, TRUE);
623         frame_adjust_focus(client->frame, FALSE);
624         break;
625     case EnterNotify:
626         if (client_normal(client)) {
627             if (ob_state == State_Starting) {
628                 /* move it to the top of the focus order */
629                 guint desktop = client->desktop;
630                 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
631                 focus_order[desktop] = g_list_remove(focus_order[desktop],
632                                                      client);
633                 focus_order[desktop] = g_list_prepend(focus_order[desktop],
634                                                       client);
635             } else if (config_focus_follow) {
636 #ifdef DEBUG_FOCUS
637                 g_message("EnterNotify on %lx, focusing window",
638                           client->window);
639 #endif
640                 client_focus(client);
641             }
642         }
643         break;
644     case ConfigureRequest:
645         /* compress these */
646         while (XCheckTypedWindowEvent(ob_display, client->window,
647                                       ConfigureRequest, &ce)) {
648             ++i;
649             /* XXX if this causes bad things.. we can compress config req's
650                with the same mask. */
651             e->xconfigurerequest.value_mask |=
652                 ce.xconfigurerequest.value_mask;
653             if (ce.xconfigurerequest.value_mask & CWX)
654                 e->xconfigurerequest.x = ce.xconfigurerequest.x;
655             if (ce.xconfigurerequest.value_mask & CWY)
656                 e->xconfigurerequest.y = ce.xconfigurerequest.y;
657             if (ce.xconfigurerequest.value_mask & CWWidth)
658                 e->xconfigurerequest.width = ce.xconfigurerequest.width;
659             if (ce.xconfigurerequest.value_mask & CWHeight)
660                 e->xconfigurerequest.height = ce.xconfigurerequest.height;
661             if (ce.xconfigurerequest.value_mask & CWBorderWidth)
662                 e->xconfigurerequest.border_width =
663                     ce.xconfigurerequest.border_width;
664             if (ce.xconfigurerequest.value_mask & CWStackMode)
665                 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
666         }
667
668         /* if we are iconic (or shaded (fvwm does this)) ignore the event */
669         if (client->iconic || client->shaded) return;
670
671         if (e->xconfigurerequest.value_mask & CWBorderWidth)
672             client->border_width = e->xconfigurerequest.border_width;
673
674         /* resize, then move, as specified in the EWMH section 7.7 */
675         if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
676                                                CWX | CWY)) {
677             int x, y, w, h;
678             Corner corner;
679                
680             x = (e->xconfigurerequest.value_mask & CWX) ?
681                 e->xconfigurerequest.x : client->area.x;
682             y = (e->xconfigurerequest.value_mask & CWY) ?
683                 e->xconfigurerequest.y : client->area.y;
684             w = (e->xconfigurerequest.value_mask & CWWidth) ?
685                 e->xconfigurerequest.width : client->area.width;
686             h = (e->xconfigurerequest.value_mask & CWHeight) ?
687                 e->xconfigurerequest.height : client->area.height;
688                
689             switch (client->gravity) {
690             case NorthEastGravity:
691             case EastGravity:
692                 corner = Corner_TopRight;
693                 break;
694             case SouthWestGravity:
695             case SouthGravity:
696                 corner = Corner_BottomLeft;
697                 break;
698             case SouthEastGravity:
699                 corner = Corner_BottomRight;
700                 break;
701             default:     /* NorthWest, Static, etc */
702                 corner = Corner_TopLeft;
703             }
704
705             client_configure(client, corner, x, y, w, h, FALSE, FALSE);
706         }
707
708         if (e->xconfigurerequest.value_mask & CWStackMode) {
709             switch (e->xconfigurerequest.detail) {
710             case Below:
711             case BottomIf:
712                 stacking_lower(CLIENT_AS_WINDOW(client));
713                 break;
714
715             case Above:
716             case TopIf:
717             default:
718                 stacking_raise(CLIENT_AS_WINDOW(client));
719                 break;
720             }
721         }
722         break;
723     case UnmapNotify:
724         if (client->ignore_unmaps) {
725             client->ignore_unmaps--;
726             break;
727         }
728         client_unmanage(client);
729         break;
730     case DestroyNotify:
731         client_unmanage(client);
732         break;
733     case ReparentNotify:
734         /* this is when the client is first taken captive in the frame */
735         if (e->xreparent.parent == client->frame->plate) break;
736
737         /*
738           This event is quite rare and is usually handled in unmapHandler.
739           However, if the window is unmapped when the reparent event occurs,
740           the window manager never sees it because an unmap event is not sent
741           to an already unmapped window.
742         */
743
744         /* we don't want the reparent event, put it back on the stack for the
745            X server to deal with after we unmanage the window */
746         XPutBackEvent(ob_display, e);
747      
748         client_unmanage(client);
749         break;
750     case MapRequest:
751         g_message("MapRequest for 0x%lx", client->window);
752         if (!client->iconic) break; /* this normally doesn't happen, but if it
753                                        does, we don't want it! */
754         if (screen_showing_desktop)
755             screen_show_desktop(FALSE);
756         client_iconify(client, FALSE, TRUE);
757         if (!client->frame->visible)
758             /* if its not visible still, then don't mess with it */
759             break;
760         if (client->shaded)
761             client_shade(client, FALSE);
762         client_focus(client);
763         stacking_raise(CLIENT_AS_WINDOW(client));
764         break;
765     case ClientMessage:
766         /* validate cuz we query stuff off the client here */
767         if (!client_validate(client)) break;
768   
769         if (e->xclient.format != 32) return;
770
771         msgtype = e->xclient.message_type;
772         if (msgtype == prop_atoms.wm_change_state) {
773             /* compress changes into a single change */
774             while (XCheckTypedWindowEvent(ob_display, e->type,
775                                           client->window, &ce)) {
776                 /* XXX: it would be nice to compress ALL messages of a
777                    type, not just messages in a row without other
778                    message types between. */
779                 if (ce.xclient.message_type != msgtype) {
780                     XPutBackEvent(ob_display, &ce);
781                     break;
782                 }
783                 e->xclient = ce.xclient;
784             }
785             client_set_wm_state(client, e->xclient.data.l[0]);
786         } else if (msgtype == prop_atoms.net_wm_desktop) {
787             /* compress changes into a single change */
788             while (XCheckTypedWindowEvent(ob_display, e->type,
789                                           client->window, &ce)) {
790                 /* XXX: it would be nice to compress ALL messages of a
791                    type, not just messages in a row without other
792                    message types between. */
793                 if (ce.xclient.message_type != msgtype) {
794                     XPutBackEvent(ob_display, &ce);
795                     break;
796                 }
797                 e->xclient = ce.xclient;
798             }
799             if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
800                 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
801                 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
802                                    FALSE);
803         } else if (msgtype == prop_atoms.net_wm_state) {
804             /* can't compress these */
805             g_message("net_wm_state %s %ld %ld for 0x%lx",
806                       (e->xclient.data.l[0] == 0 ? "Remove" :
807                        e->xclient.data.l[0] == 1 ? "Add" :
808                        e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
809                       e->xclient.data.l[1], e->xclient.data.l[2],
810                       client->window);
811             client_set_state(client, e->xclient.data.l[0],
812                              e->xclient.data.l[1], e->xclient.data.l[2]);
813         } else if (msgtype == prop_atoms.net_close_window) {
814             g_message("net_close_window for 0x%lx", client->window);
815             client_close(client);
816         } else if (msgtype == prop_atoms.net_active_window) {
817             g_message("net_active_window for 0x%lx", client->window);
818             client_activate(client);
819         } else if (msgtype == prop_atoms.net_wm_moveresize) {
820             g_message("net_wm_moveresize for 0x%lx", client->window);
821             if ((Atom)e->xclient.data.l[2] ==
822                 prop_atoms.net_wm_moveresize_size_topleft ||
823                 (Atom)e->xclient.data.l[2] ==
824                 prop_atoms.net_wm_moveresize_size_top ||
825                 (Atom)e->xclient.data.l[2] ==
826                 prop_atoms.net_wm_moveresize_size_topright ||
827                 (Atom)e->xclient.data.l[2] ==
828                 prop_atoms.net_wm_moveresize_size_right ||
829                 (Atom)e->xclient.data.l[2] ==
830                 prop_atoms.net_wm_moveresize_size_right ||
831                 (Atom)e->xclient.data.l[2] ==
832                 prop_atoms.net_wm_moveresize_size_bottomright ||
833                 (Atom)e->xclient.data.l[2] ==
834                 prop_atoms.net_wm_moveresize_size_bottom ||
835                 (Atom)e->xclient.data.l[2] ==
836                 prop_atoms.net_wm_moveresize_size_bottomleft ||
837                 (Atom)e->xclient.data.l[2] ==
838                 prop_atoms.net_wm_moveresize_size_left ||
839                 (Atom)e->xclient.data.l[2] ==
840                 prop_atoms.net_wm_moveresize_move ||
841                 (Atom)e->xclient.data.l[2] ==
842                 prop_atoms.net_wm_moveresize_size_keyboard ||
843                 (Atom)e->xclient.data.l[2] ==
844                 prop_atoms.net_wm_moveresize_move_keyboard) {
845
846                 moveresize_start(client, e->xclient.data.l[0],
847                                  e->xclient.data.l[1], e->xclient.data.l[3],
848                                  e->xclient.data.l[2]);
849             }
850         } else if (msgtype == prop_atoms.net_moveresize_window) {
851             int oldg = client->gravity;
852             int tmpg, x, y, w, h;
853
854             if (e->xclient.data.l[0] & 0xff)
855                 tmpg = e->xclient.data.l[0] & 0xff;
856             else
857                 tmpg = oldg;
858
859             if (e->xclient.data.l[0] & 1 << 8)
860                 x = e->xclient.data.l[1];
861             else
862                 x = client->area.x;
863             if (e->xclient.data.l[0] & 1 << 9)
864                 y = e->xclient.data.l[2];
865             else
866                 y = client->area.y;
867             if (e->xclient.data.l[0] & 1 << 10)
868                 w = e->xclient.data.l[3];
869             else
870                 w = client->area.y;
871             if (e->xclient.data.l[0] & 1 << 11)
872                 h = e->xclient.data.l[4];
873             else
874                 h = client->area.y;
875             client->gravity = tmpg;
876             client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
877             client->gravity = oldg;
878         }
879         break;
880     case PropertyNotify:
881         /* validate cuz we query stuff off the client here */
882         if (!client_validate(client)) break;
883   
884         /* compress changes to a single property into a single change */
885         while (XCheckTypedWindowEvent(ob_display, e->type,
886                                       client->window, &ce)) {
887             /* XXX: it would be nice to compress ALL changes to a property,
888                not just changes in a row without other props between. */
889             if (ce.xproperty.atom != e->xproperty.atom) {
890                 XPutBackEvent(ob_display, &ce);
891                 break;
892             }
893         }
894
895         msgtype = e->xproperty.atom;
896         if (msgtype == XA_WM_NORMAL_HINTS) {
897             client_update_normal_hints(client);
898             /* normal hints can make a window non-resizable */
899             client_setup_decor_and_functions(client);
900         }
901         else if (msgtype == XA_WM_HINTS)
902             client_update_wmhints(client);
903         else if (msgtype == XA_WM_TRANSIENT_FOR) {
904             client_update_transient_for(client);
905             client_get_type(client);
906             /* type may have changed, so update the layer */
907             client_calc_layer(client);
908             client_setup_decor_and_functions(client);
909         }
910         else if (msgtype == prop_atoms.net_wm_name ||
911                  msgtype == prop_atoms.wm_name ||
912                  msgtype == prop_atoms.net_wm_icon_name ||
913                  msgtype == prop_atoms.wm_icon_name)
914             client_update_title(client);
915         else if (msgtype == prop_atoms.wm_class)
916             client_update_class(client);
917         else if (msgtype == prop_atoms.wm_protocols) {
918             client_update_protocols(client);
919             client_setup_decor_and_functions(client);
920         }
921         else if (msgtype == prop_atoms.net_wm_strut)
922             client_update_strut(client);
923         else if (msgtype == prop_atoms.net_wm_icon ||
924                  msgtype == prop_atoms.kwm_win_icon)
925             client_update_icons(client);
926     default:
927         ;
928 #ifdef SHAPE
929         if (extensions_shape && e->type == extensions_shape_event_basep) {
930             client->shaped = ((XShapeEvent*)e)->shaped;
931             frame_adjust_shape(client->frame);
932         }
933 #endif
934     }
935 }
936
937 static void event_handle_menu(Menu *menu, Client *client, XEvent *e)
938 {
939     MenuEntry *entry;
940
941     g_message("EVENT %d", e->type);
942     switch (e->type) {
943     case ButtonPress:
944         g_message("BUTTON PRESS");
945         if (e->xbutton.button == 3)
946             menu_hide(menu);
947         else if (e->xbutton.button == 1) {
948             entry = menu_find_entry(menu, e->xbutton.window);
949             if (!entry)
950                 stacking_raise(MENU_AS_WINDOW(menu));
951         }
952         break;
953     case ButtonRelease:
954         g_message("BUTTON RELEASED");
955         if (!menu->shown) break;
956
957 /*        grab_pointer_window(FALSE, None, menu->frame);*/
958
959         if (e->xbutton.button == 1) {
960             entry = menu_find_entry(menu, e->xbutton.window);
961             if (entry) {
962                 int junk;
963                 Window wjunk;
964                 guint ujunk, b, w, h;
965                 XGetGeometry(ob_display, e->xbutton.window,
966                              &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
967                 if (e->xbutton.x >= (signed)-b &&
968                     e->xbutton.y >= (signed)-b &&
969                     e->xbutton.x < (signed)(w+b) &&
970                     e->xbutton.y < (signed)(h+b)) {
971                     menu_entry_fire(entry);
972                 }
973             }
974         }
975         
976         break;
977     case EnterNotify:
978     case LeaveNotify:
979         g_message("enter/leave");
980         entry = menu_find_entry(menu, e->xcrossing.window);
981         if (entry) {
982             if (menu->mouseover)
983                 menu->mouseover(entry, e->type == EnterNotify);
984             else
985                 menu_control_mouseover(entry, e->type == EnterNotify);
986             
987             menu_entry_render(entry);
988         }
989         break;
990     }
991 }
992
993 void event_add_fd_handler(event_fd_handler *h) {
994   g_datalist_id_set_data(&fd_handler_list, h->fd, h);
995   FD_SET(h->fd, &allset);
996   max_fd = MAX(max_fd, h->fd);
997 }
998
999 void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1000 {
1001   *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1002 }
1003
1004 void event_remove_fd(int n)
1005 {
1006   int tmpmax = 0;
1007   FD_CLR(n, &allset);
1008   g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1009   g_datalist_foreach(&fd_handler_list, find_max_fd_foreach, (gpointer)&tmpmax);
1010   max_fd = MAX(x_fd, tmpmax);
1011 }
1012
1013 void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1014 {
1015     if (FD_ISSET( (int)n, &selset)) {
1016         event_fd_handler *h = (event_fd_handler *)data;
1017         g_assert(h->fd == (int)n);
1018         h->handler(h->fd, h->data);
1019     }
1020 }
1021
1022 void fd_event_handle()
1023 {
1024     g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1025 }
1026
1027 static void event_handle_dock(Dock *s, XEvent *e)
1028 {
1029     switch (e->type) {
1030     case ButtonPress:
1031         stacking_raise(DOCK_AS_WINDOW(s));
1032         break;
1033     case EnterNotify:
1034         dock_hide(FALSE);
1035         break;
1036     case LeaveNotify:
1037         dock_hide(TRUE);
1038         break;
1039     }
1040 }
1041
1042 static void event_handle_dockapp(DockApp *app, XEvent *e)
1043 {
1044     switch (e->type) {
1045     case MotionNotify:
1046         dock_app_drag(app, &e->xmotion);
1047         break;
1048     case UnmapNotify:
1049         if (app->ignore_unmaps) {
1050             app->ignore_unmaps--;
1051             break;
1052         }
1053         dock_remove(app, TRUE);
1054         break;
1055     case DestroyNotify:
1056         dock_remove(app, FALSE);
1057         break;
1058     case ReparentNotify:
1059         dock_remove(app, FALSE);
1060         break;
1061     case ConfigureNotify:
1062         dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1063         break;
1064     }
1065 }