11 #include "menuframe.h"
15 #include "framerender.h"
17 #include "moveresize.h"
20 #include "extensions.h"
24 #include <X11/keysym.h>
25 #include <X11/Xatom.h>
28 #ifdef HAVE_SYS_SELECT_H
29 # include <sys/select.h>
36 #include <X11/ICE/ICElib.h>
44 static void event_process(const XEvent
*e
, gpointer data
);
45 static void event_handle_root(XEvent
*e
);
46 static void event_handle_menu(XEvent
*e
);
47 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
48 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
49 static void event_handle_client(ObClient
*c
, XEvent
*e
);
50 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
52 static gboolean
focus_delay_func(gpointer data
);
53 static void focus_delay_client_dest(gpointer data
);
55 static gboolean
menu_hide_delay_func(gpointer data
);
57 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
58 (e)->xfocus.detail == NotifyAncestor || \
59 (e)->xfocus.detail > NotifyNonlinearVirtual)
60 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
61 (e)->xfocus.detail == NotifyInferior || \
62 (e)->xfocus.detail == NotifyAncestor || \
63 (e)->xfocus.detail > NotifyNonlinearVirtual)
65 Time event_lasttime
= 0;
67 /*! The value of the mask for the NumLock modifier */
68 unsigned int NumLockMask
;
69 /*! The value of the mask for the ScrollLock modifier */
70 unsigned int ScrollLockMask
;
71 /*! The key codes for the modifier keys */
72 static XModifierKeymap
*modmap
;
73 /*! Table of the constant modifier masks */
74 static const int mask_table
[] = {
75 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
76 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
78 static int mask_table_size
;
80 static ObClient
*focus_delay_client
;
82 static gboolean menu_can_hide
;
85 static void ice_handler(int fd
, gpointer conn
)
88 IceProcessMessages(conn
, NULL
, &b
);
91 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
92 IcePointer
*watch_data
)
97 fd
= IceConnectionNumber(conn
);
98 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
100 ob_main_loop_fd_remove(ob_main_loop
, fd
);
106 void event_startup(gboolean reconfig
)
108 if (reconfig
) return;
110 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
112 /* get lock masks that are defined by the display (not constant) */
113 modmap
= XGetModifierMapping(ob_display
);
115 if (modmap
&& modmap
->max_keypermod
> 0) {
117 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
118 /* get the values of the keyboard lock modifiers
119 Note: Caps lock is not retrieved the same way as Scroll and Num
120 lock since it doesn't need to be. */
121 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
122 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
125 for (cnt
= 0; cnt
< size
; ++cnt
) {
126 if (! modmap
->modifiermap
[cnt
]) continue;
128 if (num_lock
== modmap
->modifiermap
[cnt
])
129 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
130 if (scroll_lock
== modmap
->modifiermap
[cnt
])
131 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
135 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
138 IceAddConnectionWatch(ice_watch
, NULL
);
141 client_add_destructor(focus_delay_client_dest
);
144 void event_shutdown(gboolean reconfig
)
146 if (reconfig
) return;
149 IceRemoveConnectionWatch(ice_watch
, NULL
);
152 client_remove_destructor(focus_delay_client_dest
);
153 XFreeModifiermap(modmap
);
156 static Window
event_get_window(XEvent
*e
)
163 window
= RootWindow(ob_display
, ob_screen
);
166 window
= e
->xmap
.window
;
169 window
= e
->xunmap
.window
;
172 window
= e
->xdestroywindow
.window
;
174 case ConfigureRequest
:
175 window
= e
->xconfigurerequest
.window
;
177 case ConfigureNotify
:
178 window
= e
->xconfigure
.window
;
182 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
183 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
185 window
= ((XkbBellNotifyEvent
*)e
)->window
;
191 window
= e
->xany
.window
;
196 static void event_set_lasttime(XEvent
*e
)
200 /* grab the lasttime and hack up the state */
216 t
= e
->xproperty
.time
;
220 t
= e
->xcrossing
.time
;
223 /* if more event types are anticipated, get their timestamp
228 if (t
> event_lasttime
)
232 #define STRIP_MODS(s) \
233 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
234 /* kill off the Button1Mask etc, only want the modifiers */ \
235 s &= (ControlMask | ShiftMask | Mod1Mask | \
236 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
238 static void event_hack_mods(XEvent *e)
246 STRIP_MODS(e
->xbutton
.state
);
249 STRIP_MODS(e
->xkey
.state
);
252 STRIP_MODS(e
->xkey
.state
);
253 /* remove from the state the mask of the modifier being released, if
254 it is a modifier key being released (this is a little ugly..) */
255 kp
= modmap
->modifiermap
;
256 for (i
= 0; i
< mask_table_size
; ++i
) {
257 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
258 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
259 /* remove the mask for it */
260 e
->xkey
.state
&= ~mask_table
[i
];
261 /* cause the first loop to break; */
263 break; /* get outta here! */
270 STRIP_MODS(e
->xmotion
.state
);
271 /* compress events */
274 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
276 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
277 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
284 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
288 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
289 because of RevertToPointerRoot. If the focus ends up reverting to
290 pointer root on a workspace change, then the FocusIn event that we
291 want will be of type NotifyAncestor. This situation does not occur
292 for FocusOut, so it is safely ignored there.
294 if (INVALID_FOCUSIN(e
) ||
297 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
298 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
300 /* says a client was not found for the event (or a valid FocusIn
303 e
->xfocus
.window
= None
;
308 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
309 e
->xfocus
.mode
, e
->xfocus
.detail
);
313 if (INVALID_FOCUSOUT(e
)) {
315 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
316 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
322 ob_debug("FocusOut on %lx mode %d detail %d\n",
323 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
328 gboolean fallback
= TRUE
;
331 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
333 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
335 if (fe
.type
== FocusOut
) {
337 ob_debug("found pending FocusOut\n");
339 if (!INVALID_FOCUSOUT(&fe
)) {
340 /* if there is a VALID FocusOut still coming, don't
341 fallback focus yet, we'll deal with it then */
342 XPutBackEvent(ob_display
, &fe
);
348 ob_debug("found pending FocusIn\n");
350 /* is the focused window getting a FocusOut/In back to
353 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
354 !event_ignore(&fe
, client
)) {
356 if focus_client is not set, then we can't do
357 this. we need the FocusIn. This happens in the
358 case when the set_focus_client(NULL) in the
359 focus_fallback function fires and then
360 focus_fallback picks the currently focused
361 window (such as on a SendToDesktop-esque action.
365 ob_debug("focused window got an Out/In back to "
366 "itself IGNORED both\n");
370 event_process(&fe
, NULL
);
372 ob_debug("focused window got an Out/In back to "
373 "itself but focus_client was null "
374 "IGNORED just the Out\n");
383 /* once all the FocusOut's have been dealt with, if
384 there is a FocusIn still left and it is valid, then
386 event_process(&fe
, &d
);
388 ob_debug("FocusIn was OK, so don't fallback\n");
397 ob_debug("no valid FocusIn and no FocusOut events found, "
400 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
406 /* NotifyUngrab occurs when a mouse button is released and the event is
407 caused, like when lowering a window */
408 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
409 pointer (Ancestor happens when the pointer is on a window border) */
410 if (e
->xcrossing
.mode
== NotifyGrab
||
411 e
->xcrossing
.detail
== NotifyInferior
||
412 (e
->xcrossing
.mode
== NotifyUngrab
&&
413 (e
->xcrossing
.detail
== NotifyAncestor
||
414 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
415 e
->xcrossing
.detail
== NotifyVirtual
))) {
417 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
418 (e
->type
== EnterNotify
? "Enter" : "Leave"),
420 e
->xcrossing
.detail
, client
?client
->window
:0);
425 ob_debug("%sNotify mode %d detail %d on %lx\n",
426 (e
->type
== EnterNotify
? "Enter" : "Leave"),
428 e
->xcrossing
.detail
, client
?client
->window
:0);
435 static void event_process(const XEvent
*ec
, gpointer data
)
438 ObGroup
*group
= NULL
;
439 ObClient
*client
= NULL
;
441 ObDockApp
*dockapp
= NULL
;
442 ObWindow
*obwin
= NULL
;
444 ObEventData
*ed
= data
;
446 /* make a copy we can mangle */
450 window
= event_get_window(e
);
451 if (!(e
->type
== PropertyNotify
&&
452 (group
= g_hash_table_lookup(group_map
, &window
))))
453 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
454 switch (obwin
->type
) {
456 dock
= WINDOW_AS_DOCK(obwin
);
459 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
462 client
= WINDOW_AS_CLIENT(obwin
);
465 case Window_Internal
:
466 /* not to be used for events */
467 g_assert_not_reached();
472 event_set_lasttime(e
);
474 if (event_ignore(e
, client
)) {
481 /* deal with it in the kernel */
483 event_handle_group(group
, e
);
485 event_handle_client(client
, e
);
487 event_handle_dockapp(dockapp
, e
);
489 event_handle_dock(dock
, e
);
490 else if (window
== RootWindow(ob_display
, ob_screen
))
491 event_handle_root(e
);
492 else if (e
->type
== MapRequest
)
493 client_manage(window
);
494 else if (e
->type
== ConfigureRequest
) {
495 /* unhandled configure requests must be used to configure the
499 xwc
.x
= e
->xconfigurerequest
.x
;
500 xwc
.y
= e
->xconfigurerequest
.y
;
501 xwc
.width
= e
->xconfigurerequest
.width
;
502 xwc
.height
= e
->xconfigurerequest
.height
;
503 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
504 xwc
.sibling
= e
->xconfigurerequest
.above
;
505 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
507 /* we are not to be held responsible if someone sends us an
509 xerror_set_ignore(TRUE
);
510 XConfigureWindow(ob_display
, window
,
511 e
->xconfigurerequest
.value_mask
, &xwc
);
512 xerror_set_ignore(FALSE
);
515 /* user input (action-bound) events */
516 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
517 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
518 e
->type
== KeyRelease
)
520 if (menu_frame_visible
)
521 event_handle_menu(e
);
523 if (!keyboard_process_interactive_grab(e
, &client
)) {
524 if (moveresize_in_progress
)
527 menu_can_hide
= FALSE
;
528 ob_main_loop_timeout_add(ob_main_loop
,
530 menu_hide_delay_func
,
533 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
534 e
->type
== MotionNotify
)
535 mouse_event(client
, e
);
536 else if (e
->type
== KeyPress
)
537 /* when in the middle of a focus cycling action, this
538 causes the window which appears to be focused to be
539 the one on which the actions will be executed */
540 keyboard_event((focus_cycle_target
?
542 (client
? client
: focus_client
)), e
);
548 static void event_handle_root(XEvent
*e
)
554 ob_debug("Another WM has requested to replace us. Exiting.\n");
559 if (e
->xclient
.format
!= 32) break;
561 msgtype
= e
->xclient
.message_type
;
562 if (msgtype
== prop_atoms
.net_current_desktop
) {
563 unsigned int d
= e
->xclient
.data
.l
[0];
564 if (d
< screen_num_desktops
)
565 screen_set_desktop(d
);
566 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
567 unsigned int d
= e
->xclient
.data
.l
[0];
569 screen_set_num_desktops(d
);
570 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
571 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
575 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
576 screen_update_desktop_names();
577 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
578 screen_update_layout();
580 case ConfigureNotify
:
582 XRRUpdateConfiguration(e
);
589 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
590 ob_debug("VIDMODE EVENT\n");
596 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
600 g_assert(e
->type
== PropertyNotify
);
602 for (it
= group
->members
; it
; it
= g_slist_next(it
))
603 event_handle_client(it
->data
, e
);
606 static void event_handle_client(ObClient
*client
, XEvent
*e
)
614 case VisibilityNotify
:
615 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
619 /* Wheel buttons don't draw because they are an instant click, so it
620 is a waste of resources to go drawing it. */
621 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
622 con
= frame_context(client
, e
->xbutton
.window
);
623 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
625 case OB_FRAME_CONTEXT_MAXIMIZE
:
626 client
->frame
->max_press
= (e
->type
== ButtonPress
);
627 framerender_frame(client
->frame
);
629 case OB_FRAME_CONTEXT_CLOSE
:
630 client
->frame
->close_press
= (e
->type
== ButtonPress
);
631 framerender_frame(client
->frame
);
633 case OB_FRAME_CONTEXT_ICONIFY
:
634 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
635 framerender_frame(client
->frame
);
637 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
638 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
639 framerender_frame(client
->frame
);
641 case OB_FRAME_CONTEXT_SHADE
:
642 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
643 framerender_frame(client
->frame
);
646 /* nothing changes with clicks for any other contexts */
653 ob_debug("FocusIn on client for %lx\n", client
->window
);
655 if (client
!= focus_client
) {
656 focus_set_client(client
);
657 frame_adjust_focus(client
->frame
, TRUE
);
662 ob_debug("FocusOut on client for %lx\n", client
->window
);
664 /* are we a fullscreen window or a transient of one? (checks layer)
665 if we are then we need to be iconified since we are losing focus
667 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
668 !client_search_focus_tree_full(client
))
669 /* iconify fullscreen windows when they and their transients
671 client_iconify(client
, TRUE
, TRUE
);
672 frame_adjust_focus(client
->frame
, FALSE
);
675 con
= frame_context(client
, e
->xcrossing
.window
);
677 case OB_FRAME_CONTEXT_MAXIMIZE
:
678 client
->frame
->max_hover
= FALSE
;
679 frame_adjust_state(client
->frame
);
681 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
682 client
->frame
->desk_hover
= FALSE
;
683 frame_adjust_state(client
->frame
);
685 case OB_FRAME_CONTEXT_SHADE
:
686 client
->frame
->shade_hover
= FALSE
;
687 frame_adjust_state(client
->frame
);
689 case OB_FRAME_CONTEXT_ICONIFY
:
690 client
->frame
->iconify_hover
= FALSE
;
691 frame_adjust_state(client
->frame
);
693 case OB_FRAME_CONTEXT_CLOSE
:
694 client
->frame
->close_hover
= FALSE
;
695 frame_adjust_state(client
->frame
);
697 case OB_FRAME_CONTEXT_FRAME
:
698 /* XXX if doing a 'reconfigure' make sure you kill this timer,
699 maybe all timers.. */
700 if (config_focus_delay
&& client
== focus_delay_client
) {
701 ob_main_loop_timeout_remove_data(ob_main_loop
,
704 focus_delay_client
= NULL
;
711 con
= frame_context(client
, e
->xcrossing
.window
);
713 case OB_FRAME_CONTEXT_MAXIMIZE
:
714 client
->frame
->max_hover
= TRUE
;
715 frame_adjust_state(client
->frame
);
717 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
718 client
->frame
->desk_hover
= TRUE
;
719 frame_adjust_state(client
->frame
);
721 case OB_FRAME_CONTEXT_SHADE
:
722 client
->frame
->shade_hover
= TRUE
;
723 frame_adjust_state(client
->frame
);
725 case OB_FRAME_CONTEXT_ICONIFY
:
726 client
->frame
->iconify_hover
= TRUE
;
727 frame_adjust_state(client
->frame
);
729 case OB_FRAME_CONTEXT_CLOSE
:
730 client
->frame
->close_hover
= TRUE
;
731 frame_adjust_state(client
->frame
);
733 case OB_FRAME_CONTEXT_FRAME
:
734 if (client_normal(client
)) {
735 if (config_focus_follow
) {
737 ob_debug("EnterNotify on %lx, focusing window\n",
740 if (config_focus_delay
) {
741 ob_main_loop_timeout_add(ob_main_loop
,
745 focus_delay_client
= client
;
747 client_focus(client
);
755 case ConfigureRequest
:
757 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
758 ConfigureRequest
, &ce
)) {
760 /* XXX if this causes bad things.. we can compress config req's
761 with the same mask. */
762 e
->xconfigurerequest
.value_mask
|=
763 ce
.xconfigurerequest
.value_mask
;
764 if (ce
.xconfigurerequest
.value_mask
& CWX
)
765 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
766 if (ce
.xconfigurerequest
.value_mask
& CWY
)
767 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
768 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
769 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
770 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
771 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
772 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
773 e
->xconfigurerequest
.border_width
=
774 ce
.xconfigurerequest
.border_width
;
775 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
776 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
779 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
780 if (client
->iconic
|| client
->shaded
) return;
782 /* resize, then move, as specified in the EWMH section 7.7 */
783 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
789 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
790 client
->border_width
= e
->xconfigurerequest
.border_width
;
792 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
793 e
->xconfigurerequest
.x
: client
->area
.x
;
794 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
795 e
->xconfigurerequest
.y
: client
->area
.y
;
796 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
797 e
->xconfigurerequest
.width
: client
->area
.width
;
798 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
799 e
->xconfigurerequest
.height
: client
->area
.height
;
805 client
->frame
->size
.left
+ client
->frame
->size
.right
;
807 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
808 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
809 client_normal(client
));
810 if (e
->xconfigurerequest
.value_mask
& CWX
)
812 if (e
->xconfigurerequest
.value_mask
& CWY
)
816 switch (client
->gravity
) {
817 case NorthEastGravity
:
819 corner
= OB_CORNER_TOPRIGHT
;
821 case SouthWestGravity
:
823 corner
= OB_CORNER_BOTTOMLEFT
;
825 case SouthEastGravity
:
826 corner
= OB_CORNER_BOTTOMRIGHT
;
828 default: /* NorthWest, Static, etc */
829 corner
= OB_CORNER_TOPLEFT
;
832 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
836 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
837 switch (e
->xconfigurerequest
.detail
) {
840 stacking_lower(CLIENT_AS_WINDOW(client
));
846 stacking_raise(CLIENT_AS_WINDOW(client
));
852 if (client
->ignore_unmaps
) {
853 client
->ignore_unmaps
--;
856 client_unmanage(client
);
859 client_unmanage(client
);
862 /* this is when the client is first taken captive in the frame */
863 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
866 This event is quite rare and is usually handled in unmapHandler.
867 However, if the window is unmapped when the reparent event occurs,
868 the window manager never sees it because an unmap event is not sent
869 to an already unmapped window.
872 /* we don't want the reparent event, put it back on the stack for the
873 X server to deal with after we unmanage the window */
874 XPutBackEvent(ob_display
, e
);
876 client_unmanage(client
);
879 ob_debug("MapRequest for 0x%lx\n", client
->window
);
880 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
881 does, we don't want it! */
882 if (screen_showing_desktop
)
883 screen_show_desktop(FALSE
);
884 client_iconify(client
, FALSE
, TRUE
);
885 if (!client
->frame
->visible
)
886 /* if its not visible still, then don't mess with it */
889 client_shade(client
, FALSE
);
890 client_focus(client
);
891 stacking_raise(CLIENT_AS_WINDOW(client
));
894 /* validate cuz we query stuff off the client here */
895 if (!client_validate(client
)) break;
897 if (e
->xclient
.format
!= 32) return;
899 msgtype
= e
->xclient
.message_type
;
900 if (msgtype
== prop_atoms
.wm_change_state
) {
901 /* compress changes into a single change */
902 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
904 /* XXX: it would be nice to compress ALL messages of a
905 type, not just messages in a row without other
906 message types between. */
907 if (ce
.xclient
.message_type
!= msgtype
) {
908 XPutBackEvent(ob_display
, &ce
);
911 e
->xclient
= ce
.xclient
;
913 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
914 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
915 /* compress changes into a single change */
916 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
918 /* XXX: it would be nice to compress ALL messages of a
919 type, not just messages in a row without other
920 message types between. */
921 if (ce
.xclient
.message_type
!= msgtype
) {
922 XPutBackEvent(ob_display
, &ce
);
925 e
->xclient
= ce
.xclient
;
927 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
928 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
929 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
931 } else if (msgtype
== prop_atoms
.net_wm_state
) {
932 /* can't compress these */
933 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
934 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
935 e
->xclient
.data
.l
[0] == 1 ? "Add" :
936 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
937 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
939 client_set_state(client
, e
->xclient
.data
.l
[0],
940 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
941 } else if (msgtype
== prop_atoms
.net_close_window
) {
942 ob_debug("net_close_window for 0x%lx\n", client
->window
);
943 client_close(client
);
944 } else if (msgtype
== prop_atoms
.net_active_window
) {
945 ob_debug("net_active_window for 0x%lx\n", client
->window
);
946 client_activate(client
, FALSE
);
947 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
948 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
949 if ((Atom
)e
->xclient
.data
.l
[2] ==
950 prop_atoms
.net_wm_moveresize_size_topleft
||
951 (Atom
)e
->xclient
.data
.l
[2] ==
952 prop_atoms
.net_wm_moveresize_size_top
||
953 (Atom
)e
->xclient
.data
.l
[2] ==
954 prop_atoms
.net_wm_moveresize_size_topright
||
955 (Atom
)e
->xclient
.data
.l
[2] ==
956 prop_atoms
.net_wm_moveresize_size_right
||
957 (Atom
)e
->xclient
.data
.l
[2] ==
958 prop_atoms
.net_wm_moveresize_size_right
||
959 (Atom
)e
->xclient
.data
.l
[2] ==
960 prop_atoms
.net_wm_moveresize_size_bottomright
||
961 (Atom
)e
->xclient
.data
.l
[2] ==
962 prop_atoms
.net_wm_moveresize_size_bottom
||
963 (Atom
)e
->xclient
.data
.l
[2] ==
964 prop_atoms
.net_wm_moveresize_size_bottomleft
||
965 (Atom
)e
->xclient
.data
.l
[2] ==
966 prop_atoms
.net_wm_moveresize_size_left
||
967 (Atom
)e
->xclient
.data
.l
[2] ==
968 prop_atoms
.net_wm_moveresize_move
||
969 (Atom
)e
->xclient
.data
.l
[2] ==
970 prop_atoms
.net_wm_moveresize_size_keyboard
||
971 (Atom
)e
->xclient
.data
.l
[2] ==
972 prop_atoms
.net_wm_moveresize_move_keyboard
) {
974 moveresize_start(client
, e
->xclient
.data
.l
[0],
975 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
976 e
->xclient
.data
.l
[2]);
978 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
979 int oldg
= client
->gravity
;
980 int tmpg
, x
, y
, w
, h
;
982 if (e
->xclient
.data
.l
[0] & 0xff)
983 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
987 if (e
->xclient
.data
.l
[0] & 1 << 8)
988 x
= e
->xclient
.data
.l
[1];
991 if (e
->xclient
.data
.l
[0] & 1 << 9)
992 y
= e
->xclient
.data
.l
[2];
995 if (e
->xclient
.data
.l
[0] & 1 << 10)
996 w
= e
->xclient
.data
.l
[3];
998 w
= client
->area
.width
;
999 if (e
->xclient
.data
.l
[0] & 1 << 11)
1000 h
= e
->xclient
.data
.l
[4];
1002 h
= client
->area
.height
;
1003 client
->gravity
= tmpg
;
1009 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1011 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1012 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1013 client_normal(client
));
1014 if (e
->xclient
.data
.l
[0] & 1 << 8)
1016 if (e
->xclient
.data
.l
[0] & 1 << 9)
1020 client_configure(client
, OB_CORNER_TOPLEFT
,
1021 x
, y
, w
, h
, FALSE
, TRUE
);
1023 client
->gravity
= oldg
;
1026 case PropertyNotify
:
1027 /* validate cuz we query stuff off the client here */
1028 if (!client_validate(client
)) break;
1030 /* compress changes to a single property into a single change */
1031 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1035 /* XXX: it would be nice to compress ALL changes to a property,
1036 not just changes in a row without other props between. */
1038 a
= ce
.xproperty
.atom
;
1039 b
= e
->xproperty
.atom
;
1043 if ((a
== prop_atoms
.net_wm_name
||
1044 a
== prop_atoms
.wm_name
||
1045 a
== prop_atoms
.net_wm_icon_name
||
1046 a
== prop_atoms
.wm_icon_name
)
1048 (b
== prop_atoms
.net_wm_name
||
1049 b
== prop_atoms
.wm_name
||
1050 b
== prop_atoms
.net_wm_icon_name
||
1051 b
== prop_atoms
.wm_icon_name
)) {
1054 if ((a
== prop_atoms
.net_wm_icon
||
1055 a
== prop_atoms
.kwm_win_icon
)
1057 (b
== prop_atoms
.net_wm_icon
||
1058 b
== prop_atoms
.kwm_win_icon
))
1061 XPutBackEvent(ob_display
, &ce
);
1065 msgtype
= e
->xproperty
.atom
;
1066 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1067 client_update_normal_hints(client
);
1068 /* normal hints can make a window non-resizable */
1069 client_setup_decor_and_functions(client
);
1070 } else if (msgtype
== XA_WM_HINTS
) {
1071 client_update_wmhints(client
);
1072 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1073 client_update_transient_for(client
);
1074 client_get_type(client
);
1075 /* type may have changed, so update the layer */
1076 client_calc_layer(client
);
1077 client_setup_decor_and_functions(client
);
1078 } else if (msgtype
== prop_atoms
.net_wm_name
||
1079 msgtype
== prop_atoms
.wm_name
||
1080 msgtype
== prop_atoms
.net_wm_icon_name
||
1081 msgtype
== prop_atoms
.wm_icon_name
) {
1082 client_update_title(client
);
1083 } else if (msgtype
== prop_atoms
.wm_class
) {
1084 client_update_class(client
);
1085 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1086 client_update_protocols(client
);
1087 client_setup_decor_and_functions(client
);
1089 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1090 client_update_strut(client
);
1092 else if (msgtype
== prop_atoms
.net_wm_icon
||
1093 msgtype
== prop_atoms
.kwm_win_icon
) {
1094 client_update_icons(client
);
1096 else if (msgtype
== prop_atoms
.sm_client_id
) {
1097 client_update_sm_client_id(client
);
1102 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1103 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1104 frame_adjust_shape(client
->frame
);
1110 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1114 stacking_raise(DOCK_AS_WINDOW(s
));
1125 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1129 dock_app_drag(app
, &e
->xmotion
);
1132 if (app
->ignore_unmaps
) {
1133 app
->ignore_unmaps
--;
1136 dock_remove(app
, TRUE
);
1139 dock_remove(app
, FALSE
);
1141 case ReparentNotify
:
1142 dock_remove(app
, FALSE
);
1144 case ConfigureNotify
:
1145 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1150 ObMenuFrame
* find_active_menu()
1155 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1160 return it
? it
->data
: NULL
;
1163 static void event_handle_menu(XEvent
*ev
)
1166 ObMenuEntryFrame
*e
;
1170 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1171 ev
->xbutton
.y_root
)))
1172 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1173 else if (menu_can_hide
)
1174 menu_frame_hide_all();
1177 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1178 ev
->xmotion
.y_root
))) {
1179 menu_frame_move_on_screen(f
);
1180 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1181 ev
->xmotion
.y_root
)))
1182 menu_frame_select(f
, e
);
1187 a
= find_active_menu();
1189 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1191 menu_frame_select(a
, NULL
);
1196 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1197 menu_frame_hide_all();
1198 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1200 if ((f
= find_active_menu()))
1201 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1202 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1204 if ((f
= find_active_menu()) && f
->parent
)
1205 menu_frame_select(f
, NULL
);
1206 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1208 if ((f
= find_active_menu()) && f
->child
)
1209 menu_frame_select_next(f
->child
);
1210 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1212 if ((f
= find_active_menu()))
1213 menu_frame_select_previous(f
);
1214 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1216 if ((f
= find_active_menu()))
1217 menu_frame_select_next(f
);
1223 static gboolean
menu_hide_delay_func(gpointer data
)
1225 menu_can_hide
= TRUE
;
1226 return FALSE
; /* no repeat */
1229 static gboolean
focus_delay_func(gpointer data
)
1231 client_focus(focus_delay_client
);
1232 return FALSE
; /* no repeat */
1235 static void focus_delay_client_dest(gpointer data
)
1238 if (c
== focus_delay_client
) {
1239 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1240 focus_delay_client
);
1241 focus_delay_client
= NULL
;