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
;
75 static ObClient
*focus_delay_client
;
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
);
100 static void sn_handler(const XEvent
*e
, gpointer display
)
104 sn_display_process_event(display
, &ec
);
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
, NULL
, NULL
);
139 IceAddConnectionWatch(ice_watch
, NULL
);
143 ob_main_loop_x_add(ob_main_loop
, sn_handler
, ob_sn_display
, NULL
);
146 client_add_destructor(focus_delay_client_dest
);
149 void event_shutdown()
151 client_remove_destructor(focus_delay_client_dest
);
152 XFreeModifiermap(modmap
);
155 static Window
event_get_window(XEvent
*e
)
162 window
= RootWindow(ob_display
, ob_screen
);
165 window
= e
->xmap
.window
;
168 window
= e
->xunmap
.window
;
171 window
= e
->xdestroywindow
.window
;
173 case ConfigureRequest
:
174 window
= e
->xconfigurerequest
.window
;
176 case ConfigureNotify
:
177 window
= e
->xconfigure
.window
;
181 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
182 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
184 window
= ((XkbBellNotifyEvent
*)e
)->window
;
190 window
= e
->xany
.window
;
195 static void event_set_lasttime(XEvent
*e
)
199 /* grab the lasttime and hack up the state */
215 t
= e
->xproperty
.time
;
219 t
= e
->xcrossing
.time
;
222 /* if more event types are anticipated, get their timestamp
227 if (t
> event_lasttime
)
231 #define STRIP_MODS(s) \
232 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
233 /* kill off the Button1Mask etc, only want the modifiers */ \
234 s &= (ControlMask | ShiftMask | Mod1Mask | \
235 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
237 static void event_hack_mods(XEvent *e)
245 STRIP_MODS(e
->xbutton
.state
);
248 STRIP_MODS(e
->xkey
.state
);
251 STRIP_MODS(e
->xkey
.state
);
252 /* remove from the state the mask of the modifier being released, if
253 it is a modifier key being released (this is a little ugly..) */
254 kp
= modmap
->modifiermap
;
255 for (i
= 0; i
< mask_table_size
; ++i
) {
256 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
257 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
258 /* remove the mask for it */
259 e
->xkey
.state
&= ~mask_table
[i
];
260 /* cause the first loop to break; */
262 break; /* get outta here! */
269 STRIP_MODS(e
->xmotion
.state
);
270 /* compress events */
273 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
275 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
276 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
283 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
287 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
288 because of RevertToPointerRoot. If the focus ends up reverting to
289 pointer root on a workspace change, then the FocusIn event that we
290 want will be of type NotifyAncestor. This situation does not occur
291 for FocusOut, so it is safely ignored there.
293 if (INVALID_FOCUSIN(e
) ||
296 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
297 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
299 /* says a client was not found for the event (or a valid FocusIn
302 e
->xfocus
.window
= None
;
307 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
308 e
->xfocus
.mode
, e
->xfocus
.detail
);
312 if (INVALID_FOCUSOUT(e
)) {
314 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
315 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
321 ob_debug("FocusOut on %lx mode %d detail %d\n",
322 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
327 gboolean fallback
= TRUE
;
330 if (!XCheckTypedWindowEvent(ob_display
, e
->xfocus
.window
,
332 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
334 if (fe
.type
== FocusOut
) {
336 ob_debug("found pending FocusOut");
338 if (!INVALID_FOCUSOUT(&fe
)) {
339 /* if there is a VALID FocusOut still coming, don't
340 fallback focus yet, we'll deal with it then */
341 XPutBackEvent(ob_display
, &fe
);
347 ob_debug("found pending FocusIn");
349 /* is the focused window getting a FocusOut/In back to
352 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
353 !event_ignore(&fe
, client
)) {
355 if focus_client is not set, then we can't do
356 this. we need the FocusIn. This happens in the
357 case when the set_focus_client(NULL) in the
358 focus_fallback function fires and then
359 focus_fallback picks the currently focused
360 window (such as on a SendToDesktop-esque action.
364 ob_debug("focused window got an Out/In back to "
365 "itself IGNORED both");
369 event_process(&fe
, NULL
);
371 ob_debug("focused window got an Out/In back to "
372 "itself but focus_client was null "
373 "IGNORED just the Out");
379 /* once all the FocusOut's have been dealt with, if there
380 is a FocusIn still left and it is valid, then use it */
381 event_process(&fe
, NULL
);
382 /* secret magic way of event_process telling us that no
383 client was found for the FocusIn event. ^_^ */
384 if (fe
.xfocus
.window
!= None
) {
392 ob_debug("no valid FocusIn and no FocusOut events found, "
395 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
401 /* NotifyUngrab occurs when a mouse button is released and the event is
402 caused, like when lowering a window */
403 /* NotifyVirtual occurs when ungrabbing the pointer */
404 if (e
->xcrossing
.mode
== NotifyGrab
||
405 e
->xcrossing
.detail
== NotifyInferior
||
406 (e
->xcrossing
.mode
== NotifyUngrab
&&
407 e
->xcrossing
.detail
== NotifyVirtual
)) {
409 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
410 (e
->type
== EnterNotify
? "Enter" : "Leave"),
412 e
->xcrossing
.detail
, client
?client
->window
:0);
417 ob_debug("%sNotify mode %d detail %d on %lx",
418 (e
->type
== EnterNotify
? "Enter" : "Leave"),
420 e
->xcrossing
.detail
, client
?client
->window
:0);
427 static void event_process(const XEvent
*ec
, gpointer data
)
430 ObClient
*client
= NULL
;
432 ObDockApp
*dockapp
= NULL
;
433 ObWindow
*obwin
= NULL
;
436 /* make a copy we can mangle */
440 window
= event_get_window(e
);
441 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
442 switch (obwin
->type
) {
444 dock
= WINDOW_AS_DOCK(obwin
);
447 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
450 client
= WINDOW_AS_CLIENT(obwin
);
453 case Window_Internal
:
454 /* not to be used for events */
455 g_assert_not_reached();
460 event_set_lasttime(e
);
462 if (event_ignore(e
, client
))
465 /* deal with it in the kernel */
467 event_handle_client(client
, e
);
469 event_handle_dockapp(dockapp
, e
);
471 event_handle_dock(dock
, e
);
472 else if (window
== RootWindow(ob_display
, ob_screen
))
473 event_handle_root(e
);
474 else if (e
->type
== MapRequest
)
475 client_manage(window
);
476 else if (e
->type
== ConfigureRequest
) {
477 /* unhandled configure requests must be used to configure the
481 xwc
.x
= e
->xconfigurerequest
.x
;
482 xwc
.y
= e
->xconfigurerequest
.y
;
483 xwc
.width
= e
->xconfigurerequest
.width
;
484 xwc
.height
= e
->xconfigurerequest
.height
;
485 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
486 xwc
.sibling
= e
->xconfigurerequest
.above
;
487 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
489 /* we are not to be held responsible if someone sends us an
491 xerror_set_ignore(TRUE
);
492 XConfigureWindow(ob_display
, window
,
493 e
->xconfigurerequest
.value_mask
, &xwc
);
494 xerror_set_ignore(FALSE
);
497 /* user input (action-bound) events */
498 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
499 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
500 e
->type
== KeyRelease
)
502 if (menu_frame_visible
)
503 event_handle_menu(e
);
504 else if (moveresize_in_progress
)
507 ObFrameContext context
;
509 context
= frame_context(client
, e
->xany
.window
);
511 if (!keyboard_process_interactive_grab(e
, &client
, &context
)) {
512 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
513 e
->type
== MotionNotify
)
514 mouse_event(client
, context
, e
);
515 else if (e
->type
== KeyPress
)
516 keyboard_event(client
, e
);
522 static void event_handle_root(XEvent
*e
)
528 ob_debug("Another WM has requested to replace us. Exiting.\n");
533 if (e
->xclient
.format
!= 32) break;
535 msgtype
= e
->xclient
.message_type
;
536 if (msgtype
== prop_atoms
.net_current_desktop
) {
537 unsigned int d
= e
->xclient
.data
.l
[0];
538 if (d
< screen_num_desktops
)
539 screen_set_desktop(d
);
540 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
541 unsigned int d
= e
->xclient
.data
.l
[0];
543 screen_set_num_desktops(d
);
544 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
545 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
549 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
550 screen_update_desktop_names();
551 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
552 screen_update_layout();
554 case ConfigureNotify
:
556 XRRUpdateConfiguration(e
);
563 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
564 ob_debug("VIDMODE EVENT\n");
570 static void event_handle_client(ObClient
*client
, XEvent
*e
)
578 case VisibilityNotify
:
579 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
583 /* Wheel buttons don't draw because they are an instant click, so it
584 is a waste of resources to go drawing it. */
585 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
586 switch (frame_context(client
, e
->xbutton
.window
)) {
587 case OB_FRAME_CONTEXT_MAXIMIZE
:
588 client
->frame
->max_press
= (e
->type
== ButtonPress
);
589 framerender_frame(client
->frame
);
591 case OB_FRAME_CONTEXT_CLOSE
:
592 client
->frame
->close_press
= (e
->type
== ButtonPress
);
593 framerender_frame(client
->frame
);
595 case OB_FRAME_CONTEXT_ICONIFY
:
596 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
597 framerender_frame(client
->frame
);
599 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
600 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
601 framerender_frame(client
->frame
);
603 case OB_FRAME_CONTEXT_SHADE
:
604 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
605 framerender_frame(client
->frame
);
608 /* nothing changes with clicks for any other contexts */
615 ob_debug("FocusIn on client for %lx\n", client
->window
);
617 if (client
!= focus_client
) {
618 focus_set_client(client
);
619 frame_adjust_focus(client
->frame
, TRUE
);
624 ob_debug("FocusOut on client for %lx\n", client
->window
);
626 /* are we a fullscreen window or a transient of one? (checks layer)
627 if we are then we need to be iconified since we are losing focus
629 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
630 !client_search_focus_tree_full(client
))
631 /* iconify fullscreen windows when they and their transients
633 client_iconify(client
, TRUE
, TRUE
);
634 frame_adjust_focus(client
->frame
, FALSE
);
637 con
= frame_context(client
, e
->xcrossing
.window
);
639 case OB_FRAME_CONTEXT_MAXIMIZE
:
640 client
->frame
->max_hover
= FALSE
;
641 frame_adjust_state(client
->frame
);
643 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
644 client
->frame
->desk_hover
= FALSE
;
645 frame_adjust_state(client
->frame
);
647 case OB_FRAME_CONTEXT_SHADE
:
648 client
->frame
->shade_hover
= FALSE
;
649 frame_adjust_state(client
->frame
);
651 case OB_FRAME_CONTEXT_ICONIFY
:
652 client
->frame
->iconify_hover
= FALSE
;
653 frame_adjust_state(client
->frame
);
655 case OB_FRAME_CONTEXT_CLOSE
:
656 client
->frame
->close_hover
= FALSE
;
657 frame_adjust_state(client
->frame
);
659 case OB_FRAME_CONTEXT_FRAME
:
660 /* XXX if doing a 'reconfigure' make sure you kill this timer,
661 maybe all timers.. */
662 if (config_focus_delay
) {
663 focus_delay_client
= NULL
;
664 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
671 con
= frame_context(client
, e
->xcrossing
.window
);
673 case OB_FRAME_CONTEXT_MAXIMIZE
:
674 client
->frame
->max_hover
= TRUE
;
675 frame_adjust_state(client
->frame
);
677 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
678 client
->frame
->desk_hover
= TRUE
;
679 frame_adjust_state(client
->frame
);
681 case OB_FRAME_CONTEXT_SHADE
:
682 client
->frame
->shade_hover
= TRUE
;
683 frame_adjust_state(client
->frame
);
685 case OB_FRAME_CONTEXT_ICONIFY
:
686 client
->frame
->iconify_hover
= TRUE
;
687 frame_adjust_state(client
->frame
);
689 case OB_FRAME_CONTEXT_CLOSE
:
690 client
->frame
->close_hover
= TRUE
;
691 frame_adjust_state(client
->frame
);
693 case OB_FRAME_CONTEXT_FRAME
:
694 if (client_normal(client
)) {
695 if (ob_state() == OB_STATE_STARTING
) {
696 /* move it to the top of the focus order */
697 guint desktop
= client
->desktop
;
698 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
699 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
701 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
703 } else if (config_focus_follow
) {
705 ob_debug("EnterNotify on %lx, focusing window\n",
708 if (config_focus_delay
) {
709 ob_main_loop_timeout_add(ob_main_loop
,
713 focus_delay_client
= client
;
715 client_focus(client
);
723 case ConfigureRequest
:
725 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
726 ConfigureRequest
, &ce
)) {
728 /* XXX if this causes bad things.. we can compress config req's
729 with the same mask. */
730 e
->xconfigurerequest
.value_mask
|=
731 ce
.xconfigurerequest
.value_mask
;
732 if (ce
.xconfigurerequest
.value_mask
& CWX
)
733 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
734 if (ce
.xconfigurerequest
.value_mask
& CWY
)
735 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
736 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
737 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
738 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
739 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
740 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
741 e
->xconfigurerequest
.border_width
=
742 ce
.xconfigurerequest
.border_width
;
743 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
744 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
747 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
748 if (client
->iconic
|| client
->shaded
) return;
750 /* resize, then move, as specified in the EWMH section 7.7 */
751 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
757 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
758 client
->border_width
= e
->xconfigurerequest
.border_width
;
760 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
761 e
->xconfigurerequest
.x
: client
->area
.x
;
762 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
763 e
->xconfigurerequest
.y
: client
->area
.y
;
764 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
765 e
->xconfigurerequest
.width
: client
->area
.width
;
766 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
767 e
->xconfigurerequest
.height
: client
->area
.height
;
773 client
->frame
->size
.left
+ client
->frame
->size
.right
;
775 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
776 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
777 client_normal(client
));
778 if (e
->xconfigurerequest
.value_mask
& CWX
)
780 if (e
->xconfigurerequest
.value_mask
& CWY
)
784 switch (client
->gravity
) {
785 case NorthEastGravity
:
787 corner
= OB_CORNER_TOPRIGHT
;
789 case SouthWestGravity
:
791 corner
= OB_CORNER_BOTTOMLEFT
;
793 case SouthEastGravity
:
794 corner
= OB_CORNER_BOTTOMRIGHT
;
796 default: /* NorthWest, Static, etc */
797 corner
= OB_CORNER_TOPLEFT
;
800 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
804 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
805 switch (e
->xconfigurerequest
.detail
) {
808 stacking_lower(CLIENT_AS_WINDOW(client
));
814 stacking_raise(CLIENT_AS_WINDOW(client
));
820 if (client
->ignore_unmaps
) {
821 client
->ignore_unmaps
--;
824 client_unmanage(client
);
827 client_unmanage(client
);
830 /* this is when the client is first taken captive in the frame */
831 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
834 This event is quite rare and is usually handled in unmapHandler.
835 However, if the window is unmapped when the reparent event occurs,
836 the window manager never sees it because an unmap event is not sent
837 to an already unmapped window.
840 /* we don't want the reparent event, put it back on the stack for the
841 X server to deal with after we unmanage the window */
842 XPutBackEvent(ob_display
, e
);
844 client_unmanage(client
);
847 ob_debug("MapRequest for 0x%lx\n", client
->window
);
848 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
849 does, we don't want it! */
850 if (screen_showing_desktop
)
851 screen_show_desktop(FALSE
);
852 client_iconify(client
, FALSE
, TRUE
);
853 if (!client
->frame
->visible
)
854 /* if its not visible still, then don't mess with it */
857 client_shade(client
, FALSE
);
858 client_focus(client
);
859 stacking_raise(CLIENT_AS_WINDOW(client
));
862 /* validate cuz we query stuff off the client here */
863 if (!client_validate(client
)) break;
865 if (e
->xclient
.format
!= 32) return;
867 msgtype
= e
->xclient
.message_type
;
868 if (msgtype
== prop_atoms
.wm_change_state
) {
869 /* compress changes into a single change */
870 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
872 /* XXX: it would be nice to compress ALL messages of a
873 type, not just messages in a row without other
874 message types between. */
875 if (ce
.xclient
.message_type
!= msgtype
) {
876 XPutBackEvent(ob_display
, &ce
);
879 e
->xclient
= ce
.xclient
;
881 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
882 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
883 /* compress changes into a single change */
884 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
886 /* XXX: it would be nice to compress ALL messages of a
887 type, not just messages in a row without other
888 message types between. */
889 if (ce
.xclient
.message_type
!= msgtype
) {
890 XPutBackEvent(ob_display
, &ce
);
893 e
->xclient
= ce
.xclient
;
895 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
896 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
897 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
899 } else if (msgtype
== prop_atoms
.net_wm_state
) {
900 /* can't compress these */
901 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
902 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
903 e
->xclient
.data
.l
[0] == 1 ? "Add" :
904 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
905 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
907 client_set_state(client
, e
->xclient
.data
.l
[0],
908 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
909 } else if (msgtype
== prop_atoms
.net_close_window
) {
910 ob_debug("net_close_window for 0x%lx\n", client
->window
);
911 client_close(client
);
912 } else if (msgtype
== prop_atoms
.net_active_window
) {
913 ob_debug("net_active_window for 0x%lx\n", client
->window
);
914 client_activate(client
, FALSE
);
915 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
916 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
917 if ((Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_size_topleft
||
919 (Atom
)e
->xclient
.data
.l
[2] ==
920 prop_atoms
.net_wm_moveresize_size_top
||
921 (Atom
)e
->xclient
.data
.l
[2] ==
922 prop_atoms
.net_wm_moveresize_size_topright
||
923 (Atom
)e
->xclient
.data
.l
[2] ==
924 prop_atoms
.net_wm_moveresize_size_right
||
925 (Atom
)e
->xclient
.data
.l
[2] ==
926 prop_atoms
.net_wm_moveresize_size_right
||
927 (Atom
)e
->xclient
.data
.l
[2] ==
928 prop_atoms
.net_wm_moveresize_size_bottomright
||
929 (Atom
)e
->xclient
.data
.l
[2] ==
930 prop_atoms
.net_wm_moveresize_size_bottom
||
931 (Atom
)e
->xclient
.data
.l
[2] ==
932 prop_atoms
.net_wm_moveresize_size_bottomleft
||
933 (Atom
)e
->xclient
.data
.l
[2] ==
934 prop_atoms
.net_wm_moveresize_size_left
||
935 (Atom
)e
->xclient
.data
.l
[2] ==
936 prop_atoms
.net_wm_moveresize_move
||
937 (Atom
)e
->xclient
.data
.l
[2] ==
938 prop_atoms
.net_wm_moveresize_size_keyboard
||
939 (Atom
)e
->xclient
.data
.l
[2] ==
940 prop_atoms
.net_wm_moveresize_move_keyboard
) {
942 moveresize_start(client
, e
->xclient
.data
.l
[0],
943 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
944 e
->xclient
.data
.l
[2]);
946 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
947 int oldg
= client
->gravity
;
948 int tmpg
, x
, y
, w
, h
;
950 if (e
->xclient
.data
.l
[0] & 0xff)
951 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
955 if (e
->xclient
.data
.l
[0] & 1 << 8)
956 x
= e
->xclient
.data
.l
[1];
959 if (e
->xclient
.data
.l
[0] & 1 << 9)
960 y
= e
->xclient
.data
.l
[2];
963 if (e
->xclient
.data
.l
[0] & 1 << 10)
964 w
= e
->xclient
.data
.l
[3];
966 w
= client
->area
.width
;
967 if (e
->xclient
.data
.l
[0] & 1 << 11)
968 h
= e
->xclient
.data
.l
[4];
970 h
= client
->area
.height
;
971 client
->gravity
= tmpg
;
977 client
->frame
->size
.left
+ client
->frame
->size
.right
;
979 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
980 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
981 client_normal(client
));
982 if (e
->xclient
.data
.l
[0] & 1 << 8)
984 if (e
->xclient
.data
.l
[0] & 1 << 9)
988 client_configure(client
, OB_CORNER_TOPLEFT
,
989 x
, y
, w
, h
, FALSE
, TRUE
);
991 client
->gravity
= oldg
;
995 /* validate cuz we query stuff off the client here */
996 if (!client_validate(client
)) break;
998 /* compress changes to a single property into a single change */
999 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1003 /* XXX: it would be nice to compress ALL changes to a property,
1004 not just changes in a row without other props between. */
1006 a
= ce
.xproperty
.atom
;
1007 b
= e
->xproperty
.atom
;
1011 if ((a
== prop_atoms
.net_wm_name
||
1012 a
== prop_atoms
.wm_name
||
1013 a
== prop_atoms
.net_wm_icon_name
||
1014 a
== prop_atoms
.wm_icon_name
)
1016 (b
== prop_atoms
.net_wm_name
||
1017 b
== prop_atoms
.wm_name
||
1018 b
== prop_atoms
.net_wm_icon_name
||
1019 b
== prop_atoms
.wm_icon_name
)) {
1022 if ((a
== prop_atoms
.net_wm_icon
||
1023 a
== prop_atoms
.kwm_win_icon
)
1025 (b
== prop_atoms
.net_wm_icon
||
1026 b
== prop_atoms
.kwm_win_icon
))
1029 XPutBackEvent(ob_display
, &ce
);
1033 msgtype
= e
->xproperty
.atom
;
1034 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1035 client_update_normal_hints(client
);
1036 /* normal hints can make a window non-resizable */
1037 client_setup_decor_and_functions(client
);
1038 } else if (msgtype
== XA_WM_HINTS
) {
1039 client_update_wmhints(client
);
1040 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1041 client_update_transient_for(client
);
1042 client_get_type(client
);
1043 /* type may have changed, so update the layer */
1044 client_calc_layer(client
);
1045 client_setup_decor_and_functions(client
);
1046 } else if (msgtype
== prop_atoms
.net_wm_name
||
1047 msgtype
== prop_atoms
.wm_name
||
1048 msgtype
== prop_atoms
.net_wm_icon_name
||
1049 msgtype
== prop_atoms
.wm_icon_name
) {
1050 client_update_title(client
);
1051 } else if (msgtype
== prop_atoms
.wm_class
) {
1052 client_update_class(client
);
1053 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1054 client_update_protocols(client
);
1055 client_setup_decor_and_functions(client
);
1057 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1058 client_update_strut(client
);
1060 else if (msgtype
== prop_atoms
.net_wm_icon
||
1061 msgtype
== prop_atoms
.kwm_win_icon
) {
1062 client_update_icons(client
);
1067 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1068 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1069 frame_adjust_shape(client
->frame
);
1075 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1079 stacking_raise(DOCK_AS_WINDOW(s
));
1090 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1094 dock_app_drag(app
, &e
->xmotion
);
1097 if (app
->ignore_unmaps
) {
1098 app
->ignore_unmaps
--;
1101 dock_remove(app
, TRUE
);
1104 dock_remove(app
, FALSE
);
1106 case ReparentNotify
:
1107 dock_remove(app
, FALSE
);
1109 case ConfigureNotify
:
1110 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1115 ObMenuFrame
* find_active_menu()
1120 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1125 return it
? it
->data
: NULL
;
1128 static void event_handle_menu(XEvent
*ev
)
1131 ObMenuEntryFrame
*e
;
1135 if (!(f
= menu_frame_under(ev
->xbutton
.x_root
,
1136 ev
->xbutton
.y_root
)))
1137 menu_frame_hide_all();
1139 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1140 ev
->xbutton
.y_root
)))
1141 menu_entry_frame_execute(e
,
1142 !(ev
->xbutton
.state
& ControlMask
));
1146 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1147 ev
->xmotion
.y_root
))) {
1148 menu_frame_move_on_screen(f
);
1149 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1150 ev
->xmotion
.y_root
)))
1151 menu_frame_select(f
, e
);
1155 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1156 menu_frame_hide_all();
1157 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1159 if ((f
= find_active_menu()))
1160 menu_entry_frame_execute(f
->selected
,
1161 !(ev
->xkey
.state
& ControlMask
));
1162 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1164 if ((f
= find_active_menu()) && f
->parent
)
1165 menu_frame_select(f
, NULL
);
1166 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1168 if ((f
= find_active_menu()) && f
->child
)
1169 menu_frame_select_next(f
->child
);
1170 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1172 if ((f
= find_active_menu()))
1173 menu_frame_select_previous(f
);
1174 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1176 if ((f
= find_active_menu()))
1177 menu_frame_select_next(f
);
1183 static gboolean
focus_delay_func(gpointer data
)
1185 client_focus(focus_delay_client
);
1186 return FALSE
; /* no repeat */
1189 static void focus_delay_client_dest(gpointer data
)
1192 if (c
== focus_delay_client
) {
1193 focus_delay_client
= NULL
;
1194 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);