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