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;
141 client_remove_destructor(focus_delay_client_dest
);
142 XFreeModifiermap(modmap
);
145 static Window
event_get_window(XEvent
*e
)
152 window
= RootWindow(ob_display
, ob_screen
);
155 window
= e
->xmap
.window
;
158 window
= e
->xunmap
.window
;
161 window
= e
->xdestroywindow
.window
;
163 case ConfigureRequest
:
164 window
= e
->xconfigurerequest
.window
;
166 case ConfigureNotify
:
167 window
= e
->xconfigure
.window
;
171 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
172 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
174 window
= ((XkbBellNotifyEvent
*)e
)->window
;
180 window
= e
->xany
.window
;
185 static void event_set_lasttime(XEvent
*e
)
189 /* grab the lasttime and hack up the state */
205 t
= e
->xproperty
.time
;
209 t
= e
->xcrossing
.time
;
212 /* if more event types are anticipated, get their timestamp
217 if (t
> event_lasttime
)
221 #define STRIP_MODS(s) \
222 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
223 /* kill off the Button1Mask etc, only want the modifiers */ \
224 s &= (ControlMask | ShiftMask | Mod1Mask | \
225 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
227 static void event_hack_mods(XEvent *e)
235 STRIP_MODS(e
->xbutton
.state
);
238 STRIP_MODS(e
->xkey
.state
);
241 STRIP_MODS(e
->xkey
.state
);
242 /* remove from the state the mask of the modifier being released, if
243 it is a modifier key being released (this is a little ugly..) */
244 kp
= modmap
->modifiermap
;
245 for (i
= 0; i
< mask_table_size
; ++i
) {
246 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
247 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
248 /* remove the mask for it */
249 e
->xkey
.state
&= ~mask_table
[i
];
250 /* cause the first loop to break; */
252 break; /* get outta here! */
259 STRIP_MODS(e
->xmotion
.state
);
260 /* compress events */
263 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
265 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
266 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
273 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
277 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
278 because of RevertToPointerRoot. If the focus ends up reverting to
279 pointer root on a workspace change, then the FocusIn event that we
280 want will be of type NotifyAncestor. This situation does not occur
281 for FocusOut, so it is safely ignored there.
283 if (INVALID_FOCUSIN(e
) ||
286 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
287 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
289 /* says a client was not found for the event (or a valid FocusIn
292 e
->xfocus
.window
= None
;
297 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
298 e
->xfocus
.mode
, e
->xfocus
.detail
);
302 if (INVALID_FOCUSOUT(e
)) {
304 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
305 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
311 ob_debug("FocusOut on %lx mode %d detail %d\n",
312 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
317 gboolean fallback
= TRUE
;
320 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
322 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
324 if (fe
.type
== FocusOut
) {
326 ob_debug("found pending FocusOut\n");
328 if (!INVALID_FOCUSOUT(&fe
)) {
329 /* if there is a VALID FocusOut still coming, don't
330 fallback focus yet, we'll deal with it then */
331 XPutBackEvent(ob_display
, &fe
);
337 ob_debug("found pending FocusIn\n");
339 /* is the focused window getting a FocusOut/In back to
342 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
343 !event_ignore(&fe
, client
)) {
345 if focus_client is not set, then we can't do
346 this. we need the FocusIn. This happens in the
347 case when the set_focus_client(NULL) in the
348 focus_fallback function fires and then
349 focus_fallback picks the currently focused
350 window (such as on a SendToDesktop-esque action.
354 ob_debug("focused window got an Out/In back to "
355 "itself IGNORED both\n");
359 event_process(&fe
, NULL
);
361 ob_debug("focused window got an Out/In back to "
362 "itself but focus_client was null "
363 "IGNORED just the Out\n");
369 /* once all the FocusOut's have been dealt with, if there
370 is a FocusIn still left and it is valid, then use it */
371 event_process(&fe
, NULL
);
372 /* secret magic way of event_process telling us that no
373 client was found for the FocusIn event. ^_^ */
374 if (fe
.xfocus
.window
!= None
) {
382 ob_debug("no valid FocusIn and no FocusOut events found, "
385 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
391 /* NotifyUngrab occurs when a mouse button is released and the event is
392 caused, like when lowering a window */
393 /* NotifyVirtual occurs when ungrabbing the pointer */
394 if (e
->xcrossing
.mode
== NotifyGrab
||
395 e
->xcrossing
.detail
== NotifyInferior
||
396 (e
->xcrossing
.mode
== NotifyUngrab
&&
397 e
->xcrossing
.detail
== NotifyVirtual
)) {
399 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
400 (e
->type
== EnterNotify
? "Enter" : "Leave"),
402 e
->xcrossing
.detail
, client
?client
->window
:0);
407 ob_debug("%sNotify mode %d detail %d on %lx\n",
408 (e
->type
== EnterNotify
? "Enter" : "Leave"),
410 e
->xcrossing
.detail
, client
?client
->window
:0);
417 static void event_process(const XEvent
*ec
, gpointer data
)
420 ObClient
*client
= NULL
;
422 ObDockApp
*dockapp
= NULL
;
423 ObWindow
*obwin
= NULL
;
426 /* make a copy we can mangle */
430 window
= event_get_window(e
);
431 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
432 switch (obwin
->type
) {
434 dock
= WINDOW_AS_DOCK(obwin
);
437 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
440 client
= WINDOW_AS_CLIENT(obwin
);
443 case Window_Internal
:
444 /* not to be used for events */
445 g_assert_not_reached();
450 event_set_lasttime(e
);
452 if (event_ignore(e
, client
))
455 /* deal with it in the kernel */
457 event_handle_client(client
, e
);
459 event_handle_dockapp(dockapp
, e
);
461 event_handle_dock(dock
, e
);
462 else if (window
== RootWindow(ob_display
, ob_screen
))
463 event_handle_root(e
);
464 else if (e
->type
== MapRequest
)
465 client_manage(window
);
466 else if (e
->type
== ConfigureRequest
) {
467 /* unhandled configure requests must be used to configure the
471 xwc
.x
= e
->xconfigurerequest
.x
;
472 xwc
.y
= e
->xconfigurerequest
.y
;
473 xwc
.width
= e
->xconfigurerequest
.width
;
474 xwc
.height
= e
->xconfigurerequest
.height
;
475 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
476 xwc
.sibling
= e
->xconfigurerequest
.above
;
477 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
479 /* we are not to be held responsible if someone sends us an
481 xerror_set_ignore(TRUE
);
482 XConfigureWindow(ob_display
, window
,
483 e
->xconfigurerequest
.value_mask
, &xwc
);
484 xerror_set_ignore(FALSE
);
487 /* user input (action-bound) events */
488 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
489 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
490 e
->type
== KeyRelease
)
492 if (menu_frame_visible
)
493 event_handle_menu(e
);
495 if (!keyboard_process_interactive_grab(e
, &client
)) {
496 if (moveresize_in_progress
)
499 menu_can_hide
= FALSE
;
500 ob_main_loop_timeout_add(ob_main_loop
,
502 menu_hide_delay_func
,
505 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
506 e
->type
== MotionNotify
)
507 mouse_event(client
, e
);
508 else if (e
->type
== KeyPress
)
509 /* when in the middle of a focus cycling action, this
510 causes the window which appears to be focused to be
511 the one on which the actions will be executed */
512 keyboard_event((focus_cycle_target
?
514 (client
? client
: focus_client
)), e
);
520 static void event_handle_root(XEvent
*e
)
526 ob_debug("Another WM has requested to replace us. Exiting.\n");
531 if (e
->xclient
.format
!= 32) break;
533 msgtype
= e
->xclient
.message_type
;
534 if (msgtype
== prop_atoms
.net_current_desktop
) {
535 unsigned int d
= e
->xclient
.data
.l
[0];
536 if (d
< screen_num_desktops
)
537 screen_set_desktop(d
);
538 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
539 unsigned int d
= e
->xclient
.data
.l
[0];
541 screen_set_num_desktops(d
);
542 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
543 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
547 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
548 screen_update_desktop_names();
549 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
550 screen_update_layout();
552 case ConfigureNotify
:
554 XRRUpdateConfiguration(e
);
561 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
562 ob_debug("VIDMODE EVENT\n");
568 static void event_handle_client(ObClient
*client
, XEvent
*e
)
576 case VisibilityNotify
:
577 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
581 /* Wheel buttons don't draw because they are an instant click, so it
582 is a waste of resources to go drawing it. */
583 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
584 switch (frame_context(client
, e
->xbutton
.window
)) {
585 case OB_FRAME_CONTEXT_MAXIMIZE
:
586 client
->frame
->max_press
= (e
->type
== ButtonPress
);
587 framerender_frame(client
->frame
);
589 case OB_FRAME_CONTEXT_CLOSE
:
590 client
->frame
->close_press
= (e
->type
== ButtonPress
);
591 framerender_frame(client
->frame
);
593 case OB_FRAME_CONTEXT_ICONIFY
:
594 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
595 framerender_frame(client
->frame
);
597 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
598 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
599 framerender_frame(client
->frame
);
601 case OB_FRAME_CONTEXT_SHADE
:
602 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
603 framerender_frame(client
->frame
);
606 /* nothing changes with clicks for any other contexts */
613 ob_debug("FocusIn on client for %lx\n", client
->window
);
615 if (client
!= focus_client
) {
616 focus_set_client(client
);
617 frame_adjust_focus(client
->frame
, TRUE
);
622 ob_debug("FocusOut on client for %lx\n", client
->window
);
624 /* are we a fullscreen window or a transient of one? (checks layer)
625 if we are then we need to be iconified since we are losing focus
627 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
628 !client_search_focus_tree_full(client
))
629 /* iconify fullscreen windows when they and their transients
631 client_iconify(client
, TRUE
, TRUE
);
632 frame_adjust_focus(client
->frame
, FALSE
);
635 con
= frame_context(client
, e
->xcrossing
.window
);
637 case OB_FRAME_CONTEXT_MAXIMIZE
:
638 client
->frame
->max_hover
= FALSE
;
639 frame_adjust_state(client
->frame
);
641 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
642 client
->frame
->desk_hover
= FALSE
;
643 frame_adjust_state(client
->frame
);
645 case OB_FRAME_CONTEXT_SHADE
:
646 client
->frame
->shade_hover
= FALSE
;
647 frame_adjust_state(client
->frame
);
649 case OB_FRAME_CONTEXT_ICONIFY
:
650 client
->frame
->iconify_hover
= FALSE
;
651 frame_adjust_state(client
->frame
);
653 case OB_FRAME_CONTEXT_CLOSE
:
654 client
->frame
->close_hover
= FALSE
;
655 frame_adjust_state(client
->frame
);
657 case OB_FRAME_CONTEXT_FRAME
:
658 /* XXX if doing a 'reconfigure' make sure you kill this timer,
659 maybe all timers.. */
660 if (config_focus_delay
&& client
== focus_delay_client
) {
661 ob_main_loop_timeout_remove_data(ob_main_loop
,
664 focus_delay_client
= NULL
;
671 con
= frame_context(client
, e
->xcrossing
.window
);
673 case OB_FRAME_CONTEXT_MAXIMIZE
:
674 client
->frame
->max_hover
= TRUE
;
675 frame_adjust_state(client
->frame
);
677 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
678 client
->frame
->desk_hover
= TRUE
;
679 frame_adjust_state(client
->frame
);
681 case OB_FRAME_CONTEXT_SHADE
:
682 client
->frame
->shade_hover
= TRUE
;
683 frame_adjust_state(client
->frame
);
685 case OB_FRAME_CONTEXT_ICONIFY
:
686 client
->frame
->iconify_hover
= TRUE
;
687 frame_adjust_state(client
->frame
);
689 case OB_FRAME_CONTEXT_CLOSE
:
690 client
->frame
->close_hover
= TRUE
;
691 frame_adjust_state(client
->frame
);
693 case OB_FRAME_CONTEXT_FRAME
:
694 if (client_normal(client
)) {
695 if (ob_state() == OB_STATE_STARTING
) {
696 /* move it to the top of the focus order */
697 guint desktop
= client
->desktop
;
698 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
699 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
701 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
703 } else if (config_focus_follow
) {
705 ob_debug("EnterNotify on %lx, focusing window\n",
708 if (config_focus_delay
) {
709 ob_main_loop_timeout_add(ob_main_loop
,
713 focus_delay_client
= client
;
715 client_focus(client
);
723 case ConfigureRequest
:
725 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
726 ConfigureRequest
, &ce
)) {
728 /* XXX if this causes bad things.. we can compress config req's
729 with the same mask. */
730 e
->xconfigurerequest
.value_mask
|=
731 ce
.xconfigurerequest
.value_mask
;
732 if (ce
.xconfigurerequest
.value_mask
& CWX
)
733 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
734 if (ce
.xconfigurerequest
.value_mask
& CWY
)
735 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
736 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
737 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
738 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
739 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
740 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
741 e
->xconfigurerequest
.border_width
=
742 ce
.xconfigurerequest
.border_width
;
743 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
744 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
747 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
748 if (client
->iconic
|| client
->shaded
) return;
750 /* resize, then move, as specified in the EWMH section 7.7 */
751 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
757 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
758 client
->border_width
= e
->xconfigurerequest
.border_width
;
760 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
761 e
->xconfigurerequest
.x
: client
->area
.x
;
762 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
763 e
->xconfigurerequest
.y
: client
->area
.y
;
764 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
765 e
->xconfigurerequest
.width
: client
->area
.width
;
766 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
767 e
->xconfigurerequest
.height
: client
->area
.height
;
773 client
->frame
->size
.left
+ client
->frame
->size
.right
;
775 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
776 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
777 client_normal(client
));
778 if (e
->xconfigurerequest
.value_mask
& CWX
)
780 if (e
->xconfigurerequest
.value_mask
& CWY
)
784 switch (client
->gravity
) {
785 case NorthEastGravity
:
787 corner
= OB_CORNER_TOPRIGHT
;
789 case SouthWestGravity
:
791 corner
= OB_CORNER_BOTTOMLEFT
;
793 case SouthEastGravity
:
794 corner
= OB_CORNER_BOTTOMRIGHT
;
796 default: /* NorthWest, Static, etc */
797 corner
= OB_CORNER_TOPLEFT
;
800 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
804 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
805 switch (e
->xconfigurerequest
.detail
) {
808 stacking_lower(CLIENT_AS_WINDOW(client
));
814 stacking_raise(CLIENT_AS_WINDOW(client
));
820 if (client
->ignore_unmaps
) {
821 client
->ignore_unmaps
--;
824 client_unmanage(client
);
827 client_unmanage(client
);
830 /* this is when the client is first taken captive in the frame */
831 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
834 This event is quite rare and is usually handled in unmapHandler.
835 However, if the window is unmapped when the reparent event occurs,
836 the window manager never sees it because an unmap event is not sent
837 to an already unmapped window.
840 /* we don't want the reparent event, put it back on the stack for the
841 X server to deal with after we unmanage the window */
842 XPutBackEvent(ob_display
, e
);
844 client_unmanage(client
);
847 ob_debug("MapRequest for 0x%lx\n", client
->window
);
848 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
849 does, we don't want it! */
850 if (screen_showing_desktop
)
851 screen_show_desktop(FALSE
);
852 client_iconify(client
, FALSE
, TRUE
);
853 if (!client
->frame
->visible
)
854 /* if its not visible still, then don't mess with it */
857 client_shade(client
, FALSE
);
858 client_focus(client
);
859 stacking_raise(CLIENT_AS_WINDOW(client
));
862 /* validate cuz we query stuff off the client here */
863 if (!client_validate(client
)) break;
865 if (e
->xclient
.format
!= 32) return;
867 msgtype
= e
->xclient
.message_type
;
868 if (msgtype
== prop_atoms
.wm_change_state
) {
869 /* compress changes into a single change */
870 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
872 /* XXX: it would be nice to compress ALL messages of a
873 type, not just messages in a row without other
874 message types between. */
875 if (ce
.xclient
.message_type
!= msgtype
) {
876 XPutBackEvent(ob_display
, &ce
);
879 e
->xclient
= ce
.xclient
;
881 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
882 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
883 /* compress changes into a single change */
884 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
886 /* XXX: it would be nice to compress ALL messages of a
887 type, not just messages in a row without other
888 message types between. */
889 if (ce
.xclient
.message_type
!= msgtype
) {
890 XPutBackEvent(ob_display
, &ce
);
893 e
->xclient
= ce
.xclient
;
895 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
896 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
897 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
899 } else if (msgtype
== prop_atoms
.net_wm_state
) {
900 /* can't compress these */
901 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
902 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
903 e
->xclient
.data
.l
[0] == 1 ? "Add" :
904 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
905 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
907 client_set_state(client
, e
->xclient
.data
.l
[0],
908 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
909 } else if (msgtype
== prop_atoms
.net_close_window
) {
910 ob_debug("net_close_window for 0x%lx\n", client
->window
);
911 client_close(client
);
912 } else if (msgtype
== prop_atoms
.net_active_window
) {
913 ob_debug("net_active_window for 0x%lx\n", client
->window
);
914 client_activate(client
, FALSE
);
915 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
916 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
917 if ((Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_topleft
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_top
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_size_topright
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_right
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_right
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_size_bottomright
||
929 (Atom
)e
->xclient
.data
.l
[2] ==
930 prop_atoms
.net_wm_moveresize_size_bottom
||
931 (Atom
)e
->xclient
.data
.l
[2] ==
932 prop_atoms
.net_wm_moveresize_size_bottomleft
||
933 (Atom
)e
->xclient
.data
.l
[2] ==
934 prop_atoms
.net_wm_moveresize_size_left
||
935 (Atom
)e
->xclient
.data
.l
[2] ==
936 prop_atoms
.net_wm_moveresize_move
||
937 (Atom
)e
->xclient
.data
.l
[2] ==
938 prop_atoms
.net_wm_moveresize_size_keyboard
||
939 (Atom
)e
->xclient
.data
.l
[2] ==
940 prop_atoms
.net_wm_moveresize_move_keyboard
) {
942 moveresize_start(client
, e
->xclient
.data
.l
[0],
943 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
944 e
->xclient
.data
.l
[2]);
946 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
947 int oldg
= client
->gravity
;
948 int tmpg
, x
, y
, w
, h
;
950 if (e
->xclient
.data
.l
[0] & 0xff)
951 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
955 if (e
->xclient
.data
.l
[0] & 1 << 8)
956 x
= e
->xclient
.data
.l
[1];
959 if (e
->xclient
.data
.l
[0] & 1 << 9)
960 y
= e
->xclient
.data
.l
[2];
963 if (e
->xclient
.data
.l
[0] & 1 << 10)
964 w
= e
->xclient
.data
.l
[3];
966 w
= client
->area
.width
;
967 if (e
->xclient
.data
.l
[0] & 1 << 11)
968 h
= e
->xclient
.data
.l
[4];
970 h
= client
->area
.height
;
971 client
->gravity
= tmpg
;
977 client
->frame
->size
.left
+ client
->frame
->size
.right
;
979 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
980 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
981 client_normal(client
));
982 if (e
->xclient
.data
.l
[0] & 1 << 8)
984 if (e
->xclient
.data
.l
[0] & 1 << 9)
988 client_configure(client
, OB_CORNER_TOPLEFT
,
989 x
, y
, w
, h
, FALSE
, TRUE
);
991 client
->gravity
= oldg
;
995 /* validate cuz we query stuff off the client here */
996 if (!client_validate(client
)) break;
998 /* compress changes to a single property into a single change */
999 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1003 /* XXX: it would be nice to compress ALL changes to a property,
1004 not just changes in a row without other props between. */
1006 a
= ce
.xproperty
.atom
;
1007 b
= e
->xproperty
.atom
;
1011 if ((a
== prop_atoms
.net_wm_name
||
1012 a
== prop_atoms
.wm_name
||
1013 a
== prop_atoms
.net_wm_icon_name
||
1014 a
== prop_atoms
.wm_icon_name
)
1016 (b
== prop_atoms
.net_wm_name
||
1017 b
== prop_atoms
.wm_name
||
1018 b
== prop_atoms
.net_wm_icon_name
||
1019 b
== prop_atoms
.wm_icon_name
)) {
1022 if ((a
== prop_atoms
.net_wm_icon
||
1023 a
== prop_atoms
.kwm_win_icon
)
1025 (b
== prop_atoms
.net_wm_icon
||
1026 b
== prop_atoms
.kwm_win_icon
))
1029 XPutBackEvent(ob_display
, &ce
);
1033 msgtype
= e
->xproperty
.atom
;
1034 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1035 client_update_normal_hints(client
);
1036 /* normal hints can make a window non-resizable */
1037 client_setup_decor_and_functions(client
);
1038 } else if (msgtype
== XA_WM_HINTS
) {
1039 client_update_wmhints(client
);
1040 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1041 client_update_transient_for(client
);
1042 client_get_type(client
);
1043 /* type may have changed, so update the layer */
1044 client_calc_layer(client
);
1045 client_setup_decor_and_functions(client
);
1046 } else if (msgtype
== prop_atoms
.net_wm_name
||
1047 msgtype
== prop_atoms
.wm_name
||
1048 msgtype
== prop_atoms
.net_wm_icon_name
||
1049 msgtype
== prop_atoms
.wm_icon_name
) {
1050 client_update_title(client
);
1051 } else if (msgtype
== prop_atoms
.wm_class
) {
1052 client_update_class(client
);
1053 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1054 client_update_protocols(client
);
1055 client_setup_decor_and_functions(client
);
1057 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1058 client_update_strut(client
);
1060 else if (msgtype
== prop_atoms
.net_wm_icon
||
1061 msgtype
== prop_atoms
.kwm_win_icon
) {
1062 client_update_icons(client
);
1067 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1068 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1069 frame_adjust_shape(client
->frame
);
1075 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1079 stacking_raise(DOCK_AS_WINDOW(s
));
1090 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1094 dock_app_drag(app
, &e
->xmotion
);
1097 if (app
->ignore_unmaps
) {
1098 app
->ignore_unmaps
--;
1101 dock_remove(app
, TRUE
);
1104 dock_remove(app
, FALSE
);
1106 case ReparentNotify
:
1107 dock_remove(app
, FALSE
);
1109 case ConfigureNotify
:
1110 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1115 ObMenuFrame
* find_active_menu()
1120 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1125 return it
? it
->data
: NULL
;
1128 static void event_handle_menu(XEvent
*ev
)
1131 ObMenuEntryFrame
*e
;
1135 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1136 ev
->xbutton
.y_root
)))
1137 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1138 else if (menu_can_hide
)
1139 menu_frame_hide_all();
1142 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1143 ev
->xmotion
.y_root
))) {
1144 menu_frame_move_on_screen(f
);
1145 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1146 ev
->xmotion
.y_root
)))
1147 menu_frame_select(f
, e
);
1151 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1152 menu_frame_hide_all();
1153 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1155 if ((f
= find_active_menu()))
1156 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1157 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1159 if ((f
= find_active_menu()) && f
->parent
)
1160 menu_frame_select(f
, NULL
);
1161 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1163 if ((f
= find_active_menu()) && f
->child
)
1164 menu_frame_select_next(f
->child
);
1165 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1167 if ((f
= find_active_menu()))
1168 menu_frame_select_previous(f
);
1169 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1171 if ((f
= find_active_menu()))
1172 menu_frame_select_next(f
);
1178 static gboolean
menu_hide_delay_func(gpointer data
)
1180 menu_can_hide
= TRUE
;
1181 return FALSE
; /* no repeat */
1184 static gboolean
focus_delay_func(gpointer data
)
1186 client_focus(focus_delay_client
);
1187 return FALSE
; /* no repeat */
1190 static void focus_delay_client_dest(gpointer data
)
1193 if (c
== focus_delay_client
) {
1194 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1195 focus_delay_client
);
1196 focus_delay_client
= NULL
;