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 /*! A list of all possible combinations of keyboard lock masks */
26 static unsigned int mask_list
[8];
27 /*! The value of the mask for the NumLock modifier */
28 static unsigned int NumLockMask
;
29 /*! The value of the mask for the ScrollLock modifier */
30 static unsigned int ScrollLockMask
;
31 /*! The key codes for the modifier keys */
32 static XModifierKeymap
*modmap
;
33 /*! Table of the constant modifier masks */
34 static const int mask_table
[] = {
35 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
36 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
38 static int mask_table_size
;
42 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
44 /* get lock masks that are defined by the display (not constant) */
45 modmap
= XGetModifierMapping(ob_display
);
47 if (modmap
&& modmap
->max_keypermod
> 0) {
49 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
50 /* get the values of the keyboard lock modifiers
51 Note: Caps lock is not retrieved the same way as Scroll and Num
52 lock since it doesn't need to be. */
53 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
54 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
57 for (cnt
= 0; cnt
< size
; ++cnt
) {
58 if (! modmap
->modifiermap
[cnt
]) continue;
60 if (num_lock
== modmap
->modifiermap
[cnt
])
61 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
62 if (scroll_lock
== modmap
->modifiermap
[cnt
])
63 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
68 mask_list
[1] = LockMask
;
69 mask_list
[2] = NumLockMask
;
70 mask_list
[3] = LockMask
| NumLockMask
;
71 mask_list
[4] = ScrollLockMask
;
72 mask_list
[5] = ScrollLockMask
| LockMask
;
73 mask_list
[6] = ScrollLockMask
| NumLockMask
;
74 mask_list
[7] = ScrollLockMask
| LockMask
| NumLockMask
;
79 XFreeModifiermap(modmap
);
91 There are slightly different event retrieval semantics here for
92 local (or high bandwidth) versus remote (or low bandwidth)
93 connections to the display/Xserver.
96 if (!XPending(ob_display
))
100 This XSync allows for far more compression of events, which
101 makes things like Motion events perform far far better. Since
102 it also means network traffic for every event instead of every
103 X events (where X is the number retrieved at a time), it
104 probably should not be used for setups where Openbox is
105 running on a remote/low bandwidth display/Xserver.
107 XSync(ob_display
, FALSE
);
108 if (!XEventsQueued(ob_display
, QueuedAlready
))
111 XNextEvent(ob_display
, &e
);
116 timer_dispatch((GTimeVal
**)&wait
);
117 x_fd
= ConnectionNumber(ob_display
);
119 FD_SET(x_fd
, &selset
);
120 select(x_fd
+ 1, &selset
, NULL
, NULL
, wait
);
123 void event_process(XEvent
*e
)
134 window
= e
->xunmap
.window
;
137 window
= e
->xdestroywindow
.window
;
139 case ConfigureRequest
:
140 window
= e
->xconfigurerequest
.window
;
144 if (e
->type
== extensions_xkb_event_basep
) {
145 switch (((XkbAnyEvent
*)&e
)->xkb_type
) {
147 window
= ((XkbBellNotifyEvent
*)&e
)->window
;
152 window
= e
->xany
.window
;
155 /* grab the lasttime and hack up the state */
159 event_lasttime
= e
->xbutton
.time
;
160 e
->xbutton
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
161 /* kill off the Button1Mask etc, only want the modifiers */
162 e
->xbutton
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
163 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
166 event_lasttime
= e
->xkey
.time
;
167 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
168 /* kill off the Button1Mask etc, only want the modifiers */
169 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
170 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
171 /* add to the state the mask of the modifier being pressed, if it is
172 a modifier key being pressed (this is a little ugly..) */
173 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
174 /* kp = modmap->modifiermap;*/
175 /* for (i = 0; i < mask_table_size; ++i) {*/
176 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
177 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
178 /* add the mask for it */
179 /* e->xkey.state |= mask_table[i];*/
180 /* cause the first loop to break; */
181 /* i = mask_table_size;*/
182 /* break;*/ /* get outta here! */
190 event_lasttime
= e
->xkey
.time
;
191 e
->xkey
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
192 /* kill off the Button1Mask etc, only want the modifiers */
193 e
->xkey
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
194 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
195 /* remove from the state the mask of the modifier being released, if
196 it is a modifier key being released (this is a little ugly..) */
197 kp
= modmap
->modifiermap
;
198 for (i
= 0; i
< mask_table_size
; ++i
) {
199 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
200 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
201 /* remove the mask for it */
202 e
->xkey
.state
&= ~mask_table
[i
];
203 /* cause the first loop to break; */
205 break; /* get outta here! */
212 event_lasttime
= e
->xmotion
.time
;
213 e
->xmotion
.state
&= ~(LockMask
| NumLockMask
| ScrollLockMask
);
214 /* kill off the Button1Mask etc, only want the modifiers */
215 e
->xmotion
.state
&= (ControlMask
| ShiftMask
| Mod1Mask
|
216 Mod2Mask
| Mod3Mask
| Mod4Mask
| Mod5Mask
);
217 /* compress events */
218 while (XCheckTypedWindowEvent(ob_display
, window
, e
->type
, &ce
)) {
219 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
220 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
224 event_lasttime
= e
->xproperty
.time
;
228 if (e
->xfocus
.mode
== NotifyGrab
)
229 /*|| e.xfocus.mode == NotifyUngrab ||*/
231 /* From Metacity, from WindowMaker, ignore all funky pointer
232 root events. Its commented out cuz I don't think we need this
233 at all. If problems arise we can look into it */
234 /*e.xfocus.detail > NotifyNonlinearVirtual) */
235 return; /* skip me! */
236 if (e
->type
== FocusOut
) {
237 /* FocusOut events just make us look for FocusIn events. They
238 are mostly ignored otherwise. */
240 if (XCheckTypedEvent(ob_display
, FocusIn
, &fi
)) {
242 /* dont unfocus the window we just focused! */
243 if (fi
.xfocus
.window
== e
->xfocus
.window
)
250 event_lasttime
= e
->xcrossing
.time
;
254 client
= g_hash_table_lookup(client_map
, (gpointer
)window
);
256 /* deal with it in the kernel */
258 event_handle_client(client
, e
);
259 } else if (window
== ob_root
)
260 event_handle_root(e
);
261 else if (e
->type
== ConfigureRequest
) {
262 /* unhandled configure requests must be used to configure the
266 xwc
.x
= e
->xconfigurerequest
.x
;
267 xwc
.y
= e
->xconfigurerequest
.y
;
268 xwc
.width
= e
->xconfigurerequest
.width
;
269 xwc
.height
= e
->xconfigurerequest
.height
;
270 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
271 xwc
.sibling
= e
->xconfigurerequest
.above
;
272 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
274 g_message("Proxying configure event for 0x%lx", window
);
276 /* we are not to be held responsible if someone sends us an
278 xerror_set_ignore(TRUE
);
279 XConfigureWindow(ob_display
, window
,
280 e
->xconfigurerequest
.value_mask
, &xwc
);
281 xerror_set_ignore(FALSE
);
284 /* dispatch the event to registered handlers */
285 dispatch_x(e
, client
);
288 static void event_handle_root(XEvent
*e
)
294 g_message("MapRequest on root");
295 client_manage(e
->xmap
.window
);
298 if (e
->xclient
.format
!= 32) break;
300 msgtype
= e
->xclient
.message_type
;
301 if (msgtype
== prop_atoms
.net_current_desktop
) {
302 unsigned int d
= e
->xclient
.data
.l
[0];
303 if (d
<= screen_num_desktops
)
304 screen_set_desktop(d
);
305 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
306 unsigned int d
= e
->xclient
.data
.l
[0];
308 screen_set_num_desktops(d
);
309 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
310 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
314 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
315 screen_update_desktop_names();
316 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
317 screen_update_layout();
322 static void event_handle_client(Client
*client
, XEvent
*e
)
329 client
->focused
= TRUE
;
330 engine_frame_adjust_focus(client
->frame
);
332 /* focus state can affect the stacking layer */
333 client_calc_layer(client
);
335 focus_set_client(client
);
338 client
->focused
= FALSE
;
339 engine_frame_adjust_focus(client
->frame
);
341 /* focus state can affect the stacking layer */
342 client_calc_layer(client
);
344 if (focus_client
== client
)
345 focus_set_client(NULL
);
347 case ConfigureRequest
:
348 g_message("ConfigureRequest for window %lx", client
->window
);
350 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
351 ConfigureRequest
, &ce
)) {
352 /* XXX if this causes bad things.. we can compress config req's
353 with the same mask. */
354 e
->xconfigurerequest
.value_mask
|=
355 ce
.xconfigurerequest
.value_mask
;
356 if (ce
.xconfigurerequest
.value_mask
& CWX
)
357 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
358 if (ce
.xconfigurerequest
.value_mask
& CWY
)
359 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
360 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
361 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
362 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
363 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
364 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
365 e
->xconfigurerequest
.border_width
=
366 ce
.xconfigurerequest
.border_width
;
367 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
368 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
371 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
372 if (client
->iconic
|| client
->shaded
) return;
374 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
375 client
->border_width
= e
->xconfigurerequest
.border_width
;
377 /* resize, then move, as specified in the EWMH section 7.7 */
378 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
383 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
384 e
->xconfigurerequest
.x
: client
->area
.x
;
385 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
386 e
->xconfigurerequest
.y
: client
->area
.y
;
387 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
388 e
->xconfigurerequest
.width
: client
->area
.width
;
389 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
390 e
->xconfigurerequest
.height
: client
->area
.height
;
392 switch (client
->gravity
) {
393 case NorthEastGravity
:
395 corner
= Corner_TopRight
;
397 case SouthWestGravity
:
399 corner
= Corner_BottomLeft
;
401 case SouthEastGravity
:
402 corner
= Corner_BottomRight
;
404 default: /* NorthWest, Static, etc */
405 corner
= Corner_TopLeft
;
408 client_configure(client
, corner
, x
, y
, w
, h
, FALSE
, FALSE
);
411 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
412 switch (e
->xconfigurerequest
.detail
) {
415 stacking_lower(client
);
421 stacking_raise(client
);
427 if (client
->ignore_unmaps
) {
428 client
->ignore_unmaps
--;
431 g_message("UnmapNotify for %lx", client
->window
);
432 client_unmanage(client
);
435 g_message("DestroyNotify for %lx", client
->window
);
436 client_unmanage(client
);
439 /* this is when the client is first taken captive in the frame */
440 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
443 This event is quite rare and is usually handled in unmapHandler.
444 However, if the window is unmapped when the reparent event occurs,
445 the window manager never sees it because an unmap event is not sent
446 to an already unmapped window.
449 /* we don't want the reparent event, put it back on the stack for the
450 X server to deal with after we unmanage the window */
451 XPutBackEvent(ob_display
, e
);
453 client_unmanage(client
);
456 /* we shouldn't be able to get this unless we're iconic */
457 g_assert(client
->iconic
);
459 /*HOOKFIRECLIENT(requestactivate, client);XXX*/
462 /* validate cuz we query stuff off the client here */
463 if (!client_validate(client
)) break;
465 if (e
->xclient
.format
!= 32) return;
467 msgtype
= e
->xclient
.message_type
;
468 if (msgtype
== prop_atoms
.wm_change_state
) {
469 /* compress changes into a single change */
470 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
471 client
->window
, &ce
)) {
472 /* XXX: it would be nice to compress ALL messages of a
473 type, not just messages in a row without other
474 message types between. */
475 if (ce
.xclient
.message_type
!= msgtype
) {
476 XPutBackEvent(ob_display
, &ce
);
479 e
->xclient
= ce
.xclient
;
481 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
482 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
483 /* compress changes into a single change */
484 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
485 client
->window
, &ce
)) {
486 /* XXX: it would be nice to compress ALL messages of a
487 type, not just messages in a row without other
488 message types between. */
489 if (ce
.xclient
.message_type
!= msgtype
) {
490 XPutBackEvent(ob_display
, &ce
);
493 e
->xclient
= ce
.xclient
;
495 client_set_desktop(client
, e
->xclient
.data
.l
[0]);
496 } else if (msgtype
== prop_atoms
.net_wm_state
) {
497 /* can't compress these */
498 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
499 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
500 e
->xclient
.data
.l
[0] == 1 ? "Add" :
501 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
502 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
504 client_set_state(client
, e
->xclient
.data
.l
[0],
505 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
506 } else if (msgtype
== prop_atoms
.net_close_window
) {
507 g_message("net_close_window for 0x%lx\n", client
->window
);
508 client_close(client
);
509 } else if (msgtype
== prop_atoms
.net_active_window
) {
510 g_message("net_active_window for 0x%lx\n", client
->window
);
511 if (screen_showing_desktop
)
512 screen_show_desktop(FALSE
);
514 client_iconify(client
, FALSE
, TRUE
);
515 else if (!client
->frame
->visible
)
516 /* if its not visible for other reasons, then don't mess
519 /*HOOKFIRECLIENT(requestactivate, client);XXX*/
523 /* validate cuz we query stuff off the client here */
524 if (!client_validate(client
)) break;
526 /* compress changes to a single property into a single change */
527 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
528 client
->window
, &ce
)) {
529 /* XXX: it would be nice to compress ALL changes to a property,
530 not just changes in a row without other props between. */
531 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
532 XPutBackEvent(ob_display
, &ce
);
537 msgtype
= e
->xproperty
.atom
;
538 if (msgtype
== XA_WM_NORMAL_HINTS
) {
539 client_update_normal_hints(client
);
540 /* normal hints can make a window non-resizable */
541 client_setup_decor_and_functions(client
);
543 else if (msgtype
== XA_WM_HINTS
)
544 client_update_wmhints(client
);
545 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
546 client_update_transient_for(client
);
547 client_get_type(client
);
548 /* type may have changed, so update the layer */
549 client_calc_layer(client
);
550 client_setup_decor_and_functions(client
);
552 else if (msgtype
== prop_atoms
.net_wm_name
||
553 msgtype
== prop_atoms
.wm_name
)
554 client_update_title(client
);
555 else if (msgtype
== prop_atoms
.net_wm_icon_name
||
556 msgtype
== prop_atoms
.wm_icon_name
)
557 client_update_icon_title(client
);
558 else if (msgtype
== prop_atoms
.wm_class
)
559 client_update_class(client
);
560 else if (msgtype
== prop_atoms
.wm_protocols
) {
561 client_update_protocols(client
);
562 client_setup_decor_and_functions(client
);
564 else if (msgtype
== prop_atoms
.net_wm_strut
)
565 client_update_strut(client
);
566 else if (msgtype
== prop_atoms
.net_wm_icon
)
567 client_update_icons(client
);
568 else if (msgtype
== prop_atoms
.kwm_win_icon
)
569 client_update_kwm_icon(client
);