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