]> Dogcows Code - chaz/openbox/blob - openbox/event.c
7e0dc0aaf0aba1a1bf115fe59fdcb8fe580ac3c1
[chaz/openbox] / openbox / event.c
1 #include "openbox.h"
2 #include "client.h"
3 #include "config.h"
4 #include "xerror.h"
5 #include "prop.h"
6 #include "screen.h"
7 #include "frame.h"
8 #include "engine.h"
9 #include "focus.h"
10 #include "stacking.h"
11 #include "extensions.h"
12 #include "timer.h"
13 #include "engine.h"
14 #include "dispatch.h"
15
16 #include <X11/Xlib.h>
17 #include <X11/keysym.h>
18 #include <X11/Xatom.h>
19 #ifdef HAVE_SYS_SELECT_H
20 # include <sys/select.h>
21 #endif
22
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);
26
27 Time event_lasttime = 0;
28
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
39 };
40 static int mask_table_size;
41
42 void event_startup()
43 {
44 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
45
46 /* get lock masks that are defined by the display (not constant) */
47 modmap = XGetModifierMapping(ob_display);
48 g_assert(modmap);
49 if (modmap && modmap->max_keypermod > 0) {
50 size_t cnt;
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,
57 XK_Scroll_Lock);
58
59 for (cnt = 0; cnt < size; ++cnt) {
60 if (! modmap->modifiermap[cnt]) continue;
61
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];
66 }
67 }
68 }
69
70 void event_shutdown()
71 {
72 XFreeModifiermap(modmap);
73 }
74
75 void event_loop()
76 {
77 fd_set selset;
78 XEvent e;
79 int x_fd;
80 struct timeval *wait;
81
82 while (TRUE) {
83 /*
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.
87 */
88 if (ob_remote) {
89 if (!XPending(ob_display))
90 break;
91 } else {
92 /*
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.
99 */
100 XSync(ob_display, FALSE);
101 if (!XEventsQueued(ob_display, QueuedAlready))
102 break;
103 }
104 XNextEvent(ob_display, &e);
105
106 event_process(&e);
107 }
108
109 timer_dispatch((GTimeVal**)&wait);
110 x_fd = ConnectionNumber(ob_display);
111 FD_ZERO(&selset);
112 FD_SET(x_fd, &selset);
113 select(x_fd + 1, &selset, NULL, NULL, wait);
114 }
115
116 void event_process(XEvent *e)
117 {
118 XEvent ce;
119 KeyCode *kp;
120 Window window;
121 int i, k;
122 Client *client;
123
124 /* pick a window */
125 switch (e->type) {
126 case MapRequest:
127 window = e->xmap.window;
128 break;
129 case UnmapNotify:
130 window = e->xunmap.window;
131 break;
132 case DestroyNotify:
133 window = e->xdestroywindow.window;
134 break;
135 case ConfigureRequest:
136 window = e->xconfigurerequest.window;
137 break;
138 default:
139 #ifdef XKB
140 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
141 switch (((XkbAnyEvent*)&e)->xkb_type) {
142 case XkbBellNotify:
143 window = ((XkbBellNotifyEvent*)&e)->window;
144 default:
145 window = None;
146 }
147 } else
148 #endif
149 window = e->xany.window;
150 }
151
152 /* grab the lasttime and hack up the state */
153 switch (e->type) {
154 case ButtonPress:
155 case ButtonRelease:
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);
161 break;
162 case KeyPress:
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! */
180 /* }*/
181 /* ++kp;*/
182 /* }*/
183 /* }*/
184
185 break;
186 case KeyRelease:
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; */
201 i = mask_table_size;
202 break; /* get outta here! */
203 }
204 ++kp;
205 }
206 }
207 break;
208 case MotionNotify:
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;
218 }
219 break;
220 case PropertyNotify:
221 event_lasttime = e->xproperty.time;
222 break;
223 case FocusIn:
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);
230 break;
231 case FocusOut:
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;
237
238 g_message("FocusOut on %lx", window);
239 /* FocusOut events just make us look for FocusIn events. They
240 are mostly ignored otherwise. */
241 {
242 XEvent fi;
243 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
244 event_process(&fi);
245
246 if (fi.xfocus.window == e->xfocus.window)
247 return;
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_fallback(FALSE);
252 } else
253 focus_fallback(FALSE);
254 }
255 break;
256 case EnterNotify:
257 case LeaveNotify:
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;
262 break;
263 default:
264 event_lasttime = CurrentTime;
265 break;
266 }
267
268 client = g_hash_table_lookup(client_map, &window);
269
270 /* deal with it in the kernel */
271 if (client)
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
281 window directly */
282 XWindowChanges xwc;
283
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;
291
292 /* we are not to be held responsible if someone sends us an
293 invalid request! */
294 xerror_set_ignore(TRUE);
295 XConfigureWindow(ob_display, window,
296 e->xconfigurerequest.value_mask, &xwc);
297 xerror_set_ignore(FALSE);
298 }
299
300 /* dispatch the event to registered handlers */
301 dispatch_x(e, client);
302 }
303
304 static void event_handle_root(XEvent *e)
305 {
306 Atom msgtype;
307
308 switch(e->type) {
309 case ClientMessage:
310 if (e->xclient.format != 32) break;
311
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];
319 if (d > 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);
323 }
324 break;
325 case PropertyNotify:
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();
330 break;
331 }
332 }
333
334 static void event_handle_client(Client *client, XEvent *e)
335 {
336 XEvent ce;
337 Atom msgtype;
338 int i=0;
339 ConfigValue focus_follow;
340
341 switch (e->type) {
342 case FocusIn:
343 focus_set_client(client);
344 case FocusOut:
345 g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
346 client->window);
347 /* focus state can affect the stacking layer */
348 client_calc_layer(client);
349 engine_frame_adjust_focus(client->frame);
350 break;
351 case EnterNotify:
352 if (client_normal(client)) {
353 if (ob_state == State_Starting) {
354 /* move it to the top of the focus order */
355 guint desktop = client->desktop;
356 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
357 focus_order[desktop] = g_list_remove(focus_order[desktop],
358 client);
359 focus_order[desktop] = g_list_prepend(focus_order[desktop],
360 client);
361 } else {
362 if (!config_get("focusFollowsMouse",Config_Bool,&focus_follow))
363 g_assert_not_reached();
364 if (focus_follow.bool)
365 client_focus(client);
366 }
367 }
368 break;
369 case ConfigureRequest:
370 /* compress these */
371 while (XCheckTypedWindowEvent(ob_display, client->window,
372 ConfigureRequest, &ce)) {
373 ++i;
374 /* XXX if this causes bad things.. we can compress config req's
375 with the same mask. */
376 e->xconfigurerequest.value_mask |=
377 ce.xconfigurerequest.value_mask;
378 if (ce.xconfigurerequest.value_mask & CWX)
379 e->xconfigurerequest.x = ce.xconfigurerequest.x;
380 if (ce.xconfigurerequest.value_mask & CWY)
381 e->xconfigurerequest.y = ce.xconfigurerequest.y;
382 if (ce.xconfigurerequest.value_mask & CWWidth)
383 e->xconfigurerequest.width = ce.xconfigurerequest.width;
384 if (ce.xconfigurerequest.value_mask & CWHeight)
385 e->xconfigurerequest.height = ce.xconfigurerequest.height;
386 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
387 e->xconfigurerequest.border_width =
388 ce.xconfigurerequest.border_width;
389 if (ce.xconfigurerequest.value_mask & CWStackMode)
390 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
391 }
392 if (i) g_message("Compressed %d Configures", i);
393
394 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
395 if (client->iconic || client->shaded) return;
396
397 if (e->xconfigurerequest.value_mask & CWBorderWidth)
398 client->border_width = e->xconfigurerequest.border_width;
399
400 /* resize, then move, as specified in the EWMH section 7.7 */
401 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
402 CWX | CWY)) {
403 int x, y, w, h;
404 Corner corner;
405
406 x = (e->xconfigurerequest.value_mask & CWX) ?
407 e->xconfigurerequest.x : client->area.x;
408 y = (e->xconfigurerequest.value_mask & CWY) ?
409 e->xconfigurerequest.y : client->area.y;
410 w = (e->xconfigurerequest.value_mask & CWWidth) ?
411 e->xconfigurerequest.width : client->area.width;
412 h = (e->xconfigurerequest.value_mask & CWHeight) ?
413 e->xconfigurerequest.height : client->area.height;
414
415 switch (client->gravity) {
416 case NorthEastGravity:
417 case EastGravity:
418 corner = Corner_TopRight;
419 break;
420 case SouthWestGravity:
421 case SouthGravity:
422 corner = Corner_BottomLeft;
423 break;
424 case SouthEastGravity:
425 corner = Corner_BottomRight;
426 break;
427 default: /* NorthWest, Static, etc */
428 corner = Corner_TopLeft;
429 }
430
431 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
432 }
433
434 if (e->xconfigurerequest.value_mask & CWStackMode) {
435 switch (e->xconfigurerequest.detail) {
436 case Below:
437 case BottomIf:
438 stacking_lower(client);
439 break;
440
441 case Above:
442 case TopIf:
443 default:
444 stacking_raise(client);
445 break;
446 }
447 }
448 break;
449 case UnmapNotify:
450 if (client->ignore_unmaps) {
451 client->ignore_unmaps--;
452 break;
453 }
454 client_unmanage(client);
455 break;
456 case DestroyNotify:
457 client_unmanage(client);
458 break;
459 case ReparentNotify:
460 /* this is when the client is first taken captive in the frame */
461 if (e->xreparent.parent == client->frame->plate) break;
462
463 /*
464 This event is quite rare and is usually handled in unmapHandler.
465 However, if the window is unmapped when the reparent event occurs,
466 the window manager never sees it because an unmap event is not sent
467 to an already unmapped window.
468 */
469
470 /* we don't want the reparent event, put it back on the stack for the
471 X server to deal with after we unmanage the window */
472 XPutBackEvent(ob_display, e);
473
474 client_unmanage(client);
475 break;
476 case MapRequest:
477 if (!client->iconic) break; /* this normally doesn't happen, but if it
478 does, we don't want it! */
479 if (screen_showing_desktop)
480 screen_show_desktop(FALSE);
481 client_iconify(client, FALSE, TRUE);
482 if (!client->frame->visible)
483 /* if its not visible still, then don't mess with it */
484 break;
485 if (client->shaded)
486 client_shade(client, FALSE);
487 client_focus(client);
488 stacking_raise(client);
489 break;
490 case ClientMessage:
491 /* validate cuz we query stuff off the client here */
492 if (!client_validate(client)) break;
493
494 if (e->xclient.format != 32) return;
495
496 msgtype = e->xclient.message_type;
497 if (msgtype == prop_atoms.wm_change_state) {
498 /* compress changes into a single change */
499 while (XCheckTypedWindowEvent(ob_display, e->type,
500 client->window, &ce)) {
501 /* XXX: it would be nice to compress ALL messages of a
502 type, not just messages in a row without other
503 message types between. */
504 if (ce.xclient.message_type != msgtype) {
505 XPutBackEvent(ob_display, &ce);
506 break;
507 }
508 e->xclient = ce.xclient;
509 }
510 client_set_wm_state(client, e->xclient.data.l[0]);
511 } else if (msgtype == prop_atoms.net_wm_desktop) {
512 /* compress changes into a single change */
513 while (XCheckTypedWindowEvent(ob_display, e->type,
514 client->window, &ce)) {
515 /* XXX: it would be nice to compress ALL messages of a
516 type, not just messages in a row without other
517 message types between. */
518 if (ce.xclient.message_type != msgtype) {
519 XPutBackEvent(ob_display, &ce);
520 break;
521 }
522 e->xclient = ce.xclient;
523 }
524 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
525 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
526 client_set_desktop(client, (unsigned)e->xclient.data.l[0]);
527 } else if (msgtype == prop_atoms.net_wm_state) {
528 /* can't compress these */
529 g_message("net_wm_state %s %ld %ld for 0x%lx",
530 (e->xclient.data.l[0] == 0 ? "Remove" :
531 e->xclient.data.l[0] == 1 ? "Add" :
532 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
533 e->xclient.data.l[1], e->xclient.data.l[2],
534 client->window);
535 client_set_state(client, e->xclient.data.l[0],
536 e->xclient.data.l[1], e->xclient.data.l[2]);
537 } else if (msgtype == prop_atoms.net_close_window) {
538 g_message("net_close_window for 0x%lx", client->window);
539 client_close(client);
540 } else if (msgtype == prop_atoms.net_active_window) {
541 g_message("net_active_window for 0x%lx", client->window);
542 if (screen_showing_desktop)
543 screen_show_desktop(FALSE);
544 if (client->iconic)
545 client_iconify(client, FALSE, TRUE);
546 else if (!client->frame->visible)
547 /* if its not visible for other reasons, then don't mess
548 with it */
549 break;
550 if (client->shaded)
551 client_shade(client, FALSE);
552 client_focus(client);
553 stacking_raise(client);
554 }
555 break;
556 case PropertyNotify:
557 /* validate cuz we query stuff off the client here */
558 if (!client_validate(client)) break;
559
560 /* compress changes to a single property into a single change */
561 while (XCheckTypedWindowEvent(ob_display, e->type,
562 client->window, &ce)) {
563 /* XXX: it would be nice to compress ALL changes to a property,
564 not just changes in a row without other props between. */
565 if (ce.xproperty.atom != e->xproperty.atom) {
566 XPutBackEvent(ob_display, &ce);
567 break;
568 }
569 }
570
571 msgtype = e->xproperty.atom;
572 if (msgtype == XA_WM_NORMAL_HINTS) {
573 client_update_normal_hints(client);
574 /* normal hints can make a window non-resizable */
575 client_setup_decor_and_functions(client);
576 }
577 else if (msgtype == XA_WM_HINTS)
578 client_update_wmhints(client);
579 else if (msgtype == XA_WM_TRANSIENT_FOR) {
580 client_update_transient_for(client);
581 client_get_type(client);
582 /* type may have changed, so update the layer */
583 client_calc_layer(client);
584 client_setup_decor_and_functions(client);
585 }
586 else if (msgtype == prop_atoms.net_wm_name ||
587 msgtype == prop_atoms.wm_name)
588 client_update_title(client);
589 else if (msgtype == prop_atoms.net_wm_icon_name ||
590 msgtype == prop_atoms.wm_icon_name)
591 client_update_icon_title(client);
592 else if (msgtype == prop_atoms.wm_class)
593 client_update_class(client);
594 else if (msgtype == prop_atoms.wm_protocols) {
595 client_update_protocols(client);
596 client_setup_decor_and_functions(client);
597 }
598 else if (msgtype == prop_atoms.net_wm_strut)
599 client_update_strut(client);
600 else if (msgtype == prop_atoms.net_wm_icon)
601 client_update_icons(client);
602 else if (msgtype == prop_atoms.kwm_win_icon)
603 client_update_kwm_icon(client);
604 default:
605 ;
606 #ifdef SHAPE
607 if (extensions_shape && e->type == extensions_shape_event_basep) {
608 client->shaped = ((XShapeEvent*)e)->shaped;
609 engine_frame_adjust_shape(client->frame);
610 }
611 #endif
612 }
613 }
This page took 0.06737 seconds and 4 git commands to generate.