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_handle_root(XEvent
*e
);
46 static void event_handle_menu(XEvent
*e
);
47 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
48 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
49 static void event_handle_client(ObClient
*c
, XEvent
*e
);
50 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
52 static gboolean
focus_delay_func(gpointer data
);
53 static void focus_delay_client_dest(gpointer data
);
55 static gboolean
menu_hide_delay_func(gpointer data
);
57 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
58 (e)->xfocus.detail == NotifyAncestor || \
59 (e)->xfocus.detail > NotifyNonlinearVirtual)
60 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
61 (e)->xfocus.detail == NotifyInferior || \
62 (e)->xfocus.detail == NotifyAncestor || \
63 (e)->xfocus.detail > NotifyNonlinearVirtual)
65 Time event_lasttime
= 0;
67 /*! The value of the mask for the NumLock modifier */
68 unsigned int NumLockMask
;
69 /*! The value of the mask for the ScrollLock modifier */
70 unsigned int ScrollLockMask
;
71 /*! The key codes for the modifier keys */
72 static XModifierKeymap
*modmap
;
73 /*! Table of the constant modifier masks */
74 static const int mask_table
[] = {
75 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
76 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
78 static int mask_table_size
;
80 static ObClient
*focus_delay_client
;
82 static gboolean menu_can_hide
;
85 static void ice_handler(int fd
, gpointer conn
)
88 IceProcessMessages(conn
, NULL
, &b
);
91 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
92 IcePointer
*watch_data
)
97 fd
= IceConnectionNumber(conn
);
98 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
100 ob_main_loop_fd_remove(ob_main_loop
, fd
);
106 void event_startup(gboolean reconfig
)
108 if (reconfig
) return;
110 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
112 /* get lock masks that are defined by the display (not constant) */
113 modmap
= XGetModifierMapping(ob_display
);
115 if (modmap
&& modmap
->max_keypermod
> 0) {
117 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
118 /* get the values of the keyboard lock modifiers
119 Note: Caps lock is not retrieved the same way as Scroll and Num
120 lock since it doesn't need to be. */
121 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
122 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
125 for (cnt
= 0; cnt
< size
; ++cnt
) {
126 if (! modmap
->modifiermap
[cnt
]) continue;
128 if (num_lock
== modmap
->modifiermap
[cnt
])
129 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
130 if (scroll_lock
== modmap
->modifiermap
[cnt
])
131 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
135 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
138 IceAddConnectionWatch(ice_watch
, NULL
);
141 client_add_destructor(focus_delay_client_dest
);
144 void event_shutdown(gboolean reconfig
)
146 if (reconfig
) return;
149 IceRemoveConnectionWatch(ice_watch
, NULL
);
152 client_remove_destructor(focus_delay_client_dest
);
153 XFreeModifiermap(modmap
);
156 static Window
event_get_window(XEvent
*e
)
163 window
= RootWindow(ob_display
, ob_screen
);
166 window
= e
->xmap
.window
;
169 window
= e
->xunmap
.window
;
172 window
= e
->xdestroywindow
.window
;
174 case ConfigureRequest
:
175 window
= e
->xconfigurerequest
.window
;
177 case ConfigureNotify
:
178 window
= e
->xconfigure
.window
;
182 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
183 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
185 window
= ((XkbBellNotifyEvent
*)e
)->window
;
191 window
= e
->xany
.window
;
196 static void event_set_lasttime(XEvent
*e
)
200 /* grab the lasttime and hack up the state */
216 t
= e
->xproperty
.time
;
220 t
= e
->xcrossing
.time
;
223 /* if more event types are anticipated, get their timestamp
228 if (t
> event_lasttime
)
232 #define STRIP_MODS(s) \
233 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
234 /* kill off the Button1Mask etc, only want the modifiers */ \
235 s &= (ControlMask | ShiftMask | Mod1Mask | \
236 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
238 static void event_hack_mods(XEvent *e)
246 STRIP_MODS(e
->xbutton
.state
);
249 STRIP_MODS(e
->xkey
.state
);
252 STRIP_MODS(e
->xkey
.state
);
253 /* remove from the state the mask of the modifier being released, if
254 it is a modifier key being released (this is a little ugly..) */
255 kp
= modmap
->modifiermap
;
256 for (i
= 0; i
< mask_table_size
; ++i
) {
257 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
258 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
259 /* remove the mask for it */
260 e
->xkey
.state
&= ~mask_table
[i
];
261 /* cause the first loop to break; */
263 break; /* get outta here! */
270 STRIP_MODS(e
->xmotion
.state
);
271 /* compress events */
274 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
276 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
277 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
284 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
288 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
289 because of RevertToPointerRoot. If the focus ends up reverting to
290 pointer root on a workspace change, then the FocusIn event that we
291 want will be of type NotifyAncestor. This situation does not occur
292 for FocusOut, so it is safely ignored there.
294 if (INVALID_FOCUSIN(e
) ||
297 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
298 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
300 /* says a client was not found for the event (or a valid FocusIn
303 e
->xfocus
.window
= None
;
308 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
309 e
->xfocus
.mode
, e
->xfocus
.detail
);
313 if (INVALID_FOCUSOUT(e
)) {
315 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
316 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
322 ob_debug("FocusOut on %lx mode %d detail %d\n",
323 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
328 gboolean fallback
= TRUE
;
331 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
333 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
335 if (fe
.type
== FocusOut
) {
337 ob_debug("found pending FocusOut\n");
339 if (!INVALID_FOCUSOUT(&fe
)) {
340 /* if there is a VALID FocusOut still coming, don't
341 fallback focus yet, we'll deal with it then */
342 XPutBackEvent(ob_display
, &fe
);
348 ob_debug("found pending FocusIn\n");
350 /* is the focused window getting a FocusOut/In back to
353 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
354 !event_ignore(&fe
, client
)) {
356 if focus_client is not set, then we can't do
357 this. we need the FocusIn. This happens in the
358 case when the set_focus_client(NULL) in the
359 focus_fallback function fires and then
360 focus_fallback picks the currently focused
361 window (such as on a SendToDesktop-esque action.
365 ob_debug("focused window got an Out/In back to "
366 "itself IGNORED both\n");
370 event_process(&fe
, NULL
);
372 ob_debug("focused window got an Out/In back to "
373 "itself but focus_client was null "
374 "IGNORED just the Out\n");
383 /* once all the FocusOut's have been dealt with, if
384 there is a FocusIn still left and it is valid, then
386 event_process(&fe
, &d
);
389 ob_debug("FocusIn was OK, so don't fallback\n");
399 ob_debug("no valid FocusIn and no FocusOut events found, "
402 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
408 /* NotifyUngrab occurs when a mouse button is released and the event is
409 caused, like when lowering a window */
410 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
411 pointer (Ancestor happens when the pointer is on a window border) */
412 if (e
->xcrossing
.mode
== NotifyGrab
||
413 e
->xcrossing
.detail
== NotifyInferior
||
414 (e
->xcrossing
.mode
== NotifyUngrab
&&
415 (e
->xcrossing
.detail
== NotifyAncestor
||
416 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
417 e
->xcrossing
.detail
== NotifyVirtual
))) {
419 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
420 (e
->type
== EnterNotify
? "Enter" : "Leave"),
422 e
->xcrossing
.detail
, client
?client
->window
:0);
427 ob_debug("%sNotify mode %d detail %d on %lx\n",
428 (e
->type
== EnterNotify
? "Enter" : "Leave"),
430 e
->xcrossing
.detail
, client
?client
->window
:0);
437 static void event_process(const XEvent
*ec
, gpointer data
)
440 ObGroup
*group
= NULL
;
441 ObClient
*client
= NULL
;
443 ObDockApp
*dockapp
= NULL
;
444 ObWindow
*obwin
= NULL
;
446 ObEventData
*ed
= data
;
448 /* make a copy we can mangle */
452 window
= event_get_window(e
);
453 if (!(e
->type
== PropertyNotify
&&
454 (group
= g_hash_table_lookup(group_map
, &window
))))
455 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
456 switch (obwin
->type
) {
458 dock
= WINDOW_AS_DOCK(obwin
);
461 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
464 client
= WINDOW_AS_CLIENT(obwin
);
467 case Window_Internal
:
468 /* not to be used for events */
469 g_assert_not_reached();
474 event_set_lasttime(e
);
476 if (event_ignore(e
, client
)) {
483 /* deal with it in the kernel */
485 event_handle_group(group
, e
);
487 event_handle_client(client
, e
);
489 event_handle_dockapp(dockapp
, e
);
491 event_handle_dock(dock
, e
);
492 else if (window
== RootWindow(ob_display
, ob_screen
))
493 event_handle_root(e
);
494 else if (e
->type
== MapRequest
)
495 client_manage(window
);
496 else if (e
->type
== ConfigureRequest
) {
497 /* unhandled configure requests must be used to configure the
501 xwc
.x
= e
->xconfigurerequest
.x
;
502 xwc
.y
= e
->xconfigurerequest
.y
;
503 xwc
.width
= e
->xconfigurerequest
.width
;
504 xwc
.height
= e
->xconfigurerequest
.height
;
505 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
506 xwc
.sibling
= e
->xconfigurerequest
.above
;
507 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
509 /* we are not to be held responsible if someone sends us an
511 xerror_set_ignore(TRUE
);
512 XConfigureWindow(ob_display
, window
,
513 e
->xconfigurerequest
.value_mask
, &xwc
);
514 xerror_set_ignore(FALSE
);
517 /* user input (action-bound) events */
518 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
519 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
520 e
->type
== KeyRelease
)
522 if (menu_frame_visible
)
523 event_handle_menu(e
);
525 if (!keyboard_process_interactive_grab(e
, &client
)) {
526 if (moveresize_in_progress
)
529 menu_can_hide
= FALSE
;
530 ob_main_loop_timeout_add(ob_main_loop
,
532 menu_hide_delay_func
,
535 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
536 e
->type
== MotionNotify
)
537 mouse_event(client
, e
);
538 else if (e
->type
== KeyPress
)
539 /* when in the middle of a focus cycling action, this
540 causes the window which appears to be focused to be
541 the one on which the actions will be executed */
542 keyboard_event((focus_cycle_target
?
544 (client
? client
: focus_client
)), e
);
550 static void event_handle_root(XEvent
*e
)
556 ob_debug("Another WM has requested to replace us. Exiting.\n");
561 if (e
->xclient
.format
!= 32) break;
563 msgtype
= e
->xclient
.message_type
;
564 if (msgtype
== prop_atoms
.net_current_desktop
) {
565 unsigned int d
= e
->xclient
.data
.l
[0];
566 if (d
< screen_num_desktops
)
567 screen_set_desktop(d
);
568 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
569 unsigned int d
= e
->xclient
.data
.l
[0];
571 screen_set_num_desktops(d
);
572 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
573 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
577 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
578 screen_update_desktop_names();
579 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
580 screen_update_layout();
582 case ConfigureNotify
:
584 XRRUpdateConfiguration(e
);
591 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
592 ob_debug("VIDMODE EVENT\n");
598 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
602 g_assert(e
->type
== PropertyNotify
);
604 for (it
= group
->members
; it
; it
= g_slist_next(it
))
605 event_handle_client(it
->data
, e
);
608 static void event_handle_client(ObClient
*client
, XEvent
*e
)
616 case VisibilityNotify
:
617 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
621 /* Wheel buttons don't draw because they are an instant click, so it
622 is a waste of resources to go drawing it. */
623 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
624 con
= frame_context(client
, e
->xbutton
.window
);
625 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
627 case OB_FRAME_CONTEXT_MAXIMIZE
:
628 client
->frame
->max_press
= (e
->type
== ButtonPress
);
629 framerender_frame(client
->frame
);
631 case OB_FRAME_CONTEXT_CLOSE
:
632 client
->frame
->close_press
= (e
->type
== ButtonPress
);
633 framerender_frame(client
->frame
);
635 case OB_FRAME_CONTEXT_ICONIFY
:
636 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
637 framerender_frame(client
->frame
);
639 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
640 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
641 framerender_frame(client
->frame
);
643 case OB_FRAME_CONTEXT_SHADE
:
644 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
645 framerender_frame(client
->frame
);
648 /* nothing changes with clicks for any other contexts */
655 ob_debug("FocusIn on client for %lx\n", client
->window
);
657 if (client
!= focus_client
) {
658 focus_set_client(client
);
659 frame_adjust_focus(client
->frame
, TRUE
);
664 ob_debug("FocusOut on client for %lx\n", client
->window
);
666 /* are we a fullscreen window or a transient of one? (checks layer)
667 if we are then we need to be iconified since we are losing focus
669 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
670 !client_search_focus_tree_full(client
))
671 /* iconify fullscreen windows when they and their transients
673 client_iconify(client
, TRUE
, TRUE
);
674 frame_adjust_focus(client
->frame
, FALSE
);
677 con
= frame_context(client
, e
->xcrossing
.window
);
679 case OB_FRAME_CONTEXT_MAXIMIZE
:
680 client
->frame
->max_hover
= FALSE
;
681 frame_adjust_state(client
->frame
);
683 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
684 client
->frame
->desk_hover
= FALSE
;
685 frame_adjust_state(client
->frame
);
687 case OB_FRAME_CONTEXT_SHADE
:
688 client
->frame
->shade_hover
= FALSE
;
689 frame_adjust_state(client
->frame
);
691 case OB_FRAME_CONTEXT_ICONIFY
:
692 client
->frame
->iconify_hover
= FALSE
;
693 frame_adjust_state(client
->frame
);
695 case OB_FRAME_CONTEXT_CLOSE
:
696 client
->frame
->close_hover
= FALSE
;
697 frame_adjust_state(client
->frame
);
699 case OB_FRAME_CONTEXT_FRAME
:
700 /* XXX if doing a 'reconfigure' make sure you kill this timer,
701 maybe all timers.. */
702 if (config_focus_delay
&& client
== focus_delay_client
) {
703 ob_main_loop_timeout_remove_data(ob_main_loop
,
706 focus_delay_client
= NULL
;
713 con
= frame_context(client
, e
->xcrossing
.window
);
715 case OB_FRAME_CONTEXT_MAXIMIZE
:
716 client
->frame
->max_hover
= TRUE
;
717 frame_adjust_state(client
->frame
);
719 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
720 client
->frame
->desk_hover
= TRUE
;
721 frame_adjust_state(client
->frame
);
723 case OB_FRAME_CONTEXT_SHADE
:
724 client
->frame
->shade_hover
= TRUE
;
725 frame_adjust_state(client
->frame
);
727 case OB_FRAME_CONTEXT_ICONIFY
:
728 client
->frame
->iconify_hover
= TRUE
;
729 frame_adjust_state(client
->frame
);
731 case OB_FRAME_CONTEXT_CLOSE
:
732 client
->frame
->close_hover
= TRUE
;
733 frame_adjust_state(client
->frame
);
735 case OB_FRAME_CONTEXT_FRAME
:
736 if (client_normal(client
)) {
737 if (config_focus_follow
) {
739 ob_debug("EnterNotify on %lx, focusing window\n",
742 if (config_focus_delay
) {
743 ob_main_loop_timeout_add(ob_main_loop
,
747 focus_delay_client
= client
;
749 client_focus(client
);
757 case ConfigureRequest
:
759 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
760 ConfigureRequest
, &ce
)) {
762 /* XXX if this causes bad things.. we can compress config req's
763 with the same mask. */
764 e
->xconfigurerequest
.value_mask
|=
765 ce
.xconfigurerequest
.value_mask
;
766 if (ce
.xconfigurerequest
.value_mask
& CWX
)
767 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
768 if (ce
.xconfigurerequest
.value_mask
& CWY
)
769 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
770 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
771 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
772 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
773 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
774 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
775 e
->xconfigurerequest
.border_width
=
776 ce
.xconfigurerequest
.border_width
;
777 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
778 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
781 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
782 if (client
->iconic
|| client
->shaded
) return;
784 /* resize, then move, as specified in the EWMH section 7.7 */
785 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
791 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
792 client
->border_width
= e
->xconfigurerequest
.border_width
;
794 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
795 e
->xconfigurerequest
.x
: client
->area
.x
;
796 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
797 e
->xconfigurerequest
.y
: client
->area
.y
;
798 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
799 e
->xconfigurerequest
.width
: client
->area
.width
;
800 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
801 e
->xconfigurerequest
.height
: client
->area
.height
;
807 client
->frame
->size
.left
+ client
->frame
->size
.right
;
809 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
810 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
811 client_normal(client
));
812 if (e
->xconfigurerequest
.value_mask
& CWX
)
814 if (e
->xconfigurerequest
.value_mask
& CWY
)
818 switch (client
->gravity
) {
819 case NorthEastGravity
:
821 corner
= OB_CORNER_TOPRIGHT
;
823 case SouthWestGravity
:
825 corner
= OB_CORNER_BOTTOMLEFT
;
827 case SouthEastGravity
:
828 corner
= OB_CORNER_BOTTOMRIGHT
;
830 default: /* NorthWest, Static, etc */
831 corner
= OB_CORNER_TOPLEFT
;
834 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
838 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
839 switch (e
->xconfigurerequest
.detail
) {
842 stacking_lower(CLIENT_AS_WINDOW(client
));
848 stacking_raise(CLIENT_AS_WINDOW(client
));
854 if (client
->ignore_unmaps
) {
855 client
->ignore_unmaps
--;
858 client_unmanage(client
);
861 client_unmanage(client
);
864 /* this is when the client is first taken captive in the frame */
865 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
868 This event is quite rare and is usually handled in unmapHandler.
869 However, if the window is unmapped when the reparent event occurs,
870 the window manager never sees it because an unmap event is not sent
871 to an already unmapped window.
874 /* we don't want the reparent event, put it back on the stack for the
875 X server to deal with after we unmanage the window */
876 XPutBackEvent(ob_display
, e
);
878 client_unmanage(client
);
881 ob_debug("MapRequest for 0x%lx\n", client
->window
);
882 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
883 does, we don't want it! */
884 if (screen_showing_desktop
)
885 screen_show_desktop(FALSE
);
886 client_iconify(client
, FALSE
, TRUE
);
887 if (!client
->frame
->visible
)
888 /* if its not visible still, then don't mess with it */
891 client_shade(client
, FALSE
);
892 client_focus(client
);
893 stacking_raise(CLIENT_AS_WINDOW(client
));
896 /* validate cuz we query stuff off the client here */
897 if (!client_validate(client
)) break;
899 if (e
->xclient
.format
!= 32) return;
901 msgtype
= e
->xclient
.message_type
;
902 if (msgtype
== prop_atoms
.wm_change_state
) {
903 /* compress changes into a single change */
904 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
906 /* XXX: it would be nice to compress ALL messages of a
907 type, not just messages in a row without other
908 message types between. */
909 if (ce
.xclient
.message_type
!= msgtype
) {
910 XPutBackEvent(ob_display
, &ce
);
913 e
->xclient
= ce
.xclient
;
915 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
916 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
917 /* compress changes into a single change */
918 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
920 /* XXX: it would be nice to compress ALL messages of a
921 type, not just messages in a row without other
922 message types between. */
923 if (ce
.xclient
.message_type
!= msgtype
) {
924 XPutBackEvent(ob_display
, &ce
);
927 e
->xclient
= ce
.xclient
;
929 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
930 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
931 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
933 } else if (msgtype
== prop_atoms
.net_wm_state
) {
934 /* can't compress these */
935 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
936 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
937 e
->xclient
.data
.l
[0] == 1 ? "Add" :
938 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
939 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
941 client_set_state(client
, e
->xclient
.data
.l
[0],
942 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
943 } else if (msgtype
== prop_atoms
.net_close_window
) {
944 ob_debug("net_close_window for 0x%lx\n", client
->window
);
945 client_close(client
);
946 } else if (msgtype
== prop_atoms
.net_active_window
) {
947 ob_debug("net_active_window for 0x%lx\n", client
->window
);
948 client_activate(client
, FALSE
);
949 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
950 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
951 if ((Atom
)e
->xclient
.data
.l
[2] ==
952 prop_atoms
.net_wm_moveresize_size_topleft
||
953 (Atom
)e
->xclient
.data
.l
[2] ==
954 prop_atoms
.net_wm_moveresize_size_top
||
955 (Atom
)e
->xclient
.data
.l
[2] ==
956 prop_atoms
.net_wm_moveresize_size_topright
||
957 (Atom
)e
->xclient
.data
.l
[2] ==
958 prop_atoms
.net_wm_moveresize_size_right
||
959 (Atom
)e
->xclient
.data
.l
[2] ==
960 prop_atoms
.net_wm_moveresize_size_right
||
961 (Atom
)e
->xclient
.data
.l
[2] ==
962 prop_atoms
.net_wm_moveresize_size_bottomright
||
963 (Atom
)e
->xclient
.data
.l
[2] ==
964 prop_atoms
.net_wm_moveresize_size_bottom
||
965 (Atom
)e
->xclient
.data
.l
[2] ==
966 prop_atoms
.net_wm_moveresize_size_bottomleft
||
967 (Atom
)e
->xclient
.data
.l
[2] ==
968 prop_atoms
.net_wm_moveresize_size_left
||
969 (Atom
)e
->xclient
.data
.l
[2] ==
970 prop_atoms
.net_wm_moveresize_move
||
971 (Atom
)e
->xclient
.data
.l
[2] ==
972 prop_atoms
.net_wm_moveresize_size_keyboard
||
973 (Atom
)e
->xclient
.data
.l
[2] ==
974 prop_atoms
.net_wm_moveresize_move_keyboard
) {
976 moveresize_start(client
, e
->xclient
.data
.l
[0],
977 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
978 e
->xclient
.data
.l
[2]);
980 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
981 int oldg
= client
->gravity
;
982 int tmpg
, x
, y
, w
, h
;
984 if (e
->xclient
.data
.l
[0] & 0xff)
985 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
989 if (e
->xclient
.data
.l
[0] & 1 << 8)
990 x
= e
->xclient
.data
.l
[1];
993 if (e
->xclient
.data
.l
[0] & 1 << 9)
994 y
= e
->xclient
.data
.l
[2];
997 if (e
->xclient
.data
.l
[0] & 1 << 10)
998 w
= e
->xclient
.data
.l
[3];
1000 w
= client
->area
.width
;
1001 if (e
->xclient
.data
.l
[0] & 1 << 11)
1002 h
= e
->xclient
.data
.l
[4];
1004 h
= client
->area
.height
;
1005 client
->gravity
= tmpg
;
1011 client
->frame
->size
.left
+ client
->frame
->size
.right
;
1013 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
1014 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
1015 client_normal(client
));
1016 if (e
->xclient
.data
.l
[0] & 1 << 8)
1018 if (e
->xclient
.data
.l
[0] & 1 << 9)
1022 client_configure(client
, OB_CORNER_TOPLEFT
,
1023 x
, y
, w
, h
, FALSE
, TRUE
);
1025 client
->gravity
= oldg
;
1028 case PropertyNotify
:
1029 /* validate cuz we query stuff off the client here */
1030 if (!client_validate(client
)) break;
1032 /* compress changes to a single property into a single change */
1033 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1037 /* XXX: it would be nice to compress ALL changes to a property,
1038 not just changes in a row without other props between. */
1040 a
= ce
.xproperty
.atom
;
1041 b
= e
->xproperty
.atom
;
1045 if ((a
== prop_atoms
.net_wm_name
||
1046 a
== prop_atoms
.wm_name
||
1047 a
== prop_atoms
.net_wm_icon_name
||
1048 a
== prop_atoms
.wm_icon_name
)
1050 (b
== prop_atoms
.net_wm_name
||
1051 b
== prop_atoms
.wm_name
||
1052 b
== prop_atoms
.net_wm_icon_name
||
1053 b
== prop_atoms
.wm_icon_name
)) {
1056 if ((a
== prop_atoms
.net_wm_icon
||
1057 a
== prop_atoms
.kwm_win_icon
)
1059 (b
== prop_atoms
.net_wm_icon
||
1060 b
== prop_atoms
.kwm_win_icon
))
1063 XPutBackEvent(ob_display
, &ce
);
1067 msgtype
= e
->xproperty
.atom
;
1068 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1069 client_update_normal_hints(client
);
1070 /* normal hints can make a window non-resizable */
1071 client_setup_decor_and_functions(client
);
1072 } else if (msgtype
== XA_WM_HINTS
) {
1073 client_update_wmhints(client
);
1074 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1075 client_update_transient_for(client
);
1076 client_get_type(client
);
1077 /* type may have changed, so update the layer */
1078 client_calc_layer(client
);
1079 client_setup_decor_and_functions(client
);
1080 } else if (msgtype
== prop_atoms
.net_wm_name
||
1081 msgtype
== prop_atoms
.wm_name
||
1082 msgtype
== prop_atoms
.net_wm_icon_name
||
1083 msgtype
== prop_atoms
.wm_icon_name
) {
1084 client_update_title(client
);
1085 } else if (msgtype
== prop_atoms
.wm_class
) {
1086 client_update_class(client
);
1087 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1088 client_update_protocols(client
);
1089 client_setup_decor_and_functions(client
);
1091 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1092 client_update_strut(client
);
1094 else if (msgtype
== prop_atoms
.net_wm_icon
||
1095 msgtype
== prop_atoms
.kwm_win_icon
) {
1096 client_update_icons(client
);
1098 else if (msgtype
== prop_atoms
.sm_client_id
) {
1099 client_update_sm_client_id(client
);
1104 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1105 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1106 frame_adjust_shape(client
->frame
);
1112 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1116 stacking_raise(DOCK_AS_WINDOW(s
));
1127 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1131 dock_app_drag(app
, &e
->xmotion
);
1134 if (app
->ignore_unmaps
) {
1135 app
->ignore_unmaps
--;
1138 dock_remove(app
, TRUE
);
1141 dock_remove(app
, FALSE
);
1143 case ReparentNotify
:
1144 dock_remove(app
, FALSE
);
1146 case ConfigureNotify
:
1147 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1152 ObMenuFrame
* find_active_menu()
1157 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1162 return it
? it
->data
: NULL
;
1165 static void event_handle_menu(XEvent
*ev
)
1168 ObMenuEntryFrame
*e
;
1172 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1173 ev
->xbutton
.y_root
)))
1174 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1175 else if (menu_can_hide
)
1176 menu_frame_hide_all();
1179 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1180 ev
->xmotion
.y_root
))) {
1181 menu_frame_move_on_screen(f
);
1182 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1183 ev
->xmotion
.y_root
)))
1184 menu_frame_select(f
, e
);
1189 a
= find_active_menu();
1191 a
->selected
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1193 menu_frame_select(a
, NULL
);
1198 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1199 menu_frame_hide_all();
1200 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1202 if ((f
= find_active_menu()))
1203 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
1204 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1206 if ((f
= find_active_menu()) && f
->parent
)
1207 menu_frame_select(f
, NULL
);
1208 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1210 if ((f
= find_active_menu()) && f
->child
)
1211 menu_frame_select_next(f
->child
);
1212 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1214 if ((f
= find_active_menu()))
1215 menu_frame_select_previous(f
);
1216 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1218 if ((f
= find_active_menu()))
1219 menu_frame_select_next(f
);
1225 static gboolean
menu_hide_delay_func(gpointer data
)
1227 menu_can_hide
= TRUE
;
1228 return FALSE
; /* no repeat */
1231 static gboolean
focus_delay_func(gpointer data
)
1233 client_focus(focus_delay_client
);
1234 return FALSE
; /* no repeat */
1237 static void focus_delay_client_dest(gpointer data
)
1240 if (c
== focus_delay_client
) {
1241 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1242 focus_delay_client
);
1243 focus_delay_client
= NULL
;