]> Dogcows Code - chaz/openbox/blob - openbox/event.c
more focus improvements. everything seems to work!
[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 ||
223 !(e->xfocus.detail == NotifyNonlinearVirtual ||
224 e->xfocus.detail == NotifyNonlinear))
225 return;
226 g_message("FOCUS IN %lx", window);
227 break;
228 case FocusOut:
229 if (e->xfocus.mode == NotifyGrab ||
230 !(e->xfocus.detail == NotifyNonlinearVirtual ||
231 e->xfocus.detail == NotifyNonlinear))
232 return;
233
234 /* FocusOut events just make us look for FocusIn events. They
235 are mostly ignored otherwise. */
236 {
237 XEvent fi;
238 if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
239 g_message("FOCUS OUT %lx IN %lx", e->xfocus.window, fi.xfocus.window);
240 event_process(&fi);
241
242 if (fi.xfocus.window == e->xfocus.window)
243 return;
244 } else {
245 g_message("FOCUS OUT %lx IN 0x0", e->xfocus.window);
246 }
247 }
248 break;
249 case EnterNotify:
250 case LeaveNotify:
251 event_lasttime = e->xcrossing.time;
252 /* XXX this caused problems before... but i don't remember why. hah.
253 so back it is. if problems arise again, then try filtering on the
254 detail instead of the mode. */
255 if (e->xcrossing.mode != NotifyNormal) return;
256 break;
257 }
258
259 client = g_hash_table_lookup(client_map, (gpointer)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 g_message("Proxying configure event for 0x%lx", window);
282
283 /* we are not to be held responsible if someone sends us an
284 invalid request! */
285 xerror_set_ignore(TRUE);
286 XConfigureWindow(ob_display, window,
287 e->xconfigurerequest.value_mask, &xwc);
288 xerror_set_ignore(FALSE);
289 }
290
291 /* dispatch the event to registered handlers */
292 dispatch_x(e, client);
293 }
294
295 static void event_handle_root(XEvent *e)
296 {
297 Atom msgtype;
298
299 switch(e->type) {
300 case ClientMessage:
301 if (e->xclient.format != 32) break;
302
303 msgtype = e->xclient.message_type;
304 if (msgtype == prop_atoms.net_current_desktop) {
305 unsigned int d = e->xclient.data.l[0];
306 if (d <= screen_num_desktops)
307 screen_set_desktop(d);
308 } else if (msgtype == prop_atoms.net_number_of_desktops) {
309 unsigned int d = e->xclient.data.l[0];
310 if (d > 0)
311 screen_set_num_desktops(d);
312 } else if (msgtype == prop_atoms.net_showing_desktop) {
313 screen_show_desktop(e->xclient.data.l[0] != 0);
314 }
315 break;
316 case PropertyNotify:
317 if (e->xproperty.atom == prop_atoms.net_desktop_names)
318 screen_update_desktop_names();
319 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
320 screen_update_layout();
321 break;
322 }
323 }
324
325 static void event_handle_client(Client *client, XEvent *e)
326 {
327 XEvent ce;
328 Atom msgtype;
329
330 switch (e->type) {
331 case FocusIn:
332 case FocusOut:
333 client_set_focused(client, e->type == FocusIn);
334 break;
335 case ConfigureRequest:
336 g_message("ConfigureRequest for window %lx", client->window);
337 /* compress these */
338 while (XCheckTypedWindowEvent(ob_display, client->window,
339 ConfigureRequest, &ce)) {
340 /* XXX if this causes bad things.. we can compress config req's
341 with the same mask. */
342 e->xconfigurerequest.value_mask |=
343 ce.xconfigurerequest.value_mask;
344 if (ce.xconfigurerequest.value_mask & CWX)
345 e->xconfigurerequest.x = ce.xconfigurerequest.x;
346 if (ce.xconfigurerequest.value_mask & CWY)
347 e->xconfigurerequest.y = ce.xconfigurerequest.y;
348 if (ce.xconfigurerequest.value_mask & CWWidth)
349 e->xconfigurerequest.width = ce.xconfigurerequest.width;
350 if (ce.xconfigurerequest.value_mask & CWHeight)
351 e->xconfigurerequest.height = ce.xconfigurerequest.height;
352 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
353 e->xconfigurerequest.border_width =
354 ce.xconfigurerequest.border_width;
355 if (ce.xconfigurerequest.value_mask & CWStackMode)
356 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
357 }
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 g_message("UnmapNotify for %lx", client->window);
420 client_unmanage(client);
421 break;
422 case DestroyNotify:
423 g_message("DestroyNotify for %lx", client->window);
424 client_unmanage(client);
425 break;
426 case ReparentNotify:
427 /* this is when the client is first taken captive in the frame */
428 if (e->xreparent.parent == client->frame->plate) break;
429
430 /*
431 This event is quite rare and is usually handled in unmapHandler.
432 However, if the window is unmapped when the reparent event occurs,
433 the window manager never sees it because an unmap event is not sent
434 to an already unmapped window.
435 */
436
437 /* we don't want the reparent event, put it back on the stack for the
438 X server to deal with after we unmanage the window */
439 XPutBackEvent(ob_display, e);
440
441 client_unmanage(client);
442 break;
443 case MapRequest:
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 client_set_desktop(client, e->xclient.data.l[0]);
490 } else if (msgtype == prop_atoms.net_wm_state) {
491 /* can't compress these */
492 g_message("net_wm_state %s %ld %ld for 0x%lx",
493 (e->xclient.data.l[0] == 0 ? "Remove" :
494 e->xclient.data.l[0] == 1 ? "Add" :
495 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
496 e->xclient.data.l[1], e->xclient.data.l[2],
497 client->window);
498 client_set_state(client, e->xclient.data.l[0],
499 e->xclient.data.l[1], e->xclient.data.l[2]);
500 } else if (msgtype == prop_atoms.net_close_window) {
501 g_message("net_close_window for 0x%lx", client->window);
502 client_close(client);
503 } else if (msgtype == prop_atoms.net_active_window) {
504 g_message("net_active_window for 0x%lx", client->window);
505 if (screen_showing_desktop)
506 screen_show_desktop(FALSE);
507 if (client->iconic)
508 client_iconify(client, FALSE, TRUE);
509 else if (!client->frame->visible)
510 /* if its not visible for other reasons, then don't mess
511 with it */
512 break;
513 if (client->shaded)
514 client_shade(client, FALSE);
515 client_focus(client);
516 stacking_raise(client);
517 }
518 break;
519 case PropertyNotify:
520 /* validate cuz we query stuff off the client here */
521 if (!client_validate(client)) break;
522
523 /* compress changes to a single property into a single change */
524 while (XCheckTypedWindowEvent(ob_display, e->type,
525 client->window, &ce)) {
526 /* XXX: it would be nice to compress ALL changes to a property,
527 not just changes in a row without other props between. */
528 if (ce.xproperty.atom != e->xproperty.atom) {
529 XPutBackEvent(ob_display, &ce);
530 break;
531 }
532 }
533
534 msgtype = e->xproperty.atom;
535 if (msgtype == XA_WM_NORMAL_HINTS) {
536 client_update_normal_hints(client);
537 /* normal hints can make a window non-resizable */
538 client_setup_decor_and_functions(client);
539 }
540 else if (msgtype == XA_WM_HINTS)
541 client_update_wmhints(client);
542 else if (msgtype == XA_WM_TRANSIENT_FOR) {
543 client_update_transient_for(client);
544 client_get_type(client);
545 /* type may have changed, so update the layer */
546 client_calc_layer(client);
547 client_setup_decor_and_functions(client);
548 }
549 else if (msgtype == prop_atoms.net_wm_name ||
550 msgtype == prop_atoms.wm_name)
551 client_update_title(client);
552 else if (msgtype == prop_atoms.net_wm_icon_name ||
553 msgtype == prop_atoms.wm_icon_name)
554 client_update_icon_title(client);
555 else if (msgtype == prop_atoms.wm_class)
556 client_update_class(client);
557 else if (msgtype == prop_atoms.wm_protocols) {
558 client_update_protocols(client);
559 client_setup_decor_and_functions(client);
560 }
561 else if (msgtype == prop_atoms.net_wm_strut)
562 client_update_strut(client);
563 else if (msgtype == prop_atoms.net_wm_icon)
564 client_update_icons(client);
565 else if (msgtype == prop_atoms.kwm_win_icon)
566 client_update_kwm_icon(client);
567 }
568 }
This page took 0.061879 seconds and 5 git commands to generate.