4 We have a window W that we are tracking events on. Focus
5 can be on the following classes of objects
7 None : defined by X protocol
8 PointerRoot : defined by X protocol
10 Ancestor : An ancestor of W, including W's root window
11 Descendant : A descendant of W
12 Other: : A window that is neither an ancestor or
15 has_pointer(W): the pointer is in W or one of its descendants.
20 X sends FocusIn or FocusOut events to W with a detail of NotifyPointer
21 in the following transitions, when the pointer is inside W
23 Other => Ancestor: FocusIn
24 Ancestor => {Other,None}: FocusOut
25 Ancestor => PointerRoot: FocusOut, then FocusIn
26 {None,W,Descendant,Other} => PointerRoot: FocusIn
27 PointerRoot => Ancestor: FocusOut, then FocusIn
28 PointerRoot => {None,W,Descendant,Other} => FocusOut
30 [ Ignoring keyboard grabs for the moment ]
32 Basic focus tracking algorithm
33 ==============================
35 Keystroke events are delivered within W if and only if one of two
38 has_focus_window(W): F==W || F==Descendant
39 has_pointer_focus(W): (F==Ancestor || F==PointerRoot) && has_pointer(W)
41 These two conditions are mutually exclusive.
43 has_focus_window(W) is easy to track.
45 FocusIn: detail != NotifyInferior: Set has_focus_iwndow
46 FocusOut: detail != NotifyInferior: Clear has_focus_iwndow
48 has_pointer_focus(W) is harder to track.
50 We can separate out the transitions from !has_pointer_focus(W) to
51 has_pointer_focus(W) into four cases:
53 T1: [(F==W || F==Descendant) => F==Ancestor]; has_pointer(W)
55 T2: [(F==W || F==Descendant) => F==PointerRoot]; has_pointer(W)
57 T3: [(F==None || F==Other) => (F==PointerRoot || F==Ancestor)];
60 T4: [!has_pointer(W) => has_pointer(W)]; (F==Ancestor || F==PointerRoot)
62 All of these can be tracked by watching events on W.
64 T1:, we get a FocusOut with a mode of Ancestor or Virtual
65 We need to separately track has_pointer(W) to distinguish
66 this from the case where we get these events and !has_pointer(W)
68 T2, T3: together these are exactly the cases where we get
69 FocusIn/NotifyPointer.
71 For T4, we get an EnterNotify with the focus flag set. An
72 EnterNotify with a focus flag set will also be sent if
73 F==W, so we have to to explicitly test for that case
74 using has_focus_window(W)
77 The transitions from has_pointer_focus(W) to !has_pointer_focus(W)
78 are exactly the opposite
80 F1: [(F==W || F==Descendant) <= F==Ancestor]; has_pointer(W)
82 F2: [(F==W || F==Descendant) <= F==PointerRoot]; has_pointer(W)
84 F3: [(F==None || F==Other) <= (F==PointerRoot || F==Ancestor)];
87 F4: [!has_pointer(W) <= has_pointer(W)]; (F==Ancestor || F==PointerRoot)
89 And can be tracked in the same ways:
91 F1: we get a FocusIn with a mode of Ancestor or Virtual
92 We need to separately track has_pointer(W) to distinguish
93 this from the case we get these events and !has_pointer(W)
95 F2, F3: together these are exactly the cases where we get
96 FocusOut/NotifyPointer.
98 F4: we get an LeaveNotify with the focus flag set. An
99 LeaveNotify with a focus flag set will also be sent if
100 F==W, so we have to to explicitly test for that case
101 using has_focus_window(W).
104 Modifications for keyboard grabs
105 ================================
107 The above algorithm ignores keyboard grabs, which also
108 generate focus events, and needs to be modified somewhat
109 to take keyboard grabs into effect. The basic idea
110 is that for has_pointer_focus(W)/has_window_focus(W) we track
111 them ignoring grabs and ungrabs, and then supplement
112 that with another predicate has_focus(W) which pays
113 attention to grabs and ungrabs.
117 When tracking has_pointer_focus(W), ignore all Focus
118 events with a mode of NotifyGrab or NotifyUngrab.
120 Note that this means that with grabs, we don't perfectly.
121 track the delivery of keyboard events ... since we think
122 we are getting events in the case where
124 has_pointer_focus(W) && !(G == None || G==W || G==descendant)
126 But the X protocol doesn't provide sufficient information
127 to do this right... example:
129 F=Ancestor, G=None => F=Ancestor, G=Ancestor
131 We stop getting events, but receive no notification.
133 The case of no window manager and keyboard grabs is pretty
138 When tracking has_focus_window(W), ignore all Focus
139 events with a mode of NotifyGrab or NotifyUngrab.
141 Modification 3: instead of calculating focus as
143 has_focus_window(W) || has_pointer_focus(W)
147 has_focus(W) || has_pointer_focus(W)
149 where has_focus(W) is defined as:
151 has_focus(W): F==W || F==Descendant || G=W
153 Tracking has_focus(W) is done by
155 FocusIn: detail != NotifyInferior, mode != NotifyWhileGrabbed:
157 FocusOut: detail != NotifyInferior, mode != NotifyWhileGrabbed:
160 We still need to track has_focus_window(W) for the T4/F4