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 it can happen now when the window is on
851 another desktop, but we still don't
853 client_activate(client
, FALSE
);
856 /* validate cuz we query stuff off the client here */
857 if (!client_validate(client
)) break;
859 if (e
->xclient
.format
!= 32) return;
861 msgtype
= e
->xclient
.message_type
;
862 if (msgtype
== prop_atoms
.wm_change_state
) {
863 /* compress changes into a single change */
864 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
866 /* XXX: it would be nice to compress ALL messages of a
867 type, not just messages in a row without other
868 message types between. */
869 if (ce
.xclient
.message_type
!= msgtype
) {
870 XPutBackEvent(ob_display
, &ce
);
873 e
->xclient
= ce
.xclient
;
875 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
876 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
877 /* compress changes into a single change */
878 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
880 /* XXX: it would be nice to compress ALL messages of a
881 type, not just messages in a row without other
882 message types between. */
883 if (ce
.xclient
.message_type
!= msgtype
) {
884 XPutBackEvent(ob_display
, &ce
);
887 e
->xclient
= ce
.xclient
;
889 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
890 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
891 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
893 } else if (msgtype
== prop_atoms
.net_wm_state
) {
894 /* can't compress these */
895 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
896 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
897 e
->xclient
.data
.l
[0] == 1 ? "Add" :
898 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
899 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
901 client_set_state(client
, e
->xclient
.data
.l
[0],
902 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
903 } else if (msgtype
== prop_atoms
.net_close_window
) {
904 ob_debug("net_close_window for 0x%lx\n", client
->window
);
905 client_close(client
);
906 } else if (msgtype
== prop_atoms
.net_active_window
) {
907 ob_debug("net_active_window for 0x%lx\n", client
->window
);
908 client_activate(client
, FALSE
);
909 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
910 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
911 if ((Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_topleft
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_size_top
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_topright
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_right
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_right
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_size_bottomright
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_bottom
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_bottomleft
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_size_left
||
929 (Atom
)e
->xclient
.data
.l
[2] ==
930 prop_atoms
.net_wm_moveresize_move
||
931 (Atom
)e
->xclient
.data
.l
[2] ==
932 prop_atoms
.net_wm_moveresize_size_keyboard
||
933 (Atom
)e
->xclient
.data
.l
[2] ==
934 prop_atoms
.net_wm_moveresize_move_keyboard
) {
936 moveresize_start(client
, e
->xclient
.data
.l
[0],
937 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
938 e
->xclient
.data
.l
[2]);
940 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
941 gint oldg
= client
->gravity
;
942 gint tmpg
, x
, y
, w
, h
;
944 if (e
->xclient
.data
.l
[0] & 0xff)
945 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
949 if (e
->xclient
.data
.l
[0] & 1 << 8)
950 x
= e
->xclient
.data
.l
[1];
953 if (e
->xclient
.data
.l
[0] & 1 << 9)
954 y
= e
->xclient
.data
.l
[2];
957 if (e
->xclient
.data
.l
[0] & 1 << 10)
958 w
= e
->xclient
.data
.l
[3];
960 w
= client
->area
.width
;
961 if (e
->xclient
.data
.l
[0] & 1 << 11)
962 h
= e
->xclient
.data
.l
[4];
964 h
= client
->area
.height
;
965 client
->gravity
= tmpg
;
971 client
->frame
->size
.left
+ client
->frame
->size
.right
;
973 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
974 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
975 client_normal(client
));
976 if (e
->xclient
.data
.l
[0] & 1 << 8)
978 if (e
->xclient
.data
.l
[0] & 1 << 9)
982 client_configure(client
, OB_CORNER_TOPLEFT
,
983 x
, y
, w
, h
, FALSE
, TRUE
);
985 client
->gravity
= oldg
;
989 /* validate cuz we query stuff off the client here */
990 if (!client_validate(client
)) break;
992 /* compress changes to a single property into a single change */
993 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
997 /* XXX: it would be nice to compress ALL changes to a property,
998 not just changes in a row without other props between. */
1000 a
= ce
.xproperty
.atom
;
1001 b
= e
->xproperty
.atom
;
1005 if ((a
== prop_atoms
.net_wm_name
||
1006 a
== prop_atoms
.wm_name
||
1007 a
== prop_atoms
.net_wm_icon_name
||
1008 a
== prop_atoms
.wm_icon_name
)
1010 (b
== prop_atoms
.net_wm_name
||
1011 b
== prop_atoms
.wm_name
||
1012 b
== prop_atoms
.net_wm_icon_name
||
1013 b
== prop_atoms
.wm_icon_name
)) {
1016 if ((a
== prop_atoms
.net_wm_icon
||
1017 a
== prop_atoms
.kwm_win_icon
)
1019 (b
== prop_atoms
.net_wm_icon
||
1020 b
== prop_atoms
.kwm_win_icon
))
1023 XPutBackEvent(ob_display
, &ce
);
1027 msgtype
= e
->xproperty
.atom
;
1028 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1029 client_update_normal_hints(client
);
1030 /* normal hints can make a window non-resizable */
1031 client_setup_decor_and_functions(client
);
1032 } else if (msgtype
== XA_WM_HINTS
) {
1033 client_update_wmhints(client
);
1034 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1035 client_update_transient_for(client
);
1036 client_get_type(client
);
1037 /* type may have changed, so update the layer */
1038 client_calc_layer(client
);
1039 client_setup_decor_and_functions(client
);
1040 } else if (msgtype
== prop_atoms
.net_wm_name
||
1041 msgtype
== prop_atoms
.wm_name
||
1042 msgtype
== prop_atoms
.net_wm_icon_name
||
1043 msgtype
== prop_atoms
.wm_icon_name
) {
1044 client_update_title(client
);
1045 } else if (msgtype
== prop_atoms
.wm_class
) {
1046 client_update_class(client
);
1047 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1048 client_update_protocols(client
);
1049 client_setup_decor_and_functions(client
);
1051 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1052 client_update_strut(client
);
1054 else if (msgtype
== prop_atoms
.net_wm_icon
||
1055 msgtype
== prop_atoms
.kwm_win_icon
) {
1056 client_update_icons(client
);
1058 else if (msgtype
== prop_atoms
.sm_client_id
) {
1059 client_update_sm_client_id(client
);
1064 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1065 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1066 frame_adjust_shape(client
->frame
);
1072 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1076 stacking_raise(DOCK_AS_WINDOW(s
));
1087 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1091 dock_app_drag(app
, &e
->xmotion
);
1094 if (app
->ignore_unmaps
) {
1095 app
->ignore_unmaps
--;
1098 dock_remove(app
, TRUE
);
1101 dock_remove(app
, FALSE
);
1103 case ReparentNotify
:
1104 dock_remove(app
, FALSE
);
1106 case ConfigureNotify
:
1107 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1112 ObMenuFrame
* find_active_menu()
1115 ObMenuFrame
*ret
= NULL
;
1117 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1126 ObMenuFrame
* find_active_or_last_menu()
1128 ObMenuFrame
*ret
= NULL
;
1130 ret
= find_active_menu();
1131 if (!ret
&& menu_frame_visible
)
1132 ret
= menu_frame_visible
->data
;
1136 static void event_handle_menu(XEvent
*ev
)
1139 ObMenuEntryFrame
*e
;
1143 if (menu_can_hide
) {
1144 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1145 ev
->xbutton
.y_root
)))
1146 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1148 menu_frame_hide_all();
1152 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1153 ev
->xmotion
.y_root
))) {
1154 menu_frame_move_on_screen(f
);
1155 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1156 ev
->xmotion
.y_root
)))
1157 menu_frame_select(f
, e
);
1162 a
= find_active_menu();
1164 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1166 menu_frame_select(a
, NULL
);
1171 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1172 menu_frame_hide_all();
1173 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1175 if ((f
= find_active_menu()))
1176 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1177 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1179 if ((f
= find_active_or_last_menu()) && f
->parent
)
1180 menu_frame_select(f
, NULL
);
1181 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1183 if ((f
= find_active_or_last_menu()) && f
->child
)
1184 menu_frame_select_next(f
->child
);
1185 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1187 if ((f
= find_active_or_last_menu()))
1188 menu_frame_select_previous(f
);
1189 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1191 if ((f
= find_active_or_last_menu()))
1192 menu_frame_select_next(f
);
1198 static gboolean
menu_hide_delay_func(gpointer data
)
1200 menu_can_hide
= TRUE
;
1201 return FALSE
; /* no repeat */
1204 static gboolean
focus_delay_func(gpointer data
)
1208 if (focus_client
!= c
) {
1210 if (config_focus_raise
)
1213 return FALSE
; /* no repeat */
1216 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1218 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1221 void event_halt_focus_delay()
1223 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1226 void event_ignore_queued_enters()
1228 GSList
*saved
= NULL
, *it
;
1231 XSync(ob_display
, FALSE
);
1233 /* count the events */
1235 e
= g_new(XEvent
, 1);
1236 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1239 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1240 if (win
&& WINDOW_IS_CLIENT(win
))
1241 ++ignore_enter_focus
;
1243 saved
= g_slist_append(saved
, e
);
1249 /* put the events back */
1250 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1251 XPutBackEvent(ob_display
, it
->data
);
1254 g_slist_free(saved
);