 07e7719441
			
		
	
	07e7719441
	
	
	
		
			
			2006-10-08 Matthias Clasen <mclasen@redhat.com> * Apply a cleanup patch by Kjartan Maraas (#341812)
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 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.
 |