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 (e
->type
== extensions_xkb_event_basep
) {
140 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
142 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
147 window
= e
->xany
.window
;
150 /* grab the lasttime and hack up the state */
154 event_lasttime
= e
->xbutton
.time
;
155 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
156 /* kill off the Button1Mask etc, only want the modifiers */
157 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
158 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
161 event_lasttime
= e
->xkey
.time
;
162 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
163 /* kill off the Button1Mask etc, only want the modifiers */
164 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
165 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
166 /* add to the state the mask of the modifier being pressed, if it is
167 a modifier key being pressed (this is a little ugly..) */
168 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
169 /* kp = modmap->modifiermap;*/
170 /* for (i = 0; i < mask_table_size; ++i) {*/
171 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
172 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
173 /* add the mask for it */
174 /* e->xkey.state |= mask_table[i];*/
175 /* cause the first loop to break; */
176 /* i = mask_table_size;*/
177 /* break;*/ /* get outta here! */
185 event_lasttime
= e
->xkey
.time
;
186 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
187 /* kill off the Button1Mask etc, only want the modifiers */
188 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
189 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
190 /* remove from the state the mask of the modifier being released, if
191 it is a modifier key being released (this is a little ugly..) */
192 kp
= modmap
->modifiermap
;
193 for (i
= 0; i
< mask_table_size
; ++i
) {
194 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
195 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
196 /* remove the mask for it */
197 e
->xkey
.state
&= ~mask_table
[i
];
198 /* cause the first loop to break; */
200 break; /* get outta here! */
207 event_lasttime
= e
->xmotion
.time
;
208 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
209 /* kill off the Button1Mask etc, only want the modifiers */
210 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
211 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
212 /* compress events */
213 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
214 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
215 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
219 event_lasttime
= e
->xproperty
.time
;
222 if (e
->xfocus
.mode
== NotifyGrab
) return;
223 /* if (e->type == FocusIn && window == focus_backup && focus_client != NULL) {
225 /* Something's focused but we got a focus event for the backup
226 window. this means that something unfocused before we received
227 the new FocusIn. Just ignore it, and refocus what should be
229 /* client_focus(focus_client);
235 if (e
->xfocus
.mode
== NotifyGrab
) return;
236 /*|| e.xfocus.mode == NotifyUngrab ||*/
237 /* From Metacity, from WindowMaker, ignore all funky pointer
238 root events. Its commented out cuz I don't think we need this
239 at all. If problems arise we can look into it */
240 /*e.xfocus.detail > NotifyNonlinearVirtual) */
242 /* FocusOut events just make us look for FocusIn events. They
243 are mostly ignored otherwise. */
246 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
248 /* dont unfocus the window we just focused! */
249 if (fi
.xfocus
.window
== e
->xfocus
.window
)
256 event_lasttime
= e
->xcrossing
.time
;
257 /* XXX this caused problems before... but i don't remember why. hah.
258 so back it is. if problems arise again, then try filtering on the
259 detail instead of the mode. */
260 if (e
->xcrossing
.mode
!= NotifyNormal
) return;
264 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
266 /* deal with it in the kernel */
268 event_handle_client(client
, e
);
269 else if (window
== ob_root
)
270 event_handle_root(e
);
271 else if (e
->type
== MapRequest
)
272 client_manage(window
);
273 else if (e
->type
== ConfigureRequest
) {
274 /* unhandled configure requests must be used to configure the
278 xwc
.x
= e
->xconfigurerequest
.x
;
279 xwc
.y
= e
->xconfigurerequest
.y
;
280 xwc
.width
= e
->xconfigurerequest
.width
;
281 xwc
.height
= e
->xconfigurerequest
.height
;
282 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
283 xwc
.sibling
= e
->xconfigurerequest
.above
;
284 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
286 g_message("Proxying configure event for 0x%lx", window
);
288 /* we are not to be held responsible if someone sends us an
290 xerror_set_ignore(TRUE
);
291 XConfigureWindow(ob_display
, window
,
292 e
->xconfigurerequest
.value_mask
, &xwc
);
293 xerror_set_ignore(FALSE
);
296 /* dispatch the event to registered handlers */
297 dispatch_x(e
, client
);
300 static void event_handle_root(XEvent
*e
)
306 if (e
->xclient
.format
!= 32) break;
308 msgtype
= e
->xclient
.message_type
;
309 if (msgtype
== prop_atoms
.net_current_desktop
) {
310 unsigned int d
= e
->xclient
.data
.l
[0];
311 if (d
<= screen_num_desktops
)
312 screen_set_desktop(d
);
313 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
314 unsigned int d
= e
->xclient
.data
.l
[0];
316 screen_set_num_desktops(d
);
317 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
318 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
322 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
323 screen_update_desktop_names();
324 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
325 screen_update_layout();
330 static void event_handle_client(Client
*client
, XEvent
*e
)
337 if (focus_client
!= client
)
338 focus_set_client(client
);
340 /* focus state can affect the stacking layer */
341 client_calc_layer(client
);
343 engine_frame_adjust_focus(client
->frame
);
346 if (focus_client
== client
)
347 focus_set_client(NULL
);
349 /* focus state can affect the stacking layer */
350 client_calc_layer(client
);
352 engine_frame_adjust_focus(client
->frame
);
354 case ConfigureRequest
:
355 g_message("ConfigureRequest for window %lx", client
->window
);
357 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
358 ConfigureRequest
, &ce
)) {
359 /* XXX if this causes bad things.. we can compress config req's
360 with the same mask. */
361 e
->xconfigurerequest
.value_mask
|=
362 ce
.xconfigurerequest
.value_mask
;
363 if (ce
.xconfigurerequest
.value_mask
& CWX
)
364 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
365 if (ce
.xconfigurerequest
.value_mask
& CWY
)
366 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
367 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
368 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
369 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
370 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
371 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
372 e
->xconfigurerequest
.border_width
=
373 ce
.xconfigurerequest
.border_width
;
374 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
375 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
378 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
379 if (client
->iconic
|| client
->shaded
) return;
381 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
382 client
->border_width
= e
->xconfigurerequest
.border_width
;
384 /* resize, then move, as specified in the EWMH section 7.7 */
385 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
390 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
391 e
->xconfigurerequest
.x
: client
->area
.x
;
392 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
393 e
->xconfigurerequest
.y
: client
->area
.y
;
394 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
395 e
->xconfigurerequest
.width
: client
->area
.width
;
396 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
397 e
->xconfigurerequest
.height
: client
->area
.height
;
399 switch (client
->gravity
) {
400 case NorthEastGravity
:
402 corner
= Corner_TopRight
;
404 case SouthWestGravity
:
406 corner
= Corner_BottomLeft
;
408 case SouthEastGravity
:
409 corner
= Corner_BottomRight
;
411 default: /* NorthWest, Static, etc */
412 corner
= Corner_TopLeft
;
415 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
418 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
419 switch (e
->xconfigurerequest
.detail
) {
422 stacking_lower(client
);
428 stacking_raise(client
);
434 if (client
->ignore_unmaps
) {
435 client
->ignore_unmaps
--;
438 g_message("UnmapNotify for %lx", client
->window
);
439 client_unmanage(client
);
442 g_message("DestroyNotify for %lx", client
->window
);
443 client_unmanage(client
);
446 /* this is when the client is first taken captive in the frame */
447 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
450 This event is quite rare and is usually handled in unmapHandler.
451 However, if the window is unmapped when the reparent event occurs,
452 the window manager never sees it because an unmap event is not sent
453 to an already unmapped window.
456 /* we don't want the reparent event, put it back on the stack for the
457 X server to deal with after we unmanage the window */
458 XPutBackEvent(ob_display
, e
);
460 client_unmanage(client
);
463 if (screen_showing_desktop
)
464 screen_show_desktop(FALSE
);
465 client_iconify(client
, FALSE
, TRUE
);
466 if (!client
->frame
->visible
)
467 /* if its not visible still, then don't mess with it */
470 client_shade(client
, FALSE
);
471 client_focus(client
);
472 stacking_raise(client
);
475 /* validate cuz we query stuff off the client here */
476 if (!client_validate(client
)) break;
478 if (e
->xclient
.format
!= 32) return;
480 msgtype
= e
->xclient
.message_type
;
481 if (msgtype
== prop_atoms
.wm_change_state
) {
482 /* compress changes into a single change */
483 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
484 client
->window
, &ce
)) {
485 /* XXX: it would be nice to compress ALL messages of a
486 type, not just messages in a row without other
487 message types between. */
488 if (ce
.xclient
.message_type
!= msgtype
) {
489 XPutBackEvent(ob_display
, &ce
);
492 e
->xclient
= ce
.xclient
;
494 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
495 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
496 /* compress changes into a single change */
497 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
498 client
->window
, &ce
)) {
499 /* XXX: it would be nice to compress ALL messages of a
500 type, not just messages in a row without other
501 message types between. */
502 if (ce
.xclient
.message_type
!= msgtype
) {
503 XPutBackEvent(ob_display
, &ce
);
506 e
->xclient
= ce
.xclient
;
508 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
509 } else if (msgtype
== prop_atoms
.net_wm_state
) {
510 /* can't compress these */
511 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
512 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
513 e
->xclient
.data
.l
[0] == 1 ? "Add" :
514 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
515 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
517 client_set_state(client
, e
->xclient
.data
.l
[0],
518 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
519 } else if (msgtype
== prop_atoms
.net_close_window
) {
520 g_message("net_close_window for 0x%lx\n", client
->window
);
521 client_close(client
);
522 } else if (msgtype
== prop_atoms
.net_active_window
) {
523 g_message("net_active_window for 0x%lx\n", client
->window
);
524 if (screen_showing_desktop
)
525 screen_show_desktop(FALSE
);
527 client_iconify(client
, FALSE
, TRUE
);
528 else if (!client
->frame
->visible
)
529 /* if its not visible for other reasons, then don't mess
533 client_shade(client
, FALSE
);
534 client_focus(client
);
535 stacking_raise(client
);
539 /* validate cuz we query stuff off the client here */
540 if (!client_validate(client
)) break;
542 /* compress changes to a single property into a single change */
543 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
544 client
->window
, &ce
)) {
545 /* XXX: it would be nice to compress ALL changes to a property,
546 not just changes in a row without other props between. */
547 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
548 XPutBackEvent(ob_display
, &ce
);
553 msgtype
= e
->xproperty
.atom
;
554 if (msgtype
== XA_WM_NORMAL_HINTS
) {
555 client_update_normal_hints(client
);
556 /* normal hints can make a window non-resizable */
557 client_setup_decor_and_functions(client
);
559 else if (msgtype
== XA_WM_HINTS
)
560 client_update_wmhints(client
);
561 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
562 client_update_transient_for(client
);
563 client_get_type(client
);
564 /* type may have changed, so update the layer */
565 client_calc_layer(client
);
566 client_setup_decor_and_functions(client
);
568 else if (msgtype
== prop_atoms
.net_wm_name
||
569 msgtype
== prop_atoms
.wm_name
)
570 client_update_title(client
);
571 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
572 msgtype
== prop_atoms
.wm_icon_name
)
573 client_update_icon_title(client
);
574 else if (msgtype
== prop_atoms
.wm_class
)
575 client_update_class(client
);
576 else if (msgtype
== prop_atoms
.wm_protocols
) {
577 client_update_protocols(client
);
578 client_setup_decor_and_functions(client
);
580 else if (msgtype
== prop_atoms
.net_wm_strut
)
581 client_update_strut(client
);
582 else if (msgtype
== prop_atoms
.net_wm_icon
)
583 client_update_icons(client
);
584 else if (msgtype
== prop_atoms
.kwm_win_icon
)
585 client_update_kwm_icon(client
);