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
)
312 /* This means focus moved to the frame window */
313 if (detail
== NotifyInferior
)
319 g_assert(e
->type
== FocusOut
);
322 /* These are ones we never want.. */
324 /* This means focus was taken by a keyboard/mouse grab. */
325 if (mode
== NotifyGrab
)
328 /* Focus left the root window revertedto state */
329 if (win
== RootWindow(ob_display
, ob_screen
))
332 /* These are the ones we want.. */
334 /* This means focus moved from a client to the root window */
335 if (detail
== NotifyVirtual
)
337 /* This means focus moved from one client to another */
338 if (detail
== NotifyNonlinearVirtual
)
340 /* This means focus had moved to our frame window and now moved off */
341 if (detail
== NotifyNonlinear
)
349 static Bool
look_for_focusin(Display
*d
, XEvent
*e
, XPointer arg
)
351 return e
->type
== FocusIn
&& wanted_focusevent(e
);
354 static gboolean
event_ignore(XEvent
*e
, ObClient
*client
)
358 if (!wanted_focusevent(e
))
362 if (!wanted_focusevent(e
))
369 static void event_process(const XEvent
*ec
, gpointer data
)
372 ObGroup
*group
= NULL
;
373 ObClient
*client
= NULL
;
375 ObDockApp
*dockapp
= NULL
;
376 ObWindow
*obwin
= NULL
;
378 ObEventData
*ed
= data
;
380 /* make a copy we can mangle */
384 window
= event_get_window(e
);
385 if (!(e
->type
== PropertyNotify
&&
386 (group
= g_hash_table_lookup(group_map
, &window
))))
387 if ((obwin
= g_hash_table_lookup(window_map
, &window
))) {
388 switch (obwin
->type
) {
390 dock
= WINDOW_AS_DOCK(obwin
);
393 dockapp
= WINDOW_AS_DOCKAPP(obwin
);
396 client
= WINDOW_AS_CLIENT(obwin
);
399 case Window_Internal
:
400 /* not to be used for events */
401 g_assert_not_reached();
406 event_set_curtime(e
);
408 if (event_ignore(e
, client
)) {
415 /* deal with it in the kernel */
417 if (menu_frame_visible
&&
418 (e
->type
== EnterNotify
|| e
->type
== LeaveNotify
))
420 /* crossing events for menu */
421 event_handle_menu(e
);
422 } else if (e
->type
== FocusIn
) {
423 if (e
->xfocus
.detail
== NotifyPointerRoot
||
424 e
->xfocus
.detail
== NotifyDetailNone
)
426 ob_debug_type(OB_DEBUG_FOCUS
, "Focus went to pointer root/none\n");
427 /* Focus has been reverted to the root window or nothing.
428 FocusOut events come after UnmapNotify, so we don't need to
429 worry about focusing an invalid window
431 if (!focus_left_screen
)
432 focus_fallback(TRUE
);
433 } else if (e
->xfocus
.detail
== NotifyInferior
) {
434 ob_debug_type(OB_DEBUG_FOCUS
,
435 "Focus went to root or our frame window");
436 /* Focus has been given to the root window. */
437 focus_fallback(TRUE
);
438 } else if (client
&& client
!= focus_client
) {
439 focus_left_screen
= FALSE
;
440 frame_adjust_focus(client
->frame
, TRUE
);
441 focus_set_client(client
);
442 client_calc_layer(client
);
444 } else if (e
->type
== FocusOut
) {
445 gboolean nomove
= FALSE
;
448 ob_debug_type(OB_DEBUG_FOCUS
, "FocusOut Event\n");
450 /* Look for the followup FocusIn */
451 if (!XCheckIfEvent(ob_display
, &ce
, look_for_focusin
, NULL
)) {
452 /* There is no FocusIn, this means focus went to a window that
453 is not being managed, or a window on another screen. */
457 xerror_set_ignore(TRUE
);
458 if (XGetInputFocus(ob_display
, &win
, &i
) != 0 &&
459 XGetGeometry(ob_display
, win
, &root
, &i
,&i
,&u
,&u
,&u
,&u
) != 0 &&
460 root
!= RootWindow(ob_display
, ob_screen
))
462 ob_debug_type(OB_DEBUG_FOCUS
,
463 "Focus went to another screen !\n");
464 focus_left_screen
= TRUE
;
467 ob_debug_type(OB_DEBUG_FOCUS
,
468 "Focus went to a black hole !\n");
469 xerror_set_ignore(FALSE
);
470 /* nothing is focused */
471 focus_set_client(NULL
);
472 } else if (ce
.xany
.window
== e
->xany
.window
) {
473 ob_debug_type(OB_DEBUG_FOCUS
, "Focus didn't go anywhere\n");
474 /* If focus didn't actually move anywhere, there is nothing to do*/
477 /* Focus did move, so process the FocusIn event */
478 ObEventData ed
= { .ignored
= FALSE
};
479 event_process(&ce
, &ed
);
481 /* The FocusIn was ignored, this means it was on a window
482 that isn't a client. */
483 ob_debug_type(OB_DEBUG_FOCUS
,
484 "Focus went to an unmanaged window 0x%x !\n",
486 focus_fallback(TRUE
);
490 if (client
&& !nomove
) {
491 frame_adjust_focus(client
->frame
, FALSE
);
492 /* focus_set_client has already been called for sure */
493 client_calc_layer(client
);
496 event_handle_group(group
, e
);
498 event_handle_client(client
, e
);
500 event_handle_dockapp(dockapp
, e
);
502 event_handle_dock(dock
, e
);
503 else if (window
== RootWindow(ob_display
, ob_screen
))
504 event_handle_root(e
);
505 else if (e
->type
== MapRequest
)
506 client_manage(window
);
507 else if (e
->type
== ConfigureRequest
) {
508 /* unhandled configure requests must be used to configure the
512 xwc
.x
= e
->xconfigurerequest
.x
;
513 xwc
.y
= e
->xconfigurerequest
.y
;
514 xwc
.width
= e
->xconfigurerequest
.width
;
515 xwc
.height
= e
->xconfigurerequest
.height
;
516 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
517 xwc
.sibling
= e
->xconfigurerequest
.above
;
518 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
520 /* we are not to be held responsible if someone sends us an
522 xerror_set_ignore(TRUE
);
523 XConfigureWindow(ob_display
, window
,
524 e
->xconfigurerequest
.value_mask
, &xwc
);
525 xerror_set_ignore(FALSE
);
528 else if (extensions_sync
&&
529 e
->type
== extensions_sync_event_basep
+ XSyncAlarmNotify
)
531 XSyncAlarmNotifyEvent
*se
= (XSyncAlarmNotifyEvent
*)e
;
532 if (se
->alarm
== moveresize_alarm
&& moveresize_in_progress
)
537 if (e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
538 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
539 e
->type
== KeyRelease
)
541 event_handle_user_input(client
, e
);
544 /* if something happens and it's not from an XEvent, then we don't know
546 event_curtime
= CurrentTime
;
549 static void event_handle_root(XEvent
*e
)
555 ob_debug("Another WM has requested to replace us. Exiting.\n");
560 if (e
->xclient
.format
!= 32) break;
562 msgtype
= e
->xclient
.message_type
;
563 if (msgtype
== prop_atoms
.net_current_desktop
) {
564 guint d
= e
->xclient
.data
.l
[0];
565 if (d
< screen_num_desktops
) {
566 event_curtime
= e
->xclient
.data
.l
[1];
567 if (event_curtime
== 0)
568 ob_debug_type(OB_DEBUG_APP_BUGS
,
569 "_NET_CURRENT_DESKTOP message is missing "
571 screen_set_desktop(d
);
573 } else if (msgtype
== prop_atoms
.net_number_of_desktops
) {
574 guint d
= e
->xclient
.data
.l
[0];
576 screen_set_num_desktops(d
);
577 } else if (msgtype
== prop_atoms
.net_showing_desktop
) {
578 screen_show_desktop(e
->xclient
.data
.l
[0] != 0, TRUE
);
579 } else if (msgtype
== prop_atoms
.openbox_control
) {
580 if (e
->xclient
.data
.l
[0] == 1)
582 else if (e
->xclient
.data
.l
[0] == 2)
587 if (e
->xproperty
.atom
== prop_atoms
.net_desktop_names
)
588 screen_update_desktop_names();
589 else if (e
->xproperty
.atom
== prop_atoms
.net_desktop_layout
)
590 screen_update_layout();
592 case ConfigureNotify
:
594 XRRUpdateConfiguration(e
);
603 static void event_handle_group(ObGroup
*group
, XEvent
*e
)
607 g_assert(e
->type
== PropertyNotify
);
609 for (it
= group
->members
; it
; it
= g_slist_next(it
))
610 event_handle_client(it
->data
, e
);
613 void event_enter_client(ObClient
*client
)
615 g_assert(config_focus_follow
);
617 if (client_normal(client
) && client_can_focus(client
)) {
618 if (config_focus_delay
) {
619 ObFocusDelayData
*data
;
621 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
623 data
= g_new(ObFocusDelayData
, 1);
624 data
->client
= client
;
625 data
->time
= event_curtime
;
627 ob_main_loop_timeout_add(ob_main_loop
,
630 data
, focus_delay_cmp
, focus_delay_dest
);
632 ObFocusDelayData data
;
633 data
.client
= client
;
634 data
.time
= event_curtime
;
635 focus_delay_func(&data
);
640 static void event_handle_client(ObClient
*client
, XEvent
*e
)
649 /* Wheel buttons don't draw because they are an instant click, so it
650 is a waste of resources to go drawing it. */
651 if (!(e
->xbutton
.button
== 4 || e
->xbutton
.button
== 5)) {
652 con
= frame_context(client
, e
->xbutton
.window
);
653 con
= mouse_button_frame_context(con
, e
->xbutton
.button
);
655 case OB_FRAME_CONTEXT_MAXIMIZE
:
656 client
->frame
->max_press
= (e
->type
== ButtonPress
);
657 framerender_frame(client
->frame
);
659 case OB_FRAME_CONTEXT_CLOSE
:
660 client
->frame
->close_press
= (e
->type
== ButtonPress
);
661 framerender_frame(client
->frame
);
663 case OB_FRAME_CONTEXT_ICONIFY
:
664 client
->frame
->iconify_press
= (e
->type
== ButtonPress
);
665 framerender_frame(client
->frame
);
667 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
668 client
->frame
->desk_press
= (e
->type
== ButtonPress
);
669 framerender_frame(client
->frame
);
671 case OB_FRAME_CONTEXT_SHADE
:
672 client
->frame
->shade_press
= (e
->type
== ButtonPress
);
673 framerender_frame(client
->frame
);
676 /* nothing changes with clicks for any other contexts */
682 con
= frame_context(client
, e
->xcrossing
.window
);
684 case OB_FRAME_CONTEXT_MAXIMIZE
:
685 client
->frame
->max_hover
= FALSE
;
686 frame_adjust_state(client
->frame
);
688 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
689 client
->frame
->desk_hover
= FALSE
;
690 frame_adjust_state(client
->frame
);
692 case OB_FRAME_CONTEXT_SHADE
:
693 client
->frame
->shade_hover
= FALSE
;
694 frame_adjust_state(client
->frame
);
696 case OB_FRAME_CONTEXT_ICONIFY
:
697 client
->frame
->iconify_hover
= FALSE
;
698 frame_adjust_state(client
->frame
);
700 case OB_FRAME_CONTEXT_CLOSE
:
701 client
->frame
->close_hover
= FALSE
;
702 frame_adjust_state(client
->frame
);
704 case OB_FRAME_CONTEXT_FRAME
:
705 /* When the mouse leaves an animating window, don't use the
706 corresponding enter events. Pretend like the animating window
707 doesn't even exist..! */
708 if (frame_iconify_animating(client
->frame
))
709 event_ignore_queued_enters();
711 ob_debug_type(OB_DEBUG_FOCUS
,
712 "%sNotify mode %d detail %d on %lx\n",
713 (e
->type
== EnterNotify
? "Enter" : "Leave"),
715 e
->xcrossing
.detail
, (client
?client
->window
:0));
716 if (keyboard_interactively_grabbed())
718 if (config_focus_follow
&& config_focus_delay
&&
719 /* leave inferior events can happen when the mouse goes onto
720 the window's border and then into the window before the
722 e
->xcrossing
.detail
!= NotifyInferior
)
724 ob_main_loop_timeout_remove_data(ob_main_loop
,
735 gboolean nofocus
= FALSE
;
737 if (ignore_enter_focus
) {
738 ignore_enter_focus
--;
742 con
= frame_context(client
, e
->xcrossing
.window
);
744 case OB_FRAME_CONTEXT_MAXIMIZE
:
745 client
->frame
->max_hover
= TRUE
;
746 frame_adjust_state(client
->frame
);
748 case OB_FRAME_CONTEXT_ALLDESKTOPS
:
749 client
->frame
->desk_hover
= TRUE
;
750 frame_adjust_state(client
->frame
);
752 case OB_FRAME_CONTEXT_SHADE
:
753 client
->frame
->shade_hover
= TRUE
;
754 frame_adjust_state(client
->frame
);
756 case OB_FRAME_CONTEXT_ICONIFY
:
757 client
->frame
->iconify_hover
= TRUE
;
758 frame_adjust_state(client
->frame
);
760 case OB_FRAME_CONTEXT_CLOSE
:
761 client
->frame
->close_hover
= TRUE
;
762 frame_adjust_state(client
->frame
);
764 case OB_FRAME_CONTEXT_FRAME
:
765 if (keyboard_interactively_grabbed())
767 if (e
->xcrossing
.mode
== NotifyGrab
||
768 e
->xcrossing
.mode
== NotifyUngrab
||
769 /*ignore enters when we're already in the window */
770 e
->xcrossing
.detail
== NotifyInferior
)
772 ob_debug_type(OB_DEBUG_FOCUS
,
773 "%sNotify mode %d detail %d on %lx IGNORED\n",
774 (e
->type
== EnterNotify
? "Enter" : "Leave"),
776 e
->xcrossing
.detail
, client
?client
->window
:0);
778 ob_debug_type(OB_DEBUG_FOCUS
,
779 "%sNotify mode %d detail %d on %lx, "
780 "focusing window: %d\n",
781 (e
->type
== EnterNotify
? "Enter" : "Leave"),
783 e
->xcrossing
.detail
, (client
?client
->window
:0),
785 if (!nofocus
&& config_focus_follow
)
786 event_enter_client(client
);
794 case ConfigureRequest
:
795 /* dont compress these unless you're going to watch for property
796 notifies in between (these can change what the configure would
798 also you can't compress stacking events
801 ob_debug("ConfigureRequest desktop %d wmstate %d vis %d\n",
802 screen_desktop
, client
->wmstate
, client
->frame
->visible
);
804 /* don't allow clients to move shaded windows (fvwm does this) */
805 if (client
->shaded
) {
806 e
->xconfigurerequest
.value_mask
&= ~CWX
;
807 e
->xconfigurerequest
.value_mask
&= ~CWY
;
810 /* resize, then move, as specified in the EWMH section 7.7 */
811 if (e
->xconfigurerequest
.value_mask
& (CWWidth
| CWHeight
|
816 if (e
->xconfigurerequest
.value_mask
& CWBorderWidth
)
817 client
->border_width
= e
->xconfigurerequest
.border_width
;
819 x
= (e
->xconfigurerequest
.value_mask
& CWX
) ?
820 e
->xconfigurerequest
.x
: client
->area
.x
;
821 y
= (e
->xconfigurerequest
.value_mask
& CWY
) ?
822 e
->xconfigurerequest
.y
: client
->area
.y
;
823 w
= (e
->xconfigurerequest
.value_mask
& CWWidth
) ?
824 e
->xconfigurerequest
.width
: client
->area
.width
;
825 h
= (e
->xconfigurerequest
.value_mask
& CWHeight
) ?
826 e
->xconfigurerequest
.height
: client
->area
.height
;
828 ob_debug("ConfigureRequest x %d %d y %d %d\n",
829 e
->xconfigurerequest
.value_mask
& CWX
, x
,
830 e
->xconfigurerequest
.value_mask
& CWY
, y
);
832 /* check for broken apps moving to their root position
834 XXX remove this some day...that would be nice. right now all
835 kde apps do this when they try activate themselves on another
836 desktop. eg. open amarok window on desktop 1, switch to desktop
837 2, click amarok tray icon. it will move by its decoration size.
839 if (x
!= client
->area
.x
&&
840 x
== (client
->frame
->area
.x
+ client
->frame
->size
.left
-
841 (gint
)client
->border_width
) &&
842 y
!= client
->area
.y
&&
843 y
== (client
->frame
->area
.y
+ client
->frame
->size
.top
-
844 (gint
)client
->border_width
))
846 ob_debug_type(OB_DEBUG_APP_BUGS
,
847 "Application %s is trying to move via "
848 "ConfigureRequest to it's root window position "
849 "but it is not using StaticGravity\n",
856 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
857 client_configure_full(client
, x
, y
, w
, h
, FALSE
, TRUE
, TRUE
);
860 if (e
->xconfigurerequest
.value_mask
& CWStackMode
) {
861 switch (e
->xconfigurerequest
.detail
) {
864 /* Apps are so rude. And this is totally disconnected from
865 activation/focus. Bleh. */
866 /*client_lower(client);*/
872 /* Apps are so rude. And this is totally disconnected from
873 activation/focus. Bleh. */
874 /*client_raise(client);*/
880 if (client
->ignore_unmaps
) {
881 client
->ignore_unmaps
--;
884 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
886 client
->window
, e
->xunmap
.event
, e
->xunmap
.from_configure
,
887 client
->ignore_unmaps
);
888 client_unmanage(client
);
891 ob_debug("DestroyNotify for window 0x%x\n", client
->window
);
892 client_unmanage(client
);
895 /* this is when the client is first taken captive in the frame */
896 if (e
->xreparent
.parent
== client
->frame
->plate
) break;
899 This event is quite rare and is usually handled in unmapHandler.
900 However, if the window is unmapped when the reparent event occurs,
901 the window manager never sees it because an unmap event is not sent
902 to an already unmapped window.
905 /* we don't want the reparent event, put it back on the stack for the
906 X server to deal with after we unmanage the window */
907 XPutBackEvent(ob_display
, e
);
909 ob_debug("ReparentNotify for window 0x%x\n", client
->window
);
910 client_unmanage(client
);
913 ob_debug("MapRequest for 0x%lx\n", client
->window
);
914 if (!client
->iconic
) break; /* this normally doesn't happen, but if it
915 does, we don't want it!
916 it can happen now when the window is on
917 another desktop, but we still don't
919 client_activate(client
, FALSE
, TRUE
);
922 /* validate cuz we query stuff off the client here */
923 if (!client_validate(client
)) break;
925 if (e
->xclient
.format
!= 32) return;
927 msgtype
= e
->xclient
.message_type
;
928 if (msgtype
== prop_atoms
.wm_change_state
) {
929 /* compress changes into a single change */
930 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
932 /* XXX: it would be nice to compress ALL messages of a
933 type, not just messages in a row without other
934 message types between. */
935 if (ce
.xclient
.message_type
!= msgtype
) {
936 XPutBackEvent(ob_display
, &ce
);
939 e
->xclient
= ce
.xclient
;
941 client_set_wm_state(client
, e
->xclient
.data
.l
[0]);
942 } else if (msgtype
== prop_atoms
.net_wm_desktop
) {
943 /* compress changes into a single change */
944 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
946 /* XXX: it would be nice to compress ALL messages of a
947 type, not just messages in a row without other
948 message types between. */
949 if (ce
.xclient
.message_type
!= msgtype
) {
950 XPutBackEvent(ob_display
, &ce
);
953 e
->xclient
= ce
.xclient
;
955 if ((unsigned)e
->xclient
.data
.l
[0] < screen_num_desktops
||
956 (unsigned)e
->xclient
.data
.l
[0] == DESKTOP_ALL
)
957 client_set_desktop(client
, (unsigned)e
->xclient
.data
.l
[0],
959 } else if (msgtype
== prop_atoms
.net_wm_state
) {
960 /* can't compress these */
961 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
962 (e
->xclient
.data
.l
[0] == 0 ? "Remove" :
963 e
->xclient
.data
.l
[0] == 1 ? "Add" :
964 e
->xclient
.data
.l
[0] == 2 ? "Toggle" : "INVALID"),
965 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2],
967 client_set_state(client
, e
->xclient
.data
.l
[0],
968 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[2]);
969 } else if (msgtype
== prop_atoms
.net_close_window
) {
970 ob_debug("net_close_window for 0x%lx\n", client
->window
);
971 client_close(client
);
972 } else if (msgtype
== prop_atoms
.net_active_window
) {
973 ob_debug("net_active_window for 0x%lx source=%s\n",
975 (e
->xclient
.data
.l
[0] == 0 ? "unknown" :
976 (e
->xclient
.data
.l
[0] == 1 ? "application" :
977 (e
->xclient
.data
.l
[0] == 2 ? "user" : "INVALID"))));
978 /* XXX make use of data.l[2] !? */
979 event_curtime
= e
->xclient
.data
.l
[1];
980 ob_debug_type(OB_DEBUG_APP_BUGS
,
981 "_NET_ACTIVE_WINDOW message for window %s is "
982 "missing a timestamp\n", client
->title
);
983 client_activate(client
, FALSE
,
984 (e
->xclient
.data
.l
[0] == 0 ||
985 e
->xclient
.data
.l
[0] == 2));
986 } else if (msgtype
== prop_atoms
.net_wm_moveresize
) {
987 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
988 client
->window
, e
->xclient
.data
.l
[2]);
989 if ((Atom
)e
->xclient
.data
.l
[2] ==
990 prop_atoms
.net_wm_moveresize_size_topleft
||
991 (Atom
)e
->xclient
.data
.l
[2] ==
992 prop_atoms
.net_wm_moveresize_size_top
||
993 (Atom
)e
->xclient
.data
.l
[2] ==
994 prop_atoms
.net_wm_moveresize_size_topright
||
995 (Atom
)e
->xclient
.data
.l
[2] ==
996 prop_atoms
.net_wm_moveresize_size_right
||
997 (Atom
)e
->xclient
.data
.l
[2] ==
998 prop_atoms
.net_wm_moveresize_size_right
||
999 (Atom
)e
->xclient
.data
.l
[2] ==
1000 prop_atoms
.net_wm_moveresize_size_bottomright
||
1001 (Atom
)e
->xclient
.data
.l
[2] ==
1002 prop_atoms
.net_wm_moveresize_size_bottom
||
1003 (Atom
)e
->xclient
.data
.l
[2] ==
1004 prop_atoms
.net_wm_moveresize_size_bottomleft
||
1005 (Atom
)e
->xclient
.data
.l
[2] ==
1006 prop_atoms
.net_wm_moveresize_size_left
||
1007 (Atom
)e
->xclient
.data
.l
[2] ==
1008 prop_atoms
.net_wm_moveresize_move
||
1009 (Atom
)e
->xclient
.data
.l
[2] ==
1010 prop_atoms
.net_wm_moveresize_size_keyboard
||
1011 (Atom
)e
->xclient
.data
.l
[2] ==
1012 prop_atoms
.net_wm_moveresize_move_keyboard
) {
1014 moveresize_start(client
, e
->xclient
.data
.l
[0],
1015 e
->xclient
.data
.l
[1], e
->xclient
.data
.l
[3],
1016 e
->xclient
.data
.l
[2]);
1018 else if ((Atom
)e
->xclient
.data
.l
[2] ==
1019 prop_atoms
.net_wm_moveresize_cancel
)
1020 moveresize_end(TRUE
);
1021 } else if (msgtype
== prop_atoms
.net_moveresize_window
) {
1022 gint grav
, x
, y
, w
, h
;
1024 if (e
->xclient
.data
.l
[0] & 0xff)
1025 grav
= e
->xclient
.data
.l
[0] & 0xff;
1027 grav
= client
->gravity
;
1029 if (e
->xclient
.data
.l
[0] & 1 << 8)
1030 x
= e
->xclient
.data
.l
[1];
1033 if (e
->xclient
.data
.l
[0] & 1 << 9)
1034 y
= e
->xclient
.data
.l
[2];
1037 if (e
->xclient
.data
.l
[0] & 1 << 10)
1038 w
= e
->xclient
.data
.l
[3];
1040 w
= client
->area
.width
;
1041 if (e
->xclient
.data
.l
[0] & 1 << 11)
1042 h
= e
->xclient
.data
.l
[4];
1044 h
= client
->area
.height
;
1046 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1047 e
->xclient
.data
.l
[0] & 1 << 8, x
,
1048 e
->xclient
.data
.l
[0] & 1 << 9, y
);
1049 client_convert_gravity(client
, grav
, &x
, &y
, w
, h
);
1050 client_find_onscreen(client
, &x
, &y
, w
, h
, FALSE
);
1051 client_configure(client
, x
, y
, w
, h
, FALSE
, TRUE
);
1054 case PropertyNotify
:
1055 /* validate cuz we query stuff off the client here */
1056 if (!client_validate(client
)) break;
1058 /* compress changes to a single property into a single change */
1059 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
1063 /* XXX: it would be nice to compress ALL changes to a property,
1064 not just changes in a row without other props between. */
1066 a
= ce
.xproperty
.atom
;
1067 b
= e
->xproperty
.atom
;
1071 if ((a
== prop_atoms
.net_wm_name
||
1072 a
== prop_atoms
.wm_name
||
1073 a
== prop_atoms
.net_wm_icon_name
||
1074 a
== prop_atoms
.wm_icon_name
)
1076 (b
== prop_atoms
.net_wm_name
||
1077 b
== prop_atoms
.wm_name
||
1078 b
== prop_atoms
.net_wm_icon_name
||
1079 b
== prop_atoms
.wm_icon_name
)) {
1082 if (a
== prop_atoms
.net_wm_icon
&&
1083 b
== prop_atoms
.net_wm_icon
)
1086 XPutBackEvent(ob_display
, &ce
);
1090 msgtype
= e
->xproperty
.atom
;
1091 if (msgtype
== XA_WM_NORMAL_HINTS
) {
1092 client_update_normal_hints(client
);
1093 /* normal hints can make a window non-resizable */
1094 client_setup_decor_and_functions(client
);
1095 } else if (msgtype
== XA_WM_HINTS
) {
1096 client_update_wmhints(client
);
1097 } else if (msgtype
== XA_WM_TRANSIENT_FOR
) {
1098 client_update_transient_for(client
);
1099 client_get_type(client
);
1100 /* type may have changed, so update the layer */
1101 client_calc_layer(client
);
1102 client_setup_decor_and_functions(client
);
1103 } else if (msgtype
== prop_atoms
.net_wm_name
||
1104 msgtype
== prop_atoms
.wm_name
||
1105 msgtype
== prop_atoms
.net_wm_icon_name
||
1106 msgtype
== prop_atoms
.wm_icon_name
) {
1107 client_update_title(client
);
1108 } else if (msgtype
== prop_atoms
.wm_class
) {
1109 client_update_class(client
);
1110 } else if (msgtype
== prop_atoms
.wm_protocols
) {
1111 client_update_protocols(client
);
1112 client_setup_decor_and_functions(client
);
1114 else if (msgtype
== prop_atoms
.net_wm_strut
) {
1115 client_update_strut(client
);
1117 else if (msgtype
== prop_atoms
.net_wm_icon
) {
1118 client_update_icons(client
);
1120 else if (msgtype
== prop_atoms
.net_wm_icon_geometry
) {
1121 client_update_icon_geometry(client
);
1123 else if (msgtype
== prop_atoms
.net_wm_user_time
) {
1124 client_update_user_time(client
);
1127 else if (msgtype
== prop_atoms
.net_wm_sync_request_counter
) {
1128 client_update_sync_request_counter(client
);
1131 else if (msgtype
== prop_atoms
.sm_client_id
) {
1132 client_update_sm_client_id(client
);
1134 case ColormapNotify
:
1135 client_update_colormap(client
, e
->xcolormap
.colormap
);
1140 if (extensions_shape
&& e
->type
== extensions_shape_event_basep
) {
1141 client
->shaped
= ((XShapeEvent
*)e
)->shaped
;
1142 frame_adjust_shape(client
->frame
);
1148 static void event_handle_dock(ObDock
*s
, XEvent
*e
)
1152 if (e
->xbutton
.button
== 1)
1153 stacking_raise(DOCK_AS_WINDOW(s
));
1154 else if (e
->xbutton
.button
== 2)
1155 stacking_lower(DOCK_AS_WINDOW(s
));
1166 static void event_handle_dockapp(ObDockApp
*app
, XEvent
*e
)
1170 dock_app_drag(app
, &e
->xmotion
);
1173 if (app
->ignore_unmaps
) {
1174 app
->ignore_unmaps
--;
1177 dock_remove(app
, TRUE
);
1180 dock_remove(app
, FALSE
);
1182 case ReparentNotify
:
1183 dock_remove(app
, FALSE
);
1185 case ConfigureNotify
:
1186 dock_app_configure(app
, e
->xconfigure
.width
, e
->xconfigure
.height
);
1191 static ObMenuFrame
* find_active_menu()
1194 ObMenuFrame
*ret
= NULL
;
1196 for (it
= menu_frame_visible
; it
; it
= g_list_next(it
)) {
1205 static ObMenuFrame
* find_active_or_last_menu()
1207 ObMenuFrame
*ret
= NULL
;
1209 ret
= find_active_menu();
1210 if (!ret
&& menu_frame_visible
)
1211 ret
= menu_frame_visible
->data
;
1215 static gboolean
event_handle_menu_keyboard(XEvent
*ev
)
1217 guint keycode
, state
;
1220 gboolean ret
= TRUE
;
1222 keycode
= ev
->xkey
.keycode
;
1223 state
= ev
->xkey
.state
;
1224 unikey
= translate_unichar(keycode
);
1226 frame
= find_active_or_last_menu();
1230 else if (keycode
== ob_keycode(OB_KEY_ESCAPE
) && state
== 0) {
1231 /* Escape closes the active menu */
1232 menu_frame_hide(frame
);
1235 else if (keycode
== ob_keycode(OB_KEY_RETURN
) && (state
== 0 ||
1236 state
== ControlMask
))
1238 /* Enter runs the active item or goes into the submenu.
1239 Control-Enter runs it without closing the menu. */
1241 menu_frame_select_next(frame
->child
);
1243 menu_entry_frame_execute(frame
->selected
, state
, ev
->xkey
.time
);
1246 else if (keycode
== ob_keycode(OB_KEY_LEFT
) && ev
->xkey
.state
== 0) {
1247 /* Left goes to the parent menu */
1248 menu_frame_select(frame
, NULL
, TRUE
);
1251 else if (keycode
== ob_keycode(OB_KEY_RIGHT
) && ev
->xkey
.state
== 0) {
1252 /* Right goes to the selected submenu */
1253 if (frame
->child
) menu_frame_select_next(frame
->child
);
1256 else if (keycode
== ob_keycode(OB_KEY_UP
) && state
== 0) {
1257 menu_frame_select_previous(frame
);
1260 else if (keycode
== ob_keycode(OB_KEY_DOWN
) && state
== 0) {
1261 menu_frame_select_next(frame
);
1264 /* keyboard accelerator shortcuts. */
1265 else if (ev
->xkey
.state
== 0 &&
1266 /* was it a valid key? */
1268 /* don't bother if the menu is empty. */
1273 ObMenuEntryFrame
*found
= NULL
;
1274 guint num_found
= 0;
1276 /* start after the selected one */
1277 start
= frame
->entries
;
1278 if (frame
->selected
) {
1279 for (it
= start
; frame
->selected
!= it
->data
; it
= g_list_next(it
))
1280 g_assert(it
!= NULL
); /* nothing was selected? */
1281 /* next with wraparound */
1282 start
= g_list_next(it
);
1283 if (start
== NULL
) start
= frame
->entries
;
1288 ObMenuEntryFrame
*e
= it
->data
;
1289 gunichar entrykey
= 0;
1291 if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1292 e
->entry
->data
.normal
.enabled
)
1293 entrykey
= e
->entry
->data
.normal
.shortcut
;
1294 else if (e
->entry
->type
== OB_MENU_ENTRY_TYPE_SUBMENU
)
1295 entrykey
= e
->entry
->data
.submenu
.submenu
->shortcut
;
1297 if (unikey
== entrykey
) {
1298 if (found
== NULL
) found
= e
;
1302 /* next with wraparound */
1303 it
= g_list_next(it
);
1304 if (it
== NULL
) it
= frame
->entries
;
1305 } while (it
!= start
);
1308 if (found
->entry
->type
== OB_MENU_ENTRY_TYPE_NORMAL
&&
1311 menu_frame_select(frame
, found
, TRUE
);
1313 menu_entry_frame_execute(found
, state
, ev
->xkey
.time
);
1315 menu_frame_select(frame
, found
, TRUE
);
1317 menu_frame_select_next(frame
->child
);
1328 static gboolean
event_handle_menu(XEvent
*ev
)
1331 ObMenuEntryFrame
*e
;
1332 gboolean ret
= TRUE
;
1336 if ((ev
->xbutton
.button
< 4 || ev
->xbutton
.button
> 5)
1339 if ((e
= menu_entry_frame_under(ev
->xbutton
.x_root
,
1340 ev
->xbutton
.y_root
)))
1341 menu_entry_frame_execute(e
, ev
->xbutton
.state
,
1344 menu_frame_hide_all();
1348 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
))) {
1349 if (e
->ignore_enters
)
1352 menu_frame_select(e
->frame
, e
, FALSE
);
1356 if ((e
= g_hash_table_lookup(menu_frame_map
, &ev
->xcrossing
.window
)) &&
1357 (f
= find_active_menu()) && f
->selected
== e
&&
1358 e
->entry
->type
!= OB_MENU_ENTRY_TYPE_SUBMENU
)
1360 menu_frame_select(e
->frame
, NULL
, FALSE
);
1363 if ((e
= menu_entry_frame_under(ev
->xmotion
.x_root
,
1364 ev
->xmotion
.y_root
)))
1365 menu_frame_select(e
->frame
, e
, FALSE
);
1368 ret
= event_handle_menu_keyboard(ev
);
1374 static void event_handle_user_input(ObClient
*client
, XEvent
*e
)
1376 g_assert(e
->type
== ButtonPress
|| e
->type
== ButtonRelease
||
1377 e
->type
== MotionNotify
|| e
->type
== KeyPress
||
1378 e
->type
== KeyRelease
);
1380 if (menu_frame_visible
) {
1381 if (event_handle_menu(e
))
1382 /* don't use the event if the menu used it, but if the menu
1383 didn't use it and it's a keypress that is bound, it will
1384 close the menu and be used */
1388 /* if the keyboard interactive action uses the event then dont
1389 use it for bindings. likewise is moveresize uses the event. */
1390 if (!keyboard_process_interactive_grab(e
, &client
) &&
1391 !(moveresize_in_progress
&& moveresize_event(e
)))
1393 if (moveresize_in_progress
)
1394 /* make further actions work on the client being
1396 client
= moveresize_client
;
1398 menu_can_hide
= FALSE
;
1399 ob_main_loop_timeout_add(ob_main_loop
,
1400 config_menu_hide_delay
* 1000,
1401 menu_hide_delay_func
,
1402 NULL
, g_direct_equal
, NULL
);
1404 if (e
->type
== ButtonPress
||
1405 e
->type
== ButtonRelease
||
1406 e
->type
== MotionNotify
)
1408 /* the frame may not be "visible" but they can still click on it
1409 in the case where it is animating before disappearing */
1410 if (!client
|| !frame_iconify_animating(client
->frame
))
1411 mouse_event(client
, e
);
1412 } else if (e
->type
== KeyPress
) {
1413 keyboard_event((focus_cycle_target
? focus_cycle_target
:
1414 (client
? client
: focus_client
)), e
);
1419 static gboolean
menu_hide_delay_func(gpointer data
)
1421 menu_can_hide
= TRUE
;
1422 return FALSE
; /* no repeat */
1425 static void focus_delay_dest(gpointer data
)
1430 static gboolean
focus_delay_cmp(gconstpointer d1
, gconstpointer d2
)
1432 const ObFocusDelayData
*f1
= d1
;
1433 return f1
->client
== d2
;
1436 static gboolean
focus_delay_func(gpointer data
)
1438 ObFocusDelayData
*d
= data
;
1439 Time old
= event_curtime
;
1441 event_curtime
= d
->time
;
1442 if (focus_client
!= d
->client
) {
1443 if (client_focus(d
->client
) && config_focus_raise
)
1444 client_raise(d
->client
);
1446 event_curtime
= old
;
1447 return FALSE
; /* no repeat */
1450 static void focus_delay_client_dest(ObClient
*client
, gpointer data
)
1452 ob_main_loop_timeout_remove_data(ob_main_loop
, focus_delay_func
,
1456 void event_halt_focus_delay()
1458 ob_main_loop_timeout_remove(ob_main_loop
, focus_delay_func
);
1461 void event_ignore_queued_enters()
1463 GSList
*saved
= NULL
, *it
;
1466 XSync(ob_display
, FALSE
);
1468 /* count the events */
1470 e
= g_new(XEvent
, 1);
1471 if (XCheckTypedEvent(ob_display
, EnterNotify
, e
)) {
1474 win
= g_hash_table_lookup(window_map
, &e
->xany
.window
);
1475 if (win
&& WINDOW_IS_CLIENT(win
))
1476 ++ignore_enter_focus
;
1478 saved
= g_slist_append(saved
, e
);
1484 /* put the events back */
1485 for (it
= saved
; it
; it
= g_slist_next(it
)) {
1486 XPutBackEvent(ob_display
, it
->data
);
1489 g_slist_free(saved
);
1492 gboolean
event_time_after(Time t1
, Time t2
)
1494 g_assert(t1
!= CurrentTime
);
1495 g_assert(t2
!= CurrentTime
);
1498 Timestamp values wrap around (after about 49.7 days). The server, given
1499 its current time is represented by timestamp T, always interprets
1500 timestamps from clients by treating half of the timestamp space as being
1501 later in time than T.
1502 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1505 /* TIME_HALF is half of the number space of a Time type variable */
1506 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1508 if (t2
>= TIME_HALF
)
1509 /* t2 is in the second half so t1 might wrap around and be smaller than
1511 return t1
>= t2
|| t1
< (t2
+ TIME_HALF
);
1513 /* t2 is in the first half so t1 has to come after it */
1514 return t1
>= t2
&& t1
< (t2
+ TIME_HALF
);