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
;
315 Window win
= e
->xany
.window
;
317 if (e
->type
== FocusIn
) {
319 /* These are ones we never want.. */
321 /* This means focus was given by a keyboard/mouse grab. */
322 if (mode
== NotifyGrab
)
324 /* This means focus was given back from a keyboard/mouse grab. */
325 if (mode
== NotifyUngrab
)
328 /* These are the ones we want.. */
330 if (win
== RootWindow(ob_display
, ob_screen
)) {
331 /* This means focus reverted off of a client */
332 if (detail
== NotifyPointerRoot
|| detail
== NotifyDetailNone
)
338 /* This means focus moved from the root window to a client */
339 if (detail
== NotifyVirtual
)
341 /* This means focus moved from one client to another */
342 if (detail
== NotifyNonlinearVirtual
)
345 /* This means focus reverted off of a client */
346 if (detail
== NotifyInferior
)
352 g_assert(e
->type
== FocusOut
);
355 /* These are ones we never want.. */
357 /* This means focus was taken by a keyboard/mouse grab. */
358 if (mode
== NotifyGrab
)
361 /* Focus left the root window revertedto state */
362 if (win
== RootWindow(ob_display
, ob_screen
))
365 /* These are the ones we want.. */
367 /* This means focus moved from a client to the root window */
368 if (detail
== NotifyVirtual
)
370 /* This means focus moved from one client to another */
371 if (detail
== NotifyNonlinearVirtual
)
379 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
381 return e
->type
== FocusIn
&& wanted_focusevent(e
);
384 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
389 if (e
->xcrossing
.detail
== NotifyInferior
)
394 /* I don't think this should ever happen with our event masks, but
395 if it does, we don't want it. */
398 if (!wanted_focusevent(e
))
405 static void event_process(const XEvent
*ec
, gpointer data
)
408 ObGroup
*group
= NULL
;
409 ObClient
*client
= NULL
;
411 ObDockApp
*dockapp
= NULL
;
412 ObWindow
*obwin
= NULL
;
414 ObEventData
*ed
= data
;
416 /* make a copy we can mangle */
420 window
= event_get_window(e
);
421 if (!(e
->type
== PropertyNotify
&&
422 (group
= g_hash_table_lookup(group_map
, &window
))))
423 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
424 switch (obwin
->type
) {
426 dock
= WINDOW_AS_DOCK(obwin
);
429 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
432 client
= WINDOW_AS_CLIENT(obwin
);
435 case Window_Internal
:
436 /* not to be used for events */
437 g_assert_not_reached();
442 #if 1 /* focus debugging stuff */
443 if (e
->type
== FocusIn
|| e
->type
== FocusOut
) {
444 gint mode
= e
->xfocus
.mode
;
445 gint detail
= e
->xfocus
.detail
;
446 Window window
= e
->xfocus
.window
;
447 if (detail
== NotifyVirtual
) {
448 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
449 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
452 else if (detail
== NotifyNonlinearVirtual
) {
453 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
454 (e
->type
== FocusIn
? "IN" : "OUT"), window
);
458 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
459 (e
->type
== FocusIn
? "IN" : "OUT"),
460 detail
, mode
, window
);
464 event_set_curtime(e
);
466 if (event_ignore(e
, client
)) {
473 /* deal with it in the kernel */
475 event_handle_group(group
, e
);
477 event_handle_client(client
, e
);
479 event_handle_dockapp(dockapp
, e
);
481 event_handle_dock(dock
, e
);
482 else if (window
== RootWindow(ob_display
, ob_screen
))
483 event_handle_root(e
);
484 else if (e
->type
== MapRequest
)
485 client_manage(window
);
486 else if (e
->type
== ConfigureRequest
) {
487 /* unhandled configure requests must be used to configure the
491 xwc
.x
= e
->xconfigurerequest
.x
;
492 xwc
.y
= e
->xconfigurerequest
.y
;
493 xwc
.width
= e
->xconfigurerequest
.width
;
494 xwc
.height
= e
->xconfigurerequest
.height
;
495 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
496 xwc
.sibling
= e
->xconfigurerequest
.above
;
497 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
499 /* we are not to be held responsible if someone sends us an
501 xerror_set_ignore(TRUE
);
502 XConfigureWindow(ob_display
, window
,
503 e
->xconfigurerequest
.value_mask
, &xwc
);
504 xerror_set_ignore(FALSE
);
507 /* user input (action-bound) events */
508 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
509 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
510 e
->type
== KeyRelease
)
512 if (menu_frame_visible
)
513 event_handle_menu(e
);
515 if (!keyboard_process_interactive_grab(e
, &client
)) {
516 if (moveresize_in_progress
) {
519 /* make further actions work on the client being
521 client
= moveresize_client
;
524 menu_can_hide
= FALSE
;
525 ob_main_loop_timeout_add(ob_main_loop
,
526 config_menu_hide_delay
* 1000,
527 menu_hide_delay_func
,
530 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
531 e
->type
== MotionNotify
)
532 mouse_event(client
, e
);
533 else if (e
->type
== KeyPress
) {
534 keyboard_event((focus_cycle_target
? focus_cycle_target
:
535 (focus_hilite
? focus_hilite
: client
)),
541 /* if something happens and it's not from an XEvent, then we don't know
543 event_curtime
= CurrentTime
;
546 static void event_handle_root(XEvent
*e
)
552 ob_debug("Another WM has requested to replace us. Exiting.\n");
557 if (e
->xclient
.format
!= 32) break;
559 msgtype
= e
->xclient
.message_type
;
560 if (msgtype
== prop_atoms
.net_current_desktop
) {
561 guint d
= e
->xclient
.data
.l
[0];
562 event_curtime
= e
->xclient
.data
.l
[1];
563 if (d
< screen_num_desktops
)
564 screen_set_desktop(d
);
565 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
566 guint d
= e
->xclient
.data
.l
[0];
568 screen_set_num_desktops(d
);
569 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
570 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
571 } else if (msgtype
== prop_atoms
.ob_control
) {
572 if (e
->xclient
.data
.l
[0] == 1)
574 else if (e
->xclient
.data
.l
[0] == 2)
579 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
580 screen_update_desktop_names();
581 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
582 screen_update_layout();
584 case ConfigureNotify
:
586 XRRUpdateConfiguration(e
);
595 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
599 g_assert(e
->type
== PropertyNotify
);
601 for (it
= group
->members
; it
; it
= g_slist_next(it
))
602 event_handle_client(it
->data
, e
);
605 void event_enter_client(ObClient
*client
)
607 g_assert(config_focus_follow
);
609 if (client_normal(client
) && client_can_focus(client
)) {
610 if (config_focus_delay
) {
611 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
612 ob_main_loop_timeout_add(ob_main_loop
,
617 focus_delay_func(client
);
621 static void event_handle_client(ObClient
*client
, XEvent
*e
)
629 case VisibilityNotify
:
630 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
634 /* Wheel buttons don't draw because they are an instant click, so it
635 is a waste of resources to go drawing it. */
636 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
637 con
= frame_context(client
, e
->xbutton
.window
);
638 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
640 case OB_FRAME_CONTEXT_MAXIMIZE
:
641 client
->frame
->max_press
= (e
->type
== ButtonPress
);
642 framerender_frame(client
->frame
);
644 case OB_FRAME_CONTEXT_CLOSE
:
645 client
->frame
->close_press
= (e
->type
== ButtonPress
);
646 framerender_frame(client
->frame
);
648 case OB_FRAME_CONTEXT_ICONIFY
:
649 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
650 framerender_frame(client
->frame
);
652 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
653 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
654 framerender_frame(client
->frame
);
656 case OB_FRAME_CONTEXT_SHADE
:
657 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
658 framerender_frame(client
->frame
);
661 /* nothing changes with clicks for any other contexts */
667 if (client
!= focus_client
) {
668 focus_set_client(client
);
669 frame_adjust_focus(client
->frame
, TRUE
);
670 client_calc_layer(client
);
674 /* Look for the followup FocusIn */
675 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
676 /* There is no FocusIn, this means focus went to a window that
677 is not being managed, or a window on another screen. */
678 ob_debug("Focus went to a black hole !\n");
679 } else if (ce
.xany
.window
== e
->xany
.window
) {
680 /* If focus didn't actually move anywhere, there is nothing to do*/
682 } else if (ce
.xfocus
.detail
== NotifyPointerRoot
||
683 ce
.xfocus
.detail
== NotifyDetailNone
) {
684 ob_debug("Focus went to root\n");
685 /* Focus has been reverted to the root window or nothing, so fall
686 back to something other than the window which just had it. */
687 focus_fallback(FALSE
);
688 } else if (ce
.xfocus
.detail
== NotifyInferior
) {
689 ob_debug("Focus went to parent\n");
690 /* Focus has been reverted to parent, which is our frame window,
691 so fall back to something other than the window which had it. */
692 focus_fallback(FALSE
);
694 /* Focus did move, so process the FocusIn event */
695 ObEventData ed
= { .ignored
= FALSE
};
696 event_process(&ce
, &ed
);
698 /* The FocusIn was ignored, this means it was on a window
699 that isn't a client. */
700 ob_debug("Focus went to an unmanaged window 0x%x !\n",
702 focus_fallback(TRUE
);
706 /* This client is no longer focused, so show that */
708 frame_adjust_focus(client
->frame
, FALSE
);
709 client_calc_layer(client
);
712 con
= frame_context(client
, e
->xcrossing
.window
);
714 case OB_FRAME_CONTEXT_MAXIMIZE
:
715 client
->frame
->max_hover
= FALSE
;
716 frame_adjust_state(client
->frame
);
718 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
719 client
->frame
->desk_hover
= FALSE
;
720 frame_adjust_state(client
->frame
);
722 case OB_FRAME_CONTEXT_SHADE
:
723 client
->frame
->shade_hover
= FALSE
;
724 frame_adjust_state(client
->frame
);
726 case OB_FRAME_CONTEXT_ICONIFY
:
727 client
->frame
->iconify_hover
= FALSE
;
728 frame_adjust_state(client
->frame
);
730 case OB_FRAME_CONTEXT_CLOSE
:
731 client
->frame
->close_hover
= FALSE
;
732 frame_adjust_state(client
->frame
);
734 case OB_FRAME_CONTEXT_FRAME
:
735 if (config_focus_follow
&& config_focus_delay
)
736 ob_main_loop_timeout_remove_data(ob_main_loop
,
746 gboolean nofocus
= FALSE
;
748 if (ignore_enter_focus
) {
749 ignore_enter_focus
--;
753 con
= frame_context(client
, e
->xcrossing
.window
);
755 case OB_FRAME_CONTEXT_MAXIMIZE
:
756 client
->frame
->max_hover
= TRUE
;
757 frame_adjust_state(client
->frame
);
759 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
760 client
->frame
->desk_hover
= TRUE
;
761 frame_adjust_state(client
->frame
);
763 case OB_FRAME_CONTEXT_SHADE
:
764 client
->frame
->shade_hover
= TRUE
;
765 frame_adjust_state(client
->frame
);
767 case OB_FRAME_CONTEXT_ICONIFY
:
768 client
->frame
->iconify_hover
= TRUE
;
769 frame_adjust_state(client
->frame
);
771 case OB_FRAME_CONTEXT_CLOSE
:
772 client
->frame
->close_hover
= TRUE
;
773 frame_adjust_state(client
->frame
);
775 case OB_FRAME_CONTEXT_FRAME
:
776 if (e
->xcrossing
.mode
== NotifyGrab
||
777 e
->xcrossing
.mode
== NotifyUngrab
)
780 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
781 (e
->type
== EnterNotify
? "Enter" : "Leave"),
783 e
->xcrossing
.detail
, client
?client
->window
:0);
787 ob_debug("%sNotify mode %d detail %d on %lx, "
788 "focusing window: %d\n",
789 (e
->type
== EnterNotify
? "Enter" : "Leave"),
791 e
->xcrossing
.detail
, (client
?client
->window
:0),
794 if (!nofocus
&& config_focus_follow
)
795 event_enter_client(client
);
803 case ConfigureRequest
:
805 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
806 ConfigureRequest
, &ce
)) {
808 /* XXX if this causes bad things.. we can compress config req's
809 with the same mask. */
810 e
->xconfigurerequest
.value_mask
|=
811 ce
.xconfigurerequest
.value_mask
;
812 if (ce
.xconfigurerequest
.value_mask
& CWX
)
813 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
814 if (ce
.xconfigurerequest
.value_mask
& CWY
)
815 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
816 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
817 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
818 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
819 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
820 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
821 e
->xconfigurerequest
.border_width
=
822 ce
.xconfigurerequest
.border_width
;
823 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
824 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
827 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
828 if (client
->iconic
|| client
->shaded
) return;
830 /* resize, then move, as specified in the EWMH section 7.7 */
831 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
837 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
838 client
->border_width
= e
->xconfigurerequest
.border_width
;
840 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
841 e
->xconfigurerequest
.x
: client
->area
.x
;
842 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
843 e
->xconfigurerequest
.y
: client
->area
.y
;
844 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
845 e
->xconfigurerequest
.width
: client
->area
.width
;
846 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
847 e
->xconfigurerequest
.height
: client
->area
.height
;
853 client
->frame
->size
.left
+ client
->frame
->size
.right
;
855 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
856 /* make this rude for size-only changes but not for position
858 gboolean moving
= ((e
->xconfigurerequest
.value_mask
& CWX
) ||
859 (e
->xconfigurerequest
.value_mask
& CWY
));
861 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
863 if (e
->xconfigurerequest
.value_mask
& CWX
)
865 if (e
->xconfigurerequest
.value_mask
& CWY
)
869 switch (client
->gravity
) {
870 case NorthEastGravity
:
872 corner
= OB_CORNER_TOPRIGHT
;
874 case SouthWestGravity
:
876 corner
= OB_CORNER_BOTTOMLEFT
;
878 case SouthEastGravity
:
879 corner
= OB_CORNER_BOTTOMRIGHT
;
881 default: /* NorthWest, Static, etc */
882 corner
= OB_CORNER_TOPLEFT
;
885 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
889 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
890 switch (e
->xconfigurerequest
.detail
) {
893 /* Apps are so rude. And this is totally disconnected from
894 activation/focus. Bleh. */
895 /*client_lower(client);*/
901 /* Apps are so rude. And this is totally disconnected from
902 activation/focus. Bleh. */
903 /*client_raise(client);*/
909 ob_debug("UnmapNotify for window 0x%x\n", client
->window
);
910 if (client
->ignore_unmaps
) {
911 client
->ignore_unmaps
--;
914 client_unmanage(client
);
917 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
918 client_unmanage(client
);
921 /* this is when the client is first taken captive in the frame */
922 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
925 This event is quite rare and is usually handled in unmapHandler.
926 However, if the window is unmapped when the reparent event occurs,
927 the window manager never sees it because an unmap event is not sent
928 to an already unmapped window.
931 /* we don't want the reparent event, put it back on the stack for the
932 X server to deal with after we unmanage the window */
933 XPutBackEvent(ob_display
, e
);
935 client_unmanage(client
);
938 ob_debug("MapRequest for 0x%lx\n", client
->window
);
939 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
940 does, we don't want it!
941 it can happen now when the window is on
942 another desktop, but we still don't
944 client_activate(client
, FALSE
, TRUE
);
947 /* validate cuz we query stuff off the client here */
948 if (!client_validate(client
)) break;
950 if (e
->xclient
.format
!= 32) return;
952 msgtype
= e
->xclient
.message_type
;
953 if (msgtype
== prop_atoms
.wm_change_state
) {
954 /* compress changes into a single change */
955 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
957 /* XXX: it would be nice to compress ALL messages of a
958 type, not just messages in a row without other
959 message types between. */
960 if (ce
.xclient
.message_type
!= msgtype
) {
961 XPutBackEvent(ob_display
, &ce
);
964 e
->xclient
= ce
.xclient
;
966 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
967 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
968 /* compress changes into a single change */
969 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
971 /* XXX: it would be nice to compress ALL messages of a
972 type, not just messages in a row without other
973 message types between. */
974 if (ce
.xclient
.message_type
!= msgtype
) {
975 XPutBackEvent(ob_display
, &ce
);
978 e
->xclient
= ce
.xclient
;
980 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
981 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
982 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
984 } else if (msgtype
== prop_atoms
.net_wm_state
) {
985 /* can't compress these */
986 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
987 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
988 e
->xclient
.data
.l
[0] == 1 ? "Add" :
989 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
990 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
992 client_set_state(client
, e
->xclient
.data
.l
[0],
993 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
994 } else if (msgtype
== prop_atoms
.net_close_window
) {
995 ob_debug("net_close_window for 0x%lx\n", client
->window
);
996 client_close(client
);
997 } else if (msgtype
== prop_atoms
.net_active_window
) {
998 ob_debug("net_active_window for 0x%lx source=%s\n",
1000 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
1001 (e
->xclient
.data
.l
[0] == 1 ? "application" :
1002 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
1003 /* XXX make use of data.l[2] ! */
1004 event_curtime
= e
->xclient
.data
.l
[1];
1005 client_activate(client
, FALSE
,
1006 (e
->xclient
.data
.l
[0] == 0 ||
1007 e
->xclient
.data
.l
[0] == 2));
1008 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
1009 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1010 client
->window
, e
->xclient
.data
.l
[2]);
1011 if ((Atom
)e
->xclient
.data
.l
[2] ==
1012 prop_atoms
.net_wm_moveresize_size_topleft
||
1013 (Atom
)e
->xclient
.data
.l
[2] ==
1014 prop_atoms
.net_wm_moveresize_size_top
||
1015 (Atom
)e
->xclient
.data
.l
[2] ==
1016 prop_atoms
.net_wm_moveresize_size_topright
||
1017 (Atom
)e
->xclient
.data
.l
[2] ==
1018 prop_atoms
.net_wm_moveresize_size_right
||
1019 (Atom
)e
->xclient
.data
.l
[2] ==
1020 prop_atoms
.net_wm_moveresize_size_right
||
1021 (Atom
)e
->xclient
.data
.l
[2] ==
1022 prop_atoms
.net_wm_moveresize_size_bottomright
||
1023 (Atom
)e
->xclient
.data
.l
[2] ==
1024 prop_atoms
.net_wm_moveresize_size_bottom
||
1025 (Atom
)e
->xclient
.data
.l
[2] ==
1026 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1027 (Atom
)e
->xclient
.data
.l
[2] ==
1028 prop_atoms
.net_wm_moveresize_size_left
||
1029 (Atom
)e
->xclient
.data
.l
[2] ==
1030 prop_atoms
.net_wm_moveresize_move
||
1031 (Atom
)e
->xclient
.data
.l
[2] ==
1032 prop_atoms
.net_wm_moveresize_size_keyboard
||
1033 (Atom
)e
->xclient
.data
.l
[2] ==
1034 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1036 moveresize_start(client
, e
->xclient
.data
.l
[0],
1037 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1038 e
->xclient
.data
.l
[2]);
1040 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1041 prop_atoms
.net_wm_moveresize_cancel
)
1042 moveresize_end(TRUE
);
1043 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1044 gint oldg
= client
->gravity
;
1045 gint tmpg
, x
, y
, w
, h
;
1047 if (e
->xclient
.data
.l
[0] & 0xff)
1048 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1052 if (e
->xclient
.data
.l
[0] & 1 << 8)
1053 x
= e
->xclient
.data
.l
[1];
1056 if (e
->xclient
.data
.l
[0] & 1 << 9)
1057 y
= e
->xclient
.data
.l
[2];
1060 if (e
->xclient
.data
.l
[0] & 1 << 10)
1061 w
= e
->xclient
.data
.l
[3];
1063 w
= client
->area
.width
;
1064 if (e
->xclient
.data
.l
[0] & 1 << 11)
1065 h
= e
->xclient
.data
.l
[4];
1067 h
= client
->area
.height
;
1068 client
->gravity
= tmpg
;
1074 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1076 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1077 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1078 client_normal(client
));
1079 if (e
->xclient
.data
.l
[0] & 1 << 8)
1081 if (e
->xclient
.data
.l
[0] & 1 << 9)
1085 client_configure(client
, OB_CORNER_TOPLEFT
,
1086 x
, y
, w
, h
, FALSE
, TRUE
);
1088 client
->gravity
= oldg
;
1091 case PropertyNotify
:
1092 /* validate cuz we query stuff off the client here */
1093 if (!client_validate(client
)) break;
1095 /* compress changes to a single property into a single change */
1096 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1100 /* XXX: it would be nice to compress ALL changes to a property,
1101 not just changes in a row without other props between. */
1103 a
= ce
.xproperty
.atom
;
1104 b
= e
->xproperty
.atom
;
1108 if ((a
== prop_atoms
.net_wm_name
||
1109 a
== prop_atoms
.wm_name
||
1110 a
== prop_atoms
.net_wm_icon_name
||
1111 a
== prop_atoms
.wm_icon_name
)
1113 (b
== prop_atoms
.net_wm_name
||
1114 b
== prop_atoms
.wm_name
||
1115 b
== prop_atoms
.net_wm_icon_name
||
1116 b
== prop_atoms
.wm_icon_name
)) {
1119 if (a
== prop_atoms
.net_wm_icon
&&
1120 b
== prop_atoms
.net_wm_icon
)
1123 XPutBackEvent(ob_display
, &ce
);
1127 msgtype
= e
->xproperty
.atom
;
1128 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1129 client_update_normal_hints(client
);
1130 /* normal hints can make a window non-resizable */
1131 client_setup_decor_and_functions(client
);
1132 } else if (msgtype
== XA_WM_HINTS
) {
1133 client_update_wmhints(client
);
1134 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1135 client_update_transient_for(client
);
1136 client_get_type(client
);
1137 /* type may have changed, so update the layer */
1138 client_calc_layer(client
);
1139 client_setup_decor_and_functions(client
);
1140 } else if (msgtype
== prop_atoms
.net_wm_name
||
1141 msgtype
== prop_atoms
.wm_name
||
1142 msgtype
== prop_atoms
.net_wm_icon_name
||
1143 msgtype
== prop_atoms
.wm_icon_name
) {
1144 client_update_title(client
);
1145 } else if (msgtype
== prop_atoms
.wm_class
) {
1146 client_update_class(client
);
1147 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1148 client_update_protocols(client
);
1149 client_setup_decor_and_functions(client
);
1151 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1152 client_update_strut(client
);
1154 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1155 client_update_icons(client
);
1157 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1158 client_update_user_time(client
, TRUE
);
1160 else if (msgtype
== prop_atoms
.sm_client_id
) {
1161 client_update_sm_client_id(client
);
1166 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1167 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1168 frame_adjust_shape(client
->frame
);
1174 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1178 if (e
->xbutton
.button
== 1)
1179 stacking_raise(DOCK_AS_WINDOW(s
));
1180 else if (e
->xbutton
.button
== 2)
1181 stacking_lower(DOCK_AS_WINDOW(s
));
1192 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1196 dock_app_drag(app
, &e
->xmotion
);
1199 if (app
->ignore_unmaps
) {
1200 app
->ignore_unmaps
--;
1203 dock_remove(app
, TRUE
);
1206 dock_remove(app
, FALSE
);
1208 case ReparentNotify
:
1209 dock_remove(app
, FALSE
);
1211 case ConfigureNotify
:
1212 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1217 ObMenuFrame
* find_active_menu()
1220 ObMenuFrame
*ret
= NULL
;
1222 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1231 ObMenuFrame
* find_active_or_last_menu()
1233 ObMenuFrame
*ret
= NULL
;
1235 ret
= find_active_menu();
1236 if (!ret
&& menu_frame_visible
)
1237 ret
= menu_frame_visible
->data
;
1241 static void event_handle_menu(XEvent
*ev
)
1244 ObMenuEntryFrame
*e
;
1248 if (menu_can_hide
) {
1249 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1250 ev
->xbutton
.y_root
)))
1251 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1254 menu_frame_hide_all();
1258 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1259 ev
->xmotion
.y_root
))) {
1260 menu_frame_move_on_screen(f
);
1261 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1262 ev
->xmotion
.y_root
)))
1263 menu_frame_select(f
, e
);
1268 a
= find_active_menu();
1270 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1272 menu_frame_select(a
, NULL
);
1277 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1278 menu_frame_hide_all();
1279 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1281 if ((f
= find_active_menu()))
1282 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
,
1284 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1286 if ((f
= find_active_or_last_menu()) && f
->parent
)
1287 menu_frame_select(f
, NULL
);
1288 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1290 if ((f
= find_active_or_last_menu()) && f
->child
)
1291 menu_frame_select_next(f
->child
);
1292 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1294 if ((f
= find_active_or_last_menu()))
1295 menu_frame_select_previous(f
);
1296 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1298 if ((f
= find_active_or_last_menu()))
1299 menu_frame_select_next(f
);
1305 static gboolean
menu_hide_delay_func(gpointer data
)
1307 menu_can_hide
= TRUE
;
1308 return FALSE
; /* no repeat */
1311 static gboolean
focus_delay_func(gpointer data
)
1315 if (focus_client
!= c
) {
1316 if (client_validate(c
)) {
1318 if (config_focus_raise
)
1322 return FALSE
; /* no repeat */
1325 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1327 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1331 static void event_client_dest(ObClient
*client
, gpointer data
)
1333 if (client
== focus_hilite
)
1334 focus_hilite
= NULL
;
1337 void event_halt_focus_delay()
1339 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1342 void event_ignore_queued_enters()
1344 GSList
*saved
= NULL
, *it
;
1347 XSync(ob_display
, FALSE
);
1349 /* count the events */
1351 e
= g_new(XEvent
, 1);
1352 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1355 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1356 if (win
&& WINDOW_IS_CLIENT(win
))
1357 ++ignore_enter_focus
;
1359 saved
= g_slist_append(saved
, e
);
1365 /* put the events back */
1366 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1367 XPutBackEvent(ob_display
, it
->data
);
1370 g_slist_free(saved
);