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