10 #include "extensions.h"
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
19 static void event_process(XEvent
*e
);
20 static void event_handle_root(XEvent
*e
);
21 static void event_handle_client(Client
*c
, XEvent
*e
);
23 Time event_lasttime
= 0;
25 /*! The value of the mask for the NumLock modifier */
26 unsigned int NumLockMask
;
27 /*! The value of the mask for the ScrollLock modifier */
28 unsigned int ScrollLockMask
;
29 /*! The key codes for the modifier keys */
30 static XModifierKeymap
*modmap
;
31 /*! Table of the constant modifier masks */
32 static const int mask_table
[] = {
33 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
34 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
36 static int mask_table_size
;
40 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
42 /* get lock masks that are defined by the display (not constant) */
43 modmap
= XGetModifierMapping(ob_display
);
45 if (modmap
&& modmap
->max_keypermod
> 0) {
47 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
48 /* get the values of the keyboard lock modifiers
49 Note: Caps lock is not retrieved the same way as Scroll and Num
50 lock since it doesn't need to be. */
51 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
52 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
55 for (cnt
= 0; cnt
< size
; ++cnt
) {
56 if (! modmap
->modifiermap
[cnt
]) continue;
58 if (num_lock
== modmap
->modifiermap
[cnt
])
59 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
60 if (scroll_lock
== modmap
->modifiermap
[cnt
])
61 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
68 XFreeModifiermap(modmap
);
80 There are slightly different event retrieval semantics here for
81 local (or high bandwidth) versus remote (or low bandwidth)
82 connections to the display/Xserver.
85 if (!XPending(ob_display
))
89 This XSync allows for far more compression of events, which
90 makes things like Motion events perform far far better. Since
91 it also means network traffic for every event instead of every
92 X events (where X is the number retrieved at a time), it
93 probably should not be used for setups where Openbox is
94 running on a remote/low bandwidth display/Xserver.
96 XSync(ob_display
, FALSE
);
97 if (!XEventsQueued(ob_display
, QueuedAlready
))
100 XNextEvent(ob_display
, &e
);
105 timer_dispatch((GTimeVal
**)&wait
);
106 x_fd
= ConnectionNumber(ob_display
);
108 FD_SET(x_fd
, &selset
);
109 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
112 void event_process(XEvent
*e
)
123 window
= e
->xunmap
.window
;
126 window
= e
->xdestroywindow
.window
;
128 case ConfigureRequest
:
129 window
= e
->xconfigurerequest
.window
;
133 if (e
->type
== extensions_xkb_event_basep
) {
134 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
136 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
141 window
= e
->xany
.window
;
144 /* grab the lasttime and hack up the state */
148 event_lasttime
= e
->xbutton
.time
;
149 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
150 /* kill off the Button1Mask etc, only want the modifiers */
151 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
152 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
155 event_lasttime
= e
->xkey
.time
;
156 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
157 /* kill off the Button1Mask etc, only want the modifiers */
158 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
159 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
160 /* add to the state the mask of the modifier being pressed, if it is
161 a modifier key being pressed (this is a little ugly..) */
162 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
163 /* kp = modmap->modifiermap;*/
164 /* for (i = 0; i < mask_table_size; ++i) {*/
165 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
166 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
167 /* add the mask for it */
168 /* e->xkey.state |= mask_table[i];*/
169 /* cause the first loop to break; */
170 /* i = mask_table_size;*/
171 /* break;*/ /* get outta here! */
179 event_lasttime
= e
->xkey
.time
;
180 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
181 /* kill off the Button1Mask etc, only want the modifiers */
182 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
183 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
184 /* remove from the state the mask of the modifier being released, if
185 it is a modifier key being released (this is a little ugly..) */
186 kp
= modmap
->modifiermap
;
187 for (i
= 0; i
< mask_table_size
; ++i
) {
188 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
189 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
190 /* remove the mask for it */
191 e
->xkey
.state
&= ~mask_table
[i
];
192 /* cause the first loop to break; */
194 break; /* get outta here! */
201 event_lasttime
= e
->xmotion
.time
;
202 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
203 /* kill off the Button1Mask etc, only want the modifiers */
204 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
205 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
206 /* compress events */
207 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
208 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
209 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
213 event_lasttime
= e
->xproperty
.time
;
217 if (e
->xfocus
.mode
== NotifyGrab
)
218 /*|| e.xfocus.mode == NotifyUngrab ||*/
220 /* From Metacity, from WindowMaker, ignore all funky pointer
221 root events. Its commented out cuz I don't think we need this
222 at all. If problems arise we can look into it */
223 /*e.xfocus.detail > NotifyNonlinearVirtual) */
224 return; /* skip me! */
225 if (e
->type
== FocusOut
) {
226 /* FocusOut events just make us look for FocusIn events. They
227 are mostly ignored otherwise. */
229 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
231 /* dont unfocus the window we just focused! */
232 if (fi
.xfocus
.window
== e
->xfocus
.window
)
239 event_lasttime
= e
->xcrossing
.time
;
240 /* XXX this caused problems before... but i don't remember why. hah.
241 so back it is. if problems arise again, then try filtering on the
242 detail instead of the mode. */
243 if (e
->xcrossing
.mode
!= NotifyNormal
) return;
247 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
249 /* deal with it in the kernel */
251 event_handle_client(client
, e
);
252 } else if (window
== ob_root
)
253 event_handle_root(e
);
254 else if (e
->type
== ConfigureRequest
) {
255 /* unhandled configure requests must be used to configure the
259 xwc
.x
= e
->xconfigurerequest
.x
;
260 xwc
.y
= e
->xconfigurerequest
.y
;
261 xwc
.width
= e
->xconfigurerequest
.width
;
262 xwc
.height
= e
->xconfigurerequest
.height
;
263 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
264 xwc
.sibling
= e
->xconfigurerequest
.above
;
265 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
267 g_message("Proxying configure event for 0x%lx", window
);
269 /* we are not to be held responsible if someone sends us an
271 xerror_set_ignore(TRUE
);
272 XConfigureWindow(ob_display
, window
,
273 e
->xconfigurerequest
.value_mask
, &xwc
);
274 xerror_set_ignore(FALSE
);
277 /* dispatch the event to registered handlers */
278 dispatch_x(e
, client
);
281 static void event_handle_root(XEvent
*e
)
287 g_message("MapRequest on root");
288 client_manage(e
->xmap
.window
);
291 if (e
->xclient
.format
!= 32) break;
293 msgtype
= e
->xclient
.message_type
;
294 if (msgtype
== prop_atoms
.net_current_desktop
) {
295 unsigned int d
= e
->xclient
.data
.l
[0];
296 if (d
<= screen_num_desktops
)
297 screen_set_desktop(d
);
298 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
299 unsigned int d
= e
->xclient
.data
.l
[0];
301 screen_set_num_desktops(d
);
302 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
303 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
307 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
308 screen_update_desktop_names();
309 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
310 screen_update_layout();
315 static void event_handle_client(Client
*client
, XEvent
*e
)
322 client
->focused
= TRUE
;
323 engine_frame_adjust_focus(client
->frame
);
325 /* focus state can affect the stacking layer */
326 client_calc_layer(client
);
328 if (focus_client
!= client
)
329 focus_set_client(client
);
332 client
->focused
= FALSE
;
333 engine_frame_adjust_focus(client
->frame
);
335 /* focus state can affect the stacking layer */
336 client_calc_layer(client
);
338 if (focus_client
== client
)
339 focus_set_client(NULL
);
341 case ConfigureRequest
:
342 g_message("ConfigureRequest for window %lx", client
->window
);
344 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
345 ConfigureRequest
, &ce
)) {
346 /* XXX if this causes bad things.. we can compress config req's
347 with the same mask. */
348 e
->xconfigurerequest
.value_mask
|=
349 ce
.xconfigurerequest
.value_mask
;
350 if (ce
.xconfigurerequest
.value_mask
& CWX
)
351 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
352 if (ce
.xconfigurerequest
.value_mask
& CWY
)
353 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
354 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
355 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
356 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
357 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
358 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
359 e
->xconfigurerequest
.border_width
=
360 ce
.xconfigurerequest
.border_width
;
361 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
362 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
365 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
366 if (client
->iconic
|| client
->shaded
) return;
368 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
369 client
->border_width
= e
->xconfigurerequest
.border_width
;
371 /* resize, then move, as specified in the EWMH section 7.7 */
372 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
377 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
378 e
->xconfigurerequest
.x
: client
->area
.x
;
379 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
380 e
->xconfigurerequest
.y
: client
->area
.y
;
381 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
382 e
->xconfigurerequest
.width
: client
->area
.width
;
383 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
384 e
->xconfigurerequest
.height
: client
->area
.height
;
386 switch (client
->gravity
) {
387 case NorthEastGravity
:
389 corner
= Corner_TopRight
;
391 case SouthWestGravity
:
393 corner
= Corner_BottomLeft
;
395 case SouthEastGravity
:
396 corner
= Corner_BottomRight
;
398 default: /* NorthWest, Static, etc */
399 corner
= Corner_TopLeft
;
402 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
405 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
406 switch (e
->xconfigurerequest
.detail
) {
409 stacking_lower(client
);
415 stacking_raise(client
);
421 if (client
->ignore_unmaps
) {
422 client
->ignore_unmaps
--;
425 g_message("UnmapNotify for %lx", client
->window
);
426 client_unmanage(client
);
429 g_message("DestroyNotify for %lx", client
->window
);
430 client_unmanage(client
);
433 /* this is when the client is first taken captive in the frame */
434 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
437 This event is quite rare and is usually handled in unmapHandler.
438 However, if the window is unmapped when the reparent event occurs,
439 the window manager never sees it because an unmap event is not sent
440 to an already unmapped window.
443 /* we don't want the reparent event, put it back on the stack for the
444 X server to deal with after we unmanage the window */
445 XPutBackEvent(ob_display
, e
);
447 client_unmanage(client
);
450 /* we shouldn't be able to get this unless we're iconic */
451 g_assert(client
->iconic
);
453 /*HOOKFIRECLIENT(requestactivate, client);XXX*/
456 /* validate cuz we query stuff off the client here */
457 if (!client_validate(client
)) break;
459 if (e
->xclient
.format
!= 32) return;
461 msgtype
= e
->xclient
.message_type
;
462 if (msgtype
== prop_atoms
.wm_change_state
) {
463 /* compress changes into a single change */
464 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
465 client
->window
, &ce
)) {
466 /* XXX: it would be nice to compress ALL messages of a
467 type, not just messages in a row without other
468 message types between. */
469 if (ce
.xclient
.message_type
!= msgtype
) {
470 XPutBackEvent(ob_display
, &ce
);
473 e
->xclient
= ce
.xclient
;
475 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
476 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
477 /* compress changes into a single change */
478 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
479 client
->window
, &ce
)) {
480 /* XXX: it would be nice to compress ALL messages of a
481 type, not just messages in a row without other
482 message types between. */
483 if (ce
.xclient
.message_type
!= msgtype
) {
484 XPutBackEvent(ob_display
, &ce
);
487 e
->xclient
= ce
.xclient
;
489 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
490 } else if (msgtype
== prop_atoms
.net_wm_state
) {
491 /* can't compress these */
492 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
493 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
494 e
->xclient
.data
.l
[0] == 1 ? "Add" :
495 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
496 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
498 client_set_state(client
, e
->xclient
.data
.l
[0],
499 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
500 } else if (msgtype
== prop_atoms
.net_close_window
) {
501 g_message("net_close_window for 0x%lx\n", client
->window
);
502 client_close(client
);
503 } else if (msgtype
== prop_atoms
.net_active_window
) {
504 g_message("net_active_window for 0x%lx\n", client
->window
);
505 if (screen_showing_desktop
)
506 screen_show_desktop(FALSE
);
508 client_iconify(client
, FALSE
, TRUE
);
509 else if (!client
->frame
->visible
)
510 /* if its not visible for other reasons, then don't mess
514 client_shade(client
, FALSE
);
515 client_focus(client
);
516 stacking_raise(client
);
520 /* validate cuz we query stuff off the client here */
521 if (!client_validate(client
)) break;
523 /* compress changes to a single property into a single change */
524 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
525 client
->window
, &ce
)) {
526 /* XXX: it would be nice to compress ALL changes to a property,
527 not just changes in a row without other props between. */
528 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
529 XPutBackEvent(ob_display
, &ce
);
534 msgtype
= e
->xproperty
.atom
;
535 if (msgtype
== XA_WM_NORMAL_HINTS
) {
536 client_update_normal_hints(client
);
537 /* normal hints can make a window non-resizable */
538 client_setup_decor_and_functions(client
);
540 else if (msgtype
== XA_WM_HINTS
)
541 client_update_wmhints(client
);
542 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
543 client_update_transient_for(client
);
544 client_get_type(client
);
545 /* type may have changed, so update the layer */
546 client_calc_layer(client
);
547 client_setup_decor_and_functions(client
);
549 else if (msgtype
== prop_atoms
.net_wm_name
||
550 msgtype
== prop_atoms
.wm_name
)
551 client_update_title(client
);
552 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
553 msgtype
== prop_atoms
.wm_icon_name
)
554 client_update_icon_title(client
);
555 else if (msgtype
== prop_atoms
.wm_class
)
556 client_update_class(client
);
557 else if (msgtype
== prop_atoms
.wm_protocols
) {
558 client_update_protocols(client
);
559 client_setup_decor_and_functions(client
);
561 else if (msgtype
== prop_atoms
.net_wm_strut
)
562 client_update_strut(client
);
563 else if (msgtype
== prop_atoms
.net_wm_icon
)
564 client_update_icons(client
);
565 else if (msgtype
== prop_atoms
.kwm_win_icon
)
566 client_update_kwm_icon(client
);