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