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>
28 # include <libsn/sn.h>
31 #ifdef HAVE_SYS_SELECT_H
32 # include <sys/select.h>
39 #include <X11/ICE/ICElib.h>
42 static void event_process(const XEvent
*e
, gpointer data
);
43 static void event_handle_root(XEvent
*e
);
44 static void event_handle_menu(XEvent
*e
);
45 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
46 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
47 static void event_handle_client(ObClient
*c
, XEvent
*e
);
49 static gboolean
focus_delay_func(gpointer data
);
50 static void focus_delay_client_dest(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
;
76 static void ice_handler(int fd
, gpointer conn
)
79 IceProcessMessages(conn
, NULL
, &b
);
82 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
83 IcePointer
*watch_data
)
88 fd
= IceConnectionNumber(conn
);
89 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
91 ob_main_loop_fd_remove(ob_main_loop
, fd
);
98 static void sn_handler(const XEvent
*e
, gpointer display
)
102 sn_display_process_event(display
, &ec
);
109 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
111 /* get lock masks that are defined by the display (not constant) */
112 modmap
= XGetModifierMapping(ob_display
);
114 if (modmap
&& modmap
->max_keypermod
> 0) {
116 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
117 /* get the values of the keyboard lock modifiers
118 Note: Caps lock is not retrieved the same way as Scroll and Num
119 lock since it doesn't need to be. */
120 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
121 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
124 for (cnt
= 0; cnt
< size
; ++cnt
) {
125 if (! modmap
->modifiermap
[cnt
]) continue;
127 if (num_lock
== modmap
->modifiermap
[cnt
])
128 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
129 if (scroll_lock
== modmap
->modifiermap
[cnt
])
130 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
134 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
137 IceAddConnectionWatch(ice_watch
, NULL
);
141 ob_main_loop_x_add(ob_main_loop
, sn_handler
, ob_sn_display
, NULL
);
144 client_add_destructor(focus_delay_client_dest
);
147 void event_shutdown()
149 client_remove_destructor(focus_delay_client_dest
);
150 XFreeModifiermap(modmap
);
153 static Window
event_get_window(XEvent
*e
)
160 window
= RootWindow(ob_display
, ob_screen
);
163 window
= e
->xmap
.window
;
166 window
= e
->xunmap
.window
;
169 window
= e
->xdestroywindow
.window
;
171 case ConfigureRequest
:
172 window
= e
->xconfigurerequest
.window
;
174 case ConfigureNotify
:
175 window
= e
->xconfigure
.window
;
179 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
180 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
182 window
= ((XkbBellNotifyEvent
*)e
)->window
;
188 window
= e
->xany
.window
;
193 static void event_set_lasttime(XEvent
*e
)
197 /* grab the lasttime and hack up the state */
213 t
= e
->xproperty
.time
;
217 t
= e
->xcrossing
.time
;
220 /* if more event types are anticipated, get their timestamp
225 if (t
> event_lasttime
)
229 #define STRIP_MODS(s) \
230 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
231 /* kill off the Button1Mask etc, only want the modifiers */ \
232 s &= (ControlMask | ShiftMask | Mod1Mask | \
233 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
235 static void event_hack_mods(XEvent *e)
243 STRIP_MODS(e
->xbutton
.state
);
246 STRIP_MODS(e
->xkey
.state
);
249 STRIP_MODS(e
->xkey
.state
);
250 /* remove from the state the mask of the modifier being released, if
251 it is a modifier key being released (this is a little ugly..) */
252 kp
= modmap
->modifiermap
;
253 for (i
= 0; i
< mask_table_size
; ++i
) {
254 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
255 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
256 /* remove the mask for it */
257 e
->xkey
.state
&= ~mask_table
[i
];
258 /* cause the first loop to break; */
260 break; /* get outta here! */
267 STRIP_MODS(e
->xmotion
.state
);
268 /* compress events */
271 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
273 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
274 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
281 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
285 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
286 because of RevertToPointerRoot. If the focus ends up reverting to
287 pointer root on a workspace change, then the FocusIn event that we
288 want will be of type NotifyAncestor. This situation does not occur
289 for FocusOut, so it is safely ignored there.
291 if (INVALID_FOCUSIN(e
) ||
294 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
295 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
297 /* says a client was not found for the event (or a valid FocusIn
300 e
->xfocus
.window
= None
;
305 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
306 e
->xfocus
.mode
, e
->xfocus
.detail
);
310 if (INVALID_FOCUSOUT(e
)) {
312 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
313 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
319 ob_debug("FocusOut on %lx mode %d detail %d\n",
320 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
325 gboolean fallback
= TRUE
;
328 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
330 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
332 if (fe
.type
== FocusOut
) {
334 ob_debug("found pending FocusOut");
336 if (!INVALID_FOCUSOUT(&fe
)) {
337 /* if there is a VALID FocusOut still coming, don't
338 fallback focus yet, we'll deal with it then */
339 XPutBackEvent(ob_display
, &fe
);
345 ob_debug("found pending FocusIn");
347 /* is the focused window getting a FocusOut/In back to
350 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
351 !event_ignore(&fe
, client
)) {
353 if focus_client is not set, then we can't do
354 this. we need the FocusIn. This happens in the
355 case when the set_focus_client(NULL) in the
356 focus_fallback function fires and then
357 focus_fallback picks the currently focused
358 window (such as on a SendToDesktop-esque action.
362 ob_debug("focused window got an Out/In back to "
363 "itself IGNORED both");
367 event_process(&fe
, NULL
);
369 ob_debug("focused window got an Out/In back to "
370 "itself but focus_client was null "
371 "IGNORED just the Out");
377 /* once all the FocusOut's have been dealt with, if there
378 is a FocusIn still left and it is valid, then use it */
379 event_process(&fe
, NULL
);
380 /* secret magic way of event_process telling us that no
381 client was found for the FocusIn event. ^_^ */
382 if (fe
.xfocus
.window
!= None
) {
390 ob_debug("no valid FocusIn and no FocusOut events found, "
393 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
399 /* NotifyUngrab occurs when a mouse button is released and the event is
400 caused, like when lowering a window */
401 /* NotifyVirtual occurs when ungrabbing the pointer */
402 if (e
->xcrossing
.mode
== NotifyGrab
||
403 e
->xcrossing
.detail
== NotifyInferior
||
404 (e
->xcrossing
.mode
== NotifyUngrab
&&
405 e
->xcrossing
.detail
== NotifyVirtual
)) {
407 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
408 (e
->type
== EnterNotify
? "Enter" : "Leave"),
410 e
->xcrossing
.detail
, client
?client
->window
:0);
415 ob_debug("%sNotify mode %d detail %d on %lx",
416 (e
->type
== EnterNotify
? "Enter" : "Leave"),
418 e
->xcrossing
.detail
, client
?client
->window
:0);
425 static void event_process(const XEvent
*ec
, gpointer data
)
428 ObClient
*client
= NULL
;
430 ObDockApp
*dockapp
= NULL
;
431 ObWindow
*obwin
= NULL
;
434 /* make a copy we can mangle */
438 window
= event_get_window(e
);
439 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
440 switch (obwin
->type
) {
442 dock
= WINDOW_AS_DOCK(obwin
);
445 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
448 client
= WINDOW_AS_CLIENT(obwin
);
451 case Window_Internal
:
452 /* not to be used for events */
453 g_assert_not_reached();
458 event_set_lasttime(e
);
460 if (event_ignore(e
, client
))
463 /* deal with it in the kernel */
465 event_handle_client(client
, e
);
467 event_handle_dockapp(dockapp
, e
);
469 event_handle_dock(dock
, e
);
470 else if (window
== RootWindow(ob_display
, ob_screen
))
471 event_handle_root(e
);
472 else if (e
->type
== MapRequest
)
473 client_manage(window
);
474 else if (e
->type
== ConfigureRequest
) {
475 /* unhandled configure requests must be used to configure the
479 xwc
.x
= e
->xconfigurerequest
.x
;
480 xwc
.y
= e
->xconfigurerequest
.y
;
481 xwc
.width
= e
->xconfigurerequest
.width
;
482 xwc
.height
= e
->xconfigurerequest
.height
;
483 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
484 xwc
.sibling
= e
->xconfigurerequest
.above
;
485 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
487 /* we are not to be held responsible if someone sends us an
489 xerror_set_ignore(TRUE
);
490 XConfigureWindow(ob_display
, window
,
491 e
->xconfigurerequest
.value_mask
, &xwc
);
492 xerror_set_ignore(FALSE
);
495 /* user input (action-bound) events */
496 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
497 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
498 e
->type
== KeyRelease
)
500 if (menu_frame_visible
)
501 event_handle_menu(e
);
502 else if (moveresize_in_progress
)
505 ObFrameContext context
;
507 context
= frame_context(client
, e
->xany
.window
);
509 if (!keyboard_process_interactive_grab(e
, &client
, &context
)) {
510 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
511 e
->type
== MotionNotify
)
512 mouse_event(client
, context
, e
);
513 else if (e
->type
== KeyPress
)
514 keyboard_event(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
)
661 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
667 con
= frame_context(client
, e
->xcrossing
.window
);
669 case OB_FRAME_CONTEXT_MAXIMIZE
:
670 client
->frame
->max_hover
= TRUE
;
671 frame_adjust_state(client
->frame
);
673 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
674 client
->frame
->desk_hover
= TRUE
;
675 frame_adjust_state(client
->frame
);
677 case OB_FRAME_CONTEXT_SHADE
:
678 client
->frame
->shade_hover
= TRUE
;
679 frame_adjust_state(client
->frame
);
681 case OB_FRAME_CONTEXT_ICONIFY
:
682 client
->frame
->iconify_hover
= TRUE
;
683 frame_adjust_state(client
->frame
);
685 case OB_FRAME_CONTEXT_CLOSE
:
686 client
->frame
->close_hover
= TRUE
;
687 frame_adjust_state(client
->frame
);
689 case OB_FRAME_CONTEXT_FRAME
:
690 if (client_normal(client
)) {
691 if (ob_state() == OB_STATE_STARTING
) {
692 /* move it to the top of the focus order */
693 guint desktop
= client
->desktop
;
694 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
695 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
697 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
699 } else if (config_focus_follow
) {
701 ob_debug("EnterNotify on %lx, focusing window\n",
704 if (config_focus_delay
) {
705 ob_main_loop_timeout_add(ob_main_loop
,
711 client_focus(client
);
719 case ConfigureRequest
:
721 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
722 ConfigureRequest
, &ce
)) {
724 /* XXX if this causes bad things.. we can compress config req's
725 with the same mask. */
726 e
->xconfigurerequest
.value_mask
|=
727 ce
.xconfigurerequest
.value_mask
;
728 if (ce
.xconfigurerequest
.value_mask
& CWX
)
729 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
730 if (ce
.xconfigurerequest
.value_mask
& CWY
)
731 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
732 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
733 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
734 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
735 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
736 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
737 e
->xconfigurerequest
.border_width
=
738 ce
.xconfigurerequest
.border_width
;
739 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
740 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
743 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
744 if (client
->iconic
|| client
->shaded
) return;
746 /* resize, then move, as specified in the EWMH section 7.7 */
747 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
753 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
754 client
->border_width
= e
->xconfigurerequest
.border_width
;
756 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
757 e
->xconfigurerequest
.x
: client
->area
.x
;
758 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
759 e
->xconfigurerequest
.y
: client
->area
.y
;
760 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
761 e
->xconfigurerequest
.width
: client
->area
.width
;
762 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
763 e
->xconfigurerequest
.height
: client
->area
.height
;
769 client
->frame
->size
.left
+ client
->frame
->size
.right
;
771 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
772 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
773 client_normal(client
));
774 if (e
->xconfigurerequest
.value_mask
& CWX
)
776 if (e
->xconfigurerequest
.value_mask
& CWY
)
780 switch (client
->gravity
) {
781 case NorthEastGravity
:
783 corner
= OB_CORNER_TOPRIGHT
;
785 case SouthWestGravity
:
787 corner
= OB_CORNER_BOTTOMLEFT
;
789 case SouthEastGravity
:
790 corner
= OB_CORNER_BOTTOMRIGHT
;
792 default: /* NorthWest, Static, etc */
793 corner
= OB_CORNER_TOPLEFT
;
796 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
800 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
801 switch (e
->xconfigurerequest
.detail
) {
804 stacking_lower(CLIENT_AS_WINDOW(client
));
810 stacking_raise(CLIENT_AS_WINDOW(client
));
816 if (client
->ignore_unmaps
) {
817 client
->ignore_unmaps
--;
820 client_unmanage(client
);
823 client_unmanage(client
);
826 /* this is when the client is first taken captive in the frame */
827 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
830 This event is quite rare and is usually handled in unmapHandler.
831 However, if the window is unmapped when the reparent event occurs,
832 the window manager never sees it because an unmap event is not sent
833 to an already unmapped window.
836 /* we don't want the reparent event, put it back on the stack for the
837 X server to deal with after we unmanage the window */
838 XPutBackEvent(ob_display
, e
);
840 client_unmanage(client
);
843 ob_debug("MapRequest for 0x%lx\n", client
->window
);
844 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
845 does, we don't want it! */
846 if (screen_showing_desktop
)
847 screen_show_desktop(FALSE
);
848 client_iconify(client
, FALSE
, TRUE
);
849 if (!client
->frame
->visible
)
850 /* if its not visible still, then don't mess with it */
853 client_shade(client
, FALSE
);
854 client_focus(client
);
855 stacking_raise(CLIENT_AS_WINDOW(client
));
858 /* validate cuz we query stuff off the client here */
859 if (!client_validate(client
)) break;
861 if (e
->xclient
.format
!= 32) return;
863 msgtype
= e
->xclient
.message_type
;
864 if (msgtype
== prop_atoms
.wm_change_state
) {
865 /* compress changes into a single change */
866 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
868 /* XXX: it would be nice to compress ALL messages of a
869 type, not just messages in a row without other
870 message types between. */
871 if (ce
.xclient
.message_type
!= msgtype
) {
872 XPutBackEvent(ob_display
, &ce
);
875 e
->xclient
= ce
.xclient
;
877 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
878 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
879 /* compress changes into a single change */
880 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
882 /* XXX: it would be nice to compress ALL messages of a
883 type, not just messages in a row without other
884 message types between. */
885 if (ce
.xclient
.message_type
!= msgtype
) {
886 XPutBackEvent(ob_display
, &ce
);
889 e
->xclient
= ce
.xclient
;
891 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
892 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
893 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
895 } else if (msgtype
== prop_atoms
.net_wm_state
) {
896 /* can't compress these */
897 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
898 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
899 e
->xclient
.data
.l
[0] == 1 ? "Add" :
900 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
901 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
903 client_set_state(client
, e
->xclient
.data
.l
[0],
904 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
905 } else if (msgtype
== prop_atoms
.net_close_window
) {
906 ob_debug("net_close_window for 0x%lx\n", client
->window
);
907 client_close(client
);
908 } else if (msgtype
== prop_atoms
.net_active_window
) {
909 ob_debug("net_active_window for 0x%lx\n", client
->window
);
910 client_activate(client
, FALSE
);
911 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
912 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
913 if ((Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_size_topleft
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_top
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_topright
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_right
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_size_right
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_bottomright
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_bottom
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_size_bottomleft
||
929 (Atom
)e
->xclient
.data
.l
[2] ==
930 prop_atoms
.net_wm_moveresize_size_left
||
931 (Atom
)e
->xclient
.data
.l
[2] ==
932 prop_atoms
.net_wm_moveresize_move
||
933 (Atom
)e
->xclient
.data
.l
[2] ==
934 prop_atoms
.net_wm_moveresize_size_keyboard
||
935 (Atom
)e
->xclient
.data
.l
[2] ==
936 prop_atoms
.net_wm_moveresize_move_keyboard
) {
938 moveresize_start(client
, e
->xclient
.data
.l
[0],
939 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
940 e
->xclient
.data
.l
[2]);
942 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
943 int oldg
= client
->gravity
;
944 int tmpg
, x
, y
, w
, h
;
946 if (e
->xclient
.data
.l
[0] & 0xff)
947 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
951 if (e
->xclient
.data
.l
[0] & 1 << 8)
952 x
= e
->xclient
.data
.l
[1];
955 if (e
->xclient
.data
.l
[0] & 1 << 9)
956 y
= e
->xclient
.data
.l
[2];
959 if (e
->xclient
.data
.l
[0] & 1 << 10)
960 w
= e
->xclient
.data
.l
[3];
962 w
= client
->area
.width
;
963 if (e
->xclient
.data
.l
[0] & 1 << 11)
964 h
= e
->xclient
.data
.l
[4];
966 h
= client
->area
.height
;
967 client
->gravity
= tmpg
;
973 client
->frame
->size
.left
+ client
->frame
->size
.right
;
975 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
976 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
977 client_normal(client
));
978 if (e
->xclient
.data
.l
[0] & 1 << 8)
980 if (e
->xclient
.data
.l
[0] & 1 << 9)
984 client_configure(client
, OB_CORNER_TOPLEFT
,
985 x
, y
, w
, h
, FALSE
, TRUE
);
987 client
->gravity
= oldg
;
991 /* validate cuz we query stuff off the client here */
992 if (!client_validate(client
)) break;
994 /* compress changes to a single property into a single change */
995 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
999 /* XXX: it would be nice to compress ALL changes to a property,
1000 not just changes in a row without other props between. */
1002 a
= ce
.xproperty
.atom
;
1003 b
= e
->xproperty
.atom
;
1007 if ((a
== prop_atoms
.net_wm_name
||
1008 a
== prop_atoms
.wm_name
||
1009 a
== prop_atoms
.net_wm_icon_name
||
1010 a
== prop_atoms
.wm_icon_name
)
1012 (b
== prop_atoms
.net_wm_name
||
1013 b
== prop_atoms
.wm_name
||
1014 b
== prop_atoms
.net_wm_icon_name
||
1015 b
== prop_atoms
.wm_icon_name
)) {
1018 if ((a
== prop_atoms
.net_wm_icon
||
1019 a
== prop_atoms
.kwm_win_icon
)
1021 (b
== prop_atoms
.net_wm_icon
||
1022 b
== prop_atoms
.kwm_win_icon
))
1025 XPutBackEvent(ob_display
, &ce
);
1029 msgtype
= e
->xproperty
.atom
;
1030 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1031 client_update_normal_hints(client
);
1032 /* normal hints can make a window non-resizable */
1033 client_setup_decor_and_functions(client
);
1034 } else if (msgtype
== XA_WM_HINTS
) {
1035 client_update_wmhints(client
);
1036 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1037 client_update_transient_for(client
);
1038 client_get_type(client
);
1039 /* type may have changed, so update the layer */
1040 client_calc_layer(client
);
1041 client_setup_decor_and_functions(client
);
1042 } else if (msgtype
== prop_atoms
.net_wm_name
||
1043 msgtype
== prop_atoms
.wm_name
||
1044 msgtype
== prop_atoms
.net_wm_icon_name
||
1045 msgtype
== prop_atoms
.wm_icon_name
) {
1046 client_update_title(client
);
1047 } else if (msgtype
== prop_atoms
.wm_class
) {
1048 client_update_class(client
);
1049 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1050 client_update_protocols(client
);
1051 client_setup_decor_and_functions(client
);
1053 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1054 client_update_strut(client
);
1056 else if (msgtype
== prop_atoms
.net_wm_icon
||
1057 msgtype
== prop_atoms
.kwm_win_icon
) {
1058 client_update_icons(client
);
1063 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1064 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1065 frame_adjust_shape(client
->frame
);
1071 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1075 stacking_raise(DOCK_AS_WINDOW(s
));
1086 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1090 dock_app_drag(app
, &e
->xmotion
);
1093 if (app
->ignore_unmaps
) {
1094 app
->ignore_unmaps
--;
1097 dock_remove(app
, TRUE
);
1100 dock_remove(app
, FALSE
);
1102 case ReparentNotify
:
1103 dock_remove(app
, FALSE
);
1105 case ConfigureNotify
:
1106 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1111 ObMenuFrame
* find_active_menu()
1116 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1121 return it
? it
->data
: NULL
;
1124 static void event_handle_menu(XEvent
*ev
)
1127 ObMenuEntryFrame
*e
;
1131 if (!(f
= menu_frame_under(ev
->xbutton
.x_root
,
1132 ev
->xbutton
.y_root
)))
1133 menu_frame_hide_all();
1135 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1136 ev
->xbutton
.y_root
)))
1137 menu_entry_frame_execute(e
,
1138 !(ev
->xbutton
.state
& ControlMask
));
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
,
1157 !(ev
->xkey
.state
& ControlMask
));
1158 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1160 if ((f
= find_active_menu()) && f
->parent
)
1161 menu_frame_select(f
, NULL
);
1162 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1164 if ((f
= find_active_menu()) && f
->child
)
1165 menu_frame_select_next(f
->child
);
1166 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1168 if ((f
= find_active_menu()))
1169 menu_frame_select_previous(f
);
1170 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1172 if ((f
= find_active_menu()))
1173 menu_frame_select_next(f
);
1179 static gboolean
focus_delay_func(gpointer data
)
1183 return FALSE
; /* no repeat */
1186 static void focus_delay_client_dest(gpointer data
)
1188 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);