1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2003 Ben Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
31 #include "menuframe.h"
35 #include "framerender.h"
37 #include "moveresize.h"
40 #include "extensions.h"
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
55 #include <X11/ICE/ICElib.h>
58 static void event_process(const XEvent
*e
, gpointer data
);
59 static void event_done(gpointer data
);
60 static void event_client_dest(ObClient
*client
, gpointer data
);
61 static void event_handle_root(XEvent
*e
);
62 static void event_handle_menu(XEvent
*e
);
63 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
64 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
65 static void event_handle_client(ObClient
*c
, XEvent
*e
);
66 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
68 static gboolean
focus_delay_func(gpointer data
);
69 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
71 static gboolean
menu_hide_delay_func(gpointer data
);
73 Time event_lasttime
= 0;
75 /*! The value of the mask for the NumLock modifier */
77 /*! The value of the mask for the ScrollLock modifier */
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap
*modmap
;
81 /*! Table of the constant modifier masks */
82 static const gint mask_table
[] = {
83 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
84 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
86 static gint mask_table_size
;
88 static guint ignore_enter_focus
= 0;
90 static gboolean menu_can_hide
;
92 static ObClient
*focus_in
, *focus_out
;
95 static void ice_handler(gint fd
, gpointer conn
)
98 IceProcessMessages(conn
, NULL
, &b
);
101 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
102 IcePointer
*watch_data
)
107 fd
= IceConnectionNumber(conn
);
108 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
110 ob_main_loop_fd_remove(ob_main_loop
, fd
);
116 void event_startup(gboolean reconfig
)
118 if (reconfig
) return;
120 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
122 /* get lock masks that are defined by the display (not constant) */
123 modmap
= XGetModifierMapping(ob_display
);
125 if (modmap
&& modmap
->max_keypermod
> 0) {
127 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
128 /* get the values of the keyboard lock modifiers
129 Note: Caps lock is not retrieved the same way as Scroll and Num
130 lock since it doesn't need to be. */
131 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
132 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
135 for (cnt
= 0; cnt
< size
; ++cnt
) {
136 if (! modmap
->modifiermap
[cnt
]) continue;
138 if (num_lock
== modmap
->modifiermap
[cnt
])
139 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
140 if (scroll_lock
== modmap
->modifiermap
[cnt
])
141 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
145 ob_main_loop_x_add(ob_main_loop
, event_process
, event_done
, NULL
, NULL
);
148 IceAddConnectionWatch(ice_watch
, NULL
);
151 client_add_destructor(focus_delay_client_dest
, NULL
);
152 client_add_destructor(event_client_dest
, NULL
);
155 void event_shutdown(gboolean reconfig
)
157 if (reconfig
) return;
160 IceRemoveConnectionWatch(ice_watch
, NULL
);
163 client_remove_destructor(focus_delay_client_dest
);
164 XFreeModifiermap(modmap
);
167 static Window
event_get_window(XEvent
*e
)
174 window
= RootWindow(ob_display
, ob_screen
);
177 window
= e
->xmap
.window
;
180 window
= e
->xunmap
.window
;
183 window
= e
->xdestroywindow
.window
;
185 case ConfigureRequest
:
186 window
= e
->xconfigurerequest
.window
;
188 case ConfigureNotify
:
189 window
= e
->xconfigure
.window
;
193 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
194 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
196 window
= ((XkbBellNotifyEvent
*)e
)->window
;
202 window
= e
->xany
.window
;
207 static void event_set_lasttime(XEvent
*e
)
211 /* grab the lasttime and hack up the state */
227 t
= e
->xproperty
.time
;
231 t
= e
->xcrossing
.time
;
234 /* if more event types are anticipated, get their timestamp
239 if (t
> event_lasttime
)
243 #define STRIP_MODS(s) \
244 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
245 /* kill off the Button1Mask etc, only want the modifiers */ \
246 s &= (ControlMask | ShiftMask | Mod1Mask | \
247 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
249 static void event_hack_mods(XEvent *e)
257 STRIP_MODS(e
->xbutton
.state
);
260 STRIP_MODS(e
->xkey
.state
);
263 STRIP_MODS(e
->xkey
.state
);
264 /* remove from the state the mask of the modifier being released, if
265 it is a modifier key being released (this is a little ugly..) */
266 kp
= modmap
->modifiermap
;
267 for (i
= 0; i
< mask_table_size
; ++i
) {
268 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
269 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
270 /* remove the mask for it */
271 e
->xkey
.state
&= ~mask_table
[i
];
272 /* cause the first loop to break; */
274 break; /* get outta here! */
281 STRIP_MODS(e
->xmotion
.state
);
282 /* compress events */
285 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
287 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
288 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
295 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
300 if (e
->xcrossing
.detail
== NotifyInferior
)
304 if (e
->xfocus
.detail
> NotifyNonlinearVirtual
)
308 if (e
->xfocus
.detail
> NotifyNonlinearVirtual
)
310 if (e
->xfocus
.detail
== NotifyInferior
||
311 e
->xfocus
.mode
== NotifyGrab
)
318 static void event_client_dest(ObClient
*client
, gpointer data
)
320 if (client
== focus_in
)
322 if (client
== focus_out
)
324 if (client
== focus_hilite
)
328 static void event_done(gpointer data
)
330 static ObClient
*last
= NULL
;
332 /* sometimes focus_hilite can be on an unfocused window, this make sure
333 it loses its focus hilite when focus moves */
335 (focus_in
&& focus_hilite
!= focus_in
) &&
336 (focus_out
&& focus_hilite
!= focus_out
))
338 frame_adjust_focus(focus_hilite
->frame
, FALSE
);
342 if (focus_in
!= focus_client
) {
343 focus_set_client(focus_in
);
344 frame_adjust_focus(focus_in
->frame
, TRUE
);
345 client_calc_layer(focus_in
);
348 focus_hilite
= focus_in
;
351 if (focus_out
== focus_client
)
352 focus_set_client(NULL
);
353 frame_adjust_focus(focus_out
->frame
, FALSE
);
354 client_calc_layer(focus_out
);
360 if (focus_client
!= last
) {
365 /* is focus anywhere valid? */
366 XGetInputFocus(ob_display
, &w
, &r
);
369 ob_debug("Focus was found on 0x%x revert %d\n", w
, r
);
372 if (!w
|| w
== PointerRoot
)
373 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
378 focus_in
= focus_out
= NULL
;
381 static void event_process(const XEvent
*ec
, gpointer data
)
384 ObGroup
*group
= NULL
;
385 ObClient
*client
= NULL
;
387 ObDockApp
*dockapp
= NULL
;
388 ObWindow
*obwin
= NULL
;
391 /* make a copy we can mangle */
395 window
= event_get_window(e
);
396 if (!(e
->type
== PropertyNotify
&&
397 (group
= g_hash_table_lookup(group_map
, &window
))))
398 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
399 switch (obwin
->type
) {
401 dock
= WINDOW_AS_DOCK(obwin
);
404 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
407 client
= WINDOW_AS_CLIENT(obwin
);
410 case Window_Internal
:
411 /* not to be used for events */
412 g_assert_not_reached();
417 event_set_lasttime(e
);
419 if (event_ignore(e
, client
))
422 /* deal with it in the kernel */
424 event_handle_group(group
, e
);
426 event_handle_client(client
, e
);
428 event_handle_dockapp(dockapp
, e
);
430 event_handle_dock(dock
, e
);
431 else if (window
== RootWindow(ob_display
, ob_screen
))
432 event_handle_root(e
);
433 else if (e
->type
== MapRequest
)
434 client_manage(window
);
435 else if (e
->type
== ConfigureRequest
) {
436 /* unhandled configure requests must be used to configure the
440 xwc
.x
= e
->xconfigurerequest
.x
;
441 xwc
.y
= e
->xconfigurerequest
.y
;
442 xwc
.width
= e
->xconfigurerequest
.width
;
443 xwc
.height
= e
->xconfigurerequest
.height
;
444 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
445 xwc
.sibling
= e
->xconfigurerequest
.above
;
446 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
448 /* we are not to be held responsible if someone sends us an
450 xerror_set_ignore(TRUE
);
451 XConfigureWindow(ob_display
, window
,
452 e
->xconfigurerequest
.value_mask
, &xwc
);
453 xerror_set_ignore(FALSE
);
456 /* user input (action-bound) events */
457 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
458 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
459 e
->type
== KeyRelease
)
461 if (menu_frame_visible
)
462 event_handle_menu(e
);
464 if (!keyboard_process_interactive_grab(e
, &client
)) {
465 if (moveresize_in_progress
) {
468 /* make further actions work on the client being
470 client
= moveresize_client
;
473 menu_can_hide
= FALSE
;
474 ob_main_loop_timeout_add(ob_main_loop
,
476 menu_hide_delay_func
,
479 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
480 e
->type
== MotionNotify
)
481 mouse_event(client
, e
);
482 else if (e
->type
== KeyPress
)
483 keyboard_event((focus_cycle_target
? focus_cycle_target
:
484 (focus_hilite
? focus_hilite
: client
)),
491 static void event_handle_root(XEvent
*e
)
497 ob_debug("Another WM has requested to replace us. Exiting.\n");
502 if (e
->xclient
.format
!= 32) break;
504 msgtype
= e
->xclient
.message_type
;
505 if (msgtype
== prop_atoms
.net_current_desktop
) {
506 guint d
= e
->xclient
.data
.l
[0];
507 if (d
< screen_num_desktops
)
508 screen_set_desktop(d
);
509 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
510 guint d
= e
->xclient
.data
.l
[0];
512 screen_set_num_desktops(d
);
513 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
514 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
518 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
519 screen_update_desktop_names();
520 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
521 screen_update_layout();
523 case ConfigureNotify
:
525 XRRUpdateConfiguration(e
);
532 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
533 ob_debug("VIDMODE EVENT\n");
539 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
543 g_assert(e
->type
== PropertyNotify
);
545 for (it
= group
->members
; it
; it
= g_slist_next(it
))
546 event_handle_client(it
->data
, e
);
549 void event_enter_client(ObClient
*client
)
551 g_assert(config_focus_follow
);
553 if (client_normal(client
) && client_can_focus(client
)) {
554 if (config_focus_delay
) {
555 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
556 ob_main_loop_timeout_add(ob_main_loop
,
561 focus_delay_func(client
);
565 static void event_handle_client(ObClient
*client
, XEvent
*e
)
573 case VisibilityNotify
:
574 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
578 /* Wheel buttons don't draw because they are an instant click, so it
579 is a waste of resources to go drawing it. */
580 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
581 con
= frame_context(client
, e
->xbutton
.window
);
582 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
584 case OB_FRAME_CONTEXT_MAXIMIZE
:
585 client
->frame
->max_press
= (e
->type
== ButtonPress
);
586 framerender_frame(client
->frame
);
588 case OB_FRAME_CONTEXT_CLOSE
:
589 client
->frame
->close_press
= (e
->type
== ButtonPress
);
590 framerender_frame(client
->frame
);
592 case OB_FRAME_CONTEXT_ICONIFY
:
593 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
594 framerender_frame(client
->frame
);
596 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
597 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
598 framerender_frame(client
->frame
);
600 case OB_FRAME_CONTEXT_SHADE
:
601 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
602 framerender_frame(client
->frame
);
605 /* nothing changes with clicks for any other contexts */
612 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
613 e
->xfocus
.window
, client
->window
,
614 e
->xfocus
.mode
, e
->xfocus
.detail
);
617 if (focus_out
== client
)
622 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
623 e
->xfocus
.window
, client
->window
,
624 e
->xfocus
.mode
, e
->xfocus
.detail
);
626 if (focus_hilite
== client
|| focus_client
== client
)
628 if (focus_in
== client
)
632 con
= frame_context(client
, e
->xcrossing
.window
);
634 case OB_FRAME_CONTEXT_MAXIMIZE
:
635 client
->frame
->max_hover
= FALSE
;
636 frame_adjust_state(client
->frame
);
638 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
639 client
->frame
->desk_hover
= FALSE
;
640 frame_adjust_state(client
->frame
);
642 case OB_FRAME_CONTEXT_SHADE
:
643 client
->frame
->shade_hover
= FALSE
;
644 frame_adjust_state(client
->frame
);
646 case OB_FRAME_CONTEXT_ICONIFY
:
647 client
->frame
->iconify_hover
= FALSE
;
648 frame_adjust_state(client
->frame
);
650 case OB_FRAME_CONTEXT_CLOSE
:
651 client
->frame
->close_hover
= FALSE
;
652 frame_adjust_state(client
->frame
);
654 case OB_FRAME_CONTEXT_FRAME
:
655 if (config_focus_follow
&& config_focus_delay
)
656 ob_main_loop_timeout_remove_data(ob_main_loop
,
666 gboolean nofocus
= FALSE
;
668 if (ignore_enter_focus
) {
669 ignore_enter_focus
--;
673 con
= frame_context(client
, e
->xcrossing
.window
);
675 case OB_FRAME_CONTEXT_MAXIMIZE
:
676 client
->frame
->max_hover
= TRUE
;
677 frame_adjust_state(client
->frame
);
679 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
680 client
->frame
->desk_hover
= TRUE
;
681 frame_adjust_state(client
->frame
);
683 case OB_FRAME_CONTEXT_SHADE
:
684 client
->frame
->shade_hover
= TRUE
;
685 frame_adjust_state(client
->frame
);
687 case OB_FRAME_CONTEXT_ICONIFY
:
688 client
->frame
->iconify_hover
= TRUE
;
689 frame_adjust_state(client
->frame
);
691 case OB_FRAME_CONTEXT_CLOSE
:
692 client
->frame
->close_hover
= TRUE
;
693 frame_adjust_state(client
->frame
);
695 case OB_FRAME_CONTEXT_FRAME
:
696 if (e
->xcrossing
.mode
== NotifyGrab
||
697 e
->xcrossing
.mode
== NotifyUngrab
)
700 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
701 (e
->type
== EnterNotify
? "Enter" : "Leave"),
703 e
->xcrossing
.detail
, client
?client
->window
:0);
707 ob_debug("%sNotify mode %d detail %d on %lx, "
708 "focusing window: %d\n",
709 (e
->type
== EnterNotify
? "Enter" : "Leave"),
711 e
->xcrossing
.detail
, (client
?client
->window
:0),
714 if (!nofocus
&& config_focus_follow
)
715 event_enter_client(client
);
723 case ConfigureRequest
:
725 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
726 ConfigureRequest
, &ce
)) {
728 /* XXX if this causes bad things.. we can compress config req's
729 with the same mask. */
730 e
->xconfigurerequest
.value_mask
|=
731 ce
.xconfigurerequest
.value_mask
;
732 if (ce
.xconfigurerequest
.value_mask
& CWX
)
733 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
734 if (ce
.xconfigurerequest
.value_mask
& CWY
)
735 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
736 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
737 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
738 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
739 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
740 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
741 e
->xconfigurerequest
.border_width
=
742 ce
.xconfigurerequest
.border_width
;
743 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
744 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
747 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
748 if (client
->iconic
|| client
->shaded
) return;
750 /* resize, then move, as specified in the EWMH section 7.7 */
751 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
757 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
758 client
->border_width
= e
->xconfigurerequest
.border_width
;
760 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
761 e
->xconfigurerequest
.x
: client
->area
.x
;
762 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
763 e
->xconfigurerequest
.y
: client
->area
.y
;
764 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
765 e
->xconfigurerequest
.width
: client
->area
.width
;
766 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
767 e
->xconfigurerequest
.height
: client
->area
.height
;
773 client
->frame
->size
.left
+ client
->frame
->size
.right
;
775 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
776 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
777 client_normal(client
));
778 if (e
->xconfigurerequest
.value_mask
& CWX
)
780 if (e
->xconfigurerequest
.value_mask
& CWY
)
784 switch (client
->gravity
) {
785 case NorthEastGravity
:
787 corner
= OB_CORNER_TOPRIGHT
;
789 case SouthWestGravity
:
791 corner
= OB_CORNER_BOTTOMLEFT
;
793 case SouthEastGravity
:
794 corner
= OB_CORNER_BOTTOMRIGHT
;
796 default: /* NorthWest, Static, etc */
797 corner
= OB_CORNER_TOPLEFT
;
800 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
804 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
805 switch (e
->xconfigurerequest
.detail
) {
808 client_lower(client
);
814 client_raise(client
);
820 if (client
->ignore_unmaps
) {
821 client
->ignore_unmaps
--;
824 client_unmanage(client
);
827 client_unmanage(client
);
830 /* this is when the client is first taken captive in the frame */
831 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
834 This event is quite rare and is usually handled in unmapHandler.
835 However, if the window is unmapped when the reparent event occurs,
836 the window manager never sees it because an unmap event is not sent
837 to an already unmapped window.
840 /* we don't want the reparent event, put it back on the stack for the
841 X server to deal with after we unmanage the window */
842 XPutBackEvent(ob_display
, e
);
844 client_unmanage(client
);
847 ob_debug("MapRequest for 0x%lx\n", client
->window
);
848 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
849 does, we don't want it! */
850 client_activate(client
, FALSE
);
853 /* validate cuz we query stuff off the client here */
854 if (!client_validate(client
)) break;
856 if (e
->xclient
.format
!= 32) return;
858 msgtype
= e
->xclient
.message_type
;
859 if (msgtype
== prop_atoms
.wm_change_state
) {
860 /* compress changes into a single change */
861 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
863 /* XXX: it would be nice to compress ALL messages of a
864 type, not just messages in a row without other
865 message types between. */
866 if (ce
.xclient
.message_type
!= msgtype
) {
867 XPutBackEvent(ob_display
, &ce
);
870 e
->xclient
= ce
.xclient
;
872 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
873 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
874 /* compress changes into a single change */
875 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
877 /* XXX: it would be nice to compress ALL messages of a
878 type, not just messages in a row without other
879 message types between. */
880 if (ce
.xclient
.message_type
!= msgtype
) {
881 XPutBackEvent(ob_display
, &ce
);
884 e
->xclient
= ce
.xclient
;
886 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
887 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
888 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
890 } else if (msgtype
== prop_atoms
.net_wm_state
) {
891 /* can't compress these */
892 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
893 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
894 e
->xclient
.data
.l
[0] == 1 ? "Add" :
895 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
896 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
898 client_set_state(client
, e
->xclient
.data
.l
[0],
899 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
900 } else if (msgtype
== prop_atoms
.net_close_window
) {
901 ob_debug("net_close_window for 0x%lx\n", client
->window
);
902 client_close(client
);
903 } else if (msgtype
== prop_atoms
.net_active_window
) {
904 ob_debug("net_active_window for 0x%lx\n", client
->window
);
905 client_activate(client
, FALSE
);
906 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
907 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
908 if ((Atom
)e
->xclient
.data
.l
[2] ==
909 prop_atoms
.net_wm_moveresize_size_topleft
||
910 (Atom
)e
->xclient
.data
.l
[2] ==
911 prop_atoms
.net_wm_moveresize_size_top
||
912 (Atom
)e
->xclient
.data
.l
[2] ==
913 prop_atoms
.net_wm_moveresize_size_topright
||
914 (Atom
)e
->xclient
.data
.l
[2] ==
915 prop_atoms
.net_wm_moveresize_size_right
||
916 (Atom
)e
->xclient
.data
.l
[2] ==
917 prop_atoms
.net_wm_moveresize_size_right
||
918 (Atom
)e
->xclient
.data
.l
[2] ==
919 prop_atoms
.net_wm_moveresize_size_bottomright
||
920 (Atom
)e
->xclient
.data
.l
[2] ==
921 prop_atoms
.net_wm_moveresize_size_bottom
||
922 (Atom
)e
->xclient
.data
.l
[2] ==
923 prop_atoms
.net_wm_moveresize_size_bottomleft
||
924 (Atom
)e
->xclient
.data
.l
[2] ==
925 prop_atoms
.net_wm_moveresize_size_left
||
926 (Atom
)e
->xclient
.data
.l
[2] ==
927 prop_atoms
.net_wm_moveresize_move
||
928 (Atom
)e
->xclient
.data
.l
[2] ==
929 prop_atoms
.net_wm_moveresize_size_keyboard
||
930 (Atom
)e
->xclient
.data
.l
[2] ==
931 prop_atoms
.net_wm_moveresize_move_keyboard
) {
933 moveresize_start(client
, e
->xclient
.data
.l
[0],
934 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
935 e
->xclient
.data
.l
[2]);
937 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
938 gint oldg
= client
->gravity
;
939 gint tmpg
, x
, y
, w
, h
;
941 if (e
->xclient
.data
.l
[0] & 0xff)
942 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
946 if (e
->xclient
.data
.l
[0] & 1 << 8)
947 x
= e
->xclient
.data
.l
[1];
950 if (e
->xclient
.data
.l
[0] & 1 << 9)
951 y
= e
->xclient
.data
.l
[2];
954 if (e
->xclient
.data
.l
[0] & 1 << 10)
955 w
= e
->xclient
.data
.l
[3];
957 w
= client
->area
.width
;
958 if (e
->xclient
.data
.l
[0] & 1 << 11)
959 h
= e
->xclient
.data
.l
[4];
961 h
= client
->area
.height
;
962 client
->gravity
= tmpg
;
968 client
->frame
->size
.left
+ client
->frame
->size
.right
;
970 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
971 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
972 client_normal(client
));
973 if (e
->xclient
.data
.l
[0] & 1 << 8)
975 if (e
->xclient
.data
.l
[0] & 1 << 9)
979 client_configure(client
, OB_CORNER_TOPLEFT
,
980 x
, y
, w
, h
, FALSE
, TRUE
);
982 client
->gravity
= oldg
;
986 /* validate cuz we query stuff off the client here */
987 if (!client_validate(client
)) break;
989 /* compress changes to a single property into a single change */
990 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
994 /* XXX: it would be nice to compress ALL changes to a property,
995 not just changes in a row without other props between. */
997 a
= ce
.xproperty
.atom
;
998 b
= e
->xproperty
.atom
;
1002 if ((a
== prop_atoms
.net_wm_name
||
1003 a
== prop_atoms
.wm_name
||
1004 a
== prop_atoms
.net_wm_icon_name
||
1005 a
== prop_atoms
.wm_icon_name
)
1007 (b
== prop_atoms
.net_wm_name
||
1008 b
== prop_atoms
.wm_name
||
1009 b
== prop_atoms
.net_wm_icon_name
||
1010 b
== prop_atoms
.wm_icon_name
)) {
1013 if ((a
== prop_atoms
.net_wm_icon
||
1014 a
== prop_atoms
.kwm_win_icon
)
1016 (b
== prop_atoms
.net_wm_icon
||
1017 b
== prop_atoms
.kwm_win_icon
))
1020 XPutBackEvent(ob_display
, &ce
);
1024 msgtype
= e
->xproperty
.atom
;
1025 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1026 client_update_normal_hints(client
);
1027 /* normal hints can make a window non-resizable */
1028 client_setup_decor_and_functions(client
);
1029 } else if (msgtype
== XA_WM_HINTS
) {
1030 client_update_wmhints(client
);
1031 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1032 client_update_transient_for(client
);
1033 client_get_type(client
);
1034 /* type may have changed, so update the layer */
1035 client_calc_layer(client
);
1036 client_setup_decor_and_functions(client
);
1037 } else if (msgtype
== prop_atoms
.net_wm_name
||
1038 msgtype
== prop_atoms
.wm_name
||
1039 msgtype
== prop_atoms
.net_wm_icon_name
||
1040 msgtype
== prop_atoms
.wm_icon_name
) {
1041 client_update_title(client
);
1042 } else if (msgtype
== prop_atoms
.wm_class
) {
1043 client_update_class(client
);
1044 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1045 client_update_protocols(client
);
1046 client_setup_decor_and_functions(client
);
1048 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1049 client_update_strut(client
);
1051 else if (msgtype
== prop_atoms
.net_wm_icon
||
1052 msgtype
== prop_atoms
.kwm_win_icon
) {
1053 client_update_icons(client
);
1055 else if (msgtype
== prop_atoms
.sm_client_id
) {
1056 client_update_sm_client_id(client
);
1061 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1062 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1063 frame_adjust_shape(client
->frame
);
1069 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1073 stacking_raise(DOCK_AS_WINDOW(s
));
1084 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1088 dock_app_drag(app
, &e
->xmotion
);
1091 if (app
->ignore_unmaps
) {
1092 app
->ignore_unmaps
--;
1095 dock_remove(app
, TRUE
);
1098 dock_remove(app
, FALSE
);
1100 case ReparentNotify
:
1101 dock_remove(app
, FALSE
);
1103 case ConfigureNotify
:
1104 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1109 ObMenuFrame
* find_active_menu()
1112 ObMenuFrame
*ret
= NULL
;
1114 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1123 ObMenuFrame
* find_active_or_last_menu()
1125 ObMenuFrame
*ret
= NULL
;
1127 ret
= find_active_menu();
1128 if (!ret
&& menu_frame_visible
)
1129 ret
= menu_frame_visible
->data
;
1133 static void event_handle_menu(XEvent
*ev
)
1136 ObMenuEntryFrame
*e
;
1140 if (menu_can_hide
) {
1141 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1142 ev
->xbutton
.y_root
)))
1143 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1145 menu_frame_hide_all();
1149 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1150 ev
->xmotion
.y_root
))) {
1151 menu_frame_move_on_screen(f
);
1152 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1153 ev
->xmotion
.y_root
)))
1154 menu_frame_select(f
, e
);
1159 a
= find_active_menu();
1161 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1163 menu_frame_select(a
, NULL
);
1168 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1169 menu_frame_hide_all();
1170 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1172 if ((f
= find_active_menu()))
1173 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1174 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1176 if ((f
= find_active_or_last_menu()) && f
->parent
)
1177 menu_frame_select(f
, NULL
);
1178 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1180 if ((f
= find_active_or_last_menu()) && f
->child
)
1181 menu_frame_select_next(f
->child
);
1182 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1184 if ((f
= find_active_or_last_menu()))
1185 menu_frame_select_previous(f
);
1186 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1188 if ((f
= find_active_or_last_menu()))
1189 menu_frame_select_next(f
);
1195 static gboolean
menu_hide_delay_func(gpointer data
)
1197 menu_can_hide
= TRUE
;
1198 return FALSE
; /* no repeat */
1201 static gboolean
focus_delay_func(gpointer data
)
1205 if (focus_client
!= c
) {
1207 if (config_focus_raise
)
1210 return FALSE
; /* no repeat */
1213 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1215 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1218 void event_halt_focus_delay()
1220 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1223 void event_ignore_queued_enters()
1225 GSList
*saved
= NULL
, *it
;
1228 XSync(ob_display
, FALSE
);
1230 /* count the events */
1232 e
= g_new(XEvent
, 1);
1233 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1236 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1237 if (win
&& WINDOW_IS_CLIENT(win
))
1238 ++ignore_enter_focus
;
1240 saved
= g_slist_append(saved
, e
);
1246 /* put the events back */
1247 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1248 XPutBackEvent(ob_display
, it
->data
);
1251 g_slist_free(saved
);