]> Dogcows Code - chaz/openbox/blob - openbox/event.c
menus works on some level. add a built-in root menu
[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 else if (client)
333 event_handle_client(client, e);
334 else if (window == ob_root)
335 event_handle_root(e);
336 else if (e->type == MapRequest)
337 client_manage(window);
338 else if (e->type == ConfigureRequest) {
339 /* unhandled configure requests must be used to configure the
340 window directly */
341 XWindowChanges xwc;
342
343 xwc.x = e->xconfigurerequest.x;
344 xwc.y = e->xconfigurerequest.y;
345 xwc.width = e->xconfigurerequest.width;
346 xwc.height = e->xconfigurerequest.height;
347 xwc.border_width = e->xconfigurerequest.border_width;
348 xwc.sibling = e->xconfigurerequest.above;
349 xwc.stack_mode = e->xconfigurerequest.detail;
350
351 /* we are not to be held responsible if someone sends us an
352 invalid request! */
353 xerror_set_ignore(TRUE);
354 XConfigureWindow(ob_display, window,
355 e->xconfigurerequest.value_mask, &xwc);
356 xerror_set_ignore(FALSE);
357 }
358
359 /* user input (action-bound) events */
360 /*
361 if (e->type == ButtonPress || e->type == ButtonRelease ||
362 e->type == MotionNotify)
363 mouse_event(e, client);
364 else if (e->type == KeyPress || e->type == KeyRelease)
365 ;
366 */
367
368 /* dispatch the event to registered handlers */
369 dispatch_x(e, client);
370 }
371
372 static void event_handle_root(XEvent *e)
373 {
374 Atom msgtype;
375
376 switch(e->type) {
377 case ClientMessage:
378 if (e->xclient.format != 32) break;
379
380 msgtype = e->xclient.message_type;
381 if (msgtype == prop_atoms.net_current_desktop) {
382 unsigned int d = e->xclient.data.l[0];
383 if (d < screen_num_desktops)
384 screen_set_desktop(d);
385 } else if (msgtype == prop_atoms.net_number_of_desktops) {
386 unsigned int d = e->xclient.data.l[0];
387 if (d > 0)
388 screen_set_num_desktops(d);
389 } else if (msgtype == prop_atoms.net_showing_desktop) {
390 screen_show_desktop(e->xclient.data.l[0] != 0);
391 }
392 break;
393 case PropertyNotify:
394 if (e->xproperty.atom == prop_atoms.net_desktop_names)
395 screen_update_desktop_names();
396 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
397 screen_update_layout();
398 break;
399 }
400 }
401
402 static void event_handle_client(Client *client, XEvent *e)
403 {
404 XEvent ce;
405 Atom msgtype;
406 int i=0;
407
408 switch (e->type) {
409 case ButtonPress:
410 case ButtonRelease:
411 switch (frame_context(client, e->xbutton.window)) {
412 case Context_Maximize:
413 client->frame->max_press = (e->type == ButtonPress);
414 framerender_frame(client->frame);
415 break;
416 case Context_Close:
417 client->frame->close_press = (e->type == ButtonPress);
418 framerender_frame(client->frame);
419 break;
420 case Context_Iconify:
421 client->frame->iconify_press = (e->type == ButtonPress);
422 framerender_frame(client->frame);
423 break;
424 case Context_AllDesktops:
425 client->frame->desk_press = (e->type == ButtonPress);
426 framerender_frame(client->frame);
427 break;
428 case Context_Shade:
429 client->frame->shade_press = (e->type == ButtonPress);
430 framerender_frame(client->frame);
431 break;
432 default:
433 /* nothing changes with clicks for any other contexts */
434 break;
435 }
436 break;
437 case FocusIn:
438 focus_set_client(client);
439 case FocusOut:
440 #ifdef DEBUG_FOCUS
441 g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
442 client->window);
443 #endif
444 /* focus state can affect the stacking layer */
445 client_calc_layer(client);
446 frame_adjust_focus(client->frame);
447 break;
448 case EnterNotify:
449 if (client_normal(client)) {
450 if (ob_state == State_Starting) {
451 /* move it to the top of the focus order */
452 guint desktop = client->desktop;
453 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
454 focus_order[desktop] = g_list_remove(focus_order[desktop],
455 client);
456 focus_order[desktop] = g_list_prepend(focus_order[desktop],
457 client);
458 } else if (config_focus_follow) {
459 #ifdef DEBUG_FOCUS
460 g_message("EnterNotify on %lx, focusing window",
461 client->window);
462 #endif
463 client_focus(client);
464 }
465 }
466 break;
467 case ConfigureRequest:
468 /* compress these */
469 while (XCheckTypedWindowEvent(ob_display, client->window,
470 ConfigureRequest, &ce)) {
471 ++i;
472 /* XXX if this causes bad things.. we can compress config req's
473 with the same mask. */
474 e->xconfigurerequest.value_mask |=
475 ce.xconfigurerequest.value_mask;
476 if (ce.xconfigurerequest.value_mask & CWX)
477 e->xconfigurerequest.x = ce.xconfigurerequest.x;
478 if (ce.xconfigurerequest.value_mask & CWY)
479 e->xconfigurerequest.y = ce.xconfigurerequest.y;
480 if (ce.xconfigurerequest.value_mask & CWWidth)
481 e->xconfigurerequest.width = ce.xconfigurerequest.width;
482 if (ce.xconfigurerequest.value_mask & CWHeight)
483 e->xconfigurerequest.height = ce.xconfigurerequest.height;
484 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
485 e->xconfigurerequest.border_width =
486 ce.xconfigurerequest.border_width;
487 if (ce.xconfigurerequest.value_mask & CWStackMode)
488 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
489 }
490
491 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
492 if (client->iconic || client->shaded) return;
493
494 if (e->xconfigurerequest.value_mask & CWBorderWidth)
495 client->border_width = e->xconfigurerequest.border_width;
496
497 /* resize, then move, as specified in the EWMH section 7.7 */
498 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
499 CWX | CWY)) {
500 int x, y, w, h;
501 Corner corner;
502
503 x = (e->xconfigurerequest.value_mask & CWX) ?
504 e->xconfigurerequest.x : client->area.x;
505 y = (e->xconfigurerequest.value_mask & CWY) ?
506 e->xconfigurerequest.y : client->area.y;
507 w = (e->xconfigurerequest.value_mask & CWWidth) ?
508 e->xconfigurerequest.width : client->area.width;
509 h = (e->xconfigurerequest.value_mask & CWHeight) ?
510 e->xconfigurerequest.height : client->area.height;
511
512 switch (client->gravity) {
513 case NorthEastGravity:
514 case EastGravity:
515 corner = Corner_TopRight;
516 break;
517 case SouthWestGravity:
518 case SouthGravity:
519 corner = Corner_BottomLeft;
520 break;
521 case SouthEastGravity:
522 corner = Corner_BottomRight;
523 break;
524 default: /* NorthWest, Static, etc */
525 corner = Corner_TopLeft;
526 }
527
528 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
529 }
530
531 if (e->xconfigurerequest.value_mask & CWStackMode) {
532 switch (e->xconfigurerequest.detail) {
533 case Below:
534 case BottomIf:
535 stacking_lower(client);
536 break;
537
538 case Above:
539 case TopIf:
540 default:
541 stacking_raise(client);
542 break;
543 }
544 }
545 break;
546 case UnmapNotify:
547 if (client->ignore_unmaps) {
548 client->ignore_unmaps--;
549 break;
550 }
551 client_unmanage(client);
552 break;
553 case DestroyNotify:
554 client_unmanage(client);
555 break;
556 case ReparentNotify:
557 /* this is when the client is first taken captive in the frame */
558 if (e->xreparent.parent == client->frame->plate) break;
559
560 /*
561 This event is quite rare and is usually handled in unmapHandler.
562 However, if the window is unmapped when the reparent event occurs,
563 the window manager never sees it because an unmap event is not sent
564 to an already unmapped window.
565 */
566
567 /* we don't want the reparent event, put it back on the stack for the
568 X server to deal with after we unmanage the window */
569 XPutBackEvent(ob_display, e);
570
571 client_unmanage(client);
572 break;
573 case MapRequest:
574 g_message("MapRequest for 0x%lx", client->window);
575 if (!client->iconic) break; /* this normally doesn't happen, but if it
576 does, we don't want it! */
577 if (screen_showing_desktop)
578 screen_show_desktop(FALSE);
579 client_iconify(client, FALSE, TRUE);
580 if (!client->frame->visible)
581 /* if its not visible still, then don't mess with it */
582 break;
583 if (client->shaded)
584 client_shade(client, FALSE);
585 client_focus(client);
586 stacking_raise(client);
587 break;
588 case ClientMessage:
589 /* validate cuz we query stuff off the client here */
590 if (!client_validate(client)) break;
591
592 if (e->xclient.format != 32) return;
593
594 msgtype = e->xclient.message_type;
595 if (msgtype == prop_atoms.wm_change_state) {
596 /* compress changes into a single change */
597 while (XCheckTypedWindowEvent(ob_display, e->type,
598 client->window, &ce)) {
599 /* XXX: it would be nice to compress ALL messages of a
600 type, not just messages in a row without other
601 message types between. */
602 if (ce.xclient.message_type != msgtype) {
603 XPutBackEvent(ob_display, &ce);
604 break;
605 }
606 e->xclient = ce.xclient;
607 }
608 client_set_wm_state(client, e->xclient.data.l[0]);
609 } else if (msgtype == prop_atoms.net_wm_desktop) {
610 /* compress changes into a single change */
611 while (XCheckTypedWindowEvent(ob_display, e->type,
612 client->window, &ce)) {
613 /* XXX: it would be nice to compress ALL messages of a
614 type, not just messages in a row without other
615 message types between. */
616 if (ce.xclient.message_type != msgtype) {
617 XPutBackEvent(ob_display, &ce);
618 break;
619 }
620 e->xclient = ce.xclient;
621 }
622 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
623 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
624 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
625 FALSE);
626 } else if (msgtype == prop_atoms.net_wm_state) {
627 /* can't compress these */
628 g_message("net_wm_state %s %ld %ld for 0x%lx",
629 (e->xclient.data.l[0] == 0 ? "Remove" :
630 e->xclient.data.l[0] == 1 ? "Add" :
631 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
632 e->xclient.data.l[1], e->xclient.data.l[2],
633 client->window);
634 client_set_state(client, e->xclient.data.l[0],
635 e->xclient.data.l[1], e->xclient.data.l[2]);
636 } else if (msgtype == prop_atoms.net_close_window) {
637 g_message("net_close_window for 0x%lx", client->window);
638 client_close(client);
639 } else if (msgtype == prop_atoms.net_active_window) {
640 g_message("net_active_window for 0x%lx", client->window);
641 if (screen_showing_desktop)
642 screen_show_desktop(FALSE);
643 if (client->iconic)
644 client_iconify(client, FALSE, TRUE);
645 else if (!client->frame->visible)
646 /* if its not visible for other reasons, then don't mess
647 with it */
648 break;
649 if (client->shaded)
650 client_shade(client, FALSE);
651 client_focus(client);
652 stacking_raise(client);
653 }
654 break;
655 case PropertyNotify:
656 /* validate cuz we query stuff off the client here */
657 if (!client_validate(client)) break;
658
659 /* compress changes to a single property into a single change */
660 while (XCheckTypedWindowEvent(ob_display, e->type,
661 client->window, &ce)) {
662 /* XXX: it would be nice to compress ALL changes to a property,
663 not just changes in a row without other props between. */
664 if (ce.xproperty.atom != e->xproperty.atom) {
665 XPutBackEvent(ob_display, &ce);
666 break;
667 }
668 }
669
670 msgtype = e->xproperty.atom;
671 if (msgtype == XA_WM_NORMAL_HINTS) {
672 client_update_normal_hints(client);
673 /* normal hints can make a window non-resizable */
674 client_setup_decor_and_functions(client);
675 }
676 else if (msgtype == XA_WM_HINTS)
677 client_update_wmhints(client);
678 else if (msgtype == XA_WM_TRANSIENT_FOR) {
679 client_update_transient_for(client);
680 client_get_type(client);
681 /* type may have changed, so update the layer */
682 client_calc_layer(client);
683 client_setup_decor_and_functions(client);
684 }
685 else if (msgtype == prop_atoms.net_wm_name ||
686 msgtype == prop_atoms.wm_name)
687 client_update_title(client);
688 else if (msgtype == prop_atoms.net_wm_icon_name ||
689 msgtype == prop_atoms.wm_icon_name)
690 client_update_icon_title(client);
691 else if (msgtype == prop_atoms.wm_class)
692 client_update_class(client);
693 else if (msgtype == prop_atoms.wm_protocols) {
694 client_update_protocols(client);
695 client_setup_decor_and_functions(client);
696 }
697 else if (msgtype == prop_atoms.net_wm_strut)
698 client_update_strut(client);
699 else if (msgtype == prop_atoms.net_wm_icon)
700 client_update_icons(client);
701 else if (msgtype == prop_atoms.kwm_win_icon)
702 client_update_kwm_icon(client);
703 default:
704 ;
705 #ifdef SHAPE
706 if (extensions_shape && e->type == extensions_shape_event_basep) {
707 client->shaped = ((XShapeEvent*)e)->shaped;
708 frame_adjust_shape(client->frame);
709 }
710 #endif
711 }
712 }
713
714 static void event_handle_menu(Menu *menu, XEvent *e)
715 {
716 MenuEntry *entry;
717
718 g_message("EVENT %d", e->type);
719 switch (e->type) {
720 case ButtonPress:
721 if (e->xbutton.button == 3)
722 menu_hide(menu);
723 break;
724 case ButtonRelease:
725 if (!menu->shown) break;
726
727 /* grab_pointer_window(FALSE, None, menu->frame);*/
728
729 entry = menu_find_entry(menu, e->xbutton.window);
730 if (entry) {
731 int junk;
732 Window wjunk;
733 guint ujunk, b, w, h;
734 XGetGeometry(ob_display, e->xbutton.window,
735 &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
736 if (e->xbutton.x >= (signed)-b &&
737 e->xbutton.y >= (signed)-b &&
738 e->xbutton.x < (signed)(w+b) &&
739 e->xbutton.y < (signed)(h+b)) {
740 menu_entry_fire(entry);
741 }
742 }
743 break;
744 case EnterNotify:
745 case LeaveNotify:
746 g_message("enter/leave");
747 entry = menu_find_entry(menu, e->xcrossing.window);
748 if (entry) {
749 entry->hilite = e->type == EnterNotify;
750 menu_entry_render(entry);
751 }
752 break;
753 }
754 }
This page took 0.069247 seconds and 4 git commands to generate.