mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-30 15:31:34 +00:00
dcedc5bcef
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
162 lines
4.9 KiB
Plaintext
162 lines
4.9 KiB
Plaintext
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.
|