]> Dogcows Code - chaz/openbox/blob - openbox/event.c
allow a number of enter events to be ignored. Don't just skip any enter/leave events...
[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 "debug.h"
20 #include "openbox.h"
21 #include "dock.h"
22 #include "client.h"
23 #include "xerror.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "screen.h"
27 #include "frame.h"
28 #include "menu.h"
29 #include "menuframe.h"
30 #include "keyboard.h"
31 #include "mouse.h"
32 #include "mainloop.h"
33 #include "framerender.h"
34 #include "focus.h"
35 #include "moveresize.h"
36 #include "group.h"
37 #include "stacking.h"
38 #include "extensions.h"
39 #include "event.h"
40
41 #include <X11/Xlib.h>
42 #include <X11/keysym.h>
43 #include <X11/Xatom.h>
44 #include <glib.h>
45
46 #ifdef HAVE_SYS_SELECT_H
47 # include <sys/select.h>
48 #endif
49 #ifdef HAVE_SIGNAL_H
50 # include <signal.h>
51 #endif
52
53 #ifdef USE_SM
54 #include <X11/ICE/ICElib.h>
55 #endif
56
57 typedef struct
58 {
59 gboolean ignored;
60 } ObEventData;
61
62 static void event_process(const XEvent *e, gpointer data);
63 static void event_handle_root(XEvent *e);
64 static void event_handle_menu(XEvent *e);
65 static void event_handle_dock(ObDock *s, XEvent *e);
66 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
67 static void event_handle_client(ObClient *c, XEvent *e);
68 static void event_handle_group(ObGroup *g, XEvent *e);
69
70 static gboolean focus_delay_func(gpointer data);
71 static void focus_delay_client_dest(gpointer data);
72
73 static gboolean menu_hide_delay_func(gpointer data);
74
75 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
76 (e)->xfocus.detail == NotifyAncestor || \
77 (e)->xfocus.detail > NotifyNonlinearVirtual)
78 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
79 (e)->xfocus.detail == NotifyInferior || \
80 (e)->xfocus.detail == NotifyAncestor || \
81 (e)->xfocus.detail > NotifyNonlinearVirtual)
82
83 Time event_lasttime = 0;
84
85 /*! The value of the mask for the NumLock modifier */
86 unsigned int NumLockMask;
87 /*! The value of the mask for the ScrollLock modifier */
88 unsigned int ScrollLockMask;
89 /*! The key codes for the modifier keys */
90 static XModifierKeymap *modmap;
91 /*! Table of the constant modifier masks */
92 static const int mask_table[] = {
93 ShiftMask, LockMask, ControlMask, Mod1Mask,
94 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
95 };
96 static int mask_table_size;
97
98 static guint ignore_enter_focus = 0;
99 static ObClient *focus_delay_client;
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 static void event_handle_client(ObClient *client, XEvent *e)
605 {
606 XEvent ce;
607 Atom msgtype;
608 int i=0;
609 ObFrameContext con;
610
611 switch (e->type) {
612 case VisibilityNotify:
613 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
614 break;
615 case ButtonPress:
616 case ButtonRelease:
617 /* Wheel buttons don't draw because they are an instant click, so it
618 is a waste of resources to go drawing it. */
619 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
620 con = frame_context(client, e->xbutton.window);
621 con = mouse_button_frame_context(con, e->xbutton.button);
622 switch (con) {
623 case OB_FRAME_CONTEXT_MAXIMIZE:
624 client->frame->max_press = (e->type == ButtonPress);
625 framerender_frame(client->frame);
626 break;
627 case OB_FRAME_CONTEXT_CLOSE:
628 client->frame->close_press = (e->type == ButtonPress);
629 framerender_frame(client->frame);
630 break;
631 case OB_FRAME_CONTEXT_ICONIFY:
632 client->frame->iconify_press = (e->type == ButtonPress);
633 framerender_frame(client->frame);
634 break;
635 case OB_FRAME_CONTEXT_ALLDESKTOPS:
636 client->frame->desk_press = (e->type == ButtonPress);
637 framerender_frame(client->frame);
638 break;
639 case OB_FRAME_CONTEXT_SHADE:
640 client->frame->shade_press = (e->type == ButtonPress);
641 framerender_frame(client->frame);
642 break;
643 default:
644 /* nothing changes with clicks for any other contexts */
645 break;
646 }
647 }
648 break;
649 case FocusIn:
650 #ifdef DEBUG_FOCUS
651 ob_debug("FocusIn on client for %lx\n", client->window);
652 #endif
653 if (client != focus_client) {
654 focus_set_client(client);
655 frame_adjust_focus(client->frame, TRUE);
656 }
657 break;
658 case FocusOut:
659 #ifdef DEBUG_FOCUS
660 ob_debug("FocusOut on client for %lx\n", client->window);
661 #endif
662 /* are we a fullscreen window or a transient of one? (checks layer)
663 if we are then we need to be iconified since we are losing focus
664 */
665 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
666 !client_search_focus_tree_full(client))
667 /* iconify fullscreen windows when they and their transients
668 aren't focused */
669 client_iconify(client, TRUE, TRUE);
670 frame_adjust_focus(client->frame, FALSE);
671 break;
672 case LeaveNotify:
673 con = frame_context(client, e->xcrossing.window);
674 switch (con) {
675 case OB_FRAME_CONTEXT_MAXIMIZE:
676 client->frame->max_hover = FALSE;
677 frame_adjust_state(client->frame);
678 break;
679 case OB_FRAME_CONTEXT_ALLDESKTOPS:
680 client->frame->desk_hover = FALSE;
681 frame_adjust_state(client->frame);
682 break;
683 case OB_FRAME_CONTEXT_SHADE:
684 client->frame->shade_hover = FALSE;
685 frame_adjust_state(client->frame);
686 break;
687 case OB_FRAME_CONTEXT_ICONIFY:
688 client->frame->iconify_hover = FALSE;
689 frame_adjust_state(client->frame);
690 break;
691 case OB_FRAME_CONTEXT_CLOSE:
692 client->frame->close_hover = FALSE;
693 frame_adjust_state(client->frame);
694 break;
695 case OB_FRAME_CONTEXT_FRAME:
696 /* XXX if doing a 'reconfigure' make sure you kill this timer,
697 maybe all timers.. */
698 if (config_focus_delay && client == focus_delay_client) {
699 ob_main_loop_timeout_remove_data(ob_main_loop,
700 focus_delay_func,
701 focus_delay_client);
702 focus_delay_client = NULL;
703 }
704 default:
705 break;
706 }
707 break;
708 case EnterNotify:
709 {
710 gboolean nofocus = FALSE;
711
712 if (ignore_enter_focus) {
713 ignore_enter_focus--;
714 nofocus = TRUE;
715 }
716
717 con = frame_context(client, e->xcrossing.window);
718 switch (con) {
719 case OB_FRAME_CONTEXT_MAXIMIZE:
720 client->frame->max_hover = TRUE;
721 frame_adjust_state(client->frame);
722 break;
723 case OB_FRAME_CONTEXT_ALLDESKTOPS:
724 client->frame->desk_hover = TRUE;
725 frame_adjust_state(client->frame);
726 break;
727 case OB_FRAME_CONTEXT_SHADE:
728 client->frame->shade_hover = TRUE;
729 frame_adjust_state(client->frame);
730 break;
731 case OB_FRAME_CONTEXT_ICONIFY:
732 client->frame->iconify_hover = TRUE;
733 frame_adjust_state(client->frame);
734 break;
735 case OB_FRAME_CONTEXT_CLOSE:
736 client->frame->close_hover = TRUE;
737 frame_adjust_state(client->frame);
738 break;
739 case OB_FRAME_CONTEXT_FRAME:
740 if (!nofocus && client_normal(client) && config_focus_follow) {
741 if (e->xcrossing.mode == NotifyGrab ||
742 e->xcrossing.detail == NotifyInferior)
743 {
744 #ifdef DEBUG_FOCUS
745 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
746 (e->type == EnterNotify ? "Enter" : "Leave"),
747 e->xcrossing.mode,
748 e->xcrossing.detail, client?client->window:0);
749 #endif
750 } else {
751 #ifdef DEBUG_FOCUS
752 ob_debug("%sNotify mode %d detail %d on %lx, "
753 "focusing window\n",
754 (e->type == EnterNotify ? "Enter" : "Leave"),
755 e->xcrossing.mode,
756 e->xcrossing.detail, client?client->window:0);
757 #endif
758 if (config_focus_delay) {
759 ob_main_loop_timeout_add(ob_main_loop,
760 config_focus_delay,
761 focus_delay_func,
762 client, NULL);
763 focus_delay_client = client;
764 } else
765 client_focus(client);
766 }
767 }
768 break;
769 default:
770 break;
771 }
772 break;
773 }
774 case ConfigureRequest:
775 /* compress these */
776 while (XCheckTypedWindowEvent(ob_display, client->window,
777 ConfigureRequest, &ce)) {
778 ++i;
779 /* XXX if this causes bad things.. we can compress config req's
780 with the same mask. */
781 e->xconfigurerequest.value_mask |=
782 ce.xconfigurerequest.value_mask;
783 if (ce.xconfigurerequest.value_mask & CWX)
784 e->xconfigurerequest.x = ce.xconfigurerequest.x;
785 if (ce.xconfigurerequest.value_mask & CWY)
786 e->xconfigurerequest.y = ce.xconfigurerequest.y;
787 if (ce.xconfigurerequest.value_mask & CWWidth)
788 e->xconfigurerequest.width = ce.xconfigurerequest.width;
789 if (ce.xconfigurerequest.value_mask & CWHeight)
790 e->xconfigurerequest.height = ce.xconfigurerequest.height;
791 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
792 e->xconfigurerequest.border_width =
793 ce.xconfigurerequest.border_width;
794 if (ce.xconfigurerequest.value_mask & CWStackMode)
795 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
796 }
797
798 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
799 if (client->iconic || client->shaded) return;
800
801 /* resize, then move, as specified in the EWMH section 7.7 */
802 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
803 CWX | CWY |
804 CWBorderWidth)) {
805 int x, y, w, h;
806 ObCorner corner;
807
808 if (e->xconfigurerequest.value_mask & CWBorderWidth)
809 client->border_width = e->xconfigurerequest.border_width;
810
811 x = (e->xconfigurerequest.value_mask & CWX) ?
812 e->xconfigurerequest.x : client->area.x;
813 y = (e->xconfigurerequest.value_mask & CWY) ?
814 e->xconfigurerequest.y : client->area.y;
815 w = (e->xconfigurerequest.value_mask & CWWidth) ?
816 e->xconfigurerequest.width : client->area.width;
817 h = (e->xconfigurerequest.value_mask & CWHeight) ?
818 e->xconfigurerequest.height : client->area.height;
819
820 {
821 int newx = x;
822 int newy = y;
823 int fw = w +
824 client->frame->size.left + client->frame->size.right;
825 int fh = h +
826 client->frame->size.top + client->frame->size.bottom;
827 client_find_onscreen(client, &newx, &newy, fw, fh,
828 client_normal(client));
829 if (e->xconfigurerequest.value_mask & CWX)
830 x = newx;
831 if (e->xconfigurerequest.value_mask & CWY)
832 y = newy;
833 }
834
835 switch (client->gravity) {
836 case NorthEastGravity:
837 case EastGravity:
838 corner = OB_CORNER_TOPRIGHT;
839 break;
840 case SouthWestGravity:
841 case SouthGravity:
842 corner = OB_CORNER_BOTTOMLEFT;
843 break;
844 case SouthEastGravity:
845 corner = OB_CORNER_BOTTOMRIGHT;
846 break;
847 default: /* NorthWest, Static, etc */
848 corner = OB_CORNER_TOPLEFT;
849 }
850
851 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
852 TRUE);
853 }
854
855 if (e->xconfigurerequest.value_mask & CWStackMode) {
856 switch (e->xconfigurerequest.detail) {
857 case Below:
858 case BottomIf:
859 stacking_lower(CLIENT_AS_WINDOW(client));
860 break;
861
862 case Above:
863 case TopIf:
864 default:
865 stacking_raise(CLIENT_AS_WINDOW(client));
866 break;
867 }
868 }
869 break;
870 case UnmapNotify:
871 if (client->ignore_unmaps) {
872 client->ignore_unmaps--;
873 break;
874 }
875 client_unmanage(client);
876 break;
877 case DestroyNotify:
878 client_unmanage(client);
879 break;
880 case ReparentNotify:
881 /* this is when the client is first taken captive in the frame */
882 if (e->xreparent.parent == client->frame->plate) break;
883
884 /*
885 This event is quite rare and is usually handled in unmapHandler.
886 However, if the window is unmapped when the reparent event occurs,
887 the window manager never sees it because an unmap event is not sent
888 to an already unmapped window.
889 */
890
891 /* we don't want the reparent event, put it back on the stack for the
892 X server to deal with after we unmanage the window */
893 XPutBackEvent(ob_display, e);
894
895 client_unmanage(client);
896 break;
897 case MapRequest:
898 ob_debug("MapRequest for 0x%lx\n", client->window);
899 if (!client->iconic) break; /* this normally doesn't happen, but if it
900 does, we don't want it! */
901 if (screen_showing_desktop)
902 screen_show_desktop(FALSE);
903 client_iconify(client, FALSE, TRUE);
904 if (!client->frame->visible)
905 /* if its not visible still, then don't mess with it */
906 break;
907 if (client->shaded)
908 client_shade(client, FALSE);
909 client_focus(client);
910 stacking_raise(CLIENT_AS_WINDOW(client));
911 break;
912 case ClientMessage:
913 /* validate cuz we query stuff off the client here */
914 if (!client_validate(client)) break;
915
916 if (e->xclient.format != 32) return;
917
918 msgtype = e->xclient.message_type;
919 if (msgtype == prop_atoms.wm_change_state) {
920 /* compress changes into a single change */
921 while (XCheckTypedWindowEvent(ob_display, client->window,
922 e->type, &ce)) {
923 /* XXX: it would be nice to compress ALL messages of a
924 type, not just messages in a row without other
925 message types between. */
926 if (ce.xclient.message_type != msgtype) {
927 XPutBackEvent(ob_display, &ce);
928 break;
929 }
930 e->xclient = ce.xclient;
931 }
932 client_set_wm_state(client, e->xclient.data.l[0]);
933 } else if (msgtype == prop_atoms.net_wm_desktop) {
934 /* compress changes into a single change */
935 while (XCheckTypedWindowEvent(ob_display, client->window,
936 e->type, &ce)) {
937 /* XXX: it would be nice to compress ALL messages of a
938 type, not just messages in a row without other
939 message types between. */
940 if (ce.xclient.message_type != msgtype) {
941 XPutBackEvent(ob_display, &ce);
942 break;
943 }
944 e->xclient = ce.xclient;
945 }
946 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
947 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
948 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
949 FALSE);
950 } else if (msgtype == prop_atoms.net_wm_state) {
951 /* can't compress these */
952 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
953 (e->xclient.data.l[0] == 0 ? "Remove" :
954 e->xclient.data.l[0] == 1 ? "Add" :
955 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
956 e->xclient.data.l[1], e->xclient.data.l[2],
957 client->window);
958 client_set_state(client, e->xclient.data.l[0],
959 e->xclient.data.l[1], e->xclient.data.l[2]);
960 } else if (msgtype == prop_atoms.net_close_window) {
961 ob_debug("net_close_window for 0x%lx\n", client->window);
962 client_close(client);
963 } else if (msgtype == prop_atoms.net_active_window) {
964 ob_debug("net_active_window for 0x%lx\n", client->window);
965 client_activate(client, FALSE);
966 } else if (msgtype == prop_atoms.net_wm_moveresize) {
967 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
968 if ((Atom)e->xclient.data.l[2] ==
969 prop_atoms.net_wm_moveresize_size_topleft ||
970 (Atom)e->xclient.data.l[2] ==
971 prop_atoms.net_wm_moveresize_size_top ||
972 (Atom)e->xclient.data.l[2] ==
973 prop_atoms.net_wm_moveresize_size_topright ||
974 (Atom)e->xclient.data.l[2] ==
975 prop_atoms.net_wm_moveresize_size_right ||
976 (Atom)e->xclient.data.l[2] ==
977 prop_atoms.net_wm_moveresize_size_right ||
978 (Atom)e->xclient.data.l[2] ==
979 prop_atoms.net_wm_moveresize_size_bottomright ||
980 (Atom)e->xclient.data.l[2] ==
981 prop_atoms.net_wm_moveresize_size_bottom ||
982 (Atom)e->xclient.data.l[2] ==
983 prop_atoms.net_wm_moveresize_size_bottomleft ||
984 (Atom)e->xclient.data.l[2] ==
985 prop_atoms.net_wm_moveresize_size_left ||
986 (Atom)e->xclient.data.l[2] ==
987 prop_atoms.net_wm_moveresize_move ||
988 (Atom)e->xclient.data.l[2] ==
989 prop_atoms.net_wm_moveresize_size_keyboard ||
990 (Atom)e->xclient.data.l[2] ==
991 prop_atoms.net_wm_moveresize_move_keyboard) {
992
993 moveresize_start(client, e->xclient.data.l[0],
994 e->xclient.data.l[1], e->xclient.data.l[3],
995 e->xclient.data.l[2]);
996 }
997 } else if (msgtype == prop_atoms.net_moveresize_window) {
998 int oldg = client->gravity;
999 int tmpg, x, y, w, h;
1000
1001 if (e->xclient.data.l[0] & 0xff)
1002 tmpg = e->xclient.data.l[0] & 0xff;
1003 else
1004 tmpg = oldg;
1005
1006 if (e->xclient.data.l[0] & 1 << 8)
1007 x = e->xclient.data.l[1];
1008 else
1009 x = client->area.x;
1010 if (e->xclient.data.l[0] & 1 << 9)
1011 y = e->xclient.data.l[2];
1012 else
1013 y = client->area.y;
1014 if (e->xclient.data.l[0] & 1 << 10)
1015 w = e->xclient.data.l[3];
1016 else
1017 w = client->area.width;
1018 if (e->xclient.data.l[0] & 1 << 11)
1019 h = e->xclient.data.l[4];
1020 else
1021 h = client->area.height;
1022 client->gravity = tmpg;
1023
1024 {
1025 int newx = x;
1026 int newy = y;
1027 int fw = w +
1028 client->frame->size.left + client->frame->size.right;
1029 int fh = h +
1030 client->frame->size.top + client->frame->size.bottom;
1031 client_find_onscreen(client, &newx, &newy, fw, fh,
1032 client_normal(client));
1033 if (e->xclient.data.l[0] & 1 << 8)
1034 x = newx;
1035 if (e->xclient.data.l[0] & 1 << 9)
1036 y = newy;
1037 }
1038
1039 client_configure(client, OB_CORNER_TOPLEFT,
1040 x, y, w, h, FALSE, TRUE);
1041
1042 client->gravity = oldg;
1043 }
1044 break;
1045 case PropertyNotify:
1046 /* validate cuz we query stuff off the client here */
1047 if (!client_validate(client)) break;
1048
1049 /* compress changes to a single property into a single change */
1050 while (XCheckTypedWindowEvent(ob_display, client->window,
1051 e->type, &ce)) {
1052 Atom a, b;
1053
1054 /* XXX: it would be nice to compress ALL changes to a property,
1055 not just changes in a row without other props between. */
1056
1057 a = ce.xproperty.atom;
1058 b = e->xproperty.atom;
1059
1060 if (a == b)
1061 continue;
1062 if ((a == prop_atoms.net_wm_name ||
1063 a == prop_atoms.wm_name ||
1064 a == prop_atoms.net_wm_icon_name ||
1065 a == prop_atoms.wm_icon_name)
1066 &&
1067 (b == prop_atoms.net_wm_name ||
1068 b == prop_atoms.wm_name ||
1069 b == prop_atoms.net_wm_icon_name ||
1070 b == prop_atoms.wm_icon_name)) {
1071 continue;
1072 }
1073 if ((a == prop_atoms.net_wm_icon ||
1074 a == prop_atoms.kwm_win_icon)
1075 &&
1076 (b == prop_atoms.net_wm_icon ||
1077 b == prop_atoms.kwm_win_icon))
1078 continue;
1079
1080 XPutBackEvent(ob_display, &ce);
1081 break;
1082 }
1083
1084 msgtype = e->xproperty.atom;
1085 if (msgtype == XA_WM_NORMAL_HINTS) {
1086 client_update_normal_hints(client);
1087 /* normal hints can make a window non-resizable */
1088 client_setup_decor_and_functions(client);
1089 } else if (msgtype == XA_WM_HINTS) {
1090 client_update_wmhints(client);
1091 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1092 client_update_transient_for(client);
1093 client_get_type(client);
1094 /* type may have changed, so update the layer */
1095 client_calc_layer(client);
1096 client_setup_decor_and_functions(client);
1097 } else if (msgtype == prop_atoms.net_wm_name ||
1098 msgtype == prop_atoms.wm_name ||
1099 msgtype == prop_atoms.net_wm_icon_name ||
1100 msgtype == prop_atoms.wm_icon_name) {
1101 client_update_title(client);
1102 } else if (msgtype == prop_atoms.wm_class) {
1103 client_update_class(client);
1104 } else if (msgtype == prop_atoms.wm_protocols) {
1105 client_update_protocols(client);
1106 client_setup_decor_and_functions(client);
1107 }
1108 else if (msgtype == prop_atoms.net_wm_strut) {
1109 client_update_strut(client);
1110 }
1111 else if (msgtype == prop_atoms.net_wm_icon ||
1112 msgtype == prop_atoms.kwm_win_icon) {
1113 client_update_icons(client);
1114 }
1115 else if (msgtype == prop_atoms.sm_client_id) {
1116 client_update_sm_client_id(client);
1117 }
1118 default:
1119 ;
1120 #ifdef SHAPE
1121 if (extensions_shape && e->type == extensions_shape_event_basep) {
1122 client->shaped = ((XShapeEvent*)e)->shaped;
1123 frame_adjust_shape(client->frame);
1124 }
1125 #endif
1126 }
1127 }
1128
1129 static void event_handle_dock(ObDock *s, XEvent *e)
1130 {
1131 switch (e->type) {
1132 case ButtonPress:
1133 stacking_raise(DOCK_AS_WINDOW(s));
1134 break;
1135 case EnterNotify:
1136 dock_hide(FALSE);
1137 break;
1138 case LeaveNotify:
1139 dock_hide(TRUE);
1140 break;
1141 }
1142 }
1143
1144 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1145 {
1146 switch (e->type) {
1147 case MotionNotify:
1148 dock_app_drag(app, &e->xmotion);
1149 break;
1150 case UnmapNotify:
1151 if (app->ignore_unmaps) {
1152 app->ignore_unmaps--;
1153 break;
1154 }
1155 dock_remove(app, TRUE);
1156 break;
1157 case DestroyNotify:
1158 dock_remove(app, FALSE);
1159 break;
1160 case ReparentNotify:
1161 dock_remove(app, FALSE);
1162 break;
1163 case ConfigureNotify:
1164 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1165 break;
1166 }
1167 }
1168
1169 ObMenuFrame* find_active_menu()
1170 {
1171 GList *it;
1172 ObMenuFrame *f;
1173
1174 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1175 f = it->data;
1176 if (f->selected)
1177 break;
1178 }
1179 return it ? it->data : NULL;
1180 }
1181
1182 static void event_handle_menu(XEvent *ev)
1183 {
1184 ObMenuFrame *f;
1185 ObMenuEntryFrame *e;
1186
1187 switch (ev->type) {
1188 case ButtonRelease:
1189 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1190 ev->xbutton.y_root)))
1191 menu_entry_frame_execute(e, ev->xbutton.state);
1192 else if (menu_can_hide)
1193 menu_frame_hide_all();
1194 break;
1195 case MotionNotify:
1196 if ((f = menu_frame_under(ev->xmotion.x_root,
1197 ev->xmotion.y_root))) {
1198 menu_frame_move_on_screen(f);
1199 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1200 ev->xmotion.y_root)))
1201 menu_frame_select(f, e);
1202 }
1203 {
1204 ObMenuFrame *a;
1205
1206 a = find_active_menu();
1207 if (a && a != f &&
1208 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1209 {
1210 menu_frame_select(a, NULL);
1211 }
1212 }
1213 break;
1214 case KeyPress:
1215 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1216 menu_frame_hide_all();
1217 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1218 ObMenuFrame *f;
1219 if ((f = find_active_menu()))
1220 menu_entry_frame_execute(f->selected, ev->xkey.state);
1221 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1222 ObMenuFrame *f;
1223 if ((f = find_active_menu()) && f->parent)
1224 menu_frame_select(f, NULL);
1225 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1226 ObMenuFrame *f;
1227 if ((f = find_active_menu()) && f->child)
1228 menu_frame_select_next(f->child);
1229 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1230 ObMenuFrame *f;
1231 if ((f = find_active_menu()))
1232 menu_frame_select_previous(f);
1233 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1234 ObMenuFrame *f;
1235 if ((f = find_active_menu()))
1236 menu_frame_select_next(f);
1237 }
1238 break;
1239 }
1240 }
1241
1242 static gboolean menu_hide_delay_func(gpointer data)
1243 {
1244 menu_can_hide = TRUE;
1245 return FALSE; /* no repeat */
1246 }
1247
1248 static gboolean focus_delay_func(gpointer data)
1249 {
1250 client_focus(focus_delay_client);
1251 return FALSE; /* no repeat */
1252 }
1253
1254 static void focus_delay_client_dest(gpointer data)
1255 {
1256 ObClient *c = data;
1257 if (c == focus_delay_client) {
1258 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1259 focus_delay_client);
1260 focus_delay_client = NULL;
1261 }
1262 }
1263
1264 void event_ignore_enter_focus(guint num)
1265 {
1266 ignore_enter_focus += num;
1267 }
This page took 0.096741 seconds and 5 git commands to generate.