forked from AuroraMiddleware/gtk
Fix a bug in focus tracking when we move between has_pointer_focus and
2005-05-04 Owen Taylor <otaylor@redhat.com> * gdk/x11/gdkevents-x11.c gdk/x11/gdkwindow-x11.h: Fix a bug in focus tracking when we move between has_pointer_focus and has_focus_window directly. (#109246, Billy Biggs, Niko Tyni and others) * gdk/x11/gdkevents-x11.c: Also fix some extremely confusion that could happen in the case of no window manager + keyboard grabs, by moving to a more consistent model of when we pay attention to mode=NotifyGrab/NotifyUngrab events. * docs/focus_tracking.txt: Extensive writeup about how to track focus under X11
This commit is contained in:
parent
d990d01640
commit
dcedc5bcef
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
||||
2005-05-04 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* gdk/x11/gdkevents-x11.c gdk/x11/gdkwindow-x11.h: Fix a bug
|
||||
in focus tracking when we move between has_pointer_focus and
|
||||
has_focus_window directly. (#109246, Billy Biggs, Niko Tyni
|
||||
and others)
|
||||
|
||||
* gdk/x11/gdkevents-x11.c: Also fix some extremely confusion
|
||||
that could happen in the case of no window manager + keyboard grabs,
|
||||
by moving to a more consistent model of when we pay attention
|
||||
to mode=NotifyGrab/NotifyUngrab events.
|
||||
|
||||
* docs/focus_tracking.txt: Extensive writeup about how to track
|
||||
focus under X11.
|
||||
|
||||
Wed May 4 13:21:41 2005 Søren Sandmann <sandmann@redhat.com>
|
||||
|
||||
* tests/testcairo.c (draw): Replace cairo_show_surface() uses with
|
||||
|
@ -1,3 +1,18 @@
|
||||
2005-05-04 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* gdk/x11/gdkevents-x11.c gdk/x11/gdkwindow-x11.h: Fix a bug
|
||||
in focus tracking when we move between has_pointer_focus and
|
||||
has_focus_window directly. (#109246, Billy Biggs, Niko Tyni
|
||||
and others)
|
||||
|
||||
* gdk/x11/gdkevents-x11.c: Also fix some extremely confusion
|
||||
that could happen in the case of no window manager + keyboard grabs,
|
||||
by moving to a more consistent model of when we pay attention
|
||||
to mode=NotifyGrab/NotifyUngrab events.
|
||||
|
||||
* docs/focus_tracking.txt: Extensive writeup about how to track
|
||||
focus under X11.
|
||||
|
||||
Wed May 4 13:21:41 2005 Søren Sandmann <sandmann@redhat.com>
|
||||
|
||||
* tests/testcairo.c (draw): Replace cairo_show_surface() uses with
|
||||
|
@ -1,3 +1,18 @@
|
||||
2005-05-04 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* gdk/x11/gdkevents-x11.c gdk/x11/gdkwindow-x11.h: Fix a bug
|
||||
in focus tracking when we move between has_pointer_focus and
|
||||
has_focus_window directly. (#109246, Billy Biggs, Niko Tyni
|
||||
and others)
|
||||
|
||||
* gdk/x11/gdkevents-x11.c: Also fix some extremely confusion
|
||||
that could happen in the case of no window manager + keyboard grabs,
|
||||
by moving to a more consistent model of when we pay attention
|
||||
to mode=NotifyGrab/NotifyUngrab events.
|
||||
|
||||
* docs/focus_tracking.txt: Extensive writeup about how to track
|
||||
focus under X11.
|
||||
|
||||
Wed May 4 13:21:41 2005 Søren Sandmann <sandmann@redhat.com>
|
||||
|
||||
* tests/testcairo.c (draw): Replace cairo_show_surface() uses with
|
||||
|
161
docs/focus_tracking.txt
Normal file
161
docs/focus_tracking.txt
Normal file
@ -0,0 +1,161 @@
|
||||
Notational conventions
|
||||
======================
|
||||
|
||||
We have a window W that we are tracking events on. Focus
|
||||
can be on the following classes of objects
|
||||
|
||||
None : defined by X protocol
|
||||
PointerRoot : defined by X protocol
|
||||
W : the window itself
|
||||
Ancestor : An ancestor of W, including W's root window
|
||||
Descendant : A descendant of W
|
||||
Other: : A window that is neither an ancestor or
|
||||
descendant of W
|
||||
|
||||
has_pointer(W): the pointer is in W or one of its descendants.
|
||||
|
||||
NotifyPointer events
|
||||
====================
|
||||
|
||||
X sends FocusIn or FocusOut events to W with a detail of NotifyPointer
|
||||
in the following transitions, when the pointer is inside W
|
||||
|
||||
Other => Ancestor: FocusIn
|
||||
Ancestor => {Other,None}: FocusOut
|
||||
Ancestor => PointerRoot: FocusOut, then FocusIn
|
||||
{None,W,Descendant,Other} => PointerRoot: FocusIn
|
||||
PointerRoot => Ancestor: FocusOut, then FocusIn
|
||||
PointerRoot => {None,W,Descendant,Other} => FocusOut
|
||||
|
||||
[ Ignoring keyboard grabs for the moment ]
|
||||
|
||||
Basic focus tracking algorithm
|
||||
==============================
|
||||
|
||||
Keystroke events are delivered within W if and only if one of two
|
||||
predicates hold:
|
||||
|
||||
has_focus_window(W): F==W || F==Descendant
|
||||
has_pointer_focus(W): (F==Ancestor || F==PointerRoot) && has_pointer(W)
|
||||
|
||||
These two conditions are mutually exclusive.
|
||||
|
||||
has_focus_window(W) is easy to track.
|
||||
|
||||
FocusIn: detail != NotifyInferior: Set has_focus_iwndow
|
||||
FocusOut: detail != NotifyInferior: Clear has_focus_iwndow
|
||||
|
||||
has_pointer_focus(W) is harder to track.
|
||||
|
||||
We can separate out the transitions from !has_pointer_focus(W) to
|
||||
has_pointer_focus(W) into four cases:
|
||||
|
||||
T1: [(F==W || F==Descendant) => F==Ancestor]; has_pointer(W)
|
||||
|
||||
T2: [(F==W || F==Descendant) => F==PointerRoot]; has_pointer(W)
|
||||
|
||||
T3: [(F==None || F==Other) => (F==PointerRoot || F==Ancestor)];
|
||||
has_pointer(W)
|
||||
|
||||
T4: [!has_pointer(W) => has_pointer(W)]; (F==Ancestor || F==PointerRoot)
|
||||
|
||||
All of these can be tracked by watching events on W.
|
||||
|
||||
T1:, we get a FocusOut with a mode of Ancestor or Virtual
|
||||
We need to separately track has_pointer(W) to distinguish
|
||||
this from the case where we get these events and !has_pointer(W)
|
||||
|
||||
T2, T3: together these are exactly the cases where we get
|
||||
FocusIn/NotifyPointer.
|
||||
|
||||
For T4, we get an EnterNotify with the focus flag set. An
|
||||
EnterNotify with a focus flag set will also be sent if
|
||||
F==W, so we have to to explicitly test for that case
|
||||
using has_focus_window(W)
|
||||
|
||||
|
||||
The transitions from has_pointer_focus(W) to !has_pointer_focus(W)
|
||||
are exactly the opposite
|
||||
|
||||
F1: [(F==W || F==Descendant) <= F==Ancestor]; has_pointer(W)
|
||||
|
||||
F2: [(F==W || F==Descendant) <= F==PointerRoot]; has_pointer(W)
|
||||
|
||||
F3: [(F==None || F==Other) <= (F==PointerRoot || F==Ancestor)];
|
||||
has_pointer(W)
|
||||
|
||||
F4: [!has_pointer(W) <= has_pointer(W)]; (F==Ancestor || F==PointerRoot)
|
||||
|
||||
And can be tracked in the same ways:
|
||||
|
||||
F1: we get a FocusIn with a mode of Ancestor or Virtual
|
||||
We need to separately track has_pointer(W) to distinguish
|
||||
this from the case we get these events and !has_pointer(W)
|
||||
|
||||
F2, F3: together these are exactly the cases where we get
|
||||
FocusOut/NotifyPointer.
|
||||
|
||||
F4: we get an LeaveNotify with the focus flag set. An
|
||||
LeaveNotify with a focus flag set will also be sent if
|
||||
F==W, so we have to to explicity test for that case
|
||||
using has_focus_window(W).
|
||||
|
||||
|
||||
Modifications for keyboard grabs
|
||||
================================
|
||||
|
||||
The above algorithm ignores keyboard grabs, which also
|
||||
generate focus events, and needs to be modified somewhat
|
||||
to take keyboard grabs into effect. The basic idea
|
||||
is that for has_pointer_focus(W)/has_window_focus(W) we track
|
||||
them ignoring grabs and ungrabs, and then supplement
|
||||
that with another predicate has_focus(W) which pays
|
||||
attention to grabs and ungrabs.
|
||||
|
||||
Modification 1:
|
||||
|
||||
When tracking has_pointer_focus(W), ignore all Focus
|
||||
events with a mode of NotifyGrab or NotifyUngrab.
|
||||
|
||||
Note that this means that with grabs, we don't perfectly.
|
||||
track the delivery of keyboard events ... since we think
|
||||
we are getting events in the case where
|
||||
|
||||
has_pointer_focus(W) && !(G == None || G==W || G==descendant)
|
||||
|
||||
But the X protocol doesn't provide sufficient information
|
||||
to do this right... example:
|
||||
|
||||
F=Ancestor, G=None => F=Ancestor, G=Ancestor
|
||||
|
||||
We stop getting events, but receive no notification.
|
||||
|
||||
The case of no window manager and keyboard grabs is pretty
|
||||
rare in any case.
|
||||
|
||||
Modification 2:
|
||||
|
||||
When tracking has_focus_window(W), ignore all Focus
|
||||
events with a mode of NotifyGrab or NotifyUngrab.
|
||||
|
||||
Modification 3: instead of calculating focus as
|
||||
|
||||
has_focus_window(W) || has_pointer_focus(W)
|
||||
|
||||
Calculate it as
|
||||
|
||||
has_focus(W) || has_pointer_focus(W)
|
||||
|
||||
where has_focus(W) is defined as:
|
||||
|
||||
has_focus(W): F==W || F==Descendant || G=W
|
||||
|
||||
Tracking has_focus(W) is done by
|
||||
|
||||
FocusIn: detail != NotifyInferior, mode != NotifyWhileGrabbed:
|
||||
set has_focus
|
||||
FocusOut: detail != NotifyInferior, mode != NotifyWhileGrabbed:
|
||||
clear has_focus
|
||||
|
||||
We still need to track has_focus_window(W) for the T4/F4
|
||||
transitions.
|
@ -1218,16 +1218,19 @@ gdk_event_translate (GdkDisplay *display,
|
||||
}
|
||||
|
||||
/* Handle focusing (in the case where no window manager is running */
|
||||
if (toplevel &&
|
||||
xevent->xcrossing.detail != NotifyInferior &&
|
||||
xevent->xcrossing.focus && !toplevel->has_focus_window)
|
||||
if (toplevel && xevent->xcrossing.detail != NotifyInferior)
|
||||
{
|
||||
gboolean had_focus = HAS_FOCUS (toplevel);
|
||||
toplevel->has_pointer = TRUE;
|
||||
|
||||
toplevel->has_pointer_focus = TRUE;
|
||||
|
||||
if (HAS_FOCUS (toplevel) != had_focus)
|
||||
generate_focus_event (window, TRUE);
|
||||
if (xevent->xcrossing.focus && !toplevel->has_focus_window)
|
||||
{
|
||||
gboolean had_focus = HAS_FOCUS (toplevel);
|
||||
|
||||
toplevel->has_pointer_focus = TRUE;
|
||||
|
||||
if (HAS_FOCUS (toplevel) != had_focus)
|
||||
generate_focus_event (window, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell XInput stuff about it if appropriate */
|
||||
@ -1312,16 +1315,19 @@ gdk_event_translate (GdkDisplay *display,
|
||||
}
|
||||
|
||||
/* Handle focusing (in the case where no window manager is running */
|
||||
if (toplevel &&
|
||||
xevent->xcrossing.detail != NotifyInferior &&
|
||||
xevent->xcrossing.focus && !toplevel->has_focus_window)
|
||||
if (toplevel && xevent->xcrossing.detail != NotifyInferior)
|
||||
{
|
||||
gboolean had_focus = HAS_FOCUS (toplevel);
|
||||
|
||||
toplevel->has_pointer_focus = FALSE;
|
||||
|
||||
if (HAS_FOCUS (toplevel) != had_focus)
|
||||
generate_focus_event (window, FALSE);
|
||||
toplevel->has_pointer = FALSE;
|
||||
|
||||
if (xevent->xcrossing.focus && !toplevel->has_focus_window)
|
||||
{
|
||||
gboolean had_focus = HAS_FOCUS (toplevel);
|
||||
|
||||
toplevel->has_pointer_focus = FALSE;
|
||||
|
||||
if (HAS_FOCUS (toplevel) != had_focus)
|
||||
generate_focus_event (window, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
event->crossing.type = GDK_LEAVE_NOTIFY;
|
||||
@ -1404,10 +1410,25 @@ gdk_event_translate (GdkDisplay *display,
|
||||
switch (xevent->xfocus.detail)
|
||||
{
|
||||
case NotifyAncestor:
|
||||
case NotifyNonlinear:
|
||||
case NotifyVirtual:
|
||||
/* When the focus moves from an ancestor of the window to
|
||||
* the window or a descendent of the window, *and* the
|
||||
* pointer is inside the window, then we were previously
|
||||
* receiving keystroke events in the has_pointer_focus
|
||||
* case and are now receiving them in the
|
||||
* has_focus_window case.
|
||||
*/
|
||||
if (toplevel->has_pointer &&
|
||||
xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_pointer_focus = FALSE;
|
||||
|
||||
/* fall through */
|
||||
case NotifyNonlinear:
|
||||
case NotifyNonlinearVirtual:
|
||||
toplevel->has_focus_window = TRUE;
|
||||
if (xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_focus_window = TRUE;
|
||||
/* We pretend that the focus moves to the grab
|
||||
* window, so we pay attention to NotifyGrab
|
||||
* NotifyUngrab, and ignore NotifyWhileGrabbed
|
||||
@ -1420,7 +1441,8 @@ gdk_event_translate (GdkDisplay *display,
|
||||
* but the pointer focus is ignored while a
|
||||
* grab is in effect
|
||||
*/
|
||||
if (xevent->xfocus.mode != NotifyGrab)
|
||||
if (xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_pointer_focus = TRUE;
|
||||
break;
|
||||
case NotifyInferior:
|
||||
@ -1447,15 +1469,31 @@ gdk_event_translate (GdkDisplay *display,
|
||||
switch (xevent->xfocus.detail)
|
||||
{
|
||||
case NotifyAncestor:
|
||||
case NotifyNonlinear:
|
||||
case NotifyVirtual:
|
||||
/* When the focus moves from the window or a descendent
|
||||
* of the window to an ancestor of the window, *and* the
|
||||
* pointer is inside the window, then we were previously
|
||||
* receiving keystroke events in the has_focus_window
|
||||
* case and are now receiving them in the
|
||||
* has_pointer_focus case.
|
||||
*/
|
||||
if (toplevel->has_pointer &&
|
||||
xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_pointer_focus = TRUE;
|
||||
|
||||
/* fall through */
|
||||
case NotifyNonlinear:
|
||||
case NotifyNonlinearVirtual:
|
||||
toplevel->has_focus_window = FALSE;
|
||||
if (xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_focus_window = FALSE;
|
||||
if (xevent->xfocus.mode != NotifyWhileGrabbed)
|
||||
toplevel->has_focus = FALSE;
|
||||
break;
|
||||
case NotifyPointer:
|
||||
if (xevent->xfocus.mode != NotifyUngrab)
|
||||
if (xevent->xfocus.mode != NotifyGrab &&
|
||||
xevent->xfocus.mode != NotifyUngrab)
|
||||
toplevel->has_pointer_focus = FALSE;
|
||||
break;
|
||||
case NotifyInferior:
|
||||
|
@ -96,9 +96,14 @@ struct _GdkToplevelX11
|
||||
*/
|
||||
guint has_focus : 1;
|
||||
|
||||
/* Set if !window->has_focus_window, but events are being sent to the
|
||||
* window because the pointer is in it. (Typically, no window
|
||||
* manager is running.
|
||||
/* Set if the pointer is inside this window. (This is needed for
|
||||
* for focus tracking)
|
||||
*/
|
||||
guint has_pointer : 1;
|
||||
|
||||
/* Set if the window is a descendent of the focus window and the pointer is
|
||||
* inside it. (This is the case where the window will receive keystroke
|
||||
* events even window->has_focus_window is FALSE)
|
||||
*/
|
||||
guint has_pointer_focus : 1;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user