]> Dogcows Code - chaz/openbox/blob - openbox/event.c
yet more improvements to focus handling
[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
28 /*! The value of the mask for the NumLock modifier */
29 unsigned int NumLockMask;
30 /*! The value of the mask for the ScrollLock modifier */
31 unsigned int ScrollLockMask;
32 /*! The key codes for the modifier keys */
33 static XModifierKeymap *modmap;
34 /*! Table of the constant modifier masks */
35 static const int mask_table[] = {
36 ShiftMask, LockMask, ControlMask, Mod1Mask,
37 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
38 };
39 static int mask_table_size;
40
41 void event_startup()
42 {
43 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
44
45 /* get lock masks that are defined by the display (not constant) */
46 modmap = XGetModifierMapping(ob_display);
47 g_assert(modmap);
48 if (modmap && modmap->max_keypermod > 0) {
49 size_t cnt;
50 const size_t size = mask_table_size * modmap->max_keypermod;
51 /* get the values of the keyboard lock modifiers
52 Note: Caps lock is not retrieved the same way as Scroll and Num
53 lock since it doesn't need to be. */
54 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
55 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
56 XK_Scroll_Lock);
57
58 for (cnt = 0; cnt < size; ++cnt) {
59 if (! modmap->modifiermap[cnt]) continue;
60
61 if (num_lock == modmap->modifiermap[cnt])
62 NumLockMask = mask_table[cnt / modmap->max_keypermod];
63 if (scroll_lock == modmap->modifiermap[cnt])
64 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
65 }
66 }
67 }
68
69 void event_shutdown()
70 {
71 XFreeModifiermap(modmap);
72 }
73
74 void event_loop()
75 {
76 fd_set selset;
77 XEvent e;
78 int x_fd;
79 struct timeval *wait;
80
81 while (TRUE) {
82 /*
83 There are slightly different event retrieval semantics here for
84 local (or high bandwidth) versus remote (or low bandwidth)
85 connections to the display/Xserver.
86 */
87 if (ob_remote) {
88 if (!XPending(ob_display))
89 break;
90 } else {
91 /*
92 This XSync allows for far more compression of events, which
93 makes things like Motion events perform far far better. Since
94 it also means network traffic for every event instead of every
95 X events (where X is the number retrieved at a time), it
96 probably should not be used for setups where Openbox is
97 running on a remote/low bandwidth display/Xserver.
98 */
99 XSync(ob_display, FALSE);
100 if (!XEventsQueued(ob_display, QueuedAlready))
101 break;
102 }
103 XNextEvent(ob_display, &e);
104
105 event_process(&e);
106 }
107
108 timer_dispatch((GTimeVal**)&wait);
109 x_fd = ConnectionNumber(ob_display);
110 FD_ZERO(&selset);
111 FD_SET(x_fd, &selset);
112 select(x_fd + 1, &selset, NULL, NULL, wait);
113 }
114
115 void event_process(XEvent *e)
116 {
117 XEvent ce;
118 KeyCode *kp;
119 Window window;
120 int i, k;
121 Client *client;
122
123 /* pick a window */
124 switch (e->type) {
125 case MapRequest:
126 window = e->xmap.window;
127 break;
128 case UnmapNotify:
129 window = e->xunmap.window;
130 break;
131 case DestroyNotify:
132 window = e->xdestroywindow.window;
133 break;
134 case ConfigureRequest:
135 window = e->xconfigurerequest.window;
136 break;
137 default:
138 /* XKB events */
139 if (e->type == extensions_xkb_event_basep) {
140 switch (((XkbAnyEvent*)&e)->xkb_type) {
141 case XkbBellNotify:
142 window = ((XkbBellNotifyEvent*)&e)->window;
143 default:
144 window = None;
145 }
146 } else
147 window = e->xany.window;
148 }
149
150 /* grab the lasttime and hack up the state */
151 switch (e->type) {
152 case ButtonPress:
153 case ButtonRelease:
154 event_lasttime = e->xbutton.time;
155 e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
156 /* kill off the Button1Mask etc, only want the modifiers */
157 e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
158 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
159 break;
160 case KeyPress:
161 event_lasttime = e->xkey.time;
162 e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
163 /* kill off the Button1Mask etc, only want the modifiers */
164 e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
165 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
166 /* add to the state the mask of the modifier being pressed, if it is
167 a modifier key being pressed (this is a little ugly..) */
168 /* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
169 /* kp = modmap->modifiermap;*/
170 /* for (i = 0; i < mask_table_size; ++i) {*/
171 /* for (k = 0; k < modmap->max_keypermod; ++k) {*/
172 /* if (*kp == e->xkey.keycode) {*/ /* found the keycode */
173 /* add the mask for it */
174 /* e->xkey.state |= mask_table[i];*/
175 /* cause the first loop to break; */
176 /* i = mask_table_size;*/
177 /* break;*/ /* get outta here! */
178 /* }*/
179 /* ++kp;*/
180 /* }*/
181 /* }*/
182
183 break;
184 case KeyRelease:
185 event_lasttime = e->xkey.time;
186 e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
187 /* kill off the Button1Mask etc, only want the modifiers */
188 e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
189 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
190 /* remove from the state the mask of the modifier being released, if
191 it is a modifier key being released (this is a little ugly..) */
192 kp = modmap->modifiermap;
193 for (i = 0; i < mask_table_size; ++i) {
194 for (k = 0; k < modmap->max_keypermod; ++k) {
195 if (*kp == e->xkey.keycode) { /* found the keycode */
196 /* remove the mask for it */
197 e->xkey.state &= ~mask_table[i];
198 /* cause the first loop to break; */
199 i = mask_table_size;
200 break; /* get outta here! */
201 }
202 ++kp;
203 }
204 }
205 break;
206 case MotionNotify:
207 event_lasttime = e->xmotion.time;
208 e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
209 /* kill off the Button1Mask etc, only want the modifiers */
210 e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
211 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
212 /* compress events */
213 while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
214 e->xmotion.x_root = ce.xmotion.x_root;
215 e->xmotion.y_root = ce.xmotion.y_root;
216 }
217 break;
218 case PropertyNotify:
219 event_lasttime = e->xproperty.time;
220 break;
221 case FocusIn:
222 case FocusOut:
223 if (e->xfocus.mode == NotifyGrab)
224 /*|| e.xfocus.mode == NotifyUngrab ||*/
225
226 /* From Metacity, from WindowMaker, ignore all funky pointer
227 root events. Its commented out cuz I don't think we need this
228 at all. If problems arise we can look into it */
229 /*e.xfocus.detail > NotifyNonlinearVirtual) */
230 return; /* skip me! */
231 if (e->type == FocusOut) {
232 /* FocusOut events just make us look for FocusIn events. They
233 are mostly ignored otherwise. */
234 XEvent fi;
235 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
236 event_process(&fi);
237 /* dont unfocus the window we just focused! */
238 if (fi.xfocus.window == e->xfocus.window)
239 return;
240 }
241 } else if (window == focus_backup && focus_client != NULL)
242 /* Something's focused but we got a focus event for the backup
243 window. this means that something unfocused before we received
244 the new FocusIn. Just ignore it. */
245 return;
246 break;
247 case EnterNotify:
248 case LeaveNotify:
249 event_lasttime = e->xcrossing.time;
250 /* XXX this caused problems before... but i don't remember why. hah.
251 so back it is. if problems arise again, then try filtering on the
252 detail instead of the mode. */
253 if (e->xcrossing.mode != NotifyNormal) return;
254 break;
255 }
256
257 client = g_hash_table_lookup(client_map, (gpointer)window);
258
259 /* deal with it in the kernel */
260 if (client)
261 event_handle_client(client, e);
262 else if (window == ob_root)
263 event_handle_root(e);
264 else if (e->type == MapRequest)
265 client_manage(window);
266 else if (e->type == ConfigureRequest) {
267 /* unhandled configure requests must be used to configure the
268 window directly */
269 XWindowChanges xwc;
270
271 xwc.x = e->xconfigurerequest.x;
272 xwc.y = e->xconfigurerequest.y;
273 xwc.width = e->xconfigurerequest.width;
274 xwc.height = e->xconfigurerequest.height;
275 xwc.border_width = e->xconfigurerequest.border_width;
276 xwc.sibling = e->xconfigurerequest.above;
277 xwc.stack_mode = e->xconfigurerequest.detail;
278
279 g_message("Proxying configure event for 0x%lx", window);
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
328 switch (e->type) {
329 case FocusIn:
330 if (focus_client != client)
331 focus_set_client(client);
332
333 /* focus state can affect the stacking layer */
334 client_calc_layer(client);
335
336 engine_frame_adjust_focus(client->frame);
337 break;
338 case FocusOut:
339 if (focus_client == client)
340 focus_set_client(NULL);
341
342 /* focus state can affect the stacking layer */
343 client_calc_layer(client);
344
345 engine_frame_adjust_focus(client->frame);
346 break;
347 case ConfigureRequest:
348 g_message("ConfigureRequest for window %lx", client->window);
349 /* compress these */
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;
369 }
370
371 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
372 if (client->iconic || client->shaded) return;
373
374 if (e->xconfigurerequest.value_mask & CWBorderWidth)
375 client->border_width = e->xconfigurerequest.border_width;
376
377 /* resize, then move, as specified in the EWMH section 7.7 */
378 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
379 CWX | CWY)) {
380 int x, y, w, h;
381 Corner corner;
382
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;
391
392 switch (client->gravity) {
393 case NorthEastGravity:
394 case EastGravity:
395 corner = Corner_TopRight;
396 break;
397 case SouthWestGravity:
398 case SouthGravity:
399 corner = Corner_BottomLeft;
400 break;
401 case SouthEastGravity:
402 corner = Corner_BottomRight;
403 break;
404 default: /* NorthWest, Static, etc */
405 corner = Corner_TopLeft;
406 }
407
408 client_configure(client, corner, x, y, w, h, FALSE, FALSE);
409 }
410
411 if (e->xconfigurerequest.value_mask & CWStackMode) {
412 switch (e->xconfigurerequest.detail) {
413 case Below:
414 case BottomIf:
415 stacking_lower(client);
416 break;
417
418 case Above:
419 case TopIf:
420 default:
421 stacking_raise(client);
422 break;
423 }
424 }
425 break;
426 case UnmapNotify:
427 if (client->ignore_unmaps) {
428 client->ignore_unmaps--;
429 break;
430 }
431 g_message("UnmapNotify for %lx", client->window);
432 client_unmanage(client);
433 break;
434 case DestroyNotify:
435 g_message("DestroyNotify for %lx", client->window);
436 client_unmanage(client);
437 break;
438 case ReparentNotify:
439 /* this is when the client is first taken captive in the frame */
440 if (e->xreparent.parent == client->frame->plate) break;
441
442 /*
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.
447 */
448
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);
452
453 client_unmanage(client);
454 break;
455 case MapRequest:
456 if (screen_showing_desktop)
457 screen_show_desktop(FALSE);
458 client_iconify(client, FALSE, TRUE);
459 if (!client->frame->visible)
460 /* if its not visible still, then don't mess with it */
461 break;
462 if (client->shaded)
463 client_shade(client, FALSE);
464 client_focus(client);
465 stacking_raise(client);
466 break;
467 case ClientMessage:
468 /* validate cuz we query stuff off the client here */
469 if (!client_validate(client)) break;
470
471 if (e->xclient.format != 32) return;
472
473 msgtype = e->xclient.message_type;
474 if (msgtype == prop_atoms.wm_change_state) {
475 /* compress changes into a single change */
476 while (XCheckTypedWindowEvent(ob_display, e->type,
477 client->window, &ce)) {
478 /* XXX: it would be nice to compress ALL messages of a
479 type, not just messages in a row without other
480 message types between. */
481 if (ce.xclient.message_type != msgtype) {
482 XPutBackEvent(ob_display, &ce);
483 break;
484 }
485 e->xclient = ce.xclient;
486 }
487 client_set_wm_state(client, e->xclient.data.l[0]);
488 } else if (msgtype == prop_atoms.net_wm_desktop) {
489 /* compress changes into a single change */
490 while (XCheckTypedWindowEvent(ob_display, e->type,
491 client->window, &ce)) {
492 /* XXX: it would be nice to compress ALL messages of a
493 type, not just messages in a row without other
494 message types between. */
495 if (ce.xclient.message_type != msgtype) {
496 XPutBackEvent(ob_display, &ce);
497 break;
498 }
499 e->xclient = ce.xclient;
500 }
501 client_set_desktop(client, e->xclient.data.l[0]);
502 } else if (msgtype == prop_atoms.net_wm_state) {
503 /* can't compress these */
504 g_message("net_wm_state %s %ld %ld for 0x%lx\n",
505 (e->xclient.data.l[0] == 0 ? "Remove" :
506 e->xclient.data.l[0] == 1 ? "Add" :
507 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
508 e->xclient.data.l[1], e->xclient.data.l[2],
509 client->window);
510 client_set_state(client, e->xclient.data.l[0],
511 e->xclient.data.l[1], e->xclient.data.l[2]);
512 } else if (msgtype == prop_atoms.net_close_window) {
513 g_message("net_close_window for 0x%lx\n", client->window);
514 client_close(client);
515 } else if (msgtype == prop_atoms.net_active_window) {
516 g_message("net_active_window for 0x%lx\n", client->window);
517 if (screen_showing_desktop)
518 screen_show_desktop(FALSE);
519 if (client->iconic)
520 client_iconify(client, FALSE, TRUE);
521 else if (!client->frame->visible)
522 /* if its not visible for other reasons, then don't mess
523 with it */
524 break;
525 if (client->shaded)
526 client_shade(client, FALSE);
527 client_focus(client);
528 stacking_raise(client);
529 }
530 break;
531 case PropertyNotify:
532 /* validate cuz we query stuff off the client here */
533 if (!client_validate(client)) break;
534
535 /* compress changes to a single property into a single change */
536 while (XCheckTypedWindowEvent(ob_display, e->type,
537 client->window, &ce)) {
538 /* XXX: it would be nice to compress ALL changes to a property,
539 not just changes in a row without other props between. */
540 if (ce.xproperty.atom != e->xproperty.atom) {
541 XPutBackEvent(ob_display, &ce);
542 break;
543 }
544 }
545
546 msgtype = e->xproperty.atom;
547 if (msgtype == XA_WM_NORMAL_HINTS) {
548 client_update_normal_hints(client);
549 /* normal hints can make a window non-resizable */
550 client_setup_decor_and_functions(client);
551 }
552 else if (msgtype == XA_WM_HINTS)
553 client_update_wmhints(client);
554 else if (msgtype == XA_WM_TRANSIENT_FOR) {
555 client_update_transient_for(client);
556 client_get_type(client);
557 /* type may have changed, so update the layer */
558 client_calc_layer(client);
559 client_setup_decor_and_functions(client);
560 }
561 else if (msgtype == prop_atoms.net_wm_name ||
562 msgtype == prop_atoms.wm_name)
563 client_update_title(client);
564 else if (msgtype == prop_atoms.net_wm_icon_name ||
565 msgtype == prop_atoms.wm_icon_name)
566 client_update_icon_title(client);
567 else if (msgtype == prop_atoms.wm_class)
568 client_update_class(client);
569 else if (msgtype == prop_atoms.wm_protocols) {
570 client_update_protocols(client);
571 client_setup_decor_and_functions(client);
572 }
573 else if (msgtype == prop_atoms.net_wm_strut)
574 client_update_strut(client);
575 else if (msgtype == prop_atoms.net_wm_icon)
576 client_update_icons(client);
577 else if (msgtype == prop_atoms.kwm_win_icon)
578 client_update_kwm_icon(client);
579 }
580 }
This page took 0.060929 seconds and 5 git commands to generate.