summaryrefslogtreecommitdiff
path: root/docs/focus_tracking.txt
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2005-05-05 00:12:21 +0000
committerOwen Taylor <otaylor@src.gnome.org>2005-05-05 00:12:21 +0000
commitdcedc5bcefb038ef6464427b97a5a23aeb546dca (patch)
tree7a55655a55a732814cfa0820e252c86645ccf966 /docs/focus_tracking.txt
parentd990d0164018129b5cc744de3d0fcafb4e9b3360 (diff)
downloadgtk+-dcedc5bcefb038ef6464427b97a5a23aeb546dca.tar.gz
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
Diffstat (limited to 'docs/focus_tracking.txt')
-rw-r--r--docs/focus_tracking.txt161
1 files changed, 161 insertions, 0 deletions
diff --git a/docs/focus_tracking.txt b/docs/focus_tracking.txt
new file mode 100644
index 0000000000..c0fcf08a4f
--- /dev/null
+++ b/docs/focus_tracking.txt
@@ -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.