11 #include "menuframe.h"
15 #include "framerender.h"
17 #include "moveresize.h"
19 #include "extensions.h"
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
27 #ifdef HAVE_SYS_SELECT_H
28 # include <sys/select.h>
35 #include <X11/ICE/ICElib.h>
38 static void event_process(const XEvent
*e
, gpointer data
);
39 static void event_handle_root(XEvent
*e
);
40 static void event_handle_menu(XEvent
*e
);
41 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
42 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
43 static void event_handle_client(ObClient
*c
, XEvent
*e
);
45 static gboolean
focus_delay_func(gpointer data
);
46 static void focus_delay_client_dest(gpointer data
);
48 static gboolean
menu_hide_delay_func(gpointer data
);
50 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
51 (e)->xfocus.detail == NotifyAncestor || \
52 (e)->xfocus.detail > NotifyNonlinearVirtual)
53 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
54 (e)->xfocus.detail == NotifyInferior || \
55 (e)->xfocus.detail == NotifyAncestor || \
56 (e)->xfocus.detail > NotifyNonlinearVirtual)
58 Time event_lasttime
= 0;
60 /*! The value of the mask for the NumLock modifier */
61 unsigned int NumLockMask
;
62 /*! The value of the mask for the ScrollLock modifier */
63 unsigned int ScrollLockMask
;
64 /*! The key codes for the modifier keys */
65 static XModifierKeymap
*modmap
;
66 /*! Table of the constant modifier masks */
67 static const int mask_table
[] = {
68 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
69 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
71 static int mask_table_size
;
73 static ObClient
*focus_delay_client
;
75 static gboolean menu_can_hide
;
78 static void ice_handler(int fd
, gpointer conn
)
81 IceProcessMessages(conn
, NULL
, &b
);
84 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
85 IcePointer
*watch_data
)
90 fd
= IceConnectionNumber(conn
);
91 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
93 ob_main_loop_fd_remove(ob_main_loop
, fd
);
99 void event_startup(gboolean reconfig
)
101 if (reconfig
) return;
103 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
105 /* get lock masks that are defined by the display (not constant) */
106 modmap
= XGetModifierMapping(ob_display
);
108 if (modmap
&& modmap
->max_keypermod
> 0) {
110 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
111 /* get the values of the keyboard lock modifiers
112 Note: Caps lock is not retrieved the same way as Scroll and Num
113 lock since it doesn't need to be. */
114 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
115 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
118 for (cnt
= 0; cnt
< size
; ++cnt
) {
119 if (! modmap
->modifiermap
[cnt
]) continue;
121 if (num_lock
== modmap
->modifiermap
[cnt
])
122 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
123 if (scroll_lock
== modmap
->modifiermap
[cnt
])
124 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
128 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
131 IceAddConnectionWatch(ice_watch
, NULL
);
134 client_add_destructor(focus_delay_client_dest
);
137 void event_shutdown(gboolean reconfig
)
139 if (reconfig
) return;
142 IceRemoveConnectionWatch(ice_watch
, NULL
);
145 client_remove_destructor(focus_delay_client_dest
);
146 XFreeModifiermap(modmap
);
149 static Window
event_get_window(XEvent
*e
)
156 window
= RootWindow(ob_display
, ob_screen
);
159 window
= e
->xmap
.window
;
162 window
= e
->xunmap
.window
;
165 window
= e
->xdestroywindow
.window
;
167 case ConfigureRequest
:
168 window
= e
->xconfigurerequest
.window
;
170 case ConfigureNotify
:
171 window
= e
->xconfigure
.window
;
175 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
176 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
178 window
= ((XkbBellNotifyEvent
*)e
)->window
;
184 window
= e
->xany
.window
;
189 static void event_set_lasttime(XEvent
*e
)
193 /* grab the lasttime and hack up the state */
209 t
= e
->xproperty
.time
;
213 t
= e
->xcrossing
.time
;
216 /* if more event types are anticipated, get their timestamp
221 if (t
> event_lasttime
)
225 #define STRIP_MODS(s) \
226 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
227 /* kill off the Button1Mask etc, only want the modifiers */ \
228 s &= (ControlMask | ShiftMask | Mod1Mask | \
229 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
231 static void event_hack_mods(XEvent *e)
239 STRIP_MODS(e
->xbutton
.state
);
242 STRIP_MODS(e
->xkey
.state
);
245 STRIP_MODS(e
->xkey
.state
);
246 /* remove from the state the mask of the modifier being released, if
247 it is a modifier key being released (this is a little ugly..) */
248 kp
= modmap
->modifiermap
;
249 for (i
= 0; i
< mask_table_size
; ++i
) {
250 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
251 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
252 /* remove the mask for it */
253 e
->xkey
.state
&= ~mask_table
[i
];
254 /* cause the first loop to break; */
256 break; /* get outta here! */
263 STRIP_MODS(e
->xmotion
.state
);
264 /* compress events */
267 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
269 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
270 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
277 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
281 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
282 because of RevertToPointerRoot. If the focus ends up reverting to
283 pointer root on a workspace change, then the FocusIn event that we
284 want will be of type NotifyAncestor. This situation does not occur
285 for FocusOut, so it is safely ignored there.
287 if (INVALID_FOCUSIN(e
) ||
290 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
291 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
293 /* says a client was not found for the event (or a valid FocusIn
296 e
->xfocus
.window
= None
;
301 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
302 e
->xfocus
.mode
, e
->xfocus
.detail
);
306 if (INVALID_FOCUSOUT(e
)) {
308 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
309 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
315 ob_debug("FocusOut on %lx mode %d detail %d\n",
316 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
321 gboolean fallback
= TRUE
;
324 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
326 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
328 if (fe
.type
== FocusOut
) {
330 ob_debug("found pending FocusOut\n");
332 if (!INVALID_FOCUSOUT(&fe
)) {
333 /* if there is a VALID FocusOut still coming, don't
334 fallback focus yet, we'll deal with it then */
335 XPutBackEvent(ob_display
, &fe
);
341 ob_debug("found pending FocusIn\n");
343 /* is the focused window getting a FocusOut/In back to
346 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
347 !event_ignore(&fe
, client
)) {
349 if focus_client is not set, then we can't do
350 this. we need the FocusIn. This happens in the
351 case when the set_focus_client(NULL) in the
352 focus_fallback function fires and then
353 focus_fallback picks the currently focused
354 window (such as on a SendToDesktop-esque action.
358 ob_debug("focused window got an Out/In back to "
359 "itself IGNORED both\n");
363 event_process(&fe
, NULL
);
365 ob_debug("focused window got an Out/In back to "
366 "itself but focus_client was null "
367 "IGNORED just the Out\n");
373 /* once all the FocusOut's have been dealt with, if there
374 is a FocusIn still left and it is valid, then use it */
375 event_process(&fe
, NULL
);
376 /* secret magic way of event_process telling us that no
377 client was found for the FocusIn event. ^_^ */
378 if (fe
.xfocus
.window
!= None
) {
386 ob_debug("no valid FocusIn and no FocusOut events found, "
389 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
395 /* NotifyUngrab occurs when a mouse button is released and the event is
396 caused, like when lowering a window */
397 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
398 pointer (Ancestor happens when the pointer is on a window border) */
399 if (e
->xcrossing
.mode
== NotifyGrab
||
400 e
->xcrossing
.detail
== NotifyInferior
||
401 (e
->xcrossing
.mode
== NotifyUngrab
&&
402 (e
->xcrossing
.detail
== NotifyAncestor
||
403 e
->xcrossing
.detail
== NotifyNonlinearVirtual
||
404 e
->xcrossing
.detail
== NotifyVirtual
))) {
406 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
407 (e
->type
== EnterNotify
? "Enter" : "Leave"),
409 e
->xcrossing
.detail
, client
?client
->window
:0);
414 ob_debug("%sNotify mode %d detail %d on %lx\n",
415 (e
->type
== EnterNotify
? "Enter" : "Leave"),
417 e
->xcrossing
.detail
, client
?client
->window
:0);
424 static void event_process(const XEvent
*ec
, gpointer data
)
427 ObClient
*client
= NULL
;
429 ObDockApp
*dockapp
= NULL
;
430 ObWindow
*obwin
= NULL
;
433 /* make a copy we can mangle */
437 window
= event_get_window(e
);
438 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
439 switch (obwin
->type
) {
441 dock
= WINDOW_AS_DOCK(obwin
);
444 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
447 client
= WINDOW_AS_CLIENT(obwin
);
450 case Window_Internal
:
451 /* not to be used for events */
452 g_assert_not_reached();
457 event_set_lasttime(e
);
459 if (event_ignore(e
, client
))
462 /* deal with it in the kernel */
464 event_handle_client(client
, e
);
466 event_handle_dockapp(dockapp
, e
);
468 event_handle_dock(dock
, e
);
469 else if (window
== RootWindow(ob_display
, ob_screen
))
470 event_handle_root(e
);
471 else if (e
->type
== MapRequest
)
472 client_manage(window
);
473 else if (e
->type
== ConfigureRequest
) {
474 /* unhandled configure requests must be used to configure the
478 xwc
.x
= e
->xconfigurerequest
.x
;
479 xwc
.y
= e
->xconfigurerequest
.y
;
480 xwc
.width
= e
->xconfigurerequest
.width
;
481 xwc
.height
= e
->xconfigurerequest
.height
;
482 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
483 xwc
.sibling
= e
->xconfigurerequest
.above
;
484 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
486 /* we are not to be held responsible if someone sends us an
488 xerror_set_ignore(TRUE
);
489 XConfigureWindow(ob_display
, window
,
490 e
->xconfigurerequest
.value_mask
, &xwc
);
491 xerror_set_ignore(FALSE
);
494 /* user input (action-bound) events */
495 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
496 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
497 e
->type
== KeyRelease
)
499 if (menu_frame_visible
)
500 event_handle_menu(e
);
502 if (!keyboard_process_interactive_grab(e
, &client
)) {
503 if (moveresize_in_progress
)
506 menu_can_hide
= FALSE
;
507 ob_main_loop_timeout_add(ob_main_loop
,
509 menu_hide_delay_func
,
512 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
513 e
->type
== MotionNotify
)
514 mouse_event(client
, e
);
515 else if (e
->type
== KeyPress
)
516 /* when in the middle of a focus cycling action, this
517 causes the window which appears to be focused to be
518 the one on which the actions will be executed */
519 keyboard_event((focus_cycle_target
?
521 (client
? client
: focus_client
)), e
);
527 static void event_handle_root(XEvent
*e
)
533 ob_debug("Another WM has requested to replace us. Exiting.\n");
538 if (e
->xclient
.format
!= 32) break;
540 msgtype
= e
->xclient
.message_type
;
541 if (msgtype
== prop_atoms
.net_current_desktop
) {
542 unsigned int d
= e
->xclient
.data
.l
[0];
543 if (d
< screen_num_desktops
)
544 screen_set_desktop(d
);
545 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
546 unsigned int d
= e
->xclient
.data
.l
[0];
548 screen_set_num_desktops(d
);
549 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
550 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
554 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
555 screen_update_desktop_names();
556 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
557 screen_update_layout();
559 case ConfigureNotify
:
561 XRRUpdateConfiguration(e
);
568 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
569 ob_debug("VIDMODE EVENT\n");
575 static void event_handle_client(ObClient
*client
, XEvent
*e
)
583 case VisibilityNotify
:
584 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
588 /* Wheel buttons don't draw because they are an instant click, so it
589 is a waste of resources to go drawing it. */
590 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
591 con
= frame_context(client
, e
->xbutton
.window
);
592 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
594 case OB_FRAME_CONTEXT_MAXIMIZE
:
595 client
->frame
->max_press
= (e
->type
== ButtonPress
);
596 framerender_frame(client
->frame
);
598 case OB_FRAME_CONTEXT_CLOSE
:
599 client
->frame
->close_press
= (e
->type
== ButtonPress
);
600 framerender_frame(client
->frame
);
602 case OB_FRAME_CONTEXT_ICONIFY
:
603 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
604 framerender_frame(client
->frame
);
606 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
607 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
608 framerender_frame(client
->frame
);
610 case OB_FRAME_CONTEXT_SHADE
:
611 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
612 framerender_frame(client
->frame
);
615 /* nothing changes with clicks for any other contexts */
622 ob_debug("FocusIn on client for %lx\n", client
->window
);
624 if (client
!= focus_client
) {
625 focus_set_client(client
);
626 frame_adjust_focus(client
->frame
, TRUE
);
631 ob_debug("FocusOut on client for %lx\n", client
->window
);
633 /* are we a fullscreen window or a transient of one? (checks layer)
634 if we are then we need to be iconified since we are losing focus
636 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
637 !client_search_focus_tree_full(client
))
638 /* iconify fullscreen windows when they and their transients
640 client_iconify(client
, TRUE
, TRUE
);
641 frame_adjust_focus(client
->frame
, FALSE
);
644 con
= frame_context(client
, e
->xcrossing
.window
);
646 case OB_FRAME_CONTEXT_MAXIMIZE
:
647 client
->frame
->max_hover
= FALSE
;
648 frame_adjust_state(client
->frame
);
650 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
651 client
->frame
->desk_hover
= FALSE
;
652 frame_adjust_state(client
->frame
);
654 case OB_FRAME_CONTEXT_SHADE
:
655 client
->frame
->shade_hover
= FALSE
;
656 frame_adjust_state(client
->frame
);
658 case OB_FRAME_CONTEXT_ICONIFY
:
659 client
->frame
->iconify_hover
= FALSE
;
660 frame_adjust_state(client
->frame
);
662 case OB_FRAME_CONTEXT_CLOSE
:
663 client
->frame
->close_hover
= FALSE
;
664 frame_adjust_state(client
->frame
);
666 case OB_FRAME_CONTEXT_FRAME
:
667 /* XXX if doing a 'reconfigure' make sure you kill this timer,
668 maybe all timers.. */
669 if (config_focus_delay
&& client
== focus_delay_client
) {
670 ob_main_loop_timeout_remove_data(ob_main_loop
,
673 focus_delay_client
= NULL
;
680 con
= frame_context(client
, e
->xcrossing
.window
);
682 case OB_FRAME_CONTEXT_MAXIMIZE
:
683 client
->frame
->max_hover
= TRUE
;
684 frame_adjust_state(client
->frame
);
686 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
687 client
->frame
->desk_hover
= TRUE
;
688 frame_adjust_state(client
->frame
);
690 case OB_FRAME_CONTEXT_SHADE
:
691 client
->frame
->shade_hover
= TRUE
;
692 frame_adjust_state(client
->frame
);
694 case OB_FRAME_CONTEXT_ICONIFY
:
695 client
->frame
->iconify_hover
= TRUE
;
696 frame_adjust_state(client
->frame
);
698 case OB_FRAME_CONTEXT_CLOSE
:
699 client
->frame
->close_hover
= TRUE
;
700 frame_adjust_state(client
->frame
);
702 case OB_FRAME_CONTEXT_FRAME
:
703 if (client_normal(client
)) {
704 if (config_focus_follow
) {
706 ob_debug("EnterNotify on %lx, focusing window\n",
709 if (config_focus_delay
) {
710 ob_main_loop_timeout_add(ob_main_loop
,
714 focus_delay_client
= client
;
716 client_focus(client
);
724 case ConfigureRequest
:
726 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
727 ConfigureRequest
, &ce
)) {
729 /* XXX if this causes bad things.. we can compress config req's
730 with the same mask. */
731 e
->xconfigurerequest
.value_mask
|=
732 ce
.xconfigurerequest
.value_mask
;
733 if (ce
.xconfigurerequest
.value_mask
& CWX
)
734 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
735 if (ce
.xconfigurerequest
.value_mask
& CWY
)
736 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
737 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
738 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
739 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
740 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
741 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
742 e
->xconfigurerequest
.border_width
=
743 ce
.xconfigurerequest
.border_width
;
744 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
745 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
748 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
749 if (client
->iconic
|| client
->shaded
) return;
751 /* resize, then move, as specified in the EWMH section 7.7 */
752 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
758 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
759 client
->border_width
= e
->xconfigurerequest
.border_width
;
761 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
762 e
->xconfigurerequest
.x
: client
->area
.x
;
763 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
764 e
->xconfigurerequest
.y
: client
->area
.y
;
765 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
766 e
->xconfigurerequest
.width
: client
->area
.width
;
767 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
768 e
->xconfigurerequest
.height
: client
->area
.height
;
774 client
->frame
->size
.left
+ client
->frame
->size
.right
;
776 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
777 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
778 client_normal(client
));
779 if (e
->xconfigurerequest
.value_mask
& CWX
)
781 if (e
->xconfigurerequest
.value_mask
& CWY
)
785 switch (client
->gravity
) {
786 case NorthEastGravity
:
788 corner
= OB_CORNER_TOPRIGHT
;
790 case SouthWestGravity
:
792 corner
= OB_CORNER_BOTTOMLEFT
;
794 case SouthEastGravity
:
795 corner
= OB_CORNER_BOTTOMRIGHT
;
797 default: /* NorthWest, Static, etc */
798 corner
= OB_CORNER_TOPLEFT
;
801 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
805 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
806 switch (e
->xconfigurerequest
.detail
) {
809 stacking_lower(CLIENT_AS_WINDOW(client
));
815 stacking_raise(CLIENT_AS_WINDOW(client
));
821 if (client
->ignore_unmaps
) {
822 client
->ignore_unmaps
--;
825 client_unmanage(client
);
828 client_unmanage(client
);
831 /* this is when the client is first taken captive in the frame */
832 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
835 This event is quite rare and is usually handled in unmapHandler.
836 However, if the window is unmapped when the reparent event occurs,
837 the window manager never sees it because an unmap event is not sent
838 to an already unmapped window.
841 /* we don't want the reparent event, put it back on the stack for the
842 X server to deal with after we unmanage the window */
843 XPutBackEvent(ob_display
, e
);
845 client_unmanage(client
);
848 ob_debug("MapRequest for 0x%lx\n", client
->window
);
849 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
850 does, we don't want it! */
851 if (screen_showing_desktop
)
852 screen_show_desktop(FALSE
);
853 client_iconify(client
, FALSE
, TRUE
);
854 if (!client
->frame
->visible
)
855 /* if its not visible still, then don't mess with it */
858 client_shade(client
, FALSE
);
859 client_focus(client
);
860 stacking_raise(CLIENT_AS_WINDOW(client
));
863 /* validate cuz we query stuff off the client here */
864 if (!client_validate(client
)) break;
866 if (e
->xclient
.format
!= 32) return;
868 msgtype
= e
->xclient
.message_type
;
869 if (msgtype
== prop_atoms
.wm_change_state
) {
870 /* compress changes into a single change */
871 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
873 /* XXX: it would be nice to compress ALL messages of a
874 type, not just messages in a row without other
875 message types between. */
876 if (ce
.xclient
.message_type
!= msgtype
) {
877 XPutBackEvent(ob_display
, &ce
);
880 e
->xclient
= ce
.xclient
;
882 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
883 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
884 /* compress changes into a single change */
885 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
887 /* XXX: it would be nice to compress ALL messages of a
888 type, not just messages in a row without other
889 message types between. */
890 if (ce
.xclient
.message_type
!= msgtype
) {
891 XPutBackEvent(ob_display
, &ce
);
894 e
->xclient
= ce
.xclient
;
896 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
897 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
898 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
900 } else if (msgtype
== prop_atoms
.net_wm_state
) {
901 /* can't compress these */
902 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
903 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
904 e
->xclient
.data
.l
[0] == 1 ? "Add" :
905 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
906 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
908 client_set_state(client
, e
->xclient
.data
.l
[0],
909 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
910 } else if (msgtype
== prop_atoms
.net_close_window
) {
911 ob_debug("net_close_window for 0x%lx\n", client
->window
);
912 client_close(client
);
913 } else if (msgtype
== prop_atoms
.net_active_window
) {
914 ob_debug("net_active_window for 0x%lx\n", client
->window
);
915 client_activate(client
, FALSE
);
916 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
917 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
918 if ((Atom
)e
->xclient
.data
.l
[2] ==
919 prop_atoms
.net_wm_moveresize_size_topleft
||
920 (Atom
)e
->xclient
.data
.l
[2] ==
921 prop_atoms
.net_wm_moveresize_size_top
||
922 (Atom
)e
->xclient
.data
.l
[2] ==
923 prop_atoms
.net_wm_moveresize_size_topright
||
924 (Atom
)e
->xclient
.data
.l
[2] ==
925 prop_atoms
.net_wm_moveresize_size_right
||
926 (Atom
)e
->xclient
.data
.l
[2] ==
927 prop_atoms
.net_wm_moveresize_size_right
||
928 (Atom
)e
->xclient
.data
.l
[2] ==
929 prop_atoms
.net_wm_moveresize_size_bottomright
||
930 (Atom
)e
->xclient
.data
.l
[2] ==
931 prop_atoms
.net_wm_moveresize_size_bottom
||
932 (Atom
)e
->xclient
.data
.l
[2] ==
933 prop_atoms
.net_wm_moveresize_size_bottomleft
||
934 (Atom
)e
->xclient
.data
.l
[2] ==
935 prop_atoms
.net_wm_moveresize_size_left
||
936 (Atom
)e
->xclient
.data
.l
[2] ==
937 prop_atoms
.net_wm_moveresize_move
||
938 (Atom
)e
->xclient
.data
.l
[2] ==
939 prop_atoms
.net_wm_moveresize_size_keyboard
||
940 (Atom
)e
->xclient
.data
.l
[2] ==
941 prop_atoms
.net_wm_moveresize_move_keyboard
) {
943 moveresize_start(client
, e
->xclient
.data
.l
[0],
944 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
945 e
->xclient
.data
.l
[2]);
947 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
948 int oldg
= client
->gravity
;
949 int tmpg
, x
, y
, w
, h
;
951 if (e
->xclient
.data
.l
[0] & 0xff)
952 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
956 if (e
->xclient
.data
.l
[0] & 1 << 8)
957 x
= e
->xclient
.data
.l
[1];
960 if (e
->xclient
.data
.l
[0] & 1 << 9)
961 y
= e
->xclient
.data
.l
[2];
964 if (e
->xclient
.data
.l
[0] & 1 << 10)
965 w
= e
->xclient
.data
.l
[3];
967 w
= client
->area
.width
;
968 if (e
->xclient
.data
.l
[0] & 1 << 11)
969 h
= e
->xclient
.data
.l
[4];
971 h
= client
->area
.height
;
972 client
->gravity
= tmpg
;
978 client
->frame
->size
.left
+ client
->frame
->size
.right
;
980 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
981 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
982 client_normal(client
));
983 if (e
->xclient
.data
.l
[0] & 1 << 8)
985 if (e
->xclient
.data
.l
[0] & 1 << 9)
989 client_configure(client
, OB_CORNER_TOPLEFT
,
990 x
, y
, w
, h
, FALSE
, TRUE
);
992 client
->gravity
= oldg
;
996 /* validate cuz we query stuff off the client here */
997 if (!client_validate(client
)) break;
999 /* compress changes to a single property into a single change */
1000 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1004 /* XXX: it would be nice to compress ALL changes to a property,
1005 not just changes in a row without other props between. */
1007 a
= ce
.xproperty
.atom
;
1008 b
= e
->xproperty
.atom
;
1012 if ((a
== prop_atoms
.net_wm_name
||
1013 a
== prop_atoms
.wm_name
||
1014 a
== prop_atoms
.net_wm_icon_name
||
1015 a
== prop_atoms
.wm_icon_name
)
1017 (b
== prop_atoms
.net_wm_name
||
1018 b
== prop_atoms
.wm_name
||
1019 b
== prop_atoms
.net_wm_icon_name
||
1020 b
== prop_atoms
.wm_icon_name
)) {
1023 if ((a
== prop_atoms
.net_wm_icon
||
1024 a
== prop_atoms
.kwm_win_icon
)
1026 (b
== prop_atoms
.net_wm_icon
||
1027 b
== prop_atoms
.kwm_win_icon
))
1030 XPutBackEvent(ob_display
, &ce
);
1034 msgtype
= e
->xproperty
.atom
;
1035 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1036 client_update_normal_hints(client
);
1037 /* normal hints can make a window non-resizable */
1038 client_setup_decor_and_functions(client
);
1039 } else if (msgtype
== XA_WM_HINTS
) {
1040 client_update_wmhints(client
);
1041 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1042 client_update_transient_for(client
);
1043 client_get_type(client
);
1044 /* type may have changed, so update the layer */
1045 client_calc_layer(client
);
1046 client_setup_decor_and_functions(client
);
1047 } else if (msgtype
== prop_atoms
.net_wm_name
||
1048 msgtype
== prop_atoms
.wm_name
||
1049 msgtype
== prop_atoms
.net_wm_icon_name
||
1050 msgtype
== prop_atoms
.wm_icon_name
) {
1051 client_update_title(client
);
1052 } else if (msgtype
== prop_atoms
.wm_class
) {
1053 client_update_class(client
);
1054 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1055 client_update_protocols(client
);
1056 client_setup_decor_and_functions(client
);
1058 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1059 client_update_strut(client
);
1061 else if (msgtype
== prop_atoms
.net_wm_icon
||
1062 msgtype
== prop_atoms
.kwm_win_icon
) {
1063 client_update_icons(client
);
1068 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1069 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1070 frame_adjust_shape(client
->frame
);
1076 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1080 stacking_raise(DOCK_AS_WINDOW(s
));
1091 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1095 dock_app_drag(app
, &e
->xmotion
);
1098 if (app
->ignore_unmaps
) {
1099 app
->ignore_unmaps
--;
1102 dock_remove(app
, TRUE
);
1105 dock_remove(app
, FALSE
);
1107 case ReparentNotify
:
1108 dock_remove(app
, FALSE
);
1110 case ConfigureNotify
:
1111 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1116 ObMenuFrame
* find_active_menu()
1121 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1126 return it
? it
->data
: NULL
;
1129 static void event_handle_menu(XEvent
*ev
)
1132 ObMenuEntryFrame
*e
;
1136 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1137 ev
->xbutton
.y_root
)))
1138 menu_entry_frame_execute(e
, ev
->xbutton
.state
);
1139 else if (menu_can_hide
)
1140 menu_frame_hide_all();
1143 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1144 ev
->xmotion
.y_root
))) {
1145 menu_frame_move_on_screen(f
);
1146 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1147 ev
->xmotion
.y_root
)))
1148 menu_frame_select(f
, e
);
1152 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1153 menu_frame_hide_all();
1154 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1156 if ((f
= find_active_menu()))
1157 menu_entry_frame_execute(f
->selected
, ev
->xkey
.state
);
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
menu_hide_delay_func(gpointer data
)
1181 menu_can_hide
= TRUE
;
1182 return FALSE
; /* no repeat */
1185 static gboolean
focus_delay_func(gpointer data
)
1187 client_focus(focus_delay_client
);
1188 return FALSE
; /* no repeat */
1191 static void focus_delay_client_dest(gpointer data
)
1194 if (c
== focus_delay_client
) {
1195 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1196 focus_delay_client
);
1197 focus_delay_client
= NULL
;