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>
44 static void event_process(const XEvent
*e
, gpointer data
);
45 static void event_done(gpointer data
);
46 static void event_handle_root(XEvent
*e
);
47 static void event_handle_menu(XEvent
*e
);
48 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
49 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
50 static void event_handle_client(ObClient
*c
, XEvent
*e
);
51 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
53 static gboolean
focus_delay_func(gpointer data
);
54 static void focus_delay_client_dest(gpointer data
);
56 static gboolean
menu_hide_delay_func(gpointer data
);
58 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
59 (e)->xfocus.detail == NotifyAncestor || \
60 (e)->xfocus.detail > NotifyNonlinearVirtual)
61 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
62 (e)->xfocus.detail == NotifyInferior || \
63 (e)->xfocus.detail == NotifyAncestor || \
64 (e)->xfocus.detail > NotifyNonlinearVirtual)
66 Time event_lasttime
= 0;
68 /*! The value of the mask for the NumLock modifier */
69 unsigned int NumLockMask
;
70 /*! The value of the mask for the ScrollLock modifier */
71 unsigned int ScrollLockMask
;
72 /*! The key codes for the modifier keys */
73 static XModifierKeymap
*modmap
;
74 /*! Table of the constant modifier masks */
75 static const int mask_table
[] = {
76 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
77 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
79 static int mask_table_size
;
81 static ObClient
*focus_delay_client
;
83 static gboolean menu_can_hide
;
86 static void ice_handler(int fd
, gpointer conn
)
89 IceProcessMessages(conn
, NULL
, &b
);
92 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
93 IcePointer
*watch_data
)
98 fd
= IceConnectionNumber(conn
);
99 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
101 ob_main_loop_fd_remove(ob_main_loop
, fd
);
107 void event_startup(gboolean reconfig
)
109 if (reconfig
) return;
111 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
113 /* get lock masks that are defined by the display (not constant) */
114 modmap
= XGetModifierMapping(ob_display
);
116 if (modmap
&& modmap
->max_keypermod
> 0) {
118 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
119 /* get the values of the keyboard lock modifiers
120 Note: Caps lock is not retrieved the same way as Scroll and Num
121 lock since it doesn't need to be. */
122 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
123 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
126 for (cnt
= 0; cnt
< size
; ++cnt
) {
127 if (! modmap
->modifiermap
[cnt
]) continue;
129 if (num_lock
== modmap
->modifiermap
[cnt
])
130 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
131 if (scroll_lock
== modmap
->modifiermap
[cnt
])
132 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
136 ob_main_loop_x_add(ob_main_loop
, event_process
, event_done
, NULL
, NULL
);
139 IceAddConnectionWatch(ice_watch
, NULL
);
142 client_add_destructor(focus_delay_client_dest
);
145 void event_shutdown(gboolean reconfig
)
147 if (reconfig
) return;
150 IceRemoveConnectionWatch(ice_watch
, NULL
);
153 client_remove_destructor(focus_delay_client_dest
);
154 XFreeModifiermap(modmap
);
157 static Window
event_get_window(XEvent
*e
)
164 window
= RootWindow(ob_display
, ob_screen
);
167 window
= e
->xmap
.window
;
170 window
= e
->xunmap
.window
;
173 window
= e
->xdestroywindow
.window
;
175 case ConfigureRequest
:
176 window
= e
->xconfigurerequest
.window
;
178 case ConfigureNotify
:
179 window
= e
->xconfigure
.window
;
183 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
184 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
186 window
= ((XkbBellNotifyEvent
*)e
)->window
;
192 window
= e
->xany
.window
;
197 static void event_set_lasttime(XEvent
*e
)
201 /* grab the lasttime and hack up the state */
217 t
= e
->xproperty
.time
;
221 t
= e
->xcrossing
.time
;
224 /* if more event types are anticipated, get their timestamp
229 if (t
> event_lasttime
)
233 #define STRIP_MODS(s) \
234 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
235 /* kill off the Button1Mask etc, only want the modifiers */ \
236 s &= (ControlMask | ShiftMask | Mod1Mask | \
237 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
239 static void event_hack_mods(XEvent *e)
247 STRIP_MODS(e
->xbutton
.state
);
250 STRIP_MODS(e
->xkey
.state
);
253 STRIP_MODS(e
->xkey
.state
);
254 /* remove from the state the mask of the modifier being released, if
255 it is a modifier key being released (this is a little ugly..) */
256 kp
= modmap
->modifiermap
;
257 for (i
= 0; i
< mask_table_size
; ++i
) {
258 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
259 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
260 /* remove the mask for it */
261 e
->xkey
.state
&= ~mask_table
[i
];
262 /* cause the first loop to break; */
264 break; /* get outta here! */
271 STRIP_MODS(e
->xmotion
.state
);
272 /* compress events */
275 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
277 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
278 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
285 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
287 gboolean ignore
= FALSE
;
292 while (XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
295 if (!INVALID_FOCUSIN(&ce
)) {
296 XPutBackEvent(ob_display
, &ce
);
302 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
303 because of RevertToPointerRoot. If the focus ends up reverting to
304 pointer root on a workspace change, then the FocusIn event that we
305 want will be of type NotifyAncestor. This situation does not occur
306 for FocusOut, so it is safely ignored there.
308 if (ignore
|| INVALID_FOCUSIN(e
) || client
== NULL
) {
310 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
311 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
317 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
318 e
->xfocus
.mode
, e
->xfocus
.detail
);
322 while (XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
325 if (!INVALID_FOCUSOUT(&ce
)) {
326 XPutBackEvent(ob_display
, &ce
);
332 if (ignore
|| INVALID_FOCUSOUT(e
)) {
334 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
335 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
341 ob_debug("FocusOut on %lx mode %d detail %d\n",
342 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
347 /* NotifyUngrab occurs when a mouse button is released and the event is
348 caused, like when lowering a window */
349 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
350 pointer (Ancestor happens when the pointer is on a window border) */
351 if (e
->xcrossing
.mode
== NotifyGrab
||
352 e
->xcrossing
.detail
== NotifyInferior
||
353 (e
->xcrossing
.mode
== NotifyUngrab
&&
354 (e
->xcrossing
.detail
== NotifyAncestor
||
355 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
356 e
->xcrossing
.detail
== NotifyVirtual
))) {
358 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
359 (e
->type
== EnterNotify
? "Enter" : "Leave"),
361 e
->xcrossing
.detail
, client
?client
->window
:0);
366 ob_debug("%sNotify mode %d detail %d on %lx\n",
367 (e
->type
== EnterNotify
? "Enter" : "Leave"),
369 e
->xcrossing
.detail
, client
?client
->window
:0);
376 static void event_process(const XEvent
*ec
, gpointer data
)
379 ObGroup
*group
= NULL
;
380 ObClient
*client
= NULL
;
382 ObDockApp
*dockapp
= NULL
;
383 ObWindow
*obwin
= NULL
;
385 ObEventData
*ed
= data
;
387 /* make a copy we can mangle */
391 window
= event_get_window(e
);
392 if (!(e
->type
== PropertyNotify
&&
393 (group
= g_hash_table_lookup(group_map
, &window
))))
394 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
395 switch (obwin
->type
) {
397 dock
= WINDOW_AS_DOCK(obwin
);
400 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
403 client
= WINDOW_AS_CLIENT(obwin
);
406 case Window_Internal
:
407 /* not to be used for events */
408 g_assert_not_reached();
413 event_set_lasttime(e
);
415 if (event_ignore(e
, client
)) {
422 /* deal with it in the kernel */
424 event_handle_group(group
, e
);
426 event_handle_client(client
, e
);
428 event_handle_dockapp(dockapp
, e
);
430 event_handle_dock(dock
, e
);
431 else if (window
== RootWindow(ob_display
, ob_screen
))
432 event_handle_root(e
);
433 else if (e
->type
== MapRequest
)
434 client_manage(window
);
435 else if (e
->type
== ConfigureRequest
) {
436 /* unhandled configure requests must be used to configure the
440 xwc
.x
= e
->xconfigurerequest
.x
;
441 xwc
.y
= e
->xconfigurerequest
.y
;
442 xwc
.width
= e
->xconfigurerequest
.width
;
443 xwc
.height
= e
->xconfigurerequest
.height
;
444 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
445 xwc
.sibling
= e
->xconfigurerequest
.above
;
446 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
448 /* we are not to be held responsible if someone sends us an
450 xerror_set_ignore(TRUE
);
451 XConfigureWindow(ob_display
, window
,
452 e
->xconfigurerequest
.value_mask
, &xwc
);
453 xerror_set_ignore(FALSE
);
456 /* user input (action-bound) events */
457 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
458 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
459 e
->type
== KeyRelease
)
461 if (menu_frame_visible
)
462 event_handle_menu(e
);
464 if (!keyboard_process_interactive_grab(e
, &client
)) {
465 if (moveresize_in_progress
)
468 menu_can_hide
= FALSE
;
469 ob_main_loop_timeout_add(ob_main_loop
,
471 menu_hide_delay_func
,
474 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
475 e
->type
== MotionNotify
)
476 mouse_event(client
, e
);
477 else if (e
->type
== KeyPress
)
478 /* when in the middle of a focus cycling action, this
479 causes the window which appears to be focused to be
480 the one on which the actions will be executed */
481 keyboard_event((focus_cycle_target
?
483 (client
? client
: focus_client
)), e
);
489 static void event_done(gpointer data
)
492 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
495 static void event_handle_root(XEvent
*e
)
501 ob_debug("Another WM has requested to replace us. Exiting.\n");
506 if (e
->xclient
.format
!= 32) break;
508 msgtype
= e
->xclient
.message_type
;
509 if (msgtype
== prop_atoms
.net_current_desktop
) {
510 unsigned int d
= e
->xclient
.data
.l
[0];
511 if (d
< screen_num_desktops
)
512 screen_set_desktop(d
);
513 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
514 unsigned int d
= e
->xclient
.data
.l
[0];
516 screen_set_num_desktops(d
);
517 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
518 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
522 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
523 screen_update_desktop_names();
524 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
525 screen_update_layout();
527 case ConfigureNotify
:
529 XRRUpdateConfiguration(e
);
536 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
537 ob_debug("VIDMODE EVENT\n");
543 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
547 g_assert(e
->type
== PropertyNotify
);
549 for (it
= group
->members
; it
; it
= g_slist_next(it
))
550 event_handle_client(it
->data
, e
);
553 static void event_handle_client(ObClient
*client
, XEvent
*e
)
561 case VisibilityNotify
:
562 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
566 /* Wheel buttons don't draw because they are an instant click, so it
567 is a waste of resources to go drawing it. */
568 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
569 con
= frame_context(client
, e
->xbutton
.window
);
570 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
572 case OB_FRAME_CONTEXT_MAXIMIZE
:
573 client
->frame
->max_press
= (e
->type
== ButtonPress
);
574 framerender_frame(client
->frame
);
576 case OB_FRAME_CONTEXT_CLOSE
:
577 client
->frame
->close_press
= (e
->type
== ButtonPress
);
578 framerender_frame(client
->frame
);
580 case OB_FRAME_CONTEXT_ICONIFY
:
581 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
582 framerender_frame(client
->frame
);
584 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
585 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
586 framerender_frame(client
->frame
);
588 case OB_FRAME_CONTEXT_SHADE
:
589 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
590 framerender_frame(client
->frame
);
593 /* nothing changes with clicks for any other contexts */
600 ob_debug("Focus%s on client for %lx\n", (e
->type
==FocusIn
?"In":"Out"),
603 if (client
!= focus_client
)
604 focus_set_client(client
);
605 frame_adjust_focus(client
->frame
, e
->type
== FocusIn
);
609 ob_debug("Focus%s on client for %lx\n", (e
->type
==FocusIn
?"In":"Out"),
612 if (client
== focus_client
)
614 frame_adjust_focus(client
->frame
, e
->type
== FocusIn
);
617 con
= frame_context(client
, e
->xcrossing
.window
);
619 case OB_FRAME_CONTEXT_MAXIMIZE
:
620 client
->frame
->max_hover
= FALSE
;
621 frame_adjust_state(client
->frame
);
623 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
624 client
->frame
->desk_hover
= FALSE
;
625 frame_adjust_state(client
->frame
);
627 case OB_FRAME_CONTEXT_SHADE
:
628 client
->frame
->shade_hover
= FALSE
;
629 frame_adjust_state(client
->frame
);
631 case OB_FRAME_CONTEXT_ICONIFY
:
632 client
->frame
->iconify_hover
= FALSE
;
633 frame_adjust_state(client
->frame
);
635 case OB_FRAME_CONTEXT_CLOSE
:
636 client
->frame
->close_hover
= FALSE
;
637 frame_adjust_state(client
->frame
);
639 case OB_FRAME_CONTEXT_FRAME
:
640 /* XXX if doing a 'reconfigure' make sure you kill this timer,
641 maybe all timers.. */
642 if (config_focus_delay
&& client
== focus_delay_client
) {
643 ob_main_loop_timeout_remove_data(ob_main_loop
,
646 focus_delay_client
= NULL
;
653 con
= frame_context(client
, e
->xcrossing
.window
);
655 case OB_FRAME_CONTEXT_MAXIMIZE
:
656 client
->frame
->max_hover
= TRUE
;
657 frame_adjust_state(client
->frame
);
659 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
660 client
->frame
->desk_hover
= TRUE
;
661 frame_adjust_state(client
->frame
);
663 case OB_FRAME_CONTEXT_SHADE
:
664 client
->frame
->shade_hover
= TRUE
;
665 frame_adjust_state(client
->frame
);
667 case OB_FRAME_CONTEXT_ICONIFY
:
668 client
->frame
->iconify_hover
= TRUE
;
669 frame_adjust_state(client
->frame
);
671 case OB_FRAME_CONTEXT_CLOSE
:
672 client
->frame
->close_hover
= TRUE
;
673 frame_adjust_state(client
->frame
);
675 case OB_FRAME_CONTEXT_FRAME
:
676 if (client_normal(client
)) {
677 if (config_focus_follow
) {
679 ob_debug("EnterNotify on %lx, focusing window\n",
682 if (config_focus_delay
) {
683 ob_main_loop_timeout_add(ob_main_loop
,
687 focus_delay_client
= client
;
689 client_focus(client
);
697 case ConfigureRequest
:
699 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
700 ConfigureRequest
, &ce
)) {
702 /* XXX if this causes bad things.. we can compress config req's
703 with the same mask. */
704 e
->xconfigurerequest
.value_mask
|=
705 ce
.xconfigurerequest
.value_mask
;
706 if (ce
.xconfigurerequest
.value_mask
& CWX
)
707 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
708 if (ce
.xconfigurerequest
.value_mask
& CWY
)
709 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
710 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
711 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
712 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
713 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
714 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
715 e
->xconfigurerequest
.border_width
=
716 ce
.xconfigurerequest
.border_width
;
717 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
718 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
721 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
722 if (client
->iconic
|| client
->shaded
) return;
724 /* resize, then move, as specified in the EWMH section 7.7 */
725 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
731 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
732 client
->border_width
= e
->xconfigurerequest
.border_width
;
734 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
735 e
->xconfigurerequest
.x
: client
->area
.x
;
736 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
737 e
->xconfigurerequest
.y
: client
->area
.y
;
738 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
739 e
->xconfigurerequest
.width
: client
->area
.width
;
740 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
741 e
->xconfigurerequest
.height
: client
->area
.height
;
747 client
->frame
->size
.left
+ client
->frame
->size
.right
;
749 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
750 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
751 client_normal(client
));
752 if (e
->xconfigurerequest
.value_mask
& CWX
)
754 if (e
->xconfigurerequest
.value_mask
& CWY
)
758 switch (client
->gravity
) {
759 case NorthEastGravity
:
761 corner
= OB_CORNER_TOPRIGHT
;
763 case SouthWestGravity
:
765 corner
= OB_CORNER_BOTTOMLEFT
;
767 case SouthEastGravity
:
768 corner
= OB_CORNER_BOTTOMRIGHT
;
770 default: /* NorthWest, Static, etc */
771 corner
= OB_CORNER_TOPLEFT
;
774 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
778 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
779 switch (e
->xconfigurerequest
.detail
) {
782 stacking_lower(CLIENT_AS_WINDOW(client
));
788 stacking_raise(CLIENT_AS_WINDOW(client
));
794 if (client
->ignore_unmaps
) {
795 client
->ignore_unmaps
--;
798 client_unmanage(client
);
801 client_unmanage(client
);
804 /* this is when the client is first taken captive in the frame */
805 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
808 This event is quite rare and is usually handled in unmapHandler.
809 However, if the window is unmapped when the reparent event occurs,
810 the window manager never sees it because an unmap event is not sent
811 to an already unmapped window.
814 /* we don't want the reparent event, put it back on the stack for the
815 X server to deal with after we unmanage the window */
816 XPutBackEvent(ob_display
, e
);
818 client_unmanage(client
);
821 ob_debug("MapRequest for 0x%lx\n", client
->window
);
822 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
823 does, we don't want it! */
824 if (screen_showing_desktop
)
825 screen_show_desktop(FALSE
);
826 client_iconify(client
, FALSE
, TRUE
);
827 if (!client
->frame
->visible
)
828 /* if its not visible still, then don't mess with it */
831 client_shade(client
, FALSE
);
832 client_focus(client
);
833 stacking_raise(CLIENT_AS_WINDOW(client
));
836 /* validate cuz we query stuff off the client here */
837 if (!client_validate(client
)) break;
839 if (e
->xclient
.format
!= 32) return;
841 msgtype
= e
->xclient
.message_type
;
842 if (msgtype
== prop_atoms
.wm_change_state
) {
843 /* compress changes into a single change */
844 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
846 /* XXX: it would be nice to compress ALL messages of a
847 type, not just messages in a row without other
848 message types between. */
849 if (ce
.xclient
.message_type
!= msgtype
) {
850 XPutBackEvent(ob_display
, &ce
);
853 e
->xclient
= ce
.xclient
;
855 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
856 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
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 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
870 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
871 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
873 } else if (msgtype
== prop_atoms
.net_wm_state
) {
874 /* can't compress these */
875 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
876 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
877 e
->xclient
.data
.l
[0] == 1 ? "Add" :
878 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
879 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
881 client_set_state(client
, e
->xclient
.data
.l
[0],
882 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
883 } else if (msgtype
== prop_atoms
.net_close_window
) {
884 ob_debug("net_close_window for 0x%lx\n", client
->window
);
885 client_close(client
);
886 } else if (msgtype
== prop_atoms
.net_active_window
) {
887 ob_debug("net_active_window for 0x%lx\n", client
->window
);
888 client_activate(client
, FALSE
);
889 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
890 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
891 if ((Atom
)e
->xclient
.data
.l
[2] ==
892 prop_atoms
.net_wm_moveresize_size_topleft
||
893 (Atom
)e
->xclient
.data
.l
[2] ==
894 prop_atoms
.net_wm_moveresize_size_top
||
895 (Atom
)e
->xclient
.data
.l
[2] ==
896 prop_atoms
.net_wm_moveresize_size_topright
||
897 (Atom
)e
->xclient
.data
.l
[2] ==
898 prop_atoms
.net_wm_moveresize_size_right
||
899 (Atom
)e
->xclient
.data
.l
[2] ==
900 prop_atoms
.net_wm_moveresize_size_right
||
901 (Atom
)e
->xclient
.data
.l
[2] ==
902 prop_atoms
.net_wm_moveresize_size_bottomright
||
903 (Atom
)e
->xclient
.data
.l
[2] ==
904 prop_atoms
.net_wm_moveresize_size_bottom
||
905 (Atom
)e
->xclient
.data
.l
[2] ==
906 prop_atoms
.net_wm_moveresize_size_bottomleft
||
907 (Atom
)e
->xclient
.data
.l
[2] ==
908 prop_atoms
.net_wm_moveresize_size_left
||
909 (Atom
)e
->xclient
.data
.l
[2] ==
910 prop_atoms
.net_wm_moveresize_move
||
911 (Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_keyboard
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_move_keyboard
) {
916 moveresize_start(client
, e
->xclient
.data
.l
[0],
917 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
918 e
->xclient
.data
.l
[2]);
920 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
921 int oldg
= client
->gravity
;
922 int tmpg
, x
, y
, w
, h
;
924 if (e
->xclient
.data
.l
[0] & 0xff)
925 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
929 if (e
->xclient
.data
.l
[0] & 1 << 8)
930 x
= e
->xclient
.data
.l
[1];
933 if (e
->xclient
.data
.l
[0] & 1 << 9)
934 y
= e
->xclient
.data
.l
[2];
937 if (e
->xclient
.data
.l
[0] & 1 << 10)
938 w
= e
->xclient
.data
.l
[3];
940 w
= client
->area
.width
;
941 if (e
->xclient
.data
.l
[0] & 1 << 11)
942 h
= e
->xclient
.data
.l
[4];
944 h
= client
->area
.height
;
945 client
->gravity
= tmpg
;
951 client
->frame
->size
.left
+ client
->frame
->size
.right
;
953 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
954 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
955 client_normal(client
));
956 if (e
->xclient
.data
.l
[0] & 1 << 8)
958 if (e
->xclient
.data
.l
[0] & 1 << 9)
962 client_configure(client
, OB_CORNER_TOPLEFT
,
963 x
, y
, w
, h
, FALSE
, TRUE
);
965 client
->gravity
= oldg
;
969 /* validate cuz we query stuff off the client here */
970 if (!client_validate(client
)) break;
972 /* compress changes to a single property into a single change */
973 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
977 /* XXX: it would be nice to compress ALL changes to a property,
978 not just changes in a row without other props between. */
980 a
= ce
.xproperty
.atom
;
981 b
= e
->xproperty
.atom
;
985 if ((a
== prop_atoms
.net_wm_name
||
986 a
== prop_atoms
.wm_name
||
987 a
== prop_atoms
.net_wm_icon_name
||
988 a
== prop_atoms
.wm_icon_name
)
990 (b
== prop_atoms
.net_wm_name
||
991 b
== prop_atoms
.wm_name
||
992 b
== prop_atoms
.net_wm_icon_name
||
993 b
== prop_atoms
.wm_icon_name
)) {
996 if ((a
== prop_atoms
.net_wm_icon
||
997 a
== prop_atoms
.kwm_win_icon
)
999 (b
== prop_atoms
.net_wm_icon
||
1000 b
== prop_atoms
.kwm_win_icon
))
1003 XPutBackEvent(ob_display
, &ce
);
1007 msgtype
= e
->xproperty
.atom
;
1008 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1009 client_update_normal_hints(client
);
1010 /* normal hints can make a window non-resizable */
1011 client_setup_decor_and_functions(client
);
1012 } else if (msgtype
== XA_WM_HINTS
) {
1013 client_update_wmhints(client
);
1014 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1015 client_update_transient_for(client
);
1016 client_get_type(client
);
1017 /* type may have changed, so update the layer */
1018 client_calc_layer(client
);
1019 client_setup_decor_and_functions(client
);
1020 } else if (msgtype
== prop_atoms
.net_wm_name
||
1021 msgtype
== prop_atoms
.wm_name
||
1022 msgtype
== prop_atoms
.net_wm_icon_name
||
1023 msgtype
== prop_atoms
.wm_icon_name
) {
1024 client_update_title(client
);
1025 } else if (msgtype
== prop_atoms
.wm_class
) {
1026 client_update_class(client
);
1027 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1028 client_update_protocols(client
);
1029 client_setup_decor_and_functions(client
);
1031 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1032 client_update_strut(client
);
1034 else if (msgtype
== prop_atoms
.net_wm_icon
||
1035 msgtype
== prop_atoms
.kwm_win_icon
) {
1036 client_update_icons(client
);
1038 else if (msgtype
== prop_atoms
.sm_client_id
) {
1039 client_update_sm_client_id(client
);
1044 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1045 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1046 frame_adjust_shape(client
->frame
);
1052 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1056 stacking_raise(DOCK_AS_WINDOW(s
));
1067 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1071 dock_app_drag(app
, &e
->xmotion
);
1074 if (app
->ignore_unmaps
) {
1075 app
->ignore_unmaps
--;
1078 dock_remove(app
, TRUE
);
1081 dock_remove(app
, FALSE
);
1083 case ReparentNotify
:
1084 dock_remove(app
, FALSE
);
1086 case ConfigureNotify
:
1087 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1092 ObMenuFrame
* find_active_menu()
1097 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1102 return it
? it
->data
: NULL
;
1105 static void event_handle_menu(XEvent
*ev
)
1108 ObMenuEntryFrame
*e
;
1112 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1113 ev
->xbutton
.y_root
)))
1114 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1115 else if (menu_can_hide
)
1116 menu_frame_hide_all();
1119 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1120 ev
->xmotion
.y_root
))) {
1121 menu_frame_move_on_screen(f
);
1122 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1123 ev
->xmotion
.y_root
)))
1124 menu_frame_select(f
, e
);
1129 a
= find_active_menu();
1131 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1133 menu_frame_select(a
, NULL
);
1138 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1139 menu_frame_hide_all();
1140 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1142 if ((f
= find_active_menu()))
1143 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1144 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1146 if ((f
= find_active_menu()) && f
->parent
)
1147 menu_frame_select(f
, NULL
);
1148 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1150 if ((f
= find_active_menu()) && f
->child
)
1151 menu_frame_select_next(f
->child
);
1152 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1154 if ((f
= find_active_menu()))
1155 menu_frame_select_previous(f
);
1156 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1158 if ((f
= find_active_menu()))
1159 menu_frame_select_next(f
);
1165 static gboolean
menu_hide_delay_func(gpointer data
)
1167 menu_can_hide
= TRUE
;
1168 return FALSE
; /* no repeat */
1171 static gboolean
focus_delay_func(gpointer data
)
1173 client_focus(focus_delay_client
);
1174 return FALSE
; /* no repeat */
1177 static void focus_delay_client_dest(gpointer data
)
1180 if (c
== focus_delay_client
) {
1181 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1182 focus_delay_client
);
1183 focus_delay_client
= NULL
;