| 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 explicitly 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. |