1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
32 #include "menuframe.h"
36 #include "framerender.h"
38 #include "moveresize.h"
41 #include "extensions.h"
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
55 # include <X11/XKBlib.h>
59 #include <X11/ICE/ICElib.h>
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
);
76 static gboolean
focus_delay_func(gpointer data
);
77 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
79 static gboolean
menu_hide_delay_func(gpointer data
);
81 /* The time for the current event being processed */
82 Time event_curtime
= CurrentTime
;
84 /*! The value of the mask for the NumLock modifier */
86 /*! The value of the mask for the ScrollLock modifier */
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
95 static gint mask_table_size
;
97 static guint ignore_enter_focus
= 0;
99 static gboolean menu_can_hide
;
102 static void ice_handler(gint fd
, gpointer conn
)
105 IceProcessMessages(conn
, NULL
, &b
);
108 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
109 IcePointer
*watch_data
)
114 fd
= IceConnectionNumber(conn
);
115 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
117 ob_main_loop_fd_remove(ob_main_loop
, fd
);
123 void event_startup(gboolean reconfig
)
125 if (reconfig
) return;
127 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
129 /* get lock masks that are defined by the display (not constant) */
130 modmap
= XGetModifierMapping(ob_display
);
132 if (modmap
&& modmap
->max_keypermod
> 0) {
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
,
142 for (cnt
= 0; cnt
< size
; ++cnt
) {
143 if (! modmap
->modifiermap
[cnt
]) continue;
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
];
152 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
155 IceAddConnectionWatch(ice_watch
, NULL
);
158 client_add_destructor(focus_delay_client_dest
, NULL
);
159 client_add_destructor(event_client_dest
, NULL
);
162 void event_shutdown(gboolean reconfig
)
164 if (reconfig
) return;
167 IceRemoveConnectionWatch(ice_watch
, NULL
);
170 client_remove_destructor(focus_delay_client_dest
);
171 client_remove_destructor(event_client_dest
);
172 XFreeModifiermap(modmap
);
175 static Window
event_get_window(XEvent
*e
)
182 window
= RootWindow(ob_display
, ob_screen
);
185 window
= e
->xmap
.window
;
188 window
= e
->xunmap
.window
;
191 window
= e
->xdestroywindow
.window
;
193 case ConfigureRequest
:
194 window
= e
->xconfigurerequest
.window
;
196 case ConfigureNotify
:
197 window
= e
->xconfigure
.window
;
201 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
202 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
204 window
= ((XkbBellNotifyEvent
*)e
)->window
;
210 window
= e
->xany
.window
;
215 static void event_set_curtime(XEvent
*e
)
217 Time t
= CurrentTime
;
219 /* grab the lasttime and hack up the state */
235 t
= e
->xproperty
.time
;
239 t
= e
->xcrossing
.time
;
242 /* if more event types are anticipated, get their timestamp
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) \
256 static void event_hack_mods(XEvent *e)
259 XkbStateRec xkb_state
;
267 STRIP_MODS(e
->xbutton
.state
);
270 STRIP_MODS(e
->xkey
.state
);
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..) */
277 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
278 e
->xkey
.state
= xkb_state
.compat_state
;
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; */
290 break; /* get outta here! */
297 STRIP_MODS(e
->xmotion
.state
);
298 /* compress events */
301 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
303 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
304 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
311 static gboolean
wanted_focusevent(XEvent
*e
)
313 gint mode
= e
->xfocus
.mode
;
314 gint detail
= e
->xfocus
.detail
;
316 if (e
->type
== FocusIn
) {
318 /* These are ones we never want.. */
320 /* This means focus was given by a keyboard/mouse grab. */
321 if (mode
== NotifyGrab
)
323 /* This means focus was given back from a keyboard/mouse grab. */
324 if (mode
== NotifyUngrab
)
327 /* These are the ones we want.. */
329 /* This means focus moved from the root window to a client */
330 if (detail
== NotifyVirtual
)
332 /* This means focus moved from one client to another */
333 if (detail
== NotifyNonlinearVirtual
)
339 g_assert(e
->type
== FocusOut
);
342 /* These are ones we never want.. */
344 /* This means focus was taken by a keyboard/mouse grab. */
345 if (mode
== NotifyGrab
)
348 /* These are the ones we want.. */
350 /* This means focus moved from a client to the root window */
351 if (detail
== NotifyVirtual
)
353 /* This means focus moved from one client to another */
354 if (detail
== NotifyNonlinearVirtual
)
362 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
364 return e
->type
== FocusIn
&& wanted_focusevent(e
);
367 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
372 if (e
->xcrossing
.detail
== NotifyInferior
)
377 /* I don't think this should ever happen with our event masks, but
378 if it does, we don't want it. */
381 if (!wanted_focusevent(e
))
388 static void event_process(const XEvent
*ec
, gpointer data
)
391 ObGroup
*group
= NULL
;
392 ObClient
*client
= NULL
;
394 ObDockApp
*dockapp
= NULL
;
395 ObWindow
*obwin
= NULL
;
397 ObEventData
*ed
= data
;
399 /* make a copy we can mangle */
403 window
= event_get_window(e
);
404 if (!(e
->type
== PropertyNotify
&&
405 (group
= g_hash_table_lookup(group_map
, &window
))))
406 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
407 switch (obwin
->type
) {
409 dock
= WINDOW_AS_DOCK(obwin
);
412 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
415 client
= WINDOW_AS_CLIENT(obwin
);
418 case Window_Internal
:
419 /* not to be used for events */
420 g_assert_not_reached();
425 #if 0 /* focus debugging stuff */
426 if (e
->type
== FocusIn
|| e
->type
== FocusOut
) {
427 gint mode
= e
->xfocus
.mode
;
428 gint detail
= e
->xfocus
.detail
;
429 Window window
= e
->xfocus
.window
;
430 if (detail
== NotifyVirtual
) {
431 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
432 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
435 else if (detail
== NotifyNonlinearVirtual
) {
436 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
437 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
441 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
442 (e
->type
== FocusIn
? "IN" : "OUT"),
443 detail
, mode
, window
);
447 event_set_curtime(e
);
449 if (event_ignore(e
, client
)) {
456 /* deal with it in the kernel */
458 event_handle_group(group
, e
);
460 event_handle_client(client
, e
);
462 event_handle_dockapp(dockapp
, e
);
464 event_handle_dock(dock
, e
);
465 else if (window
== RootWindow(ob_display
, ob_screen
))
466 event_handle_root(e
);
467 else if (e
->type
== MapRequest
)
468 client_manage(window
);
469 else if (e
->type
== ConfigureRequest
) {
470 /* unhandled configure requests must be used to configure the
474 xwc
.x
= e
->xconfigurerequest
.x
;
475 xwc
.y
= e
->xconfigurerequest
.y
;
476 xwc
.width
= e
->xconfigurerequest
.width
;
477 xwc
.height
= e
->xconfigurerequest
.height
;
478 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
479 xwc
.sibling
= e
->xconfigurerequest
.above
;
480 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
482 /* we are not to be held responsible if someone sends us an
484 xerror_set_ignore(TRUE
);
485 XConfigureWindow(ob_display
, window
,
486 e
->xconfigurerequest
.value_mask
, &xwc
);
487 xerror_set_ignore(FALSE
);
490 /* user input (action-bound) events */
491 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
492 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
493 e
->type
== KeyRelease
)
495 if (menu_frame_visible
)
496 event_handle_menu(e
);
498 if (!keyboard_process_interactive_grab(e
, &client
)) {
499 if (moveresize_in_progress
) {
502 /* make further actions work on the client being
504 client
= moveresize_client
;
507 menu_can_hide
= FALSE
;
508 ob_main_loop_timeout_add(ob_main_loop
,
509 config_menu_hide_delay
* 1000,
510 menu_hide_delay_func
,
513 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
514 e
->type
== MotionNotify
)
515 mouse_event(client
, e
);
516 else if (e
->type
== KeyPress
) {
517 keyboard_event((focus_cycle_target
? focus_cycle_target
:
518 (focus_hilite
? focus_hilite
: client
)),
524 /* if something happens and it's not from an XEvent, then we don't know
526 event_curtime
= CurrentTime
;
529 static void event_handle_root(XEvent
*e
)
535 ob_debug("Another WM has requested to replace us. Exiting.\n");
540 if (e
->xclient
.format
!= 32) break;
542 msgtype
= e
->xclient
.message_type
;
543 if (msgtype
== prop_atoms
.net_current_desktop
) {
544 guint d
= e
->xclient
.data
.l
[0];
545 if (d
< screen_num_desktops
)
546 screen_set_desktop(d
);
547 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
548 guint d
= e
->xclient
.data
.l
[0];
550 screen_set_num_desktops(d
);
551 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
552 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
553 } else if (msgtype
== prop_atoms
.ob_control
) {
554 if (e
->xclient
.data
.l
[0] == 1)
556 else if (e
->xclient
.data
.l
[0] == 2)
561 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
562 screen_update_desktop_names();
563 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
564 screen_update_layout();
566 case ConfigureNotify
:
568 XRRUpdateConfiguration(e
);
577 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
581 g_assert(e
->type
== PropertyNotify
);
583 for (it
= group
->members
; it
; it
= g_slist_next(it
))
584 event_handle_client(it
->data
, e
);
587 void event_enter_client(ObClient
*client
)
589 g_assert(config_focus_follow
);
591 if (client_normal(client
) && client_can_focus(client
)) {
592 if (config_focus_delay
) {
593 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
594 ob_main_loop_timeout_add(ob_main_loop
,
599 focus_delay_func(client
);
603 static void event_handle_client(ObClient
*client
, XEvent
*e
)
611 case VisibilityNotify
:
612 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
616 /* Wheel buttons don't draw because they are an instant click, so it
617 is a waste of resources to go drawing it. */
618 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
619 con
= frame_context(client
, e
->xbutton
.window
);
620 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
622 case OB_FRAME_CONTEXT_MAXIMIZE
:
623 client
->frame
->max_press
= (e
->type
== ButtonPress
);
624 framerender_frame(client
->frame
);
626 case OB_FRAME_CONTEXT_CLOSE
:
627 client
->frame
->close_press
= (e
->type
== ButtonPress
);
628 framerender_frame(client
->frame
);
630 case OB_FRAME_CONTEXT_ICONIFY
:
631 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
632 framerender_frame(client
->frame
);
634 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
635 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
636 framerender_frame(client
->frame
);
638 case OB_FRAME_CONTEXT_SHADE
:
639 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
640 framerender_frame(client
->frame
);
643 /* nothing changes with clicks for any other contexts */
649 if (client
!= focus_client
) {
650 focus_set_client(client
);
651 frame_adjust_focus(client
->frame
, TRUE
);
652 client_calc_layer(client
);
656 /* Look for the followup FocusIn */
657 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
658 /* There is no FocusIn, move focus where we can still hear events*/
659 focus_set_client(NULL
);
660 } else if (ce
.xany
.window
== e
->xany
.window
) {
661 /* If focus didn't actually move anywhere, there is nothing to do*/
664 /* Focus did move, so process the FocusIn event */
666 event_process(&ce
, &ed
);
668 /* The FocusIn was ignored, this means it was on a window
669 that isn't a client. */
670 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
674 /* This client is no longer focused, so show that */
676 frame_adjust_focus(client
->frame
, FALSE
);
677 client_calc_layer(client
);
680 con
= frame_context(client
, e
->xcrossing
.window
);
682 case OB_FRAME_CONTEXT_MAXIMIZE
:
683 client
->frame
->max_hover
= FALSE
;
684 frame_adjust_state(client
->frame
);
686 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
687 client
->frame
->desk_hover
= FALSE
;
688 frame_adjust_state(client
->frame
);
690 case OB_FRAME_CONTEXT_SHADE
:
691 client
->frame
->shade_hover
= FALSE
;
692 frame_adjust_state(client
->frame
);
694 case OB_FRAME_CONTEXT_ICONIFY
:
695 client
->frame
->iconify_hover
= FALSE
;
696 frame_adjust_state(client
->frame
);
698 case OB_FRAME_CONTEXT_CLOSE
:
699 client
->frame
->close_hover
= FALSE
;
700 frame_adjust_state(client
->frame
);
702 case OB_FRAME_CONTEXT_FRAME
:
703 if (config_focus_follow
&& config_focus_delay
)
704 ob_main_loop_timeout_remove_data(ob_main_loop
,
714 gboolean nofocus
= FALSE
;
716 if (ignore_enter_focus
) {
717 ignore_enter_focus
--;
721 con
= frame_context(client
, e
->xcrossing
.window
);
723 case OB_FRAME_CONTEXT_MAXIMIZE
:
724 client
->frame
->max_hover
= TRUE
;
725 frame_adjust_state(client
->frame
);
727 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
728 client
->frame
->desk_hover
= TRUE
;
729 frame_adjust_state(client
->frame
);
731 case OB_FRAME_CONTEXT_SHADE
:
732 client
->frame
->shade_hover
= TRUE
;
733 frame_adjust_state(client
->frame
);
735 case OB_FRAME_CONTEXT_ICONIFY
:
736 client
->frame
->iconify_hover
= TRUE
;
737 frame_adjust_state(client
->frame
);
739 case OB_FRAME_CONTEXT_CLOSE
:
740 client
->frame
->close_hover
= TRUE
;
741 frame_adjust_state(client
->frame
);
743 case OB_FRAME_CONTEXT_FRAME
:
744 if (e
->xcrossing
.mode
== NotifyGrab
||
745 e
->xcrossing
.mode
== NotifyUngrab
)
748 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
749 (e
->type
== EnterNotify
? "Enter" : "Leave"),
751 e
->xcrossing
.detail
, client
?client
->window
:0);
755 ob_debug("%sNotify mode %d detail %d on %lx, "
756 "focusing window: %d\n",
757 (e
->type
== EnterNotify
? "Enter" : "Leave"),
759 e
->xcrossing
.detail
, (client
?client
->window
:0),
762 if (!nofocus
&& config_focus_follow
)
763 event_enter_client(client
);
771 case ConfigureRequest
:
773 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
774 ConfigureRequest
, &ce
)) {
776 /* XXX if this causes bad things.. we can compress config req's
777 with the same mask. */
778 e
->xconfigurerequest
.value_mask
|=
779 ce
.xconfigurerequest
.value_mask
;
780 if (ce
.xconfigurerequest
.value_mask
& CWX
)
781 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
782 if (ce
.xconfigurerequest
.value_mask
& CWY
)
783 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
784 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
785 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
786 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
787 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
788 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
789 e
->xconfigurerequest
.border_width
=
790 ce
.xconfigurerequest
.border_width
;
791 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
792 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
795 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
796 if (client
->iconic
|| client
->shaded
) return;
798 /* resize, then move, as specified in the EWMH section 7.7 */
799 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
805 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
806 client
->border_width
= e
->xconfigurerequest
.border_width
;
808 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
809 e
->xconfigurerequest
.x
: client
->area
.x
;
810 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
811 e
->xconfigurerequest
.y
: client
->area
.y
;
812 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
813 e
->xconfigurerequest
.width
: client
->area
.width
;
814 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
815 e
->xconfigurerequest
.height
: client
->area
.height
;
821 client
->frame
->size
.left
+ client
->frame
->size
.right
;
823 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
824 /* make this rude for size-only changes but not for position
826 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
827 (e
->xconfigurerequest
.value_mask
& CWY
));
829 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
831 if (e
->xconfigurerequest
.value_mask
& CWX
)
833 if (e
->xconfigurerequest
.value_mask
& CWY
)
837 switch (client
->gravity
) {
838 case NorthEastGravity
:
840 corner
= OB_CORNER_TOPRIGHT
;
842 case SouthWestGravity
:
844 corner
= OB_CORNER_BOTTOMLEFT
;
846 case SouthEastGravity
:
847 corner
= OB_CORNER_BOTTOMRIGHT
;
849 default: /* NorthWest, Static, etc */
850 corner
= OB_CORNER_TOPLEFT
;
853 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
857 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
858 switch (e
->xconfigurerequest
.detail
) {
861 /* Apps are so rude. And this is totally disconnected from
862 activation/focus. Bleh. */
863 /*client_lower(client);*/
869 /* Apps are so rude. And this is totally disconnected from
870 activation/focus. Bleh. */
871 /*client_raise(client);*/
877 if (client
->ignore_unmaps
) {
878 client
->ignore_unmaps
--;
881 client_unmanage(client
);
884 client_unmanage(client
);
887 /* this is when the client is first taken captive in the frame */
888 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
891 This event is quite rare and is usually handled in unmapHandler.
892 However, if the window is unmapped when the reparent event occurs,
893 the window manager never sees it because an unmap event is not sent
894 to an already unmapped window.
897 /* we don't want the reparent event, put it back on the stack for the
898 X server to deal with after we unmanage the window */
899 XPutBackEvent(ob_display
, e
);
901 client_unmanage(client
);
904 ob_debug("MapRequest for 0x%lx\n", client
->window
);
905 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
906 does, we don't want it!
907 it can happen now when the window is on
908 another desktop, but we still don't
910 client_activate(client
, FALSE
, TRUE
, CurrentTime
);
913 /* validate cuz we query stuff off the client here */
914 if (!client_validate(client
)) break;
916 if (e
->xclient
.format
!= 32) return;
918 msgtype
= e
->xclient
.message_type
;
919 if (msgtype
== prop_atoms
.wm_change_state
) {
920 /* compress changes into a single change */
921 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
923 /* XXX: it would be nice to compress ALL messages of a
924 type, not just messages in a row without other
925 message types between. */
926 if (ce
.xclient
.message_type
!= msgtype
) {
927 XPutBackEvent(ob_display
, &ce
);
930 e
->xclient
= ce
.xclient
;
932 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
933 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
934 /* compress changes into a single change */
935 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
937 /* XXX: it would be nice to compress ALL messages of a
938 type, not just messages in a row without other
939 message types between. */
940 if (ce
.xclient
.message_type
!= msgtype
) {
941 XPutBackEvent(ob_display
, &ce
);
944 e
->xclient
= ce
.xclient
;
946 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
947 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
948 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
950 } else if (msgtype
== prop_atoms
.net_wm_state
) {
951 /* can't compress these */
952 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
953 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
954 e
->xclient
.data
.l
[0] == 1 ? "Add" :
955 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
956 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
958 client_set_state(client
, e
->xclient
.data
.l
[0],
959 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
960 } else if (msgtype
== prop_atoms
.net_close_window
) {
961 ob_debug("net_close_window for 0x%lx\n", client
->window
);
962 client_close(client
);
963 } else if (msgtype
== prop_atoms
.net_active_window
) {
964 ob_debug("net_active_window for 0x%lx source=%s\n",
966 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
967 (e
->xclient
.data
.l
[0] == 1 ? "application" :
968 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
969 /* XXX make use of data.l[1] and [2] ! */
970 client_activate(client
, FALSE
,
971 (e
->xclient
.data
.l
[0] == 0 ||
972 e
->xclient
.data
.l
[0] == 2),
973 e
->xclient
.data
.l
[1]);
974 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
975 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
976 client
->window
, e
->xclient
.data
.l
[2]);
977 if ((Atom
)e
->xclient
.data
.l
[2] ==
978 prop_atoms
.net_wm_moveresize_size_topleft
||
979 (Atom
)e
->xclient
.data
.l
[2] ==
980 prop_atoms
.net_wm_moveresize_size_top
||
981 (Atom
)e
->xclient
.data
.l
[2] ==
982 prop_atoms
.net_wm_moveresize_size_topright
||
983 (Atom
)e
->xclient
.data
.l
[2] ==
984 prop_atoms
.net_wm_moveresize_size_right
||
985 (Atom
)e
->xclient
.data
.l
[2] ==
986 prop_atoms
.net_wm_moveresize_size_right
||
987 (Atom
)e
->xclient
.data
.l
[2] ==
988 prop_atoms
.net_wm_moveresize_size_bottomright
||
989 (Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_size_bottom
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_size_bottomleft
||
993 (Atom
)e
->xclient
.data
.l
[2] ==
994 prop_atoms
.net_wm_moveresize_size_left
||
995 (Atom
)e
->xclient
.data
.l
[2] ==
996 prop_atoms
.net_wm_moveresize_move
||
997 (Atom
)e
->xclient
.data
.l
[2] ==
998 prop_atoms
.net_wm_moveresize_size_keyboard
||
999 (Atom
)e
->xclient
.data
.l
[2] ==
1000 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1002 moveresize_start(client
, e
->xclient
.data
.l
[0],
1003 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1004 e
->xclient
.data
.l
[2]);
1006 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1007 prop_atoms
.net_wm_moveresize_cancel
)
1008 moveresize_end(TRUE
);
1009 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1010 gint oldg
= client
->gravity
;
1011 gint tmpg
, x
, y
, w
, h
;
1013 if (e
->xclient
.data
.l
[0] & 0xff)
1014 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1018 if (e
->xclient
.data
.l
[0] & 1 << 8)
1019 x
= e
->xclient
.data
.l
[1];
1022 if (e
->xclient
.data
.l
[0] & 1 << 9)
1023 y
= e
->xclient
.data
.l
[2];
1026 if (e
->xclient
.data
.l
[0] & 1 << 10)
1027 w
= e
->xclient
.data
.l
[3];
1029 w
= client
->area
.width
;
1030 if (e
->xclient
.data
.l
[0] & 1 << 11)
1031 h
= e
->xclient
.data
.l
[4];
1033 h
= client
->area
.height
;
1034 client
->gravity
= tmpg
;
1040 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1042 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1043 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1044 client_normal(client
));
1045 if (e
->xclient
.data
.l
[0] & 1 << 8)
1047 if (e
->xclient
.data
.l
[0] & 1 << 9)
1051 client_configure(client
, OB_CORNER_TOPLEFT
,
1052 x
, y
, w
, h
, FALSE
, TRUE
);
1054 client
->gravity
= oldg
;
1057 case PropertyNotify
:
1058 /* validate cuz we query stuff off the client here */
1059 if (!client_validate(client
)) break;
1061 /* compress changes to a single property into a single change */
1062 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1066 /* XXX: it would be nice to compress ALL changes to a property,
1067 not just changes in a row without other props between. */
1069 a
= ce
.xproperty
.atom
;
1070 b
= e
->xproperty
.atom
;
1074 if ((a
== prop_atoms
.net_wm_name
||
1075 a
== prop_atoms
.wm_name
||
1076 a
== prop_atoms
.net_wm_icon_name
||
1077 a
== prop_atoms
.wm_icon_name
)
1079 (b
== prop_atoms
.net_wm_name
||
1080 b
== prop_atoms
.wm_name
||
1081 b
== prop_atoms
.net_wm_icon_name
||
1082 b
== prop_atoms
.wm_icon_name
)) {
1085 if (a
== prop_atoms
.net_wm_icon
&&
1086 b
== prop_atoms
.net_wm_icon
)
1089 XPutBackEvent(ob_display
, &ce
);
1093 msgtype
= e
->xproperty
.atom
;
1094 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1095 client_update_normal_hints(client
);
1096 /* normal hints can make a window non-resizable */
1097 client_setup_decor_and_functions(client
);
1098 } else if (msgtype
== XA_WM_HINTS
) {
1099 client_update_wmhints(client
);
1100 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1101 client_update_transient_for(client
);
1102 client_get_type(client
);
1103 /* type may have changed, so update the layer */
1104 client_calc_layer(client
);
1105 client_setup_decor_and_functions(client
);
1106 } else if (msgtype
== prop_atoms
.net_wm_name
||
1107 msgtype
== prop_atoms
.wm_name
||
1108 msgtype
== prop_atoms
.net_wm_icon_name
||
1109 msgtype
== prop_atoms
.wm_icon_name
) {
1110 client_update_title(client
);
1111 } else if (msgtype
== prop_atoms
.wm_class
) {
1112 client_update_class(client
);
1113 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1114 client_update_protocols(client
);
1115 client_setup_decor_and_functions(client
);
1117 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1118 client_update_strut(client
);
1120 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1121 client_update_icons(client
);
1123 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1124 client_update_user_time(client
, TRUE
);
1126 else if (msgtype
== prop_atoms
.sm_client_id
) {
1127 client_update_sm_client_id(client
);
1132 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1133 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1134 frame_adjust_shape(client
->frame
);
1140 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1144 if (e
->xbutton
.button
== 1)
1145 stacking_raise(DOCK_AS_WINDOW(s
));
1146 else if (e
->xbutton
.button
== 2)
1147 stacking_lower(DOCK_AS_WINDOW(s
));
1158 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1162 dock_app_drag(app
, &e
->xmotion
);
1165 if (app
->ignore_unmaps
) {
1166 app
->ignore_unmaps
--;
1169 dock_remove(app
, TRUE
);
1172 dock_remove(app
, FALSE
);
1174 case ReparentNotify
:
1175 dock_remove(app
, FALSE
);
1177 case ConfigureNotify
:
1178 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1183 ObMenuFrame
* find_active_menu()
1186 ObMenuFrame
*ret
= NULL
;
1188 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1197 ObMenuFrame
* find_active_or_last_menu()
1199 ObMenuFrame
*ret
= NULL
;
1201 ret
= find_active_menu();
1202 if (!ret
&& menu_frame_visible
)
1203 ret
= menu_frame_visible
->data
;
1207 static void event_handle_menu(XEvent
*ev
)
1210 ObMenuEntryFrame
*e
;
1214 if (menu_can_hide
) {
1215 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1216 ev
->xbutton
.y_root
)))
1217 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1220 menu_frame_hide_all();
1224 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1225 ev
->xmotion
.y_root
))) {
1226 menu_frame_move_on_screen(f
);
1227 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1228 ev
->xmotion
.y_root
)))
1229 menu_frame_select(f
, e
);
1234 a
= find_active_menu();
1236 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1238 menu_frame_select(a
, NULL
);
1243 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1244 menu_frame_hide_all();
1245 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1247 if ((f
= find_active_menu()))
1248 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1250 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1252 if ((f
= find_active_or_last_menu()) && f
->parent
)
1253 menu_frame_select(f
, NULL
);
1254 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1256 if ((f
= find_active_or_last_menu()) && f
->child
)
1257 menu_frame_select_next(f
->child
);
1258 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1260 if ((f
= find_active_or_last_menu()))
1261 menu_frame_select_previous(f
);
1262 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1264 if ((f
= find_active_or_last_menu()))
1265 menu_frame_select_next(f
);
1271 static gboolean
menu_hide_delay_func(gpointer data
)
1273 menu_can_hide
= TRUE
;
1274 return FALSE
; /* no repeat */
1277 static gboolean
focus_delay_func(gpointer data
)
1281 if (focus_client
!= c
) {
1282 if (client_validate(c
)) {
1284 if (config_focus_raise
)
1288 return FALSE
; /* no repeat */
1291 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1293 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1297 static void event_client_dest(ObClient
*client
, gpointer data
)
1299 if (client
== focus_hilite
)
1300 focus_hilite
= NULL
;
1303 void event_halt_focus_delay()
1305 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1308 void event_ignore_queued_enters()
1310 GSList
*saved
= NULL
, *it
;
1313 XSync(ob_display
, FALSE
);
1315 /* count the events */
1317 e
= g_new(XEvent
, 1);
1318 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1321 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1322 if (win
&& WINDOW_IS_CLIENT(win
))
1323 ++ignore_enter_focus
;
1325 saved
= g_slist_append(saved
, e
);
1331 /* put the events back */
1332 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1333 XPutBackEvent(ob_display
, it
->data
);
1336 g_slist_free(saved
);