]> Dogcows Code - chaz/openbox/blob - openbox/event.c
use the focus client when the client is null for keyboard events
[chaz/openbox] / openbox / event.c
1 #include "debug.h"
2 #include "openbox.h"
3 #include "dock.h"
4 #include "client.h"
5 #include "xerror.h"
6 #include "prop.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "frame.h"
10 #include "menu.h"
11 #include "menuframe.h"
12 #include "keyboard.h"
13 #include "mouse.h"
14 #include "mainloop.h"
15 #include "framerender.h"
16 #include "focus.h"
17 #include "moveresize.h"
18 #include "stacking.h"
19 #include "extensions.h"
20 #include "event.h"
21
22 #include <X11/Xlib.h>
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
25 #include <glib.h>
26
27 #ifdef HAVE_SYS_SELECT_H
28 # include <sys/select.h>
29 #endif
30 #ifdef HAVE_SIGNAL_H
31 # include <signal.h>
32 #endif
33
34 #ifdef USE_SM
35 #include <X11/ICE/ICElib.h>
36 #endif
37
38 static void event_process(const XEvent *e, gpointer data);
39 static void event_handle_root(XEvent *e);
40 static void event_handle_menu(XEvent *e);
41 static void event_handle_dock(ObDock *s, XEvent *e);
42 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
43 static void event_handle_client(ObClient *c, XEvent *e);
44
45 static gboolean focus_delay_func(gpointer data);
46 static void focus_delay_client_dest(gpointer data);
47
48 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
49 (e)->xfocus.detail == NotifyAncestor || \
50 (e)->xfocus.detail > NotifyNonlinearVirtual)
51 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
52 (e)->xfocus.detail == NotifyInferior || \
53 (e)->xfocus.detail == NotifyAncestor || \
54 (e)->xfocus.detail > NotifyNonlinearVirtual)
55
56 Time event_lasttime = 0;
57
58 /*! The value of the mask for the NumLock modifier */
59 unsigned int NumLockMask;
60 /*! The value of the mask for the ScrollLock modifier */
61 unsigned int ScrollLockMask;
62 /*! The key codes for the modifier keys */
63 static XModifierKeymap *modmap;
64 /*! Table of the constant modifier masks */
65 static const int mask_table[] = {
66 ShiftMask, LockMask, ControlMask, Mod1Mask,
67 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
68 };
69 static int mask_table_size;
70
71 static ObClient *focus_delay_client;
72
73 #ifdef USE_SM
74 static void ice_handler(int fd, gpointer conn)
75 {
76 Bool b;
77 IceProcessMessages(conn, NULL, &b);
78 }
79
80 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
81 IcePointer *watch_data)
82 {
83 static gint fd = -1;
84
85 if (opening) {
86 fd = IceConnectionNumber(conn);
87 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
88 } else {
89 ob_main_loop_fd_remove(ob_main_loop, fd);
90 fd = -1;
91 }
92 }
93 #endif
94
95 void event_startup(gboolean reconfig)
96 {
97 if (reconfig) return;
98
99 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
100
101 /* get lock masks that are defined by the display (not constant) */
102 modmap = XGetModifierMapping(ob_display);
103 g_assert(modmap);
104 if (modmap && modmap->max_keypermod > 0) {
105 size_t cnt;
106 const size_t size = mask_table_size * modmap->max_keypermod;
107 /* get the values of the keyboard lock modifiers
108 Note: Caps lock is not retrieved the same way as Scroll and Num
109 lock since it doesn't need to be. */
110 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
111 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
112 XK_Scroll_Lock);
113
114 for (cnt = 0; cnt < size; ++cnt) {
115 if (! modmap->modifiermap[cnt]) continue;
116
117 if (num_lock == modmap->modifiermap[cnt])
118 NumLockMask = mask_table[cnt / modmap->max_keypermod];
119 if (scroll_lock == modmap->modifiermap[cnt])
120 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
121 }
122 }
123
124 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
125
126 #ifdef USE_SM
127 IceAddConnectionWatch(ice_watch, NULL);
128 #endif
129
130 client_add_destructor(focus_delay_client_dest);
131 }
132
133 void event_shutdown(gboolean reconfig)
134 {
135 if (reconfig) return;
136
137 client_remove_destructor(focus_delay_client_dest);
138 XFreeModifiermap(modmap);
139 }
140
141 static Window event_get_window(XEvent *e)
142 {
143 Window window;
144
145 /* pick a window */
146 switch (e->type) {
147 case SelectionClear:
148 window = RootWindow(ob_display, ob_screen);
149 break;
150 case MapRequest:
151 window = e->xmap.window;
152 break;
153 case UnmapNotify:
154 window = e->xunmap.window;
155 break;
156 case DestroyNotify:
157 window = e->xdestroywindow.window;
158 break;
159 case ConfigureRequest:
160 window = e->xconfigurerequest.window;
161 break;
162 case ConfigureNotify:
163 window = e->xconfigure.window;
164 break;
165 default:
166 #ifdef XKB
167 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
168 switch (((XkbAnyEvent*)e)->xkb_type) {
169 case XkbBellNotify:
170 window = ((XkbBellNotifyEvent*)e)->window;
171 default:
172 window = None;
173 }
174 } else
175 #endif
176 window = e->xany.window;
177 }
178 return window;
179 }
180
181 static void event_set_lasttime(XEvent *e)
182 {
183 Time t = 0;
184
185 /* grab the lasttime and hack up the state */
186 switch (e->type) {
187 case ButtonPress:
188 case ButtonRelease:
189 t = e->xbutton.time;
190 break;
191 case KeyPress:
192 t = e->xkey.time;
193 break;
194 case KeyRelease:
195 t = e->xkey.time;
196 break;
197 case MotionNotify:
198 t = e->xmotion.time;
199 break;
200 case PropertyNotify:
201 t = e->xproperty.time;
202 break;
203 case EnterNotify:
204 case LeaveNotify:
205 t = e->xcrossing.time;
206 break;
207 default:
208 /* if more event types are anticipated, get their timestamp
209 explicitly */
210 break;
211 }
212
213 if (t > event_lasttime)
214 event_lasttime = t;
215 }
216
217 #define STRIP_MODS(s) \
218 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
219 /* kill off the Button1Mask etc, only want the modifiers */ \
220 s &= (ControlMask | ShiftMask | Mod1Mask | \
221 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
222
223 static void event_hack_mods(XEvent *e)
224 {
225 KeyCode *kp;
226 int i, k;
227
228 switch (e->type) {
229 case ButtonPress:
230 case ButtonRelease:
231 STRIP_MODS(e->xbutton.state);
232 break;
233 case KeyPress:
234 STRIP_MODS(e->xkey.state);
235 break;
236 case KeyRelease:
237 STRIP_MODS(e->xkey.state);
238 /* remove from the state the mask of the modifier being released, if
239 it is a modifier key being released (this is a little ugly..) */
240 kp = modmap->modifiermap;
241 for (i = 0; i < mask_table_size; ++i) {
242 for (k = 0; k < modmap->max_keypermod; ++k) {
243 if (*kp == e->xkey.keycode) { /* found the keycode */
244 /* remove the mask for it */
245 e->xkey.state &= ~mask_table[i];
246 /* cause the first loop to break; */
247 i = mask_table_size;
248 break; /* get outta here! */
249 }
250 ++kp;
251 }
252 }
253 break;
254 case MotionNotify:
255 STRIP_MODS(e->xmotion.state);
256 /* compress events */
257 {
258 XEvent ce;
259 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
260 e->type, &ce)) {
261 e->xmotion.x_root = ce.xmotion.x_root;
262 e->xmotion.y_root = ce.xmotion.y_root;
263 }
264 }
265 break;
266 }
267 }
268
269 static gboolean event_ignore(XEvent *e, ObClient *client)
270 {
271 switch(e->type) {
272 case FocusIn:
273 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
274 because of RevertToPointerRoot. If the focus ends up reverting to
275 pointer root on a workspace change, then the FocusIn event that we
276 want will be of type NotifyAncestor. This situation does not occur
277 for FocusOut, so it is safely ignored there.
278 */
279 if (INVALID_FOCUSIN(e) ||
280 client == NULL) {
281 #ifdef DEBUG_FOCUS
282 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
283 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
284 #endif
285 /* says a client was not found for the event (or a valid FocusIn
286 event was not found.
287 */
288 e->xfocus.window = None;
289 return TRUE;
290 }
291
292 #ifdef DEBUG_FOCUS
293 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
294 e->xfocus.mode, e->xfocus.detail);
295 #endif
296 break;
297 case FocusOut:
298 if (INVALID_FOCUSOUT(e)) {
299 #ifdef DEBUG_FOCUS
300 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
301 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
302 #endif
303 return TRUE;
304 }
305
306 #ifdef DEBUG_FOCUS
307 ob_debug("FocusOut on %lx mode %d detail %d\n",
308 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
309 #endif
310
311 {
312 XEvent fe;
313 gboolean fallback = TRUE;
314
315 while (TRUE) {
316 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
317 FocusOut, &fe))
318 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
319 break;
320 if (fe.type == FocusOut) {
321 #ifdef DEBUG_FOCUS
322 ob_debug("found pending FocusOut\n");
323 #endif
324 if (!INVALID_FOCUSOUT(&fe)) {
325 /* if there is a VALID FocusOut still coming, don't
326 fallback focus yet, we'll deal with it then */
327 XPutBackEvent(ob_display, &fe);
328 fallback = FALSE;
329 break;
330 }
331 } else {
332 #ifdef DEBUG_FOCUS
333 ob_debug("found pending FocusIn\n");
334 #endif
335 /* is the focused window getting a FocusOut/In back to
336 itself?
337 */
338 if (fe.xfocus.window == e->xfocus.window &&
339 !event_ignore(&fe, client)) {
340 /*
341 if focus_client is not set, then we can't do
342 this. we need the FocusIn. This happens in the
343 case when the set_focus_client(NULL) in the
344 focus_fallback function fires and then
345 focus_fallback picks the currently focused
346 window (such as on a SendToDesktop-esque action.
347 */
348 if (focus_client) {
349 #ifdef DEBUG_FOCUS
350 ob_debug("focused window got an Out/In back to "
351 "itself IGNORED both\n");
352 #endif
353 return TRUE;
354 } else {
355 event_process(&fe, NULL);
356 #ifdef DEBUG_FOCUS
357 ob_debug("focused window got an Out/In back to "
358 "itself but focus_client was null "
359 "IGNORED just the Out\n");
360 #endif
361 return TRUE;
362 }
363 }
364
365 /* once all the FocusOut's have been dealt with, if there
366 is a FocusIn still left and it is valid, then use it */
367 event_process(&fe, NULL);
368 /* secret magic way of event_process telling us that no
369 client was found for the FocusIn event. ^_^ */
370 if (fe.xfocus.window != None) {
371 fallback = FALSE;
372 break;
373 }
374 }
375 }
376 if (fallback) {
377 #ifdef DEBUG_FOCUS
378 ob_debug("no valid FocusIn and no FocusOut events found, "
379 "falling back\n");
380 #endif
381 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
382 }
383 }
384 break;
385 case EnterNotify:
386 case LeaveNotify:
387 /* NotifyUngrab occurs when a mouse button is released and the event is
388 caused, like when lowering a window */
389 /* NotifyVirtual occurs when ungrabbing the pointer */
390 if (e->xcrossing.mode == NotifyGrab ||
391 e->xcrossing.detail == NotifyInferior ||
392 (e->xcrossing.mode == NotifyUngrab &&
393 e->xcrossing.detail == NotifyVirtual)) {
394 #ifdef DEBUG_FOCUS
395 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
396 (e->type == EnterNotify ? "Enter" : "Leave"),
397 e->xcrossing.mode,
398 e->xcrossing.detail, client?client->window:0);
399 #endif
400 return TRUE;
401 }
402 #ifdef DEBUG_FOCUS
403 ob_debug("%sNotify mode %d detail %d on %lx\n",
404 (e->type == EnterNotify ? "Enter" : "Leave"),
405 e->xcrossing.mode,
406 e->xcrossing.detail, client?client->window:0);
407 #endif
408 break;
409 }
410 return FALSE;
411 }
412
413 static void event_process(const XEvent *ec, gpointer data)
414 {
415 Window window;
416 ObClient *client = NULL;
417 ObDock *dock = NULL;
418 ObDockApp *dockapp = NULL;
419 ObWindow *obwin = NULL;
420 XEvent ee, *e;
421
422 /* make a copy we can mangle */
423 ee = *ec;
424 e = &ee;
425
426 window = event_get_window(e);
427 if ((obwin = g_hash_table_lookup(window_map, &window))) {
428 switch (obwin->type) {
429 case Window_Dock:
430 dock = WINDOW_AS_DOCK(obwin);
431 break;
432 case Window_DockApp:
433 dockapp = WINDOW_AS_DOCKAPP(obwin);
434 break;
435 case Window_Client:
436 client = WINDOW_AS_CLIENT(obwin);
437 break;
438 case Window_Menu:
439 case Window_Internal:
440 /* not to be used for events */
441 g_assert_not_reached();
442 break;
443 }
444 }
445
446 event_set_lasttime(e);
447 event_hack_mods(e);
448 if (event_ignore(e, client))
449 return;
450
451 /* deal with it in the kernel */
452 if (client)
453 event_handle_client(client, e);
454 else if (dockapp)
455 event_handle_dockapp(dockapp, e);
456 else if (dock)
457 event_handle_dock(dock, e);
458 else if (window == RootWindow(ob_display, ob_screen))
459 event_handle_root(e);
460 else if (e->type == MapRequest)
461 client_manage(window);
462 else if (e->type == ConfigureRequest) {
463 /* unhandled configure requests must be used to configure the
464 window directly */
465 XWindowChanges xwc;
466
467 xwc.x = e->xconfigurerequest.x;
468 xwc.y = e->xconfigurerequest.y;
469 xwc.width = e->xconfigurerequest.width;
470 xwc.height = e->xconfigurerequest.height;
471 xwc.border_width = e->xconfigurerequest.border_width;
472 xwc.sibling = e->xconfigurerequest.above;
473 xwc.stack_mode = e->xconfigurerequest.detail;
474
475 /* we are not to be held responsible if someone sends us an
476 invalid request! */
477 xerror_set_ignore(TRUE);
478 XConfigureWindow(ob_display, window,
479 e->xconfigurerequest.value_mask, &xwc);
480 xerror_set_ignore(FALSE);
481 }
482
483 /* user input (action-bound) events */
484 if (e->type == ButtonPress || e->type == ButtonRelease ||
485 e->type == MotionNotify || e->type == KeyPress ||
486 e->type == KeyRelease)
487 {
488 if (menu_frame_visible)
489 event_handle_menu(e);
490 else {
491 if (!keyboard_process_interactive_grab(e, &client)) {
492 if (moveresize_in_progress)
493 moveresize_event(e);
494
495 if (e->type == ButtonPress || e->type == ButtonRelease ||
496 e->type == MotionNotify)
497 mouse_event(client, e);
498 else if (e->type == KeyPress)
499 /* when in the middle of a focus cycling action, this
500 causes the window which appears to be focused to be
501 the one on which the actions will be executed */
502 keyboard_event((focus_cycle_target ?
503 focus_cycle_target :
504 (client ? client : focus_client)), e);
505 }
506 }
507 }
508 }
509
510 static void event_handle_root(XEvent *e)
511 {
512 Atom msgtype;
513
514 switch(e->type) {
515 case SelectionClear:
516 ob_debug("Another WM has requested to replace us. Exiting.\n");
517 ob_exit();
518 break;
519
520 case ClientMessage:
521 if (e->xclient.format != 32) break;
522
523 msgtype = e->xclient.message_type;
524 if (msgtype == prop_atoms.net_current_desktop) {
525 unsigned int d = e->xclient.data.l[0];
526 if (d < screen_num_desktops)
527 screen_set_desktop(d);
528 } else if (msgtype == prop_atoms.net_number_of_desktops) {
529 unsigned int d = e->xclient.data.l[0];
530 if (d > 0)
531 screen_set_num_desktops(d);
532 } else if (msgtype == prop_atoms.net_showing_desktop) {
533 screen_show_desktop(e->xclient.data.l[0] != 0);
534 }
535 break;
536 case PropertyNotify:
537 if (e->xproperty.atom == prop_atoms.net_desktop_names)
538 screen_update_desktop_names();
539 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
540 screen_update_layout();
541 break;
542 case ConfigureNotify:
543 #ifdef XRANDR
544 XRRUpdateConfiguration(e);
545 #endif
546 screen_resize();
547 break;
548 default:
549 ;
550 #ifdef VIDMODE
551 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
552 ob_debug("VIDMODE EVENT\n");
553 }
554 #endif
555 }
556 }
557
558 static void event_handle_client(ObClient *client, XEvent *e)
559 {
560 XEvent ce;
561 Atom msgtype;
562 int i=0;
563 ObFrameContext con;
564
565 switch (e->type) {
566 case VisibilityNotify:
567 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
568 break;
569 case ButtonPress:
570 case ButtonRelease:
571 /* Wheel buttons don't draw because they are an instant click, so it
572 is a waste of resources to go drawing it. */
573 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
574 switch (frame_context(client, e->xbutton.window)) {
575 case OB_FRAME_CONTEXT_MAXIMIZE:
576 client->frame->max_press = (e->type == ButtonPress);
577 framerender_frame(client->frame);
578 break;
579 case OB_FRAME_CONTEXT_CLOSE:
580 client->frame->close_press = (e->type == ButtonPress);
581 framerender_frame(client->frame);
582 break;
583 case OB_FRAME_CONTEXT_ICONIFY:
584 client->frame->iconify_press = (e->type == ButtonPress);
585 framerender_frame(client->frame);
586 break;
587 case OB_FRAME_CONTEXT_ALLDESKTOPS:
588 client->frame->desk_press = (e->type == ButtonPress);
589 framerender_frame(client->frame);
590 break;
591 case OB_FRAME_CONTEXT_SHADE:
592 client->frame->shade_press = (e->type == ButtonPress);
593 framerender_frame(client->frame);
594 break;
595 default:
596 /* nothing changes with clicks for any other contexts */
597 break;
598 }
599 }
600 break;
601 case FocusIn:
602 #ifdef DEBUG_FOCUS
603 ob_debug("FocusIn on client for %lx\n", client->window);
604 #endif
605 if (client != focus_client) {
606 focus_set_client(client);
607 frame_adjust_focus(client->frame, TRUE);
608 }
609 break;
610 case FocusOut:
611 #ifdef DEBUG_FOCUS
612 ob_debug("FocusOut on client for %lx\n", client->window);
613 #endif
614 /* are we a fullscreen window or a transient of one? (checks layer)
615 if we are then we need to be iconified since we are losing focus
616 */
617 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
618 !client_search_focus_tree_full(client))
619 /* iconify fullscreen windows when they and their transients
620 aren't focused */
621 client_iconify(client, TRUE, TRUE);
622 frame_adjust_focus(client->frame, FALSE);
623 break;
624 case LeaveNotify:
625 con = frame_context(client, e->xcrossing.window);
626 switch (con) {
627 case OB_FRAME_CONTEXT_MAXIMIZE:
628 client->frame->max_hover = FALSE;
629 frame_adjust_state(client->frame);
630 break;
631 case OB_FRAME_CONTEXT_ALLDESKTOPS:
632 client->frame->desk_hover = FALSE;
633 frame_adjust_state(client->frame);
634 break;
635 case OB_FRAME_CONTEXT_SHADE:
636 client->frame->shade_hover = FALSE;
637 frame_adjust_state(client->frame);
638 break;
639 case OB_FRAME_CONTEXT_ICONIFY:
640 client->frame->iconify_hover = FALSE;
641 frame_adjust_state(client->frame);
642 break;
643 case OB_FRAME_CONTEXT_CLOSE:
644 client->frame->close_hover = FALSE;
645 frame_adjust_state(client->frame);
646 break;
647 case OB_FRAME_CONTEXT_FRAME:
648 /* XXX if doing a 'reconfigure' make sure you kill this timer,
649 maybe all timers.. */
650 if (config_focus_delay && client == focus_delay_client) {
651 ob_main_loop_timeout_remove_data(ob_main_loop,
652 focus_delay_func,
653 focus_delay_client);
654 focus_delay_client = NULL;
655 }
656 default:
657 break;
658 }
659 break;
660 case EnterNotify:
661 con = frame_context(client, e->xcrossing.window);
662 switch (con) {
663 case OB_FRAME_CONTEXT_MAXIMIZE:
664 client->frame->max_hover = TRUE;
665 frame_adjust_state(client->frame);
666 break;
667 case OB_FRAME_CONTEXT_ALLDESKTOPS:
668 client->frame->desk_hover = TRUE;
669 frame_adjust_state(client->frame);
670 break;
671 case OB_FRAME_CONTEXT_SHADE:
672 client->frame->shade_hover = TRUE;
673 frame_adjust_state(client->frame);
674 break;
675 case OB_FRAME_CONTEXT_ICONIFY:
676 client->frame->iconify_hover = TRUE;
677 frame_adjust_state(client->frame);
678 break;
679 case OB_FRAME_CONTEXT_CLOSE:
680 client->frame->close_hover = TRUE;
681 frame_adjust_state(client->frame);
682 break;
683 case OB_FRAME_CONTEXT_FRAME:
684 if (client_normal(client)) {
685 if (ob_state() == OB_STATE_STARTING) {
686 /* move it to the top of the focus order */
687 guint desktop = client->desktop;
688 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
689 focus_order[desktop] = g_list_remove(focus_order[desktop],
690 client);
691 focus_order[desktop] = g_list_prepend(focus_order[desktop],
692 client);
693 } else if (config_focus_follow) {
694 #ifdef DEBUG_FOCUS
695 ob_debug("EnterNotify on %lx, focusing window\n",
696 client->window);
697 #endif
698 if (config_focus_delay) {
699 ob_main_loop_timeout_add(ob_main_loop,
700 config_focus_delay,
701 focus_delay_func,
702 client, NULL);
703 focus_delay_client = client;
704 } else
705 client_focus(client);
706 }
707 }
708 break;
709 default:
710 break;
711 }
712 break;
713 case ConfigureRequest:
714 /* compress these */
715 while (XCheckTypedWindowEvent(ob_display, client->window,
716 ConfigureRequest, &ce)) {
717 ++i;
718 /* XXX if this causes bad things.. we can compress config req's
719 with the same mask. */
720 e->xconfigurerequest.value_mask |=
721 ce.xconfigurerequest.value_mask;
722 if (ce.xconfigurerequest.value_mask & CWX)
723 e->xconfigurerequest.x = ce.xconfigurerequest.x;
724 if (ce.xconfigurerequest.value_mask & CWY)
725 e->xconfigurerequest.y = ce.xconfigurerequest.y;
726 if (ce.xconfigurerequest.value_mask & CWWidth)
727 e->xconfigurerequest.width = ce.xconfigurerequest.width;
728 if (ce.xconfigurerequest.value_mask & CWHeight)
729 e->xconfigurerequest.height = ce.xconfigurerequest.height;
730 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
731 e->xconfigurerequest.border_width =
732 ce.xconfigurerequest.border_width;
733 if (ce.xconfigurerequest.value_mask & CWStackMode)
734 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
735 }
736
737 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
738 if (client->iconic || client->shaded) return;
739
740 /* resize, then move, as specified in the EWMH section 7.7 */
741 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
742 CWX | CWY |
743 CWBorderWidth)) {
744 int x, y, w, h;
745 ObCorner corner;
746
747 if (e->xconfigurerequest.value_mask & CWBorderWidth)
748 client->border_width = e->xconfigurerequest.border_width;
749
750 x = (e->xconfigurerequest.value_mask & CWX) ?
751 e->xconfigurerequest.x : client->area.x;
752 y = (e->xconfigurerequest.value_mask & CWY) ?
753 e->xconfigurerequest.y : client->area.y;
754 w = (e->xconfigurerequest.value_mask & CWWidth) ?
755 e->xconfigurerequest.width : client->area.width;
756 h = (e->xconfigurerequest.value_mask & CWHeight) ?
757 e->xconfigurerequest.height : client->area.height;
758
759 {
760 int newx = x;
761 int newy = y;
762 int fw = w +
763 client->frame->size.left + client->frame->size.right;
764 int fh = h +
765 client->frame->size.top + client->frame->size.bottom;
766 client_find_onscreen(client, &newx, &newy, fw, fh,
767 client_normal(client));
768 if (e->xconfigurerequest.value_mask & CWX)
769 x = newx;
770 if (e->xconfigurerequest.value_mask & CWY)
771 y = newy;
772 }
773
774 switch (client->gravity) {
775 case NorthEastGravity:
776 case EastGravity:
777 corner = OB_CORNER_TOPRIGHT;
778 break;
779 case SouthWestGravity:
780 case SouthGravity:
781 corner = OB_CORNER_BOTTOMLEFT;
782 break;
783 case SouthEastGravity:
784 corner = OB_CORNER_BOTTOMRIGHT;
785 break;
786 default: /* NorthWest, Static, etc */
787 corner = OB_CORNER_TOPLEFT;
788 }
789
790 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
791 TRUE);
792 }
793
794 if (e->xconfigurerequest.value_mask & CWStackMode) {
795 switch (e->xconfigurerequest.detail) {
796 case Below:
797 case BottomIf:
798 stacking_lower(CLIENT_AS_WINDOW(client));
799 break;
800
801 case Above:
802 case TopIf:
803 default:
804 stacking_raise(CLIENT_AS_WINDOW(client));
805 break;
806 }
807 }
808 break;
809 case UnmapNotify:
810 if (client->ignore_unmaps) {
811 client->ignore_unmaps--;
812 break;
813 }
814 client_unmanage(client);
815 break;
816 case DestroyNotify:
817 client_unmanage(client);
818 break;
819 case ReparentNotify:
820 /* this is when the client is first taken captive in the frame */
821 if (e->xreparent.parent == client->frame->plate) break;
822
823 /*
824 This event is quite rare and is usually handled in unmapHandler.
825 However, if the window is unmapped when the reparent event occurs,
826 the window manager never sees it because an unmap event is not sent
827 to an already unmapped window.
828 */
829
830 /* we don't want the reparent event, put it back on the stack for the
831 X server to deal with after we unmanage the window */
832 XPutBackEvent(ob_display, e);
833
834 client_unmanage(client);
835 break;
836 case MapRequest:
837 ob_debug("MapRequest for 0x%lx\n", client->window);
838 if (!client->iconic) break; /* this normally doesn't happen, but if it
839 does, we don't want it! */
840 if (screen_showing_desktop)
841 screen_show_desktop(FALSE);
842 client_iconify(client, FALSE, TRUE);
843 if (!client->frame->visible)
844 /* if its not visible still, then don't mess with it */
845 break;
846 if (client->shaded)
847 client_shade(client, FALSE);
848 client_focus(client);
849 stacking_raise(CLIENT_AS_WINDOW(client));
850 break;
851 case ClientMessage:
852 /* validate cuz we query stuff off the client here */
853 if (!client_validate(client)) break;
854
855 if (e->xclient.format != 32) return;
856
857 msgtype = e->xclient.message_type;
858 if (msgtype == prop_atoms.wm_change_state) {
859 /* compress changes into a single change */
860 while (XCheckTypedWindowEvent(ob_display, client->window,
861 e->type, &ce)) {
862 /* XXX: it would be nice to compress ALL messages of a
863 type, not just messages in a row without other
864 message types between. */
865 if (ce.xclient.message_type != msgtype) {
866 XPutBackEvent(ob_display, &ce);
867 break;
868 }
869 e->xclient = ce.xclient;
870 }
871 client_set_wm_state(client, e->xclient.data.l[0]);
872 } else if (msgtype == prop_atoms.net_wm_desktop) {
873 /* compress changes into a single change */
874 while (XCheckTypedWindowEvent(ob_display, client->window,
875 e->type, &ce)) {
876 /* XXX: it would be nice to compress ALL messages of a
877 type, not just messages in a row without other
878 message types between. */
879 if (ce.xclient.message_type != msgtype) {
880 XPutBackEvent(ob_display, &ce);
881 break;
882 }
883 e->xclient = ce.xclient;
884 }
885 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
886 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
887 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
888 FALSE);
889 } else if (msgtype == prop_atoms.net_wm_state) {
890 /* can't compress these */
891 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
892 (e->xclient.data.l[0] == 0 ? "Remove" :
893 e->xclient.data.l[0] == 1 ? "Add" :
894 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
895 e->xclient.data.l[1], e->xclient.data.l[2],
896 client->window);
897 client_set_state(client, e->xclient.data.l[0],
898 e->xclient.data.l[1], e->xclient.data.l[2]);
899 } else if (msgtype == prop_atoms.net_close_window) {
900 ob_debug("net_close_window for 0x%lx\n", client->window);
901 client_close(client);
902 } else if (msgtype == prop_atoms.net_active_window) {
903 ob_debug("net_active_window for 0x%lx\n", client->window);
904 client_activate(client, FALSE);
905 } else if (msgtype == prop_atoms.net_wm_moveresize) {
906 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
907 if ((Atom)e->xclient.data.l[2] ==
908 prop_atoms.net_wm_moveresize_size_topleft ||
909 (Atom)e->xclient.data.l[2] ==
910 prop_atoms.net_wm_moveresize_size_top ||
911 (Atom)e->xclient.data.l[2] ==
912 prop_atoms.net_wm_moveresize_size_topright ||
913 (Atom)e->xclient.data.l[2] ==
914 prop_atoms.net_wm_moveresize_size_right ||
915 (Atom)e->xclient.data.l[2] ==
916 prop_atoms.net_wm_moveresize_size_right ||
917 (Atom)e->xclient.data.l[2] ==
918 prop_atoms.net_wm_moveresize_size_bottomright ||
919 (Atom)e->xclient.data.l[2] ==
920 prop_atoms.net_wm_moveresize_size_bottom ||
921 (Atom)e->xclient.data.l[2] ==
922 prop_atoms.net_wm_moveresize_size_bottomleft ||
923 (Atom)e->xclient.data.l[2] ==
924 prop_atoms.net_wm_moveresize_size_left ||
925 (Atom)e->xclient.data.l[2] ==
926 prop_atoms.net_wm_moveresize_move ||
927 (Atom)e->xclient.data.l[2] ==
928 prop_atoms.net_wm_moveresize_size_keyboard ||
929 (Atom)e->xclient.data.l[2] ==
930 prop_atoms.net_wm_moveresize_move_keyboard) {
931
932 moveresize_start(client, e->xclient.data.l[0],
933 e->xclient.data.l[1], e->xclient.data.l[3],
934 e->xclient.data.l[2]);
935 }
936 } else if (msgtype == prop_atoms.net_moveresize_window) {
937 int oldg = client->gravity;
938 int tmpg, x, y, w, h;
939
940 if (e->xclient.data.l[0] & 0xff)
941 tmpg = e->xclient.data.l[0] & 0xff;
942 else
943 tmpg = oldg;
944
945 if (e->xclient.data.l[0] & 1 << 8)
946 x = e->xclient.data.l[1];
947 else
948 x = client->area.x;
949 if (e->xclient.data.l[0] & 1 << 9)
950 y = e->xclient.data.l[2];
951 else
952 y = client->area.y;
953 if (e->xclient.data.l[0] & 1 << 10)
954 w = e->xclient.data.l[3];
955 else
956 w = client->area.width;
957 if (e->xclient.data.l[0] & 1 << 11)
958 h = e->xclient.data.l[4];
959 else
960 h = client->area.height;
961 client->gravity = tmpg;
962
963 {
964 int newx = x;
965 int newy = y;
966 int fw = w +
967 client->frame->size.left + client->frame->size.right;
968 int fh = h +
969 client->frame->size.top + client->frame->size.bottom;
970 client_find_onscreen(client, &newx, &newy, fw, fh,
971 client_normal(client));
972 if (e->xclient.data.l[0] & 1 << 8)
973 x = newx;
974 if (e->xclient.data.l[0] & 1 << 9)
975 y = newy;
976 }
977
978 client_configure(client, OB_CORNER_TOPLEFT,
979 x, y, w, h, FALSE, TRUE);
980
981 client->gravity = oldg;
982 }
983 break;
984 case PropertyNotify:
985 /* validate cuz we query stuff off the client here */
986 if (!client_validate(client)) break;
987
988 /* compress changes to a single property into a single change */
989 while (XCheckTypedWindowEvent(ob_display, client->window,
990 e->type, &ce)) {
991 Atom a, b;
992
993 /* XXX: it would be nice to compress ALL changes to a property,
994 not just changes in a row without other props between. */
995
996 a = ce.xproperty.atom;
997 b = e->xproperty.atom;
998
999 if (a == b)
1000 continue;
1001 if ((a == prop_atoms.net_wm_name ||
1002 a == prop_atoms.wm_name ||
1003 a == prop_atoms.net_wm_icon_name ||
1004 a == prop_atoms.wm_icon_name)
1005 &&
1006 (b == prop_atoms.net_wm_name ||
1007 b == prop_atoms.wm_name ||
1008 b == prop_atoms.net_wm_icon_name ||
1009 b == prop_atoms.wm_icon_name)) {
1010 continue;
1011 }
1012 if ((a == prop_atoms.net_wm_icon ||
1013 a == prop_atoms.kwm_win_icon)
1014 &&
1015 (b == prop_atoms.net_wm_icon ||
1016 b == prop_atoms.kwm_win_icon))
1017 continue;
1018
1019 XPutBackEvent(ob_display, &ce);
1020 break;
1021 }
1022
1023 msgtype = e->xproperty.atom;
1024 if (msgtype == XA_WM_NORMAL_HINTS) {
1025 client_update_normal_hints(client);
1026 /* normal hints can make a window non-resizable */
1027 client_setup_decor_and_functions(client);
1028 } else if (msgtype == XA_WM_HINTS) {
1029 client_update_wmhints(client);
1030 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1031 client_update_transient_for(client);
1032 client_get_type(client);
1033 /* type may have changed, so update the layer */
1034 client_calc_layer(client);
1035 client_setup_decor_and_functions(client);
1036 } else if (msgtype == prop_atoms.net_wm_name ||
1037 msgtype == prop_atoms.wm_name ||
1038 msgtype == prop_atoms.net_wm_icon_name ||
1039 msgtype == prop_atoms.wm_icon_name) {
1040 client_update_title(client);
1041 } else if (msgtype == prop_atoms.wm_class) {
1042 client_update_class(client);
1043 } else if (msgtype == prop_atoms.wm_protocols) {
1044 client_update_protocols(client);
1045 client_setup_decor_and_functions(client);
1046 }
1047 else if (msgtype == prop_atoms.net_wm_strut) {
1048 client_update_strut(client);
1049 }
1050 else if (msgtype == prop_atoms.net_wm_icon ||
1051 msgtype == prop_atoms.kwm_win_icon) {
1052 client_update_icons(client);
1053 }
1054 default:
1055 ;
1056 #ifdef SHAPE
1057 if (extensions_shape && e->type == extensions_shape_event_basep) {
1058 client->shaped = ((XShapeEvent*)e)->shaped;
1059 frame_adjust_shape(client->frame);
1060 }
1061 #endif
1062 }
1063 }
1064
1065 static void event_handle_dock(ObDock *s, XEvent *e)
1066 {
1067 switch (e->type) {
1068 case ButtonPress:
1069 stacking_raise(DOCK_AS_WINDOW(s));
1070 break;
1071 case EnterNotify:
1072 dock_hide(FALSE);
1073 break;
1074 case LeaveNotify:
1075 dock_hide(TRUE);
1076 break;
1077 }
1078 }
1079
1080 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1081 {
1082 switch (e->type) {
1083 case MotionNotify:
1084 dock_app_drag(app, &e->xmotion);
1085 break;
1086 case UnmapNotify:
1087 if (app->ignore_unmaps) {
1088 app->ignore_unmaps--;
1089 break;
1090 }
1091 dock_remove(app, TRUE);
1092 break;
1093 case DestroyNotify:
1094 dock_remove(app, FALSE);
1095 break;
1096 case ReparentNotify:
1097 dock_remove(app, FALSE);
1098 break;
1099 case ConfigureNotify:
1100 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1101 break;
1102 }
1103 }
1104
1105 ObMenuFrame* find_active_menu()
1106 {
1107 GList *it;
1108 ObMenuFrame *f;
1109
1110 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1111 f = it->data;
1112 if (f->selected)
1113 break;
1114 }
1115 return it ? it->data : NULL;
1116 }
1117
1118 static void event_handle_menu(XEvent *ev)
1119 {
1120 ObMenuFrame *f;
1121 ObMenuEntryFrame *e;
1122
1123 switch (ev->type) {
1124 case ButtonRelease:
1125 if (!(f = menu_frame_under(ev->xbutton.x_root,
1126 ev->xbutton.y_root)))
1127 menu_frame_hide_all();
1128 else {
1129 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1130 ev->xbutton.y_root)))
1131 menu_entry_frame_execute(e, ev->xbutton.state);
1132 }
1133 break;
1134 case MotionNotify:
1135 if ((f = menu_frame_under(ev->xmotion.x_root,
1136 ev->xmotion.y_root))) {
1137 menu_frame_move_on_screen(f);
1138 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1139 ev->xmotion.y_root)))
1140 menu_frame_select(f, e);
1141 }
1142 break;
1143 case KeyPress:
1144 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1145 menu_frame_hide_all();
1146 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1147 ObMenuFrame *f;
1148 if ((f = find_active_menu()))
1149 menu_entry_frame_execute(f->selected, ev->xkey.state);
1150 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1151 ObMenuFrame *f;
1152 if ((f = find_active_menu()) && f->parent)
1153 menu_frame_select(f, NULL);
1154 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1155 ObMenuFrame *f;
1156 if ((f = find_active_menu()) && f->child)
1157 menu_frame_select_next(f->child);
1158 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1159 ObMenuFrame *f;
1160 if ((f = find_active_menu()))
1161 menu_frame_select_previous(f);
1162 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1163 ObMenuFrame *f;
1164 if ((f = find_active_menu()))
1165 menu_frame_select_next(f);
1166 }
1167 break;
1168 }
1169 }
1170
1171 static gboolean focus_delay_func(gpointer data)
1172 {
1173 client_focus(focus_delay_client);
1174 return FALSE; /* no repeat */
1175 }
1176
1177 static void focus_delay_client_dest(gpointer data)
1178 {
1179 ObClient *c = data;
1180 if (c == focus_delay_client) {
1181 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1182 focus_delay_client);
1183 focus_delay_client = NULL;
1184 }
1185 }
This page took 0.0883659999999999 seconds and 5 git commands to generate.