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