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