9 #include "framerender.h"
12 #include "extensions.h"
17 #include <X11/keysym.h>
18 #include <X11/Xatom.h>
19 #ifdef HAVE_SYS_SELECT_H
20 # include <sys/select.h>
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
);
28 Time event_lasttime
= 0;
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
41 static int mask_table_size
;
45 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
47 /* get lock masks that are defined by the display (not constant) */
48 modmap
= XGetModifierMapping(ob_display
);
50 if (modmap
&& modmap
->max_keypermod
> 0) {
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
,
60 for (cnt
= 0; cnt
< size
; ++cnt
) {
61 if (! modmap
->modifiermap
[cnt
]) continue;
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
];
73 XFreeModifiermap(modmap
);
82 gboolean had_event
= FALSE
;
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.
91 if (!XPending(ob_display
))
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.
102 XSync(ob_display
, FALSE
);
103 if (!XEventsQueued(ob_display
, QueuedAlready
))
106 XNextEvent(ob_display
, &e
);
113 timer_dispatch((GTimeVal
**)&wait
);
114 x_fd
= ConnectionNumber(ob_display
);
116 FD_SET(x_fd
, &selset
);
117 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
121 static Window
event_get_window(XEvent
*e
)
128 window
= e
->xmap
.window
;
131 window
= e
->xunmap
.window
;
134 window
= e
->xdestroywindow
.window
;
136 case ConfigureRequest
:
137 window
= e
->xconfigurerequest
.window
;
141 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
142 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
144 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
150 window
= e
->xany
.window
;
155 static void event_set_lasttime(XEvent
*e
)
157 /* grab the lasttime and hack up the state */
161 event_lasttime
= e
->xbutton
.time
;
164 event_lasttime
= e
->xkey
.time
;
167 event_lasttime
= e
->xkey
.time
;
170 event_lasttime
= e
->xmotion
.time
;
173 event_lasttime
= e
->xproperty
.time
;
177 event_lasttime
= e
->xcrossing
.time
;
180 event_lasttime
= CurrentTime
;
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) \
191 static void event_hack_mods(XEvent *e)
199 STRIP_MODS(e
->xbutton
.state
);
202 STRIP_MODS(e
->xkey
.state
);
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; */
216 break; /* get outta here! */
223 STRIP_MODS(e
->xmotion
.state
);
224 /* compress events */
227 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
229 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
230 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
237 static gboolean
event_ignore(XEvent
*e
, Client
*client
)
242 g_message("FocusIn on %lx mode %d detail %d", window
,
243 e
->xfocus
.mode
, e
->xfocus
.detail
);
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.
251 if (e
->xfocus
.detail
== NotifyInferior
||
252 e
->xfocus
.detail
> NotifyNonlinearVirtual
||
254 /* says a client was not found for the event (or a valid FocusIn
257 e
->xfocus
.window
= None
;
262 g_message("FocusIn on %lx", window
);
267 g_message("FocusOut on %lx mode %d detail %d", window
,
268 e
->xfocus
.mode
, e
->xfocus
.detail
);
270 if (e
->xfocus
.mode
== NotifyGrab
||
271 e
->xfocus
.detail
== NotifyInferior
||
272 e
->xfocus
.detail
== NotifyAncestor
||
273 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return TRUE
;
276 g_message("FocusOut on %lx", window
);
278 /* Try process a FocusIn first, and if a legit one isn't found, then
279 do the fallback shiznit. */
282 gboolean isfo
= FALSE
;
284 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
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
290 if ((isfo
= XCheckTypedEvent(ob_display
, FocusOut
, &fo
)))
291 XPutBackEvent(ob_display
, &fo
);
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
)
300 focus_fallback(Fallback_NoFocus
);
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
)
315 static void event_process(XEvent
*e
)
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
);
326 if (event_ignore(e
, client
))
329 /* deal with it in the kernel */
331 event_handle_menu(menu
, e
);
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
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
;
351 /* we are not to be held responsible if someone sends us an
353 xerror_set_ignore(TRUE
);
354 XConfigureWindow(ob_display
, window
,
355 e
->xconfigurerequest
.value_mask
, &xwc
);
356 xerror_set_ignore(FALSE
);
359 /* user input (action-bound) events */
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)
368 /* dispatch the event to registered handlers */
369 dispatch_x(e
, client
);
372 static void event_handle_root(XEvent
*e
)
378 if (e
->xclient
.format
!= 32) break;
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];
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);
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();
402 static void event_handle_client(Client
*client
, XEvent
*e
)
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
);
417 client
->frame
->close_press
= (e
->type
== ButtonPress
);
418 framerender_frame(client
->frame
);
420 case Context_Iconify
:
421 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
422 framerender_frame(client
->frame
);
424 case Context_AllDesktops
:
425 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
426 framerender_frame(client
->frame
);
429 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
430 framerender_frame(client
->frame
);
433 /* nothing changes with clicks for any other contexts */
438 focus_set_client(client
);
441 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
444 /* focus state can affect the stacking layer */
445 client_calc_layer(client
);
446 frame_adjust_focus(client
->frame
);
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
],
456 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
458 } else if (config_focus_follow
) {
460 g_message("EnterNotify on %lx, focusing window",
463 client_focus(client
);
467 case ConfigureRequest
:
469 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
470 ConfigureRequest
, &ce
)) {
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
;
491 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
492 if (client
->iconic
|| client
->shaded
) return;
494 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
495 client
->border_width
= e
->xconfigurerequest
.border_width
;
497 /* resize, then move, as specified in the EWMH section 7.7 */
498 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
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
;
512 switch (client
->gravity
) {
513 case NorthEastGravity
:
515 corner
= Corner_TopRight
;
517 case SouthWestGravity
:
519 corner
= Corner_BottomLeft
;
521 case SouthEastGravity
:
522 corner
= Corner_BottomRight
;
524 default: /* NorthWest, Static, etc */
525 corner
= Corner_TopLeft
;
528 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
531 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
532 switch (e
->xconfigurerequest
.detail
) {
535 stacking_lower(client
);
541 stacking_raise(client
);
547 if (client
->ignore_unmaps
) {
548 client
->ignore_unmaps
--;
551 client_unmanage(client
);
554 client_unmanage(client
);
557 /* this is when the client is first taken captive in the frame */
558 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
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.
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
);
571 client_unmanage(client
);
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 */
584 client_shade(client
, FALSE
);
585 client_focus(client
);
586 stacking_raise(client
);
589 /* validate cuz we query stuff off the client here */
590 if (!client_validate(client
)) break;
592 if (e
->xclient
.format
!= 32) return;
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
);
606 e
->xclient
= ce
.xclient
;
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
);
620 e
->xclient
= ce
.xclient
;
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],
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],
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
);
644 client_iconify(client
, FALSE
, TRUE
);
645 else if (!client
->frame
->visible
)
646 /* if its not visible for other reasons, then don't mess
650 client_shade(client
, FALSE
);
651 client_focus(client
);
652 stacking_raise(client
);
656 /* validate cuz we query stuff off the client here */
657 if (!client_validate(client
)) break;
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
);
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
);
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
);
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
);
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
);
706 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
707 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
708 frame_adjust_shape(client
->frame
);
714 static void event_handle_menu(Menu
*menu
, XEvent
*e
)
718 g_message("EVENT %d", e
->type
);
721 if (e
->xbutton
.button
== 3)
725 if (!menu
->shown
) break;
727 /* grab_pointer_window(FALSE, None, menu->frame);*/
729 entry
= menu_find_entry(menu
, e
->xbutton
.window
);
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
);
746 g_message("enter/leave");
747 entry
= menu_find_entry(menu
, e
->xcrossing
.window
);
749 entry
->hilite
= e
->type
== EnterNotify
;
750 menu_entry_render(entry
);