11 #include "menuframe.h"
15 #include "framerender.h"
17 #include "moveresize.h"
19 #include "extensions.h"
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
27 #ifdef HAVE_SYS_SELECT_H
28 # include <sys/select.h>
35 #include <X11/ICE/ICElib.h>
38 static void event_process(const XEvent
*e
, gpointer data
);
39 static void event_handle_root(XEvent
*e
);
40 static void event_handle_menu(XEvent
*e
);
41 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
42 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
43 static void event_handle_client(ObClient
*c
, XEvent
*e
);
45 static gboolean
focus_delay_func(gpointer data
);
46 static void focus_delay_client_dest(gpointer data
);
48 static gboolean
menu_hide_delay_func(gpointer data
);
50 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
51 (e)->xfocus.detail == NotifyAncestor || \
52 (e)->xfocus.detail > NotifyNonlinearVirtual)
53 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
54 (e)->xfocus.detail == NotifyInferior || \
55 (e)->xfocus.detail == NotifyAncestor || \
56 (e)->xfocus.detail > NotifyNonlinearVirtual)
58 Time event_lasttime
= 0;
60 /*! The value of the mask for the NumLock modifier */
61 unsigned int NumLockMask
;
62 /*! The value of the mask for the ScrollLock modifier */
63 unsigned int ScrollLockMask
;
64 /*! The key codes for the modifier keys */
65 static XModifierKeymap
*modmap
;
66 /*! Table of the constant modifier masks */
67 static const int mask_table
[] = {
68 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
69 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
71 static int mask_table_size
;
73 static ObClient
*focus_delay_client
;
75 static gboolean menu_can_hide
;
78 static void ice_handler(int fd
, gpointer conn
)
81 IceProcessMessages(conn
, NULL
, &b
);
84 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
85 IcePointer
*watch_data
)
90 fd
= IceConnectionNumber(conn
);
91 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
93 ob_main_loop_fd_remove(ob_main_loop
, fd
);
99 void event_startup(gboolean reconfig
)
101 if (reconfig
) return;
103 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
105 /* get lock masks that are defined by the display (not constant) */
106 modmap
= XGetModifierMapping(ob_display
);
108 if (modmap
&& modmap
->max_keypermod
> 0) {
110 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
111 /* get the values of the keyboard lock modifiers
112 Note: Caps lock is not retrieved the same way as Scroll and Num
113 lock since it doesn't need to be. */
114 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
115 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
118 for (cnt
= 0; cnt
< size
; ++cnt
) {
119 if (! modmap
->modifiermap
[cnt
]) continue;
121 if (num_lock
== modmap
->modifiermap
[cnt
])
122 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
123 if (scroll_lock
== modmap
->modifiermap
[cnt
])
124 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
128 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
131 IceAddConnectionWatch(ice_watch
, NULL
);
134 client_add_destructor(focus_delay_client_dest
);
137 void event_shutdown(gboolean reconfig
)
139 if (reconfig
) return;
142 IceRemoveConnectionWatch(ice_watch
, NULL
);
145 client_remove_destructor(focus_delay_client_dest
);
146 XFreeModifiermap(modmap
);
149 static Window
event_get_window(XEvent
*e
)
156 window
= RootWindow(ob_display
, ob_screen
);
159 window
= e
->xmap
.window
;
162 window
= e
->xunmap
.window
;
165 window
= e
->xdestroywindow
.window
;
167 case ConfigureRequest
:
168 window
= e
->xconfigurerequest
.window
;
170 case ConfigureNotify
:
171 window
= e
->xconfigure
.window
;
175 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
176 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
178 window
= ((XkbBellNotifyEvent
*)e
)->window
;
184 window
= e
->xany
.window
;
189 static void event_set_lasttime(XEvent
*e
)
193 /* grab the lasttime and hack up the state */
209 t
= e
->xproperty
.time
;
213 t
= e
->xcrossing
.time
;
216 /* if more event types are anticipated, get their timestamp
221 if (t
> event_lasttime
)
225 #define STRIP_MODS(s) \
226 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
227 /* kill off the Button1Mask etc, only want the modifiers */ \
228 s &= (ControlMask | ShiftMask | Mod1Mask | \
229 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
231 static void event_hack_mods(XEvent *e)
239 STRIP_MODS(e
->xbutton
.state
);
242 STRIP_MODS(e
->xkey
.state
);
245 STRIP_MODS(e
->xkey
.state
);
246 /* remove from the state the mask of the modifier being released, if
247 it is a modifier key being released (this is a little ugly..) */
248 kp
= modmap
->modifiermap
;
249 for (i
= 0; i
< mask_table_size
; ++i
) {
250 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
251 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
252 /* remove the mask for it */
253 e
->xkey
.state
&= ~mask_table
[i
];
254 /* cause the first loop to break; */
256 break; /* get outta here! */
263 STRIP_MODS(e
->xmotion
.state
);
264 /* compress events */
267 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
269 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
270 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
277 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
281 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
282 because of RevertToPointerRoot. If the focus ends up reverting to
283 pointer root on a workspace change, then the FocusIn event that we
284 want will be of type NotifyAncestor. This situation does not occur
285 for FocusOut, so it is safely ignored there.
287 if (INVALID_FOCUSIN(e
) ||
290 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
291 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
293 /* says a client was not found for the event (or a valid FocusIn
296 e
->xfocus
.window
= None
;
301 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
302 e
->xfocus
.mode
, e
->xfocus
.detail
);
306 if (INVALID_FOCUSOUT(e
)) {
308 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
309 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
315 ob_debug("FocusOut on %lx mode %d detail %d\n",
316 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
321 gboolean fallback
= TRUE
;
324 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
326 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
328 if (fe
.type
== FocusOut
) {
330 ob_debug("found pending FocusOut\n");
332 if (!INVALID_FOCUSOUT(&fe
)) {
333 /* if there is a VALID FocusOut still coming, don't
334 fallback focus yet, we'll deal with it then */
335 XPutBackEvent(ob_display
, &fe
);
341 ob_debug("found pending FocusIn\n");
343 /* is the focused window getting a FocusOut/In back to
346 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
347 !event_ignore(&fe
, client
)) {
349 if focus_client is not set, then we can't do
350 this. we need the FocusIn. This happens in the
351 case when the set_focus_client(NULL) in the
352 focus_fallback function fires and then
353 focus_fallback picks the currently focused
354 window (such as on a SendToDesktop-esque action.
358 ob_debug("focused window got an Out/In back to "
359 "itself IGNORED both\n");
363 event_process(&fe
, NULL
);
365 ob_debug("focused window got an Out/In back to "
366 "itself but focus_client was null "
367 "IGNORED just the Out\n");
373 /* once all the FocusOut's have been dealt with, if there
374 is a FocusIn still left and it is valid, then use it */
375 event_process(&fe
, NULL
);
376 /* secret magic way of event_process telling us that no
377 client was found for the FocusIn event. ^_^ */
378 if (fe
.xfocus
.window
!= None
) {
386 ob_debug("no valid FocusIn and no FocusOut events found, "
389 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
395 /* NotifyUngrab occurs when a mouse button is released and the event is
396 caused, like when lowering a window */
397 /* NotifyVirtual occurs when ungrabbing the pointer */
398 if (e
->xcrossing
.mode
== NotifyGrab
||
399 e
->xcrossing
.detail
== NotifyInferior
||
400 (e
->xcrossing
.mode
== NotifyUngrab
&&
401 e
->xcrossing
.detail
== NotifyVirtual
)) {
403 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
404 (e
->type
== EnterNotify
? "Enter" : "Leave"),
406 e
->xcrossing
.detail
, client
?client
->window
:0);
411 ob_debug("%sNotify mode %d detail %d on %lx\n",
412 (e
->type
== EnterNotify
? "Enter" : "Leave"),
414 e
->xcrossing
.detail
, client
?client
->window
:0);
421 static void event_process(const XEvent
*ec
, gpointer data
)
424 ObClient
*client
= NULL
;
426 ObDockApp
*dockapp
= NULL
;
427 ObWindow
*obwin
= NULL
;
430 /* make a copy we can mangle */
434 window
= event_get_window(e
);
435 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
436 switch (obwin
->type
) {
438 dock
= WINDOW_AS_DOCK(obwin
);
441 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
444 client
= WINDOW_AS_CLIENT(obwin
);
447 case Window_Internal
:
448 /* not to be used for events */
449 g_assert_not_reached();
454 event_set_lasttime(e
);
456 if (event_ignore(e
, client
))
459 /* deal with it in the kernel */
461 event_handle_client(client
, e
);
463 event_handle_dockapp(dockapp
, e
);
465 event_handle_dock(dock
, e
);
466 else if (window
== RootWindow(ob_display
, ob_screen
))
467 event_handle_root(e
);
468 else if (e
->type
== MapRequest
)
469 client_manage(window
);
470 else if (e
->type
== ConfigureRequest
) {
471 /* unhandled configure requests must be used to configure the
475 xwc
.x
= e
->xconfigurerequest
.x
;
476 xwc
.y
= e
->xconfigurerequest
.y
;
477 xwc
.width
= e
->xconfigurerequest
.width
;
478 xwc
.height
= e
->xconfigurerequest
.height
;
479 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
480 xwc
.sibling
= e
->xconfigurerequest
.above
;
481 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
483 /* we are not to be held responsible if someone sends us an
485 xerror_set_ignore(TRUE
);
486 XConfigureWindow(ob_display
, window
,
487 e
->xconfigurerequest
.value_mask
, &xwc
);
488 xerror_set_ignore(FALSE
);
491 /* user input (action-bound) events */
492 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
493 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
494 e
->type
== KeyRelease
)
496 if (menu_frame_visible
)
497 event_handle_menu(e
);
499 if (!keyboard_process_interactive_grab(e
, &client
)) {
500 if (moveresize_in_progress
)
503 menu_can_hide
= FALSE
;
504 ob_main_loop_timeout_add(ob_main_loop
,
506 menu_hide_delay_func
,
509 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
510 e
->type
== MotionNotify
)
511 mouse_event(client
, e
);
512 else if (e
->type
== KeyPress
)
513 /* when in the middle of a focus cycling action, this
514 causes the window which appears to be focused to be
515 the one on which the actions will be executed */
516 keyboard_event((focus_cycle_target
?
518 (client
? client
: focus_client
)), e
);
524 static void event_handle_root(XEvent
*e
)
530 ob_debug("Another WM has requested to replace us. Exiting.\n");
535 if (e
->xclient
.format
!= 32) break;
537 msgtype
= e
->xclient
.message_type
;
538 if (msgtype
== prop_atoms
.net_current_desktop
) {
539 unsigned int d
= e
->xclient
.data
.l
[0];
540 if (d
< screen_num_desktops
)
541 screen_set_desktop(d
);
542 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
543 unsigned int d
= e
->xclient
.data
.l
[0];
545 screen_set_num_desktops(d
);
546 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
547 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
551 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
552 screen_update_desktop_names();
553 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
554 screen_update_layout();
556 case ConfigureNotify
:
558 XRRUpdateConfiguration(e
);
565 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
566 ob_debug("VIDMODE EVENT\n");
572 static void event_handle_client(ObClient
*client
, XEvent
*e
)
580 case VisibilityNotify
:
581 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
585 /* Wheel buttons don't draw because they are an instant click, so it
586 is a waste of resources to go drawing it. */
587 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
588 con
= frame_context(client
, e
->xbutton
.window
);
589 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
591 case OB_FRAME_CONTEXT_MAXIMIZE
:
592 client
->frame
->max_press
= (e
->type
== ButtonPress
);
593 framerender_frame(client
->frame
);
595 case OB_FRAME_CONTEXT_CLOSE
:
596 client
->frame
->close_press
= (e
->type
== ButtonPress
);
597 framerender_frame(client
->frame
);
599 case OB_FRAME_CONTEXT_ICONIFY
:
600 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
601 framerender_frame(client
->frame
);
603 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
604 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
605 framerender_frame(client
->frame
);
607 case OB_FRAME_CONTEXT_SHADE
:
608 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
609 framerender_frame(client
->frame
);
612 /* nothing changes with clicks for any other contexts */
619 ob_debug("FocusIn on client for %lx\n", client
->window
);
621 if (client
!= focus_client
) {
622 focus_set_client(client
);
623 frame_adjust_focus(client
->frame
, TRUE
);
628 ob_debug("FocusOut on client for %lx\n", client
->window
);
630 /* are we a fullscreen window or a transient of one? (checks layer)
631 if we are then we need to be iconified since we are losing focus
633 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
634 !client_search_focus_tree_full(client
))
635 /* iconify fullscreen windows when they and their transients
637 client_iconify(client
, TRUE
, TRUE
);
638 frame_adjust_focus(client
->frame
, FALSE
);
641 con
= frame_context(client
, e
->xcrossing
.window
);
643 case OB_FRAME_CONTEXT_MAXIMIZE
:
644 client
->frame
->max_hover
= FALSE
;
645 frame_adjust_state(client
->frame
);
647 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
648 client
->frame
->desk_hover
= FALSE
;
649 frame_adjust_state(client
->frame
);
651 case OB_FRAME_CONTEXT_SHADE
:
652 client
->frame
->shade_hover
= FALSE
;
653 frame_adjust_state(client
->frame
);
655 case OB_FRAME_CONTEXT_ICONIFY
:
656 client
->frame
->iconify_hover
= FALSE
;
657 frame_adjust_state(client
->frame
);
659 case OB_FRAME_CONTEXT_CLOSE
:
660 client
->frame
->close_hover
= FALSE
;
661 frame_adjust_state(client
->frame
);
663 case OB_FRAME_CONTEXT_FRAME
:
664 /* XXX if doing a 'reconfigure' make sure you kill this timer,
665 maybe all timers.. */
666 if (config_focus_delay
&& client
== focus_delay_client
) {
667 ob_main_loop_timeout_remove_data(ob_main_loop
,
670 focus_delay_client
= NULL
;
677 con
= frame_context(client
, e
->xcrossing
.window
);
679 case OB_FRAME_CONTEXT_MAXIMIZE
:
680 client
->frame
->max_hover
= TRUE
;
681 frame_adjust_state(client
->frame
);
683 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
684 client
->frame
->desk_hover
= TRUE
;
685 frame_adjust_state(client
->frame
);
687 case OB_FRAME_CONTEXT_SHADE
:
688 client
->frame
->shade_hover
= TRUE
;
689 frame_adjust_state(client
->frame
);
691 case OB_FRAME_CONTEXT_ICONIFY
:
692 client
->frame
->iconify_hover
= TRUE
;
693 frame_adjust_state(client
->frame
);
695 case OB_FRAME_CONTEXT_CLOSE
:
696 client
->frame
->close_hover
= TRUE
;
697 frame_adjust_state(client
->frame
);
699 case OB_FRAME_CONTEXT_FRAME
:
700 if (client_normal(client
)) {
701 if (ob_state() == OB_STATE_STARTING
) {
702 /* move it to the top of the focus order */
703 guint desktop
= client
->desktop
;
704 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
705 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
707 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
709 } else if (config_focus_follow
) {
711 ob_debug("EnterNotify on %lx, focusing window\n",
714 if (config_focus_delay
) {
715 ob_main_loop_timeout_add(ob_main_loop
,
719 focus_delay_client
= client
;
721 client_focus(client
);
729 case ConfigureRequest
:
731 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
732 ConfigureRequest
, &ce
)) {
734 /* XXX if this causes bad things.. we can compress config req's
735 with the same mask. */
736 e
->xconfigurerequest
.value_mask
|=
737 ce
.xconfigurerequest
.value_mask
;
738 if (ce
.xconfigurerequest
.value_mask
& CWX
)
739 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
740 if (ce
.xconfigurerequest
.value_mask
& CWY
)
741 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
742 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
743 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
744 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
745 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
746 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
747 e
->xconfigurerequest
.border_width
=
748 ce
.xconfigurerequest
.border_width
;
749 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
750 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
753 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
754 if (client
->iconic
|| client
->shaded
) return;
756 /* resize, then move, as specified in the EWMH section 7.7 */
757 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
763 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
764 client
->border_width
= e
->xconfigurerequest
.border_width
;
766 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
767 e
->xconfigurerequest
.x
: client
->area
.x
;
768 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
769 e
->xconfigurerequest
.y
: client
->area
.y
;
770 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
771 e
->xconfigurerequest
.width
: client
->area
.width
;
772 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
773 e
->xconfigurerequest
.height
: client
->area
.height
;
779 client
->frame
->size
.left
+ client
->frame
->size
.right
;
781 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
782 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
783 client_normal(client
));
784 if (e
->xconfigurerequest
.value_mask
& CWX
)
786 if (e
->xconfigurerequest
.value_mask
& CWY
)
790 switch (client
->gravity
) {
791 case NorthEastGravity
:
793 corner
= OB_CORNER_TOPRIGHT
;
795 case SouthWestGravity
:
797 corner
= OB_CORNER_BOTTOMLEFT
;
799 case SouthEastGravity
:
800 corner
= OB_CORNER_BOTTOMRIGHT
;
802 default: /* NorthWest, Static, etc */
803 corner
= OB_CORNER_TOPLEFT
;
806 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
810 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
811 switch (e
->xconfigurerequest
.detail
) {
814 stacking_lower(CLIENT_AS_WINDOW(client
));
820 stacking_raise(CLIENT_AS_WINDOW(client
));
826 if (client
->ignore_unmaps
) {
827 client
->ignore_unmaps
--;
830 client_unmanage(client
);
833 client_unmanage(client
);
836 /* this is when the client is first taken captive in the frame */
837 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
840 This event is quite rare and is usually handled in unmapHandler.
841 However, if the window is unmapped when the reparent event occurs,
842 the window manager never sees it because an unmap event is not sent
843 to an already unmapped window.
846 /* we don't want the reparent event, put it back on the stack for the
847 X server to deal with after we unmanage the window */
848 XPutBackEvent(ob_display
, e
);
850 client_unmanage(client
);
853 ob_debug("MapRequest for 0x%lx\n", client
->window
);
854 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
855 does, we don't want it! */
856 if (screen_showing_desktop
)
857 screen_show_desktop(FALSE
);
858 client_iconify(client
, FALSE
, TRUE
);
859 if (!client
->frame
->visible
)
860 /* if its not visible still, then don't mess with it */
863 client_shade(client
, FALSE
);
864 client_focus(client
);
865 stacking_raise(CLIENT_AS_WINDOW(client
));
868 /* validate cuz we query stuff off the client here */
869 if (!client_validate(client
)) break;
871 if (e
->xclient
.format
!= 32) return;
873 msgtype
= e
->xclient
.message_type
;
874 if (msgtype
== prop_atoms
.wm_change_state
) {
875 /* compress changes into a single change */
876 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
878 /* XXX: it would be nice to compress ALL messages of a
879 type, not just messages in a row without other
880 message types between. */
881 if (ce
.xclient
.message_type
!= msgtype
) {
882 XPutBackEvent(ob_display
, &ce
);
885 e
->xclient
= ce
.xclient
;
887 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
888 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
889 /* compress changes into a single change */
890 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
892 /* XXX: it would be nice to compress ALL messages of a
893 type, not just messages in a row without other
894 message types between. */
895 if (ce
.xclient
.message_type
!= msgtype
) {
896 XPutBackEvent(ob_display
, &ce
);
899 e
->xclient
= ce
.xclient
;
901 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
902 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
903 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
905 } else if (msgtype
== prop_atoms
.net_wm_state
) {
906 /* can't compress these */
907 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
908 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
909 e
->xclient
.data
.l
[0] == 1 ? "Add" :
910 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
911 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
913 client_set_state(client
, e
->xclient
.data
.l
[0],
914 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
915 } else if (msgtype
== prop_atoms
.net_close_window
) {
916 ob_debug("net_close_window for 0x%lx\n", client
->window
);
917 client_close(client
);
918 } else if (msgtype
== prop_atoms
.net_active_window
) {
919 ob_debug("net_active_window for 0x%lx\n", client
->window
);
920 client_activate(client
, FALSE
);
921 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
922 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
923 if ((Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_topleft
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_top
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_size_topright
||
929 (Atom
)e
->xclient
.data
.l
[2] ==
930 prop_atoms
.net_wm_moveresize_size_right
||
931 (Atom
)e
->xclient
.data
.l
[2] ==
932 prop_atoms
.net_wm_moveresize_size_right
||
933 (Atom
)e
->xclient
.data
.l
[2] ==
934 prop_atoms
.net_wm_moveresize_size_bottomright
||
935 (Atom
)e
->xclient
.data
.l
[2] ==
936 prop_atoms
.net_wm_moveresize_size_bottom
||
937 (Atom
)e
->xclient
.data
.l
[2] ==
938 prop_atoms
.net_wm_moveresize_size_bottomleft
||
939 (Atom
)e
->xclient
.data
.l
[2] ==
940 prop_atoms
.net_wm_moveresize_size_left
||
941 (Atom
)e
->xclient
.data
.l
[2] ==
942 prop_atoms
.net_wm_moveresize_move
||
943 (Atom
)e
->xclient
.data
.l
[2] ==
944 prop_atoms
.net_wm_moveresize_size_keyboard
||
945 (Atom
)e
->xclient
.data
.l
[2] ==
946 prop_atoms
.net_wm_moveresize_move_keyboard
) {
948 moveresize_start(client
, e
->xclient
.data
.l
[0],
949 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
950 e
->xclient
.data
.l
[2]);
952 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
953 int oldg
= client
->gravity
;
954 int tmpg
, x
, y
, w
, h
;
956 if (e
->xclient
.data
.l
[0] & 0xff)
957 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
961 if (e
->xclient
.data
.l
[0] & 1 << 8)
962 x
= e
->xclient
.data
.l
[1];
965 if (e
->xclient
.data
.l
[0] & 1 << 9)
966 y
= e
->xclient
.data
.l
[2];
969 if (e
->xclient
.data
.l
[0] & 1 << 10)
970 w
= e
->xclient
.data
.l
[3];
972 w
= client
->area
.width
;
973 if (e
->xclient
.data
.l
[0] & 1 << 11)
974 h
= e
->xclient
.data
.l
[4];
976 h
= client
->area
.height
;
977 client
->gravity
= tmpg
;
983 client
->frame
->size
.left
+ client
->frame
->size
.right
;
985 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
986 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
987 client_normal(client
));
988 if (e
->xclient
.data
.l
[0] & 1 << 8)
990 if (e
->xclient
.data
.l
[0] & 1 << 9)
994 client_configure(client
, OB_CORNER_TOPLEFT
,
995 x
, y
, w
, h
, FALSE
, TRUE
);
997 client
->gravity
= oldg
;
1000 case PropertyNotify
:
1001 /* validate cuz we query stuff off the client here */
1002 if (!client_validate(client
)) break;
1004 /* compress changes to a single property into a single change */
1005 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1009 /* XXX: it would be nice to compress ALL changes to a property,
1010 not just changes in a row without other props between. */
1012 a
= ce
.xproperty
.atom
;
1013 b
= e
->xproperty
.atom
;
1017 if ((a
== prop_atoms
.net_wm_name
||
1018 a
== prop_atoms
.wm_name
||
1019 a
== prop_atoms
.net_wm_icon_name
||
1020 a
== prop_atoms
.wm_icon_name
)
1022 (b
== prop_atoms
.net_wm_name
||
1023 b
== prop_atoms
.wm_name
||
1024 b
== prop_atoms
.net_wm_icon_name
||
1025 b
== prop_atoms
.wm_icon_name
)) {
1028 if ((a
== prop_atoms
.net_wm_icon
||
1029 a
== prop_atoms
.kwm_win_icon
)
1031 (b
== prop_atoms
.net_wm_icon
||
1032 b
== prop_atoms
.kwm_win_icon
))
1035 XPutBackEvent(ob_display
, &ce
);
1039 msgtype
= e
->xproperty
.atom
;
1040 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1041 client_update_normal_hints(client
);
1042 /* normal hints can make a window non-resizable */
1043 client_setup_decor_and_functions(client
);
1044 } else if (msgtype
== XA_WM_HINTS
) {
1045 client_update_wmhints(client
);
1046 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1047 client_update_transient_for(client
);
1048 client_get_type(client
);
1049 /* type may have changed, so update the layer */
1050 client_calc_layer(client
);
1051 client_setup_decor_and_functions(client
);
1052 } else if (msgtype
== prop_atoms
.net_wm_name
||
1053 msgtype
== prop_atoms
.wm_name
||
1054 msgtype
== prop_atoms
.net_wm_icon_name
||
1055 msgtype
== prop_atoms
.wm_icon_name
) {
1056 client_update_title(client
);
1057 } else if (msgtype
== prop_atoms
.wm_class
) {
1058 client_update_class(client
);
1059 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1060 client_update_protocols(client
);
1061 client_setup_decor_and_functions(client
);
1063 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1064 client_update_strut(client
);
1066 else if (msgtype
== prop_atoms
.net_wm_icon
||
1067 msgtype
== prop_atoms
.kwm_win_icon
) {
1068 client_update_icons(client
);
1073 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1074 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1075 frame_adjust_shape(client
->frame
);
1081 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1085 stacking_raise(DOCK_AS_WINDOW(s
));
1096 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1100 dock_app_drag(app
, &e
->xmotion
);
1103 if (app
->ignore_unmaps
) {
1104 app
->ignore_unmaps
--;
1107 dock_remove(app
, TRUE
);
1110 dock_remove(app
, FALSE
);
1112 case ReparentNotify
:
1113 dock_remove(app
, FALSE
);
1115 case ConfigureNotify
:
1116 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1121 ObMenuFrame
* find_active_menu()
1126 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1131 return it
? it
->data
: NULL
;
1134 static void event_handle_menu(XEvent
*ev
)
1137 ObMenuEntryFrame
*e
;
1141 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1142 ev
->xbutton
.y_root
)))
1143 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1144 else if (menu_can_hide
)
1145 menu_frame_hide_all();
1148 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1149 ev
->xmotion
.y_root
))) {
1150 menu_frame_move_on_screen(f
);
1151 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1152 ev
->xmotion
.y_root
)))
1153 menu_frame_select(f
, e
);
1157 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1158 menu_frame_hide_all();
1159 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1161 if ((f
= find_active_menu()))
1162 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1163 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1165 if ((f
= find_active_menu()) && f
->parent
)
1166 menu_frame_select(f
, NULL
);
1167 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1169 if ((f
= find_active_menu()) && f
->child
)
1170 menu_frame_select_next(f
->child
);
1171 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1173 if ((f
= find_active_menu()))
1174 menu_frame_select_previous(f
);
1175 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1177 if ((f
= find_active_menu()))
1178 menu_frame_select_next(f
);
1184 static gboolean
menu_hide_delay_func(gpointer data
)
1186 menu_can_hide
= TRUE
;
1187 return FALSE
; /* no repeat */
1190 static gboolean
focus_delay_func(gpointer data
)
1192 client_focus(focus_delay_client
);
1193 return FALSE
; /* no repeat */
1196 static void focus_delay_client_dest(gpointer data
)
1199 if (c
== focus_delay_client
) {
1200 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1201 focus_delay_client
);
1202 focus_delay_client
= NULL
;