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
)
326 static void event_done(gpointer data
)
328 static ObClient
*last
= NULL
;
331 if (focus_in
!= focus_client
) {
332 focus_set_client(focus_in
);
333 frame_adjust_focus(focus_in
->frame
, TRUE
);
334 client_calc_layer(focus_in
);
338 if (focus_out
== focus_client
)
339 focus_set_client(NULL
);
340 frame_adjust_focus(focus_out
->frame
, FALSE
);
341 client_calc_layer(focus_out
);
344 if (focus_client
!= last
) {
346 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
350 focus_in
= focus_out
= NULL
;
353 static void event_process(const XEvent
*ec
, gpointer data
)
356 ObGroup
*group
= NULL
;
357 ObClient
*client
= NULL
;
359 ObDockApp
*dockapp
= NULL
;
360 ObWindow
*obwin
= NULL
;
363 /* make a copy we can mangle */
367 window
= event_get_window(e
);
368 if (!(e
->type
== PropertyNotify
&&
369 (group
= g_hash_table_lookup(group_map
, &window
))))
370 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
371 switch (obwin
->type
) {
373 dock
= WINDOW_AS_DOCK(obwin
);
376 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
379 client
= WINDOW_AS_CLIENT(obwin
);
382 case Window_Internal
:
383 /* not to be used for events */
384 g_assert_not_reached();
389 event_set_lasttime(e
);
391 if (event_ignore(e
, client
))
394 /* deal with it in the kernel */
396 event_handle_group(group
, e
);
398 event_handle_client(client
, e
);
400 event_handle_dockapp(dockapp
, e
);
402 event_handle_dock(dock
, e
);
403 else if (window
== RootWindow(ob_display
, ob_screen
))
404 event_handle_root(e
);
405 else if (e
->type
== MapRequest
)
406 client_manage(window
);
407 else if (e
->type
== ConfigureRequest
) {
408 /* unhandled configure requests must be used to configure the
412 xwc
.x
= e
->xconfigurerequest
.x
;
413 xwc
.y
= e
->xconfigurerequest
.y
;
414 xwc
.width
= e
->xconfigurerequest
.width
;
415 xwc
.height
= e
->xconfigurerequest
.height
;
416 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
417 xwc
.sibling
= e
->xconfigurerequest
.above
;
418 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
420 /* we are not to be held responsible if someone sends us an
422 xerror_set_ignore(TRUE
);
423 XConfigureWindow(ob_display
, window
,
424 e
->xconfigurerequest
.value_mask
, &xwc
);
425 xerror_set_ignore(FALSE
);
428 /* user input (action-bound) events */
429 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
430 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
431 e
->type
== KeyRelease
)
433 if (menu_frame_visible
)
434 event_handle_menu(e
);
436 if (!keyboard_process_interactive_grab(e
, &client
)) {
437 if (moveresize_in_progress
) {
440 /* make further actions work on the client being
442 client
= moveresize_client
;
445 menu_can_hide
= FALSE
;
446 ob_main_loop_timeout_add(ob_main_loop
,
448 menu_hide_delay_func
,
451 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
452 e
->type
== MotionNotify
)
453 mouse_event(client
, e
);
454 else if (e
->type
== KeyPress
)
455 /* when in the middle of a focus cycling action, this
456 causes the window which appears to be focused to be
457 the one on which the actions will be executed */
458 keyboard_event((focus_cycle_target
?
459 focus_cycle_target
: client
), e
);
465 static void event_handle_root(XEvent
*e
)
471 ob_debug("Another WM has requested to replace us. Exiting.\n");
476 if (e
->xclient
.format
!= 32) break;
478 msgtype
= e
->xclient
.message_type
;
479 if (msgtype
== prop_atoms
.net_current_desktop
) {
480 unsigned int d
= e
->xclient
.data
.l
[0];
481 if (d
< screen_num_desktops
)
482 screen_set_desktop(d
);
483 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
484 unsigned int d
= e
->xclient
.data
.l
[0];
486 screen_set_num_desktops(d
);
487 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
488 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
492 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
493 screen_update_desktop_names();
494 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
495 screen_update_layout();
497 case ConfigureNotify
:
499 XRRUpdateConfiguration(e
);
506 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
507 ob_debug("VIDMODE EVENT\n");
513 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
517 g_assert(e
->type
== PropertyNotify
);
519 for (it
= group
->members
; it
; it
= g_slist_next(it
))
520 event_handle_client(it
->data
, e
);
523 void event_enter_client(ObClient
*client
)
525 g_assert(config_focus_follow
);
527 if (client_normal(client
) && client_can_focus(client
)) {
528 if (config_focus_delay
) {
529 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
530 ob_main_loop_timeout_add(ob_main_loop
,
535 focus_delay_func(client
);
539 static void event_handle_client(ObClient
*client
, XEvent
*e
)
547 case VisibilityNotify
:
548 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
552 /* Wheel buttons don't draw because they are an instant click, so it
553 is a waste of resources to go drawing it. */
554 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
555 con
= frame_context(client
, e
->xbutton
.window
);
556 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
558 case OB_FRAME_CONTEXT_MAXIMIZE
:
559 client
->frame
->max_press
= (e
->type
== ButtonPress
);
560 framerender_frame(client
->frame
);
562 case OB_FRAME_CONTEXT_CLOSE
:
563 client
->frame
->close_press
= (e
->type
== ButtonPress
);
564 framerender_frame(client
->frame
);
566 case OB_FRAME_CONTEXT_ICONIFY
:
567 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
568 framerender_frame(client
->frame
);
570 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
571 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
572 framerender_frame(client
->frame
);
574 case OB_FRAME_CONTEXT_SHADE
:
575 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
576 framerender_frame(client
->frame
);
579 /* nothing changes with clicks for any other contexts */
586 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
587 e
->xfocus
.window
, client
->window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
590 if (focus_out
== client
)
595 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
596 e
->xfocus
.window
, client
->window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
598 if (focus_in
== client
)
600 if (client
== focus_client
)
604 con
= frame_context(client
, e
->xcrossing
.window
);
606 case OB_FRAME_CONTEXT_MAXIMIZE
:
607 client
->frame
->max_hover
= FALSE
;
608 frame_adjust_state(client
->frame
);
610 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
611 client
->frame
->desk_hover
= FALSE
;
612 frame_adjust_state(client
->frame
);
614 case OB_FRAME_CONTEXT_SHADE
:
615 client
->frame
->shade_hover
= FALSE
;
616 frame_adjust_state(client
->frame
);
618 case OB_FRAME_CONTEXT_ICONIFY
:
619 client
->frame
->iconify_hover
= FALSE
;
620 frame_adjust_state(client
->frame
);
622 case OB_FRAME_CONTEXT_CLOSE
:
623 client
->frame
->close_hover
= FALSE
;
624 frame_adjust_state(client
->frame
);
626 case OB_FRAME_CONTEXT_FRAME
:
628 if (config_focus_follow && config_focus_delay)
629 ob_main_loop_timeout_remove_data(ob_main_loop,
640 gboolean nofocus
= FALSE
;
642 if (ignore_enter_focus
) {
643 ignore_enter_focus
--;
647 con
= frame_context(client
, e
->xcrossing
.window
);
649 case OB_FRAME_CONTEXT_MAXIMIZE
:
650 client
->frame
->max_hover
= TRUE
;
651 frame_adjust_state(client
->frame
);
653 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
654 client
->frame
->desk_hover
= TRUE
;
655 frame_adjust_state(client
->frame
);
657 case OB_FRAME_CONTEXT_SHADE
:
658 client
->frame
->shade_hover
= TRUE
;
659 frame_adjust_state(client
->frame
);
661 case OB_FRAME_CONTEXT_ICONIFY
:
662 client
->frame
->iconify_hover
= TRUE
;
663 frame_adjust_state(client
->frame
);
665 case OB_FRAME_CONTEXT_CLOSE
:
666 client
->frame
->close_hover
= TRUE
;
667 frame_adjust_state(client
->frame
);
669 case OB_FRAME_CONTEXT_FRAME
:
670 if (e
->xcrossing
.mode
== NotifyGrab
||
671 e
->xcrossing
.mode
== NotifyUngrab
)
674 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
675 (e
->type
== EnterNotify
? "Enter" : "Leave"),
677 e
->xcrossing
.detail
, client
?client
->window
:0);
681 ob_debug("%sNotify mode %d detail %d on %lx, "
682 "focusing window: %d\n",
683 (e
->type
== EnterNotify
? "Enter" : "Leave"),
685 e
->xcrossing
.detail
, (client
?client
->window
:0),
688 if (!nofocus
&& config_focus_follow
)
689 event_enter_client(client
);
697 case ConfigureRequest
:
699 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
700 ConfigureRequest
, &ce
)) {
702 /* XXX if this causes bad things.. we can compress config req's
703 with the same mask. */
704 e
->xconfigurerequest
.value_mask
|=
705 ce
.xconfigurerequest
.value_mask
;
706 if (ce
.xconfigurerequest
.value_mask
& CWX
)
707 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
708 if (ce
.xconfigurerequest
.value_mask
& CWY
)
709 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
710 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
711 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
712 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
713 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
714 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
715 e
->xconfigurerequest
.border_width
=
716 ce
.xconfigurerequest
.border_width
;
717 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
718 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
721 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
722 if (client
->iconic
|| client
->shaded
) return;
724 /* resize, then move, as specified in the EWMH section 7.7 */
725 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
731 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
732 client
->border_width
= e
->xconfigurerequest
.border_width
;
734 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
735 e
->xconfigurerequest
.x
: client
->area
.x
;
736 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
737 e
->xconfigurerequest
.y
: client
->area
.y
;
738 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
739 e
->xconfigurerequest
.width
: client
->area
.width
;
740 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
741 e
->xconfigurerequest
.height
: client
->area
.height
;
747 client
->frame
->size
.left
+ client
->frame
->size
.right
;
749 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
750 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
751 client_normal(client
));
752 if (e
->xconfigurerequest
.value_mask
& CWX
)
754 if (e
->xconfigurerequest
.value_mask
& CWY
)
758 switch (client
->gravity
) {
759 case NorthEastGravity
:
761 corner
= OB_CORNER_TOPRIGHT
;
763 case SouthWestGravity
:
765 corner
= OB_CORNER_BOTTOMLEFT
;
767 case SouthEastGravity
:
768 corner
= OB_CORNER_BOTTOMRIGHT
;
770 default: /* NorthWest, Static, etc */
771 corner
= OB_CORNER_TOPLEFT
;
774 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
778 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
779 switch (e
->xconfigurerequest
.detail
) {
782 client_lower(client
);
788 client_raise(client
);
794 if (client
->ignore_unmaps
) {
795 client
->ignore_unmaps
--;
798 client_unmanage(client
);
801 client_unmanage(client
);
804 /* this is when the client is first taken captive in the frame */
805 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
808 This event is quite rare and is usually handled in unmapHandler.
809 However, if the window is unmapped when the reparent event occurs,
810 the window manager never sees it because an unmap event is not sent
811 to an already unmapped window.
814 /* we don't want the reparent event, put it back on the stack for the
815 X server to deal with after we unmanage the window */
816 XPutBackEvent(ob_display
, e
);
818 client_unmanage(client
);
821 ob_debug("MapRequest for 0x%lx\n", client
->window
);
822 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
823 does, we don't want it! */
824 client_activate(client
, FALSE
);
827 /* validate cuz we query stuff off the client here */
828 if (!client_validate(client
)) break;
830 if (e
->xclient
.format
!= 32) return;
832 msgtype
= e
->xclient
.message_type
;
833 if (msgtype
== prop_atoms
.wm_change_state
) {
834 /* compress changes into a single change */
835 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
837 /* XXX: it would be nice to compress ALL messages of a
838 type, not just messages in a row without other
839 message types between. */
840 if (ce
.xclient
.message_type
!= msgtype
) {
841 XPutBackEvent(ob_display
, &ce
);
844 e
->xclient
= ce
.xclient
;
846 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
847 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
848 /* compress changes into a single change */
849 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
851 /* XXX: it would be nice to compress ALL messages of a
852 type, not just messages in a row without other
853 message types between. */
854 if (ce
.xclient
.message_type
!= msgtype
) {
855 XPutBackEvent(ob_display
, &ce
);
858 e
->xclient
= ce
.xclient
;
860 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
861 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
862 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
864 } else if (msgtype
== prop_atoms
.net_wm_state
) {
865 /* can't compress these */
866 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
867 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
868 e
->xclient
.data
.l
[0] == 1 ? "Add" :
869 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
870 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
872 client_set_state(client
, e
->xclient
.data
.l
[0],
873 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
874 } else if (msgtype
== prop_atoms
.net_close_window
) {
875 ob_debug("net_close_window for 0x%lx\n", client
->window
);
876 client_close(client
);
877 } else if (msgtype
== prop_atoms
.net_active_window
) {
878 ob_debug("net_active_window for 0x%lx\n", client
->window
);
879 client_activate(client
, FALSE
);
880 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
881 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
882 if ((Atom
)e
->xclient
.data
.l
[2] ==
883 prop_atoms
.net_wm_moveresize_size_topleft
||
884 (Atom
)e
->xclient
.data
.l
[2] ==
885 prop_atoms
.net_wm_moveresize_size_top
||
886 (Atom
)e
->xclient
.data
.l
[2] ==
887 prop_atoms
.net_wm_moveresize_size_topright
||
888 (Atom
)e
->xclient
.data
.l
[2] ==
889 prop_atoms
.net_wm_moveresize_size_right
||
890 (Atom
)e
->xclient
.data
.l
[2] ==
891 prop_atoms
.net_wm_moveresize_size_right
||
892 (Atom
)e
->xclient
.data
.l
[2] ==
893 prop_atoms
.net_wm_moveresize_size_bottomright
||
894 (Atom
)e
->xclient
.data
.l
[2] ==
895 prop_atoms
.net_wm_moveresize_size_bottom
||
896 (Atom
)e
->xclient
.data
.l
[2] ==
897 prop_atoms
.net_wm_moveresize_size_bottomleft
||
898 (Atom
)e
->xclient
.data
.l
[2] ==
899 prop_atoms
.net_wm_moveresize_size_left
||
900 (Atom
)e
->xclient
.data
.l
[2] ==
901 prop_atoms
.net_wm_moveresize_move
||
902 (Atom
)e
->xclient
.data
.l
[2] ==
903 prop_atoms
.net_wm_moveresize_size_keyboard
||
904 (Atom
)e
->xclient
.data
.l
[2] ==
905 prop_atoms
.net_wm_moveresize_move_keyboard
) {
907 moveresize_start(client
, e
->xclient
.data
.l
[0],
908 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
909 e
->xclient
.data
.l
[2]);
911 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
912 int oldg
= client
->gravity
;
913 int tmpg
, x
, y
, w
, h
;
915 if (e
->xclient
.data
.l
[0] & 0xff)
916 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
920 if (e
->xclient
.data
.l
[0] & 1 << 8)
921 x
= e
->xclient
.data
.l
[1];
924 if (e
->xclient
.data
.l
[0] & 1 << 9)
925 y
= e
->xclient
.data
.l
[2];
928 if (e
->xclient
.data
.l
[0] & 1 << 10)
929 w
= e
->xclient
.data
.l
[3];
931 w
= client
->area
.width
;
932 if (e
->xclient
.data
.l
[0] & 1 << 11)
933 h
= e
->xclient
.data
.l
[4];
935 h
= client
->area
.height
;
936 client
->gravity
= tmpg
;
942 client
->frame
->size
.left
+ client
->frame
->size
.right
;
944 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
945 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
946 client_normal(client
));
947 if (e
->xclient
.data
.l
[0] & 1 << 8)
949 if (e
->xclient
.data
.l
[0] & 1 << 9)
953 client_configure(client
, OB_CORNER_TOPLEFT
,
954 x
, y
, w
, h
, FALSE
, TRUE
);
956 client
->gravity
= oldg
;
960 /* validate cuz we query stuff off the client here */
961 if (!client_validate(client
)) break;
963 /* compress changes to a single property into a single change */
964 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
968 /* XXX: it would be nice to compress ALL changes to a property,
969 not just changes in a row without other props between. */
971 a
= ce
.xproperty
.atom
;
972 b
= e
->xproperty
.atom
;
976 if ((a
== prop_atoms
.net_wm_name
||
977 a
== prop_atoms
.wm_name
||
978 a
== prop_atoms
.net_wm_icon_name
||
979 a
== prop_atoms
.wm_icon_name
)
981 (b
== prop_atoms
.net_wm_name
||
982 b
== prop_atoms
.wm_name
||
983 b
== prop_atoms
.net_wm_icon_name
||
984 b
== prop_atoms
.wm_icon_name
)) {
987 if ((a
== prop_atoms
.net_wm_icon
||
988 a
== prop_atoms
.kwm_win_icon
)
990 (b
== prop_atoms
.net_wm_icon
||
991 b
== prop_atoms
.kwm_win_icon
))
994 XPutBackEvent(ob_display
, &ce
);
998 msgtype
= e
->xproperty
.atom
;
999 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1000 client_update_normal_hints(client
);
1001 /* normal hints can make a window non-resizable */
1002 client_setup_decor_and_functions(client
);
1003 } else if (msgtype
== XA_WM_HINTS
) {
1004 client_update_wmhints(client
);
1005 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1006 client_update_transient_for(client
);
1007 client_get_type(client
);
1008 /* type may have changed, so update the layer */
1009 client_calc_layer(client
);
1010 client_setup_decor_and_functions(client
);
1011 } else if (msgtype
== prop_atoms
.net_wm_name
||
1012 msgtype
== prop_atoms
.wm_name
||
1013 msgtype
== prop_atoms
.net_wm_icon_name
||
1014 msgtype
== prop_atoms
.wm_icon_name
) {
1015 client_update_title(client
);
1016 } else if (msgtype
== prop_atoms
.wm_class
) {
1017 client_update_class(client
);
1018 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1019 client_update_protocols(client
);
1020 client_setup_decor_and_functions(client
);
1022 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1023 client_update_strut(client
);
1025 else if (msgtype
== prop_atoms
.net_wm_icon
||
1026 msgtype
== prop_atoms
.kwm_win_icon
) {
1027 client_update_icons(client
);
1029 else if (msgtype
== prop_atoms
.sm_client_id
) {
1030 client_update_sm_client_id(client
);
1035 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1036 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1037 frame_adjust_shape(client
->frame
);
1043 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1047 stacking_raise(DOCK_AS_WINDOW(s
));
1058 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1062 dock_app_drag(app
, &e
->xmotion
);
1065 if (app
->ignore_unmaps
) {
1066 app
->ignore_unmaps
--;
1069 dock_remove(app
, TRUE
);
1072 dock_remove(app
, FALSE
);
1074 case ReparentNotify
:
1075 dock_remove(app
, FALSE
);
1077 case ConfigureNotify
:
1078 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1083 ObMenuFrame
* find_active_menu()
1086 ObMenuFrame
*ret
= NULL
;
1088 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1097 ObMenuFrame
* find_active_or_last_menu()
1099 ObMenuFrame
*ret
= NULL
;
1101 ret
= find_active_menu();
1102 if (!ret
&& menu_frame_visible
)
1103 ret
= menu_frame_visible
->data
;
1107 static void event_handle_menu(XEvent
*ev
)
1110 ObMenuEntryFrame
*e
;
1114 if (menu_can_hide
) {
1115 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1116 ev
->xbutton
.y_root
)))
1117 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1119 menu_frame_hide_all();
1123 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1124 ev
->xmotion
.y_root
))) {
1125 menu_frame_move_on_screen(f
);
1126 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1127 ev
->xmotion
.y_root
)))
1128 menu_frame_select(f
, e
);
1133 a
= find_active_menu();
1135 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1137 menu_frame_select(a
, NULL
);
1142 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1143 menu_frame_hide_all();
1144 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1146 if ((f
= find_active_menu()))
1147 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1148 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1150 if ((f
= find_active_or_last_menu()) && f
->parent
)
1151 menu_frame_select(f
, NULL
);
1152 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1154 if ((f
= find_active_or_last_menu()) && f
->child
)
1155 menu_frame_select_next(f
->child
);
1156 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1158 if ((f
= find_active_or_last_menu()))
1159 menu_frame_select_previous(f
);
1160 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1162 if ((f
= find_active_or_last_menu()))
1163 menu_frame_select_next(f
);
1169 static gboolean
menu_hide_delay_func(gpointer data
)
1171 menu_can_hide
= TRUE
;
1172 return FALSE
; /* no repeat */
1175 static gboolean
focus_delay_func(gpointer data
)
1179 if (focus_client
!= c
) {
1181 if (config_focus_raise
)
1184 return FALSE
; /* no repeat */
1187 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1189 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
, client
);
1192 void event_ignore_queued_enters()
1194 GSList
*saved
= NULL
, *it
;
1197 XSync(ob_display
, FALSE
);
1199 /* count the events */
1201 e
= g_new(XEvent
, 1);
1202 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1205 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1206 if (win
&& WINDOW_IS_CLIENT(win
))
1207 ++ignore_enter_focus
;
1209 saved
= g_slist_append(saved
, e
);
1215 /* put the events back */
1216 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1217 XPutBackEvent(ob_display
, it
->data
);
1220 g_slist_free(saved
);