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 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
50 (e)->xfocus.detail == NotifyAncestor || \
51 (e)->xfocus.detail > NotifyNonlinearVirtual)
52 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
53 (e)->xfocus.detail == NotifyInferior || \
54 (e)->xfocus.detail == NotifyAncestor || \
55 (e)->xfocus.detail > NotifyNonlinearVirtual)
57 Time event_lasttime
= 0;
59 /*! The value of the mask for the NumLock modifier */
60 unsigned int NumLockMask
;
61 /*! The value of the mask for the ScrollLock modifier */
62 unsigned int ScrollLockMask
;
63 /*! The key codes for the modifier keys */
64 static XModifierKeymap
*modmap
;
65 /*! Table of the constant modifier masks */
66 static const int mask_table
[] = {
67 ShiftMask
, LockMask
, ControlMask
, Mod1Mask
,
68 Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
70 static int mask_table_size
;
73 static void ice_handler(int fd
, gpointer conn
)
76 IceProcessMessages(conn
, NULL
, &b
);
79 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
80 IcePointer
*watch_data
)
85 fd
= IceConnectionNumber(conn
);
86 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
88 ob_main_loop_fd_remove(ob_main_loop
, fd
);
95 static void sn_handler(const XEvent
*e
, gpointer display
)
99 sn_display_process_event(display
, &ec
);
106 mask_table_size
= sizeof(mask_table
) / sizeof(mask_table
[0]);
108 /* get lock masks that are defined by the display (not constant) */
109 modmap
= XGetModifierMapping(ob_display
);
111 if (modmap
&& modmap
->max_keypermod
> 0) {
113 const size_t size
= mask_table_size
* modmap
->max_keypermod
;
114 /* get the values of the keyboard lock modifiers
115 Note: Caps lock is not retrieved the same way as Scroll and Num
116 lock since it doesn't need to be. */
117 const KeyCode num_lock
= XKeysymToKeycode(ob_display
, XK_Num_Lock
);
118 const KeyCode scroll_lock
= XKeysymToKeycode(ob_display
,
121 for (cnt
= 0; cnt
< size
; ++cnt
) {
122 if (! modmap
->modifiermap
[cnt
]) continue;
124 if (num_lock
== modmap
->modifiermap
[cnt
])
125 NumLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
126 if (scroll_lock
== modmap
->modifiermap
[cnt
])
127 ScrollLockMask
= mask_table
[cnt
/ modmap
->max_keypermod
];
131 ob_main_loop_x_add(ob_main_loop
, event_process
, NULL
, NULL
);
134 IceAddConnectionWatch(ice_watch
, NULL
);
138 ob_main_loop_x_add(ob_main_loop
, sn_handler
, ob_sn_display
, NULL
);
142 void event_shutdown()
144 XFreeModifiermap(modmap
);
147 static Window
event_get_window(XEvent
*e
)
154 window
= RootWindow(ob_display
, ob_screen
);
157 window
= e
->xmap
.window
;
160 window
= e
->xunmap
.window
;
163 window
= e
->xdestroywindow
.window
;
165 case ConfigureRequest
:
166 window
= e
->xconfigurerequest
.window
;
168 case ConfigureNotify
:
169 window
= e
->xconfigure
.window
;
173 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
174 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
176 window
= ((XkbBellNotifyEvent
*)e
)->window
;
182 window
= e
->xany
.window
;
187 static void event_set_lasttime(XEvent
*e
)
191 /* grab the lasttime and hack up the state */
207 t
= e
->xproperty
.time
;
211 t
= e
->xcrossing
.time
;
214 /* if more event types are anticipated, get their timestamp
219 if (t
> event_lasttime
)
223 #define STRIP_MODS(s) \
224 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
225 /* kill off the Button1Mask etc, only want the modifiers */ \
226 s &= (ControlMask | ShiftMask | Mod1Mask | \
227 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
229 static void event_hack_mods(XEvent *e)
237 STRIP_MODS(e
->xbutton
.state
);
240 STRIP_MODS(e
->xkey
.state
);
243 STRIP_MODS(e
->xkey
.state
);
244 /* remove from the state the mask of the modifier being released, if
245 it is a modifier key being released (this is a little ugly..) */
246 kp
= modmap
->modifiermap
;
247 for (i
= 0; i
< mask_table_size
; ++i
) {
248 for (k
= 0; k
< modmap
->max_keypermod
; ++k
) {
249 if (*kp
== e
->xkey
.keycode
) { /* found the keycode */
250 /* remove the mask for it */
251 e
->xkey
.state
&= ~mask_table
[i
];
252 /* cause the first loop to break; */
254 break; /* get outta here! */
261 STRIP_MODS(e
->xmotion
.state
);
262 /* compress events */
265 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
267 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
268 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
275 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
279 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
280 because of RevertToPointerRoot. If the focus ends up reverting to
281 pointer root on a workspace change, then the FocusIn event that we
282 want will be of type NotifyAncestor. This situation does not occur
283 for FocusOut, so it is safely ignored there.
285 if (INVALID_FOCUSIN(e
) ||
288 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
289 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
291 /* says a client was not found for the event (or a valid FocusIn
294 e
->xfocus
.window
= None
;
299 ob_debug("FocusIn on %lx mode %d detail %d\n", e
->xfocus
.window
,
300 e
->xfocus
.mode
, e
->xfocus
.detail
);
304 if (INVALID_FOCUSOUT(e
)) {
306 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
307 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
313 ob_debug("FocusOut on %lx mode %d detail %d\n",
314 e
->xfocus
.window
, e
->xfocus
.mode
, e
->xfocus
.detail
);
319 gboolean fallback
= TRUE
;
322 if (!XCheckTypedWindowEvent(ob_display
, FocusOut
,
323 e
->xfocus
.window
,&fe
))
324 if (!XCheckTypedEvent(ob_display
, FocusIn
, &fe
))
326 if (fe
.type
== FocusOut
) {
328 ob_debug("found pending FocusOut");
330 if (!INVALID_FOCUSOUT(&fe
)) {
331 /* if there is a VALID FocusOut still coming, don't
332 fallback focus yet, we'll deal with it then */
333 XPutBackEvent(ob_display
, &fe
);
339 ob_debug("found pending FocusIn");
341 /* is the focused window getting a FocusOut/In back to
344 if (fe
.xfocus
.window
== e
->xfocus
.window
&&
345 !event_ignore(&fe
, client
)) {
347 if focus_client is not set, then we can't do
348 this. we need the FocusIn. This happens in the
349 case when the set_focus_client(NULL) in the
350 focus_fallback function fires and then
351 focus_fallback picks the currently focused
352 window (such as on a SendToDesktop-esque action.
356 ob_debug("focused window got an Out/In back to "
357 "itself IGNORED both");
361 event_process(&fe
, NULL
);
363 ob_debug("focused window got an Out/In back to "
364 "itself but focus_client was null "
365 "IGNORED just the Out");
371 /* once all the FocusOut's have been dealt with, if there
372 is a FocusIn still left and it is valid, then use it */
373 event_process(&fe
, NULL
);
374 /* secret magic way of event_process telling us that no
375 client was found for the FocusIn event. ^_^ */
376 if (fe
.xfocus
.window
!= None
) {
384 ob_debug("no valid FocusIn and no FocusOut events found, "
387 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS
);
393 /* NotifyUngrab occurs when a mouse button is released and the event is
394 caused, like when lowering a window */
395 /* NotifyVirtual occurs when ungrabbing the pointer */
396 if (e
->xcrossing
.mode
== NotifyGrab
||
397 e
->xcrossing
.detail
== NotifyInferior
||
398 (e
->xcrossing
.mode
== NotifyUngrab
&&
399 e
->xcrossing
.detail
== NotifyVirtual
)) {
401 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
402 (e
->type
== EnterNotify
? "Enter" : "Leave"),
404 e
->xcrossing
.detail
, client
?client
->window
:0);
409 ob_debug("%sNotify mode %d detail %d on %lx",
410 (e
->type
== EnterNotify
? "Enter" : "Leave"),
412 e
->xcrossing
.detail
, client
?client
->window
:0);
419 static void event_process(const XEvent
*ec
, gpointer data
)
422 ObClient
*client
= NULL
;
424 ObDockApp
*dockapp
= NULL
;
425 ObWindow
*obwin
= NULL
;
428 /* make a copy we can mangle */
432 window
= event_get_window(e
);
433 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
434 switch (obwin
->type
) {
436 dock
= WINDOW_AS_DOCK(obwin
);
439 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
442 client
= WINDOW_AS_CLIENT(obwin
);
445 case Window_Internal
:
446 /* not to be used for events */
447 g_assert_not_reached();
452 event_set_lasttime(e
);
454 if (event_ignore(e
, client
))
457 /* deal with it in the kernel */
459 event_handle_client(client
, e
);
461 event_handle_dockapp(dockapp
, e
);
463 event_handle_dock(dock
, e
);
464 else if (window
== RootWindow(ob_display
, ob_screen
))
465 event_handle_root(e
);
466 else if (e
->type
== MapRequest
)
467 client_manage(window
);
468 else if (e
->type
== ConfigureRequest
) {
469 /* unhandled configure requests must be used to configure the
473 xwc
.x
= e
->xconfigurerequest
.x
;
474 xwc
.y
= e
->xconfigurerequest
.y
;
475 xwc
.width
= e
->xconfigurerequest
.width
;
476 xwc
.height
= e
->xconfigurerequest
.height
;
477 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
478 xwc
.sibling
= e
->xconfigurerequest
.above
;
479 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
481 /* we are not to be held responsible if someone sends us an
483 xerror_set_ignore(TRUE
);
484 XConfigureWindow(ob_display
, window
,
485 e
->xconfigurerequest
.value_mask
, &xwc
);
486 xerror_set_ignore(FALSE
);
489 /* user input (action-bound) events */
490 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
491 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
492 e
->type
== KeyRelease
)
494 if (menu_frame_visible
)
495 event_handle_menu(e
);
496 else if (moveresize_in_progress
)
499 ObFrameContext context
;
501 context
= frame_context(client
, e
->xany
.window
);
503 if (!keyboard_process_interactive_grab(e
, &client
, &context
)) {
504 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
505 e
->type
== MotionNotify
)
506 mouse_event(client
, context
, e
);
507 else if (e
->type
== KeyPress
)
508 keyboard_event(client
, e
);
514 static void event_handle_root(XEvent
*e
)
520 ob_debug("Another WM has requested to replace us. Exiting.\n");
525 if (e
->xclient
.format
!= 32) break;
527 msgtype
= e
->xclient
.message_type
;
528 if (msgtype
== prop_atoms
.net_current_desktop
) {
529 unsigned int d
= e
->xclient
.data
.l
[0];
530 if (d
< screen_num_desktops
)
531 screen_set_desktop(d
);
532 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
533 unsigned int d
= e
->xclient
.data
.l
[0];
535 screen_set_num_desktops(d
);
536 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
537 screen_show_desktop(e
->xclient
.data
.l
[0] != 0);
541 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
542 screen_update_desktop_names();
543 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
544 screen_update_layout();
546 case ConfigureNotify
:
548 XRRUpdateConfiguration(e
);
555 if (extensions_vidmode
&& e
->type
== extensions_vidmode_event_basep
) {
556 ob_debug("VIDMODE EVENT\n");
562 static void event_handle_client(ObClient
*client
, XEvent
*e
)
570 case VisibilityNotify
:
571 client
->frame
->obscured
= e
->xvisibility
.state
!= VisibilityUnobscured
;
575 /* Wheel buttons don't draw because they are an instant click, so it
576 is a waste of resources to go drawing it. */
577 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
578 switch (frame_context(client
, e
->xbutton
.window
)) {
579 case OB_FRAME_CONTEXT_MAXIMIZE
:
580 client
->frame
->max_press
= (e
->type
== ButtonPress
);
581 framerender_frame(client
->frame
);
583 case OB_FRAME_CONTEXT_CLOSE
:
584 client
->frame
->close_press
= (e
->type
== ButtonPress
);
585 framerender_frame(client
->frame
);
587 case OB_FRAME_CONTEXT_ICONIFY
:
588 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
589 framerender_frame(client
->frame
);
591 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
592 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
593 framerender_frame(client
->frame
);
595 case OB_FRAME_CONTEXT_SHADE
:
596 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
597 framerender_frame(client
->frame
);
600 /* nothing changes with clicks for any other contexts */
607 ob_debug("FocusIn on client for %lx\n", client
->window
);
609 if (client
!= focus_client
) {
610 focus_set_client(client
);
611 frame_adjust_focus(client
->frame
, TRUE
);
616 ob_debug("FocusOut on client for %lx\n", client
->window
);
618 /* are we a fullscreen window or a transient of one? (checks layer)
619 if we are then we need to be iconified since we are losing focus
621 if (client
->layer
== OB_STACKING_LAYER_FULLSCREEN
&& !client
->iconic
&&
622 !client_search_focus_tree_full(client
))
623 /* iconify fullscreen windows when they and their transients
625 client_iconify(client
, TRUE
, TRUE
);
626 frame_adjust_focus(client
->frame
, FALSE
);
629 con
= frame_context(client
, e
->xcrossing
.window
);
631 case OB_FRAME_CONTEXT_MAXIMIZE
:
632 client
->frame
->max_hover
= FALSE
;
633 frame_adjust_state(client
->frame
);
635 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
636 client
->frame
->desk_hover
= FALSE
;
637 frame_adjust_state(client
->frame
);
639 case OB_FRAME_CONTEXT_SHADE
:
640 client
->frame
->shade_hover
= FALSE
;
641 frame_adjust_state(client
->frame
);
643 case OB_FRAME_CONTEXT_ICONIFY
:
644 client
->frame
->iconify_hover
= FALSE
;
645 frame_adjust_state(client
->frame
);
647 case OB_FRAME_CONTEXT_CLOSE
:
648 client
->frame
->close_hover
= FALSE
;
649 frame_adjust_state(client
->frame
);
656 con
= frame_context(client
, e
->xcrossing
.window
);
658 case OB_FRAME_CONTEXT_MAXIMIZE
:
659 client
->frame
->max_hover
= TRUE
;
660 frame_adjust_state(client
->frame
);
662 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
663 client
->frame
->desk_hover
= TRUE
;
664 frame_adjust_state(client
->frame
);
666 case OB_FRAME_CONTEXT_SHADE
:
667 client
->frame
->shade_hover
= TRUE
;
668 frame_adjust_state(client
->frame
);
670 case OB_FRAME_CONTEXT_ICONIFY
:
671 client
->frame
->iconify_hover
= TRUE
;
672 frame_adjust_state(client
->frame
);
674 case OB_FRAME_CONTEXT_CLOSE
:
675 client
->frame
->close_hover
= TRUE
;
676 frame_adjust_state(client
->frame
);
678 case OB_FRAME_CONTEXT_FRAME
:
679 if (client_normal(client
)) {
680 if (ob_state() == OB_STATE_STARTING
) {
681 /* move it to the top of the focus order */
682 guint desktop
= client
->desktop
;
683 if (desktop
== DESKTOP_ALL
) desktop
= screen_desktop
;
684 focus_order
[desktop
] = g_list_remove(focus_order
[desktop
],
686 focus_order
[desktop
] = g_list_prepend(focus_order
[desktop
],
688 } else if (config_focus_follow
) {
690 ob_debug("EnterNotify on %lx, focusing window\n",
693 client_focus(client
);
701 case ConfigureRequest
:
703 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
704 ConfigureRequest
, &ce
)) {
706 /* XXX if this causes bad things.. we can compress config req's
707 with the same mask. */
708 e
->xconfigurerequest
.value_mask
|=
709 ce
.xconfigurerequest
.value_mask
;
710 if (ce
.xconfigurerequest
.value_mask
& CWX
)
711 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
712 if (ce
.xconfigurerequest
.value_mask
& CWY
)
713 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
714 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
715 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
716 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
717 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
718 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
719 e
->xconfigurerequest
.border_width
=
720 ce
.xconfigurerequest
.border_width
;
721 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
722 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
725 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
726 if (client
->iconic
|| client
->shaded
) return;
728 /* resize, then move, as specified in the EWMH section 7.7 */
729 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
735 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
736 client
->border_width
= e
->xconfigurerequest
.border_width
;
738 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
739 e
->xconfigurerequest
.x
: client
->area
.x
;
740 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
741 e
->xconfigurerequest
.y
: client
->area
.y
;
742 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
743 e
->xconfigurerequest
.width
: client
->area
.width
;
744 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
745 e
->xconfigurerequest
.height
: client
->area
.height
;
751 client
->frame
->size
.left
+ client
->frame
->size
.right
;
753 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
754 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
755 client_normal(client
));
756 if (e
->xconfigurerequest
.value_mask
& CWX
)
758 if (e
->xconfigurerequest
.value_mask
& CWY
)
762 switch (client
->gravity
) {
763 case NorthEastGravity
:
765 corner
= OB_CORNER_TOPRIGHT
;
767 case SouthWestGravity
:
769 corner
= OB_CORNER_BOTTOMLEFT
;
771 case SouthEastGravity
:
772 corner
= OB_CORNER_BOTTOMRIGHT
;
774 default: /* NorthWest, Static, etc */
775 corner
= OB_CORNER_TOPLEFT
;
778 client_configure_full(client
, corner
, x
, y
, w
, h
, FALSE
, TRUE
,
782 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
783 switch (e
->xconfigurerequest
.detail
) {
786 stacking_lower(CLIENT_AS_WINDOW(client
));
792 stacking_raise(CLIENT_AS_WINDOW(client
));
798 if (client
->ignore_unmaps
) {
799 client
->ignore_unmaps
--;
802 client_unmanage(client
);
805 client_unmanage(client
);
808 /* this is when the client is first taken captive in the frame */
809 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
812 This event is quite rare and is usually handled in unmapHandler.
813 However, if the window is unmapped when the reparent event occurs,
814 the window manager never sees it because an unmap event is not sent
815 to an already unmapped window.
818 /* we don't want the reparent event, put it back on the stack for the
819 X server to deal with after we unmanage the window */
820 XPutBackEvent(ob_display
, e
);
822 client_unmanage(client
);
825 ob_debug("MapRequest for 0x%lx\n", client
->window
);
826 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
827 does, we don't want it! */
828 if (screen_showing_desktop
)
829 screen_show_desktop(FALSE
);
830 client_iconify(client
, FALSE
, TRUE
);
831 if (!client
->frame
->visible
)
832 /* if its not visible still, then don't mess with it */
835 client_shade(client
, FALSE
);
836 client_focus(client
);
837 stacking_raise(CLIENT_AS_WINDOW(client
));
840 /* validate cuz we query stuff off the client here */
841 if (!client_validate(client
)) break;
843 if (e
->xclient
.format
!= 32) return;
845 msgtype
= e
->xclient
.message_type
;
846 if (msgtype
== prop_atoms
.wm_change_state
) {
847 /* compress changes into a single change */
848 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
849 client
->window
, &ce
)) {
850 /* XXX: it would be nice to compress ALL messages of a
851 type, not just messages in a row without other
852 message types between. */
853 if (ce
.xclient
.message_type
!= msgtype
) {
854 XPutBackEvent(ob_display
, &ce
);
857 e
->xclient
= ce
.xclient
;
859 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
860 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
861 /* compress changes into a single change */
862 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
863 client
->window
, &ce
)) {
864 /* XXX: it would be nice to compress ALL messages of a
865 type, not just messages in a row without other
866 message types between. */
867 if (ce
.xclient
.message_type
!= msgtype
) {
868 XPutBackEvent(ob_display
, &ce
);
871 e
->xclient
= ce
.xclient
;
873 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
874 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
875 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
877 } else if (msgtype
== prop_atoms
.net_wm_state
) {
878 /* can't compress these */
879 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
880 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
881 e
->xclient
.data
.l
[0] == 1 ? "Add" :
882 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
883 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
885 client_set_state(client
, e
->xclient
.data
.l
[0],
886 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
887 } else if (msgtype
== prop_atoms
.net_close_window
) {
888 ob_debug("net_close_window for 0x%lx\n", client
->window
);
889 client_close(client
);
890 } else if (msgtype
== prop_atoms
.net_active_window
) {
891 ob_debug("net_active_window for 0x%lx\n", client
->window
);
892 client_activate(client
, FALSE
);
893 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
894 ob_debug("net_wm_moveresize for 0x%lx\n", client
->window
);
895 if ((Atom
)e
->xclient
.data
.l
[2] ==
896 prop_atoms
.net_wm_moveresize_size_topleft
||
897 (Atom
)e
->xclient
.data
.l
[2] ==
898 prop_atoms
.net_wm_moveresize_size_top
||
899 (Atom
)e
->xclient
.data
.l
[2] ==
900 prop_atoms
.net_wm_moveresize_size_topright
||
901 (Atom
)e
->xclient
.data
.l
[2] ==
902 prop_atoms
.net_wm_moveresize_size_right
||
903 (Atom
)e
->xclient
.data
.l
[2] ==
904 prop_atoms
.net_wm_moveresize_size_right
||
905 (Atom
)e
->xclient
.data
.l
[2] ==
906 prop_atoms
.net_wm_moveresize_size_bottomright
||
907 (Atom
)e
->xclient
.data
.l
[2] ==
908 prop_atoms
.net_wm_moveresize_size_bottom
||
909 (Atom
)e
->xclient
.data
.l
[2] ==
910 prop_atoms
.net_wm_moveresize_size_bottomleft
||
911 (Atom
)e
->xclient
.data
.l
[2] ==
912 prop_atoms
.net_wm_moveresize_size_left
||
913 (Atom
)e
->xclient
.data
.l
[2] ==
914 prop_atoms
.net_wm_moveresize_move
||
915 (Atom
)e
->xclient
.data
.l
[2] ==
916 prop_atoms
.net_wm_moveresize_size_keyboard
||
917 (Atom
)e
->xclient
.data
.l
[2] ==
918 prop_atoms
.net_wm_moveresize_move_keyboard
) {
920 moveresize_start(client
, e
->xclient
.data
.l
[0],
921 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
922 e
->xclient
.data
.l
[2]);
924 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
925 int oldg
= client
->gravity
;
926 int tmpg
, x
, y
, w
, h
;
928 if (e
->xclient
.data
.l
[0] & 0xff)
929 tmpg
= e
->xclient
.data
.l
[0] & 0xff;
933 if (e
->xclient
.data
.l
[0] & 1 << 8)
934 x
= e
->xclient
.data
.l
[1];
937 if (e
->xclient
.data
.l
[0] & 1 << 9)
938 y
= e
->xclient
.data
.l
[2];
941 if (e
->xclient
.data
.l
[0] & 1 << 10)
942 w
= e
->xclient
.data
.l
[3];
944 w
= client
->area
.width
;
945 if (e
->xclient
.data
.l
[0] & 1 << 11)
946 h
= e
->xclient
.data
.l
[4];
948 h
= client
->area
.height
;
949 client
->gravity
= tmpg
;
955 client
->frame
->size
.left
+ client
->frame
->size
.right
;
957 client
->frame
->size
.top
+ client
->frame
->size
.bottom
;
958 client_find_onscreen(client
, &newx
, &newy
, fw
, fh
,
959 client_normal(client
));
960 if (e
->xclient
.data
.l
[0] & 1 << 8)
962 if (e
->xclient
.data
.l
[0] & 1 << 9)
966 client_configure(client
, OB_CORNER_TOPLEFT
,
967 x
, y
, w
, h
, FALSE
, TRUE
);
969 client
->gravity
= oldg
;
973 /* validate cuz we query stuff off the client here */
974 if (!client_validate(client
)) break;
976 /* compress changes to a single property into a single change */
977 while (XCheckTypedWindowEvent(ob_display
, e
->type
,
978 client
->window
, &ce
)) {
979 /* XXX: it would be nice to compress ALL changes to a property,
980 not just changes in a row without other props between. */
981 if (ce
.xproperty
.atom
!= e
->xproperty
.atom
) {
982 XPutBackEvent(ob_display
, &ce
);
987 msgtype
= e
->xproperty
.atom
;
988 if (msgtype
== XA_WM_NORMAL_HINTS
) {
989 client_update_normal_hints(client
);
990 /* normal hints can make a window non-resizable */
991 client_setup_decor_and_functions(client
);
993 else if (msgtype
== XA_WM_HINTS
)
994 client_update_wmhints(client
);
995 else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
996 client_update_transient_for(client
);
997 client_get_type(client
);
998 /* type may have changed, so update the layer */
999 client_calc_layer(client
);
1000 client_setup_decor_and_functions(client
);
1002 else if (msgtype
== prop_atoms
.net_wm_name
||
1003 msgtype
== prop_atoms
.wm_name
||
1004 msgtype
== prop_atoms
.net_wm_icon_name
||
1005 msgtype
== prop_atoms
.wm_icon_name
)
1006 client_update_title(client
);
1007 else if (msgtype
== prop_atoms
.wm_class
)
1008 client_update_class(client
);
1009 else if (msgtype
== prop_atoms
.wm_protocols
) {
1010 client_update_protocols(client
);
1011 client_setup_decor_and_functions(client
);
1013 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1014 client_update_strut(client
);
1016 else if (msgtype
== prop_atoms
.net_wm_icon
||
1017 msgtype
== prop_atoms
.kwm_win_icon
)
1018 client_update_icons(client
);
1022 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1023 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1024 frame_adjust_shape(client
->frame
);
1030 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1034 stacking_raise(DOCK_AS_WINDOW(s
));
1045 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1049 dock_app_drag(app
, &e
->xmotion
);
1052 if (app
->ignore_unmaps
) {
1053 app
->ignore_unmaps
--;
1056 dock_remove(app
, TRUE
);
1059 dock_remove(app
, FALSE
);
1061 case ReparentNotify
:
1062 dock_remove(app
, FALSE
);
1064 case ConfigureNotify
:
1065 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1070 ObMenuFrame
* find_active_menu()
1075 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1080 return it
? it
->data
: NULL
;
1083 static void event_handle_menu(XEvent
*ev
)
1086 ObMenuEntryFrame
*e
;
1090 if (!(f
= menu_frame_under(ev
->xbutton
.x_root
,
1091 ev
->xbutton
.y_root
)))
1092 menu_frame_hide_all();
1094 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1095 ev
->xbutton
.y_root
)))
1096 menu_entry_frame_execute(e
,
1097 !(ev
->xbutton
.state
& ControlMask
));
1101 if ((f
= menu_frame_under(ev
->xmotion
.x_root
,
1102 ev
->xmotion
.y_root
))) {
1103 menu_frame_move_on_screen(f
);
1104 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1105 ev
->xmotion
.y_root
)))
1106 menu_frame_select(f
, e
);
1110 if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_ESCAPE
))
1111 menu_frame_hide_all();
1112 else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RETURN
)) {
1114 if ((f
= find_active_menu()))
1115 menu_entry_frame_execute(f
->selected
,
1116 !(ev
->xkey
.state
& ControlMask
));
1117 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_LEFT
)) {
1119 if ((f
= find_active_menu()) && f
->parent
)
1120 menu_frame_select(f
, NULL
);
1121 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_RIGHT
)) {
1123 if ((f
= find_active_menu()) && f
->child
)
1124 menu_frame_select_next(f
->child
);
1125 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_UP
)) {
1127 if ((f
= find_active_menu()))
1128 menu_frame_select_previous(f
);
1129 } else if (ev
->xkey
.keycode
== ob_keycode(OB_KEY_DOWN
)) {
1131 if ((f
= find_active_menu()))
1132 menu_frame_select_next(f
);