]> Dogcows Code - chaz/openbox/blob - openbox/event.c
327e3a8dd563f2ee8472180929e47712d690df82
[chaz/openbox] / openbox / event.c
1 #include "openbox.h"
2 #include "client.h"
3 #include "xerror.h"
4 #include "prop.h"
5 #include "screen.h"
6 #include "frame.h"
7 #include "engine.h"
8 #include "focus.h"
9 #include "stacking.h"
10 #include "extensions.h"
11 #include "timer.h"
12 #include "engine.h"
13 #include "dispatch.h"
14
15 #include <X11/Xlib.h>
16 #include <X11/keysym.h>
17 #include <X11/Xatom.h>
18 #ifdef HAVE_SYS_SELECT_H
19 # include <sys/select.h>
20 #endif
21
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);
25
26 Time event_lasttime = 0;
27 Time event_unfocustime = 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 if (e->xfocus.mode == NotifyGrab ||
225 !(e->xfocus.detail == NotifyNonlinearVirtual ||
226 e->xfocus.detail == NotifyNonlinear))
227 return;
228 break;
229 case FocusOut:
230 if (e->xfocus.mode == NotifyGrab ||
231 !(e->xfocus.detail == NotifyNonlinearVirtual ||
232 e->xfocus.detail == NotifyNonlinear))
233 return;
234
235 /* FocusOut events just make us look for FocusIn events. They
236 are mostly ignored otherwise. */
237 {
238 XEvent fi;
239 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
240 event_process(&fi);
241
242 if (fi.xfocus.window == e->xfocus.window)
243 return;
244 }
245 }
246 break;
247 case EnterNotify:
248 case LeaveNotify:
249 event_lasttime = e->xcrossing.time;
250 /* NotifyUngrab occurs when a mouse button is released and the event is
251 caused, like when lowering a window */
252 if (e->xcrossing.mode == NotifyGrab) return;
253 break;
254 default:
255 event_lasttime = CurrentTime;
256 break;
257 }
258
259 client = g_hash_table_lookup(client_map, &window);
260
261 /* deal with it in the kernel */
262 if (client)
263 event_handle_client(client, e);
264 else if (window == ob_root)
265 event_handle_root(e);
266 else if (e->type == MapRequest)
267 client_manage(window);
268 else if (e->type == ConfigureRequest) {
269 /* unhandled configure requests must be used to configure the
270 window directly */
271 XWindowChanges xwc;
272
273 xwc.x = e->xconfigurerequest.x;
274 xwc.y = e->xconfigurerequest.y;
275 xwc.width = e->xconfigurerequest.width;
276 xwc.height = e->xconfigurerequest.height;
277 xwc.border_width = e->xconfigurerequest.border_width;
278 xwc.sibling = e->xconfigurerequest.above;
279 xwc.stack_mode = e->xconfigurerequest.detail;
280
281 /* we are not to be held responsible if someone sends us an
282 invalid request! */
283 xerror_set_ignore(TRUE);
284 XConfigureWindow(ob_display, window,
285 e->xconfigurerequest.value_mask, &xwc);
286 xerror_set_ignore(FALSE);
287 }
288
289 /* dispatch the event to registered handlers */
290 dispatch_x(e, client);
291 }
292
293 static void event_handle_root(XEvent *e)
294 {
295 Atom msgtype;
296
297 switch(e->type) {
298 case ClientMessage:
299 if (e->xclient.format != 32) break;
300
301 msgtype = e->xclient.message_type;
302 if (msgtype == prop_atoms.net_current_desktop) {
303 unsigned int d = e->xclient.data.l[0];
304 if (d < screen_num_desktops)
305 screen_set_desktop(d);
306 } else if (msgtype == prop_atoms.net_number_of_desktops) {
307 unsigned int d = e->xclient.data.l[0];
308 if (d > 0)
309 screen_set_num_desktops(d);
310 } else if (msgtype == prop_atoms.net_showing_desktop) {
311 screen_show_desktop(e->xclient.data.l[0] != 0);
312 }
313 break;
314 case PropertyNotify:
315 if (e->xproperty.atom == prop_atoms.net_desktop_names)
316 screen_update_desktop_names();
317 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
318 screen_update_layout();
319 break;
320 }
321 }
322
323 static void event_handle_client(Client *client, XEvent *e)
324 {
325 XEvent ce;
326 Atom msgtype;
327 int i=0;
328
329 switch (e->type) {
330 case FocusIn:
331 case FocusOut:
332 client_set_focused(client, e->type == FocusIn);
333 break;
334 case ConfigureRequest:
335 /* compress these */
336 while (XCheckTypedWindowEvent(ob_display, client->window,
337 ConfigureRequest, &ce)) {
338 ++i;
339 /* XXX if this causes bad things.. we can compress config req's
340 with the same mask. */
341 e->xconfigurerequest.value_mask |=
342 ce.xconfigurerequest.value_mask;
343 if (ce.xconfigurerequest.value_mask & CWX)
344 e->xconfigurerequest.x = ce.xconfigurerequest.x;
345 if (ce.xconfigurerequest.value_mask & CWY)
346 e->xconfigurerequest.y = ce.xconfigurerequest.y;
347 if (ce.xconfigurerequest.value_mask & CWWidth)
348 e->xconfigurerequest.width = ce.xconfigurerequest.width;
349 if (ce.xconfigurerequest.value_mask & CWHeight)
350 e->xconfigurerequest.height = ce.xconfigurerequest.height;
351 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
352 e->xconfigurerequest.border_width =
353 ce.xconfigurerequest.border_width;
354 if (ce.xconfigurerequest.value_mask & CWStackMode)
355 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
356 }
357 if (i) g_message("Compressed %d Configures", i);
358
359 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
360 if (client->iconic || client->shaded) return;
361
362 if (e->xconfigurerequest.value_mask & CWBorderWidth)
363 client->border_width = e->xconfigurerequest.border_width;
364
365 /* resize, then move, as specified in the EWMH section 7.7 */
366 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
367 CWX | CWY)) {
368 int x, y, w, h;
369 Corner corner;
370
371 x = (e->xconfigurerequest.value_mask & CWX) ?
372 e->xconfigurerequest.x : client->area.x;
373 y = (e->xconfigurerequest.value_mask & CWY) ?
374 e->xconfigurerequest.y : client->area.y;
375 w = (e->xconfigurerequest.value_mask & CWWidth) ?
376 e->xconfigurerequest.width : client->area.width;
377 h = (e->xconfigurerequest.value_mask & CWHeight) ?
378 e->xconfigurerequest.height : client->area.height;
379
380 switch (client->gravity) {
381 case NorthEastGravity:
382 case EastGravity:
383 corner = Corner_TopRight;
384 break;
385 case SouthWestGravity:
386 case SouthGravity:
387 corner = Corner_BottomLeft;
388 break;
389 case SouthEastGravity:
390 corner = Corner_BottomRight;
391 break;
392 default: /* NorthWest, Static, etc */
393 corner = Corner_TopLeft;
394 }
395
396 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
397 }
398
399 if (e->xconfigurerequest.value_mask & CWStackMode) {
400 switch (e->xconfigurerequest.detail) {
401 case Below:
402 case BottomIf:
403 stacking_lower(client);
404 break;
405
406 case Above:
407 case TopIf:
408 default:
409 stacking_raise(client);
410 break;
411 }
412 }
413 break;
414 case UnmapNotify:
415 if (client->ignore_unmaps) {
416 client->ignore_unmaps--;
417 break;
418 }
419 client_unmanage(client);
420 break;
421 case DestroyNotify:
422 client_unmanage(client);
423 break;
424 case ReparentNotify:
425 /* this is when the client is first taken captive in the frame */
426 if (e->xreparent.parent == client->frame->plate) break;
427
428 /*
429 This event is quite rare and is usually handled in unmapHandler.
430 However, if the window is unmapped when the reparent event occurs,
431 the window manager never sees it because an unmap event is not sent
432 to an already unmapped window.
433 */
434
435 /* we don't want the reparent event, put it back on the stack for the
436 X server to deal with after we unmanage the window */
437 XPutBackEvent(ob_display, e);
438
439 client_unmanage(client);
440 break;
441 case MapRequest:
442 if (!client->iconic) break; /* this normally doesn't happen, but if it
443 does, we don't want it! */
444 if (screen_showing_desktop)
445 screen_show_desktop(FALSE);
446 client_iconify(client, FALSE, TRUE);
447 if (!client->frame->visible)
448 /* if its not visible still, then don't mess with it */
449 break;
450 if (client->shaded)
451 client_shade(client, FALSE);
452 client_focus(client);
453 stacking_raise(client);
454 break;
455 case ClientMessage:
456 /* validate cuz we query stuff off the client here */
457 if (!client_validate(client)) break;
458
459 if (e->xclient.format != 32) return;
460
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);
471 break;
472 }
473 e->xclient = ce.xclient;
474 }
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);
485 break;
486 }
487 e->xclient = ce.xclient;
488 }
489 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
490 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
491 client_set_desktop(client, (unsigned)e->xclient.data.l[0]);
492 } else if (msgtype == prop_atoms.net_wm_state) {
493 /* can't compress these */
494 g_message("net_wm_state %s %ld %ld for 0x%lx",
495 (e->xclient.data.l[0] == 0 ? "Remove" :
496 e->xclient.data.l[0] == 1 ? "Add" :
497 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
498 e->xclient.data.l[1], e->xclient.data.l[2],
499 client->window);
500 client_set_state(client, e->xclient.data.l[0],
501 e->xclient.data.l[1], e->xclient.data.l[2]);
502 } else if (msgtype == prop_atoms.net_close_window) {
503 g_message("net_close_window for 0x%lx", client->window);
504 client_close(client);
505 } else if (msgtype == prop_atoms.net_active_window) {
506 g_message("net_active_window for 0x%lx", client->window);
507 if (screen_showing_desktop)
508 screen_show_desktop(FALSE);
509 if (client->iconic)
510 client_iconify(client, FALSE, TRUE);
511 else if (!client->frame->visible)
512 /* if its not visible for other reasons, then don't mess
513 with it */
514 break;
515 if (client->shaded)
516 client_shade(client, FALSE);
517 client_focus(client);
518 stacking_raise(client);
519 }
520 break;
521 case PropertyNotify:
522 /* validate cuz we query stuff off the client here */
523 if (!client_validate(client)) break;
524
525 /* compress changes to a single property into a single change */
526 while (XCheckTypedWindowEvent(ob_display, e->type,
527 client->window, &ce)) {
528 /* XXX: it would be nice to compress ALL changes to a property,
529 not just changes in a row without other props between. */
530 if (ce.xproperty.atom != e->xproperty.atom) {
531 XPutBackEvent(ob_display, &ce);
532 break;
533 }
534 }
535
536 msgtype = e->xproperty.atom;
537 if (msgtype == XA_WM_NORMAL_HINTS) {
538 client_update_normal_hints(client);
539 /* normal hints can make a window non-resizable */
540 client_setup_decor_and_functions(client);
541 }
542 else if (msgtype == XA_WM_HINTS)
543 client_update_wmhints(client);
544 else if (msgtype == XA_WM_TRANSIENT_FOR) {
545 client_update_transient_for(client);
546 client_get_type(client);
547 /* type may have changed, so update the layer */
548 client_calc_layer(client);
549 client_setup_decor_and_functions(client);
550 }
551 else if (msgtype == prop_atoms.net_wm_name ||
552 msgtype == prop_atoms.wm_name)
553 client_update_title(client);
554 else if (msgtype == prop_atoms.net_wm_icon_name ||
555 msgtype == prop_atoms.wm_icon_name)
556 client_update_icon_title(client);
557 else if (msgtype == prop_atoms.wm_class)
558 client_update_class(client);
559 else if (msgtype == prop_atoms.wm_protocols) {
560 client_update_protocols(client);
561 client_setup_decor_and_functions(client);
562 }
563 else if (msgtype == prop_atoms.net_wm_strut)
564 client_update_strut(client);
565 else if (msgtype == prop_atoms.net_wm_icon)
566 client_update_icons(client);
567 else if (msgtype == prop_atoms.kwm_win_icon)
568 client_update_kwm_icon(client);
569 default:
570 ;
571 #ifdef SHAPE
572 if (extensions_shape && e->type == extensions_shape_event_basep) {
573 client->shaped = ((XShapeEvent*)e)->shaped;
574 engine_frame_adjust_shape(client->frame);
575 }
576 #endif
577 }
578 }
This page took 0.060949 seconds and 3 git commands to generate.