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