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