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
);
458 event_set_lasttime(e
);
460 if (event_ignore(e
, client
)) {
467 /* deal with it in the kernel */
469 event_handle_group(group
, e
);
471 event_handle_client(client
, e
);
473 event_handle_dockapp(dockapp
, e
);
475 event_handle_dock(dock
, e
);
476 else if (window
== RootWindow(ob_display
, ob_screen
))
477 event_handle_root(e
);
478 else if (e
->type
== MapRequest
)
479 client_manage(window
);
480 else if (e
->type
== ConfigureRequest
) {
481 /* unhandled configure requests must be used to configure the
485 xwc
.x
= e
->xconfigurerequest
.x
;
486 xwc
.y
= e
->xconfigurerequest
.y
;
487 xwc
.width
= e
->xconfigurerequest
.width
;
488 xwc
.height
= e
->xconfigurerequest
.height
;
489 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
490 xwc
.sibling
= e
->xconfigurerequest
.above
;
491 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
493 /* we are not to be held responsible if someone sends us an
495 xerror_set_ignore(TRUE
);
496 XConfigureWindow(ob_display
, window
,
497 e
->xconfigurerequest
.value_mask
, &xwc
);
498 xerror_set_ignore(FALSE
);
501 /* user input (action-bound) events */
502 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
503 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
504 e
->type
== KeyRelease
)
506 if (menu_frame_visible
)
507 event_handle_menu(e
);
509 if (!keyboard_process_interactive_grab(e
, &client
)) {
510 if (moveresize_in_progress
) {
513 /* make further actions work on the client being
515 client
= moveresize_client
;
518 menu_can_hide
= FALSE
;
519 ob_main_loop_timeout_add(ob_main_loop
,
520 config_menu_hide_delay
* 1000,
521 menu_hide_delay_func
,
524 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
525 e
->type
== MotionNotify
)
526 mouse_event(client
, e
);
527 else if (e
->type
== KeyPress
) {
528 keyboard_event((focus_cycle_target
? focus_cycle_target
:
529 (focus_hilite
? focus_hilite
: client
)),
537 static void event_handle_root(XEvent
*e
)
543 ob_debug("Another WM has requested to replace us. Exiting.\n");
548 if (e
->xclient
.format
!= 32) break;
550 msgtype
= e
->xclient
.message_type
;
551 if (msgtype
== prop_atoms
.net_current_desktop
) {
552 guint d
= e
->xclient
.data
.l
[0];
553 if (d
< screen_num_desktops
)
554 screen_set_desktop(d
);
555 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
556 guint d
= e
->xclient
.data
.l
[0];
558 screen_set_num_desktops(d
);
559 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
560 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
561 } else if (msgtype
== prop_atoms
.ob_control
) {
562 if (e
->xclient
.data
.l
[0] == 1)
564 else if (e
->xclient
.data
.l
[0] == 2)
569 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
570 screen_update_desktop_names();
571 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
572 screen_update_layout();
574 case ConfigureNotify
:
576 XRRUpdateConfiguration(e
);
585 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
589 g_assert(e
->type
== PropertyNotify
);
591 for (it
= group
->members
; it
; it
= g_slist_next(it
))
592 event_handle_client(it
->data
, e
);
595 void event_enter_client(ObClient
*client
)
597 g_assert(config_focus_follow
);
599 if (client_normal(client
) && client_can_focus(client
)) {
600 if (config_focus_delay
) {
601 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
602 ob_main_loop_timeout_add(ob_main_loop
,
607 focus_delay_func(client
);
611 static void event_handle_client(ObClient
*client
, XEvent
*e
)
619 case VisibilityNotify
:
620 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
624 /* Wheel buttons don't draw because they are an instant click, so it
625 is a waste of resources to go drawing it. */
626 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
627 con
= frame_context(client
, e
->xbutton
.window
);
628 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
630 case OB_FRAME_CONTEXT_MAXIMIZE
:
631 client
->frame
->max_press
= (e
->type
== ButtonPress
);
632 framerender_frame(client
->frame
);
634 case OB_FRAME_CONTEXT_CLOSE
:
635 client
->frame
->close_press
= (e
->type
== ButtonPress
);
636 framerender_frame(client
->frame
);
638 case OB_FRAME_CONTEXT_ICONIFY
:
639 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
640 framerender_frame(client
->frame
);
642 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
643 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
644 framerender_frame(client
->frame
);
646 case OB_FRAME_CONTEXT_SHADE
:
647 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
648 framerender_frame(client
->frame
);
651 /* nothing changes with clicks for any other contexts */
657 if (client
!= focus_client
) {
658 focus_set_client(client
);
659 frame_adjust_focus(client
->frame
, TRUE
);
660 client_calc_layer(client
);
664 /* Look for the followup FocusIn */
665 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
666 /* There is no FocusIn, move focus where we can still hear events*/
667 focus_set_client(NULL
);
668 } else if (ce
.xany
.window
== e
->xany
.window
) {
669 /* If focus didn't actually move anywhere, there is nothing to do*/
672 /* Focus did move, so process the FocusIn event */
674 event_process(&ce
, &ed
);
676 /* The FocusIn was ignored, this means it was on a window
677 that isn't a client? How did this happen? */
678 g_assert_not_reached();
682 /* This client is no longer focused, so show that */
684 frame_adjust_focus(client
->frame
, FALSE
);
685 client_calc_layer(client
);
688 con
= frame_context(client
, e
->xcrossing
.window
);
690 case OB_FRAME_CONTEXT_MAXIMIZE
:
691 client
->frame
->max_hover
= FALSE
;
692 frame_adjust_state(client
->frame
);
694 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
695 client
->frame
->desk_hover
= FALSE
;
696 frame_adjust_state(client
->frame
);
698 case OB_FRAME_CONTEXT_SHADE
:
699 client
->frame
->shade_hover
= FALSE
;
700 frame_adjust_state(client
->frame
);
702 case OB_FRAME_CONTEXT_ICONIFY
:
703 client
->frame
->iconify_hover
= FALSE
;
704 frame_adjust_state(client
->frame
);
706 case OB_FRAME_CONTEXT_CLOSE
:
707 client
->frame
->close_hover
= FALSE
;
708 frame_adjust_state(client
->frame
);
710 case OB_FRAME_CONTEXT_FRAME
:
711 if (config_focus_follow
&& config_focus_delay
)
712 ob_main_loop_timeout_remove_data(ob_main_loop
,
722 gboolean nofocus
= FALSE
;
724 if (ignore_enter_focus
) {
725 ignore_enter_focus
--;
729 con
= frame_context(client
, e
->xcrossing
.window
);
731 case OB_FRAME_CONTEXT_MAXIMIZE
:
732 client
->frame
->max_hover
= TRUE
;
733 frame_adjust_state(client
->frame
);
735 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
736 client
->frame
->desk_hover
= TRUE
;
737 frame_adjust_state(client
->frame
);
739 case OB_FRAME_CONTEXT_SHADE
:
740 client
->frame
->shade_hover
= TRUE
;
741 frame_adjust_state(client
->frame
);
743 case OB_FRAME_CONTEXT_ICONIFY
:
744 client
->frame
->iconify_hover
= TRUE
;
745 frame_adjust_state(client
->frame
);
747 case OB_FRAME_CONTEXT_CLOSE
:
748 client
->frame
->close_hover
= TRUE
;
749 frame_adjust_state(client
->frame
);
751 case OB_FRAME_CONTEXT_FRAME
:
752 if (e
->xcrossing
.mode
== NotifyGrab
||
753 e
->xcrossing
.mode
== NotifyUngrab
)
756 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
757 (e
->type
== EnterNotify
? "Enter" : "Leave"),
759 e
->xcrossing
.detail
, client
?client
->window
:0);
763 ob_debug("%sNotify mode %d detail %d on %lx, "
764 "focusing window: %d\n",
765 (e
->type
== EnterNotify
? "Enter" : "Leave"),
767 e
->xcrossing
.detail
, (client
?client
->window
:0),
770 if (!nofocus
&& config_focus_follow
)
771 event_enter_client(client
);
779 case ConfigureRequest
:
781 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
782 ConfigureRequest
, &ce
)) {
784 /* XXX if this causes bad things.. we can compress config req's
785 with the same mask. */
786 e
->xconfigurerequest
.value_mask
|=
787 ce
.xconfigurerequest
.value_mask
;
788 if (ce
.xconfigurerequest
.value_mask
& CWX
)
789 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
790 if (ce
.xconfigurerequest
.value_mask
& CWY
)
791 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
792 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
793 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
794 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
795 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
796 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
797 e
->xconfigurerequest
.border_width
=
798 ce
.xconfigurerequest
.border_width
;
799 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
800 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
803 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
804 if (client
->iconic
|| client
->shaded
) return;
806 /* resize, then move, as specified in the EWMH section 7.7 */
807 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
813 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
814 client
->border_width
= e
->xconfigurerequest
.border_width
;
816 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
817 e
->xconfigurerequest
.x
: client
->area
.x
;
818 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
819 e
->xconfigurerequest
.y
: client
->area
.y
;
820 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
821 e
->xconfigurerequest
.width
: client
->area
.width
;
822 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
823 e
->xconfigurerequest
.height
: client
->area
.height
;
829 client
->frame
->size
.left
+ client
->frame
->size
.right
;
831 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
832 /* make this rude for size-only changes but not for position
834 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
835 (e
->xconfigurerequest
.value_mask
& CWY
));
837 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
839 if (e
->xconfigurerequest
.value_mask
& CWX
)
841 if (e
->xconfigurerequest
.value_mask
& CWY
)
845 switch (client
->gravity
) {
846 case NorthEastGravity
:
848 corner
= OB_CORNER_TOPRIGHT
;
850 case SouthWestGravity
:
852 corner
= OB_CORNER_BOTTOMLEFT
;
854 case SouthEastGravity
:
855 corner
= OB_CORNER_BOTTOMRIGHT
;
857 default: /* NorthWest, Static, etc */
858 corner
= OB_CORNER_TOPLEFT
;
861 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
865 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
866 switch (e
->xconfigurerequest
.detail
) {
869 /* Apps are so rude. And this is totally disconnected from
870 activation/focus. Bleh. */
871 /*client_lower(client);*/
877 /* Apps are so rude. And this is totally disconnected from
878 activation/focus. Bleh. */
879 /*client_raise(client);*/
885 if (client
->ignore_unmaps
) {
886 client
->ignore_unmaps
--;
889 client_unmanage(client
);
892 client_unmanage(client
);
895 /* this is when the client is first taken captive in the frame */
896 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
899 This event is quite rare and is usually handled in unmapHandler.
900 However, if the window is unmapped when the reparent event occurs,
901 the window manager never sees it because an unmap event is not sent
902 to an already unmapped window.
905 /* we don't want the reparent event, put it back on the stack for the
906 X server to deal with after we unmanage the window */
907 XPutBackEvent(ob_display
, e
);
909 client_unmanage(client
);
912 ob_debug("MapRequest for 0x%lx\n", client
->window
);
913 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
914 does, we don't want it!
915 it can happen now when the window is on
916 another desktop, but we still don't
918 client_activate(client
, FALSE
, TRUE
, CurrentTime
);
921 /* validate cuz we query stuff off the client here */
922 if (!client_validate(client
)) break;
924 if (e
->xclient
.format
!= 32) return;
926 msgtype
= e
->xclient
.message_type
;
927 if (msgtype
== prop_atoms
.wm_change_state
) {
928 /* compress changes into a single change */
929 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
931 /* XXX: it would be nice to compress ALL messages of a
932 type, not just messages in a row without other
933 message types between. */
934 if (ce
.xclient
.message_type
!= msgtype
) {
935 XPutBackEvent(ob_display
, &ce
);
938 e
->xclient
= ce
.xclient
;
940 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
941 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
942 /* compress changes into a single change */
943 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
945 /* XXX: it would be nice to compress ALL messages of a
946 type, not just messages in a row without other
947 message types between. */
948 if (ce
.xclient
.message_type
!= msgtype
) {
949 XPutBackEvent(ob_display
, &ce
);
952 e
->xclient
= ce
.xclient
;
954 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
955 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
956 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
958 } else if (msgtype
== prop_atoms
.net_wm_state
) {
959 /* can't compress these */
960 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
961 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
962 e
->xclient
.data
.l
[0] == 1 ? "Add" :
963 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
964 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
966 client_set_state(client
, e
->xclient
.data
.l
[0],
967 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
968 } else if (msgtype
== prop_atoms
.net_close_window
) {
969 ob_debug("net_close_window for 0x%lx\n", client
->window
);
970 client_close(client
);
971 } else if (msgtype
== prop_atoms
.net_active_window
) {
972 ob_debug("net_active_window for 0x%lx source=%s\n",
974 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
975 (e
->xclient
.data
.l
[0] == 1 ? "application" :
976 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
977 /* XXX make use of data.l[1] and [2] ! */
978 client_activate(client
, FALSE
,
979 (e
->xclient
.data
.l
[0] == 0 ||
980 e
->xclient
.data
.l
[0] == 2),
981 e
->xclient
.data
.l
[1]);
982 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
983 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
984 client
->window
, e
->xclient
.data
.l
[2]);
985 if ((Atom
)e
->xclient
.data
.l
[2] ==
986 prop_atoms
.net_wm_moveresize_size_topleft
||
987 (Atom
)e
->xclient
.data
.l
[2] ==
988 prop_atoms
.net_wm_moveresize_size_top
||
989 (Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_size_topright
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_size_right
||
993 (Atom
)e
->xclient
.data
.l
[2] ==
994 prop_atoms
.net_wm_moveresize_size_right
||
995 (Atom
)e
->xclient
.data
.l
[2] ==
996 prop_atoms
.net_wm_moveresize_size_bottomright
||
997 (Atom
)e
->xclient
.data
.l
[2] ==
998 prop_atoms
.net_wm_moveresize_size_bottom
||
999 (Atom
)e
->xclient
.data
.l
[2] ==
1000 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1001 (Atom
)e
->xclient
.data
.l
[2] ==
1002 prop_atoms
.net_wm_moveresize_size_left
||
1003 (Atom
)e
->xclient
.data
.l
[2] ==
1004 prop_atoms
.net_wm_moveresize_move
||
1005 (Atom
)e
->xclient
.data
.l
[2] ==
1006 prop_atoms
.net_wm_moveresize_size_keyboard
||
1007 (Atom
)e
->xclient
.data
.l
[2] ==
1008 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1010 moveresize_start(client
, e
->xclient
.data
.l
[0],
1011 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1012 e
->xclient
.data
.l
[2]);
1014 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1015 prop_atoms
.net_wm_moveresize_cancel
)
1016 moveresize_end(TRUE
);
1017 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1018 gint oldg
= client
->gravity
;
1019 gint tmpg
, x
, y
, w
, h
;
1021 if (e
->xclient
.data
.l
[0] & 0xff)
1022 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1026 if (e
->xclient
.data
.l
[0] & 1 << 8)
1027 x
= e
->xclient
.data
.l
[1];
1030 if (e
->xclient
.data
.l
[0] & 1 << 9)
1031 y
= e
->xclient
.data
.l
[2];
1034 if (e
->xclient
.data
.l
[0] & 1 << 10)
1035 w
= e
->xclient
.data
.l
[3];
1037 w
= client
->area
.width
;
1038 if (e
->xclient
.data
.l
[0] & 1 << 11)
1039 h
= e
->xclient
.data
.l
[4];
1041 h
= client
->area
.height
;
1042 client
->gravity
= tmpg
;
1048 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1050 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1051 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1052 client_normal(client
));
1053 if (e
->xclient
.data
.l
[0] & 1 << 8)
1055 if (e
->xclient
.data
.l
[0] & 1 << 9)
1059 client_configure(client
, OB_CORNER_TOPLEFT
,
1060 x
, y
, w
, h
, FALSE
, TRUE
);
1062 client
->gravity
= oldg
;
1065 case PropertyNotify
:
1066 /* validate cuz we query stuff off the client here */
1067 if (!client_validate(client
)) break;
1069 /* compress changes to a single property into a single change */
1070 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1074 /* XXX: it would be nice to compress ALL changes to a property,
1075 not just changes in a row without other props between. */
1077 a
= ce
.xproperty
.atom
;
1078 b
= e
->xproperty
.atom
;
1082 if ((a
== prop_atoms
.net_wm_name
||
1083 a
== prop_atoms
.wm_name
||
1084 a
== prop_atoms
.net_wm_icon_name
||
1085 a
== prop_atoms
.wm_icon_name
)
1087 (b
== prop_atoms
.net_wm_name
||
1088 b
== prop_atoms
.wm_name
||
1089 b
== prop_atoms
.net_wm_icon_name
||
1090 b
== prop_atoms
.wm_icon_name
)) {
1093 if (a
== prop_atoms
.net_wm_icon
&&
1094 b
== prop_atoms
.net_wm_icon
)
1097 XPutBackEvent(ob_display
, &ce
);
1101 msgtype
= e
->xproperty
.atom
;
1102 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1103 client_update_normal_hints(client
);
1104 /* normal hints can make a window non-resizable */
1105 client_setup_decor_and_functions(client
);
1106 } else if (msgtype
== XA_WM_HINTS
) {
1107 client_update_wmhints(client
);
1108 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1109 client_update_transient_for(client
);
1110 client_get_type(client
);
1111 /* type may have changed, so update the layer */
1112 client_calc_layer(client
);
1113 client_setup_decor_and_functions(client
);
1114 } else if (msgtype
== prop_atoms
.net_wm_name
||
1115 msgtype
== prop_atoms
.wm_name
||
1116 msgtype
== prop_atoms
.net_wm_icon_name
||
1117 msgtype
== prop_atoms
.wm_icon_name
) {
1118 client_update_title(client
);
1119 } else if (msgtype
== prop_atoms
.wm_class
) {
1120 client_update_class(client
);
1121 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1122 client_update_protocols(client
);
1123 client_setup_decor_and_functions(client
);
1125 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1126 client_update_strut(client
);
1128 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1129 client_update_icons(client
);
1131 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1132 client_update_user_time(client
, TRUE
);
1134 else if (msgtype
== prop_atoms
.sm_client_id
) {
1135 client_update_sm_client_id(client
);
1140 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1141 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1142 frame_adjust_shape(client
->frame
);
1148 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1152 if (e
->xbutton
.button
== 1)
1153 stacking_raise(DOCK_AS_WINDOW(s
));
1154 else if (e
->xbutton
.button
== 2)
1155 stacking_lower(DOCK_AS_WINDOW(s
));
1166 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1170 dock_app_drag(app
, &e
->xmotion
);
1173 if (app
->ignore_unmaps
) {
1174 app
->ignore_unmaps
--;
1177 dock_remove(app
, TRUE
);
1180 dock_remove(app
, FALSE
);
1182 case ReparentNotify
:
1183 dock_remove(app
, FALSE
);
1185 case ConfigureNotify
:
1186 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1191 ObMenuFrame
* find_active_menu()
1194 ObMenuFrame
*ret
= NULL
;
1196 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1205 ObMenuFrame
* find_active_or_last_menu()
1207 ObMenuFrame
*ret
= NULL
;
1209 ret
= find_active_menu();
1210 if (!ret
&& menu_frame_visible
)
1211 ret
= menu_frame_visible
->data
;
1215 static void event_handle_menu(XEvent
*ev
)
1218 ObMenuEntryFrame
*e
;
1222 if (menu_can_hide
) {
1223 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1224 ev
->xbutton
.y_root
)))
1225 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1228 menu_frame_hide_all();
1232 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1233 ev
->xmotion
.y_root
))) {
1234 menu_frame_move_on_screen(f
);
1235 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1236 ev
->xmotion
.y_root
)))
1237 menu_frame_select(f
, e
);
1242 a
= find_active_menu();
1244 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1246 menu_frame_select(a
, NULL
);
1251 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1252 menu_frame_hide_all();
1253 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1255 if ((f
= find_active_menu()))
1256 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1258 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1260 if ((f
= find_active_or_last_menu()) && f
->parent
)
1261 menu_frame_select(f
, NULL
);
1262 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1264 if ((f
= find_active_or_last_menu()) && f
->child
)
1265 menu_frame_select_next(f
->child
);
1266 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1268 if ((f
= find_active_or_last_menu()))
1269 menu_frame_select_previous(f
);
1270 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1272 if ((f
= find_active_or_last_menu()))
1273 menu_frame_select_next(f
);
1279 static gboolean
menu_hide_delay_func(gpointer data
)
1281 menu_can_hide
= TRUE
;
1282 return FALSE
; /* no repeat */
1285 static gboolean
focus_delay_func(gpointer data
)
1289 if (focus_client
!= c
) {
1291 if (config_focus_raise
)
1294 return FALSE
; /* no repeat */
1297 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1299 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1303 static void event_client_dest(ObClient
*client
, gpointer data
)
1305 if (client
== focus_hilite
)
1306 focus_hilite
= NULL
;
1309 void event_halt_focus_delay()
1311 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1314 void event_ignore_queued_enters()
1316 GSList
*saved
= NULL
, *it
;
1319 XSync(ob_display
, FALSE
);
1321 /* count the events */
1323 e
= g_new(XEvent
, 1);
1324 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1327 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1328 if (win
&& WINDOW_IS_CLIENT(win
))
1329 ++ignore_enter_focus
;
1331 saved
= g_slist_append(saved
, e
);
1337 /* put the events back */
1338 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1339 XPutBackEvent(ob_display
, it
->data
);
1342 g_slist_free(saved
);