diff options
author | Owen Taylor <otaylor@redhat.com> | 2005-05-05 00:12:21 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2005-05-05 00:12:21 +0000 |
commit | dcedc5bcefb038ef6464427b97a5a23aeb546dca (patch) | |
tree | 7a55655a55a732814cfa0820e252c86645ccf966 /docs/focus_tracking.txt | |
parent | d990d0164018129b5cc744de3d0fcafb4e9b3360 (diff) | |
download | gtk+-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.txt | 161 |
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. |