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