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 /* grab the lasttime and hack up the state */
155 event_lasttime
= e
->xbutton
.time
;
156 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
157 /* kill off the Button1Mask etc, only want the modifiers */
158 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
159 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
162 event_lasttime
= e
->xkey
.time
;
163 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
164 /* kill off the Button1Mask etc, only want the modifiers */
165 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
166 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
167 /* add to the state the mask of the modifier being pressed, if it is
168 a modifier key being pressed (this is a little ugly..) */
169 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
170 /* kp = modmap->modifiermap;*/
171 /* for (i = 0; i < mask_table_size; ++i) {*/
172 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
173 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
174 /* add the mask for it */
175 /* e->xkey.state |= mask_table[i];*/
176 /* cause the first loop to break; */
177 /* i = mask_table_size;*/
178 /* break;*/ /* get outta here! */
186 event_lasttime
= e
->xkey
.time
;
187 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
188 /* kill off the Button1Mask etc, only want the modifiers */
189 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
190 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
191 /* remove from the state the mask of the modifier being released, if
192 it is a modifier key being released (this is a little ugly..) */
193 kp
= modmap
->modifiermap
;
194 for (i
= 0; i
< mask_table_size
; ++i
) {
195 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
196 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
197 /* remove the mask for it */
198 e
->xkey
.state
&= ~mask_table
[i
];
199 /* cause the first loop to break; */
201 break; /* get outta here! */
208 event_lasttime
= e
->xmotion
.time
;
209 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
210 /* kill off the Button1Mask etc, only want the modifiers */
211 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
212 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
213 /* compress events */
214 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
215 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
216 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
220 event_lasttime
= e
->xproperty
.time
;
223 if (e
->xfocus
.mode
== NotifyGrab
||
224 !(e
->xfocus
.detail
== NotifyNonlinearVirtual
||
225 e
->xfocus
.detail
== NotifyNonlinear
))
229 if (e
->xfocus
.mode
== NotifyGrab
||
230 !(e
->xfocus
.detail
== NotifyNonlinearVirtual
||
231 e
->xfocus
.detail
== NotifyNonlinear
))
234 /* FocusOut events just make us look for FocusIn events. They
235 are mostly ignored otherwise. */
238 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
241 if (fi
.xfocus
.window
== e
->xfocus
.window
)
248 event_lasttime
= e
->xcrossing
.time
;
249 /* XXX this caused problems before... but i don't remember why. hah.
250 so back it is. if problems arise again, then try filtering on the
251 detail instead of the mode. */
252 if (e
->xcrossing
.mode
!= NotifyNormal
) return;
256 client
= g_hash_table_lookup(client_map
, &window
);
258 /* deal with it in the kernel */
260 event_handle_client(client
, e
);
261 else if (window
== ob_root
)
262 event_handle_root(e
);
263 else if (e
->type
== MapRequest
)
264 client_manage(window
);
265 else if (e
->type
== ConfigureRequest
) {
266 /* unhandled configure requests must be used to configure the
270 xwc
.x
= e
->xconfigurerequest
.x
;
271 xwc
.y
= e
->xconfigurerequest
.y
;
272 xwc
.width
= e
->xconfigurerequest
.width
;
273 xwc
.height
= e
->xconfigurerequest
.height
;
274 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
275 xwc
.sibling
= e
->xconfigurerequest
.above
;
276 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
278 /* we are not to be held responsible if someone sends us an
280 xerror_set_ignore(TRUE
);
281 XConfigureWindow(ob_display
, window
,
282 e
->xconfigurerequest
.value_mask
, &xwc
);
283 xerror_set_ignore(FALSE
);
286 /* dispatch the event to registered handlers */
287 dispatch_x(e
, client
);
290 static void event_handle_root(XEvent
*e
)
296 if (e
->xclient
.format
!= 32) break;
298 msgtype
= e
->xclient
.message_type
;
299 if (msgtype
== prop_atoms
.net_current_desktop
) {
300 unsigned int d
= e
->xclient
.data
.l
[0];
301 if (d
<= screen_num_desktops
)
302 screen_set_desktop(d
);
303 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
304 unsigned int d
= e
->xclient
.data
.l
[0];
306 screen_set_num_desktops(d
);
307 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
308 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
312 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
313 screen_update_desktop_names();
314 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
315 screen_update_layout();
320 static void event_handle_client(Client
*client
, XEvent
*e
)
329 client_set_focused(client
, e
->type
== FocusIn
);
331 case ConfigureRequest
:
333 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
334 ConfigureRequest
, &ce
)) {
336 /* XXX if this causes bad things.. we can compress config req's
337 with the same mask. */
338 e
->xconfigurerequest
.value_mask
|=
339 ce
.xconfigurerequest
.value_mask
;
340 if (ce
.xconfigurerequest
.value_mask
& CWX
)
341 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
342 if (ce
.xconfigurerequest
.value_mask
& CWY
)
343 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
344 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
345 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
346 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
347 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
348 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
349 e
->xconfigurerequest
.border_width
=
350 ce
.xconfigurerequest
.border_width
;
351 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
352 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
354 if (i
) g_message("Compressed %d Configures", i
);
356 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
357 if (client
->iconic
|| client
->shaded
) return;
359 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
360 client
->border_width
= e
->xconfigurerequest
.border_width
;
362 /* resize, then move, as specified in the EWMH section 7.7 */
363 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
368 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
369 e
->xconfigurerequest
.x
: client
->area
.x
;
370 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
371 e
->xconfigurerequest
.y
: client
->area
.y
;
372 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
373 e
->xconfigurerequest
.width
: client
->area
.width
;
374 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
375 e
->xconfigurerequest
.height
: client
->area
.height
;
377 switch (client
->gravity
) {
378 case NorthEastGravity
:
380 corner
= Corner_TopRight
;
382 case SouthWestGravity
:
384 corner
= Corner_BottomLeft
;
386 case SouthEastGravity
:
387 corner
= Corner_BottomRight
;
389 default: /* NorthWest, Static, etc */
390 corner
= Corner_TopLeft
;
393 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
396 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
397 switch (e
->xconfigurerequest
.detail
) {
400 stacking_lower(client
);
406 stacking_raise(client
);
412 if (client
->ignore_unmaps
) {
413 client
->ignore_unmaps
--;
416 g_message("UnmapNotify for %lx", client
->window
);
417 client_unmanage(client
);
420 g_message("DestroyNotify for %lx", client
->window
);
421 client_unmanage(client
);
424 /* this is when the client is first taken captive in the frame */
425 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
428 This event is quite rare and is usually handled in unmapHandler.
429 However, if the window is unmapped when the reparent event occurs,
430 the window manager never sees it because an unmap event is not sent
431 to an already unmapped window.
434 /* we don't want the reparent event, put it back on the stack for the
435 X server to deal with after we unmanage the window */
436 XPutBackEvent(ob_display
, e
);
438 client_unmanage(client
);
441 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
442 does, we don't want it! */
443 if (screen_showing_desktop
)
444 screen_show_desktop(FALSE
);
445 client_iconify(client
, FALSE
, TRUE
);
446 if (!client
->frame
->visible
)
447 /* if its not visible still, then don't mess with it */
450 client_shade(client
, FALSE
);
451 client_focus(client
);
452 stacking_raise(client
);
455 /* validate cuz we query stuff off the client here */
456 if (!client_validate(client
)) break;
458 if (e
->xclient
.format
!= 32) return;
460 msgtype
= e
->xclient
.message_type
;
461 if (msgtype
== prop_atoms
.wm_change_state
) {
462 /* compress changes into a single change */
463 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
464 client
->window
, &ce
)) {
465 /* XXX: it would be nice to compress ALL messages of a
466 type, not just messages in a row without other
467 message types between. */
468 if (ce
.xclient
.message_type
!= msgtype
) {
469 XPutBackEvent(ob_display
, &ce
);
472 e
->xclient
= ce
.xclient
;
474 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
475 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
476 /* compress changes into a single change */
477 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
478 client
->window
, &ce
)) {
479 /* XXX: it would be nice to compress ALL messages of a
480 type, not just messages in a row without other
481 message types between. */
482 if (ce
.xclient
.message_type
!= msgtype
) {
483 XPutBackEvent(ob_display
, &ce
);
486 e
->xclient
= ce
.xclient
;
488 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
489 } else if (msgtype
== prop_atoms
.net_wm_state
) {
490 /* can't compress these */
491 g_message("net_wm_state %s %ld %ld for 0x%lx",
492 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
493 e
->xclient
.data
.l
[0] == 1 ? "Add" :
494 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
495 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
497 client_set_state(client
, e
->xclient
.data
.l
[0],
498 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
499 } else if (msgtype
== prop_atoms
.net_close_window
) {
500 g_message("net_close_window for 0x%lx", client
->window
);
501 client_close(client
);
502 } else if (msgtype
== prop_atoms
.net_active_window
) {
503 g_message("net_active_window for 0x%lx", client
->window
);
504 if (screen_showing_desktop
)
505 screen_show_desktop(FALSE
);
507 client_iconify(client
, FALSE
, TRUE
);
508 else if (!client
->frame
->visible
)
509 /* if its not visible for other reasons, then don't mess
513 client_shade(client
, FALSE
);
514 client_focus(client
);
515 stacking_raise(client
);
519 /* validate cuz we query stuff off the client here */
520 if (!client_validate(client
)) break;
522 /* compress changes to a single property into a single change */
523 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
524 client
->window
, &ce
)) {
525 /* XXX: it would be nice to compress ALL changes to a property,
526 not just changes in a row without other props between. */
527 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
528 XPutBackEvent(ob_display
, &ce
);
533 msgtype
= e
->xproperty
.atom
;
534 if (msgtype
== XA_WM_NORMAL_HINTS
) {
535 client_update_normal_hints(client
);
536 /* normal hints can make a window non-resizable */
537 client_setup_decor_and_functions(client
);
539 else if (msgtype
== XA_WM_HINTS
)
540 client_update_wmhints(client
);
541 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
542 client_update_transient_for(client
);
543 client_get_type(client
);
544 /* type may have changed, so update the layer */
545 client_calc_layer(client
);
546 client_setup_decor_and_functions(client
);
548 else if (msgtype
== prop_atoms
.net_wm_name
||
549 msgtype
== prop_atoms
.wm_name
)
550 client_update_title(client
);
551 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
552 msgtype
== prop_atoms
.wm_icon_name
)
553 client_update_icon_title(client
);
554 else if (msgtype
== prop_atoms
.wm_class
)
555 client_update_class(client
);
556 else if (msgtype
== prop_atoms
.wm_protocols
) {
557 client_update_protocols(client
);
558 client_setup_decor_and_functions(client
);
560 else if (msgtype
== prop_atoms
.net_wm_strut
)
561 client_update_strut(client
);
562 else if (msgtype
== prop_atoms
.net_wm_icon
)
563 client_update_icons(client
);
564 else if (msgtype
== prop_atoms
.kwm_win_icon
)
565 client_update_kwm_icon(client
);
569 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
570 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
571 engine_frame_adjust_shape(client
->frame
);