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