]> Dogcows Code - chaz/openbox/blob - openbox/event.c
ignore all NotifyInferior crossing events again
[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 EnterNotify:
307 case LeaveNotify:
308 if (e->xcrossing.detail == NotifyInferior)
309 return TRUE;
310 break;
311 case FocusIn:
312 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
313 because of RevertToPointerRoot. If the focus ends up reverting to
314 pointer root on a workspace change, then the FocusIn event that we
315 want will be of type NotifyAncestor. This situation does not occur
316 for FocusOut, so it is safely ignored there.
317 */
318 if (INVALID_FOCUSIN(e) ||
319 client == NULL) {
320 #ifdef DEBUG_FOCUS
321 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
322 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
323 #endif
324 /* says a client was not found for the event (or a valid FocusIn
325 event was not found.
326 */
327 e->xfocus.window = None;
328 return TRUE;
329 }
330
331 #ifdef DEBUG_FOCUS
332 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
333 e->xfocus.mode, e->xfocus.detail);
334 #endif
335 break;
336 case FocusOut:
337 if (INVALID_FOCUSOUT(e)) {
338 #ifdef DEBUG_FOCUS
339 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
340 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
341 #endif
342 return TRUE;
343 }
344
345 #ifdef DEBUG_FOCUS
346 ob_debug("FocusOut on %lx mode %d detail %d\n",
347 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
348 #endif
349
350 {
351 XEvent fe;
352 gboolean fallback = TRUE;
353
354 while (TRUE) {
355 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
356 FocusOut, &fe))
357 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
358 break;
359 if (fe.type == FocusOut) {
360 #ifdef DEBUG_FOCUS
361 ob_debug("found pending FocusOut\n");
362 #endif
363 if (!INVALID_FOCUSOUT(&fe)) {
364 /* if there is a VALID FocusOut still coming, don't
365 fallback focus yet, we'll deal with it then */
366 XPutBackEvent(ob_display, &fe);
367 fallback = FALSE;
368 break;
369 }
370 } else {
371 #ifdef DEBUG_FOCUS
372 ob_debug("found pending FocusIn\n");
373 #endif
374 /* is the focused window getting a FocusOut/In back to
375 itself?
376 */
377 if (fe.xfocus.window == e->xfocus.window &&
378 !event_ignore(&fe, client)) {
379 /*
380 if focus_client is not set, then we can't do
381 this. we need the FocusIn. This happens in the
382 case when the set_focus_client(NULL) in the
383 focus_fallback function fires and then
384 focus_fallback picks the currently focused
385 window (such as on a SendToDesktop-esque action.
386 */
387 if (focus_client) {
388 #ifdef DEBUG_FOCUS
389 ob_debug("focused window got an Out/In back to "
390 "itself IGNORED both\n");
391 #endif
392 return TRUE;
393 } else {
394 event_process(&fe, NULL);
395 #ifdef DEBUG_FOCUS
396 ob_debug("focused window got an Out/In back to "
397 "itself but focus_client was null "
398 "IGNORED just the Out\n");
399 #endif
400 return TRUE;
401 }
402 }
403
404 {
405 ObEventData d;
406
407 /* once all the FocusOut's have been dealt with, if
408 there is a FocusIn still left and it is valid, then
409 use it */
410 event_process(&fe, &d);
411 if (!d.ignored) {
412 #ifdef DEBUG_FOCUS
413 ob_debug("FocusIn was OK, so don't fallback\n");
414 #endif
415 fallback = FALSE;
416 break;
417 }
418 }
419 }
420 }
421 if (fallback) {
422 #ifdef DEBUG_FOCUS
423 ob_debug("no valid FocusIn and no FocusOut events found, "
424 "falling back\n");
425 #endif
426 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
427 }
428 }
429 break;
430 }
431 return FALSE;
432 }
433
434 static void event_process(const XEvent *ec, gpointer data)
435 {
436 Window window;
437 ObGroup *group = NULL;
438 ObClient *client = NULL;
439 ObDock *dock = NULL;
440 ObDockApp *dockapp = NULL;
441 ObWindow *obwin = NULL;
442 XEvent ee, *e;
443 ObEventData *ed = data;
444
445 /* make a copy we can mangle */
446 ee = *ec;
447 e = &ee;
448
449 window = event_get_window(e);
450 if (!(e->type == PropertyNotify &&
451 (group = g_hash_table_lookup(group_map, &window))))
452 if ((obwin = g_hash_table_lookup(window_map, &window))) {
453 switch (obwin->type) {
454 case Window_Dock:
455 dock = WINDOW_AS_DOCK(obwin);
456 break;
457 case Window_DockApp:
458 dockapp = WINDOW_AS_DOCKAPP(obwin);
459 break;
460 case Window_Client:
461 client = WINDOW_AS_CLIENT(obwin);
462 break;
463 case Window_Menu:
464 case Window_Internal:
465 /* not to be used for events */
466 g_assert_not_reached();
467 break;
468 }
469 }
470
471 event_set_lasttime(e);
472 event_hack_mods(e);
473 if (event_ignore(e, client)) {
474 if (ed)
475 ed->ignored = TRUE;
476 return;
477 } else if (ed)
478 ed->ignored = FALSE;
479
480 /* deal with it in the kernel */
481 if (group)
482 event_handle_group(group, e);
483 else if (client)
484 event_handle_client(client, e);
485 else if (dockapp)
486 event_handle_dockapp(dockapp, e);
487 else if (dock)
488 event_handle_dock(dock, e);
489 else if (window == RootWindow(ob_display, ob_screen))
490 event_handle_root(e);
491 else if (e->type == MapRequest)
492 client_manage(window);
493 else if (e->type == ConfigureRequest) {
494 /* unhandled configure requests must be used to configure the
495 window directly */
496 XWindowChanges xwc;
497
498 xwc.x = e->xconfigurerequest.x;
499 xwc.y = e->xconfigurerequest.y;
500 xwc.width = e->xconfigurerequest.width;
501 xwc.height = e->xconfigurerequest.height;
502 xwc.border_width = e->xconfigurerequest.border_width;
503 xwc.sibling = e->xconfigurerequest.above;
504 xwc.stack_mode = e->xconfigurerequest.detail;
505
506 /* we are not to be held responsible if someone sends us an
507 invalid request! */
508 xerror_set_ignore(TRUE);
509 XConfigureWindow(ob_display, window,
510 e->xconfigurerequest.value_mask, &xwc);
511 xerror_set_ignore(FALSE);
512 }
513
514 /* user input (action-bound) events */
515 if (e->type == ButtonPress || e->type == ButtonRelease ||
516 e->type == MotionNotify || e->type == KeyPress ||
517 e->type == KeyRelease)
518 {
519 if (menu_frame_visible)
520 event_handle_menu(e);
521 else {
522 if (!keyboard_process_interactive_grab(e, &client)) {
523 if (moveresize_in_progress) {
524 moveresize_event(e);
525
526 /* make further actions work on the client being
527 moved/resized */
528 client = moveresize_client;
529 }
530
531 menu_can_hide = FALSE;
532 ob_main_loop_timeout_add(ob_main_loop,
533 G_USEC_PER_SEC / 4,
534 menu_hide_delay_func,
535 NULL, NULL);
536
537 if (e->type == ButtonPress || e->type == ButtonRelease ||
538 e->type == MotionNotify)
539 mouse_event(client, e);
540 else if (e->type == KeyPress)
541 /* when in the middle of a focus cycling action, this
542 causes the window which appears to be focused to be
543 the one on which the actions will be executed */
544 keyboard_event((focus_cycle_target ?
545 focus_cycle_target : client), e);
546 }
547 }
548 }
549 }
550
551 static void event_handle_root(XEvent *e)
552 {
553 Atom msgtype;
554
555 switch(e->type) {
556 case SelectionClear:
557 ob_debug("Another WM has requested to replace us. Exiting.\n");
558 ob_exit(0);
559 break;
560
561 case ClientMessage:
562 if (e->xclient.format != 32) break;
563
564 msgtype = e->xclient.message_type;
565 if (msgtype == prop_atoms.net_current_desktop) {
566 unsigned int d = e->xclient.data.l[0];
567 if (d < screen_num_desktops)
568 screen_set_desktop(d);
569 } else if (msgtype == prop_atoms.net_number_of_desktops) {
570 unsigned int d = e->xclient.data.l[0];
571 if (d > 0)
572 screen_set_num_desktops(d);
573 } else if (msgtype == prop_atoms.net_showing_desktop) {
574 screen_show_desktop(e->xclient.data.l[0] != 0);
575 }
576 break;
577 case PropertyNotify:
578 if (e->xproperty.atom == prop_atoms.net_desktop_names)
579 screen_update_desktop_names();
580 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
581 screen_update_layout();
582 break;
583 case ConfigureNotify:
584 #ifdef XRANDR
585 XRRUpdateConfiguration(e);
586 #endif
587 screen_resize();
588 break;
589 default:
590 ;
591 #ifdef VIDMODE
592 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
593 ob_debug("VIDMODE EVENT\n");
594 }
595 #endif
596 }
597 }
598
599 static void event_handle_group(ObGroup *group, XEvent *e)
600 {
601 GSList *it;
602
603 g_assert(e->type == PropertyNotify);
604
605 for (it = group->members; it; it = g_slist_next(it))
606 event_handle_client(it->data, e);
607 }
608
609 void event_enter_client(ObClient *client)
610 {
611 g_assert(config_focus_follow);
612
613 if (client_normal(client) && client_can_focus(client)) {
614 if (config_focus_delay) {
615 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
616 ob_main_loop_timeout_add(ob_main_loop,
617 config_focus_delay,
618 focus_delay_func,
619 client, NULL);
620 } else
621 focus_delay_func(client);
622 }
623 }
624
625 static void event_handle_client(ObClient *client, XEvent *e)
626 {
627 XEvent ce;
628 Atom msgtype;
629 int i=0;
630 ObFrameContext con;
631
632 switch (e->type) {
633 case VisibilityNotify:
634 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
635 break;
636 case ButtonPress:
637 case ButtonRelease:
638 /* Wheel buttons don't draw because they are an instant click, so it
639 is a waste of resources to go drawing it. */
640 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
641 con = frame_context(client, e->xbutton.window);
642 con = mouse_button_frame_context(con, e->xbutton.button);
643 switch (con) {
644 case OB_FRAME_CONTEXT_MAXIMIZE:
645 client->frame->max_press = (e->type == ButtonPress);
646 framerender_frame(client->frame);
647 break;
648 case OB_FRAME_CONTEXT_CLOSE:
649 client->frame->close_press = (e->type == ButtonPress);
650 framerender_frame(client->frame);
651 break;
652 case OB_FRAME_CONTEXT_ICONIFY:
653 client->frame->iconify_press = (e->type == ButtonPress);
654 framerender_frame(client->frame);
655 break;
656 case OB_FRAME_CONTEXT_ALLDESKTOPS:
657 client->frame->desk_press = (e->type == ButtonPress);
658 framerender_frame(client->frame);
659 break;
660 case OB_FRAME_CONTEXT_SHADE:
661 client->frame->shade_press = (e->type == ButtonPress);
662 framerender_frame(client->frame);
663 break;
664 default:
665 /* nothing changes with clicks for any other contexts */
666 break;
667 }
668 }
669 break;
670 case FocusIn:
671 #ifdef DEBUG_FOCUS
672 ob_debug("FocusIn on client for %lx\n", client->window);
673 #endif
674 if (client != focus_client) {
675 focus_set_client(client);
676 frame_adjust_focus(client->frame, TRUE);
677 }
678 break;
679 case FocusOut:
680 #ifdef DEBUG_FOCUS
681 ob_debug("FocusOut on client for %lx\n", client->window);
682 #endif
683 /* are we a fullscreen window or a transient of one? (checks layer)
684 if we are then we need to be iconified since we are losing focus
685 */
686 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
687 !client_search_focus_tree_full(client))
688 /* iconify fullscreen windows when they and their transients
689 aren't focused */
690 client_iconify(client, TRUE, TRUE);
691 frame_adjust_focus(client->frame, FALSE);
692 break;
693 case LeaveNotify:
694 con = frame_context(client, e->xcrossing.window);
695 switch (con) {
696 case OB_FRAME_CONTEXT_MAXIMIZE:
697 client->frame->max_hover = FALSE;
698 frame_adjust_state(client->frame);
699 break;
700 case OB_FRAME_CONTEXT_ALLDESKTOPS:
701 client->frame->desk_hover = FALSE;
702 frame_adjust_state(client->frame);
703 break;
704 case OB_FRAME_CONTEXT_SHADE:
705 client->frame->shade_hover = FALSE;
706 frame_adjust_state(client->frame);
707 break;
708 case OB_FRAME_CONTEXT_ICONIFY:
709 client->frame->iconify_hover = FALSE;
710 frame_adjust_state(client->frame);
711 break;
712 case OB_FRAME_CONTEXT_CLOSE:
713 client->frame->close_hover = FALSE;
714 frame_adjust_state(client->frame);
715 break;
716 case OB_FRAME_CONTEXT_FRAME:
717 /*
718 if (config_focus_follow && config_focus_delay)
719 ob_main_loop_timeout_remove_data(ob_main_loop,
720 focus_delay_func,
721 client);
722 */
723 break;
724 default:
725 break;
726 }
727 break;
728 case EnterNotify:
729 {
730 gboolean nofocus = FALSE;
731
732 if (ignore_enter_focus) {
733 ignore_enter_focus--;
734 nofocus = TRUE;
735 }
736
737 con = frame_context(client, e->xcrossing.window);
738 switch (con) {
739 case OB_FRAME_CONTEXT_MAXIMIZE:
740 client->frame->max_hover = TRUE;
741 frame_adjust_state(client->frame);
742 break;
743 case OB_FRAME_CONTEXT_ALLDESKTOPS:
744 client->frame->desk_hover = TRUE;
745 frame_adjust_state(client->frame);
746 break;
747 case OB_FRAME_CONTEXT_SHADE:
748 client->frame->shade_hover = TRUE;
749 frame_adjust_state(client->frame);
750 break;
751 case OB_FRAME_CONTEXT_ICONIFY:
752 client->frame->iconify_hover = TRUE;
753 frame_adjust_state(client->frame);
754 break;
755 case OB_FRAME_CONTEXT_CLOSE:
756 client->frame->close_hover = TRUE;
757 frame_adjust_state(client->frame);
758 break;
759 case OB_FRAME_CONTEXT_FRAME:
760 if (e->xcrossing.mode == NotifyGrab ||
761 e->xcrossing.mode == NotifyUngrab)
762 {
763 #ifdef DEBUG_FOCUS
764 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
765 (e->type == EnterNotify ? "Enter" : "Leave"),
766 e->xcrossing.mode,
767 e->xcrossing.detail, client?client->window:0);
768 #endif
769 } else {
770 #ifdef DEBUG_FOCUS
771 ob_debug("%sNotify mode %d detail %d on %lx, "
772 "focusing window\n",
773 (e->type == EnterNotify ? "Enter" : "Leave"),
774 e->xcrossing.mode,
775 e->xcrossing.detail, client?client->window:0);
776 #endif
777 if (!nofocus && config_focus_follow)
778 event_enter_client(client);
779 }
780 break;
781 default:
782 break;
783 }
784 break;
785 }
786 case ConfigureRequest:
787 /* compress these */
788 while (XCheckTypedWindowEvent(ob_display, client->window,
789 ConfigureRequest, &ce)) {
790 ++i;
791 /* XXX if this causes bad things.. we can compress config req's
792 with the same mask. */
793 e->xconfigurerequest.value_mask |=
794 ce.xconfigurerequest.value_mask;
795 if (ce.xconfigurerequest.value_mask & CWX)
796 e->xconfigurerequest.x = ce.xconfigurerequest.x;
797 if (ce.xconfigurerequest.value_mask & CWY)
798 e->xconfigurerequest.y = ce.xconfigurerequest.y;
799 if (ce.xconfigurerequest.value_mask & CWWidth)
800 e->xconfigurerequest.width = ce.xconfigurerequest.width;
801 if (ce.xconfigurerequest.value_mask & CWHeight)
802 e->xconfigurerequest.height = ce.xconfigurerequest.height;
803 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
804 e->xconfigurerequest.border_width =
805 ce.xconfigurerequest.border_width;
806 if (ce.xconfigurerequest.value_mask & CWStackMode)
807 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
808 }
809
810 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
811 if (client->iconic || client->shaded) return;
812
813 /* resize, then move, as specified in the EWMH section 7.7 */
814 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
815 CWX | CWY |
816 CWBorderWidth)) {
817 int x, y, w, h;
818 ObCorner corner;
819
820 if (e->xconfigurerequest.value_mask & CWBorderWidth)
821 client->border_width = e->xconfigurerequest.border_width;
822
823 x = (e->xconfigurerequest.value_mask & CWX) ?
824 e->xconfigurerequest.x : client->area.x;
825 y = (e->xconfigurerequest.value_mask & CWY) ?
826 e->xconfigurerequest.y : client->area.y;
827 w = (e->xconfigurerequest.value_mask & CWWidth) ?
828 e->xconfigurerequest.width : client->area.width;
829 h = (e->xconfigurerequest.value_mask & CWHeight) ?
830 e->xconfigurerequest.height : client->area.height;
831
832 {
833 int newx = x;
834 int newy = y;
835 int fw = w +
836 client->frame->size.left + client->frame->size.right;
837 int fh = h +
838 client->frame->size.top + client->frame->size.bottom;
839 client_find_onscreen(client, &newx, &newy, fw, fh,
840 client_normal(client));
841 if (e->xconfigurerequest.value_mask & CWX)
842 x = newx;
843 if (e->xconfigurerequest.value_mask & CWY)
844 y = newy;
845 }
846
847 switch (client->gravity) {
848 case NorthEastGravity:
849 case EastGravity:
850 corner = OB_CORNER_TOPRIGHT;
851 break;
852 case SouthWestGravity:
853 case SouthGravity:
854 corner = OB_CORNER_BOTTOMLEFT;
855 break;
856 case SouthEastGravity:
857 corner = OB_CORNER_BOTTOMRIGHT;
858 break;
859 default: /* NorthWest, Static, etc */
860 corner = OB_CORNER_TOPLEFT;
861 }
862
863 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
864 TRUE);
865 }
866
867 if (e->xconfigurerequest.value_mask & CWStackMode) {
868 switch (e->xconfigurerequest.detail) {
869 case Below:
870 case BottomIf:
871 stacking_lower(CLIENT_AS_WINDOW(client));
872 break;
873
874 case Above:
875 case TopIf:
876 default:
877 stacking_raise(CLIENT_AS_WINDOW(client));
878 break;
879 }
880 }
881 break;
882 case UnmapNotify:
883 if (client->ignore_unmaps) {
884 client->ignore_unmaps--;
885 break;
886 }
887 client_unmanage(client);
888 break;
889 case DestroyNotify:
890 client_unmanage(client);
891 break;
892 case ReparentNotify:
893 /* this is when the client is first taken captive in the frame */
894 if (e->xreparent.parent == client->frame->plate) break;
895
896 /*
897 This event is quite rare and is usually handled in unmapHandler.
898 However, if the window is unmapped when the reparent event occurs,
899 the window manager never sees it because an unmap event is not sent
900 to an already unmapped window.
901 */
902
903 /* we don't want the reparent event, put it back on the stack for the
904 X server to deal with after we unmanage the window */
905 XPutBackEvent(ob_display, e);
906
907 client_unmanage(client);
908 break;
909 case MapRequest:
910 ob_debug("MapRequest for 0x%lx\n", client->window);
911 if (!client->iconic) break; /* this normally doesn't happen, but if it
912 does, we don't want it! */
913 if (screen_showing_desktop)
914 screen_show_desktop(FALSE);
915 client_iconify(client, FALSE, TRUE);
916 if (!client->frame->visible)
917 /* if its not visible still, then don't mess with it */
918 break;
919 if (client->shaded)
920 client_shade(client, FALSE);
921 client_focus(client);
922 stacking_raise(CLIENT_AS_WINDOW(client));
923 break;
924 case ClientMessage:
925 /* validate cuz we query stuff off the client here */
926 if (!client_validate(client)) break;
927
928 if (e->xclient.format != 32) return;
929
930 msgtype = e->xclient.message_type;
931 if (msgtype == prop_atoms.wm_change_state) {
932 /* compress changes into a single change */
933 while (XCheckTypedWindowEvent(ob_display, client->window,
934 e->type, &ce)) {
935 /* XXX: it would be nice to compress ALL messages of a
936 type, not just messages in a row without other
937 message types between. */
938 if (ce.xclient.message_type != msgtype) {
939 XPutBackEvent(ob_display, &ce);
940 break;
941 }
942 e->xclient = ce.xclient;
943 }
944 client_set_wm_state(client, e->xclient.data.l[0]);
945 } else if (msgtype == prop_atoms.net_wm_desktop) {
946 /* compress changes into a single change */
947 while (XCheckTypedWindowEvent(ob_display, client->window,
948 e->type, &ce)) {
949 /* XXX: it would be nice to compress ALL messages of a
950 type, not just messages in a row without other
951 message types between. */
952 if (ce.xclient.message_type != msgtype) {
953 XPutBackEvent(ob_display, &ce);
954 break;
955 }
956 e->xclient = ce.xclient;
957 }
958 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
959 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
960 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
961 FALSE);
962 } else if (msgtype == prop_atoms.net_wm_state) {
963 /* can't compress these */
964 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
965 (e->xclient.data.l[0] == 0 ? "Remove" :
966 e->xclient.data.l[0] == 1 ? "Add" :
967 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
968 e->xclient.data.l[1], e->xclient.data.l[2],
969 client->window);
970 client_set_state(client, e->xclient.data.l[0],
971 e->xclient.data.l[1], e->xclient.data.l[2]);
972 } else if (msgtype == prop_atoms.net_close_window) {
973 ob_debug("net_close_window for 0x%lx\n", client->window);
974 client_close(client);
975 } else if (msgtype == prop_atoms.net_active_window) {
976 ob_debug("net_active_window for 0x%lx\n", client->window);
977 client_activate(client, FALSE);
978 } else if (msgtype == prop_atoms.net_wm_moveresize) {
979 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
980 if ((Atom)e->xclient.data.l[2] ==
981 prop_atoms.net_wm_moveresize_size_topleft ||
982 (Atom)e->xclient.data.l[2] ==
983 prop_atoms.net_wm_moveresize_size_top ||
984 (Atom)e->xclient.data.l[2] ==
985 prop_atoms.net_wm_moveresize_size_topright ||
986 (Atom)e->xclient.data.l[2] ==
987 prop_atoms.net_wm_moveresize_size_right ||
988 (Atom)e->xclient.data.l[2] ==
989 prop_atoms.net_wm_moveresize_size_right ||
990 (Atom)e->xclient.data.l[2] ==
991 prop_atoms.net_wm_moveresize_size_bottomright ||
992 (Atom)e->xclient.data.l[2] ==
993 prop_atoms.net_wm_moveresize_size_bottom ||
994 (Atom)e->xclient.data.l[2] ==
995 prop_atoms.net_wm_moveresize_size_bottomleft ||
996 (Atom)e->xclient.data.l[2] ==
997 prop_atoms.net_wm_moveresize_size_left ||
998 (Atom)e->xclient.data.l[2] ==
999 prop_atoms.net_wm_moveresize_move ||
1000 (Atom)e->xclient.data.l[2] ==
1001 prop_atoms.net_wm_moveresize_size_keyboard ||
1002 (Atom)e->xclient.data.l[2] ==
1003 prop_atoms.net_wm_moveresize_move_keyboard) {
1004
1005 moveresize_start(client, e->xclient.data.l[0],
1006 e->xclient.data.l[1], e->xclient.data.l[3],
1007 e->xclient.data.l[2]);
1008 }
1009 } else if (msgtype == prop_atoms.net_moveresize_window) {
1010 int oldg = client->gravity;
1011 int tmpg, x, y, w, h;
1012
1013 if (e->xclient.data.l[0] & 0xff)
1014 tmpg = e->xclient.data.l[0] & 0xff;
1015 else
1016 tmpg = oldg;
1017
1018 if (e->xclient.data.l[0] & 1 << 8)
1019 x = e->xclient.data.l[1];
1020 else
1021 x = client->area.x;
1022 if (e->xclient.data.l[0] & 1 << 9)
1023 y = e->xclient.data.l[2];
1024 else
1025 y = client->area.y;
1026 if (e->xclient.data.l[0] & 1 << 10)
1027 w = e->xclient.data.l[3];
1028 else
1029 w = client->area.width;
1030 if (e->xclient.data.l[0] & 1 << 11)
1031 h = e->xclient.data.l[4];
1032 else
1033 h = client->area.height;
1034 client->gravity = tmpg;
1035
1036 {
1037 int newx = x;
1038 int newy = y;
1039 int fw = w +
1040 client->frame->size.left + client->frame->size.right;
1041 int fh = h +
1042 client->frame->size.top + client->frame->size.bottom;
1043 client_find_onscreen(client, &newx, &newy, fw, fh,
1044 client_normal(client));
1045 if (e->xclient.data.l[0] & 1 << 8)
1046 x = newx;
1047 if (e->xclient.data.l[0] & 1 << 9)
1048 y = newy;
1049 }
1050
1051 client_configure(client, OB_CORNER_TOPLEFT,
1052 x, y, w, h, FALSE, TRUE);
1053
1054 client->gravity = oldg;
1055 }
1056 break;
1057 case PropertyNotify:
1058 /* validate cuz we query stuff off the client here */
1059 if (!client_validate(client)) break;
1060
1061 /* compress changes to a single property into a single change */
1062 while (XCheckTypedWindowEvent(ob_display, client->window,
1063 e->type, &ce)) {
1064 Atom a, b;
1065
1066 /* XXX: it would be nice to compress ALL changes to a property,
1067 not just changes in a row without other props between. */
1068
1069 a = ce.xproperty.atom;
1070 b = e->xproperty.atom;
1071
1072 if (a == b)
1073 continue;
1074 if ((a == prop_atoms.net_wm_name ||
1075 a == prop_atoms.wm_name ||
1076 a == prop_atoms.net_wm_icon_name ||
1077 a == prop_atoms.wm_icon_name)
1078 &&
1079 (b == prop_atoms.net_wm_name ||
1080 b == prop_atoms.wm_name ||
1081 b == prop_atoms.net_wm_icon_name ||
1082 b == prop_atoms.wm_icon_name)) {
1083 continue;
1084 }
1085 if ((a == prop_atoms.net_wm_icon ||
1086 a == prop_atoms.kwm_win_icon)
1087 &&
1088 (b == prop_atoms.net_wm_icon ||
1089 b == prop_atoms.kwm_win_icon))
1090 continue;
1091
1092 XPutBackEvent(ob_display, &ce);
1093 break;
1094 }
1095
1096 msgtype = e->xproperty.atom;
1097 if (msgtype == XA_WM_NORMAL_HINTS) {
1098 client_update_normal_hints(client);
1099 /* normal hints can make a window non-resizable */
1100 client_setup_decor_and_functions(client);
1101 } else if (msgtype == XA_WM_HINTS) {
1102 client_update_wmhints(client);
1103 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1104 client_update_transient_for(client);
1105 client_get_type(client);
1106 /* type may have changed, so update the layer */
1107 client_calc_layer(client);
1108 client_setup_decor_and_functions(client);
1109 } else if (msgtype == prop_atoms.net_wm_name ||
1110 msgtype == prop_atoms.wm_name ||
1111 msgtype == prop_atoms.net_wm_icon_name ||
1112 msgtype == prop_atoms.wm_icon_name) {
1113 client_update_title(client);
1114 } else if (msgtype == prop_atoms.wm_class) {
1115 client_update_class(client);
1116 } else if (msgtype == prop_atoms.wm_protocols) {
1117 client_update_protocols(client);
1118 client_setup_decor_and_functions(client);
1119 }
1120 else if (msgtype == prop_atoms.net_wm_strut) {
1121 client_update_strut(client);
1122 }
1123 else if (msgtype == prop_atoms.net_wm_icon ||
1124 msgtype == prop_atoms.kwm_win_icon) {
1125 client_update_icons(client);
1126 }
1127 else if (msgtype == prop_atoms.sm_client_id) {
1128 client_update_sm_client_id(client);
1129 }
1130 default:
1131 ;
1132 #ifdef SHAPE
1133 if (extensions_shape && e->type == extensions_shape_event_basep) {
1134 client->shaped = ((XShapeEvent*)e)->shaped;
1135 frame_adjust_shape(client->frame);
1136 }
1137 #endif
1138 }
1139 }
1140
1141 static void event_handle_dock(ObDock *s, XEvent *e)
1142 {
1143 switch (e->type) {
1144 case ButtonPress:
1145 stacking_raise(DOCK_AS_WINDOW(s));
1146 break;
1147 case EnterNotify:
1148 dock_hide(FALSE);
1149 break;
1150 case LeaveNotify:
1151 dock_hide(TRUE);
1152 break;
1153 }
1154 }
1155
1156 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1157 {
1158 switch (e->type) {
1159 case MotionNotify:
1160 dock_app_drag(app, &e->xmotion);
1161 break;
1162 case UnmapNotify:
1163 if (app->ignore_unmaps) {
1164 app->ignore_unmaps--;
1165 break;
1166 }
1167 dock_remove(app, TRUE);
1168 break;
1169 case DestroyNotify:
1170 dock_remove(app, FALSE);
1171 break;
1172 case ReparentNotify:
1173 dock_remove(app, FALSE);
1174 break;
1175 case ConfigureNotify:
1176 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1177 break;
1178 }
1179 }
1180
1181 ObMenuFrame* find_active_menu()
1182 {
1183 GList *it;
1184 ObMenuFrame *f;
1185
1186 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1187 f = it->data;
1188 if (f->selected)
1189 break;
1190 }
1191 return it ? it->data : NULL;
1192 }
1193
1194 static void event_handle_menu(XEvent *ev)
1195 {
1196 ObMenuFrame *f;
1197 ObMenuEntryFrame *e;
1198
1199 switch (ev->type) {
1200 case ButtonRelease:
1201 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1202 ev->xbutton.y_root)))
1203 menu_entry_frame_execute(e, ev->xbutton.state);
1204 else if (menu_can_hide)
1205 menu_frame_hide_all();
1206 break;
1207 case MotionNotify:
1208 if ((f = menu_frame_under(ev->xmotion.x_root,
1209 ev->xmotion.y_root))) {
1210 menu_frame_move_on_screen(f);
1211 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1212 ev->xmotion.y_root)))
1213 menu_frame_select(f, e);
1214 }
1215 {
1216 ObMenuFrame *a;
1217
1218 a = find_active_menu();
1219 if (a && a != f &&
1220 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1221 {
1222 menu_frame_select(a, NULL);
1223 }
1224 }
1225 break;
1226 case KeyPress:
1227 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1228 menu_frame_hide_all();
1229 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1230 ObMenuFrame *f;
1231 if ((f = find_active_menu()))
1232 menu_entry_frame_execute(f->selected, ev->xkey.state);
1233 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1234 ObMenuFrame *f;
1235 if ((f = find_active_menu()) && f->parent)
1236 menu_frame_select(f, NULL);
1237 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1238 ObMenuFrame *f;
1239 if ((f = find_active_menu()) && f->child)
1240 menu_frame_select_next(f->child);
1241 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1242 ObMenuFrame *f;
1243 if ((f = find_active_menu()))
1244 menu_frame_select_previous(f);
1245 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1246 ObMenuFrame *f;
1247 if ((f = find_active_menu()))
1248 menu_frame_select_next(f);
1249 }
1250 break;
1251 }
1252 }
1253
1254 static gboolean menu_hide_delay_func(gpointer data)
1255 {
1256 menu_can_hide = TRUE;
1257 return FALSE; /* no repeat */
1258 }
1259
1260 static gboolean focus_delay_func(gpointer data)
1261 {
1262 ObClient *c = data;
1263
1264 client_focus(c);
1265 if (config_focus_raise)
1266 stacking_raise(CLIENT_AS_WINDOW(c));
1267 return FALSE; /* no repeat */
1268 }
1269
1270 static void focus_delay_client_dest(gpointer data)
1271 {
1272 ObClient *c = data;
1273
1274 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, c);
1275 }
1276
1277 void event_ignore_queued_enters()
1278 {
1279 GSList *saved = NULL, *it;
1280 XEvent *e;
1281
1282 XSync(ob_display, FALSE);
1283
1284 /* count the events */
1285 while (TRUE) {
1286 e = g_new(XEvent, 1);
1287 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1288 saved = g_slist_append(saved, e);
1289 ++ignore_enter_focus;
1290 } else {
1291 g_free(e);
1292 break;
1293 }
1294 }
1295 /* put the events back */
1296 for (it = saved; it; it = g_slist_next(it)) {
1297 XPutBackEvent(ob_display, it->data);
1298 g_free(it->data);
1299 }
1300 g_slist_free(saved);
1301 }
This page took 0.08886 seconds and 5 git commands to generate.