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