]> Dogcows Code - chaz/openbox/blob - openbox/event.c
b048e7e64e7e95babf6c08f0536386d9bb99df5a
[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.mode == NotifyGrab ||
235 e->xfocus.detail == NotifyInferior ||
236 e->xfocus.detail == NotifyAncestor ||
237 e->xfocus.detail > NotifyNonlinearVirtual) return;
238
239 g_message("FocusOut on %lx", window);
240 /* FocusOut events just make us look for FocusIn events. They
241 are mostly ignored otherwise. */
242 {
243 XEvent fi;
244 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
245 event_process(&fi);
246
247 /* secret magic way of event_process telling us that no client
248 was found for the FocusIn event.
249
250 it should be noted!! that focus events of invalud types
251 (the ones that cause a return in the FocusIn case above)
252 will not cause this focus_fallback to be called. it will
253 be assumed that focus is going someplace sane still, or
254 there are more focus events coming to fix up the situation.
255 this may not be perfect.. but its working! and focus events
256 are too much headache to take that for granted. ktnx. ^_^
257 */
258 if (fi.xfocus.window == None)
259 focus_fallback(FALSE);
260 if (fi.xfocus.window == e->xfocus.window)
261 return;
262 } else
263 focus_fallback(FALSE);
264 }
265 break;
266 case EnterNotify:
267 case LeaveNotify:
268 event_lasttime = e->xcrossing.time;
269 /* NotifyUngrab occurs when a mouse button is released and the event is
270 caused, like when lowering a window */
271 if (e->xcrossing.mode == NotifyGrab) return;
272 break;
273 default:
274 event_lasttime = CurrentTime;
275 break;
276 }
277
278 client = g_hash_table_lookup(client_map, &window);
279
280 /* deal with it in the kernel */
281 if (client)
282 event_handle_client(client, e);
283 else if (window == ob_root)
284 event_handle_root(e);
285 else if (e->type == MapRequest)
286 client_manage(window);
287 else if (e->type == FocusIn)
288 e->xfocus.window = None; /* says a client was found for the event! */
289 else if (e->type == ConfigureRequest) {
290 /* unhandled configure requests must be used to configure the
291 window directly */
292 XWindowChanges xwc;
293
294 xwc.x = e->xconfigurerequest.x;
295 xwc.y = e->xconfigurerequest.y;
296 xwc.width = e->xconfigurerequest.width;
297 xwc.height = e->xconfigurerequest.height;
298 xwc.border_width = e->xconfigurerequest.border_width;
299 xwc.sibling = e->xconfigurerequest.above;
300 xwc.stack_mode = e->xconfigurerequest.detail;
301
302 /* we are not to be held responsible if someone sends us an
303 invalid request! */
304 xerror_set_ignore(TRUE);
305 XConfigureWindow(ob_display, window,
306 e->xconfigurerequest.value_mask, &xwc);
307 xerror_set_ignore(FALSE);
308 }
309
310 /* dispatch the event to registered handlers */
311 dispatch_x(e, client);
312 }
313
314 static void event_handle_root(XEvent *e)
315 {
316 Atom msgtype;
317
318 switch(e->type) {
319 case ClientMessage:
320 if (e->xclient.format != 32) break;
321
322 msgtype = e->xclient.message_type;
323 if (msgtype == prop_atoms.net_current_desktop) {
324 unsigned int d = e->xclient.data.l[0];
325 if (d < screen_num_desktops)
326 screen_set_desktop(d);
327 } else if (msgtype == prop_atoms.net_number_of_desktops) {
328 unsigned int d = e->xclient.data.l[0];
329 if (d > 0)
330 screen_set_num_desktops(d);
331 } else if (msgtype == prop_atoms.net_showing_desktop) {
332 screen_show_desktop(e->xclient.data.l[0] != 0);
333 }
334 break;
335 case PropertyNotify:
336 if (e->xproperty.atom == prop_atoms.net_desktop_names)
337 screen_update_desktop_names();
338 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
339 screen_update_layout();
340 break;
341 }
342 }
343
344 static void event_handle_client(Client *client, XEvent *e)
345 {
346 XEvent ce;
347 Atom msgtype;
348 int i=0;
349 ConfigValue focus_follow;
350
351 switch (e->type) {
352 case FocusIn:
353 focus_set_client(client);
354 case FocusOut:
355 g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
356 client->window);
357 /* focus state can affect the stacking layer */
358 client_calc_layer(client);
359 engine_frame_adjust_focus(client->frame);
360 break;
361 case EnterNotify:
362 if (client_normal(client)) {
363 if (ob_state == State_Starting) {
364 /* move it to the top of the focus order */
365 guint desktop = client->desktop;
366 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
367 focus_order[desktop] = g_list_remove(focus_order[desktop],
368 client);
369 focus_order[desktop] = g_list_prepend(focus_order[desktop],
370 client);
371 } else {
372 if (!config_get("focusFollowsMouse",Config_Bool,&focus_follow))
373 g_assert_not_reached();
374 if (focus_follow.bool)
375 client_focus(client);
376 }
377 }
378 break;
379 case ConfigureRequest:
380 /* compress these */
381 while (XCheckTypedWindowEvent(ob_display, client->window,
382 ConfigureRequest, &ce)) {
383 ++i;
384 /* XXX if this causes bad things.. we can compress config req's
385 with the same mask. */
386 e->xconfigurerequest.value_mask |=
387 ce.xconfigurerequest.value_mask;
388 if (ce.xconfigurerequest.value_mask & CWX)
389 e->xconfigurerequest.x = ce.xconfigurerequest.x;
390 if (ce.xconfigurerequest.value_mask & CWY)
391 e->xconfigurerequest.y = ce.xconfigurerequest.y;
392 if (ce.xconfigurerequest.value_mask & CWWidth)
393 e->xconfigurerequest.width = ce.xconfigurerequest.width;
394 if (ce.xconfigurerequest.value_mask & CWHeight)
395 e->xconfigurerequest.height = ce.xconfigurerequest.height;
396 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
397 e->xconfigurerequest.border_width =
398 ce.xconfigurerequest.border_width;
399 if (ce.xconfigurerequest.value_mask & CWStackMode)
400 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
401 }
402 if (i) g_message("Compressed %d Configures", i);
403
404 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
405 if (client->iconic || client->shaded) return;
406
407 if (e->xconfigurerequest.value_mask & CWBorderWidth)
408 client->border_width = e->xconfigurerequest.border_width;
409
410 /* resize, then move, as specified in the EWMH section 7.7 */
411 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
412 CWX | CWY)) {
413 int x, y, w, h;
414 Corner corner;
415
416 x = (e->xconfigurerequest.value_mask & CWX) ?
417 e->xconfigurerequest.x : client->area.x;
418 y = (e->xconfigurerequest.value_mask & CWY) ?
419 e->xconfigurerequest.y : client->area.y;
420 w = (e->xconfigurerequest.value_mask & CWWidth) ?
421 e->xconfigurerequest.width : client->area.width;
422 h = (e->xconfigurerequest.value_mask & CWHeight) ?
423 e->xconfigurerequest.height : client->area.height;
424
425 switch (client->gravity) {
426 case NorthEastGravity:
427 case EastGravity:
428 corner = Corner_TopRight;
429 break;
430 case SouthWestGravity:
431 case SouthGravity:
432 corner = Corner_BottomLeft;
433 break;
434 case SouthEastGravity:
435 corner = Corner_BottomRight;
436 break;
437 default: /* NorthWest, Static, etc */
438 corner = Corner_TopLeft;
439 }
440
441 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
442 }
443
444 if (e->xconfigurerequest.value_mask & CWStackMode) {
445 switch (e->xconfigurerequest.detail) {
446 case Below:
447 case BottomIf:
448 stacking_lower(client);
449 break;
450
451 case Above:
452 case TopIf:
453 default:
454 stacking_raise(client);
455 break;
456 }
457 }
458 break;
459 case UnmapNotify:
460 if (client->ignore_unmaps) {
461 client->ignore_unmaps--;
462 break;
463 }
464 client_unmanage(client);
465 break;
466 case DestroyNotify:
467 client_unmanage(client);
468 break;
469 case ReparentNotify:
470 /* this is when the client is first taken captive in the frame */
471 if (e->xreparent.parent == client->frame->plate) break;
472
473 /*
474 This event is quite rare and is usually handled in unmapHandler.
475 However, if the window is unmapped when the reparent event occurs,
476 the window manager never sees it because an unmap event is not sent
477 to an already unmapped window.
478 */
479
480 /* we don't want the reparent event, put it back on the stack for the
481 X server to deal with after we unmanage the window */
482 XPutBackEvent(ob_display, e);
483
484 client_unmanage(client);
485 break;
486 case MapRequest:
487 if (!client->iconic) break; /* this normally doesn't happen, but if it
488 does, we don't want it! */
489 if (screen_showing_desktop)
490 screen_show_desktop(FALSE);
491 client_iconify(client, FALSE, TRUE);
492 if (!client->frame->visible)
493 /* if its not visible still, then don't mess with it */
494 break;
495 if (client->shaded)
496 client_shade(client, FALSE);
497 client_focus(client);
498 stacking_raise(client);
499 break;
500 case ClientMessage:
501 /* validate cuz we query stuff off the client here */
502 if (!client_validate(client)) break;
503
504 if (e->xclient.format != 32) return;
505
506 msgtype = e->xclient.message_type;
507 if (msgtype == prop_atoms.wm_change_state) {
508 /* compress changes into a single change */
509 while (XCheckTypedWindowEvent(ob_display, e->type,
510 client->window, &ce)) {
511 /* XXX: it would be nice to compress ALL messages of a
512 type, not just messages in a row without other
513 message types between. */
514 if (ce.xclient.message_type != msgtype) {
515 XPutBackEvent(ob_display, &ce);
516 break;
517 }
518 e->xclient = ce.xclient;
519 }
520 client_set_wm_state(client, e->xclient.data.l[0]);
521 } else if (msgtype == prop_atoms.net_wm_desktop) {
522 /* compress changes into a single change */
523 while (XCheckTypedWindowEvent(ob_display, e->type,
524 client->window, &ce)) {
525 /* XXX: it would be nice to compress ALL messages of a
526 type, not just messages in a row without other
527 message types between. */
528 if (ce.xclient.message_type != msgtype) {
529 XPutBackEvent(ob_display, &ce);
530 break;
531 }
532 e->xclient = ce.xclient;
533 }
534 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
535 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
536 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
537 FALSE);
538 } else if (msgtype == prop_atoms.net_wm_state) {
539 /* can't compress these */
540 g_message("net_wm_state %s %ld %ld for 0x%lx",
541 (e->xclient.data.l[0] == 0 ? "Remove" :
542 e->xclient.data.l[0] == 1 ? "Add" :
543 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
544 e->xclient.data.l[1], e->xclient.data.l[2],
545 client->window);
546 client_set_state(client, e->xclient.data.l[0],
547 e->xclient.data.l[1], e->xclient.data.l[2]);
548 } else if (msgtype == prop_atoms.net_close_window) {
549 g_message("net_close_window for 0x%lx", client->window);
550 client_close(client);
551 } else if (msgtype == prop_atoms.net_active_window) {
552 g_message("net_active_window for 0x%lx", client->window);
553 if (screen_showing_desktop)
554 screen_show_desktop(FALSE);
555 if (client->iconic)
556 client_iconify(client, FALSE, TRUE);
557 else if (!client->frame->visible)
558 /* if its not visible for other reasons, then don't mess
559 with it */
560 break;
561 if (client->shaded)
562 client_shade(client, FALSE);
563 client_focus(client);
564 stacking_raise(client);
565 }
566 break;
567 case PropertyNotify:
568 /* validate cuz we query stuff off the client here */
569 if (!client_validate(client)) break;
570
571 /* compress changes to a single property into a single change */
572 while (XCheckTypedWindowEvent(ob_display, e->type,
573 client->window, &ce)) {
574 /* XXX: it would be nice to compress ALL changes to a property,
575 not just changes in a row without other props between. */
576 if (ce.xproperty.atom != e->xproperty.atom) {
577 XPutBackEvent(ob_display, &ce);
578 break;
579 }
580 }
581
582 msgtype = e->xproperty.atom;
583 if (msgtype == XA_WM_NORMAL_HINTS) {
584 client_update_normal_hints(client);
585 /* normal hints can make a window non-resizable */
586 client_setup_decor_and_functions(client);
587 }
588 else if (msgtype == XA_WM_HINTS)
589 client_update_wmhints(client);
590 else if (msgtype == XA_WM_TRANSIENT_FOR) {
591 client_update_transient_for(client);
592 client_get_type(client);
593 /* type may have changed, so update the layer */
594 client_calc_layer(client);
595 client_setup_decor_and_functions(client);
596 }
597 else if (msgtype == prop_atoms.net_wm_name ||
598 msgtype == prop_atoms.wm_name)
599 client_update_title(client);
600 else if (msgtype == prop_atoms.net_wm_icon_name ||
601 msgtype == prop_atoms.wm_icon_name)
602 client_update_icon_title(client);
603 else if (msgtype == prop_atoms.wm_class)
604 client_update_class(client);
605 else if (msgtype == prop_atoms.wm_protocols) {
606 client_update_protocols(client);
607 client_setup_decor_and_functions(client);
608 }
609 else if (msgtype == prop_atoms.net_wm_strut)
610 client_update_strut(client);
611 else if (msgtype == prop_atoms.net_wm_icon)
612 client_update_icons(client);
613 else if (msgtype == prop_atoms.kwm_win_icon)
614 client_update_kwm_icon(client);
615 default:
616 ;
617 #ifdef SHAPE
618 if (extensions_shape && e->type == extensions_shape_event_basep) {
619 client->shaped = ((XShapeEvent*)e)->shaped;
620 engine_frame_adjust_shape(client->frame);
621 }
622 #endif
623 }
624 }
This page took 0.06966 seconds and 4 git commands to generate.