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