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 */
76 unsigned int NumLockMask
;
77 /*! The value of the mask for the ScrollLock modifier */
78 unsigned int ScrollLockMask
;
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap
*modmap
;
81 /*! Table of the constant modifier masks */
82 static const int mask_table
[] = {
83 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
84 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
86 static int 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(int 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
);
349 if (focus_out
== focus_client
)
350 focus_set_client(NULL
);
351 frame_adjust_focus(focus_out
->frame
, FALSE
);
352 client_calc_layer(focus_out
);
355 focus_hilite
= focus_in
;
357 if (focus_client
!= last
) {
362 /* is focus anywhere valid? */
363 XGetInputFocus(ob_display
, &w
, &r
);
364 if (!w
|| w
== RootWindow(ob_display
, ob_screen
))
365 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
370 focus_in
= focus_out
= NULL
;
373 static void event_process(const XEvent
*ec
, gpointer data
)
376 ObGroup
*group
= NULL
;
377 ObClient
*client
= NULL
;
379 ObDockApp
*dockapp
= NULL
;
380 ObWindow
*obwin
= NULL
;
383 /* make a copy we can mangle */
387 window
= event_get_window(e
);
388 if (!(e
->type
== PropertyNotify
&&
389 (group
= g_hash_table_lookup(group_map
, &window
))))
390 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
391 switch (obwin
->type
) {
393 dock
= WINDOW_AS_DOCK(obwin
);
396 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
399 client
= WINDOW_AS_CLIENT(obwin
);
402 case Window_Internal
:
403 /* not to be used for events */
404 g_assert_not_reached();
409 event_set_lasttime(e
);
411 if (event_ignore(e
, client
))
414 /* deal with it in the kernel */
416 event_handle_group(group
, e
);
418 event_handle_client(client
, e
);
420 event_handle_dockapp(dockapp
, e
);
422 event_handle_dock(dock
, e
);
423 else if (window
== RootWindow(ob_display
, ob_screen
))
424 event_handle_root(e
);
425 else if (e
->type
== MapRequest
)
426 client_manage(window
);
427 else if (e
->type
== ConfigureRequest
) {
428 /* unhandled configure requests must be used to configure the
432 xwc
.x
= e
->xconfigurerequest
.x
;
433 xwc
.y
= e
->xconfigurerequest
.y
;
434 xwc
.width
= e
->xconfigurerequest
.width
;
435 xwc
.height
= e
->xconfigurerequest
.height
;
436 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
437 xwc
.sibling
= e
->xconfigurerequest
.above
;
438 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
440 /* we are not to be held responsible if someone sends us an
442 xerror_set_ignore(TRUE
);
443 XConfigureWindow(ob_display
, window
,
444 e
->xconfigurerequest
.value_mask
, &xwc
);
445 xerror_set_ignore(FALSE
);
448 /* user input (action-bound) events */
449 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
450 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
451 e
->type
== KeyRelease
)
453 if (menu_frame_visible
)
454 event_handle_menu(e
);
456 if (!keyboard_process_interactive_grab(e
, &client
)) {
457 if (moveresize_in_progress
) {
460 /* make further actions work on the client being
462 client
= moveresize_client
;
465 menu_can_hide
= FALSE
;
466 ob_main_loop_timeout_add(ob_main_loop
,
468 menu_hide_delay_func
,
471 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
472 e
->type
== MotionNotify
)
473 mouse_event(client
, e
);
474 else if (e
->type
== KeyPress
)
475 keyboard_event((focus_cycle_target
? focus_cycle_target
:
476 (focus_hilite
? focus_hilite
: client
)),
483 static void event_handle_root(XEvent
*e
)
489 ob_debug("Another WM has requested to replace us. Exiting.\n");
494 if (e
->xclient
.format
!= 32) break;
496 msgtype
= e
->xclient
.message_type
;
497 if (msgtype
== prop_atoms
.net_current_desktop
) {
498 unsigned int d
= e
->xclient
.data
.l
[0];
499 if (d
< screen_num_desktops
)
500 screen_set_desktop(d
);
501 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
502 unsigned int d
= e
->xclient
.data
.l
[0];
504 screen_set_num_desktops(d
);
505 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
506 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
510 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
511 screen_update_desktop_names();
512 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
513 screen_update_layout();
515 case ConfigureNotify
:
517 XRRUpdateConfiguration(e
);
524 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
525 ob_debug("VIDMODE EVENT\n");
531 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
535 g_assert(e
->type
== PropertyNotify
);
537 for (it
= group
->members
; it
; it
= g_slist_next(it
))
538 event_handle_client(it
->data
, e
);
541 void event_enter_client(ObClient
*client
)
543 g_assert(config_focus_follow
);
545 if (client_normal(client
) && client_can_focus(client
)) {
546 if (config_focus_delay
) {
547 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
548 ob_main_loop_timeout_add(ob_main_loop
,
553 focus_delay_func(client
);
557 static void event_handle_client(ObClient
*client
, XEvent
*e
)
565 case VisibilityNotify
:
566 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
570 /* Wheel buttons don't draw because they are an instant click, so it
571 is a waste of resources to go drawing it. */
572 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
573 con
= frame_context(client
, e
->xbutton
.window
);
574 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
576 case OB_FRAME_CONTEXT_MAXIMIZE
:
577 client
->frame
->max_press
= (e
->type
== ButtonPress
);
578 framerender_frame(client
->frame
);
580 case OB_FRAME_CONTEXT_CLOSE
:
581 client
->frame
->close_press
= (e
->type
== ButtonPress
);
582 framerender_frame(client
->frame
);
584 case OB_FRAME_CONTEXT_ICONIFY
:
585 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
586 framerender_frame(client
->frame
);
588 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
589 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
590 framerender_frame(client
->frame
);
592 case OB_FRAME_CONTEXT_SHADE
:
593 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
594 framerender_frame(client
->frame
);
597 /* nothing changes with clicks for any other contexts */
604 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
605 e
->xfocus
.window
, client
->window
,
606 e
->xfocus
.mode
, e
->xfocus
.detail
);
609 if (focus_out
== client
)
614 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
615 e
->xfocus
.window
, client
->window
,
616 e
->xfocus
.mode
, e
->xfocus
.detail
);
619 if (focus_in
== client
)
623 con
= frame_context(client
, e
->xcrossing
.window
);
625 case OB_FRAME_CONTEXT_MAXIMIZE
:
626 client
->frame
->max_hover
= FALSE
;
627 frame_adjust_state(client
->frame
);
629 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
630 client
->frame
->desk_hover
= FALSE
;
631 frame_adjust_state(client
->frame
);
633 case OB_FRAME_CONTEXT_SHADE
:
634 client
->frame
->shade_hover
= FALSE
;
635 frame_adjust_state(client
->frame
);
637 case OB_FRAME_CONTEXT_ICONIFY
:
638 client
->frame
->iconify_hover
= FALSE
;
639 frame_adjust_state(client
->frame
);
641 case OB_FRAME_CONTEXT_CLOSE
:
642 client
->frame
->close_hover
= FALSE
;
643 frame_adjust_state(client
->frame
);
645 case OB_FRAME_CONTEXT_FRAME
:
646 if (config_focus_follow
&& config_focus_delay
)
647 ob_main_loop_timeout_remove_data(ob_main_loop
,
657 gboolean nofocus
= FALSE
;
659 if (ignore_enter_focus
) {
660 ignore_enter_focus
--;
664 con
= frame_context(client
, e
->xcrossing
.window
);
666 case OB_FRAME_CONTEXT_MAXIMIZE
:
667 client
->frame
->max_hover
= TRUE
;
668 frame_adjust_state(client
->frame
);
670 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
671 client
->frame
->desk_hover
= TRUE
;
672 frame_adjust_state(client
->frame
);
674 case OB_FRAME_CONTEXT_SHADE
:
675 client
->frame
->shade_hover
= TRUE
;
676 frame_adjust_state(client
->frame
);
678 case OB_FRAME_CONTEXT_ICONIFY
:
679 client
->frame
->iconify_hover
= TRUE
;
680 frame_adjust_state(client
->frame
);
682 case OB_FRAME_CONTEXT_CLOSE
:
683 client
->frame
->close_hover
= TRUE
;
684 frame_adjust_state(client
->frame
);
686 case OB_FRAME_CONTEXT_FRAME
:
687 if (e
->xcrossing
.mode
== NotifyGrab
||
688 e
->xcrossing
.mode
== NotifyUngrab
)
691 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
692 (e
->type
== EnterNotify
? "Enter" : "Leave"),
694 e
->xcrossing
.detail
, client
?client
->window
:0);
698 ob_debug("%sNotify mode %d detail %d on %lx, "
699 "focusing window: %d\n",
700 (e
->type
== EnterNotify
? "Enter" : "Leave"),
702 e
->xcrossing
.detail
, (client
?client
->window
:0),
705 if (!nofocus
&& config_focus_follow
)
706 event_enter_client(client
);
714 case ConfigureRequest
:
716 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
717 ConfigureRequest
, &ce
)) {
719 /* XXX if this causes bad things.. we can compress config req's
720 with the same mask. */
721 e
->xconfigurerequest
.value_mask
|=
722 ce
.xconfigurerequest
.value_mask
;
723 if (ce
.xconfigurerequest
.value_mask
& CWX
)
724 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
725 if (ce
.xconfigurerequest
.value_mask
& CWY
)
726 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
727 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
728 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
729 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
730 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
731 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
732 e
->xconfigurerequest
.border_width
=
733 ce
.xconfigurerequest
.border_width
;
734 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
735 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
738 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
739 if (client
->iconic
|| client
->shaded
) return;
741 /* resize, then move, as specified in the EWMH section 7.7 */
742 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
748 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
749 client
->border_width
= e
->xconfigurerequest
.border_width
;
751 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
752 e
->xconfigurerequest
.x
: client
->area
.x
;
753 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
754 e
->xconfigurerequest
.y
: client
->area
.y
;
755 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
756 e
->xconfigurerequest
.width
: client
->area
.width
;
757 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
758 e
->xconfigurerequest
.height
: client
->area
.height
;
764 client
->frame
->size
.left
+ client
->frame
->size
.right
;
766 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
767 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
768 client_normal(client
));
769 if (e
->xconfigurerequest
.value_mask
& CWX
)
771 if (e
->xconfigurerequest
.value_mask
& CWY
)
775 switch (client
->gravity
) {
776 case NorthEastGravity
:
778 corner
= OB_CORNER_TOPRIGHT
;
780 case SouthWestGravity
:
782 corner
= OB_CORNER_BOTTOMLEFT
;
784 case SouthEastGravity
:
785 corner
= OB_CORNER_BOTTOMRIGHT
;
787 default: /* NorthWest, Static, etc */
788 corner
= OB_CORNER_TOPLEFT
;
791 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
795 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
796 switch (e
->xconfigurerequest
.detail
) {
799 client_lower(client
);
805 client_raise(client
);
811 if (client
->ignore_unmaps
) {
812 client
->ignore_unmaps
--;
815 client_unmanage(client
);
818 client_unmanage(client
);
821 /* this is when the client is first taken captive in the frame */
822 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
825 This event is quite rare and is usually handled in unmapHandler.
826 However, if the window is unmapped when the reparent event occurs,
827 the window manager never sees it because an unmap event is not sent
828 to an already unmapped window.
831 /* we don't want the reparent event, put it back on the stack for the
832 X server to deal with after we unmanage the window */
833 XPutBackEvent(ob_display
, e
);
835 client_unmanage(client
);
838 ob_debug("MapRequest for 0x%lx\n", client
->window
);
839 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
840 does, we don't want it! */
841 client_activate(client
, FALSE
);
844 /* validate cuz we query stuff off the client here */
845 if (!client_validate(client
)) break;
847 if (e
->xclient
.format
!= 32) return;
849 msgtype
= e
->xclient
.message_type
;
850 if (msgtype
== prop_atoms
.wm_change_state
) {
851 /* compress changes into a single change */
852 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
854 /* XXX: it would be nice to compress ALL messages of a
855 type, not just messages in a row without other
856 message types between. */
857 if (ce
.xclient
.message_type
!= msgtype
) {
858 XPutBackEvent(ob_display
, &ce
);
861 e
->xclient
= ce
.xclient
;
863 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
864 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
865 /* compress changes into a single change */
866 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
868 /* XXX: it would be nice to compress ALL messages of a
869 type, not just messages in a row without other
870 message types between. */
871 if (ce
.xclient
.message_type
!= msgtype
) {
872 XPutBackEvent(ob_display
, &ce
);
875 e
->xclient
= ce
.xclient
;
877 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
878 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
879 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
881 } else if (msgtype
== prop_atoms
.net_wm_state
) {
882 /* can't compress these */
883 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
884 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
885 e
->xclient
.data
.l
[0] == 1 ? "Add" :
886 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
887 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
889 client_set_state(client
, e
->xclient
.data
.l
[0],
890 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
891 } else if (msgtype
== prop_atoms
.net_close_window
) {
892 ob_debug("net_close_window for 0x%lx\n", client
->window
);
893 client_close(client
);
894 } else if (msgtype
== prop_atoms
.net_active_window
) {
895 ob_debug("net_active_window for 0x%lx\n", client
->window
);
896 client_activate(client
, FALSE
);
897 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
898 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
899 if ((Atom
)e
->xclient
.data
.l
[2] ==
900 prop_atoms
.net_wm_moveresize_size_topleft
||
901 (Atom
)e
->xclient
.data
.l
[2] ==
902 prop_atoms
.net_wm_moveresize_size_top
||
903 (Atom
)e
->xclient
.data
.l
[2] ==
904 prop_atoms
.net_wm_moveresize_size_topright
||
905 (Atom
)e
->xclient
.data
.l
[2] ==
906 prop_atoms
.net_wm_moveresize_size_right
||
907 (Atom
)e
->xclient
.data
.l
[2] ==
908 prop_atoms
.net_wm_moveresize_size_right
||
909 (Atom
)e
->xclient
.data
.l
[2] ==
910 prop_atoms
.net_wm_moveresize_size_bottomright
||
911 (Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_bottom
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_size_bottomleft
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_left
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_move
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_keyboard
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_move_keyboard
) {
924 moveresize_start(client
, e
->xclient
.data
.l
[0],
925 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
926 e
->xclient
.data
.l
[2]);
928 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
929 int oldg
= client
->gravity
;
930 int tmpg
, x
, y
, w
, h
;
932 if (e
->xclient
.data
.l
[0] & 0xff)
933 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
937 if (e
->xclient
.data
.l
[0] & 1 << 8)
938 x
= e
->xclient
.data
.l
[1];
941 if (e
->xclient
.data
.l
[0] & 1 << 9)
942 y
= e
->xclient
.data
.l
[2];
945 if (e
->xclient
.data
.l
[0] & 1 << 10)
946 w
= e
->xclient
.data
.l
[3];
948 w
= client
->area
.width
;
949 if (e
->xclient
.data
.l
[0] & 1 << 11)
950 h
= e
->xclient
.data
.l
[4];
952 h
= client
->area
.height
;
953 client
->gravity
= tmpg
;
959 client
->frame
->size
.left
+ client
->frame
->size
.right
;
961 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
962 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
963 client_normal(client
));
964 if (e
->xclient
.data
.l
[0] & 1 << 8)
966 if (e
->xclient
.data
.l
[0] & 1 << 9)
970 client_configure(client
, OB_CORNER_TOPLEFT
,
971 x
, y
, w
, h
, FALSE
, TRUE
);
973 client
->gravity
= oldg
;
977 /* validate cuz we query stuff off the client here */
978 if (!client_validate(client
)) break;
980 /* compress changes to a single property into a single change */
981 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
985 /* XXX: it would be nice to compress ALL changes to a property,
986 not just changes in a row without other props between. */
988 a
= ce
.xproperty
.atom
;
989 b
= e
->xproperty
.atom
;
993 if ((a
== prop_atoms
.net_wm_name
||
994 a
== prop_atoms
.wm_name
||
995 a
== prop_atoms
.net_wm_icon_name
||
996 a
== prop_atoms
.wm_icon_name
)
998 (b
== prop_atoms
.net_wm_name
||
999 b
== prop_atoms
.wm_name
||
1000 b
== prop_atoms
.net_wm_icon_name
||
1001 b
== prop_atoms
.wm_icon_name
)) {
1004 if ((a
== prop_atoms
.net_wm_icon
||
1005 a
== prop_atoms
.kwm_win_icon
)
1007 (b
== prop_atoms
.net_wm_icon
||
1008 b
== prop_atoms
.kwm_win_icon
))
1011 XPutBackEvent(ob_display
, &ce
);
1015 msgtype
= e
->xproperty
.atom
;
1016 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1017 client_update_normal_hints(client
);
1018 /* normal hints can make a window non-resizable */
1019 client_setup_decor_and_functions(client
);
1020 } else if (msgtype
== XA_WM_HINTS
) {
1021 client_update_wmhints(client
);
1022 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1023 client_update_transient_for(client
);
1024 client_get_type(client
);
1025 /* type may have changed, so update the layer */
1026 client_calc_layer(client
);
1027 client_setup_decor_and_functions(client
);
1028 } else if (msgtype
== prop_atoms
.net_wm_name
||
1029 msgtype
== prop_atoms
.wm_name
||
1030 msgtype
== prop_atoms
.net_wm_icon_name
||
1031 msgtype
== prop_atoms
.wm_icon_name
) {
1032 client_update_title(client
);
1033 } else if (msgtype
== prop_atoms
.wm_class
) {
1034 client_update_class(client
);
1035 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1036 client_update_protocols(client
);
1037 client_setup_decor_and_functions(client
);
1039 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1040 client_update_strut(client
);
1042 else if (msgtype
== prop_atoms
.net_wm_icon
||
1043 msgtype
== prop_atoms
.kwm_win_icon
) {
1044 client_update_icons(client
);
1046 else if (msgtype
== prop_atoms
.sm_client_id
) {
1047 client_update_sm_client_id(client
);
1052 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1053 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1054 frame_adjust_shape(client
->frame
);
1060 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1064 stacking_raise(DOCK_AS_WINDOW(s
));
1075 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1079 dock_app_drag(app
, &e
->xmotion
);
1082 if (app
->ignore_unmaps
) {
1083 app
->ignore_unmaps
--;
1086 dock_remove(app
, TRUE
);
1089 dock_remove(app
, FALSE
);
1091 case ReparentNotify
:
1092 dock_remove(app
, FALSE
);
1094 case ConfigureNotify
:
1095 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1100 ObMenuFrame
* find_active_menu()
1103 ObMenuFrame
*ret
= NULL
;
1105 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1114 ObMenuFrame
* find_active_or_last_menu()
1116 ObMenuFrame
*ret
= NULL
;
1118 ret
= find_active_menu();
1119 if (!ret
&& menu_frame_visible
)
1120 ret
= menu_frame_visible
->data
;
1124 static void event_handle_menu(XEvent
*ev
)
1127 ObMenuEntryFrame
*e
;
1131 if (menu_can_hide
) {
1132 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1133 ev
->xbutton
.y_root
)))
1134 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1136 menu_frame_hide_all();
1140 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1141 ev
->xmotion
.y_root
))) {
1142 menu_frame_move_on_screen(f
);
1143 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1144 ev
->xmotion
.y_root
)))
1145 menu_frame_select(f
, e
);
1150 a
= find_active_menu();
1152 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1154 menu_frame_select(a
, NULL
);
1159 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1160 menu_frame_hide_all();
1161 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1163 if ((f
= find_active_menu()))
1164 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1165 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1167 if ((f
= find_active_or_last_menu()) && f
->parent
)
1168 menu_frame_select(f
, NULL
);
1169 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1171 if ((f
= find_active_or_last_menu()) && f
->child
)
1172 menu_frame_select_next(f
->child
);
1173 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1175 if ((f
= find_active_or_last_menu()))
1176 menu_frame_select_previous(f
);
1177 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1179 if ((f
= find_active_or_last_menu()))
1180 menu_frame_select_next(f
);
1186 static gboolean
menu_hide_delay_func(gpointer data
)
1188 menu_can_hide
= TRUE
;
1189 return FALSE
; /* no repeat */
1192 static gboolean
focus_delay_func(gpointer data
)
1196 if (focus_client
!= c
) {
1198 if (config_focus_raise
)
1201 return FALSE
; /* no repeat */
1204 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1206 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1209 void event_halt_focus_delay()
1211 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1214 void event_ignore_queued_enters()
1216 GSList
*saved
= NULL
, *it
;
1219 XSync(ob_display
, FALSE
);
1221 /* count the events */
1223 e
= g_new(XEvent
, 1);
1224 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1227 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1228 if (win
&& WINDOW_IS_CLIENT(win
))
1229 ++ignore_enter_focus
;
1231 saved
= g_slist_append(saved
, e
);
1237 /* put the events back */
1238 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1239 XPutBackEvent(ob_display
, it
->data
);
1242 g_slist_free(saved
);