10 #include "extensions.h"
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
22 static void event_process(XEvent
*e
);
23 static void event_handle_root(XEvent
*e
);
24 static void event_handle_client(Client
*c
, XEvent
*e
);
26 Time event_lasttime
= 0;
28 /*! The value of the mask for the NumLock modifier */
29 unsigned int NumLockMask
;
30 /*! The value of the mask for the ScrollLock modifier */
31 unsigned int ScrollLockMask
;
32 /*! The key codes for the modifier keys */
33 static XModifierKeymap
*modmap
;
34 /*! Table of the constant modifier masks */
35 static const int mask_table
[] = {
36 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
37 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
39 static int mask_table_size
;
43 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
45 /* get lock masks that are defined by the display (not constant) */
46 modmap
= XGetModifierMapping(ob_display
);
48 if (modmap
&& modmap
->max_keypermod
> 0) {
50 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
51 /* get the values of the keyboard lock modifiers
52 Note: Caps lock is not retrieved the same way as Scroll and Num
53 lock since it doesn't need to be. */
54 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
55 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
58 for (cnt
= 0; cnt
< size
; ++cnt
) {
59 if (! modmap
->modifiermap
[cnt
]) continue;
61 if (num_lock
== modmap
->modifiermap
[cnt
])
62 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
63 if (scroll_lock
== modmap
->modifiermap
[cnt
])
64 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
71 XFreeModifiermap(modmap
);
83 There are slightly different event retrieval semantics here for
84 local (or high bandwidth) versus remote (or low bandwidth)
85 connections to the display/Xserver.
88 if (!XPending(ob_display
))
92 This XSync allows for far more compression of events, which
93 makes things like Motion events perform far far better. Since
94 it also means network traffic for every event instead of every
95 X events (where X is the number retrieved at a time), it
96 probably should not be used for setups where Openbox is
97 running on a remote/low bandwidth display/Xserver.
99 XSync(ob_display
, FALSE
);
100 if (!XEventsQueued(ob_display
, QueuedAlready
))
103 XNextEvent(ob_display
, &e
);
108 timer_dispatch((GTimeVal
**)&wait
);
109 x_fd
= ConnectionNumber(ob_display
);
111 FD_SET(x_fd
, &selset
);
112 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
115 void event_process(XEvent
*e
)
126 window
= e
->xmap
.window
;
129 window
= e
->xunmap
.window
;
132 window
= e
->xdestroywindow
.window
;
134 case ConfigureRequest
:
135 window
= e
->xconfigurerequest
.window
;
139 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
140 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
142 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
148 window
= e
->xany
.window
;
151 client
= g_hash_table_lookup(client_map
, &window
);
153 /* grab the lasttime and hack up the state */
157 event_lasttime
= e
->xbutton
.time
;
158 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
159 /* kill off the Button1Mask etc, only want the modifiers */
160 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
161 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
164 event_lasttime
= e
->xkey
.time
;
165 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
166 /* kill off the Button1Mask etc, only want the modifiers */
167 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
168 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
169 /* add to the state the mask of the modifier being pressed, if it is
170 a modifier key being pressed (this is a little ugly..) */
171 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
172 /* kp = modmap->modifiermap;*/
173 /* for (i = 0; i < mask_table_size; ++i) {*/
174 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
175 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
176 /* add the mask for it */
177 /* e->xkey.state |= mask_table[i];*/
178 /* cause the first loop to break; */
179 /* i = mask_table_size;*/
180 /* break;*/ /* get outta here! */
188 event_lasttime
= e
->xkey
.time
;
189 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
190 /* kill off the Button1Mask etc, only want the modifiers */
191 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
192 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
193 /* remove from the state the mask of the modifier being released, if
194 it is a modifier key being released (this is a little ugly..) */
195 kp
= modmap
->modifiermap
;
196 for (i
= 0; i
< mask_table_size
; ++i
) {
197 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
198 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
199 /* remove the mask for it */
200 e
->xkey
.state
&= ~mask_table
[i
];
201 /* cause the first loop to break; */
203 break; /* get outta here! */
210 event_lasttime
= e
->xmotion
.time
;
211 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
212 /* kill off the Button1Mask etc, only want the modifiers */
213 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
214 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
215 /* compress events */
216 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
217 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
218 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
222 event_lasttime
= e
->xproperty
.time
;
226 g_message("FocusIn on %lx mode %d detail %d", window
,
227 e
->xfocus
.mode
, e
->xfocus
.detail
);
229 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
230 because of RevertToPointerRoot. If the focus ends up reverting to
231 pointer root on a workspace change, then the FocusIn event that we
232 want will be of type NotifyAncestor. This situation does not occur
233 for FocusOut, so it is safely ignored there.
235 if (e
->xfocus
.detail
== NotifyInferior
||
236 e
->xfocus
.detail
> NotifyNonlinearVirtual
||
238 /* says a client was not found for the event (or a valid FocusIn
241 e
->xfocus
.window
= None
;
246 g_message("FocusIn on %lx", window
);
251 g_message("FocusOut on %lx mode %d detail %d", window
,
252 e
->xfocus
.mode
, e
->xfocus
.detail
);
254 if (e
->xfocus
.mode
== NotifyGrab
||
255 e
->xfocus
.detail
== NotifyInferior
||
256 e
->xfocus
.detail
== NotifyAncestor
||
257 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return;
260 g_message("FocusOut on %lx", window
);
262 /* Try process a FocusIn first, and if a legit one isn't found, then
263 do the fallback shiznit. */
266 gboolean isfo
= FALSE
;
268 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
271 /* when we have gotten a fi/fo pair, then see if there are any
272 more fo's coming. if there are, then don't fallback just yet
274 if ((isfo
= XCheckTypedEvent(ob_display
, FocusOut
, &fo
)))
275 XPutBackEvent(ob_display
, &fo
);
277 /* secret magic way of event_process telling us that no client
278 was found for the FocusIn event. ^_^ */
279 if (!isfo
&& fi
.xfocus
.window
== None
)
280 focus_fallback(FALSE
);
281 if (fi
.xfocus
.window
== e
->xfocus
.window
)
284 focus_fallback(FALSE
);
289 event_lasttime
= e
->xcrossing
.time
;
290 /* NotifyUngrab occurs when a mouse button is released and the event is
291 caused, like when lowering a window */
292 if (e
->xcrossing
.mode
== NotifyGrab
) return;
295 event_lasttime
= CurrentTime
;
299 /* deal with it in the kernel */
301 event_handle_client(client
, e
);
302 else if (window
== ob_root
)
303 event_handle_root(e
);
304 else if (e
->type
== MapRequest
)
305 client_manage(window
);
306 else if (e
->type
== ConfigureRequest
) {
307 /* unhandled configure requests must be used to configure the
311 xwc
.x
= e
->xconfigurerequest
.x
;
312 xwc
.y
= e
->xconfigurerequest
.y
;
313 xwc
.width
= e
->xconfigurerequest
.width
;
314 xwc
.height
= e
->xconfigurerequest
.height
;
315 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
316 xwc
.sibling
= e
->xconfigurerequest
.above
;
317 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
319 /* we are not to be held responsible if someone sends us an
321 xerror_set_ignore(TRUE
);
322 XConfigureWindow(ob_display
, window
,
323 e
->xconfigurerequest
.value_mask
, &xwc
);
324 xerror_set_ignore(FALSE
);
327 /* dispatch the event to registered handlers */
328 dispatch_x(e
, client
);
331 static void event_handle_root(XEvent
*e
)
337 if (e
->xclient
.format
!= 32) break;
339 msgtype
= e
->xclient
.message_type
;
340 if (msgtype
== prop_atoms
.net_current_desktop
) {
341 unsigned int d
= e
->xclient
.data
.l
[0];
342 if (d
< screen_num_desktops
)
343 screen_set_desktop(d
);
344 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
345 unsigned int d
= e
->xclient
.data
.l
[0];
347 screen_set_num_desktops(d
);
348 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
349 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
353 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
354 screen_update_desktop_names();
355 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
356 screen_update_layout();
361 static void event_handle_client(Client
*client
, XEvent
*e
)
369 focus_set_client(client
);
371 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
373 /* focus state can affect the stacking layer */
374 client_calc_layer(client
);
375 engine_frame_adjust_focus(client
->frame
);
378 if (client_normal(client
)) {
379 if (ob_state
== State_Starting
) {
380 /* move it to the top of the focus order */
381 guint desktop
= client
->desktop
;
382 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
383 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
385 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
387 } else if (focus_follow
)
388 client_focus(client
);
391 case ConfigureRequest
:
393 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
394 ConfigureRequest
, &ce
)) {
396 /* XXX if this causes bad things.. we can compress config req's
397 with the same mask. */
398 e
->xconfigurerequest
.value_mask
|=
399 ce
.xconfigurerequest
.value_mask
;
400 if (ce
.xconfigurerequest
.value_mask
& CWX
)
401 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
402 if (ce
.xconfigurerequest
.value_mask
& CWY
)
403 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
404 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
405 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
406 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
407 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
408 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
409 e
->xconfigurerequest
.border_width
=
410 ce
.xconfigurerequest
.border_width
;
411 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
412 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
414 if (i
) g_message("Compressed %d Configures", i
);
416 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
417 if (client
->iconic
|| client
->shaded
) return;
419 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
420 client
->border_width
= e
->xconfigurerequest
.border_width
;
422 /* resize, then move, as specified in the EWMH section 7.7 */
423 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
428 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
429 e
->xconfigurerequest
.x
: client
->area
.x
;
430 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
431 e
->xconfigurerequest
.y
: client
->area
.y
;
432 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
433 e
->xconfigurerequest
.width
: client
->area
.width
;
434 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
435 e
->xconfigurerequest
.height
: client
->area
.height
;
437 switch (client
->gravity
) {
438 case NorthEastGravity
:
440 corner
= Corner_TopRight
;
442 case SouthWestGravity
:
444 corner
= Corner_BottomLeft
;
446 case SouthEastGravity
:
447 corner
= Corner_BottomRight
;
449 default: /* NorthWest, Static, etc */
450 corner
= Corner_TopLeft
;
453 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
456 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
457 switch (e
->xconfigurerequest
.detail
) {
460 stacking_lower(client
);
466 stacking_raise(client
);
472 if (client
->ignore_unmaps
) {
473 client
->ignore_unmaps
--;
476 client_unmanage(client
);
479 client_unmanage(client
);
482 /* this is when the client is first taken captive in the frame */
483 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
486 This event is quite rare and is usually handled in unmapHandler.
487 However, if the window is unmapped when the reparent event occurs,
488 the window manager never sees it because an unmap event is not sent
489 to an already unmapped window.
492 /* we don't want the reparent event, put it back on the stack for the
493 X server to deal with after we unmanage the window */
494 XPutBackEvent(ob_display
, e
);
496 client_unmanage(client
);
499 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
500 does, we don't want it! */
501 if (screen_showing_desktop
)
502 screen_show_desktop(FALSE
);
503 client_iconify(client
, FALSE
, TRUE
);
504 if (!client
->frame
->visible
)
505 /* if its not visible still, then don't mess with it */
508 client_shade(client
, FALSE
);
509 client_focus(client
);
510 stacking_raise(client
);
513 /* validate cuz we query stuff off the client here */
514 if (!client_validate(client
)) break;
516 if (e
->xclient
.format
!= 32) return;
518 msgtype
= e
->xclient
.message_type
;
519 if (msgtype
== prop_atoms
.wm_change_state
) {
520 /* compress changes into a single change */
521 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
522 client
->window
, &ce
)) {
523 /* XXX: it would be nice to compress ALL messages of a
524 type, not just messages in a row without other
525 message types between. */
526 if (ce
.xclient
.message_type
!= msgtype
) {
527 XPutBackEvent(ob_display
, &ce
);
530 e
->xclient
= ce
.xclient
;
532 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
533 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
534 /* compress changes into a single change */
535 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
536 client
->window
, &ce
)) {
537 /* XXX: it would be nice to compress ALL messages of a
538 type, not just messages in a row without other
539 message types between. */
540 if (ce
.xclient
.message_type
!= msgtype
) {
541 XPutBackEvent(ob_display
, &ce
);
544 e
->xclient
= ce
.xclient
;
546 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
547 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
548 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
550 } else if (msgtype
== prop_atoms
.net_wm_state
) {
551 /* can't compress these */
552 g_message("net_wm_state %s %ld %ld for 0x%lx",
553 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
554 e
->xclient
.data
.l
[0] == 1 ? "Add" :
555 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
556 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
558 client_set_state(client
, e
->xclient
.data
.l
[0],
559 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
560 } else if (msgtype
== prop_atoms
.net_close_window
) {
561 g_message("net_close_window for 0x%lx", client
->window
);
562 client_close(client
);
563 } else if (msgtype
== prop_atoms
.net_active_window
) {
564 g_message("net_active_window for 0x%lx", client
->window
);
565 if (screen_showing_desktop
)
566 screen_show_desktop(FALSE
);
568 client_iconify(client
, FALSE
, TRUE
);
569 else if (!client
->frame
->visible
)
570 /* if its not visible for other reasons, then don't mess
574 client_shade(client
, FALSE
);
575 client_focus(client
);
576 stacking_raise(client
);
580 /* validate cuz we query stuff off the client here */
581 if (!client_validate(client
)) break;
583 /* compress changes to a single property into a single change */
584 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
585 client
->window
, &ce
)) {
586 /* XXX: it would be nice to compress ALL changes to a property,
587 not just changes in a row without other props between. */
588 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
589 XPutBackEvent(ob_display
, &ce
);
594 msgtype
= e
->xproperty
.atom
;
595 if (msgtype
== XA_WM_NORMAL_HINTS
) {
596 client_update_normal_hints(client
);
597 /* normal hints can make a window non-resizable */
598 client_setup_decor_and_functions(client
);
600 else if (msgtype
== XA_WM_HINTS
)
601 client_update_wmhints(client
);
602 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
603 client_update_transient_for(client
);
604 client_get_type(client
);
605 /* type may have changed, so update the layer */
606 client_calc_layer(client
);
607 client_setup_decor_and_functions(client
);
609 else if (msgtype
== prop_atoms
.net_wm_name
||
610 msgtype
== prop_atoms
.wm_name
)
611 client_update_title(client
);
612 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
613 msgtype
== prop_atoms
.wm_icon_name
)
614 client_update_icon_title(client
);
615 else if (msgtype
== prop_atoms
.wm_class
)
616 client_update_class(client
);
617 else if (msgtype
== prop_atoms
.wm_protocols
) {
618 client_update_protocols(client
);
619 client_setup_decor_and_functions(client
);
621 else if (msgtype
== prop_atoms
.net_wm_strut
)
622 client_update_strut(client
);
623 else if (msgtype
== prop_atoms
.net_wm_icon
)
624 client_update_icons(client
);
625 else if (msgtype
== prop_atoms
.kwm_win_icon
)
626 client_update_kwm_icon(client
);
630 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
631 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
632 engine_frame_adjust_shape(client
->frame
);