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