11 #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
);
27 Time event_lasttime
= 0;
29 /*! The value of the mask for the NumLock modifier */
30 unsigned int NumLockMask
;
31 /*! The value of the mask for the ScrollLock modifier */
32 unsigned int ScrollLockMask
;
33 /*! The key codes for the modifier keys */
34 static XModifierKeymap
*modmap
;
35 /*! Table of the constant modifier masks */
36 static const int mask_table
[] = {
37 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
38 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
40 static int mask_table_size
;
44 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
46 /* get lock masks that are defined by the display (not constant) */
47 modmap
= XGetModifierMapping(ob_display
);
49 if (modmap
&& modmap
->max_keypermod
> 0) {
51 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
52 /* get the values of the keyboard lock modifiers
53 Note: Caps lock is not retrieved the same way as Scroll and Num
54 lock since it doesn't need to be. */
55 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
56 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
59 for (cnt
= 0; cnt
< size
; ++cnt
) {
60 if (! modmap
->modifiermap
[cnt
]) continue;
62 if (num_lock
== modmap
->modifiermap
[cnt
])
63 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
64 if (scroll_lock
== modmap
->modifiermap
[cnt
])
65 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
72 XFreeModifiermap(modmap
);
84 There are slightly different event retrieval semantics here for
85 local (or high bandwidth) versus remote (or low bandwidth)
86 connections to the display/Xserver.
89 if (!XPending(ob_display
))
93 This XSync allows for far more compression of events, which
94 makes things like Motion events perform far far better. Since
95 it also means network traffic for every event instead of every
96 X events (where X is the number retrieved at a time), it
97 probably should not be used for setups where Openbox is
98 running on a remote/low bandwidth display/Xserver.
100 XSync(ob_display
, FALSE
);
101 if (!XEventsQueued(ob_display
, QueuedAlready
))
104 XNextEvent(ob_display
, &e
);
109 timer_dispatch((GTimeVal
**)&wait
);
110 x_fd
= ConnectionNumber(ob_display
);
112 FD_SET(x_fd
, &selset
);
113 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
116 void event_process(XEvent
*e
)
127 window
= e
->xmap
.window
;
130 window
= e
->xunmap
.window
;
133 window
= e
->xdestroywindow
.window
;
135 case ConfigureRequest
:
136 window
= e
->xconfigurerequest
.window
;
140 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
141 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
143 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
149 window
= e
->xany
.window
;
152 client
= g_hash_table_lookup(client_map
, &window
);
154 /* grab the lasttime and hack up the state */
158 event_lasttime
= e
->xbutton
.time
;
159 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
160 /* kill off the Button1Mask etc, only want the modifiers */
161 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
162 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
165 event_lasttime
= e
->xkey
.time
;
166 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
167 /* kill off the Button1Mask etc, only want the modifiers */
168 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
169 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
170 /* add to the state the mask of the modifier being pressed, if it is
171 a modifier key being pressed (this is a little ugly..) */
172 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
173 /* kp = modmap->modifiermap;*/
174 /* for (i = 0; i < mask_table_size; ++i) {*/
175 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
176 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
177 /* add the mask for it */
178 /* e->xkey.state |= mask_table[i];*/
179 /* cause the first loop to break; */
180 /* i = mask_table_size;*/
181 /* break;*/ /* get outta here! */
189 event_lasttime
= e
->xkey
.time
;
190 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
191 /* kill off the Button1Mask etc, only want the modifiers */
192 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
193 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
194 /* remove from the state the mask of the modifier being released, if
195 it is a modifier key being released (this is a little ugly..) */
196 kp
= modmap
->modifiermap
;
197 for (i
= 0; i
< mask_table_size
; ++i
) {
198 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
199 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
200 /* remove the mask for it */
201 e
->xkey
.state
&= ~mask_table
[i
];
202 /* cause the first loop to break; */
204 break; /* get outta here! */
211 event_lasttime
= e
->xmotion
.time
;
212 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
213 /* kill off the Button1Mask etc, only want the modifiers */
214 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
215 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
216 /* compress events */
217 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
218 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
219 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
223 event_lasttime
= e
->xproperty
.time
;
227 g_message("FocusIn on %lx mode %d detail %d", window
,
228 e
->xfocus
.mode
, e
->xfocus
.detail
);
230 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
231 because of RevertToPointerRoot. If the focus ends up reverting to
232 pointer root on a workspace change, then the FocusIn event that we
233 want will be of type NotifyAncestor. This situation does not occur
234 for FocusOut, so it is safely ignored there.
236 if (e
->xfocus
.detail
== NotifyInferior
||
237 e
->xfocus
.detail
> NotifyNonlinearVirtual
||
239 /* says a client was not found for the event (or a valid FocusIn
242 e
->xfocus
.window
= None
;
247 g_message("FocusIn on %lx", window
);
252 g_message("FocusOut on %lx mode %d detail %d", window
,
253 e
->xfocus
.mode
, e
->xfocus
.detail
);
255 if (e
->xfocus
.mode
== NotifyGrab
||
256 e
->xfocus
.detail
== NotifyInferior
||
257 e
->xfocus
.detail
== NotifyAncestor
||
258 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return;
261 g_message("FocusOut on %lx", window
);
263 /* Try process a FocusIn first, and if a legit one isn't found, then
264 do the fallback shiznit. */
267 gboolean isfo
= FALSE
;
269 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
272 /* when we have gotten a fi/fo pair, then see if there are any
273 more fo's coming. if there are, then don't fallback just yet
275 if ((isfo
= XCheckTypedEvent(ob_display
, FocusOut
, &fo
)))
276 XPutBackEvent(ob_display
, &fo
);
278 /* secret magic way of event_process telling us that no client
279 was found for the FocusIn event. ^_^ */
280 if (!isfo
&& fi
.xfocus
.window
== None
)
281 focus_fallback(Fallback_NoFocus
);
282 if (fi
.xfocus
.window
== e
->xfocus
.window
)
285 focus_fallback(Fallback_NoFocus
);
290 event_lasttime
= e
->xcrossing
.time
;
291 /* NotifyUngrab occurs when a mouse button is released and the event is
292 caused, like when lowering a window */
293 if (e
->xcrossing
.mode
== NotifyGrab
||
294 e
->xcrossing
.detail
== NotifyInferior
)
298 event_lasttime
= CurrentTime
;
302 /* deal with it in the kernel */
304 event_handle_client(client
, e
);
305 else if (window
== ob_root
)
306 event_handle_root(e
);
307 else if (e
->type
== MapRequest
)
308 client_manage(window
);
309 else if (e
->type
== ConfigureRequest
) {
310 /* unhandled configure requests must be used to configure the
314 xwc
.x
= e
->xconfigurerequest
.x
;
315 xwc
.y
= e
->xconfigurerequest
.y
;
316 xwc
.width
= e
->xconfigurerequest
.width
;
317 xwc
.height
= e
->xconfigurerequest
.height
;
318 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
319 xwc
.sibling
= e
->xconfigurerequest
.above
;
320 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
322 /* we are not to be held responsible if someone sends us an
324 xerror_set_ignore(TRUE
);
325 XConfigureWindow(ob_display
, window
,
326 e
->xconfigurerequest
.value_mask
, &xwc
);
327 xerror_set_ignore(FALSE
);
330 /* dispatch the event to registered handlers */
331 dispatch_x(e
, client
);
334 static void event_handle_root(XEvent
*e
)
340 if (e
->xclient
.format
!= 32) break;
342 msgtype
= e
->xclient
.message_type
;
343 if (msgtype
== prop_atoms
.net_current_desktop
) {
344 unsigned int d
= e
->xclient
.data
.l
[0];
345 if (d
< screen_num_desktops
)
346 screen_set_desktop(d
);
347 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
348 unsigned int d
= e
->xclient
.data
.l
[0];
350 screen_set_num_desktops(d
);
351 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
352 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
356 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
357 screen_update_desktop_names();
358 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
359 screen_update_layout();
364 static void event_handle_client(Client
*client
, XEvent
*e
)
372 focus_set_client(client
);
375 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
378 /* focus state can affect the stacking layer */
379 client_calc_layer(client
);
380 engine_frame_adjust_focus(client
->frame
);
383 if (client_normal(client
)) {
384 if (ob_state
== State_Starting
) {
385 /* move it to the top of the focus order */
386 guint desktop
= client
->desktop
;
387 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
388 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
390 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
392 } else if (config_focus_follow
) {
394 g_message("EnterNotify on %lx, focusing window",
397 client_focus(client
);
401 case ConfigureRequest
:
403 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
404 ConfigureRequest
, &ce
)) {
406 /* XXX if this causes bad things.. we can compress config req's
407 with the same mask. */
408 e
->xconfigurerequest
.value_mask
|=
409 ce
.xconfigurerequest
.value_mask
;
410 if (ce
.xconfigurerequest
.value_mask
& CWX
)
411 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
412 if (ce
.xconfigurerequest
.value_mask
& CWY
)
413 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
414 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
415 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
416 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
417 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
418 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
419 e
->xconfigurerequest
.border_width
=
420 ce
.xconfigurerequest
.border_width
;
421 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
422 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
425 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
426 if (client
->iconic
|| client
->shaded
) return;
428 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
429 client
->border_width
= e
->xconfigurerequest
.border_width
;
431 /* resize, then move, as specified in the EWMH section 7.7 */
432 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
437 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
438 e
->xconfigurerequest
.x
: client
->area
.x
;
439 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
440 e
->xconfigurerequest
.y
: client
->area
.y
;
441 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
442 e
->xconfigurerequest
.width
: client
->area
.width
;
443 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
444 e
->xconfigurerequest
.height
: client
->area
.height
;
446 switch (client
->gravity
) {
447 case NorthEastGravity
:
449 corner
= Corner_TopRight
;
451 case SouthWestGravity
:
453 corner
= Corner_BottomLeft
;
455 case SouthEastGravity
:
456 corner
= Corner_BottomRight
;
458 default: /* NorthWest, Static, etc */
459 corner
= Corner_TopLeft
;
462 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
465 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
466 switch (e
->xconfigurerequest
.detail
) {
469 stacking_lower(client
);
475 stacking_raise(client
);
481 if (client
->ignore_unmaps
) {
482 client
->ignore_unmaps
--;
485 client_unmanage(client
);
488 client_unmanage(client
);
491 /* this is when the client is first taken captive in the frame */
492 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
495 This event is quite rare and is usually handled in unmapHandler.
496 However, if the window is unmapped when the reparent event occurs,
497 the window manager never sees it because an unmap event is not sent
498 to an already unmapped window.
501 /* we don't want the reparent event, put it back on the stack for the
502 X server to deal with after we unmanage the window */
503 XPutBackEvent(ob_display
, e
);
505 client_unmanage(client
);
508 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
509 does, we don't want it! */
510 if (screen_showing_desktop
)
511 screen_show_desktop(FALSE
);
512 client_iconify(client
, FALSE
, TRUE
);
513 if (!client
->frame
->visible
)
514 /* if its not visible still, then don't mess with it */
517 client_shade(client
, FALSE
);
518 client_focus(client
);
519 stacking_raise(client
);
522 /* validate cuz we query stuff off the client here */
523 if (!client_validate(client
)) break;
525 if (e
->xclient
.format
!= 32) return;
527 msgtype
= e
->xclient
.message_type
;
528 if (msgtype
== prop_atoms
.wm_change_state
) {
529 /* compress changes into a single change */
530 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
531 client
->window
, &ce
)) {
532 /* XXX: it would be nice to compress ALL messages of a
533 type, not just messages in a row without other
534 message types between. */
535 if (ce
.xclient
.message_type
!= msgtype
) {
536 XPutBackEvent(ob_display
, &ce
);
539 e
->xclient
= ce
.xclient
;
541 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
542 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
543 /* compress changes into a single change */
544 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
545 client
->window
, &ce
)) {
546 /* XXX: it would be nice to compress ALL messages of a
547 type, not just messages in a row without other
548 message types between. */
549 if (ce
.xclient
.message_type
!= msgtype
) {
550 XPutBackEvent(ob_display
, &ce
);
553 e
->xclient
= ce
.xclient
;
555 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
556 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
557 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
559 } else if (msgtype
== prop_atoms
.net_wm_state
) {
560 /* can't compress these */
561 g_message("net_wm_state %s %ld %ld for 0x%lx",
562 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
563 e
->xclient
.data
.l
[0] == 1 ? "Add" :
564 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
565 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
567 client_set_state(client
, e
->xclient
.data
.l
[0],
568 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
569 } else if (msgtype
== prop_atoms
.net_close_window
) {
570 g_message("net_close_window for 0x%lx", client
->window
);
571 client_close(client
);
572 } else if (msgtype
== prop_atoms
.net_active_window
) {
573 g_message("net_active_window for 0x%lx", client
->window
);
574 if (screen_showing_desktop
)
575 screen_show_desktop(FALSE
);
577 client_iconify(client
, FALSE
, TRUE
);
578 else if (!client
->frame
->visible
)
579 /* if its not visible for other reasons, then don't mess
583 client_shade(client
, FALSE
);
584 client_focus(client
);
585 stacking_raise(client
);
589 /* validate cuz we query stuff off the client here */
590 if (!client_validate(client
)) break;
592 /* compress changes to a single property into a single change */
593 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
594 client
->window
, &ce
)) {
595 /* XXX: it would be nice to compress ALL changes to a property,
596 not just changes in a row without other props between. */
597 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
598 XPutBackEvent(ob_display
, &ce
);
603 msgtype
= e
->xproperty
.atom
;
604 if (msgtype
== XA_WM_NORMAL_HINTS
) {
605 client_update_normal_hints(client
);
606 /* normal hints can make a window non-resizable */
607 client_setup_decor_and_functions(client
);
609 else if (msgtype
== XA_WM_HINTS
)
610 client_update_wmhints(client
);
611 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
612 client_update_transient_for(client
);
613 client_get_type(client
);
614 /* type may have changed, so update the layer */
615 client_calc_layer(client
);
616 client_setup_decor_and_functions(client
);
618 else if (msgtype
== prop_atoms
.net_wm_name
||
619 msgtype
== prop_atoms
.wm_name
)
620 client_update_title(client
);
621 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
622 msgtype
== prop_atoms
.wm_icon_name
)
623 client_update_icon_title(client
);
624 else if (msgtype
== prop_atoms
.wm_class
)
625 client_update_class(client
);
626 else if (msgtype
== prop_atoms
.wm_protocols
) {
627 client_update_protocols(client
);
628 client_setup_decor_and_functions(client
);
630 else if (msgtype
== prop_atoms
.net_wm_strut
)
631 client_update_strut(client
);
632 else if (msgtype
== prop_atoms
.net_wm_icon
)
633 client_update_icons(client
);
634 else if (msgtype
== prop_atoms
.kwm_win_icon
)
635 client_update_kwm_icon(client
);
639 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
640 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
641 engine_frame_adjust_shape(client
->frame
);