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>
63 static void event_process(const XEvent
*e
, gpointer data
);
64 static void event_handle_root(XEvent
*e
);
65 static void event_handle_menu(XEvent
*e
);
66 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
67 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
68 static void event_handle_client(ObClient
*c
, XEvent
*e
);
69 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
71 static gboolean
focus_delay_func(gpointer data
);
72 static void focus_delay_client_dest(gpointer data
);
74 static gboolean
menu_hide_delay_func(gpointer data
);
76 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
77 (e)->xfocus.detail == NotifyAncestor || \
78 (e)->xfocus.detail > NotifyNonlinearVirtual)
79 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
80 (e)->xfocus.detail == NotifyInferior || \
81 (e)->xfocus.detail == NotifyAncestor || \
82 (e)->xfocus.detail > NotifyNonlinearVirtual)
84 Time event_lasttime
= 0;
86 /*! The value of the mask for the NumLock modifier */
87 unsigned int NumLockMask
;
88 /*! The value of the mask for the ScrollLock modifier */
89 unsigned int ScrollLockMask
;
90 /*! The key codes for the modifier keys */
91 static XModifierKeymap
*modmap
;
92 /*! Table of the constant modifier masks */
93 static const int mask_table
[] = {
94 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
95 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
97 static int mask_table_size
;
99 static guint ignore_enter_focus
= 0;
101 static gboolean menu_can_hide
;
104 static void ice_handler(int fd
, gpointer conn
)
107 IceProcessMessages(conn
, NULL
, &b
);
110 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
111 IcePointer
*watch_data
)
116 fd
= IceConnectionNumber(conn
);
117 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
119 ob_main_loop_fd_remove(ob_main_loop
, fd
);
125 void event_startup(gboolean reconfig
)
127 if (reconfig
) return;
129 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
131 /* get lock masks that are defined by the display (not constant) */
132 modmap
= XGetModifierMapping(ob_display
);
134 if (modmap
&& modmap
->max_keypermod
> 0) {
136 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
137 /* get the values of the keyboard lock modifiers
138 Note: Caps lock is not retrieved the same way as Scroll and Num
139 lock since it doesn't need to be. */
140 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
141 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
144 for (cnt
= 0; cnt
< size
; ++cnt
) {
145 if (! modmap
->modifiermap
[cnt
]) continue;
147 if (num_lock
== modmap
->modifiermap
[cnt
])
148 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
149 if (scroll_lock
== modmap
->modifiermap
[cnt
])
150 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
154 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
157 IceAddConnectionWatch(ice_watch
, NULL
);
160 client_add_destructor(focus_delay_client_dest
);
163 void event_shutdown(gboolean reconfig
)
165 if (reconfig
) return;
168 IceRemoveConnectionWatch(ice_watch
, NULL
);
171 client_remove_destructor(focus_delay_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_lasttime(XEvent
*e
)
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
247 if (t
> event_lasttime
)
251 #define STRIP_MODS(s) \
252 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
253 /* kill off the Button1Mask etc, only want the modifiers */ \
254 s &= (ControlMask | ShiftMask | Mod1Mask | \
255 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
257 static void event_hack_mods(XEvent *e)
265 STRIP_MODS(e
->xbutton
.state
);
268 STRIP_MODS(e
->xkey
.state
);
271 STRIP_MODS(e
->xkey
.state
);
272 /* remove from the state the mask of the modifier being released, if
273 it is a modifier key being released (this is a little ugly..) */
274 kp
= modmap
->modifiermap
;
275 for (i
= 0; i
< mask_table_size
; ++i
) {
276 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
277 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
278 /* remove the mask for it */
279 e
->xkey
.state
&= ~mask_table
[i
];
280 /* cause the first loop to break; */
282 break; /* get outta here! */
289 STRIP_MODS(e
->xmotion
.state
);
290 /* compress events */
293 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
295 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
296 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
303 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
308 if (e
->xcrossing
.detail
== NotifyInferior
)
312 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
313 because of RevertToPointerRoot. If the focus ends up reverting to
314 pointer root on a workspace change, then the FocusIn event that we
315 want will be of type NotifyAncestor. This situation does not occur
316 for FocusOut, so it is safely ignored there.
318 if (INVALID_FOCUSIN(e
) ||
321 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
322 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
324 /* says a client was not found for the event (or a valid FocusIn
327 e
->xfocus
.window
= None
;
332 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
333 e
->xfocus
.mode
, e
->xfocus
.detail
);
337 if (INVALID_FOCUSOUT(e
)) {
339 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
340 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
346 ob_debug("FocusOut on %lx mode %d detail %d\n",
347 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
352 gboolean fallback
= TRUE
;
355 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
357 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
359 if (fe
.type
== FocusOut
) {
361 ob_debug("found pending FocusOut\n");
363 if (!INVALID_FOCUSOUT(&fe
)) {
364 /* if there is a VALID FocusOut still coming, don't
365 fallback focus yet, we'll deal with it then */
366 XPutBackEvent(ob_display
, &fe
);
372 ob_debug("found pending FocusIn\n");
374 /* is the focused window getting a FocusOut/In back to
377 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
378 !event_ignore(&fe
, client
)) {
380 if focus_client is not set, then we can't do
381 this. we need the FocusIn. This happens in the
382 case when the set_focus_client(NULL) in the
383 focus_fallback function fires and then
384 focus_fallback picks the currently focused
385 window (such as on a SendToDesktop-esque action.
389 ob_debug("focused window got an Out/In back to "
390 "itself IGNORED both\n");
394 event_process(&fe
, NULL
);
396 ob_debug("focused window got an Out/In back to "
397 "itself but focus_client was null "
398 "IGNORED just the Out\n");
407 /* once all the FocusOut's have been dealt with, if
408 there is a FocusIn still left and it is valid, then
410 event_process(&fe
, &d
);
413 ob_debug("FocusIn was OK, so don't fallback\n");
423 ob_debug("no valid FocusIn and no FocusOut events found, "
426 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
434 static void event_process(const XEvent
*ec
, gpointer data
)
437 ObGroup
*group
= NULL
;
438 ObClient
*client
= NULL
;
440 ObDockApp
*dockapp
= NULL
;
441 ObWindow
*obwin
= NULL
;
443 ObEventData
*ed
= data
;
445 /* make a copy we can mangle */
449 window
= event_get_window(e
);
450 if (!(e
->type
== PropertyNotify
&&
451 (group
= g_hash_table_lookup(group_map
, &window
))))
452 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
453 switch (obwin
->type
) {
455 dock
= WINDOW_AS_DOCK(obwin
);
458 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
461 client
= WINDOW_AS_CLIENT(obwin
);
464 case Window_Internal
:
465 /* not to be used for events */
466 g_assert_not_reached();
471 event_set_lasttime(e
);
473 if (event_ignore(e
, client
)) {
480 /* deal with it in the kernel */
482 event_handle_group(group
, e
);
484 event_handle_client(client
, e
);
486 event_handle_dockapp(dockapp
, e
);
488 event_handle_dock(dock
, e
);
489 else if (window
== RootWindow(ob_display
, ob_screen
))
490 event_handle_root(e
);
491 else if (e
->type
== MapRequest
)
492 client_manage(window
);
493 else if (e
->type
== ConfigureRequest
) {
494 /* unhandled configure requests must be used to configure the
498 xwc
.x
= e
->xconfigurerequest
.x
;
499 xwc
.y
= e
->xconfigurerequest
.y
;
500 xwc
.width
= e
->xconfigurerequest
.width
;
501 xwc
.height
= e
->xconfigurerequest
.height
;
502 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
503 xwc
.sibling
= e
->xconfigurerequest
.above
;
504 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
506 /* we are not to be held responsible if someone sends us an
508 xerror_set_ignore(TRUE
);
509 XConfigureWindow(ob_display
, window
,
510 e
->xconfigurerequest
.value_mask
, &xwc
);
511 xerror_set_ignore(FALSE
);
514 /* user input (action-bound) events */
515 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
516 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
517 e
->type
== KeyRelease
)
519 if (menu_frame_visible
)
520 event_handle_menu(e
);
522 if (!keyboard_process_interactive_grab(e
, &client
)) {
523 if (moveresize_in_progress
) {
526 /* make further actions work on the client being
528 client
= moveresize_client
;
531 menu_can_hide
= FALSE
;
532 ob_main_loop_timeout_add(ob_main_loop
,
534 menu_hide_delay_func
,
537 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
538 e
->type
== MotionNotify
)
539 mouse_event(client
, e
);
540 else if (e
->type
== KeyPress
)
541 /* when in the middle of a focus cycling action, this
542 causes the window which appears to be focused to be
543 the one on which the actions will be executed */
544 keyboard_event((focus_cycle_target
?
545 focus_cycle_target
: client
), e
);
551 static void event_handle_root(XEvent
*e
)
557 ob_debug("Another WM has requested to replace us. Exiting.\n");
562 if (e
->xclient
.format
!= 32) break;
564 msgtype
= e
->xclient
.message_type
;
565 if (msgtype
== prop_atoms
.net_current_desktop
) {
566 unsigned int d
= e
->xclient
.data
.l
[0];
567 if (d
< screen_num_desktops
)
568 screen_set_desktop(d
);
569 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
570 unsigned int d
= e
->xclient
.data
.l
[0];
572 screen_set_num_desktops(d
);
573 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
574 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
578 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
579 screen_update_desktop_names();
580 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
581 screen_update_layout();
583 case ConfigureNotify
:
585 XRRUpdateConfiguration(e
);
592 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
593 ob_debug("VIDMODE EVENT\n");
599 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
603 g_assert(e
->type
== PropertyNotify
);
605 for (it
= group
->members
; it
; it
= g_slist_next(it
))
606 event_handle_client(it
->data
, e
);
609 void event_enter_client(ObClient
*client
)
611 g_assert(config_focus_follow
);
613 if (client_normal(client
) && client_can_focus(client
)) {
614 if (config_focus_delay
) {
615 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
616 ob_main_loop_timeout_add(ob_main_loop
,
621 focus_delay_func(client
);
625 static void event_handle_client(ObClient
*client
, XEvent
*e
)
633 case VisibilityNotify
:
634 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
638 /* Wheel buttons don't draw because they are an instant click, so it
639 is a waste of resources to go drawing it. */
640 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
641 con
= frame_context(client
, e
->xbutton
.window
);
642 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
644 case OB_FRAME_CONTEXT_MAXIMIZE
:
645 client
->frame
->max_press
= (e
->type
== ButtonPress
);
646 framerender_frame(client
->frame
);
648 case OB_FRAME_CONTEXT_CLOSE
:
649 client
->frame
->close_press
= (e
->type
== ButtonPress
);
650 framerender_frame(client
->frame
);
652 case OB_FRAME_CONTEXT_ICONIFY
:
653 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
654 framerender_frame(client
->frame
);
656 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
657 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
658 framerender_frame(client
->frame
);
660 case OB_FRAME_CONTEXT_SHADE
:
661 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
662 framerender_frame(client
->frame
);
665 /* nothing changes with clicks for any other contexts */
672 ob_debug("FocusIn on client for %lx\n", client
->window
);
674 if (client
!= focus_client
) {
675 focus_set_client(client
);
676 frame_adjust_focus(client
->frame
, TRUE
);
677 client_calc_layer(client
);
682 ob_debug("FocusOut on client for %lx\n", client
->window
);
684 frame_adjust_focus(client
->frame
, FALSE
);
685 client_calc_layer(client
);
688 con
= frame_context(client
, e
->xcrossing
.window
);
690 case OB_FRAME_CONTEXT_MAXIMIZE
:
691 client
->frame
->max_hover
= FALSE
;
692 frame_adjust_state(client
->frame
);
694 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
695 client
->frame
->desk_hover
= FALSE
;
696 frame_adjust_state(client
->frame
);
698 case OB_FRAME_CONTEXT_SHADE
:
699 client
->frame
->shade_hover
= FALSE
;
700 frame_adjust_state(client
->frame
);
702 case OB_FRAME_CONTEXT_ICONIFY
:
703 client
->frame
->iconify_hover
= FALSE
;
704 frame_adjust_state(client
->frame
);
706 case OB_FRAME_CONTEXT_CLOSE
:
707 client
->frame
->close_hover
= FALSE
;
708 frame_adjust_state(client
->frame
);
710 case OB_FRAME_CONTEXT_FRAME
:
712 if (config_focus_follow && config_focus_delay)
713 ob_main_loop_timeout_remove_data(ob_main_loop,
724 gboolean nofocus
= FALSE
;
726 if (ignore_enter_focus
) {
727 ignore_enter_focus
--;
731 con
= frame_context(client
, e
->xcrossing
.window
);
733 case OB_FRAME_CONTEXT_MAXIMIZE
:
734 client
->frame
->max_hover
= TRUE
;
735 frame_adjust_state(client
->frame
);
737 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
738 client
->frame
->desk_hover
= TRUE
;
739 frame_adjust_state(client
->frame
);
741 case OB_FRAME_CONTEXT_SHADE
:
742 client
->frame
->shade_hover
= TRUE
;
743 frame_adjust_state(client
->frame
);
745 case OB_FRAME_CONTEXT_ICONIFY
:
746 client
->frame
->iconify_hover
= TRUE
;
747 frame_adjust_state(client
->frame
);
749 case OB_FRAME_CONTEXT_CLOSE
:
750 client
->frame
->close_hover
= TRUE
;
751 frame_adjust_state(client
->frame
);
753 case OB_FRAME_CONTEXT_FRAME
:
754 if (e
->xcrossing
.mode
== NotifyGrab
||
755 e
->xcrossing
.mode
== NotifyUngrab
)
758 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
759 (e
->type
== EnterNotify
? "Enter" : "Leave"),
761 e
->xcrossing
.detail
, client
?client
->window
:0);
765 ob_debug("%sNotify mode %d detail %d on %lx, "
767 (e
->type
== EnterNotify
? "Enter" : "Leave"),
769 e
->xcrossing
.detail
, client
?client
->window
:0);
771 if (!nofocus
&& config_focus_follow
)
772 event_enter_client(client
);
780 case ConfigureRequest
:
782 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
783 ConfigureRequest
, &ce
)) {
785 /* XXX if this causes bad things.. we can compress config req's
786 with the same mask. */
787 e
->xconfigurerequest
.value_mask
|=
788 ce
.xconfigurerequest
.value_mask
;
789 if (ce
.xconfigurerequest
.value_mask
& CWX
)
790 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
791 if (ce
.xconfigurerequest
.value_mask
& CWY
)
792 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
793 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
794 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
795 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
796 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
797 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
798 e
->xconfigurerequest
.border_width
=
799 ce
.xconfigurerequest
.border_width
;
800 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
801 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
804 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
805 if (client
->iconic
|| client
->shaded
) return;
807 /* resize, then move, as specified in the EWMH section 7.7 */
808 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
814 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
815 client
->border_width
= e
->xconfigurerequest
.border_width
;
817 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
818 e
->xconfigurerequest
.x
: client
->area
.x
;
819 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
820 e
->xconfigurerequest
.y
: client
->area
.y
;
821 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
822 e
->xconfigurerequest
.width
: client
->area
.width
;
823 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
824 e
->xconfigurerequest
.height
: client
->area
.height
;
830 client
->frame
->size
.left
+ client
->frame
->size
.right
;
832 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
833 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
834 client_normal(client
));
835 if (e
->xconfigurerequest
.value_mask
& CWX
)
837 if (e
->xconfigurerequest
.value_mask
& CWY
)
841 switch (client
->gravity
) {
842 case NorthEastGravity
:
844 corner
= OB_CORNER_TOPRIGHT
;
846 case SouthWestGravity
:
848 corner
= OB_CORNER_BOTTOMLEFT
;
850 case SouthEastGravity
:
851 corner
= OB_CORNER_BOTTOMRIGHT
;
853 default: /* NorthWest, Static, etc */
854 corner
= OB_CORNER_TOPLEFT
;
857 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
861 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
862 switch (e
->xconfigurerequest
.detail
) {
865 action_run_string("Lower", client
);
871 action_run_string("Raise", client
);
877 if (client
->ignore_unmaps
) {
878 client
->ignore_unmaps
--;
881 client_unmanage(client
);
884 client_unmanage(client
);
887 /* this is when the client is first taken captive in the frame */
888 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
891 This event is quite rare and is usually handled in unmapHandler.
892 However, if the window is unmapped when the reparent event occurs,
893 the window manager never sees it because an unmap event is not sent
894 to an already unmapped window.
897 /* we don't want the reparent event, put it back on the stack for the
898 X server to deal with after we unmanage the window */
899 XPutBackEvent(ob_display
, e
);
901 client_unmanage(client
);
904 ob_debug("MapRequest for 0x%lx\n", client
->window
);
905 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
906 does, we don't want it! */
907 if (screen_showing_desktop
)
908 screen_show_desktop(FALSE
);
909 client_iconify(client
, FALSE
, TRUE
);
910 if (!client
->frame
->visible
)
911 /* if its not visible still, then don't mess with it */
914 client_shade(client
, FALSE
);
915 client_focus(client
);
916 stacking_raise(CLIENT_AS_WINDOW(client
));
919 /* validate cuz we query stuff off the client here */
920 if (!client_validate(client
)) break;
922 if (e
->xclient
.format
!= 32) return;
924 msgtype
= e
->xclient
.message_type
;
925 if (msgtype
== prop_atoms
.wm_change_state
) {
926 /* compress changes into a single change */
927 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
929 /* XXX: it would be nice to compress ALL messages of a
930 type, not just messages in a row without other
931 message types between. */
932 if (ce
.xclient
.message_type
!= msgtype
) {
933 XPutBackEvent(ob_display
, &ce
);
936 e
->xclient
= ce
.xclient
;
938 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
939 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
940 /* compress changes into a single change */
941 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
943 /* XXX: it would be nice to compress ALL messages of a
944 type, not just messages in a row without other
945 message types between. */
946 if (ce
.xclient
.message_type
!= msgtype
) {
947 XPutBackEvent(ob_display
, &ce
);
950 e
->xclient
= ce
.xclient
;
952 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
953 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
954 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
956 } else if (msgtype
== prop_atoms
.net_wm_state
) {
957 /* can't compress these */
958 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
959 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
960 e
->xclient
.data
.l
[0] == 1 ? "Add" :
961 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
962 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
964 client_set_state(client
, e
->xclient
.data
.l
[0],
965 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
966 } else if (msgtype
== prop_atoms
.net_close_window
) {
967 ob_debug("net_close_window for 0x%lx\n", client
->window
);
968 client_close(client
);
969 } else if (msgtype
== prop_atoms
.net_active_window
) {
970 ob_debug("net_active_window for 0x%lx\n", client
->window
);
971 client_activate(client
, FALSE
);
972 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
973 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
974 if ((Atom
)e
->xclient
.data
.l
[2] ==
975 prop_atoms
.net_wm_moveresize_size_topleft
||
976 (Atom
)e
->xclient
.data
.l
[2] ==
977 prop_atoms
.net_wm_moveresize_size_top
||
978 (Atom
)e
->xclient
.data
.l
[2] ==
979 prop_atoms
.net_wm_moveresize_size_topright
||
980 (Atom
)e
->xclient
.data
.l
[2] ==
981 prop_atoms
.net_wm_moveresize_size_right
||
982 (Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_right
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_bottomright
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_size_bottom
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_bottomleft
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_size_left
||
992 (Atom
)e
->xclient
.data
.l
[2] ==
993 prop_atoms
.net_wm_moveresize_move
||
994 (Atom
)e
->xclient
.data
.l
[2] ==
995 prop_atoms
.net_wm_moveresize_size_keyboard
||
996 (Atom
)e
->xclient
.data
.l
[2] ==
997 prop_atoms
.net_wm_moveresize_move_keyboard
) {
999 moveresize_start(client
, e
->xclient
.data
.l
[0],
1000 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1001 e
->xclient
.data
.l
[2]);
1003 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1004 int oldg
= client
->gravity
;
1005 int tmpg
, x
, y
, w
, h
;
1007 if (e
->xclient
.data
.l
[0] & 0xff)
1008 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1012 if (e
->xclient
.data
.l
[0] & 1 << 8)
1013 x
= e
->xclient
.data
.l
[1];
1016 if (e
->xclient
.data
.l
[0] & 1 << 9)
1017 y
= e
->xclient
.data
.l
[2];
1020 if (e
->xclient
.data
.l
[0] & 1 << 10)
1021 w
= e
->xclient
.data
.l
[3];
1023 w
= client
->area
.width
;
1024 if (e
->xclient
.data
.l
[0] & 1 << 11)
1025 h
= e
->xclient
.data
.l
[4];
1027 h
= client
->area
.height
;
1028 client
->gravity
= tmpg
;
1034 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1036 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1037 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1038 client_normal(client
));
1039 if (e
->xclient
.data
.l
[0] & 1 << 8)
1041 if (e
->xclient
.data
.l
[0] & 1 << 9)
1045 client_configure(client
, OB_CORNER_TOPLEFT
,
1046 x
, y
, w
, h
, FALSE
, TRUE
);
1048 client
->gravity
= oldg
;
1051 case PropertyNotify
:
1052 /* validate cuz we query stuff off the client here */
1053 if (!client_validate(client
)) break;
1055 /* compress changes to a single property into a single change */
1056 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1060 /* XXX: it would be nice to compress ALL changes to a property,
1061 not just changes in a row without other props between. */
1063 a
= ce
.xproperty
.atom
;
1064 b
= e
->xproperty
.atom
;
1068 if ((a
== prop_atoms
.net_wm_name
||
1069 a
== prop_atoms
.wm_name
||
1070 a
== prop_atoms
.net_wm_icon_name
||
1071 a
== prop_atoms
.wm_icon_name
)
1073 (b
== prop_atoms
.net_wm_name
||
1074 b
== prop_atoms
.wm_name
||
1075 b
== prop_atoms
.net_wm_icon_name
||
1076 b
== prop_atoms
.wm_icon_name
)) {
1079 if ((a
== prop_atoms
.net_wm_icon
||
1080 a
== prop_atoms
.kwm_win_icon
)
1082 (b
== prop_atoms
.net_wm_icon
||
1083 b
== prop_atoms
.kwm_win_icon
))
1086 XPutBackEvent(ob_display
, &ce
);
1090 msgtype
= e
->xproperty
.atom
;
1091 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1092 client_update_normal_hints(client
);
1093 /* normal hints can make a window non-resizable */
1094 client_setup_decor_and_functions(client
);
1095 } else if (msgtype
== XA_WM_HINTS
) {
1096 client_update_wmhints(client
);
1097 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1098 client_update_transient_for(client
);
1099 client_get_type(client
);
1100 /* type may have changed, so update the layer */
1101 client_calc_layer(client
);
1102 client_setup_decor_and_functions(client
);
1103 } else if (msgtype
== prop_atoms
.net_wm_name
||
1104 msgtype
== prop_atoms
.wm_name
||
1105 msgtype
== prop_atoms
.net_wm_icon_name
||
1106 msgtype
== prop_atoms
.wm_icon_name
) {
1107 client_update_title(client
);
1108 } else if (msgtype
== prop_atoms
.wm_class
) {
1109 client_update_class(client
);
1110 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1111 client_update_protocols(client
);
1112 client_setup_decor_and_functions(client
);
1114 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1115 client_update_strut(client
);
1117 else if (msgtype
== prop_atoms
.net_wm_icon
||
1118 msgtype
== prop_atoms
.kwm_win_icon
) {
1119 client_update_icons(client
);
1121 else if (msgtype
== prop_atoms
.sm_client_id
) {
1122 client_update_sm_client_id(client
);
1127 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1128 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1129 frame_adjust_shape(client
->frame
);
1135 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1139 stacking_raise(DOCK_AS_WINDOW(s
));
1150 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1154 dock_app_drag(app
, &e
->xmotion
);
1157 if (app
->ignore_unmaps
) {
1158 app
->ignore_unmaps
--;
1161 dock_remove(app
, TRUE
);
1164 dock_remove(app
, FALSE
);
1166 case ReparentNotify
:
1167 dock_remove(app
, FALSE
);
1169 case ConfigureNotify
:
1170 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1175 ObMenuFrame
* find_active_menu()
1180 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1185 return it
? it
->data
: NULL
;
1188 static void event_handle_menu(XEvent
*ev
)
1191 ObMenuEntryFrame
*e
;
1195 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1196 ev
->xbutton
.y_root
)))
1197 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1198 else if (menu_can_hide
)
1199 menu_frame_hide_all();
1202 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1203 ev
->xmotion
.y_root
))) {
1204 menu_frame_move_on_screen(f
);
1205 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1206 ev
->xmotion
.y_root
)))
1207 menu_frame_select(f
, e
);
1212 a
= find_active_menu();
1214 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1216 menu_frame_select(a
, NULL
);
1221 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1222 menu_frame_hide_all();
1223 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1225 if ((f
= find_active_menu()))
1226 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1227 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1229 if ((f
= find_active_menu()) && f
->parent
)
1230 menu_frame_select(f
, NULL
);
1231 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1233 if ((f
= find_active_menu()) && f
->child
)
1234 menu_frame_select_next(f
->child
);
1235 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1237 if ((f
= find_active_menu()))
1238 menu_frame_select_previous(f
);
1239 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1241 if ((f
= find_active_menu()))
1242 menu_frame_select_next(f
);
1248 static gboolean
menu_hide_delay_func(gpointer data
)
1250 menu_can_hide
= TRUE
;
1251 return FALSE
; /* no repeat */
1254 static gboolean
focus_delay_func(gpointer data
)
1259 if (config_focus_raise
)
1260 stacking_raise(CLIENT_AS_WINDOW(c
));
1261 return FALSE
; /* no repeat */
1264 static void focus_delay_client_dest(gpointer data
)
1268 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, c
);
1271 void event_ignore_queued_enters()
1273 GSList
*saved
= NULL
, *it
;
1276 XSync(ob_display
, FALSE
);
1278 /* count the events */
1280 e
= g_new(XEvent
, 1);
1281 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1282 saved
= g_slist_append(saved
, e
);
1283 ++ignore_enter_focus
;
1289 /* put the events back */
1290 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1291 XPutBackEvent(ob_display
, it
->data
);
1294 g_slist_free(saved
);