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