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
)
336 /* This means focus reverted off of a client */
337 if (detail
== NotifyPointerRoot
|| detail
== NotifyDetailNone
||
338 detail
== NotifyInferior
)
344 g_assert(e
->type
== FocusOut
);
347 /* These are ones we never want.. */
349 /* This means focus was taken by a keyboard/mouse grab. */
350 if (mode
== NotifyGrab
)
353 /* These are the ones we want.. */
355 /* This means focus moved from a client to the root window */
356 if (detail
== NotifyVirtual
)
358 /* This means focus moved from one client to another */
359 if (detail
== NotifyNonlinearVirtual
)
367 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
369 return e
->type
== FocusIn
&& wanted_focusevent(e
);
372 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
377 if (e
->xcrossing
.detail
== NotifyInferior
)
382 /* I don't think this should ever happen with our event masks, but
383 if it does, we don't want it. */
386 if (!wanted_focusevent(e
))
393 static void event_process(const XEvent
*ec
, gpointer data
)
396 ObGroup
*group
= NULL
;
397 ObClient
*client
= NULL
;
399 ObDockApp
*dockapp
= NULL
;
400 ObWindow
*obwin
= NULL
;
402 ObEventData
*ed
= data
;
404 /* make a copy we can mangle */
408 window
= event_get_window(e
);
409 if (!(e
->type
== PropertyNotify
&&
410 (group
= g_hash_table_lookup(group_map
, &window
))))
411 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
412 switch (obwin
->type
) {
414 dock
= WINDOW_AS_DOCK(obwin
);
417 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
420 client
= WINDOW_AS_CLIENT(obwin
);
423 case Window_Internal
:
424 /* not to be used for events */
425 g_assert_not_reached();
430 #if 1 /* focus debugging stuff */
431 if (e
->type
== FocusIn
|| e
->type
== FocusOut
) {
432 gint mode
= e
->xfocus
.mode
;
433 gint detail
= e
->xfocus
.detail
;
434 Window window
= e
->xfocus
.window
;
435 if (detail
== NotifyVirtual
) {
436 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
437 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
440 else if (detail
== NotifyNonlinearVirtual
) {
441 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
442 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
446 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
447 (e
->type
== FocusIn
? "IN" : "OUT"),
448 detail
, mode
, window
);
452 event_set_curtime(e
);
454 if (event_ignore(e
, client
)) {
461 /* deal with it in the kernel */
463 event_handle_group(group
, e
);
465 event_handle_client(client
, e
);
467 event_handle_dockapp(dockapp
, e
);
469 event_handle_dock(dock
, e
);
470 else if (window
== RootWindow(ob_display
, ob_screen
))
471 event_handle_root(e
);
472 else if (e
->type
== MapRequest
)
473 client_manage(window
);
474 else if (e
->type
== ConfigureRequest
) {
475 /* unhandled configure requests must be used to configure the
479 xwc
.x
= e
->xconfigurerequest
.x
;
480 xwc
.y
= e
->xconfigurerequest
.y
;
481 xwc
.width
= e
->xconfigurerequest
.width
;
482 xwc
.height
= e
->xconfigurerequest
.height
;
483 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
484 xwc
.sibling
= e
->xconfigurerequest
.above
;
485 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
487 /* we are not to be held responsible if someone sends us an
489 xerror_set_ignore(TRUE
);
490 XConfigureWindow(ob_display
, window
,
491 e
->xconfigurerequest
.value_mask
, &xwc
);
492 xerror_set_ignore(FALSE
);
495 /* user input (action-bound) events */
496 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
497 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
498 e
->type
== KeyRelease
)
500 if (menu_frame_visible
)
501 event_handle_menu(e
);
503 if (!keyboard_process_interactive_grab(e
, &client
)) {
504 if (moveresize_in_progress
) {
507 /* make further actions work on the client being
509 client
= moveresize_client
;
512 menu_can_hide
= FALSE
;
513 ob_main_loop_timeout_add(ob_main_loop
,
514 config_menu_hide_delay
* 1000,
515 menu_hide_delay_func
,
518 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
519 e
->type
== MotionNotify
)
520 mouse_event(client
, e
);
521 else if (e
->type
== KeyPress
) {
522 keyboard_event((focus_cycle_target
? focus_cycle_target
:
523 (focus_hilite
? focus_hilite
: client
)),
529 /* if something happens and it's not from an XEvent, then we don't know
531 event_curtime
= CurrentTime
;
534 static void event_handle_root(XEvent
*e
)
540 ob_debug("Another WM has requested to replace us. Exiting.\n");
545 if (e
->xclient
.format
!= 32) break;
547 msgtype
= e
->xclient
.message_type
;
548 if (msgtype
== prop_atoms
.net_current_desktop
) {
549 guint d
= e
->xclient
.data
.l
[0];
550 event_curtime
= e
->xclient
.data
.l
[1];
551 if (d
< screen_num_desktops
)
552 screen_set_desktop(d
);
553 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
554 guint d
= e
->xclient
.data
.l
[0];
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, this means focus went to a window that
665 is not being managed, or a window on another screen. */
666 ob_debug("Focus went to a black hole !\n");
667 } else if (ce
.xany
.window
== e
->xany
.window
) {
668 /* If focus didn't actually move anywhere, there is nothing to do*/
670 } else if (ce
.xfocus
.detail
== NotifyPointerRoot
||
671 ce
.xfocus
.detail
== NotifyDetailNone
) {
672 ob_debug("Focus went to root\n");
673 /* Focus has been reverted to the root window or nothing, so fall
674 back to something other than the window which just had it. */
675 focus_fallback(FALSE
);
676 } else if (ce
.xfocus
.detail
== NotifyInferior
) {
677 ob_debug("Focus went to parent\n");
678 /* Focus has been reverted to parent, which is our frame window,
679 so fall back to something other than the window which had it. */
680 focus_fallback(FALSE
);
682 /* Focus did move, so process the FocusIn event */
683 ObEventData ed
= { .ignored
= FALSE
};
684 event_process(&ce
, &ed
);
686 /* The FocusIn was ignored, this means it was on a window
687 that isn't a client. */
688 ob_debug("Focus went to an unmanaged window 0x%x !\n",
690 focus_fallback(TRUE
);
694 /* This client is no longer focused, so show that */
696 frame_adjust_focus(client
->frame
, FALSE
);
697 client_calc_layer(client
);
700 con
= frame_context(client
, e
->xcrossing
.window
);
702 case OB_FRAME_CONTEXT_MAXIMIZE
:
703 client
->frame
->max_hover
= FALSE
;
704 frame_adjust_state(client
->frame
);
706 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
707 client
->frame
->desk_hover
= FALSE
;
708 frame_adjust_state(client
->frame
);
710 case OB_FRAME_CONTEXT_SHADE
:
711 client
->frame
->shade_hover
= FALSE
;
712 frame_adjust_state(client
->frame
);
714 case OB_FRAME_CONTEXT_ICONIFY
:
715 client
->frame
->iconify_hover
= FALSE
;
716 frame_adjust_state(client
->frame
);
718 case OB_FRAME_CONTEXT_CLOSE
:
719 client
->frame
->close_hover
= FALSE
;
720 frame_adjust_state(client
->frame
);
722 case OB_FRAME_CONTEXT_FRAME
:
723 if (config_focus_follow
&& config_focus_delay
)
724 ob_main_loop_timeout_remove_data(ob_main_loop
,
734 gboolean nofocus
= FALSE
;
736 if (ignore_enter_focus
) {
737 ignore_enter_focus
--;
741 con
= frame_context(client
, e
->xcrossing
.window
);
743 case OB_FRAME_CONTEXT_MAXIMIZE
:
744 client
->frame
->max_hover
= TRUE
;
745 frame_adjust_state(client
->frame
);
747 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
748 client
->frame
->desk_hover
= TRUE
;
749 frame_adjust_state(client
->frame
);
751 case OB_FRAME_CONTEXT_SHADE
:
752 client
->frame
->shade_hover
= TRUE
;
753 frame_adjust_state(client
->frame
);
755 case OB_FRAME_CONTEXT_ICONIFY
:
756 client
->frame
->iconify_hover
= TRUE
;
757 frame_adjust_state(client
->frame
);
759 case OB_FRAME_CONTEXT_CLOSE
:
760 client
->frame
->close_hover
= TRUE
;
761 frame_adjust_state(client
->frame
);
763 case OB_FRAME_CONTEXT_FRAME
:
764 if (e
->xcrossing
.mode
== NotifyGrab
||
765 e
->xcrossing
.mode
== NotifyUngrab
)
768 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
769 (e
->type
== EnterNotify
? "Enter" : "Leave"),
771 e
->xcrossing
.detail
, client
?client
->window
:0);
775 ob_debug("%sNotify mode %d detail %d on %lx, "
776 "focusing window: %d\n",
777 (e
->type
== EnterNotify
? "Enter" : "Leave"),
779 e
->xcrossing
.detail
, (client
?client
->window
:0),
782 if (!nofocus
&& config_focus_follow
)
783 event_enter_client(client
);
791 case ConfigureRequest
:
793 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
794 ConfigureRequest
, &ce
)) {
796 /* XXX if this causes bad things.. we can compress config req's
797 with the same mask. */
798 e
->xconfigurerequest
.value_mask
|=
799 ce
.xconfigurerequest
.value_mask
;
800 if (ce
.xconfigurerequest
.value_mask
& CWX
)
801 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
802 if (ce
.xconfigurerequest
.value_mask
& CWY
)
803 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
804 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
805 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
806 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
807 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
808 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
809 e
->xconfigurerequest
.border_width
=
810 ce
.xconfigurerequest
.border_width
;
811 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
812 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
815 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
816 if (client
->iconic
|| client
->shaded
) return;
818 /* resize, then move, as specified in the EWMH section 7.7 */
819 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
825 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
826 client
->border_width
= e
->xconfigurerequest
.border_width
;
828 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
829 e
->xconfigurerequest
.x
: client
->area
.x
;
830 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
831 e
->xconfigurerequest
.y
: client
->area
.y
;
832 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
833 e
->xconfigurerequest
.width
: client
->area
.width
;
834 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
835 e
->xconfigurerequest
.height
: client
->area
.height
;
841 client
->frame
->size
.left
+ client
->frame
->size
.right
;
843 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
844 /* make this rude for size-only changes but not for position
846 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
847 (e
->xconfigurerequest
.value_mask
& CWY
));
849 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
851 if (e
->xconfigurerequest
.value_mask
& CWX
)
853 if (e
->xconfigurerequest
.value_mask
& CWY
)
857 switch (client
->gravity
) {
858 case NorthEastGravity
:
860 corner
= OB_CORNER_TOPRIGHT
;
862 case SouthWestGravity
:
864 corner
= OB_CORNER_BOTTOMLEFT
;
866 case SouthEastGravity
:
867 corner
= OB_CORNER_BOTTOMRIGHT
;
869 default: /* NorthWest, Static, etc */
870 corner
= OB_CORNER_TOPLEFT
;
873 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
877 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
878 switch (e
->xconfigurerequest
.detail
) {
881 /* Apps are so rude. And this is totally disconnected from
882 activation/focus. Bleh. */
883 /*client_lower(client);*/
889 /* Apps are so rude. And this is totally disconnected from
890 activation/focus. Bleh. */
891 /*client_raise(client);*/
897 ob_debug("UnmapNotify for window 0x%x\n", client
->window
);
898 if (client
->ignore_unmaps
) {
899 client
->ignore_unmaps
--;
902 client_unmanage(client
);
905 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
906 client_unmanage(client
);
909 /* this is when the client is first taken captive in the frame */
910 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
913 This event is quite rare and is usually handled in unmapHandler.
914 However, if the window is unmapped when the reparent event occurs,
915 the window manager never sees it because an unmap event is not sent
916 to an already unmapped window.
919 /* we don't want the reparent event, put it back on the stack for the
920 X server to deal with after we unmanage the window */
921 XPutBackEvent(ob_display
, e
);
923 client_unmanage(client
);
926 ob_debug("MapRequest for 0x%lx\n", client
->window
);
927 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
928 does, we don't want it!
929 it can happen now when the window is on
930 another desktop, but we still don't
932 client_activate(client
, FALSE
, TRUE
);
935 /* validate cuz we query stuff off the client here */
936 if (!client_validate(client
)) break;
938 if (e
->xclient
.format
!= 32) return;
940 msgtype
= e
->xclient
.message_type
;
941 if (msgtype
== prop_atoms
.wm_change_state
) {
942 /* compress changes into a single change */
943 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
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 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
955 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
956 /* compress changes into a single change */
957 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
959 /* XXX: it would be nice to compress ALL messages of a
960 type, not just messages in a row without other
961 message types between. */
962 if (ce
.xclient
.message_type
!= msgtype
) {
963 XPutBackEvent(ob_display
, &ce
);
966 e
->xclient
= ce
.xclient
;
968 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
969 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
970 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
972 } else if (msgtype
== prop_atoms
.net_wm_state
) {
973 /* can't compress these */
974 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
975 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
976 e
->xclient
.data
.l
[0] == 1 ? "Add" :
977 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
978 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
980 client_set_state(client
, e
->xclient
.data
.l
[0],
981 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
982 } else if (msgtype
== prop_atoms
.net_close_window
) {
983 ob_debug("net_close_window for 0x%lx\n", client
->window
);
984 client_close(client
);
985 } else if (msgtype
== prop_atoms
.net_active_window
) {
986 ob_debug("net_active_window for 0x%lx source=%s\n",
988 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
989 (e
->xclient
.data
.l
[0] == 1 ? "application" :
990 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
991 /* XXX make use of data.l[2] ! */
992 event_curtime
= e
->xclient
.data
.l
[1];
993 client_activate(client
, FALSE
,
994 (e
->xclient
.data
.l
[0] == 0 ||
995 e
->xclient
.data
.l
[0] == 2));
996 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
997 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
998 client
->window
, e
->xclient
.data
.l
[2]);
999 if ((Atom
)e
->xclient
.data
.l
[2] ==
1000 prop_atoms
.net_wm_moveresize_size_topleft
||
1001 (Atom
)e
->xclient
.data
.l
[2] ==
1002 prop_atoms
.net_wm_moveresize_size_top
||
1003 (Atom
)e
->xclient
.data
.l
[2] ==
1004 prop_atoms
.net_wm_moveresize_size_topright
||
1005 (Atom
)e
->xclient
.data
.l
[2] ==
1006 prop_atoms
.net_wm_moveresize_size_right
||
1007 (Atom
)e
->xclient
.data
.l
[2] ==
1008 prop_atoms
.net_wm_moveresize_size_right
||
1009 (Atom
)e
->xclient
.data
.l
[2] ==
1010 prop_atoms
.net_wm_moveresize_size_bottomright
||
1011 (Atom
)e
->xclient
.data
.l
[2] ==
1012 prop_atoms
.net_wm_moveresize_size_bottom
||
1013 (Atom
)e
->xclient
.data
.l
[2] ==
1014 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1015 (Atom
)e
->xclient
.data
.l
[2] ==
1016 prop_atoms
.net_wm_moveresize_size_left
||
1017 (Atom
)e
->xclient
.data
.l
[2] ==
1018 prop_atoms
.net_wm_moveresize_move
||
1019 (Atom
)e
->xclient
.data
.l
[2] ==
1020 prop_atoms
.net_wm_moveresize_size_keyboard
||
1021 (Atom
)e
->xclient
.data
.l
[2] ==
1022 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1024 moveresize_start(client
, e
->xclient
.data
.l
[0],
1025 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1026 e
->xclient
.data
.l
[2]);
1028 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1029 prop_atoms
.net_wm_moveresize_cancel
)
1030 moveresize_end(TRUE
);
1031 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1032 gint oldg
= client
->gravity
;
1033 gint tmpg
, x
, y
, w
, h
;
1035 if (e
->xclient
.data
.l
[0] & 0xff)
1036 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1040 if (e
->xclient
.data
.l
[0] & 1 << 8)
1041 x
= e
->xclient
.data
.l
[1];
1044 if (e
->xclient
.data
.l
[0] & 1 << 9)
1045 y
= e
->xclient
.data
.l
[2];
1048 if (e
->xclient
.data
.l
[0] & 1 << 10)
1049 w
= e
->xclient
.data
.l
[3];
1051 w
= client
->area
.width
;
1052 if (e
->xclient
.data
.l
[0] & 1 << 11)
1053 h
= e
->xclient
.data
.l
[4];
1055 h
= client
->area
.height
;
1056 client
->gravity
= tmpg
;
1062 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1064 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1065 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1066 client_normal(client
));
1067 if (e
->xclient
.data
.l
[0] & 1 << 8)
1069 if (e
->xclient
.data
.l
[0] & 1 << 9)
1073 client_configure(client
, OB_CORNER_TOPLEFT
,
1074 x
, y
, w
, h
, FALSE
, TRUE
);
1076 client
->gravity
= oldg
;
1079 case PropertyNotify
:
1080 /* validate cuz we query stuff off the client here */
1081 if (!client_validate(client
)) break;
1083 /* compress changes to a single property into a single change */
1084 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1088 /* XXX: it would be nice to compress ALL changes to a property,
1089 not just changes in a row without other props between. */
1091 a
= ce
.xproperty
.atom
;
1092 b
= e
->xproperty
.atom
;
1096 if ((a
== prop_atoms
.net_wm_name
||
1097 a
== prop_atoms
.wm_name
||
1098 a
== prop_atoms
.net_wm_icon_name
||
1099 a
== prop_atoms
.wm_icon_name
)
1101 (b
== prop_atoms
.net_wm_name
||
1102 b
== prop_atoms
.wm_name
||
1103 b
== prop_atoms
.net_wm_icon_name
||
1104 b
== prop_atoms
.wm_icon_name
)) {
1107 if (a
== prop_atoms
.net_wm_icon
&&
1108 b
== prop_atoms
.net_wm_icon
)
1111 XPutBackEvent(ob_display
, &ce
);
1115 msgtype
= e
->xproperty
.atom
;
1116 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1117 client_update_normal_hints(client
);
1118 /* normal hints can make a window non-resizable */
1119 client_setup_decor_and_functions(client
);
1120 } else if (msgtype
== XA_WM_HINTS
) {
1121 client_update_wmhints(client
);
1122 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1123 client_update_transient_for(client
);
1124 client_get_type(client
);
1125 /* type may have changed, so update the layer */
1126 client_calc_layer(client
);
1127 client_setup_decor_and_functions(client
);
1128 } else if (msgtype
== prop_atoms
.net_wm_name
||
1129 msgtype
== prop_atoms
.wm_name
||
1130 msgtype
== prop_atoms
.net_wm_icon_name
||
1131 msgtype
== prop_atoms
.wm_icon_name
) {
1132 client_update_title(client
);
1133 } else if (msgtype
== prop_atoms
.wm_class
) {
1134 client_update_class(client
);
1135 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1136 client_update_protocols(client
);
1137 client_setup_decor_and_functions(client
);
1139 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1140 client_update_strut(client
);
1142 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1143 client_update_icons(client
);
1145 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1146 client_update_user_time(client
, TRUE
);
1148 else if (msgtype
== prop_atoms
.sm_client_id
) {
1149 client_update_sm_client_id(client
);
1154 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1155 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1156 frame_adjust_shape(client
->frame
);
1162 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1166 if (e
->xbutton
.button
== 1)
1167 stacking_raise(DOCK_AS_WINDOW(s
));
1168 else if (e
->xbutton
.button
== 2)
1169 stacking_lower(DOCK_AS_WINDOW(s
));
1180 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1184 dock_app_drag(app
, &e
->xmotion
);
1187 if (app
->ignore_unmaps
) {
1188 app
->ignore_unmaps
--;
1191 dock_remove(app
, TRUE
);
1194 dock_remove(app
, FALSE
);
1196 case ReparentNotify
:
1197 dock_remove(app
, FALSE
);
1199 case ConfigureNotify
:
1200 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1205 ObMenuFrame
* find_active_menu()
1208 ObMenuFrame
*ret
= NULL
;
1210 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1219 ObMenuFrame
* find_active_or_last_menu()
1221 ObMenuFrame
*ret
= NULL
;
1223 ret
= find_active_menu();
1224 if (!ret
&& menu_frame_visible
)
1225 ret
= menu_frame_visible
->data
;
1229 static void event_handle_menu(XEvent
*ev
)
1232 ObMenuEntryFrame
*e
;
1236 if (menu_can_hide
) {
1237 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1238 ev
->xbutton
.y_root
)))
1239 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1242 menu_frame_hide_all();
1246 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1247 ev
->xmotion
.y_root
))) {
1248 menu_frame_move_on_screen(f
);
1249 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1250 ev
->xmotion
.y_root
)))
1251 menu_frame_select(f
, e
);
1256 a
= find_active_menu();
1258 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1260 menu_frame_select(a
, NULL
);
1265 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1266 menu_frame_hide_all();
1267 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1269 if ((f
= find_active_menu()))
1270 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1272 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1274 if ((f
= find_active_or_last_menu()) && f
->parent
)
1275 menu_frame_select(f
, NULL
);
1276 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1278 if ((f
= find_active_or_last_menu()) && f
->child
)
1279 menu_frame_select_next(f
->child
);
1280 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1282 if ((f
= find_active_or_last_menu()))
1283 menu_frame_select_previous(f
);
1284 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1286 if ((f
= find_active_or_last_menu()))
1287 menu_frame_select_next(f
);
1293 static gboolean
menu_hide_delay_func(gpointer data
)
1295 menu_can_hide
= TRUE
;
1296 return FALSE
; /* no repeat */
1299 static gboolean
focus_delay_func(gpointer data
)
1303 if (focus_client
!= c
) {
1304 if (client_validate(c
)) {
1306 if (config_focus_raise
)
1310 return FALSE
; /* no repeat */
1313 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1315 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1319 static void event_client_dest(ObClient
*client
, gpointer data
)
1321 if (client
== focus_hilite
)
1322 focus_hilite
= NULL
;
1325 void event_halt_focus_delay()
1327 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1330 void event_ignore_queued_enters()
1332 GSList
*saved
= NULL
, *it
;
1335 XSync(ob_display
, FALSE
);
1337 /* count the events */
1339 e
= g_new(XEvent
, 1);
1340 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1343 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1344 if (win
&& WINDOW_IS_CLIENT(win
))
1345 ++ignore_enter_focus
;
1347 saved
= g_slist_append(saved
, e
);
1353 /* put the events back */
1354 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1355 XPutBackEvent(ob_display
, it
->data
);
1358 g_slist_free(saved
);