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