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