]> Dogcows Code - chaz/openbox/blob - openbox/event.c
558872e85784b26e7a8fae24a62a69598c9f0ea7
[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_replace();
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 } else if (msgtype == prop_atoms.ob_control) {
600 if (e->xclient.data.l[0] == 1)
601 ob_reconfigure();
602 else if (e->xclient.data.l[0] == 2)
603 ob_restart();
604 }
605 break;
606 case PropertyNotify:
607 if (e->xproperty.atom == prop_atoms.net_desktop_names)
608 screen_update_desktop_names();
609 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
610 screen_update_layout();
611 break;
612 case ConfigureNotify:
613 #ifdef XRANDR
614 XRRUpdateConfiguration(e);
615 #endif
616 screen_resize();
617 break;
618 default:
619 ;
620 }
621 }
622
623 static void event_handle_group(ObGroup *group, XEvent *e)
624 {
625 GSList *it;
626
627 g_assert(e->type == PropertyNotify);
628
629 for (it = group->members; it; it = g_slist_next(it))
630 event_handle_client(it->data, e);
631 }
632
633 void event_enter_client(ObClient *client)
634 {
635 g_assert(config_focus_follow);
636
637 if (client_normal(client) && client_can_focus(client)) {
638 if (config_focus_delay) {
639 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
640 ob_main_loop_timeout_add(ob_main_loop,
641 config_focus_delay,
642 focus_delay_func,
643 client, NULL);
644 } else
645 focus_delay_func(client);
646 }
647 }
648
649 static void event_handle_client(ObClient *client, XEvent *e)
650 {
651 XEvent ce;
652 Atom msgtype;
653 gint i=0;
654 ObFrameContext con;
655
656 switch (e->type) {
657 case VisibilityNotify:
658 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
659 break;
660 case ButtonPress:
661 case ButtonRelease:
662 /* Wheel buttons don't draw because they are an instant click, so it
663 is a waste of resources to go drawing it. */
664 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
665 con = frame_context(client, e->xbutton.window);
666 con = mouse_button_frame_context(con, e->xbutton.button);
667 switch (con) {
668 case OB_FRAME_CONTEXT_MAXIMIZE:
669 client->frame->max_press = (e->type == ButtonPress);
670 framerender_frame(client->frame);
671 break;
672 case OB_FRAME_CONTEXT_CLOSE:
673 client->frame->close_press = (e->type == ButtonPress);
674 framerender_frame(client->frame);
675 break;
676 case OB_FRAME_CONTEXT_ICONIFY:
677 client->frame->iconify_press = (e->type == ButtonPress);
678 framerender_frame(client->frame);
679 break;
680 case OB_FRAME_CONTEXT_ALLDESKTOPS:
681 client->frame->desk_press = (e->type == ButtonPress);
682 framerender_frame(client->frame);
683 break;
684 case OB_FRAME_CONTEXT_SHADE:
685 client->frame->shade_press = (e->type == ButtonPress);
686 framerender_frame(client->frame);
687 break;
688 default:
689 /* nothing changes with clicks for any other contexts */
690 break;
691 }
692 }
693 break;
694 case FocusIn:
695 #ifdef DEBUG_FOCUS
696 ob_debug("FocusIn 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 if (client != focus_client) {
701 focus_set_client(client);
702 frame_adjust_focus(client->frame, TRUE);
703 client_calc_layer(client);
704 }
705 break;
706 case FocusOut:
707 #ifdef DEBUG_FOCUS
708 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
709 e->xfocus.window, client->window,
710 e->xfocus.mode, e->xfocus.detail);
711 #endif
712 focus_hilite = NULL;
713 frame_adjust_focus(client->frame, FALSE);
714 client_calc_layer(client);
715 break;
716 case LeaveNotify:
717 con = frame_context(client, e->xcrossing.window);
718 switch (con) {
719 case OB_FRAME_CONTEXT_MAXIMIZE:
720 client->frame->max_hover = FALSE;
721 frame_adjust_state(client->frame);
722 break;
723 case OB_FRAME_CONTEXT_ALLDESKTOPS:
724 client->frame->desk_hover = FALSE;
725 frame_adjust_state(client->frame);
726 break;
727 case OB_FRAME_CONTEXT_SHADE:
728 client->frame->shade_hover = FALSE;
729 frame_adjust_state(client->frame);
730 break;
731 case OB_FRAME_CONTEXT_ICONIFY:
732 client->frame->iconify_hover = FALSE;
733 frame_adjust_state(client->frame);
734 break;
735 case OB_FRAME_CONTEXT_CLOSE:
736 client->frame->close_hover = FALSE;
737 frame_adjust_state(client->frame);
738 break;
739 case OB_FRAME_CONTEXT_FRAME:
740 if (config_focus_follow && config_focus_delay)
741 ob_main_loop_timeout_remove_data(ob_main_loop,
742 focus_delay_func,
743 client, TRUE);
744 break;
745 default:
746 break;
747 }
748 break;
749 case EnterNotify:
750 {
751 gboolean nofocus = FALSE;
752
753 if (ignore_enter_focus) {
754 ignore_enter_focus--;
755 nofocus = TRUE;
756 }
757
758 con = frame_context(client, e->xcrossing.window);
759 switch (con) {
760 case OB_FRAME_CONTEXT_MAXIMIZE:
761 client->frame->max_hover = TRUE;
762 frame_adjust_state(client->frame);
763 break;
764 case OB_FRAME_CONTEXT_ALLDESKTOPS:
765 client->frame->desk_hover = TRUE;
766 frame_adjust_state(client->frame);
767 break;
768 case OB_FRAME_CONTEXT_SHADE:
769 client->frame->shade_hover = TRUE;
770 frame_adjust_state(client->frame);
771 break;
772 case OB_FRAME_CONTEXT_ICONIFY:
773 client->frame->iconify_hover = TRUE;
774 frame_adjust_state(client->frame);
775 break;
776 case OB_FRAME_CONTEXT_CLOSE:
777 client->frame->close_hover = TRUE;
778 frame_adjust_state(client->frame);
779 break;
780 case OB_FRAME_CONTEXT_FRAME:
781 if (e->xcrossing.mode == NotifyGrab ||
782 e->xcrossing.mode == NotifyUngrab)
783 {
784 #ifdef DEBUG_FOCUS
785 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
786 (e->type == EnterNotify ? "Enter" : "Leave"),
787 e->xcrossing.mode,
788 e->xcrossing.detail, client?client->window:0);
789 #endif
790 } else {
791 #ifdef DEBUG_FOCUS
792 ob_debug("%sNotify mode %d detail %d on %lx, "
793 "focusing window: %d\n",
794 (e->type == EnterNotify ? "Enter" : "Leave"),
795 e->xcrossing.mode,
796 e->xcrossing.detail, (client?client->window:0),
797 !nofocus);
798 #endif
799 if (!nofocus && config_focus_follow)
800 event_enter_client(client);
801 }
802 break;
803 default:
804 break;
805 }
806 break;
807 }
808 case ConfigureRequest:
809 /* compress these */
810 while (XCheckTypedWindowEvent(ob_display, client->window,
811 ConfigureRequest, &ce)) {
812 ++i;
813 /* XXX if this causes bad things.. we can compress config req's
814 with the same mask. */
815 e->xconfigurerequest.value_mask |=
816 ce.xconfigurerequest.value_mask;
817 if (ce.xconfigurerequest.value_mask & CWX)
818 e->xconfigurerequest.x = ce.xconfigurerequest.x;
819 if (ce.xconfigurerequest.value_mask & CWY)
820 e->xconfigurerequest.y = ce.xconfigurerequest.y;
821 if (ce.xconfigurerequest.value_mask & CWWidth)
822 e->xconfigurerequest.width = ce.xconfigurerequest.width;
823 if (ce.xconfigurerequest.value_mask & CWHeight)
824 e->xconfigurerequest.height = ce.xconfigurerequest.height;
825 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
826 e->xconfigurerequest.border_width =
827 ce.xconfigurerequest.border_width;
828 if (ce.xconfigurerequest.value_mask & CWStackMode)
829 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
830 }
831
832 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
833 if (client->iconic || client->shaded) return;
834
835 /* resize, then move, as specified in the EWMH section 7.7 */
836 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
837 CWX | CWY |
838 CWBorderWidth)) {
839 gint x, y, w, h;
840 ObCorner corner;
841
842 if (e->xconfigurerequest.value_mask & CWBorderWidth)
843 client->border_width = e->xconfigurerequest.border_width;
844
845 x = (e->xconfigurerequest.value_mask & CWX) ?
846 e->xconfigurerequest.x : client->area.x;
847 y = (e->xconfigurerequest.value_mask & CWY) ?
848 e->xconfigurerequest.y : client->area.y;
849 w = (e->xconfigurerequest.value_mask & CWWidth) ?
850 e->xconfigurerequest.width : client->area.width;
851 h = (e->xconfigurerequest.value_mask & CWHeight) ?
852 e->xconfigurerequest.height : client->area.height;
853
854 {
855 gint newx = x;
856 gint newy = y;
857 gint fw = w +
858 client->frame->size.left + client->frame->size.right;
859 gint fh = h +
860 client->frame->size.top + client->frame->size.bottom;
861 /* make this rude for size-only changes but not for position
862 changes.. */
863 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
864 (e->xconfigurerequest.value_mask & CWY));
865
866 client_find_onscreen(client, &newx, &newy, fw, fh,
867 !moving);
868 if (e->xconfigurerequest.value_mask & CWX)
869 x = newx;
870 if (e->xconfigurerequest.value_mask & CWY)
871 y = newy;
872 }
873
874 switch (client->gravity) {
875 case NorthEastGravity:
876 case EastGravity:
877 corner = OB_CORNER_TOPRIGHT;
878 break;
879 case SouthWestGravity:
880 case SouthGravity:
881 corner = OB_CORNER_BOTTOMLEFT;
882 break;
883 case SouthEastGravity:
884 corner = OB_CORNER_BOTTOMRIGHT;
885 break;
886 default: /* NorthWest, Static, etc */
887 corner = OB_CORNER_TOPLEFT;
888 }
889
890 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
891 TRUE);
892 }
893
894 if (e->xconfigurerequest.value_mask & CWStackMode) {
895 switch (e->xconfigurerequest.detail) {
896 case Below:
897 case BottomIf:
898 client_lower(client);
899 break;
900
901 case Above:
902 case TopIf:
903 default:
904 client_raise(client);
905 break;
906 }
907 }
908 break;
909 case UnmapNotify:
910 if (client->ignore_unmaps) {
911 client->ignore_unmaps--;
912 break;
913 }
914 client_unmanage(client);
915 break;
916 case DestroyNotify:
917 client_unmanage(client);
918 break;
919 case ReparentNotify:
920 /* this is when the client is first taken captive in the frame */
921 if (e->xreparent.parent == client->frame->plate) break;
922
923 /*
924 This event is quite rare and is usually handled in unmapHandler.
925 However, if the window is unmapped when the reparent event occurs,
926 the window manager never sees it because an unmap event is not sent
927 to an already unmapped window.
928 */
929
930 /* we don't want the reparent event, put it back on the stack for the
931 X server to deal with after we unmanage the window */
932 XPutBackEvent(ob_display, e);
933
934 client_unmanage(client);
935 break;
936 case MapRequest:
937 ob_debug("MapRequest for 0x%lx\n", client->window);
938 if (!client->iconic) break; /* this normally doesn't happen, but if it
939 does, we don't want it!
940 it can happen now when the window is on
941 another desktop, but we still don't
942 want it! */
943 client_activate(client, FALSE, TRUE);
944 break;
945 case ClientMessage:
946 /* validate cuz we query stuff off the client here */
947 if (!client_validate(client)) break;
948
949 if (e->xclient.format != 32) return;
950
951 msgtype = e->xclient.message_type;
952 if (msgtype == prop_atoms.wm_change_state) {
953 /* compress changes into a single change */
954 while (XCheckTypedWindowEvent(ob_display, client->window,
955 e->type, &ce)) {
956 /* XXX: it would be nice to compress ALL messages of a
957 type, not just messages in a row without other
958 message types between. */
959 if (ce.xclient.message_type != msgtype) {
960 XPutBackEvent(ob_display, &ce);
961 break;
962 }
963 e->xclient = ce.xclient;
964 }
965 client_set_wm_state(client, e->xclient.data.l[0]);
966 } else if (msgtype == prop_atoms.net_wm_desktop) {
967 /* compress changes into a single change */
968 while (XCheckTypedWindowEvent(ob_display, client->window,
969 e->type, &ce)) {
970 /* XXX: it would be nice to compress ALL messages of a
971 type, not just messages in a row without other
972 message types between. */
973 if (ce.xclient.message_type != msgtype) {
974 XPutBackEvent(ob_display, &ce);
975 break;
976 }
977 e->xclient = ce.xclient;
978 }
979 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
980 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
981 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
982 FALSE);
983 } else if (msgtype == prop_atoms.net_wm_state) {
984 /* can't compress these */
985 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
986 (e->xclient.data.l[0] == 0 ? "Remove" :
987 e->xclient.data.l[0] == 1 ? "Add" :
988 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
989 e->xclient.data.l[1], e->xclient.data.l[2],
990 client->window);
991 client_set_state(client, e->xclient.data.l[0],
992 e->xclient.data.l[1], e->xclient.data.l[2]);
993 } else if (msgtype == prop_atoms.net_close_window) {
994 ob_debug("net_close_window for 0x%lx\n", client->window);
995 client_close(client);
996 } else if (msgtype == prop_atoms.net_active_window) {
997 ob_debug("net_active_window for 0x%lx source=%s\n",
998 client->window,
999 (e->xclient.data.l[0] == 0 ? "unknown" :
1000 (e->xclient.data.l[0] == 1 ? "application" :
1001 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1002 /* XXX make use of data.l[1] and [2] ! */
1003 client_activate(client, FALSE,
1004 (e->xclient.data.l[0] == 0 ||
1005 e->xclient.data.l[0] == 2));
1006 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1007 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
1008 if ((Atom)e->xclient.data.l[2] ==
1009 prop_atoms.net_wm_moveresize_size_topleft ||
1010 (Atom)e->xclient.data.l[2] ==
1011 prop_atoms.net_wm_moveresize_size_top ||
1012 (Atom)e->xclient.data.l[2] ==
1013 prop_atoms.net_wm_moveresize_size_topright ||
1014 (Atom)e->xclient.data.l[2] ==
1015 prop_atoms.net_wm_moveresize_size_right ||
1016 (Atom)e->xclient.data.l[2] ==
1017 prop_atoms.net_wm_moveresize_size_right ||
1018 (Atom)e->xclient.data.l[2] ==
1019 prop_atoms.net_wm_moveresize_size_bottomright ||
1020 (Atom)e->xclient.data.l[2] ==
1021 prop_atoms.net_wm_moveresize_size_bottom ||
1022 (Atom)e->xclient.data.l[2] ==
1023 prop_atoms.net_wm_moveresize_size_bottomleft ||
1024 (Atom)e->xclient.data.l[2] ==
1025 prop_atoms.net_wm_moveresize_size_left ||
1026 (Atom)e->xclient.data.l[2] ==
1027 prop_atoms.net_wm_moveresize_move ||
1028 (Atom)e->xclient.data.l[2] ==
1029 prop_atoms.net_wm_moveresize_size_keyboard ||
1030 (Atom)e->xclient.data.l[2] ==
1031 prop_atoms.net_wm_moveresize_move_keyboard) {
1032
1033 moveresize_start(client, e->xclient.data.l[0],
1034 e->xclient.data.l[1], e->xclient.data.l[3],
1035 e->xclient.data.l[2]);
1036 }
1037 } else if (msgtype == prop_atoms.net_moveresize_window) {
1038 gint oldg = client->gravity;
1039 gint tmpg, x, y, w, h;
1040
1041 if (e->xclient.data.l[0] & 0xff)
1042 tmpg = e->xclient.data.l[0] & 0xff;
1043 else
1044 tmpg = oldg;
1045
1046 if (e->xclient.data.l[0] & 1 << 8)
1047 x = e->xclient.data.l[1];
1048 else
1049 x = client->area.x;
1050 if (e->xclient.data.l[0] & 1 << 9)
1051 y = e->xclient.data.l[2];
1052 else
1053 y = client->area.y;
1054 if (e->xclient.data.l[0] & 1 << 10)
1055 w = e->xclient.data.l[3];
1056 else
1057 w = client->area.width;
1058 if (e->xclient.data.l[0] & 1 << 11)
1059 h = e->xclient.data.l[4];
1060 else
1061 h = client->area.height;
1062 client->gravity = tmpg;
1063
1064 {
1065 gint newx = x;
1066 gint newy = y;
1067 gint fw = w +
1068 client->frame->size.left + client->frame->size.right;
1069 gint fh = h +
1070 client->frame->size.top + client->frame->size.bottom;
1071 client_find_onscreen(client, &newx, &newy, fw, fh,
1072 client_normal(client));
1073 if (e->xclient.data.l[0] & 1 << 8)
1074 x = newx;
1075 if (e->xclient.data.l[0] & 1 << 9)
1076 y = newy;
1077 }
1078
1079 client_configure(client, OB_CORNER_TOPLEFT,
1080 x, y, w, h, FALSE, TRUE);
1081
1082 client->gravity = oldg;
1083 }
1084 break;
1085 case PropertyNotify:
1086 /* validate cuz we query stuff off the client here */
1087 if (!client_validate(client)) break;
1088
1089 /* compress changes to a single property into a single change */
1090 while (XCheckTypedWindowEvent(ob_display, client->window,
1091 e->type, &ce)) {
1092 Atom a, b;
1093
1094 /* XXX: it would be nice to compress ALL changes to a property,
1095 not just changes in a row without other props between. */
1096
1097 a = ce.xproperty.atom;
1098 b = e->xproperty.atom;
1099
1100 if (a == b)
1101 continue;
1102 if ((a == prop_atoms.net_wm_name ||
1103 a == prop_atoms.wm_name ||
1104 a == prop_atoms.net_wm_icon_name ||
1105 a == prop_atoms.wm_icon_name)
1106 &&
1107 (b == prop_atoms.net_wm_name ||
1108 b == prop_atoms.wm_name ||
1109 b == prop_atoms.net_wm_icon_name ||
1110 b == prop_atoms.wm_icon_name)) {
1111 continue;
1112 }
1113 if (a == prop_atoms.net_wm_icon &&
1114 b == prop_atoms.net_wm_icon)
1115 continue;
1116
1117 XPutBackEvent(ob_display, &ce);
1118 break;
1119 }
1120
1121 msgtype = e->xproperty.atom;
1122 if (msgtype == XA_WM_NORMAL_HINTS) {
1123 client_update_normal_hints(client);
1124 /* normal hints can make a window non-resizable */
1125 client_setup_decor_and_functions(client);
1126 } else if (msgtype == XA_WM_HINTS) {
1127 client_update_wmhints(client);
1128 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1129 client_update_transient_for(client);
1130 client_get_type(client);
1131 /* type may have changed, so update the layer */
1132 client_calc_layer(client);
1133 client_setup_decor_and_functions(client);
1134 } else if (msgtype == prop_atoms.net_wm_name ||
1135 msgtype == prop_atoms.wm_name ||
1136 msgtype == prop_atoms.net_wm_icon_name ||
1137 msgtype == prop_atoms.wm_icon_name) {
1138 client_update_title(client);
1139 } else if (msgtype == prop_atoms.wm_class) {
1140 client_update_class(client);
1141 } else if (msgtype == prop_atoms.wm_protocols) {
1142 client_update_protocols(client);
1143 client_setup_decor_and_functions(client);
1144 }
1145 else if (msgtype == prop_atoms.net_wm_strut) {
1146 client_update_strut(client);
1147 }
1148 else if (msgtype == prop_atoms.net_wm_icon) {
1149 client_update_icons(client);
1150 }
1151 else if (msgtype == prop_atoms.sm_client_id) {
1152 client_update_sm_client_id(client);
1153 }
1154 default:
1155 ;
1156 #ifdef SHAPE
1157 if (extensions_shape && e->type == extensions_shape_event_basep) {
1158 client->shaped = ((XShapeEvent*)e)->shaped;
1159 frame_adjust_shape(client->frame);
1160 }
1161 #endif
1162 }
1163 }
1164
1165 static void event_handle_dock(ObDock *s, XEvent *e)
1166 {
1167 switch (e->type) {
1168 case ButtonPress:
1169 if (e->xbutton.button == 1)
1170 stacking_raise(DOCK_AS_WINDOW(s), FALSE);
1171 else if (e->xbutton.button == 2)
1172 stacking_lower(DOCK_AS_WINDOW(s), FALSE);
1173 break;
1174 case EnterNotify:
1175 dock_hide(FALSE);
1176 break;
1177 case LeaveNotify:
1178 dock_hide(TRUE);
1179 break;
1180 }
1181 }
1182
1183 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1184 {
1185 switch (e->type) {
1186 case MotionNotify:
1187 dock_app_drag(app, &e->xmotion);
1188 break;
1189 case UnmapNotify:
1190 if (app->ignore_unmaps) {
1191 app->ignore_unmaps--;
1192 break;
1193 }
1194 dock_remove(app, TRUE);
1195 break;
1196 case DestroyNotify:
1197 dock_remove(app, FALSE);
1198 break;
1199 case ReparentNotify:
1200 dock_remove(app, FALSE);
1201 break;
1202 case ConfigureNotify:
1203 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1204 break;
1205 }
1206 }
1207
1208 ObMenuFrame* find_active_menu()
1209 {
1210 GList *it;
1211 ObMenuFrame *ret = NULL;
1212
1213 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1214 ret = it->data;
1215 if (ret->selected)
1216 break;
1217 ret = NULL;
1218 }
1219 return ret;
1220 }
1221
1222 ObMenuFrame* find_active_or_last_menu()
1223 {
1224 ObMenuFrame *ret = NULL;
1225
1226 ret = find_active_menu();
1227 if (!ret && menu_frame_visible)
1228 ret = menu_frame_visible->data;
1229 return ret;
1230 }
1231
1232 static void event_handle_menu(XEvent *ev)
1233 {
1234 ObMenuFrame *f;
1235 ObMenuEntryFrame *e;
1236
1237 switch (ev->type) {
1238 case ButtonRelease:
1239 if (menu_can_hide) {
1240 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1241 ev->xbutton.y_root)))
1242 menu_entry_frame_execute(e, ev->xbutton.state);
1243 else
1244 menu_frame_hide_all();
1245 }
1246 break;
1247 case MotionNotify:
1248 if ((f = menu_frame_under(ev->xmotion.x_root,
1249 ev->xmotion.y_root))) {
1250 menu_frame_move_on_screen(f);
1251 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1252 ev->xmotion.y_root)))
1253 menu_frame_select(f, e);
1254 }
1255 {
1256 ObMenuFrame *a;
1257
1258 a = find_active_menu();
1259 if (a && a != f &&
1260 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1261 {
1262 menu_frame_select(a, NULL);
1263 }
1264 }
1265 break;
1266 case KeyPress:
1267 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1268 menu_frame_hide_all();
1269 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1270 ObMenuFrame *f;
1271 if ((f = find_active_menu()))
1272 menu_entry_frame_execute(f->selected, ev->xkey.state);
1273 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1274 ObMenuFrame *f;
1275 if ((f = find_active_or_last_menu()) && f->parent)
1276 menu_frame_select(f, NULL);
1277 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1278 ObMenuFrame *f;
1279 if ((f = find_active_or_last_menu()) && f->child)
1280 menu_frame_select_next(f->child);
1281 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1282 ObMenuFrame *f;
1283 if ((f = find_active_or_last_menu()))
1284 menu_frame_select_previous(f);
1285 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1286 ObMenuFrame *f;
1287 if ((f = find_active_or_last_menu()))
1288 menu_frame_select_next(f);
1289 }
1290 break;
1291 }
1292 }
1293
1294 static gboolean menu_hide_delay_func(gpointer data)
1295 {
1296 menu_can_hide = TRUE;
1297 return FALSE; /* no repeat */
1298 }
1299
1300 static gboolean focus_delay_func(gpointer data)
1301 {
1302 ObClient *c = data;
1303
1304 if (focus_client != c) {
1305 client_focus(c);
1306 if (config_focus_raise)
1307 client_raise(c);
1308 }
1309 return FALSE; /* no repeat */
1310 }
1311
1312 static void focus_delay_client_dest(ObClient *client, gpointer data)
1313 {
1314 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1315 client, TRUE);
1316 }
1317
1318 static void event_client_dest(ObClient *client, gpointer data)
1319 {
1320 if (client == focus_hilite)
1321 focus_hilite = NULL;
1322 }
1323
1324 void event_halt_focus_delay()
1325 {
1326 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1327 }
1328
1329 void event_ignore_queued_enters()
1330 {
1331 GSList *saved = NULL, *it;
1332 XEvent *e;
1333
1334 XSync(ob_display, FALSE);
1335
1336 /* count the events */
1337 while (TRUE) {
1338 e = g_new(XEvent, 1);
1339 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1340 ObWindow *win;
1341
1342 win = g_hash_table_lookup(window_map, &e->xany.window);
1343 if (win && WINDOW_IS_CLIENT(win))
1344 ++ignore_enter_focus;
1345
1346 saved = g_slist_append(saved, e);
1347 } else {
1348 g_free(e);
1349 break;
1350 }
1351 }
1352 /* put the events back */
1353 for (it = saved; it; it = g_slist_next(it)) {
1354 XPutBackEvent(ob_display, it->data);
1355 g_free(it->data);
1356 }
1357 g_slist_free(saved);
1358 }
This page took 0.088834 seconds and 3 git commands to generate.