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