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