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