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