1 /* -*- indent-tabs-mode: t; 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.
29 #include "menuframe.h"
33 #include "framerender.h"
35 #include "moveresize.h"
38 #include "extensions.h"
42 #include <X11/keysym.h>
43 #include <X11/Xatom.h>
46 #ifdef HAVE_SYS_SELECT_H
47 # include <sys/select.h>
54 #include <X11/ICE/ICElib.h>
62 static void event_process(const XEvent
*e
, gpointer data
);
63 static void event_handle_root(XEvent
*e
);
64 static void event_handle_menu(XEvent
*e
);
65 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
66 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
67 static void event_handle_client(ObClient
*c
, XEvent
*e
);
68 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
70 static gboolean
focus_delay_func(gpointer data
);
71 static void focus_delay_client_dest(gpointer data
);
73 static gboolean
menu_hide_delay_func(gpointer data
);
75 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
76 (e)->xfocus.detail == NotifyAncestor || \
77 (e)->xfocus.detail > NotifyNonlinearVirtual)
78 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
79 (e)->xfocus.detail == NotifyInferior || \
80 (e)->xfocus.detail == NotifyAncestor || \
81 (e)->xfocus.detail > NotifyNonlinearVirtual)
83 Time event_lasttime
= 0;
85 /*! The value of the mask for the NumLock modifier */
86 unsigned int NumLockMask
;
87 /*! The value of the mask for the ScrollLock modifier */
88 unsigned int ScrollLockMask
;
89 /*! The key codes for the modifier keys */
90 static XModifierKeymap
*modmap
;
91 /*! Table of the constant modifier masks */
92 static const int mask_table
[] = {
93 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
94 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
96 static int mask_table_size
;
98 static ObClient
*focus_delay_client
;
100 static gboolean menu_can_hide
;
103 static void ice_handler(int fd
, gpointer conn
)
106 IceProcessMessages(conn
, NULL
, &b
);
109 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
110 IcePointer
*watch_data
)
115 fd
= IceConnectionNumber(conn
);
116 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
118 ob_main_loop_fd_remove(ob_main_loop
, fd
);
124 void event_startup(gboolean reconfig
)
126 if (reconfig
) return;
128 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
130 /* get lock masks that are defined by the display (not constant) */
131 modmap
= XGetModifierMapping(ob_display
);
133 if (modmap
&& modmap
->max_keypermod
> 0) {
135 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
136 /* get the values of the keyboard lock modifiers
137 Note: Caps lock is not retrieved the same way as Scroll and Num
138 lock since it doesn't need to be. */
139 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
140 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
143 for (cnt
= 0; cnt
< size
; ++cnt
) {
144 if (! modmap
->modifiermap
[cnt
]) continue;
146 if (num_lock
== modmap
->modifiermap
[cnt
])
147 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
148 if (scroll_lock
== modmap
->modifiermap
[cnt
])
149 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
153 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
156 IceAddConnectionWatch(ice_watch
, NULL
);
159 client_add_destructor(focus_delay_client_dest
);
162 void event_shutdown(gboolean reconfig
)
164 if (reconfig
) return;
167 IceRemoveConnectionWatch(ice_watch
, NULL
);
170 client_remove_destructor(focus_delay_client_dest
);
171 XFreeModifiermap(modmap
);
174 static Window
event_get_window(XEvent
*e
)
181 window
= RootWindow(ob_display
, ob_screen
);
184 window
= e
->xmap
.window
;
187 window
= e
->xunmap
.window
;
190 window
= e
->xdestroywindow
.window
;
192 case ConfigureRequest
:
193 window
= e
->xconfigurerequest
.window
;
195 case ConfigureNotify
:
196 window
= e
->xconfigure
.window
;
200 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
201 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
203 window
= ((XkbBellNotifyEvent
*)e
)->window
;
209 window
= e
->xany
.window
;
214 static void event_set_lasttime(XEvent
*e
)
218 /* grab the lasttime and hack up the state */
234 t
= e
->xproperty
.time
;
238 t
= e
->xcrossing
.time
;
241 /* if more event types are anticipated, get their timestamp
246 if (t
> event_lasttime
)
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)
264 STRIP_MODS(e
->xbutton
.state
);
267 STRIP_MODS(e
->xkey
.state
);
270 STRIP_MODS(e
->xkey
.state
);
271 /* remove from the state the mask of the modifier being released, if
272 it is a modifier key being released (this is a little ugly..) */
273 kp
= modmap
->modifiermap
;
274 for (i
= 0; i
< mask_table_size
; ++i
) {
275 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
276 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
277 /* remove the mask for it */
278 e
->xkey
.state
&= ~mask_table
[i
];
279 /* cause the first loop to break; */
281 break; /* get outta here! */
288 STRIP_MODS(e
->xmotion
.state
);
289 /* compress events */
292 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
294 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
295 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
302 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
306 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
307 because of RevertToPointerRoot. If the focus ends up reverting to
308 pointer root on a workspace change, then the FocusIn event that we
309 want will be of type NotifyAncestor. This situation does not occur
310 for FocusOut, so it is safely ignored there.
312 if (INVALID_FOCUSIN(e
) ||
315 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
316 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
318 /* says a client was not found for the event (or a valid FocusIn
321 e
->xfocus
.window
= None
;
326 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
327 e
->xfocus
.mode
, e
->xfocus
.detail
);
331 if (INVALID_FOCUSOUT(e
)) {
333 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
334 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
340 ob_debug("FocusOut on %lx mode %d detail %d\n",
341 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
346 gboolean fallback
= TRUE
;
349 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
351 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
353 if (fe
.type
== FocusOut
) {
355 ob_debug("found pending FocusOut\n");
357 if (!INVALID_FOCUSOUT(&fe
)) {
358 /* if there is a VALID FocusOut still coming, don't
359 fallback focus yet, we'll deal with it then */
360 XPutBackEvent(ob_display
, &fe
);
366 ob_debug("found pending FocusIn\n");
368 /* is the focused window getting a FocusOut/In back to
371 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
372 !event_ignore(&fe
, client
)) {
374 if focus_client is not set, then we can't do
375 this. we need the FocusIn. This happens in the
376 case when the set_focus_client(NULL) in the
377 focus_fallback function fires and then
378 focus_fallback picks the currently focused
379 window (such as on a SendToDesktop-esque action.
383 ob_debug("focused window got an Out/In back to "
384 "itself IGNORED both\n");
388 event_process(&fe
, NULL
);
390 ob_debug("focused window got an Out/In back to "
391 "itself but focus_client was null "
392 "IGNORED just the Out\n");
401 /* once all the FocusOut's have been dealt with, if
402 there is a FocusIn still left and it is valid, then
404 event_process(&fe
, &d
);
407 ob_debug("FocusIn was OK, so don't fallback\n");
417 ob_debug("no valid FocusIn and no FocusOut events found, "
420 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
426 /* NotifyUngrab occurs when a mouse button is released and the event is
427 caused, like when lowering a window */
428 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
429 pointer (Ancestor happens when the pointer is on a window border) */
430 if (e
->xcrossing
.mode
== NotifyGrab
||
431 e
->xcrossing
.detail
== NotifyInferior
||
432 (e
->xcrossing
.mode
== NotifyUngrab
&&
433 (e
->xcrossing
.detail
== NotifyAncestor
||
434 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
435 e
->xcrossing
.detail
== NotifyVirtual
))) {
437 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
438 (e
->type
== EnterNotify
? "Enter" : "Leave"),
440 e
->xcrossing
.detail
, client
?client
->window
:0);
445 ob_debug("%sNotify mode %d detail %d on %lx\n",
446 (e
->type
== EnterNotify
? "Enter" : "Leave"),
448 e
->xcrossing
.detail
, client
?client
->window
:0);
455 static void event_process(const XEvent
*ec
, gpointer data
)
458 ObGroup
*group
= NULL
;
459 ObClient
*client
= NULL
;
461 ObDockApp
*dockapp
= NULL
;
462 ObWindow
*obwin
= NULL
;
464 ObEventData
*ed
= data
;
466 /* make a copy we can mangle */
470 window
= event_get_window(e
);
471 if (!(e
->type
== PropertyNotify
&&
472 (group
= g_hash_table_lookup(group_map
, &window
))))
473 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
474 switch (obwin
->type
) {
476 dock
= WINDOW_AS_DOCK(obwin
);
479 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
482 client
= WINDOW_AS_CLIENT(obwin
);
485 case Window_Internal
:
486 /* not to be used for events */
487 g_assert_not_reached();
492 event_set_lasttime(e
);
494 if (event_ignore(e
, client
)) {
501 /* deal with it in the kernel */
503 event_handle_group(group
, e
);
505 event_handle_client(client
, e
);
507 event_handle_dockapp(dockapp
, e
);
509 event_handle_dock(dock
, e
);
510 else if (window
== RootWindow(ob_display
, ob_screen
))
511 event_handle_root(e
);
512 else if (e
->type
== MapRequest
)
513 client_manage(window
);
514 else if (e
->type
== ConfigureRequest
) {
515 /* unhandled configure requests must be used to configure the
519 xwc
.x
= e
->xconfigurerequest
.x
;
520 xwc
.y
= e
->xconfigurerequest
.y
;
521 xwc
.width
= e
->xconfigurerequest
.width
;
522 xwc
.height
= e
->xconfigurerequest
.height
;
523 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
524 xwc
.sibling
= e
->xconfigurerequest
.above
;
525 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
527 /* we are not to be held responsible if someone sends us an
529 xerror_set_ignore(TRUE
);
530 XConfigureWindow(ob_display
, window
,
531 e
->xconfigurerequest
.value_mask
, &xwc
);
532 xerror_set_ignore(FALSE
);
535 /* user input (action-bound) events */
536 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
537 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
538 e
->type
== KeyRelease
)
540 if (menu_frame_visible
)
541 event_handle_menu(e
);
543 if (!keyboard_process_interactive_grab(e
, &client
)) {
544 if (moveresize_in_progress
)
547 menu_can_hide
= FALSE
;
548 ob_main_loop_timeout_add(ob_main_loop
,
550 menu_hide_delay_func
,
553 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
554 e
->type
== MotionNotify
)
555 mouse_event(client
, e
);
556 else if (e
->type
== KeyPress
)
557 /* when in the middle of a focus cycling action, this
558 causes the window which appears to be focused to be
559 the one on which the actions will be executed */
560 keyboard_event((focus_cycle_target
?
562 (client
? client
: focus_client
)), e
);
568 static void event_handle_root(XEvent
*e
)
574 ob_debug("Another WM has requested to replace us. Exiting.\n");
579 if (e
->xclient
.format
!= 32) break;
581 msgtype
= e
->xclient
.message_type
;
582 if (msgtype
== prop_atoms
.net_current_desktop
) {
583 unsigned int d
= e
->xclient
.data
.l
[0];
584 if (d
< screen_num_desktops
)
585 screen_set_desktop(d
);
586 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
587 unsigned int d
= e
->xclient
.data
.l
[0];
589 screen_set_num_desktops(d
);
590 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
591 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
595 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
596 screen_update_desktop_names();
597 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
598 screen_update_layout();
600 case ConfigureNotify
:
602 XRRUpdateConfiguration(e
);
609 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
610 ob_debug("VIDMODE EVENT\n");
616 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
620 g_assert(e
->type
== PropertyNotify
);
622 for (it
= group
->members
; it
; it
= g_slist_next(it
))
623 event_handle_client(it
->data
, e
);
626 static void event_handle_client(ObClient
*client
, XEvent
*e
)
634 case VisibilityNotify
:
635 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
639 /* Wheel buttons don't draw because they are an instant click, so it
640 is a waste of resources to go drawing it. */
641 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
642 con
= frame_context(client
, e
->xbutton
.window
);
643 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
645 case OB_FRAME_CONTEXT_MAXIMIZE
:
646 client
->frame
->max_press
= (e
->type
== ButtonPress
);
647 framerender_frame(client
->frame
);
649 case OB_FRAME_CONTEXT_CLOSE
:
650 client
->frame
->close_press
= (e
->type
== ButtonPress
);
651 framerender_frame(client
->frame
);
653 case OB_FRAME_CONTEXT_ICONIFY
:
654 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
655 framerender_frame(client
->frame
);
657 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
658 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
659 framerender_frame(client
->frame
);
661 case OB_FRAME_CONTEXT_SHADE
:
662 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
663 framerender_frame(client
->frame
);
666 /* nothing changes with clicks for any other contexts */
673 ob_debug("FocusIn on client for %lx\n", client
->window
);
675 if (client
!= focus_client
) {
676 focus_set_client(client
);
677 frame_adjust_focus(client
->frame
, TRUE
);
682 ob_debug("FocusOut on client for %lx\n", client
->window
);
684 /* are we a fullscreen window or a transient of one? (checks layer)
685 if we are then we need to be iconified since we are losing focus
687 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
688 !client_search_focus_tree_full(client
))
689 /* iconify fullscreen windows when they and their transients
691 client_iconify(client
, TRUE
, TRUE
);
692 frame_adjust_focus(client
->frame
, FALSE
);
695 con
= frame_context(client
, e
->xcrossing
.window
);
697 case OB_FRAME_CONTEXT_MAXIMIZE
:
698 client
->frame
->max_hover
= FALSE
;
699 frame_adjust_state(client
->frame
);
701 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
702 client
->frame
->desk_hover
= FALSE
;
703 frame_adjust_state(client
->frame
);
705 case OB_FRAME_CONTEXT_SHADE
:
706 client
->frame
->shade_hover
= FALSE
;
707 frame_adjust_state(client
->frame
);
709 case OB_FRAME_CONTEXT_ICONIFY
:
710 client
->frame
->iconify_hover
= FALSE
;
711 frame_adjust_state(client
->frame
);
713 case OB_FRAME_CONTEXT_CLOSE
:
714 client
->frame
->close_hover
= FALSE
;
715 frame_adjust_state(client
->frame
);
717 case OB_FRAME_CONTEXT_FRAME
:
718 /* XXX if doing a 'reconfigure' make sure you kill this timer,
719 maybe all timers.. */
720 if (config_focus_delay
&& client
== focus_delay_client
) {
721 ob_main_loop_timeout_remove_data(ob_main_loop
,
724 focus_delay_client
= NULL
;
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 (client_normal(client
)) {
755 if (config_focus_follow
) {
757 ob_debug("EnterNotify on %lx, focusing window\n",
760 if (config_focus_delay
) {
761 ob_main_loop_timeout_add(ob_main_loop
,
765 focus_delay_client
= client
;
767 client_focus(client
);
775 case ConfigureRequest
:
777 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
778 ConfigureRequest
, &ce
)) {
780 /* XXX if this causes bad things.. we can compress config req's
781 with the same mask. */
782 e
->xconfigurerequest
.value_mask
|=
783 ce
.xconfigurerequest
.value_mask
;
784 if (ce
.xconfigurerequest
.value_mask
& CWX
)
785 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
786 if (ce
.xconfigurerequest
.value_mask
& CWY
)
787 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
788 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
789 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
790 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
791 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
792 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
793 e
->xconfigurerequest
.border_width
=
794 ce
.xconfigurerequest
.border_width
;
795 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
796 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
799 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
800 if (client
->iconic
|| client
->shaded
) return;
802 /* resize, then move, as specified in the EWMH section 7.7 */
803 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
809 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
810 client
->border_width
= e
->xconfigurerequest
.border_width
;
812 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
813 e
->xconfigurerequest
.x
: client
->area
.x
;
814 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
815 e
->xconfigurerequest
.y
: client
->area
.y
;
816 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
817 e
->xconfigurerequest
.width
: client
->area
.width
;
818 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
819 e
->xconfigurerequest
.height
: client
->area
.height
;
825 client
->frame
->size
.left
+ client
->frame
->size
.right
;
827 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
828 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
829 client_normal(client
));
830 if (e
->xconfigurerequest
.value_mask
& CWX
)
832 if (e
->xconfigurerequest
.value_mask
& CWY
)
836 switch (client
->gravity
) {
837 case NorthEastGravity
:
839 corner
= OB_CORNER_TOPRIGHT
;
841 case SouthWestGravity
:
843 corner
= OB_CORNER_BOTTOMLEFT
;
845 case SouthEastGravity
:
846 corner
= OB_CORNER_BOTTOMRIGHT
;
848 default: /* NorthWest, Static, etc */
849 corner
= OB_CORNER_TOPLEFT
;
852 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
856 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
857 switch (e
->xconfigurerequest
.detail
) {
860 stacking_lower(CLIENT_AS_WINDOW(client
));
866 stacking_raise(CLIENT_AS_WINDOW(client
));
872 if (client
->ignore_unmaps
) {
873 client
->ignore_unmaps
--;
876 client_unmanage(client
);
879 client_unmanage(client
);
882 /* this is when the client is first taken captive in the frame */
883 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
886 This event is quite rare and is usually handled in unmapHandler.
887 However, if the window is unmapped when the reparent event occurs,
888 the window manager never sees it because an unmap event is not sent
889 to an already unmapped window.
892 /* we don't want the reparent event, put it back on the stack for the
893 X server to deal with after we unmanage the window */
894 XPutBackEvent(ob_display
, e
);
896 client_unmanage(client
);
899 ob_debug("MapRequest for 0x%lx\n", client
->window
);
900 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
901 does, we don't want it! */
902 if (screen_showing_desktop
)
903 screen_show_desktop(FALSE
);
904 client_iconify(client
, FALSE
, TRUE
);
905 if (!client
->frame
->visible
)
906 /* if its not visible still, then don't mess with it */
909 client_shade(client
, FALSE
);
910 client_focus(client
);
911 stacking_raise(CLIENT_AS_WINDOW(client
));
914 /* validate cuz we query stuff off the client here */
915 if (!client_validate(client
)) break;
917 if (e
->xclient
.format
!= 32) return;
919 msgtype
= e
->xclient
.message_type
;
920 if (msgtype
== prop_atoms
.wm_change_state
) {
921 /* compress changes into a single change */
922 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
924 /* XXX: it would be nice to compress ALL messages of a
925 type, not just messages in a row without other
926 message types between. */
927 if (ce
.xclient
.message_type
!= msgtype
) {
928 XPutBackEvent(ob_display
, &ce
);
931 e
->xclient
= ce
.xclient
;
933 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
934 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
935 /* compress changes into a single change */
936 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
938 /* XXX: it would be nice to compress ALL messages of a
939 type, not just messages in a row without other
940 message types between. */
941 if (ce
.xclient
.message_type
!= msgtype
) {
942 XPutBackEvent(ob_display
, &ce
);
945 e
->xclient
= ce
.xclient
;
947 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
948 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
949 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
951 } else if (msgtype
== prop_atoms
.net_wm_state
) {
952 /* can't compress these */
953 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
954 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
955 e
->xclient
.data
.l
[0] == 1 ? "Add" :
956 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
957 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
959 client_set_state(client
, e
->xclient
.data
.l
[0],
960 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
961 } else if (msgtype
== prop_atoms
.net_close_window
) {
962 ob_debug("net_close_window for 0x%lx\n", client
->window
);
963 client_close(client
);
964 } else if (msgtype
== prop_atoms
.net_active_window
) {
965 ob_debug("net_active_window for 0x%lx\n", client
->window
);
966 client_activate(client
, FALSE
);
967 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
968 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
969 if ((Atom
)e
->xclient
.data
.l
[2] ==
970 prop_atoms
.net_wm_moveresize_size_topleft
||
971 (Atom
)e
->xclient
.data
.l
[2] ==
972 prop_atoms
.net_wm_moveresize_size_top
||
973 (Atom
)e
->xclient
.data
.l
[2] ==
974 prop_atoms
.net_wm_moveresize_size_topright
||
975 (Atom
)e
->xclient
.data
.l
[2] ==
976 prop_atoms
.net_wm_moveresize_size_right
||
977 (Atom
)e
->xclient
.data
.l
[2] ==
978 prop_atoms
.net_wm_moveresize_size_right
||
979 (Atom
)e
->xclient
.data
.l
[2] ==
980 prop_atoms
.net_wm_moveresize_size_bottomright
||
981 (Atom
)e
->xclient
.data
.l
[2] ==
982 prop_atoms
.net_wm_moveresize_size_bottom
||
983 (Atom
)e
->xclient
.data
.l
[2] ==
984 prop_atoms
.net_wm_moveresize_size_bottomleft
||
985 (Atom
)e
->xclient
.data
.l
[2] ==
986 prop_atoms
.net_wm_moveresize_size_left
||
987 (Atom
)e
->xclient
.data
.l
[2] ==
988 prop_atoms
.net_wm_moveresize_move
||
989 (Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_size_keyboard
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_move_keyboard
) {
994 moveresize_start(client
, e
->xclient
.data
.l
[0],
995 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
996 e
->xclient
.data
.l
[2]);
998 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
999 int oldg
= client
->gravity
;
1000 int tmpg
, x
, y
, w
, h
;
1002 if (e
->xclient
.data
.l
[0] & 0xff)
1003 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
1007 if (e
->xclient
.data
.l
[0] & 1 << 8)
1008 x
= e
->xclient
.data
.l
[1];
1011 if (e
->xclient
.data
.l
[0] & 1 << 9)
1012 y
= e
->xclient
.data
.l
[2];
1015 if (e
->xclient
.data
.l
[0] & 1 << 10)
1016 w
= e
->xclient
.data
.l
[3];
1018 w
= client
->area
.width
;
1019 if (e
->xclient
.data
.l
[0] & 1 << 11)
1020 h
= e
->xclient
.data
.l
[4];
1022 h
= client
->area
.height
;
1023 client
->gravity
= tmpg
;
1029 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1031 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1032 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1033 client_normal(client
));
1034 if (e
->xclient
.data
.l
[0] & 1 << 8)
1036 if (e
->xclient
.data
.l
[0] & 1 << 9)
1040 client_configure(client
, OB_CORNER_TOPLEFT
,
1041 x
, y
, w
, h
, FALSE
, TRUE
);
1043 client
->gravity
= oldg
;
1046 case PropertyNotify
:
1047 /* validate cuz we query stuff off the client here */
1048 if (!client_validate(client
)) break;
1050 /* compress changes to a single property into a single change */
1051 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1055 /* XXX: it would be nice to compress ALL changes to a property,
1056 not just changes in a row without other props between. */
1058 a
= ce
.xproperty
.atom
;
1059 b
= e
->xproperty
.atom
;
1063 if ((a
== prop_atoms
.net_wm_name
||
1064 a
== prop_atoms
.wm_name
||
1065 a
== prop_atoms
.net_wm_icon_name
||
1066 a
== prop_atoms
.wm_icon_name
)
1068 (b
== prop_atoms
.net_wm_name
||
1069 b
== prop_atoms
.wm_name
||
1070 b
== prop_atoms
.net_wm_icon_name
||
1071 b
== prop_atoms
.wm_icon_name
)) {
1074 if ((a
== prop_atoms
.net_wm_icon
||
1075 a
== prop_atoms
.kwm_win_icon
)
1077 (b
== prop_atoms
.net_wm_icon
||
1078 b
== prop_atoms
.kwm_win_icon
))
1081 XPutBackEvent(ob_display
, &ce
);
1085 msgtype
= e
->xproperty
.atom
;
1086 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1087 client_update_normal_hints(client
);
1088 /* normal hints can make a window non-resizable */
1089 client_setup_decor_and_functions(client
);
1090 } else if (msgtype
== XA_WM_HINTS
) {
1091 client_update_wmhints(client
);
1092 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1093 client_update_transient_for(client
);
1094 client_get_type(client
);
1095 /* type may have changed, so update the layer */
1096 client_calc_layer(client
);
1097 client_setup_decor_and_functions(client
);
1098 } else if (msgtype
== prop_atoms
.net_wm_name
||
1099 msgtype
== prop_atoms
.wm_name
||
1100 msgtype
== prop_atoms
.net_wm_icon_name
||
1101 msgtype
== prop_atoms
.wm_icon_name
) {
1102 client_update_title(client
);
1103 } else if (msgtype
== prop_atoms
.wm_class
) {
1104 client_update_class(client
);
1105 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1106 client_update_protocols(client
);
1107 client_setup_decor_and_functions(client
);
1109 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1110 client_update_strut(client
);
1112 else if (msgtype
== prop_atoms
.net_wm_icon
||
1113 msgtype
== prop_atoms
.kwm_win_icon
) {
1114 client_update_icons(client
);
1116 else if (msgtype
== prop_atoms
.sm_client_id
) {
1117 client_update_sm_client_id(client
);
1122 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1123 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1124 frame_adjust_shape(client
->frame
);
1130 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1134 stacking_raise(DOCK_AS_WINDOW(s
));
1145 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1149 dock_app_drag(app
, &e
->xmotion
);
1152 if (app
->ignore_unmaps
) {
1153 app
->ignore_unmaps
--;
1156 dock_remove(app
, TRUE
);
1159 dock_remove(app
, FALSE
);
1161 case ReparentNotify
:
1162 dock_remove(app
, FALSE
);
1164 case ConfigureNotify
:
1165 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1170 ObMenuFrame
* find_active_menu()
1175 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1180 return it
? it
->data
: NULL
;
1183 static void event_handle_menu(XEvent
*ev
)
1186 ObMenuEntryFrame
*e
;
1190 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1191 ev
->xbutton
.y_root
)))
1192 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1193 else if (menu_can_hide
)
1194 menu_frame_hide_all();
1197 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1198 ev
->xmotion
.y_root
))) {
1199 menu_frame_move_on_screen(f
);
1200 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1201 ev
->xmotion
.y_root
)))
1202 menu_frame_select(f
, e
);
1207 a
= find_active_menu();
1209 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1211 menu_frame_select(a
, NULL
);
1216 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1217 menu_frame_hide_all();
1218 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1220 if ((f
= find_active_menu()))
1221 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1222 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1224 if ((f
= find_active_menu()) && f
->parent
)
1225 menu_frame_select(f
, NULL
);
1226 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1228 if ((f
= find_active_menu()) && f
->child
)
1229 menu_frame_select_next(f
->child
);
1230 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1232 if ((f
= find_active_menu()))
1233 menu_frame_select_previous(f
);
1234 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1236 if ((f
= find_active_menu()))
1237 menu_frame_select_next(f
);
1243 static gboolean
menu_hide_delay_func(gpointer data
)
1245 menu_can_hide
= TRUE
;
1246 return FALSE
; /* no repeat */
1249 static gboolean
focus_delay_func(gpointer data
)
1251 client_focus(focus_delay_client
);
1252 return FALSE
; /* no repeat */
1255 static void focus_delay_client_dest(gpointer data
)
1258 if (c
== focus_delay_client
) {
1259 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1260 focus_delay_client
);
1261 focus_delay_client
= NULL
;