1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
32 #include "menuframe.h"
37 #include "framerender.h"
39 #include "moveresize.h"
42 #include "extensions.h"
43 #include "translate.h"
46 #include <X11/keysym.h>
47 #include <X11/Xatom.h>
50 #ifdef HAVE_SYS_SELECT_H
51 # include <sys/select.h>
57 # include <unistd.h> /* for usleep() */
60 # include <X11/XKBlib.h>
64 #include <X11/ICE/ICElib.h>
78 static void event_process(const XEvent
*e
, gpointer data
);
79 static void event_handle_root(XEvent
*e
);
80 static gboolean
event_handle_menu_keyboard(XEvent
*e
);
81 static gboolean
event_handle_menu(XEvent
*e
);
82 static void event_handle_dock(ObDock
*s
, XEvent
*e
);
83 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
);
84 static void event_handle_client(ObClient
*c
, XEvent
*e
);
85 static void event_handle_group(ObGroup
*g
, XEvent
*e
);
86 static void event_handle_user_input(ObClient
*client
, XEvent
*e
);
88 static void focus_delay_dest(gpointer data
);
89 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
);
90 static gboolean
focus_delay_func(gpointer data
);
91 static void focus_delay_client_dest(ObClient
*client
, gpointer data
);
93 static gboolean
menu_hide_delay_func(gpointer data
);
95 /* The time for the current event being processed */
96 Time event_curtime
= CurrentTime
;
98 static guint ignore_enter_focus
= 0;
99 static gboolean menu_can_hide
;
100 static gboolean focus_left_screen
= FALSE
;
103 static void ice_handler(gint fd
, gpointer conn
)
106 IceProcessMessages(conn
, NULL
, &b
);
109 static void ice_watch(IceConn conn
, IcePointer data
, Bool opening
,
110 IcePointer
*watch_data
)
115 fd
= IceConnectionNumber(conn
);
116 ob_main_loop_fd_add(ob_main_loop
, fd
, ice_handler
, conn
, NULL
);
118 ob_main_loop_fd_remove(ob_main_loop
, fd
);
124 void event_startup(gboolean reconfig
)
126 if (reconfig
) return;
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
, NULL
);
137 void event_shutdown(gboolean reconfig
)
139 if (reconfig
) return;
142 IceRemoveConnectionWatch(ice_watch
, NULL
);
145 client_remove_destructor(focus_delay_client_dest
);
148 static Window
event_get_window(XEvent
*e
)
155 window
= RootWindow(ob_display
, ob_screen
);
158 window
= e
->xmap
.window
;
161 window
= e
->xunmap
.window
;
164 window
= e
->xdestroywindow
.window
;
166 case ConfigureRequest
:
167 window
= e
->xconfigurerequest
.window
;
169 case ConfigureNotify
:
170 window
= e
->xconfigure
.window
;
174 if (extensions_xkb
&& e
->type
== extensions_xkb_event_basep
) {
175 switch (((XkbAnyEvent
*)e
)->xkb_type
) {
177 window
= ((XkbBellNotifyEvent
*)e
)->window
;
184 if (extensions_sync
&&
185 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
190 window
= e
->xany
.window
;
195 static void event_set_curtime(XEvent
*e
)
197 Time t
= CurrentTime
;
199 /* grab the lasttime and hack up the state */
215 t
= e
->xproperty
.time
;
219 t
= e
->xcrossing
.time
;
223 if (extensions_sync
&&
224 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
226 t
= ((XSyncAlarmNotifyEvent
*)e
)->time
;
229 /* if more event types are anticipated, get their timestamp
237 static void event_hack_mods(XEvent
*e
)
240 XkbStateRec xkb_state
;
246 e
->xbutton
.state
= modkeys_only_modifier_masks(e
->xbutton
.state
);
249 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
252 e
->xkey
.state
= modkeys_only_modifier_masks(e
->xkey
.state
);
254 if (XkbGetState(ob_display
, XkbUseCoreKbd
, &xkb_state
) == Success
) {
255 e
->xkey
.state
= xkb_state
.compat_state
;
259 /* remove from the state the mask of the modifier key being released,
260 if it is a modifier key being released that is */
261 e
->xkey
.state
&= ~modkeys_keycode_to_mask(e
->xkey
.keycode
);
264 e
->xmotion
.state
= modkeys_only_modifier_masks(e
->xmotion
.state
);
265 /* compress events */
268 while (XCheckTypedWindowEvent(ob_display
, e
->xmotion
.window
,
270 e
->xmotion
.x_root
= ce
.xmotion
.x_root
;
271 e
->xmotion
.y_root
= ce
.xmotion
.y_root
;
278 static gboolean
wanted_focusevent(XEvent
*e
)
280 gint mode
= e
->xfocus
.mode
;
281 gint detail
= e
->xfocus
.detail
;
282 Window win
= e
->xany
.window
;
284 if (e
->type
== FocusIn
) {
286 /* These are ones we never want.. */
288 /* This means focus was given by a keyboard/mouse grab. */
289 if (mode
== NotifyGrab
)
291 /* This means focus was given back from a keyboard/mouse grab. */
292 if (mode
== NotifyUngrab
)
295 /* These are the ones we want.. */
297 if (win
== RootWindow(ob_display
, ob_screen
)) {
298 /* This means focus reverted off of a client */
299 if (detail
== NotifyPointerRoot
|| detail
== NotifyDetailNone
||
300 detail
== NotifyInferior
)
306 /* This means focus moved from the root window to a client */
307 if (detail
== NotifyVirtual
)
309 /* This means focus moved from one client to another */
310 if (detail
== NotifyNonlinearVirtual
)
316 g_assert(e
->type
== FocusOut
);
319 /* These are ones we never want.. */
321 /* This means focus was taken by a keyboard/mouse grab. */
322 if (mode
== NotifyGrab
)
325 /* Focus left the root window revertedto state */
326 if (win
== RootWindow(ob_display
, ob_screen
))
329 /* These are the ones we want.. */
331 /* This means focus moved from a client to the root window */
332 if (detail
== NotifyVirtual
)
334 /* This means focus moved from one client to another */
335 if (detail
== NotifyNonlinearVirtual
)
337 /* This means focus had moved to our frame window and now moved off */
338 if (detail
== NotifyNonlinear
)
346 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
348 return e
->type
== FocusIn
&& wanted_focusevent(e
);
351 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
355 if (!wanted_focusevent(e
))
359 if (!wanted_focusevent(e
))
366 static void event_process(const XEvent
*ec
, gpointer data
)
369 ObGroup
*group
= NULL
;
370 ObClient
*client
= NULL
;
372 ObDockApp
*dockapp
= NULL
;
373 ObWindow
*obwin
= NULL
;
375 ObEventData
*ed
= data
;
377 /* make a copy we can mangle */
381 window
= event_get_window(e
);
382 if (!(e
->type
== PropertyNotify
&&
383 (group
= g_hash_table_lookup(group_map
, &window
))))
384 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
385 switch (obwin
->type
) {
387 dock
= WINDOW_AS_DOCK(obwin
);
390 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
393 client
= WINDOW_AS_CLIENT(obwin
);
396 case Window_Internal
:
397 /* not to be used for events */
398 g_assert_not_reached();
403 event_set_curtime(e
);
405 if (event_ignore(e
, client
)) {
412 /* deal with it in the kernel */
414 if (menu_frame_visible
&&
415 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
417 /* crossing events for menu */
418 event_handle_menu(e
);
419 } else if (e
->type
== FocusIn
) {
420 if (e
->xfocus
.detail
== NotifyPointerRoot
||
421 e
->xfocus
.detail
== NotifyDetailNone
)
423 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to pointer root/none\n");
424 /* Focus has been reverted to the root window or nothing.
425 FocusOut events come after UnmapNotify, so we don't need to
426 worry about focusing an invalid window
428 if (!focus_left_screen
)
429 focus_fallback(TRUE
);
430 } else if (e
->xfocus
.detail
== NotifyInferior
) {
431 ob_debug_type(OB_DEBUG_FOCUS
,
432 "Focus went to root or our frame window");
433 /* Focus has been given to the root window. */
434 focus_fallback(TRUE
);
435 } else if (client
&& client
!= focus_client
) {
436 focus_left_screen
= FALSE
;
437 frame_adjust_focus(client
->frame
, TRUE
);
438 focus_set_client(client
);
439 client_calc_layer(client
);
441 } else if (e
->type
== FocusOut
) {
442 gboolean nomove
= FALSE
;
445 ob_debug_type(OB_DEBUG_FOCUS
, "FocusOut Event\n");
447 /* Look for the followup FocusIn */
448 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
449 /* There is no FocusIn, this means focus went to a window that
450 is not being managed, or a window on another screen. */
454 xerror_set_ignore(TRUE
);
455 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
456 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
457 root
!= RootWindow(ob_display
, ob_screen
))
459 ob_debug_type(OB_DEBUG_FOCUS
,
460 "Focus went to another screen !\n");
461 focus_left_screen
= TRUE
;
464 ob_debug_type(OB_DEBUG_FOCUS
,
465 "Focus went to a black hole !\n");
466 xerror_set_ignore(FALSE
);
467 /* nothing is focused */
468 focus_set_client(NULL
);
469 } else if (ce
.xany
.window
== e
->xany
.window
) {
470 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
471 /* If focus didn't actually move anywhere, there is nothing to do*/
474 /* Focus did move, so process the FocusIn event */
475 ObEventData ed
= { .ignored
= FALSE
};
476 event_process(&ce
, &ed
);
478 /* The FocusIn was ignored, this means it was on a window
479 that isn't a client. */
480 ob_debug_type(OB_DEBUG_FOCUS
,
481 "Focus went to an unmanaged window 0x%x !\n",
483 focus_fallback(TRUE
);
487 if (client
&& !nomove
) {
488 frame_adjust_focus(client
->frame
, FALSE
);
489 /* focus_set_client has already been called for sure */
490 client_calc_layer(client
);
493 event_handle_group(group
, e
);
495 event_handle_client(client
, e
);
497 event_handle_dockapp(dockapp
, e
);
499 event_handle_dock(dock
, e
);
500 else if (window
== RootWindow(ob_display
, ob_screen
))
501 event_handle_root(e
);
502 else if (e
->type
== MapRequest
)
503 client_manage(window
);
504 else if (e
->type
== ConfigureRequest
) {
505 /* unhandled configure requests must be used to configure the
509 xwc
.x
= e
->xconfigurerequest
.x
;
510 xwc
.y
= e
->xconfigurerequest
.y
;
511 xwc
.width
= e
->xconfigurerequest
.width
;
512 xwc
.height
= e
->xconfigurerequest
.height
;
513 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
514 xwc
.sibling
= e
->xconfigurerequest
.above
;
515 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
517 /* we are not to be held responsible if someone sends us an
519 xerror_set_ignore(TRUE
);
520 XConfigureWindow(ob_display
, window
,
521 e
->xconfigurerequest
.value_mask
, &xwc
);
522 xerror_set_ignore(FALSE
);
525 else if (extensions_sync
&&
526 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
528 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
529 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
534 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
535 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
536 e
->type
== KeyRelease
)
538 event_handle_user_input(client
, e
);
541 /* if something happens and it's not from an XEvent, then we don't know
543 event_curtime
= CurrentTime
;
546 static void event_handle_root(XEvent
*e
)
552 ob_debug("Another WM has requested to replace us. Exiting.\n");
557 if (e
->xclient
.format
!= 32) break;
559 msgtype
= e
->xclient
.message_type
;
560 if (msgtype
== prop_atoms
.net_current_desktop
) {
561 guint d
= e
->xclient
.data
.l
[0];
562 if (d
< screen_num_desktops
) {
563 event_curtime
= e
->xclient
.data
.l
[1];
564 ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime
);
565 screen_set_desktop(d
);
567 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
568 guint d
= e
->xclient
.data
.l
[0];
570 screen_set_num_desktops(d
);
571 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
572 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, TRUE
);
573 } else if (msgtype
== prop_atoms
.ob_control
) {
574 if (e
->xclient
.data
.l
[0] == 1)
576 else if (e
->xclient
.data
.l
[0] == 2)
581 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
582 screen_update_desktop_names();
583 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
584 screen_update_layout();
586 case ConfigureNotify
:
588 XRRUpdateConfiguration(e
);
597 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
601 g_assert(e
->type
== PropertyNotify
);
603 for (it
= group
->members
; it
; it
= g_slist_next(it
))
604 event_handle_client(it
->data
, e
);
607 void event_enter_client(ObClient
*client
)
609 g_assert(config_focus_follow
);
611 if (client_normal(client
) && client_can_focus(client
)) {
612 if (config_focus_delay
) {
613 ObFocusDelayData
*data
;
615 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
617 data
= g_new(ObFocusDelayData
, 1);
618 data
->client
= client
;
619 data
->time
= event_curtime
;
621 ob_main_loop_timeout_add(ob_main_loop
,
624 data
, focus_delay_cmp
, focus_delay_dest
);
626 ObFocusDelayData data
;
627 data
.client
= client
;
628 data
.time
= event_curtime
;
629 focus_delay_func(&data
);
634 static void event_handle_client(ObClient
*client
, XEvent
*e
)
644 /* Wheel buttons don't draw because they are an instant click, so it
645 is a waste of resources to go drawing it. */
646 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
647 con
= frame_context(client
, e
->xbutton
.window
);
648 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
650 case OB_FRAME_CONTEXT_MAXIMIZE
:
651 client
->frame
->max_press
= (e
->type
== ButtonPress
);
652 framerender_frame(client
->frame
);
654 case OB_FRAME_CONTEXT_CLOSE
:
655 client
->frame
->close_press
= (e
->type
== ButtonPress
);
656 framerender_frame(client
->frame
);
658 case OB_FRAME_CONTEXT_ICONIFY
:
659 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
660 framerender_frame(client
->frame
);
662 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
663 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
664 framerender_frame(client
->frame
);
666 case OB_FRAME_CONTEXT_SHADE
:
667 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
668 framerender_frame(client
->frame
);
671 /* nothing changes with clicks for any other contexts */
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 /* When the mouse leaves an animating window, don't use the
701 corresponding enter events. Pretend like the animating window
702 doesn't even exist..! */
703 if (frame_iconify_animating(client
->frame
))
704 event_ignore_queued_enters();
706 ob_debug_type(OB_DEBUG_FOCUS
,
707 "%sNotify mode %d detail %d on %lx\n",
708 (e
->type
== EnterNotify
? "Enter" : "Leave"),
710 e
->xcrossing
.detail
, (client
?client
->window
:0));
711 if (keyboard_interactively_grabbed())
713 if (config_focus_follow
&& config_focus_delay
&&
714 /* leave inferior events can happen when the mouse goes onto
715 the window's border and then into the window before the
717 e
->xcrossing
.detail
!= NotifyInferior
)
719 ob_main_loop_timeout_remove_data(ob_main_loop
,
730 gboolean nofocus
= FALSE
;
732 if (ignore_enter_focus
) {
733 ignore_enter_focus
--;
737 con
= frame_context(client
, e
->xcrossing
.window
);
739 case OB_FRAME_CONTEXT_MAXIMIZE
:
740 client
->frame
->max_hover
= TRUE
;
741 frame_adjust_state(client
->frame
);
743 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
744 client
->frame
->desk_hover
= TRUE
;
745 frame_adjust_state(client
->frame
);
747 case OB_FRAME_CONTEXT_SHADE
:
748 client
->frame
->shade_hover
= TRUE
;
749 frame_adjust_state(client
->frame
);
751 case OB_FRAME_CONTEXT_ICONIFY
:
752 client
->frame
->iconify_hover
= TRUE
;
753 frame_adjust_state(client
->frame
);
755 case OB_FRAME_CONTEXT_CLOSE
:
756 client
->frame
->close_hover
= TRUE
;
757 frame_adjust_state(client
->frame
);
759 case OB_FRAME_CONTEXT_FRAME
:
760 if (keyboard_interactively_grabbed())
762 if (e
->xcrossing
.mode
== NotifyGrab
||
763 e
->xcrossing
.mode
== NotifyUngrab
||
764 /*ignore enters when we're already in the window */
765 e
->xcrossing
.detail
== NotifyInferior
)
767 ob_debug_type(OB_DEBUG_FOCUS
,
768 "%sNotify mode %d detail %d on %lx IGNORED\n",
769 (e
->type
== EnterNotify
? "Enter" : "Leave"),
771 e
->xcrossing
.detail
, client
?client
->window
:0);
773 ob_debug_type(OB_DEBUG_FOCUS
,
774 "%sNotify mode %d detail %d on %lx, "
775 "focusing window: %d\n",
776 (e
->type
== EnterNotify
? "Enter" : "Leave"),
778 e
->xcrossing
.detail
, (client
?client
->window
:0),
780 if (!nofocus
&& config_focus_follow
)
781 event_enter_client(client
);
789 case ConfigureRequest
:
791 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
792 ConfigureRequest
, &ce
)) {
794 /* XXX if this causes bad things.. we can compress config req's
795 with the same mask. */
796 e
->xconfigurerequest
.value_mask
|=
797 ce
.xconfigurerequest
.value_mask
;
798 if (ce
.xconfigurerequest
.value_mask
& CWX
)
799 e
->xconfigurerequest
.x
= ce
.xconfigurerequest
.x
;
800 if (ce
.xconfigurerequest
.value_mask
& CWY
)
801 e
->xconfigurerequest
.y
= ce
.xconfigurerequest
.y
;
802 if (ce
.xconfigurerequest
.value_mask
& CWWidth
)
803 e
->xconfigurerequest
.width
= ce
.xconfigurerequest
.width
;
804 if (ce
.xconfigurerequest
.value_mask
& CWHeight
)
805 e
->xconfigurerequest
.height
= ce
.xconfigurerequest
.height
;
806 if (ce
.xconfigurerequest
.value_mask
& CWBorderWidth
)
807 e
->xconfigurerequest
.border_width
=
808 ce
.xconfigurerequest
.border_width
;
809 if (ce
.xconfigurerequest
.value_mask
& CWStackMode
)
810 e
->xconfigurerequest
.detail
= ce
.xconfigurerequest
.detail
;
813 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
814 if (client
->iconic
|| client
->shaded
) return;
816 /* resize, then move, as specified in the EWMH section 7.7 */
817 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
822 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
823 client
->border_width
= e
->xconfigurerequest
.border_width
;
825 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
826 e
->xconfigurerequest
.x
: client
->area
.x
;
827 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
828 e
->xconfigurerequest
.y
: client
->area
.y
;
829 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
830 e
->xconfigurerequest
.width
: client
->area
.width
;
831 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
832 e
->xconfigurerequest
.height
: client
->area
.height
;
834 ob_debug("ConfigureRequest x %d %d y %d %d\n",
835 e
->xconfigurerequest
.value_mask
& CWX
, x
,
836 e
->xconfigurerequest
.value_mask
& CWY
, y
);
838 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
839 client_configure_full(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
842 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
843 switch (e
->xconfigurerequest
.detail
) {
846 /* Apps are so rude. And this is totally disconnected from
847 activation/focus. Bleh. */
848 /*client_lower(client);*/
854 /* Apps are so rude. And this is totally disconnected from
855 activation/focus. Bleh. */
856 /*client_raise(client);*/
862 if (client
->ignore_unmaps
) {
863 client
->ignore_unmaps
--;
866 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
868 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
869 client
->ignore_unmaps
);
870 client_unmanage(client
);
873 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
874 client_unmanage(client
);
877 /* this is when the client is first taken captive in the frame */
878 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
881 This event is quite rare and is usually handled in unmapHandler.
882 However, if the window is unmapped when the reparent event occurs,
883 the window manager never sees it because an unmap event is not sent
884 to an already unmapped window.
887 /* we don't want the reparent event, put it back on the stack for the
888 X server to deal with after we unmanage the window */
889 XPutBackEvent(ob_display
, e
);
891 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
892 client_unmanage(client
);
895 ob_debug("MapRequest for 0x%lx\n", client
->window
);
896 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
897 does, we don't want it!
898 it can happen now when the window is on
899 another desktop, but we still don't
901 client_activate(client
, FALSE
, TRUE
);
904 /* validate cuz we query stuff off the client here */
905 if (!client_validate(client
)) break;
907 if (e
->xclient
.format
!= 32) return;
909 msgtype
= e
->xclient
.message_type
;
910 if (msgtype
== prop_atoms
.wm_change_state
) {
911 /* compress changes into a single change */
912 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
914 /* XXX: it would be nice to compress ALL messages of a
915 type, not just messages in a row without other
916 message types between. */
917 if (ce
.xclient
.message_type
!= msgtype
) {
918 XPutBackEvent(ob_display
, &ce
);
921 e
->xclient
= ce
.xclient
;
923 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
924 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
925 /* compress changes into a single change */
926 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
928 /* XXX: it would be nice to compress ALL messages of a
929 type, not just messages in a row without other
930 message types between. */
931 if (ce
.xclient
.message_type
!= msgtype
) {
932 XPutBackEvent(ob_display
, &ce
);
935 e
->xclient
= ce
.xclient
;
937 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
938 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
939 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
941 } else if (msgtype
== prop_atoms
.net_wm_state
) {
942 /* can't compress these */
943 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
944 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
945 e
->xclient
.data
.l
[0] == 1 ? "Add" :
946 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
947 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
949 client_set_state(client
, e
->xclient
.data
.l
[0],
950 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
951 } else if (msgtype
== prop_atoms
.net_close_window
) {
952 ob_debug("net_close_window for 0x%lx\n", client
->window
);
953 client_close(client
);
954 } else if (msgtype
== prop_atoms
.net_active_window
) {
955 ob_debug("net_active_window for 0x%lx source=%s\n",
957 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
958 (e
->xclient
.data
.l
[0] == 1 ? "application" :
959 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
960 /* XXX make use of data.l[2] ! */
961 event_curtime
= e
->xclient
.data
.l
[1];
962 client_activate(client
, FALSE
,
963 (e
->xclient
.data
.l
[0] == 0 ||
964 e
->xclient
.data
.l
[0] == 2));
965 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
966 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
967 client
->window
, e
->xclient
.data
.l
[2]);
968 if ((Atom
)e
->xclient
.data
.l
[2] ==
969 prop_atoms
.net_wm_moveresize_size_topleft
||
970 (Atom
)e
->xclient
.data
.l
[2] ==
971 prop_atoms
.net_wm_moveresize_size_top
||
972 (Atom
)e
->xclient
.data
.l
[2] ==
973 prop_atoms
.net_wm_moveresize_size_topright
||
974 (Atom
)e
->xclient
.data
.l
[2] ==
975 prop_atoms
.net_wm_moveresize_size_right
||
976 (Atom
)e
->xclient
.data
.l
[2] ==
977 prop_atoms
.net_wm_moveresize_size_right
||
978 (Atom
)e
->xclient
.data
.l
[2] ==
979 prop_atoms
.net_wm_moveresize_size_bottomright
||
980 (Atom
)e
->xclient
.data
.l
[2] ==
981 prop_atoms
.net_wm_moveresize_size_bottom
||
982 (Atom
)e
->xclient
.data
.l
[2] ==
983 prop_atoms
.net_wm_moveresize_size_bottomleft
||
984 (Atom
)e
->xclient
.data
.l
[2] ==
985 prop_atoms
.net_wm_moveresize_size_left
||
986 (Atom
)e
->xclient
.data
.l
[2] ==
987 prop_atoms
.net_wm_moveresize_move
||
988 (Atom
)e
->xclient
.data
.l
[2] ==
989 prop_atoms
.net_wm_moveresize_size_keyboard
||
990 (Atom
)e
->xclient
.data
.l
[2] ==
991 prop_atoms
.net_wm_moveresize_move_keyboard
) {
993 moveresize_start(client
, e
->xclient
.data
.l
[0],
994 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
995 e
->xclient
.data
.l
[2]);
997 else if ((Atom
)e
->xclient
.data
.l
[2] ==
998 prop_atoms
.net_wm_moveresize_cancel
)
999 moveresize_end(TRUE
);
1000 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1001 gint grav
, x
, y
, w
, h
;
1003 if (e
->xclient
.data
.l
[0] & 0xff)
1004 grav
= e
->xclient
.data
.l
[0] & 0xff;
1006 grav
= client
->gravity
;
1008 if (e
->xclient
.data
.l
[0] & 1 << 8)
1009 x
= e
->xclient
.data
.l
[1];
1012 if (e
->xclient
.data
.l
[0] & 1 << 9)
1013 y
= e
->xclient
.data
.l
[2];
1016 if (e
->xclient
.data
.l
[0] & 1 << 10)
1017 w
= e
->xclient
.data
.l
[3];
1019 w
= client
->area
.width
;
1020 if (e
->xclient
.data
.l
[0] & 1 << 11)
1021 h
= e
->xclient
.data
.l
[4];
1023 h
= client
->area
.height
;
1025 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1026 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1027 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1028 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1029 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1030 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1033 case PropertyNotify
:
1034 /* validate cuz we query stuff off the client here */
1035 if (!client_validate(client
)) break;
1037 /* compress changes to a single property into a single change */
1038 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1042 /* XXX: it would be nice to compress ALL changes to a property,
1043 not just changes in a row without other props between. */
1045 a
= ce
.xproperty
.atom
;
1046 b
= e
->xproperty
.atom
;
1050 if ((a
== prop_atoms
.net_wm_name
||
1051 a
== prop_atoms
.wm_name
||
1052 a
== prop_atoms
.net_wm_icon_name
||
1053 a
== prop_atoms
.wm_icon_name
)
1055 (b
== prop_atoms
.net_wm_name
||
1056 b
== prop_atoms
.wm_name
||
1057 b
== prop_atoms
.net_wm_icon_name
||
1058 b
== prop_atoms
.wm_icon_name
)) {
1061 if (a
== prop_atoms
.net_wm_icon
&&
1062 b
== prop_atoms
.net_wm_icon
)
1065 XPutBackEvent(ob_display
, &ce
);
1069 msgtype
= e
->xproperty
.atom
;
1070 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1071 client_update_normal_hints(client
);
1072 /* normal hints can make a window non-resizable */
1073 client_setup_decor_and_functions(client
);
1074 } else if (msgtype
== XA_WM_HINTS
) {
1075 client_update_wmhints(client
);
1076 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1077 client_update_transient_for(client
);
1078 client_get_type(client
);
1079 /* type may have changed, so update the layer */
1080 client_calc_layer(client
);
1081 client_setup_decor_and_functions(client
);
1082 } else if (msgtype
== prop_atoms
.net_wm_name
||
1083 msgtype
== prop_atoms
.wm_name
||
1084 msgtype
== prop_atoms
.net_wm_icon_name
||
1085 msgtype
== prop_atoms
.wm_icon_name
) {
1086 client_update_title(client
);
1087 } else if (msgtype
== prop_atoms
.wm_class
) {
1088 client_update_class(client
);
1089 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1090 client_update_protocols(client
);
1091 client_setup_decor_and_functions(client
);
1093 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1094 client_update_strut(client
);
1096 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1097 client_update_icons(client
);
1099 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1100 client_update_icon_geometry(client
);
1102 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1103 client_update_user_time(client
);
1106 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1107 client_update_sync_request_counter(client
);
1110 else if (msgtype
== prop_atoms
.sm_client_id
) {
1111 client_update_sm_client_id(client
);
1113 case ColormapNotify
:
1114 client_update_colormap(client
, e
->xcolormap
.colormap
);
1119 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1120 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1121 frame_adjust_shape(client
->frame
);
1127 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1131 if (e
->xbutton
.button
== 1)
1132 stacking_raise(DOCK_AS_WINDOW(s
));
1133 else if (e
->xbutton
.button
== 2)
1134 stacking_lower(DOCK_AS_WINDOW(s
));
1145 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1149 dock_app_drag(app
, &e
->xmotion
);
1152 if (app
->ignore_unmaps
) {
1153 app
->ignore_unmaps
--;
1156 dock_remove(app
, TRUE
);
1159 dock_remove(app
, FALSE
);
1161 case ReparentNotify
:
1162 dock_remove(app
, FALSE
);
1164 case ConfigureNotify
:
1165 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1170 static ObMenuFrame
* find_active_menu()
1173 ObMenuFrame
*ret
= NULL
;
1175 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1184 static ObMenuFrame
* find_active_or_last_menu()
1186 ObMenuFrame
*ret
= NULL
;
1188 ret
= find_active_menu();
1189 if (!ret
&& menu_frame_visible
)
1190 ret
= menu_frame_visible
->data
;
1194 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1196 guint keycode
, state
;
1199 gboolean ret
= TRUE
;
1201 keycode
= ev
->xkey
.keycode
;
1202 state
= ev
->xkey
.state
;
1203 unikey
= translate_unichar(keycode
);
1205 frame
= find_active_or_last_menu();
1209 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1210 /* Escape closes the active menu */
1211 menu_frame_hide(frame
);
1214 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1215 state
== ControlMask
))
1217 /* Enter runs the active item or goes into the submenu.
1218 Control-Enter runs it without closing the menu. */
1220 menu_frame_select_next(frame
->child
);
1222 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1225 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1226 /* Left goes to the parent menu */
1227 menu_frame_select(frame
, NULL
, TRUE
);
1230 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1231 /* Right goes to the selected submenu */
1232 if (frame
->child
) menu_frame_select_next(frame
->child
);
1235 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1236 menu_frame_select_previous(frame
);
1239 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1240 menu_frame_select_next(frame
);
1243 /* keyboard accelerator shortcuts. */
1244 else if (ev
->xkey
.state
== 0 &&
1245 /* was it a valid key? */
1247 /* don't bother if the menu is empty. */
1252 ObMenuEntryFrame
*found
= NULL
;
1253 guint num_found
= 0;
1255 /* start after the selected one */
1256 start
= frame
->entries
;
1257 if (frame
->selected
) {
1258 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1259 g_assert(it
!= NULL
); /* nothing was selected? */
1260 /* next with wraparound */
1261 start
= g_list_next(it
);
1262 if (start
== NULL
) start
= frame
->entries
;
1267 ObMenuEntryFrame
*e
= it
->data
;
1268 gunichar entrykey
= 0;
1270 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1271 e
->entry
->data
.normal
.enabled
)
1272 entrykey
= e
->entry
->data
.normal
.shortcut
;
1273 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1274 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1276 if (unikey
== entrykey
) {
1277 if (found
== NULL
) found
= e
;
1281 /* next with wraparound */
1282 it
= g_list_next(it
);
1283 if (it
== NULL
) it
= frame
->entries
;
1284 } while (it
!= start
);
1287 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1290 menu_frame_select(frame
, found
, TRUE
);
1292 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1294 menu_frame_select(frame
, found
, TRUE
);
1296 menu_frame_select_next(frame
->child
);
1307 static gboolean
event_handle_menu(XEvent
*ev
)
1310 ObMenuEntryFrame
*e
;
1311 gboolean ret
= TRUE
;
1315 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1318 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1319 ev
->xbutton
.y_root
)))
1320 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1323 menu_frame_hide_all();
1327 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1328 if (e
->ignore_enters
)
1331 menu_frame_select(e
->frame
, e
, FALSE
);
1335 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1336 (f
= find_active_menu()) && f
->selected
== e
&&
1337 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1339 menu_frame_select(e
->frame
, NULL
, FALSE
);
1342 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1343 ev
->xmotion
.y_root
)))
1344 menu_frame_select(e
->frame
, e
, FALSE
);
1347 ret
= event_handle_menu_keyboard(ev
);
1353 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1355 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1356 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1357 e
->type
== KeyRelease
);
1359 if (menu_frame_visible
) {
1360 if (event_handle_menu(e
))
1361 /* don't use the event if the menu used it, but if the menu
1362 didn't use it and it's a keypress that is bound, it will
1363 close the menu and be used */
1367 /* if the keyboard interactive action uses the event then dont
1368 use it for bindings. likewise is moveresize uses the event. */
1369 if (!keyboard_process_interactive_grab(e
, &client
) &&
1370 !(moveresize_in_progress
&& moveresize_event(e
)))
1372 if (moveresize_in_progress
)
1373 /* make further actions work on the client being
1375 client
= moveresize_client
;
1377 menu_can_hide
= FALSE
;
1378 ob_main_loop_timeout_add(ob_main_loop
,
1379 config_menu_hide_delay
* 1000,
1380 menu_hide_delay_func
,
1381 NULL
, g_direct_equal
, NULL
);
1383 if (e
->type
== ButtonPress
||
1384 e
->type
== ButtonRelease
||
1385 e
->type
== MotionNotify
)
1387 /* the frame may not be "visible" but they can still click on it
1388 in the case where it is animating before disappearing */
1389 if (client
&& client
->frame
->visible
)
1390 mouse_event(client
, e
);
1391 } else if (e
->type
== KeyPress
) {
1392 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1393 (client
? client
: focus_client
)), e
);
1398 static gboolean
menu_hide_delay_func(gpointer data
)
1400 menu_can_hide
= TRUE
;
1401 return FALSE
; /* no repeat */
1404 static void focus_delay_dest(gpointer data
)
1409 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1411 const ObFocusDelayData
*f1
= d1
;
1412 return f1
->client
== d2
;
1415 static gboolean
focus_delay_func(gpointer data
)
1417 ObFocusDelayData
*d
= data
;
1418 Time old
= event_curtime
;
1420 event_curtime
= d
->time
;
1421 if (focus_client
!= d
->client
) {
1422 if (client_focus(d
->client
) && config_focus_raise
)
1423 client_raise(d
->client
);
1425 event_curtime
= old
;
1426 return FALSE
; /* no repeat */
1429 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1431 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1435 void event_halt_focus_delay()
1437 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1440 void event_ignore_queued_enters()
1442 GSList
*saved
= NULL
, *it
;
1445 XSync(ob_display
, FALSE
);
1447 /* count the events */
1449 e
= g_new(XEvent
, 1);
1450 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1453 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1454 if (win
&& WINDOW_IS_CLIENT(win
))
1455 ++ignore_enter_focus
;
1457 saved
= g_slist_append(saved
, e
);
1463 /* put the events back */
1464 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1465 XPutBackEvent(ob_display
, it
->data
);
1468 g_slist_free(saved
);
1471 gboolean
event_time_after(Time t1
, Time t2
)
1473 g_assert(t1
!= CurrentTime
);
1474 g_assert(t2
!= CurrentTime
);
1477 Timestamp values wrap around (after about 49.7 days). The server, given
1478 its current time is represented by timestamp T, always interprets
1479 timestamps from clients by treating half of the timestamp space as being
1480 later in time than T.
1481 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1484 /* TIME_HALF is half of the number space of a Time type variable */
1485 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1487 if (t2
>= TIME_HALF
)
1488 /* t2 is in the second half so t1 might wrap around and be smaller than
1490 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1492 /* t2 is in the first half so t1 has to come after it */
1493 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);