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