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 most recent time at which an event with a timestamp occured. */
82 static Time event_lasttime
= 0;
83 /* The time for the current event being processed
84 (it's the event_lasttime for events without times, if this is a bug then
85 use CurrentTime instead, but it seems ok) */
86 Time event_curtime
= CurrentTime
;
88 /*! The value of the mask for the NumLock modifier */
90 /*! The value of the mask for the ScrollLock modifier */
92 /*! The key codes for the modifier keys */
93 static XModifierKeymap
*modmap
;
94 /*! Table of the constant modifier masks */
95 static const gint mask_table
[] = {
96 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
97 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
99 static gint mask_table_size
;
101 static guint ignore_enter_focus
= 0;
103 static gboolean menu_can_hide
;
106 static void ice_handler(gint fd
, gpointer conn
)
109 IceProcessMessages(conn
, NULL
, &b
);
112 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
113 IcePointer
*watch_data
)
118 fd
= IceConnectionNumber(conn
);
119 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
121 ob_main_loop_fd_remove(ob_main_loop
, fd
);
127 void event_startup(gboolean reconfig
)
129 if (reconfig
) return;
131 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
133 /* get lock masks that are defined by the display (not constant) */
134 modmap
= XGetModifierMapping(ob_display
);
136 if (modmap
&& modmap
->max_keypermod
> 0) {
138 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
139 /* get the values of the keyboard lock modifiers
140 Note: Caps lock is not retrieved the same way as Scroll and Num
141 lock since it doesn't need to be. */
142 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
143 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
146 for (cnt
= 0; cnt
< size
; ++cnt
) {
147 if (! modmap
->modifiermap
[cnt
]) continue;
149 if (num_lock
== modmap
->modifiermap
[cnt
])
150 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
151 if (scroll_lock
== modmap
->modifiermap
[cnt
])
152 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
156 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
159 IceAddConnectionWatch(ice_watch
, NULL
);
162 client_add_destructor(focus_delay_client_dest
, NULL
);
163 client_add_destructor(event_client_dest
, NULL
);
166 void event_shutdown(gboolean reconfig
)
168 if (reconfig
) return;
171 IceRemoveConnectionWatch(ice_watch
, NULL
);
174 client_remove_destructor(focus_delay_client_dest
);
175 client_remove_destructor(event_client_dest
);
176 XFreeModifiermap(modmap
);
179 static Window
event_get_window(XEvent
*e
)
186 window
= RootWindow(ob_display
, ob_screen
);
189 window
= e
->xmap
.window
;
192 window
= e
->xunmap
.window
;
195 window
= e
->xdestroywindow
.window
;
197 case ConfigureRequest
:
198 window
= e
->xconfigurerequest
.window
;
200 case ConfigureNotify
:
201 window
= e
->xconfigure
.window
;
205 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
206 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
208 window
= ((XkbBellNotifyEvent
*)e
)->window
;
214 window
= e
->xany
.window
;
219 static void event_set_lasttime(XEvent
*e
)
223 /* grab the lasttime and hack up the state */
239 t
= e
->xproperty
.time
;
243 t
= e
->xcrossing
.time
;
246 /* if more event types are anticipated, get their timestamp
251 if (t
> event_lasttime
) {
253 event_curtime
= event_lasttime
;
255 event_curtime
= event_lasttime
;
261 #define STRIP_MODS(s) \
262 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
263 /* kill off the Button1Mask etc, only want the modifiers */ \
264 s &= (ControlMask | ShiftMask | Mod1Mask | \
265 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
267 static void event_hack_mods(XEvent *e)
270 XkbStateRec xkb_state
;
278 STRIP_MODS(e
->xbutton
.state
);
281 STRIP_MODS(e
->xkey
.state
);
284 STRIP_MODS(e
->xkey
.state
);
285 /* remove from the state the mask of the modifier being released, if
286 it is a modifier key being released (this is a little ugly..) */
288 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
289 e
->xkey
.state
= xkb_state
.compat_state
;
293 kp
= modmap
->modifiermap
;
294 for (i
= 0; i
< mask_table_size
; ++i
) {
295 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
296 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
297 /* remove the mask for it */
298 e
->xkey
.state
&= ~mask_table
[i
];
299 /* cause the first loop to break; */
301 break; /* get outta here! */
308 STRIP_MODS(e
->xmotion
.state
);
309 /* compress events */
312 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
314 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
315 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
322 static gboolean
wanted_focusevent(XEvent
*e
)
324 gint mode
= e
->xfocus
.mode
;
325 gint detail
= e
->xfocus
.detail
;
327 if (e
->type
== FocusIn
) {
329 /* These are ones we never want.. */
331 /* This means focus was given by a keyboard/mouse grab. */
332 if (mode
== NotifyGrab
)
334 /* This means focus was given back from a keyboard/mouse grab. */
335 if (mode
== NotifyUngrab
)
338 /* These are the ones we want.. */
340 /* This means focus moved from the root window to a client */
341 if (detail
== NotifyVirtual
)
343 /* This means focus moved from one client to another */
344 if (detail
== NotifyNonlinearVirtual
)
350 g_assert(e
->type
== FocusOut
);
353 /* These are ones we never want.. */
355 /* This means focus was taken by a keyboard/mouse grab. */
356 if (mode
== NotifyGrab
)
359 /* These are the ones we want.. */
361 /* This means focus moved from a client to the root window */
362 if (detail
== NotifyVirtual
)
364 /* This means focus moved from one client to another */
365 if (detail
== NotifyNonlinearVirtual
)
373 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
375 return e
->type
== FocusIn
&& wanted_focusevent(e
);
378 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
383 if (e
->xcrossing
.detail
== NotifyInferior
)
388 /* I don't think this should ever happen with our event masks, but
389 if it does, we don't want it. */
392 if (!wanted_focusevent(e
))
399 static void event_process(const XEvent
*ec
, gpointer data
)
402 ObGroup
*group
= NULL
;
403 ObClient
*client
= NULL
;
405 ObDockApp
*dockapp
= NULL
;
406 ObWindow
*obwin
= NULL
;
408 ObEventData
*ed
= data
;
410 /* make a copy we can mangle */
414 window
= event_get_window(e
);
415 if (!(e
->type
== PropertyNotify
&&
416 (group
= g_hash_table_lookup(group_map
, &window
))))
417 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
418 switch (obwin
->type
) {
420 dock
= WINDOW_AS_DOCK(obwin
);
423 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
426 client
= WINDOW_AS_CLIENT(obwin
);
429 case Window_Internal
:
430 /* not to be used for events */
431 g_assert_not_reached();
436 #if 0 /* focus debugging stuff */
437 if (e
->type
== FocusIn
|| e
->type
== FocusOut
) {
438 gint mode
= e
->xfocus
.mode
;
439 gint detail
= e
->xfocus
.detail
;
440 Window window
= e
->xfocus
.window
;
441 if (detail
== NotifyVirtual
) {
442 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
443 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
446 else if (detail
== NotifyNonlinearVirtual
) {
447 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
448 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
452 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
453 (e
->type
== FocusIn
? "IN" : "OUT"),
454 detail
, mode
, window
);
457 event_set_lasttime(e
);
459 if (event_ignore(e
, client
)) {
466 /* deal with it in the kernel */
468 event_handle_group(group
, e
);
470 event_handle_client(client
, e
);
472 event_handle_dockapp(dockapp
, e
);
474 event_handle_dock(dock
, e
);
475 else if (window
== RootWindow(ob_display
, ob_screen
))
476 event_handle_root(e
);
477 else if (e
->type
== MapRequest
)
478 client_manage(window
);
479 else if (e
->type
== ConfigureRequest
) {
480 /* unhandled configure requests must be used to configure the
484 xwc
.x
= e
->xconfigurerequest
.x
;
485 xwc
.y
= e
->xconfigurerequest
.y
;
486 xwc
.width
= e
->xconfigurerequest
.width
;
487 xwc
.height
= e
->xconfigurerequest
.height
;
488 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
489 xwc
.sibling
= e
->xconfigurerequest
.above
;
490 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
492 /* we are not to be held responsible if someone sends us an
494 xerror_set_ignore(TRUE
);
495 XConfigureWindow(ob_display
, window
,
496 e
->xconfigurerequest
.value_mask
, &xwc
);
497 xerror_set_ignore(FALSE
);
500 /* user input (action-bound) events */
501 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
502 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
503 e
->type
== KeyRelease
)
505 if (menu_frame_visible
)
506 event_handle_menu(e
);
508 if (!keyboard_process_interactive_grab(e
, &client
)) {
509 if (moveresize_in_progress
) {
512 /* make further actions work on the client being
514 client
= moveresize_client
;
517 menu_can_hide
= FALSE
;
518 ob_main_loop_timeout_add(ob_main_loop
,
519 config_menu_hide_delay
* 1000,
520 menu_hide_delay_func
,
523 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
524 e
->type
== MotionNotify
)
525 mouse_event(client
, e
);
526 else if (e
->type
== KeyPress
)
527 keyboard_event((focus_cycle_target
? focus_cycle_target
:
528 (focus_hilite
? focus_hilite
: client
)),
535 static void event_handle_root(XEvent
*e
)
541 ob_debug("Another WM has requested to replace us. Exiting.\n");
546 if (e
->xclient
.format
!= 32) break;
548 msgtype
= e
->xclient
.message_type
;
549 if (msgtype
== prop_atoms
.net_current_desktop
) {
550 guint d
= e
->xclient
.data
.l
[0];
551 if (d
< screen_num_desktops
)
552 screen_set_desktop(d
);
553 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
554 guint d
= e
->xclient
.data
.l
[0];
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)
562 else if (e
->xclient
.data
.l
[0] == 2)
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();
572 case ConfigureNotify
:
574 XRRUpdateConfiguration(e
);
583 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
587 g_assert(e
->type
== PropertyNotify
);
589 for (it
= group
->members
; it
; it
= g_slist_next(it
))
590 event_handle_client(it
->data
, e
);
593 void event_enter_client(ObClient
*client
)
595 g_assert(config_focus_follow
);
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
,
605 focus_delay_func(client
);
609 static void event_handle_client(ObClient
*client
, XEvent
*e
)
617 case VisibilityNotify
:
618 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
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
);
628 case OB_FRAME_CONTEXT_MAXIMIZE
:
629 client
->frame
->max_press
= (e
->type
== ButtonPress
);
630 framerender_frame(client
->frame
);
632 case OB_FRAME_CONTEXT_CLOSE
:
633 client
->frame
->close_press
= (e
->type
== ButtonPress
);
634 framerender_frame(client
->frame
);
636 case OB_FRAME_CONTEXT_ICONIFY
:
637 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
638 framerender_frame(client
->frame
);
640 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
641 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
642 framerender_frame(client
->frame
);
644 case OB_FRAME_CONTEXT_SHADE
:
645 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
646 framerender_frame(client
->frame
);
649 /* nothing changes with clicks for any other contexts */
655 if (client
!= focus_client
) {
656 focus_set_client(client
);
657 frame_adjust_focus(client
->frame
, TRUE
);
658 client_calc_layer(client
);
662 /* Look for the followup FocusIn */
663 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
664 /* There is no FocusIn, move focus where we can still hear events*/
665 focus_set_client(NULL
);
666 } else if (ce
.xany
.window
== e
->xany
.window
) {
667 /* If focus didn't actually move anywhere, there is nothing to do*/
670 /* Focus did move, so process the FocusIn event */
672 event_process(&ce
, &ed
);
674 /* The FocusIn was ignored, this means it was on a window
675 that isn't a client? How did this happen? */
676 g_assert_not_reached();
680 /* This client is no longer focused, so show that */
681 frame_adjust_focus(client
->frame
, FALSE
);
682 client_calc_layer(client
);
685 con
= frame_context(client
, e
->xcrossing
.window
);
687 case OB_FRAME_CONTEXT_MAXIMIZE
:
688 client
->frame
->max_hover
= FALSE
;
689 frame_adjust_state(client
->frame
);
691 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
692 client
->frame
->desk_hover
= FALSE
;
693 frame_adjust_state(client
->frame
);
695 case OB_FRAME_CONTEXT_SHADE
:
696 client
->frame
->shade_hover
= FALSE
;
697 frame_adjust_state(client
->frame
);
699 case OB_FRAME_CONTEXT_ICONIFY
:
700 client
->frame
->iconify_hover
= FALSE
;
701 frame_adjust_state(client
->frame
);
703 case OB_FRAME_CONTEXT_CLOSE
:
704 client
->frame
->close_hover
= FALSE
;
705 frame_adjust_state(client
->frame
);
707 case OB_FRAME_CONTEXT_FRAME
:
708 if (config_focus_follow
&& config_focus_delay
)
709 ob_main_loop_timeout_remove_data(ob_main_loop
,
719 gboolean nofocus
= FALSE
;
721 if (ignore_enter_focus
) {
722 ignore_enter_focus
--;
726 con
= frame_context(client
, e
->xcrossing
.window
);
728 case OB_FRAME_CONTEXT_MAXIMIZE
:
729 client
->frame
->max_hover
= TRUE
;
730 frame_adjust_state(client
->frame
);
732 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
733 client
->frame
->desk_hover
= TRUE
;
734 frame_adjust_state(client
->frame
);
736 case OB_FRAME_CONTEXT_SHADE
:
737 client
->frame
->shade_hover
= TRUE
;
738 frame_adjust_state(client
->frame
);
740 case OB_FRAME_CONTEXT_ICONIFY
:
741 client
->frame
->iconify_hover
= TRUE
;
742 frame_adjust_state(client
->frame
);
744 case OB_FRAME_CONTEXT_CLOSE
:
745 client
->frame
->close_hover
= TRUE
;
746 frame_adjust_state(client
->frame
);
748 case OB_FRAME_CONTEXT_FRAME
:
749 if (e
->xcrossing
.mode
== NotifyGrab
||
750 e
->xcrossing
.mode
== NotifyUngrab
)
753 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
754 (e
->type
== EnterNotify
? "Enter" : "Leave"),
756 e
->xcrossing
.detail
, client
?client
->window
:0);
760 ob_debug("%sNotify mode %d detail %d on %lx, "
761 "focusing window: %d\n",
762 (e
->type
== EnterNotify
? "Enter" : "Leave"),
764 e
->xcrossing
.detail
, (client
?client
->window
:0),
767 if (!nofocus
&& config_focus_follow
)
768 event_enter_client(client
);
776 case ConfigureRequest
:
778 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
779 ConfigureRequest
, &ce
)) {
781 /* XXX if this causes bad things.. we can compress config req's
782 with the same mask. */
783 e
->xconfigurerequest
.value_mask
|=
784 ce
.xconfigurerequest
.value_mask
;
785 if (ce
.xconfigurerequest
.value_mask
& CWX
)
786 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
787 if (ce
.xconfigurerequest
.value_mask
& CWY
)
788 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
789 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
790 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
791 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
792 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
793 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
794 e
->xconfigurerequest
.border_width
=
795 ce
.xconfigurerequest
.border_width
;
796 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
797 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
800 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
801 if (client
->iconic
|| client
->shaded
) return;
803 /* resize, then move, as specified in the EWMH section 7.7 */
804 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
810 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
811 client
->border_width
= e
->xconfigurerequest
.border_width
;
813 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
814 e
->xconfigurerequest
.x
: client
->area
.x
;
815 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
816 e
->xconfigurerequest
.y
: client
->area
.y
;
817 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
818 e
->xconfigurerequest
.width
: client
->area
.width
;
819 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
820 e
->xconfigurerequest
.height
: client
->area
.height
;
826 client
->frame
->size
.left
+ client
->frame
->size
.right
;
828 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
829 /* make this rude for size-only changes but not for position
831 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
832 (e
->xconfigurerequest
.value_mask
& CWY
));
834 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
836 if (e
->xconfigurerequest
.value_mask
& CWX
)
838 if (e
->xconfigurerequest
.value_mask
& CWY
)
842 switch (client
->gravity
) {
843 case NorthEastGravity
:
845 corner
= OB_CORNER_TOPRIGHT
;
847 case SouthWestGravity
:
849 corner
= OB_CORNER_BOTTOMLEFT
;
851 case SouthEastGravity
:
852 corner
= OB_CORNER_BOTTOMRIGHT
;
854 default: /* NorthWest, Static, etc */
855 corner
= OB_CORNER_TOPLEFT
;
858 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
862 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
863 switch (e
->xconfigurerequest
.detail
) {
866 /* Apps are so rude. And this is totally disconnected from
867 activation/focus. Bleh. */
868 /*client_lower(client);*/
874 /* Apps are so rude. And this is totally disconnected from
875 activation/focus. Bleh. */
876 /*client_raise(client);*/
882 if (client
->ignore_unmaps
) {
883 client
->ignore_unmaps
--;
886 client_unmanage(client
);
889 client_unmanage(client
);
892 /* this is when the client is first taken captive in the frame */
893 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
896 This event is quite rare and is usually handled in unmapHandler.
897 However, if the window is unmapped when the reparent event occurs,
898 the window manager never sees it because an unmap event is not sent
899 to an already unmapped window.
902 /* we don't want the reparent event, put it back on the stack for the
903 X server to deal with after we unmanage the window */
904 XPutBackEvent(ob_display
, e
);
906 client_unmanage(client
);
909 ob_debug("MapRequest for 0x%lx\n", client
->window
);
910 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
911 does, we don't want it!
912 it can happen now when the window is on
913 another desktop, but we still don't
915 client_activate(client
, FALSE
, TRUE
, CurrentTime
);
918 /* validate cuz we query stuff off the client here */
919 if (!client_validate(client
)) break;
921 if (e
->xclient
.format
!= 32) return;
923 msgtype
= e
->xclient
.message_type
;
924 if (msgtype
== prop_atoms
.wm_change_state
) {
925 /* compress changes into a single change */
926 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
928 /* XXX: it would be nice to compress ALL messages of a
929 type, not just messages in a row without other
930 message types between. */
931 if (ce
.xclient
.message_type
!= msgtype
) {
932 XPutBackEvent(ob_display
, &ce
);
935 e
->xclient
= ce
.xclient
;
937 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
938 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
939 /* compress changes into a single change */
940 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
942 /* XXX: it would be nice to compress ALL messages of a
943 type, not just messages in a row without other
944 message types between. */
945 if (ce
.xclient
.message_type
!= msgtype
) {
946 XPutBackEvent(ob_display
, &ce
);
949 e
->xclient
= ce
.xclient
;
951 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
952 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
953 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
955 } else if (msgtype
== prop_atoms
.net_wm_state
) {
956 /* can't compress these */
957 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
958 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
959 e
->xclient
.data
.l
[0] == 1 ? "Add" :
960 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
961 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
963 client_set_state(client
, e
->xclient
.data
.l
[0],
964 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
965 } else if (msgtype
== prop_atoms
.net_close_window
) {
966 ob_debug("net_close_window for 0x%lx\n", client
->window
);
967 client_close(client
);
968 } else if (msgtype
== prop_atoms
.net_active_window
) {
969 ob_debug("net_active_window for 0x%lx source=%s\n",
971 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
972 (e
->xclient
.data
.l
[0] == 1 ? "application" :
973 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
974 /* XXX make use of data.l[1] and [2] ! */
975 client_activate(client
, FALSE
,
976 (e
->xclient
.data
.l
[0] == 0 ||
977 e
->xclient
.data
.l
[0] == 2),
978 e
->xclient
.data
.l
[1]);
979 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
980 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
981 client
->window
, e
->xclient
.data
.l
[2]);
982 if ((Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_topleft
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_top
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_topright
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_right
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_size_right
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_size_bottomright
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_size_bottom
||
996 (Atom
)e
->xclient
.data
.l
[2] ==
997 prop_atoms
.net_wm_moveresize_size_bottomleft
||
998 (Atom
)e
->xclient
.data
.l
[2] ==
999 prop_atoms
.net_wm_moveresize_size_left
||
1000 (Atom
)e
->xclient
.data
.l
[2] ==
1001 prop_atoms
.net_wm_moveresize_move
||
1002 (Atom
)e
->xclient
.data
.l
[2] ==
1003 prop_atoms
.net_wm_moveresize_size_keyboard
||
1004 (Atom
)e
->xclient
.data
.l
[2] ==
1005 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1007 moveresize_start(client
, e
->xclient
.data
.l
[0],
1008 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1009 e
->xclient
.data
.l
[2]);
1011 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1012 prop_atoms
.net_wm_moveresize_cancel
)
1013 moveresize_end(TRUE
);
1014 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1015 gint oldg
= client
->gravity
;
1016 gint tmpg
, x
, y
, w
, h
;
1018 if (e
->xclient
.data
.l
[0] & 0xff)
1019 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1023 if (e
->xclient
.data
.l
[0] & 1 << 8)
1024 x
= e
->xclient
.data
.l
[1];
1027 if (e
->xclient
.data
.l
[0] & 1 << 9)
1028 y
= e
->xclient
.data
.l
[2];
1031 if (e
->xclient
.data
.l
[0] & 1 << 10)
1032 w
= e
->xclient
.data
.l
[3];
1034 w
= client
->area
.width
;
1035 if (e
->xclient
.data
.l
[0] & 1 << 11)
1036 h
= e
->xclient
.data
.l
[4];
1038 h
= client
->area
.height
;
1039 client
->gravity
= tmpg
;
1045 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1047 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1048 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1049 client_normal(client
));
1050 if (e
->xclient
.data
.l
[0] & 1 << 8)
1052 if (e
->xclient
.data
.l
[0] & 1 << 9)
1056 client_configure(client
, OB_CORNER_TOPLEFT
,
1057 x
, y
, w
, h
, FALSE
, TRUE
);
1059 client
->gravity
= oldg
;
1062 case PropertyNotify
:
1063 /* validate cuz we query stuff off the client here */
1064 if (!client_validate(client
)) break;
1066 /* compress changes to a single property into a single change */
1067 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1071 /* XXX: it would be nice to compress ALL changes to a property,
1072 not just changes in a row without other props between. */
1074 a
= ce
.xproperty
.atom
;
1075 b
= e
->xproperty
.atom
;
1079 if ((a
== prop_atoms
.net_wm_name
||
1080 a
== prop_atoms
.wm_name
||
1081 a
== prop_atoms
.net_wm_icon_name
||
1082 a
== prop_atoms
.wm_icon_name
)
1084 (b
== prop_atoms
.net_wm_name
||
1085 b
== prop_atoms
.wm_name
||
1086 b
== prop_atoms
.net_wm_icon_name
||
1087 b
== prop_atoms
.wm_icon_name
)) {
1090 if (a
== prop_atoms
.net_wm_icon
&&
1091 b
== prop_atoms
.net_wm_icon
)
1094 XPutBackEvent(ob_display
, &ce
);
1098 msgtype
= e
->xproperty
.atom
;
1099 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1100 client_update_normal_hints(client
);
1101 /* normal hints can make a window non-resizable */
1102 client_setup_decor_and_functions(client
);
1103 } else if (msgtype
== XA_WM_HINTS
) {
1104 client_update_wmhints(client
);
1105 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1106 client_update_transient_for(client
);
1107 client_get_type(client
);
1108 /* type may have changed, so update the layer */
1109 client_calc_layer(client
);
1110 client_setup_decor_and_functions(client
);
1111 } else if (msgtype
== prop_atoms
.net_wm_name
||
1112 msgtype
== prop_atoms
.wm_name
||
1113 msgtype
== prop_atoms
.net_wm_icon_name
||
1114 msgtype
== prop_atoms
.wm_icon_name
) {
1115 client_update_title(client
);
1116 } else if (msgtype
== prop_atoms
.wm_class
) {
1117 client_update_class(client
);
1118 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1119 client_update_protocols(client
);
1120 client_setup_decor_and_functions(client
);
1122 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1123 client_update_strut(client
);
1125 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1126 client_update_icons(client
);
1128 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1129 client_update_user_time(client
, TRUE
);
1131 else if (msgtype
== prop_atoms
.sm_client_id
) {
1132 client_update_sm_client_id(client
);
1137 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1138 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1139 frame_adjust_shape(client
->frame
);
1145 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1149 if (e
->xbutton
.button
== 1)
1150 stacking_raise(DOCK_AS_WINDOW(s
));
1151 else if (e
->xbutton
.button
== 2)
1152 stacking_lower(DOCK_AS_WINDOW(s
));
1163 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1167 dock_app_drag(app
, &e
->xmotion
);
1170 if (app
->ignore_unmaps
) {
1171 app
->ignore_unmaps
--;
1174 dock_remove(app
, TRUE
);
1177 dock_remove(app
, FALSE
);
1179 case ReparentNotify
:
1180 dock_remove(app
, FALSE
);
1182 case ConfigureNotify
:
1183 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1188 ObMenuFrame
* find_active_menu()
1191 ObMenuFrame
*ret
= NULL
;
1193 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1202 ObMenuFrame
* find_active_or_last_menu()
1204 ObMenuFrame
*ret
= NULL
;
1206 ret
= find_active_menu();
1207 if (!ret
&& menu_frame_visible
)
1208 ret
= menu_frame_visible
->data
;
1212 static void event_handle_menu(XEvent
*ev
)
1215 ObMenuEntryFrame
*e
;
1219 if (menu_can_hide
) {
1220 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1221 ev
->xbutton
.y_root
)))
1222 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1225 menu_frame_hide_all();
1229 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1230 ev
->xmotion
.y_root
))) {
1231 menu_frame_move_on_screen(f
);
1232 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1233 ev
->xmotion
.y_root
)))
1234 menu_frame_select(f
, e
);
1239 a
= find_active_menu();
1241 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1243 menu_frame_select(a
, NULL
);
1248 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1249 menu_frame_hide_all();
1250 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1252 if ((f
= find_active_menu()))
1253 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1255 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1257 if ((f
= find_active_or_last_menu()) && f
->parent
)
1258 menu_frame_select(f
, NULL
);
1259 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1261 if ((f
= find_active_or_last_menu()) && f
->child
)
1262 menu_frame_select_next(f
->child
);
1263 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1265 if ((f
= find_active_or_last_menu()))
1266 menu_frame_select_previous(f
);
1267 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1269 if ((f
= find_active_or_last_menu()))
1270 menu_frame_select_next(f
);
1276 static gboolean
menu_hide_delay_func(gpointer data
)
1278 menu_can_hide
= TRUE
;
1279 return FALSE
; /* no repeat */
1282 static gboolean
focus_delay_func(gpointer data
)
1286 if (focus_client
!= c
) {
1288 if (config_focus_raise
)
1291 return FALSE
; /* no repeat */
1294 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1296 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1300 static void event_client_dest(ObClient
*client
, gpointer data
)
1302 if (client
== focus_hilite
)
1303 focus_hilite
= NULL
;
1306 void event_halt_focus_delay()
1308 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1311 void event_ignore_queued_enters()
1313 GSList
*saved
= NULL
, *it
;
1316 XSync(ob_display
, FALSE
);
1318 /* count the events */
1320 e
= g_new(XEvent
, 1);
1321 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1324 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1325 if (win
&& WINDOW_IS_CLIENT(win
))
1326 ++ignore_enter_focus
;
1328 saved
= g_slist_append(saved
, e
);
1334 /* put the events back */
1335 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1336 XPutBackEvent(ob_display
, it
->data
);
1339 g_slist_free(saved
);