]> Pileus Git - ~andy/gtk/blob - docs/focus_tracking.txt
stylecontext: Do invalidation on first resize container
[~andy/gtk] / docs / focus_tracking.txt
1 Notational conventions
2 ======================
3
4 We have a window W that we are tracking events on. Focus
5 can be on the following classes of objects
6
7   None        : defined by X protocol
8   PointerRoot : defined by X protocol
9   W           : the window itself
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
13                 descendant of W
14
15 has_pointer(W): the pointer is in W or one of its descendants.
16
17 NotifyPointer events
18 ====================
19
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
22
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
29
30 [ Ignoring keyboard grabs for the moment ]
31
32 Basic focus tracking algorithm
33 ==============================
34
35 Keystroke events are delivered within W if and only if one of two
36 predicates hold:
37
38  has_focus_window(W): F==W || F==Descendant
39  has_pointer_focus(W): (F==Ancestor || F==PointerRoot) && has_pointer(W)
40
41 These two conditions are mutually exclusive.
42
43 has_focus_window(W) is easy to track.
44
45  FocusIn: detail != NotifyInferior: Set has_focus_iwndow
46  FocusOut: detail != NotifyInferior: Clear has_focus_iwndow
47
48 has_pointer_focus(W) is harder to track.
49
50 We can separate out the transitions from !has_pointer_focus(W) to
51 has_pointer_focus(W) into four cases:
52
53  T1: [(F==W || F==Descendant) => F==Ancestor]; has_pointer(W)
54
55  T2: [(F==W || F==Descendant) => F==PointerRoot]; has_pointer(W)
56  
57  T3: [(F==None || F==Other) => (F==PointerRoot || F==Ancestor)];
58    has_pointer(W)
59
60  T4: [!has_pointer(W) => has_pointer(W)]; (F==Ancestor || F==PointerRoot)
61
62 All of these can be tracked by watching events on W.
63
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)
67
68 T2, T3: together these are exactly the cases where we get 
69   FocusIn/NotifyPointer.
70
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)
75
76
77 The transitions from has_pointer_focus(W) to !has_pointer_focus(W)
78 are exactly the opposite
79
80  F1: [(F==W || F==Descendant) <= F==Ancestor]; has_pointer(W)
81
82  F2: [(F==W || F==Descendant) <= F==PointerRoot]; has_pointer(W)
83  
84  F3: [(F==None || F==Other) <= (F==PointerRoot || F==Ancestor)];
85    has_pointer(W)
86
87  F4: [!has_pointer(W) <= has_pointer(W)]; (F==Ancestor || F==PointerRoot)
88
89 And can be tracked in the same ways:
90
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)
94
95 F2, F3: together these are exactly the cases where we get 
96   FocusOut/NotifyPointer.
97
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).
102
103
104 Modifications for keyboard grabs
105 ================================
106
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.
114
115 Modification 1:
116    
117  When tracking has_pointer_focus(W), ignore all Focus
118  events with a mode of NotifyGrab or NotifyUngrab.
119
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
123
124   has_pointer_focus(W) && !(G == None || G==W || G==descendant)
125
126  But the X protocol doesn't provide sufficient information
127  to do this right... example:
128
129    F=Ancestor, G=None   =>   F=Ancestor, G=Ancestor
130
131  We stop getting events, but receive no notification.
132         
133  The case of no window manager and keyboard grabs is pretty
134  rare in any case.
135
136 Modification 2:
137    
138  When tracking has_focus_window(W), ignore all Focus
139  events with a mode of NotifyGrab or NotifyUngrab.
140
141 Modification 3: instead of calculating focus as 
142   
143     has_focus_window(W) || has_pointer_focus(W)
144
145   Calculate it as 
146
147     has_focus(W) || has_pointer_focus(W)
148
149   where has_focus(W) is defined as:
150
151    has_focus(W): F==W || F==Descendant || G=W
152
153   Tracking has_focus(W) is done by 
154
155  FocusIn: detail != NotifyInferior, mode != NotifyWhileGrabbed: 
156     set has_focus
157  FocusOut: detail != NotifyInferior, mode != NotifyWhileGrabbed: 
158     clear has_focus
159
160  We still need to track has_focus_window(W) for the T4/F4 
161  transitions.