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 /* grab the lasttime and hack up the state */
156 event_lasttime
= e
->xbutton
.time
;
157 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
158 /* kill off the Button1Mask etc, only want the modifiers */
159 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
160 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
163 event_lasttime
= e
->xkey
.time
;
164 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
165 /* kill off the Button1Mask etc, only want the modifiers */
166 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
167 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
168 /* add to the state the mask of the modifier being pressed, if it is
169 a modifier key being pressed (this is a little ugly..) */
170 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
171 /* kp = modmap->modifiermap;*/
172 /* for (i = 0; i < mask_table_size; ++i) {*/
173 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
174 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
175 /* add the mask for it */
176 /* e->xkey.state |= mask_table[i];*/
177 /* cause the first loop to break; */
178 /* i = mask_table_size;*/
179 /* break;*/ /* get outta here! */
187 event_lasttime
= e
->xkey
.time
;
188 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
189 /* kill off the Button1Mask etc, only want the modifiers */
190 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
191 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
192 /* remove from the state the mask of the modifier being released, if
193 it is a modifier key being released (this is a little ugly..) */
194 kp
= modmap
->modifiermap
;
195 for (i
= 0; i
< mask_table_size
; ++i
) {
196 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
197 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
198 /* remove the mask for it */
199 e
->xkey
.state
&= ~mask_table
[i
];
200 /* cause the first loop to break; */
202 break; /* get outta here! */
209 event_lasttime
= e
->xmotion
.time
;
210 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
211 /* kill off the Button1Mask etc, only want the modifiers */
212 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
213 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
214 /* compress events */
215 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
216 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
217 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
221 event_lasttime
= e
->xproperty
.time
;
224 g_message("FocusIn on %lx mode %d detail %d", window
,
225 e
->xfocus
.mode
, e
->xfocus
.detail
);
226 if (e
->xfocus
.detail
== NotifyInferior
||
227 e
->xfocus
.detail
== NotifyAncestor
||
228 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return;
229 g_message("FocusIn on %lx", window
);
232 g_message("FocusOut on %lx mode %d detail %d", window
,
233 e
->xfocus
.mode
, e
->xfocus
.detail
);
234 if (e
->xfocus
.detail
== NotifyInferior
||
235 e
->xfocus
.detail
== NotifyAncestor
||
236 e
->xfocus
.detail
> NotifyNonlinearVirtual
) return;
238 g_message("FocusOut on %lx", window
);
239 /* FocusOut events just make us look for FocusIn events. They
240 are mostly ignored otherwise. */
243 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
246 if (fi
.xfocus
.window
== e
->xfocus
.window
)
248 /* secret magic way of event_process telling us that no client
249 was found for the FocusIn event */
250 if (fi
.xfocus
.window
== None
)
251 focus_set_client(NULL
);
253 focus_set_client(NULL
);
258 event_lasttime
= e
->xcrossing
.time
;
259 /* NotifyUngrab occurs when a mouse button is released and the event is
260 caused, like when lowering a window */
261 if (e
->xcrossing
.mode
== NotifyGrab
) return;
264 event_lasttime
= CurrentTime
;
268 client
= g_hash_table_lookup(client_map
, &window
);
270 /* deal with it in the kernel */
272 event_handle_client(client
, e
);
273 else if (window
== ob_root
)
274 event_handle_root(e
);
275 else if (e
->type
== MapRequest
)
276 client_manage(window
);
277 else if (e
->type
== FocusIn
)
278 e
->xfocus
.window
= None
; /* says no client was found for the event */
279 else if (e
->type
== ConfigureRequest
) {
280 /* unhandled configure requests must be used to configure the
284 xwc
.x
= e
->xconfigurerequest
.x
;
285 xwc
.y
= e
->xconfigurerequest
.y
;
286 xwc
.width
= e
->xconfigurerequest
.width
;
287 xwc
.height
= e
->xconfigurerequest
.height
;
288 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
289 xwc
.sibling
= e
->xconfigurerequest
.above
;
290 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
292 /* we are not to be held responsible if someone sends us an
294 xerror_set_ignore(TRUE
);
295 XConfigureWindow(ob_display
, window
,
296 e
->xconfigurerequest
.value_mask
, &xwc
);
297 xerror_set_ignore(FALSE
);
300 /* dispatch the event to registered handlers */
301 dispatch_x(e
, client
);
304 static void event_handle_root(XEvent
*e
)
310 if (e
->xclient
.format
!= 32) break;
312 msgtype
= e
->xclient
.message_type
;
313 if (msgtype
== prop_atoms
.net_current_desktop
) {
314 unsigned int d
= e
->xclient
.data
.l
[0];
315 if (d
< screen_num_desktops
)
316 screen_set_desktop(d
);
317 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
318 unsigned int d
= e
->xclient
.data
.l
[0];
320 screen_set_num_desktops(d
);
321 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
322 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
326 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
327 screen_update_desktop_names();
328 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
329 screen_update_layout();
334 static void event_handle_client(Client
*client
, XEvent
*e
)
339 ConfigValue focus_follow
;
343 focus_set_client(client
);
345 g_message("Focus%s on client for %lx", (e
->type
==FocusIn
?"In":"Out"),
347 /* focus state can affect the stacking layer */
348 client_calc_layer(client
);
349 engine_frame_adjust_focus(client
->frame
);
352 if (ob_state
== State_Starting
) {
353 /* move it to the top of the focus order */
354 guint desktop
= client
->desktop
;
355 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
356 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
], client
);
357 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],client
);
359 if (!config_get("focusFollowsMouse", Config_Bool
, &focus_follow
))
360 g_assert_not_reached();
361 if (focus_follow
.bool)
362 client_focus(client
);
365 case ConfigureRequest
:
367 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
368 ConfigureRequest
, &ce
)) {
370 /* XXX if this causes bad things.. we can compress config req's
371 with the same mask. */
372 e
->xconfigurerequest
.value_mask
|=
373 ce
.xconfigurerequest
.value_mask
;
374 if (ce
.xconfigurerequest
.value_mask
& CWX
)
375 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
376 if (ce
.xconfigurerequest
.value_mask
& CWY
)
377 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
378 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
379 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
380 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
381 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
382 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
383 e
->xconfigurerequest
.border_width
=
384 ce
.xconfigurerequest
.border_width
;
385 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
386 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
388 if (i
) g_message("Compressed %d Configures", i
);
390 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
391 if (client
->iconic
|| client
->shaded
) return;
393 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
394 client
->border_width
= e
->xconfigurerequest
.border_width
;
396 /* resize, then move, as specified in the EWMH section 7.7 */
397 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
402 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
403 e
->xconfigurerequest
.x
: client
->area
.x
;
404 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
405 e
->xconfigurerequest
.y
: client
->area
.y
;
406 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
407 e
->xconfigurerequest
.width
: client
->area
.width
;
408 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
409 e
->xconfigurerequest
.height
: client
->area
.height
;
411 switch (client
->gravity
) {
412 case NorthEastGravity
:
414 corner
= Corner_TopRight
;
416 case SouthWestGravity
:
418 corner
= Corner_BottomLeft
;
420 case SouthEastGravity
:
421 corner
= Corner_BottomRight
;
423 default: /* NorthWest, Static, etc */
424 corner
= Corner_TopLeft
;
427 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
430 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
431 switch (e
->xconfigurerequest
.detail
) {
434 stacking_lower(client
);
440 stacking_raise(client
);
446 if (client
->ignore_unmaps
) {
447 client
->ignore_unmaps
--;
450 client_unmanage(client
);
453 client_unmanage(client
);
456 /* this is when the client is first taken captive in the frame */
457 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
460 This event is quite rare and is usually handled in unmapHandler.
461 However, if the window is unmapped when the reparent event occurs,
462 the window manager never sees it because an unmap event is not sent
463 to an already unmapped window.
466 /* we don't want the reparent event, put it back on the stack for the
467 X server to deal with after we unmanage the window */
468 XPutBackEvent(ob_display
, e
);
470 client_unmanage(client
);
473 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
474 does, we don't want it! */
475 if (screen_showing_desktop
)
476 screen_show_desktop(FALSE
);
477 client_iconify(client
, FALSE
, TRUE
);
478 if (!client
->frame
->visible
)
479 /* if its not visible still, then don't mess with it */
482 client_shade(client
, FALSE
);
483 client_focus(client
);
484 stacking_raise(client
);
487 /* validate cuz we query stuff off the client here */
488 if (!client_validate(client
)) break;
490 if (e
->xclient
.format
!= 32) return;
492 msgtype
= e
->xclient
.message_type
;
493 if (msgtype
== prop_atoms
.wm_change_state
) {
494 /* compress changes into a single change */
495 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
496 client
->window
, &ce
)) {
497 /* XXX: it would be nice to compress ALL messages of a
498 type, not just messages in a row without other
499 message types between. */
500 if (ce
.xclient
.message_type
!= msgtype
) {
501 XPutBackEvent(ob_display
, &ce
);
504 e
->xclient
= ce
.xclient
;
506 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
507 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
508 /* compress changes into a single change */
509 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
510 client
->window
, &ce
)) {
511 /* XXX: it would be nice to compress ALL messages of a
512 type, not just messages in a row without other
513 message types between. */
514 if (ce
.xclient
.message_type
!= msgtype
) {
515 XPutBackEvent(ob_display
, &ce
);
518 e
->xclient
= ce
.xclient
;
520 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
521 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
522 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0]);
523 } else if (msgtype
== prop_atoms
.net_wm_state
) {
524 /* can't compress these */
525 g_message("net_wm_state %s %ld %ld for 0x%lx",
526 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
527 e
->xclient
.data
.l
[0] == 1 ? "Add" :
528 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
529 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
531 client_set_state(client
, e
->xclient
.data
.l
[0],
532 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
533 } else if (msgtype
== prop_atoms
.net_close_window
) {
534 g_message("net_close_window for 0x%lx", client
->window
);
535 client_close(client
);
536 } else if (msgtype
== prop_atoms
.net_active_window
) {
537 g_message("net_active_window for 0x%lx", client
->window
);
538 if (screen_showing_desktop
)
539 screen_show_desktop(FALSE
);
541 client_iconify(client
, FALSE
, TRUE
);
542 else if (!client
->frame
->visible
)
543 /* if its not visible for other reasons, then don't mess
547 client_shade(client
, FALSE
);
548 client_focus(client
);
549 stacking_raise(client
);
553 /* validate cuz we query stuff off the client here */
554 if (!client_validate(client
)) break;
556 /* compress changes to a single property into a single change */
557 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
558 client
->window
, &ce
)) {
559 /* XXX: it would be nice to compress ALL changes to a property,
560 not just changes in a row without other props between. */
561 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
562 XPutBackEvent(ob_display
, &ce
);
567 msgtype
= e
->xproperty
.atom
;
568 if (msgtype
== XA_WM_NORMAL_HINTS
) {
569 client_update_normal_hints(client
);
570 /* normal hints can make a window non-resizable */
571 client_setup_decor_and_functions(client
);
573 else if (msgtype
== XA_WM_HINTS
)
574 client_update_wmhints(client
);
575 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
576 client_update_transient_for(client
);
577 client_get_type(client
);
578 /* type may have changed, so update the layer */
579 client_calc_layer(client
);
580 client_setup_decor_and_functions(client
);
582 else if (msgtype
== prop_atoms
.net_wm_name
||
583 msgtype
== prop_atoms
.wm_name
)
584 client_update_title(client
);
585 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
586 msgtype
== prop_atoms
.wm_icon_name
)
587 client_update_icon_title(client
);
588 else if (msgtype
== prop_atoms
.wm_class
)
589 client_update_class(client
);
590 else if (msgtype
== prop_atoms
.wm_protocols
) {
591 client_update_protocols(client
);
592 client_setup_decor_and_functions(client
);
594 else if (msgtype
== prop_atoms
.net_wm_strut
)
595 client_update_strut(client
);
596 else if (msgtype
== prop_atoms
.net_wm_icon
)
597 client_update_icons(client
);
598 else if (msgtype
== prop_atoms
.kwm_win_icon
)
599 client_update_kwm_icon(client
);
603 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
604 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
605 engine_frame_adjust_shape(client
->frame
);