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