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