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