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 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
49 (e)->xfocus.detail == NotifyAncestor || \
50 (e)->xfocus.detail > NotifyNonlinearVirtual)
51 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
52 (e)->xfocus.detail == NotifyInferior || \
53 (e)->xfocus.detail == NotifyAncestor || \
54 (e)->xfocus.detail > NotifyNonlinearVirtual)
56 Time event_lasttime
= 0;
58 /*! The value of the mask for the NumLock modifier */
59 unsigned int NumLockMask
;
60 /*! The value of the mask for the ScrollLock modifier */
61 unsigned int ScrollLockMask
;
62 /*! The key codes for the modifier keys */
63 static XModifierKeymap
*modmap
;
64 /*! Table of the constant modifier masks */
65 static const int mask_table
[] = {
66 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
67 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
69 static int mask_table_size
;
71 static ObClient
*focus_delay_client
;
74 static void ice_handler(int fd
, gpointer conn
)
77 IceProcessMessages(conn
, NULL
, &b
);
80 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
81 IcePointer
*watch_data
)
86 fd
= IceConnectionNumber(conn
);
87 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
89 ob_main_loop_fd_remove(ob_main_loop
, fd
);
95 void event_startup(gboolean reconfig
)
99 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
101 /* get lock masks that are defined by the display (not constant) */
102 modmap
= XGetModifierMapping(ob_display
);
104 if (modmap
&& modmap
->max_keypermod
> 0) {
106 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
107 /* get the values of the keyboard lock modifiers
108 Note: Caps lock is not retrieved the same way as Scroll and Num
109 lock since it doesn't need to be. */
110 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
111 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
114 for (cnt
= 0; cnt
< size
; ++cnt
) {
115 if (! modmap
->modifiermap
[cnt
]) continue;
117 if (num_lock
== modmap
->modifiermap
[cnt
])
118 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
119 if (scroll_lock
== modmap
->modifiermap
[cnt
])
120 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
124 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
127 IceAddConnectionWatch(ice_watch
, NULL
);
130 client_add_destructor(focus_delay_client_dest
);
133 void event_shutdown(gboolean reconfig
)
135 if (reconfig
) return;
137 client_remove_destructor(focus_delay_client_dest
);
138 XFreeModifiermap(modmap
);
141 static Window
event_get_window(XEvent
*e
)
148 window
= RootWindow(ob_display
, ob_screen
);
151 window
= e
->xmap
.window
;
154 window
= e
->xunmap
.window
;
157 window
= e
->xdestroywindow
.window
;
159 case ConfigureRequest
:
160 window
= e
->xconfigurerequest
.window
;
162 case ConfigureNotify
:
163 window
= e
->xconfigure
.window
;
167 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
168 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
170 window
= ((XkbBellNotifyEvent
*)e
)->window
;
176 window
= e
->xany
.window
;
181 static void event_set_lasttime(XEvent
*e
)
185 /* grab the lasttime and hack up the state */
201 t
= e
->xproperty
.time
;
205 t
= e
->xcrossing
.time
;
208 /* if more event types are anticipated, get their timestamp
213 if (t
> event_lasttime
)
217 #define STRIP_MODS(s) \
218 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
219 /* kill off the Button1Mask etc, only want the modifiers */ \
220 s &= (ControlMask | ShiftMask | Mod1Mask | \
221 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
223 static void event_hack_mods(XEvent *e)
231 STRIP_MODS(e
->xbutton
.state
);
234 STRIP_MODS(e
->xkey
.state
);
237 STRIP_MODS(e
->xkey
.state
);
238 /* remove from the state the mask of the modifier being released, if
239 it is a modifier key being released (this is a little ugly..) */
240 kp
= modmap
->modifiermap
;
241 for (i
= 0; i
< mask_table_size
; ++i
) {
242 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
243 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
244 /* remove the mask for it */
245 e
->xkey
.state
&= ~mask_table
[i
];
246 /* cause the first loop to break; */
248 break; /* get outta here! */
255 STRIP_MODS(e
->xmotion
.state
);
256 /* compress events */
259 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
261 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
262 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
269 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
273 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
274 because of RevertToPointerRoot. If the focus ends up reverting to
275 pointer root on a workspace change, then the FocusIn event that we
276 want will be of type NotifyAncestor. This situation does not occur
277 for FocusOut, so it is safely ignored there.
279 if (INVALID_FOCUSIN(e
) ||
282 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
283 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
285 /* says a client was not found for the event (or a valid FocusIn
288 e
->xfocus
.window
= None
;
293 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
294 e
->xfocus
.mode
, e
->xfocus
.detail
);
298 if (INVALID_FOCUSOUT(e
)) {
300 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
301 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
307 ob_debug("FocusOut on %lx mode %d detail %d\n",
308 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
313 gboolean fallback
= TRUE
;
316 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
318 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
320 if (fe
.type
== FocusOut
) {
322 ob_debug("found pending FocusOut\n");
324 if (!INVALID_FOCUSOUT(&fe
)) {
325 /* if there is a VALID FocusOut still coming, don't
326 fallback focus yet, we'll deal with it then */
327 XPutBackEvent(ob_display
, &fe
);
333 ob_debug("found pending FocusIn\n");
335 /* is the focused window getting a FocusOut/In back to
338 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
339 !event_ignore(&fe
, client
)) {
341 if focus_client is not set, then we can't do
342 this. we need the FocusIn. This happens in the
343 case when the set_focus_client(NULL) in the
344 focus_fallback function fires and then
345 focus_fallback picks the currently focused
346 window (such as on a SendToDesktop-esque action.
350 ob_debug("focused window got an Out/In back to "
351 "itself IGNORED both\n");
355 event_process(&fe
, NULL
);
357 ob_debug("focused window got an Out/In back to "
358 "itself but focus_client was null "
359 "IGNORED just the Out\n");
365 /* once all the FocusOut's have been dealt with, if there
366 is a FocusIn still left and it is valid, then use it */
367 event_process(&fe
, NULL
);
368 /* secret magic way of event_process telling us that no
369 client was found for the FocusIn event. ^_^ */
370 if (fe
.xfocus
.window
!= None
) {
378 ob_debug("no valid FocusIn and no FocusOut events found, "
381 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
387 /* NotifyUngrab occurs when a mouse button is released and the event is
388 caused, like when lowering a window */
389 /* NotifyVirtual occurs when ungrabbing the pointer */
390 if (e
->xcrossing
.mode
== NotifyGrab
||
391 e
->xcrossing
.detail
== NotifyInferior
||
392 (e
->xcrossing
.mode
== NotifyUngrab
&&
393 e
->xcrossing
.detail
== NotifyVirtual
)) {
395 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
396 (e
->type
== EnterNotify
? "Enter" : "Leave"),
398 e
->xcrossing
.detail
, client
?client
->window
:0);
403 ob_debug("%sNotify mode %d detail %d on %lx\n",
404 (e
->type
== EnterNotify
? "Enter" : "Leave"),
406 e
->xcrossing
.detail
, client
?client
->window
:0);
413 static void event_process(const XEvent
*ec
, gpointer data
)
416 ObClient
*client
= NULL
;
418 ObDockApp
*dockapp
= NULL
;
419 ObWindow
*obwin
= NULL
;
422 /* make a copy we can mangle */
426 window
= event_get_window(e
);
427 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
428 switch (obwin
->type
) {
430 dock
= WINDOW_AS_DOCK(obwin
);
433 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
436 client
= WINDOW_AS_CLIENT(obwin
);
439 case Window_Internal
:
440 /* not to be used for events */
441 g_assert_not_reached();
446 event_set_lasttime(e
);
448 if (event_ignore(e
, client
))
451 /* deal with it in the kernel */
453 event_handle_client(client
, e
);
455 event_handle_dockapp(dockapp
, e
);
457 event_handle_dock(dock
, e
);
458 else if (window
== RootWindow(ob_display
, ob_screen
))
459 event_handle_root(e
);
460 else if (e
->type
== MapRequest
)
461 client_manage(window
);
462 else if (e
->type
== ConfigureRequest
) {
463 /* unhandled configure requests must be used to configure the
467 xwc
.x
= e
->xconfigurerequest
.x
;
468 xwc
.y
= e
->xconfigurerequest
.y
;
469 xwc
.width
= e
->xconfigurerequest
.width
;
470 xwc
.height
= e
->xconfigurerequest
.height
;
471 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
472 xwc
.sibling
= e
->xconfigurerequest
.above
;
473 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
475 /* we are not to be held responsible if someone sends us an
477 xerror_set_ignore(TRUE
);
478 XConfigureWindow(ob_display
, window
,
479 e
->xconfigurerequest
.value_mask
, &xwc
);
480 xerror_set_ignore(FALSE
);
483 /* user input (action-bound) events */
484 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
485 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
486 e
->type
== KeyRelease
)
488 if (menu_frame_visible
)
489 event_handle_menu(e
);
490 else if (moveresize_in_progress
)
493 ObFrameContext context
;
495 context
= frame_context(client
, e
->xany
.window
);
497 if (!keyboard_process_interactive_grab(e
, &client
, &context
)) {
498 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
499 e
->type
== MotionNotify
)
500 mouse_event(client
, context
, e
);
501 else if (e
->type
== KeyPress
)
502 keyboard_event(client
, e
);
508 static void event_handle_root(XEvent
*e
)
514 ob_debug("Another WM has requested to replace us. Exiting.\n");
519 if (e
->xclient
.format
!= 32) break;
521 msgtype
= e
->xclient
.message_type
;
522 if (msgtype
== prop_atoms
.net_current_desktop
) {
523 unsigned int d
= e
->xclient
.data
.l
[0];
524 if (d
< screen_num_desktops
)
525 screen_set_desktop(d
);
526 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
527 unsigned int d
= e
->xclient
.data
.l
[0];
529 screen_set_num_desktops(d
);
530 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
531 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
535 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
536 screen_update_desktop_names();
537 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
538 screen_update_layout();
540 case ConfigureNotify
:
542 XRRUpdateConfiguration(e
);
549 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
550 ob_debug("VIDMODE EVENT\n");
556 static void event_handle_client(ObClient
*client
, XEvent
*e
)
564 case VisibilityNotify
:
565 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
569 /* Wheel buttons don't draw because they are an instant click, so it
570 is a waste of resources to go drawing it. */
571 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
572 switch (frame_context(client
, e
->xbutton
.window
)) {
573 case OB_FRAME_CONTEXT_MAXIMIZE
:
574 client
->frame
->max_press
= (e
->type
== ButtonPress
);
575 framerender_frame(client
->frame
);
577 case OB_FRAME_CONTEXT_CLOSE
:
578 client
->frame
->close_press
= (e
->type
== ButtonPress
);
579 framerender_frame(client
->frame
);
581 case OB_FRAME_CONTEXT_ICONIFY
:
582 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
583 framerender_frame(client
->frame
);
585 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
586 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
587 framerender_frame(client
->frame
);
589 case OB_FRAME_CONTEXT_SHADE
:
590 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
591 framerender_frame(client
->frame
);
594 /* nothing changes with clicks for any other contexts */
601 ob_debug("FocusIn on client for %lx\n", client
->window
);
603 if (client
!= focus_client
) {
604 focus_set_client(client
);
605 frame_adjust_focus(client
->frame
, TRUE
);
610 ob_debug("FocusOut on client for %lx\n", client
->window
);
612 /* are we a fullscreen window or a transient of one? (checks layer)
613 if we are then we need to be iconified since we are losing focus
615 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
616 !client_search_focus_tree_full(client
))
617 /* iconify fullscreen windows when they and their transients
619 client_iconify(client
, TRUE
, TRUE
);
620 frame_adjust_focus(client
->frame
, FALSE
);
623 con
= frame_context(client
, e
->xcrossing
.window
);
625 case OB_FRAME_CONTEXT_MAXIMIZE
:
626 client
->frame
->max_hover
= FALSE
;
627 frame_adjust_state(client
->frame
);
629 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
630 client
->frame
->desk_hover
= FALSE
;
631 frame_adjust_state(client
->frame
);
633 case OB_FRAME_CONTEXT_SHADE
:
634 client
->frame
->shade_hover
= FALSE
;
635 frame_adjust_state(client
->frame
);
637 case OB_FRAME_CONTEXT_ICONIFY
:
638 client
->frame
->iconify_hover
= FALSE
;
639 frame_adjust_state(client
->frame
);
641 case OB_FRAME_CONTEXT_CLOSE
:
642 client
->frame
->close_hover
= FALSE
;
643 frame_adjust_state(client
->frame
);
645 case OB_FRAME_CONTEXT_FRAME
:
646 /* XXX if doing a 'reconfigure' make sure you kill this timer,
647 maybe all timers.. */
648 if (config_focus_delay
&& client
== focus_delay_client
) {
649 ob_main_loop_timeout_remove_data(ob_main_loop
,
652 focus_delay_client
= NULL
;
659 con
= frame_context(client
, e
->xcrossing
.window
);
661 case OB_FRAME_CONTEXT_MAXIMIZE
:
662 client
->frame
->max_hover
= TRUE
;
663 frame_adjust_state(client
->frame
);
665 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
666 client
->frame
->desk_hover
= TRUE
;
667 frame_adjust_state(client
->frame
);
669 case OB_FRAME_CONTEXT_SHADE
:
670 client
->frame
->shade_hover
= TRUE
;
671 frame_adjust_state(client
->frame
);
673 case OB_FRAME_CONTEXT_ICONIFY
:
674 client
->frame
->iconify_hover
= TRUE
;
675 frame_adjust_state(client
->frame
);
677 case OB_FRAME_CONTEXT_CLOSE
:
678 client
->frame
->close_hover
= TRUE
;
679 frame_adjust_state(client
->frame
);
681 case OB_FRAME_CONTEXT_FRAME
:
682 if (client_normal(client
)) {
683 if (ob_state() == OB_STATE_STARTING
) {
684 /* move it to the top of the focus order */
685 guint desktop
= client
->desktop
;
686 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
687 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
689 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
691 } else if (config_focus_follow
) {
693 ob_debug("EnterNotify on %lx, focusing window\n",
696 if (config_focus_delay
) {
697 ob_main_loop_timeout_add(ob_main_loop
,
701 focus_delay_client
= client
;
703 client_focus(client
);
711 case ConfigureRequest
:
713 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
714 ConfigureRequest
, &ce
)) {
716 /* XXX if this causes bad things.. we can compress config req's
717 with the same mask. */
718 e
->xconfigurerequest
.value_mask
|=
719 ce
.xconfigurerequest
.value_mask
;
720 if (ce
.xconfigurerequest
.value_mask
& CWX
)
721 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
722 if (ce
.xconfigurerequest
.value_mask
& CWY
)
723 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
724 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
725 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
726 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
727 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
728 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
729 e
->xconfigurerequest
.border_width
=
730 ce
.xconfigurerequest
.border_width
;
731 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
732 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
735 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
736 if (client
->iconic
|| client
->shaded
) return;
738 /* resize, then move, as specified in the EWMH section 7.7 */
739 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
745 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
746 client
->border_width
= e
->xconfigurerequest
.border_width
;
748 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
749 e
->xconfigurerequest
.x
: client
->area
.x
;
750 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
751 e
->xconfigurerequest
.y
: client
->area
.y
;
752 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
753 e
->xconfigurerequest
.width
: client
->area
.width
;
754 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
755 e
->xconfigurerequest
.height
: client
->area
.height
;
761 client
->frame
->size
.left
+ client
->frame
->size
.right
;
763 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
764 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
765 client_normal(client
));
766 if (e
->xconfigurerequest
.value_mask
& CWX
)
768 if (e
->xconfigurerequest
.value_mask
& CWY
)
772 switch (client
->gravity
) {
773 case NorthEastGravity
:
775 corner
= OB_CORNER_TOPRIGHT
;
777 case SouthWestGravity
:
779 corner
= OB_CORNER_BOTTOMLEFT
;
781 case SouthEastGravity
:
782 corner
= OB_CORNER_BOTTOMRIGHT
;
784 default: /* NorthWest, Static, etc */
785 corner
= OB_CORNER_TOPLEFT
;
788 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
792 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
793 switch (e
->xconfigurerequest
.detail
) {
796 stacking_lower(CLIENT_AS_WINDOW(client
));
802 stacking_raise(CLIENT_AS_WINDOW(client
));
808 if (client
->ignore_unmaps
) {
809 client
->ignore_unmaps
--;
812 client_unmanage(client
);
815 client_unmanage(client
);
818 /* this is when the client is first taken captive in the frame */
819 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
822 This event is quite rare and is usually handled in unmapHandler.
823 However, if the window is unmapped when the reparent event occurs,
824 the window manager never sees it because an unmap event is not sent
825 to an already unmapped window.
828 /* we don't want the reparent event, put it back on the stack for the
829 X server to deal with after we unmanage the window */
830 XPutBackEvent(ob_display
, e
);
832 client_unmanage(client
);
835 ob_debug("MapRequest for 0x%lx\n", client
->window
);
836 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
837 does, we don't want it! */
838 if (screen_showing_desktop
)
839 screen_show_desktop(FALSE
);
840 client_iconify(client
, FALSE
, TRUE
);
841 if (!client
->frame
->visible
)
842 /* if its not visible still, then don't mess with it */
845 client_shade(client
, FALSE
);
846 client_focus(client
);
847 stacking_raise(CLIENT_AS_WINDOW(client
));
850 /* validate cuz we query stuff off the client here */
851 if (!client_validate(client
)) break;
853 if (e
->xclient
.format
!= 32) return;
855 msgtype
= e
->xclient
.message_type
;
856 if (msgtype
== prop_atoms
.wm_change_state
) {
857 /* compress changes into a single change */
858 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
860 /* XXX: it would be nice to compress ALL messages of a
861 type, not just messages in a row without other
862 message types between. */
863 if (ce
.xclient
.message_type
!= msgtype
) {
864 XPutBackEvent(ob_display
, &ce
);
867 e
->xclient
= ce
.xclient
;
869 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
870 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
871 /* compress changes into a single change */
872 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
874 /* XXX: it would be nice to compress ALL messages of a
875 type, not just messages in a row without other
876 message types between. */
877 if (ce
.xclient
.message_type
!= msgtype
) {
878 XPutBackEvent(ob_display
, &ce
);
881 e
->xclient
= ce
.xclient
;
883 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
884 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
885 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
887 } else if (msgtype
== prop_atoms
.net_wm_state
) {
888 /* can't compress these */
889 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
890 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
891 e
->xclient
.data
.l
[0] == 1 ? "Add" :
892 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
893 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
895 client_set_state(client
, e
->xclient
.data
.l
[0],
896 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
897 } else if (msgtype
== prop_atoms
.net_close_window
) {
898 ob_debug("net_close_window for 0x%lx\n", client
->window
);
899 client_close(client
);
900 } else if (msgtype
== prop_atoms
.net_active_window
) {
901 ob_debug("net_active_window for 0x%lx\n", client
->window
);
902 client_activate(client
, FALSE
);
903 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
904 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
905 if ((Atom
)e
->xclient
.data
.l
[2] ==
906 prop_atoms
.net_wm_moveresize_size_topleft
||
907 (Atom
)e
->xclient
.data
.l
[2] ==
908 prop_atoms
.net_wm_moveresize_size_top
||
909 (Atom
)e
->xclient
.data
.l
[2] ==
910 prop_atoms
.net_wm_moveresize_size_topright
||
911 (Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_right
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_size_right
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_bottomright
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_bottom
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_bottomleft
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_size_left
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_move
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_keyboard
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_move_keyboard
) {
930 moveresize_start(client
, e
->xclient
.data
.l
[0],
931 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
932 e
->xclient
.data
.l
[2]);
934 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
935 int oldg
= client
->gravity
;
936 int tmpg
, x
, y
, w
, h
;
938 if (e
->xclient
.data
.l
[0] & 0xff)
939 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
943 if (e
->xclient
.data
.l
[0] & 1 << 8)
944 x
= e
->xclient
.data
.l
[1];
947 if (e
->xclient
.data
.l
[0] & 1 << 9)
948 y
= e
->xclient
.data
.l
[2];
951 if (e
->xclient
.data
.l
[0] & 1 << 10)
952 w
= e
->xclient
.data
.l
[3];
954 w
= client
->area
.width
;
955 if (e
->xclient
.data
.l
[0] & 1 << 11)
956 h
= e
->xclient
.data
.l
[4];
958 h
= client
->area
.height
;
959 client
->gravity
= tmpg
;
965 client
->frame
->size
.left
+ client
->frame
->size
.right
;
967 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
968 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
969 client_normal(client
));
970 if (e
->xclient
.data
.l
[0] & 1 << 8)
972 if (e
->xclient
.data
.l
[0] & 1 << 9)
976 client_configure(client
, OB_CORNER_TOPLEFT
,
977 x
, y
, w
, h
, FALSE
, TRUE
);
979 client
->gravity
= oldg
;
983 /* validate cuz we query stuff off the client here */
984 if (!client_validate(client
)) break;
986 /* compress changes to a single property into a single change */
987 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
991 /* XXX: it would be nice to compress ALL changes to a property,
992 not just changes in a row without other props between. */
994 a
= ce
.xproperty
.atom
;
995 b
= e
->xproperty
.atom
;
999 if ((a
== prop_atoms
.net_wm_name
||
1000 a
== prop_atoms
.wm_name
||
1001 a
== prop_atoms
.net_wm_icon_name
||
1002 a
== prop_atoms
.wm_icon_name
)
1004 (b
== prop_atoms
.net_wm_name
||
1005 b
== prop_atoms
.wm_name
||
1006 b
== prop_atoms
.net_wm_icon_name
||
1007 b
== prop_atoms
.wm_icon_name
)) {
1010 if ((a
== prop_atoms
.net_wm_icon
||
1011 a
== prop_atoms
.kwm_win_icon
)
1013 (b
== prop_atoms
.net_wm_icon
||
1014 b
== prop_atoms
.kwm_win_icon
))
1017 XPutBackEvent(ob_display
, &ce
);
1021 msgtype
= e
->xproperty
.atom
;
1022 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1023 client_update_normal_hints(client
);
1024 /* normal hints can make a window non-resizable */
1025 client_setup_decor_and_functions(client
);
1026 } else if (msgtype
== XA_WM_HINTS
) {
1027 client_update_wmhints(client
);
1028 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1029 client_update_transient_for(client
);
1030 client_get_type(client
);
1031 /* type may have changed, so update the layer */
1032 client_calc_layer(client
);
1033 client_setup_decor_and_functions(client
);
1034 } else if (msgtype
== prop_atoms
.net_wm_name
||
1035 msgtype
== prop_atoms
.wm_name
||
1036 msgtype
== prop_atoms
.net_wm_icon_name
||
1037 msgtype
== prop_atoms
.wm_icon_name
) {
1038 client_update_title(client
);
1039 } else if (msgtype
== prop_atoms
.wm_class
) {
1040 client_update_class(client
);
1041 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1042 client_update_protocols(client
);
1043 client_setup_decor_and_functions(client
);
1045 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1046 client_update_strut(client
);
1048 else if (msgtype
== prop_atoms
.net_wm_icon
||
1049 msgtype
== prop_atoms
.kwm_win_icon
) {
1050 client_update_icons(client
);
1055 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1056 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1057 frame_adjust_shape(client
->frame
);
1063 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1067 stacking_raise(DOCK_AS_WINDOW(s
));
1078 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1082 dock_app_drag(app
, &e
->xmotion
);
1085 if (app
->ignore_unmaps
) {
1086 app
->ignore_unmaps
--;
1089 dock_remove(app
, TRUE
);
1092 dock_remove(app
, FALSE
);
1094 case ReparentNotify
:
1095 dock_remove(app
, FALSE
);
1097 case ConfigureNotify
:
1098 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1103 ObMenuFrame
* find_active_menu()
1108 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1113 return it
? it
->data
: NULL
;
1116 static void event_handle_menu(XEvent
*ev
)
1119 ObMenuEntryFrame
*e
;
1123 if (!(f
= menu_frame_under(ev
->xbutton
.x_root
,
1124 ev
->xbutton
.y_root
)))
1125 menu_frame_hide_all();
1127 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1128 ev
->xbutton
.y_root
)))
1129 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1133 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1134 ev
->xmotion
.y_root
))) {
1135 menu_frame_move_on_screen(f
);
1136 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1137 ev
->xmotion
.y_root
)))
1138 menu_frame_select(f
, e
);
1142 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1143 menu_frame_hide_all();
1144 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1146 if ((f
= find_active_menu()))
1147 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1148 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1150 if ((f
= find_active_menu()) && f
->parent
)
1151 menu_frame_select(f
, NULL
);
1152 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1154 if ((f
= find_active_menu()) && f
->child
)
1155 menu_frame_select_next(f
->child
);
1156 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1158 if ((f
= find_active_menu()))
1159 menu_frame_select_previous(f
);
1160 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1162 if ((f
= find_active_menu()))
1163 menu_frame_select_next(f
);
1169 static gboolean
focus_delay_func(gpointer data
)
1171 client_focus(focus_delay_client
);
1172 return FALSE
; /* no repeat */
1175 static void focus_delay_client_dest(gpointer data
)
1178 if (c
== focus_delay_client
) {
1179 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1180 focus_delay_client
);
1181 focus_delay_client
= NULL
;