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