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>
39 static void event_process(const XEvent
*e
, gpointer data
);
40 static void event_handle_root(XEvent
*e
);
41 static void event_handle_menu(XEvent
*e
);
42 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
43 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
44 static void event_handle_client(ObClient
*c
, XEvent
*e
);
45 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
47 static gboolean
focus_delay_func(gpointer data
);
48 static void focus_delay_client_dest(gpointer data
);
50 static gboolean
menu_hide_delay_func(gpointer data
);
52 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
53 (e)->xfocus.detail == NotifyAncestor || \
54 (e)->xfocus.detail > NotifyNonlinearVirtual)
55 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
56 (e)->xfocus.detail == NotifyInferior || \
57 (e)->xfocus.detail == NotifyAncestor || \
58 (e)->xfocus.detail > NotifyNonlinearVirtual)
60 Time event_lasttime
= 0;
62 /*! The value of the mask for the NumLock modifier */
63 unsigned int NumLockMask
;
64 /*! The value of the mask for the ScrollLock modifier */
65 unsigned int ScrollLockMask
;
66 /*! The key codes for the modifier keys */
67 static XModifierKeymap
*modmap
;
68 /*! Table of the constant modifier masks */
69 static const int mask_table
[] = {
70 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
71 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
73 static int mask_table_size
;
75 static ObClient
*focus_delay_client
;
77 static gboolean menu_can_hide
;
80 static void ice_handler(int fd
, gpointer conn
)
83 IceProcessMessages(conn
, NULL
, &b
);
86 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
87 IcePointer
*watch_data
)
92 fd
= IceConnectionNumber(conn
);
93 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
95 ob_main_loop_fd_remove(ob_main_loop
, fd
);
101 void event_startup(gboolean reconfig
)
103 if (reconfig
) return;
105 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
107 /* get lock masks that are defined by the display (not constant) */
108 modmap
= XGetModifierMapping(ob_display
);
110 if (modmap
&& modmap
->max_keypermod
> 0) {
112 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
113 /* get the values of the keyboard lock modifiers
114 Note: Caps lock is not retrieved the same way as Scroll and Num
115 lock since it doesn't need to be. */
116 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
117 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
120 for (cnt
= 0; cnt
< size
; ++cnt
) {
121 if (! modmap
->modifiermap
[cnt
]) continue;
123 if (num_lock
== modmap
->modifiermap
[cnt
])
124 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
125 if (scroll_lock
== modmap
->modifiermap
[cnt
])
126 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
130 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
133 IceAddConnectionWatch(ice_watch
, NULL
);
136 client_add_destructor(focus_delay_client_dest
);
139 void event_shutdown(gboolean reconfig
)
141 if (reconfig
) return;
144 IceRemoveConnectionWatch(ice_watch
, NULL
);
147 client_remove_destructor(focus_delay_client_dest
);
148 XFreeModifiermap(modmap
);
151 static Window
event_get_window(XEvent
*e
)
158 window
= RootWindow(ob_display
, ob_screen
);
161 window
= e
->xmap
.window
;
164 window
= e
->xunmap
.window
;
167 window
= e
->xdestroywindow
.window
;
169 case ConfigureRequest
:
170 window
= e
->xconfigurerequest
.window
;
172 case ConfigureNotify
:
173 window
= e
->xconfigure
.window
;
177 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
178 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
180 window
= ((XkbBellNotifyEvent
*)e
)->window
;
186 window
= e
->xany
.window
;
191 static void event_set_lasttime(XEvent
*e
)
195 /* grab the lasttime and hack up the state */
211 t
= e
->xproperty
.time
;
215 t
= e
->xcrossing
.time
;
218 /* if more event types are anticipated, get their timestamp
223 if (t
> event_lasttime
)
227 #define STRIP_MODS(s) \
228 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
229 /* kill off the Button1Mask etc, only want the modifiers */ \
230 s &= (ControlMask | ShiftMask | Mod1Mask | \
231 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
233 static void event_hack_mods(XEvent *e)
241 STRIP_MODS(e
->xbutton
.state
);
244 STRIP_MODS(e
->xkey
.state
);
247 STRIP_MODS(e
->xkey
.state
);
248 /* remove from the state the mask of the modifier being released, if
249 it is a modifier key being released (this is a little ugly..) */
250 kp
= modmap
->modifiermap
;
251 for (i
= 0; i
< mask_table_size
; ++i
) {
252 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
253 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
254 /* remove the mask for it */
255 e
->xkey
.state
&= ~mask_table
[i
];
256 /* cause the first loop to break; */
258 break; /* get outta here! */
265 STRIP_MODS(e
->xmotion
.state
);
266 /* compress events */
269 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
271 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
272 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
279 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
283 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
284 because of RevertToPointerRoot. If the focus ends up reverting to
285 pointer root on a workspace change, then the FocusIn event that we
286 want will be of type NotifyAncestor. This situation does not occur
287 for FocusOut, so it is safely ignored there.
289 if (INVALID_FOCUSIN(e
) ||
292 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
293 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
295 /* says a client was not found for the event (or a valid FocusIn
298 e
->xfocus
.window
= None
;
303 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
304 e
->xfocus
.mode
, e
->xfocus
.detail
);
308 if (INVALID_FOCUSOUT(e
)) {
310 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
311 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
317 ob_debug("FocusOut on %lx mode %d detail %d\n",
318 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
323 gboolean fallback
= TRUE
;
326 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
328 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
330 if (fe
.type
== FocusOut
) {
332 ob_debug("found pending FocusOut\n");
334 if (!INVALID_FOCUSOUT(&fe
)) {
335 /* if there is a VALID FocusOut still coming, don't
336 fallback focus yet, we'll deal with it then */
337 XPutBackEvent(ob_display
, &fe
);
343 ob_debug("found pending FocusIn\n");
345 /* is the focused window getting a FocusOut/In back to
348 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
349 !event_ignore(&fe
, client
)) {
351 if focus_client is not set, then we can't do
352 this. we need the FocusIn. This happens in the
353 case when the set_focus_client(NULL) in the
354 focus_fallback function fires and then
355 focus_fallback picks the currently focused
356 window (such as on a SendToDesktop-esque action.
360 ob_debug("focused window got an Out/In back to "
361 "itself IGNORED both\n");
365 event_process(&fe
, NULL
);
367 ob_debug("focused window got an Out/In back to "
368 "itself but focus_client was null "
369 "IGNORED just the Out\n");
375 /* once all the FocusOut's have been dealt with, if there
376 is a FocusIn still left and it is valid, then use it */
377 event_process(&fe
, NULL
);
378 /* secret magic way of event_process telling us that no
379 client was found for the FocusIn event. ^_^ */
380 if (fe
.xfocus
.window
!= None
) {
388 ob_debug("no valid FocusIn and no FocusOut events found, "
391 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
397 /* NotifyUngrab occurs when a mouse button is released and the event is
398 caused, like when lowering a window */
399 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
400 pointer (Ancestor happens when the pointer is on a window border) */
401 if (e
->xcrossing
.mode
== NotifyGrab
||
402 e
->xcrossing
.detail
== NotifyInferior
||
403 (e
->xcrossing
.mode
== NotifyUngrab
&&
404 (e
->xcrossing
.detail
== NotifyAncestor
||
405 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
406 e
->xcrossing
.detail
== NotifyVirtual
))) {
408 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
409 (e
->type
== EnterNotify
? "Enter" : "Leave"),
411 e
->xcrossing
.detail
, client
?client
->window
:0);
416 ob_debug("%sNotify mode %d detail %d on %lx\n",
417 (e
->type
== EnterNotify
? "Enter" : "Leave"),
419 e
->xcrossing
.detail
, client
?client
->window
:0);
426 static void event_process(const XEvent
*ec
, gpointer data
)
429 ObGroup
*group
= NULL
;
430 ObClient
*client
= NULL
;
432 ObDockApp
*dockapp
= NULL
;
433 ObWindow
*obwin
= NULL
;
436 /* make a copy we can mangle */
440 window
= event_get_window(e
);
441 if (!(e
->type
== PropertyNotify
&&
442 (group
= g_hash_table_lookup(group_map
, &window
))))
443 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
444 switch (obwin
->type
) {
446 dock
= WINDOW_AS_DOCK(obwin
);
449 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
452 client
= WINDOW_AS_CLIENT(obwin
);
455 case Window_Internal
:
456 /* not to be used for events */
457 g_assert_not_reached();
462 event_set_lasttime(e
);
464 if (event_ignore(e
, client
))
467 /* deal with it in the kernel */
469 event_handle_group(group
, e
);
471 event_handle_client(client
, e
);
473 event_handle_dockapp(dockapp
, e
);
475 event_handle_dock(dock
, e
);
476 else if (window
== RootWindow(ob_display
, ob_screen
))
477 event_handle_root(e
);
478 else if (e
->type
== MapRequest
)
479 client_manage(window
);
480 else if (e
->type
== ConfigureRequest
) {
481 /* unhandled configure requests must be used to configure the
485 xwc
.x
= e
->xconfigurerequest
.x
;
486 xwc
.y
= e
->xconfigurerequest
.y
;
487 xwc
.width
= e
->xconfigurerequest
.width
;
488 xwc
.height
= e
->xconfigurerequest
.height
;
489 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
490 xwc
.sibling
= e
->xconfigurerequest
.above
;
491 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
493 /* we are not to be held responsible if someone sends us an
495 xerror_set_ignore(TRUE
);
496 XConfigureWindow(ob_display
, window
,
497 e
->xconfigurerequest
.value_mask
, &xwc
);
498 xerror_set_ignore(FALSE
);
501 /* user input (action-bound) events */
502 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
503 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
504 e
->type
== KeyRelease
)
506 if (menu_frame_visible
)
507 event_handle_menu(e
);
509 if (!keyboard_process_interactive_grab(e
, &client
)) {
510 if (moveresize_in_progress
)
513 menu_can_hide
= FALSE
;
514 ob_main_loop_timeout_add(ob_main_loop
,
516 menu_hide_delay_func
,
519 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
520 e
->type
== MotionNotify
)
521 mouse_event(client
, e
);
522 else if (e
->type
== KeyPress
)
523 /* when in the middle of a focus cycling action, this
524 causes the window which appears to be focused to be
525 the one on which the actions will be executed */
526 keyboard_event((focus_cycle_target
?
528 (client
? client
: focus_client
)), e
);
534 static void event_handle_root(XEvent
*e
)
540 ob_debug("Another WM has requested to replace us. Exiting.\n");
545 if (e
->xclient
.format
!= 32) break;
547 msgtype
= e
->xclient
.message_type
;
548 if (msgtype
== prop_atoms
.net_current_desktop
) {
549 unsigned int d
= e
->xclient
.data
.l
[0];
550 if (d
< screen_num_desktops
)
551 screen_set_desktop(d
);
552 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
553 unsigned int d
= e
->xclient
.data
.l
[0];
555 screen_set_num_desktops(d
);
556 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
557 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
561 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
562 screen_update_desktop_names();
563 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
564 screen_update_layout();
566 case ConfigureNotify
:
568 XRRUpdateConfiguration(e
);
575 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
576 ob_debug("VIDMODE EVENT\n");
582 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
586 g_assert(e
->type
== PropertyNotify
);
588 for (it
= group
->members
; it
; it
= g_slist_next(it
))
589 event_handle_client(it
->data
, e
);
592 static void event_handle_client(ObClient
*client
, XEvent
*e
)
600 case VisibilityNotify
:
601 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
605 /* Wheel buttons don't draw because they are an instant click, so it
606 is a waste of resources to go drawing it. */
607 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
608 con
= frame_context(client
, e
->xbutton
.window
);
609 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
611 case OB_FRAME_CONTEXT_MAXIMIZE
:
612 client
->frame
->max_press
= (e
->type
== ButtonPress
);
613 framerender_frame(client
->frame
);
615 case OB_FRAME_CONTEXT_CLOSE
:
616 client
->frame
->close_press
= (e
->type
== ButtonPress
);
617 framerender_frame(client
->frame
);
619 case OB_FRAME_CONTEXT_ICONIFY
:
620 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
621 framerender_frame(client
->frame
);
623 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
624 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
625 framerender_frame(client
->frame
);
627 case OB_FRAME_CONTEXT_SHADE
:
628 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
629 framerender_frame(client
->frame
);
632 /* nothing changes with clicks for any other contexts */
639 ob_debug("FocusIn on client for %lx\n", client
->window
);
641 if (client
!= focus_client
) {
642 focus_set_client(client
);
643 frame_adjust_focus(client
->frame
, TRUE
);
648 ob_debug("FocusOut on client for %lx\n", client
->window
);
650 /* are we a fullscreen window or a transient of one? (checks layer)
651 if we are then we need to be iconified since we are losing focus
653 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
654 !client_search_focus_tree_full(client
))
655 /* iconify fullscreen windows when they and their transients
657 client_iconify(client
, TRUE
, TRUE
);
658 frame_adjust_focus(client
->frame
, FALSE
);
661 con
= frame_context(client
, e
->xcrossing
.window
);
663 case OB_FRAME_CONTEXT_MAXIMIZE
:
664 client
->frame
->max_hover
= FALSE
;
665 frame_adjust_state(client
->frame
);
667 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
668 client
->frame
->desk_hover
= FALSE
;
669 frame_adjust_state(client
->frame
);
671 case OB_FRAME_CONTEXT_SHADE
:
672 client
->frame
->shade_hover
= FALSE
;
673 frame_adjust_state(client
->frame
);
675 case OB_FRAME_CONTEXT_ICONIFY
:
676 client
->frame
->iconify_hover
= FALSE
;
677 frame_adjust_state(client
->frame
);
679 case OB_FRAME_CONTEXT_CLOSE
:
680 client
->frame
->close_hover
= FALSE
;
681 frame_adjust_state(client
->frame
);
683 case OB_FRAME_CONTEXT_FRAME
:
684 /* XXX if doing a 'reconfigure' make sure you kill this timer,
685 maybe all timers.. */
686 if (config_focus_delay
&& client
== focus_delay_client
) {
687 ob_main_loop_timeout_remove_data(ob_main_loop
,
690 focus_delay_client
= NULL
;
697 con
= frame_context(client
, e
->xcrossing
.window
);
699 case OB_FRAME_CONTEXT_MAXIMIZE
:
700 client
->frame
->max_hover
= TRUE
;
701 frame_adjust_state(client
->frame
);
703 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
704 client
->frame
->desk_hover
= TRUE
;
705 frame_adjust_state(client
->frame
);
707 case OB_FRAME_CONTEXT_SHADE
:
708 client
->frame
->shade_hover
= TRUE
;
709 frame_adjust_state(client
->frame
);
711 case OB_FRAME_CONTEXT_ICONIFY
:
712 client
->frame
->iconify_hover
= TRUE
;
713 frame_adjust_state(client
->frame
);
715 case OB_FRAME_CONTEXT_CLOSE
:
716 client
->frame
->close_hover
= TRUE
;
717 frame_adjust_state(client
->frame
);
719 case OB_FRAME_CONTEXT_FRAME
:
720 if (client_normal(client
)) {
721 if (config_focus_follow
) {
723 ob_debug("EnterNotify on %lx, focusing window\n",
726 if (config_focus_delay
) {
727 ob_main_loop_timeout_add(ob_main_loop
,
731 focus_delay_client
= client
;
733 client_focus(client
);
741 case ConfigureRequest
:
743 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
744 ConfigureRequest
, &ce
)) {
746 /* XXX if this causes bad things.. we can compress config req's
747 with the same mask. */
748 e
->xconfigurerequest
.value_mask
|=
749 ce
.xconfigurerequest
.value_mask
;
750 if (ce
.xconfigurerequest
.value_mask
& CWX
)
751 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
752 if (ce
.xconfigurerequest
.value_mask
& CWY
)
753 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
754 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
755 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
756 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
757 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
758 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
759 e
->xconfigurerequest
.border_width
=
760 ce
.xconfigurerequest
.border_width
;
761 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
762 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
765 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
766 if (client
->iconic
|| client
->shaded
) return;
768 /* resize, then move, as specified in the EWMH section 7.7 */
769 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
775 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
776 client
->border_width
= e
->xconfigurerequest
.border_width
;
778 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
779 e
->xconfigurerequest
.x
: client
->area
.x
;
780 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
781 e
->xconfigurerequest
.y
: client
->area
.y
;
782 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
783 e
->xconfigurerequest
.width
: client
->area
.width
;
784 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
785 e
->xconfigurerequest
.height
: client
->area
.height
;
791 client
->frame
->size
.left
+ client
->frame
->size
.right
;
793 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
794 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
795 client_normal(client
));
796 if (e
->xconfigurerequest
.value_mask
& CWX
)
798 if (e
->xconfigurerequest
.value_mask
& CWY
)
802 switch (client
->gravity
) {
803 case NorthEastGravity
:
805 corner
= OB_CORNER_TOPRIGHT
;
807 case SouthWestGravity
:
809 corner
= OB_CORNER_BOTTOMLEFT
;
811 case SouthEastGravity
:
812 corner
= OB_CORNER_BOTTOMRIGHT
;
814 default: /* NorthWest, Static, etc */
815 corner
= OB_CORNER_TOPLEFT
;
818 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
822 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
823 switch (e
->xconfigurerequest
.detail
) {
826 stacking_lower(CLIENT_AS_WINDOW(client
));
832 stacking_raise(CLIENT_AS_WINDOW(client
));
838 if (client
->ignore_unmaps
) {
839 client
->ignore_unmaps
--;
842 client_unmanage(client
);
845 client_unmanage(client
);
848 /* this is when the client is first taken captive in the frame */
849 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
852 This event is quite rare and is usually handled in unmapHandler.
853 However, if the window is unmapped when the reparent event occurs,
854 the window manager never sees it because an unmap event is not sent
855 to an already unmapped window.
858 /* we don't want the reparent event, put it back on the stack for the
859 X server to deal with after we unmanage the window */
860 XPutBackEvent(ob_display
, e
);
862 client_unmanage(client
);
865 ob_debug("MapRequest for 0x%lx\n", client
->window
);
866 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
867 does, we don't want it! */
868 if (screen_showing_desktop
)
869 screen_show_desktop(FALSE
);
870 client_iconify(client
, FALSE
, TRUE
);
871 if (!client
->frame
->visible
)
872 /* if its not visible still, then don't mess with it */
875 client_shade(client
, FALSE
);
876 client_focus(client
);
877 stacking_raise(CLIENT_AS_WINDOW(client
));
880 /* validate cuz we query stuff off the client here */
881 if (!client_validate(client
)) break;
883 if (e
->xclient
.format
!= 32) return;
885 msgtype
= e
->xclient
.message_type
;
886 if (msgtype
== prop_atoms
.wm_change_state
) {
887 /* compress changes into a single change */
888 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
890 /* XXX: it would be nice to compress ALL messages of a
891 type, not just messages in a row without other
892 message types between. */
893 if (ce
.xclient
.message_type
!= msgtype
) {
894 XPutBackEvent(ob_display
, &ce
);
897 e
->xclient
= ce
.xclient
;
899 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
900 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
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 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
914 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
915 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
917 } else if (msgtype
== prop_atoms
.net_wm_state
) {
918 /* can't compress these */
919 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
920 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
921 e
->xclient
.data
.l
[0] == 1 ? "Add" :
922 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
923 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
925 client_set_state(client
, e
->xclient
.data
.l
[0],
926 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
927 } else if (msgtype
== prop_atoms
.net_close_window
) {
928 ob_debug("net_close_window for 0x%lx\n", client
->window
);
929 client_close(client
);
930 } else if (msgtype
== prop_atoms
.net_active_window
) {
931 ob_debug("net_active_window for 0x%lx\n", client
->window
);
932 client_activate(client
, FALSE
);
933 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
934 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
935 if ((Atom
)e
->xclient
.data
.l
[2] ==
936 prop_atoms
.net_wm_moveresize_size_topleft
||
937 (Atom
)e
->xclient
.data
.l
[2] ==
938 prop_atoms
.net_wm_moveresize_size_top
||
939 (Atom
)e
->xclient
.data
.l
[2] ==
940 prop_atoms
.net_wm_moveresize_size_topright
||
941 (Atom
)e
->xclient
.data
.l
[2] ==
942 prop_atoms
.net_wm_moveresize_size_right
||
943 (Atom
)e
->xclient
.data
.l
[2] ==
944 prop_atoms
.net_wm_moveresize_size_right
||
945 (Atom
)e
->xclient
.data
.l
[2] ==
946 prop_atoms
.net_wm_moveresize_size_bottomright
||
947 (Atom
)e
->xclient
.data
.l
[2] ==
948 prop_atoms
.net_wm_moveresize_size_bottom
||
949 (Atom
)e
->xclient
.data
.l
[2] ==
950 prop_atoms
.net_wm_moveresize_size_bottomleft
||
951 (Atom
)e
->xclient
.data
.l
[2] ==
952 prop_atoms
.net_wm_moveresize_size_left
||
953 (Atom
)e
->xclient
.data
.l
[2] ==
954 prop_atoms
.net_wm_moveresize_move
||
955 (Atom
)e
->xclient
.data
.l
[2] ==
956 prop_atoms
.net_wm_moveresize_size_keyboard
||
957 (Atom
)e
->xclient
.data
.l
[2] ==
958 prop_atoms
.net_wm_moveresize_move_keyboard
) {
960 moveresize_start(client
, e
->xclient
.data
.l
[0],
961 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
962 e
->xclient
.data
.l
[2]);
964 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
965 int oldg
= client
->gravity
;
966 int tmpg
, x
, y
, w
, h
;
968 if (e
->xclient
.data
.l
[0] & 0xff)
969 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
973 if (e
->xclient
.data
.l
[0] & 1 << 8)
974 x
= e
->xclient
.data
.l
[1];
977 if (e
->xclient
.data
.l
[0] & 1 << 9)
978 y
= e
->xclient
.data
.l
[2];
981 if (e
->xclient
.data
.l
[0] & 1 << 10)
982 w
= e
->xclient
.data
.l
[3];
984 w
= client
->area
.width
;
985 if (e
->xclient
.data
.l
[0] & 1 << 11)
986 h
= e
->xclient
.data
.l
[4];
988 h
= client
->area
.height
;
989 client
->gravity
= tmpg
;
995 client
->frame
->size
.left
+ client
->frame
->size
.right
;
997 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
998 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
999 client_normal(client
));
1000 if (e
->xclient
.data
.l
[0] & 1 << 8)
1002 if (e
->xclient
.data
.l
[0] & 1 << 9)
1006 client_configure(client
, OB_CORNER_TOPLEFT
,
1007 x
, y
, w
, h
, FALSE
, TRUE
);
1009 client
->gravity
= oldg
;
1012 case PropertyNotify
:
1013 /* validate cuz we query stuff off the client here */
1014 if (!client_validate(client
)) break;
1016 /* compress changes to a single property into a single change */
1017 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1021 /* XXX: it would be nice to compress ALL changes to a property,
1022 not just changes in a row without other props between. */
1024 a
= ce
.xproperty
.atom
;
1025 b
= e
->xproperty
.atom
;
1029 if ((a
== prop_atoms
.net_wm_name
||
1030 a
== prop_atoms
.wm_name
||
1031 a
== prop_atoms
.net_wm_icon_name
||
1032 a
== prop_atoms
.wm_icon_name
)
1034 (b
== prop_atoms
.net_wm_name
||
1035 b
== prop_atoms
.wm_name
||
1036 b
== prop_atoms
.net_wm_icon_name
||
1037 b
== prop_atoms
.wm_icon_name
)) {
1040 if ((a
== prop_atoms
.net_wm_icon
||
1041 a
== prop_atoms
.kwm_win_icon
)
1043 (b
== prop_atoms
.net_wm_icon
||
1044 b
== prop_atoms
.kwm_win_icon
))
1047 XPutBackEvent(ob_display
, &ce
);
1051 msgtype
= e
->xproperty
.atom
;
1052 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1053 client_update_normal_hints(client
);
1054 /* normal hints can make a window non-resizable */
1055 client_setup_decor_and_functions(client
);
1056 } else if (msgtype
== XA_WM_HINTS
) {
1057 client_update_wmhints(client
);
1058 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1059 client_update_transient_for(client
);
1060 client_get_type(client
);
1061 /* type may have changed, so update the layer */
1062 client_calc_layer(client
);
1063 client_setup_decor_and_functions(client
);
1064 } else if (msgtype
== prop_atoms
.net_wm_name
||
1065 msgtype
== prop_atoms
.wm_name
||
1066 msgtype
== prop_atoms
.net_wm_icon_name
||
1067 msgtype
== prop_atoms
.wm_icon_name
) {
1068 client_update_title(client
);
1069 } else if (msgtype
== prop_atoms
.wm_class
) {
1070 client_update_class(client
);
1071 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1072 client_update_protocols(client
);
1073 client_setup_decor_and_functions(client
);
1075 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1076 client_update_strut(client
);
1078 else if (msgtype
== prop_atoms
.net_wm_icon
||
1079 msgtype
== prop_atoms
.kwm_win_icon
) {
1080 client_update_icons(client
);
1082 else if (msgtype
== prop_atoms
.sm_client_id
) {
1083 client_update_sm_client_id(client
);
1088 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1089 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1090 frame_adjust_shape(client
->frame
);
1096 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1100 stacking_raise(DOCK_AS_WINDOW(s
));
1111 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1115 dock_app_drag(app
, &e
->xmotion
);
1118 if (app
->ignore_unmaps
) {
1119 app
->ignore_unmaps
--;
1122 dock_remove(app
, TRUE
);
1125 dock_remove(app
, FALSE
);
1127 case ReparentNotify
:
1128 dock_remove(app
, FALSE
);
1130 case ConfigureNotify
:
1131 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1136 ObMenuFrame
* find_active_menu()
1141 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1146 return it
? it
->data
: NULL
;
1149 static void event_handle_menu(XEvent
*ev
)
1152 ObMenuEntryFrame
*e
;
1156 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1157 ev
->xbutton
.y_root
)))
1158 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1159 else if (menu_can_hide
)
1160 menu_frame_hide_all();
1163 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1164 ev
->xmotion
.y_root
))) {
1165 menu_frame_move_on_screen(f
);
1166 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1167 ev
->xmotion
.y_root
)))
1168 menu_frame_select(f
, e
);
1172 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1173 menu_frame_hide_all();
1174 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1176 if ((f
= find_active_menu()))
1177 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1178 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1180 if ((f
= find_active_menu()) && f
->parent
)
1181 menu_frame_select(f
, NULL
);
1182 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1184 if ((f
= find_active_menu()) && f
->child
)
1185 menu_frame_select_next(f
->child
);
1186 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1188 if ((f
= find_active_menu()))
1189 menu_frame_select_previous(f
);
1190 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1192 if ((f
= find_active_menu()))
1193 menu_frame_select_next(f
);
1199 static gboolean
menu_hide_delay_func(gpointer data
)
1201 menu_can_hide
= TRUE
;
1202 return FALSE
; /* no repeat */
1205 static gboolean
focus_delay_func(gpointer data
)
1207 client_focus(focus_delay_client
);
1208 return FALSE
; /* no repeat */
1211 static void focus_delay_client_dest(gpointer data
)
1214 if (c
== focus_delay_client
) {
1215 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1216 focus_delay_client
);
1217 focus_delay_client
= NULL
;