Core focus in/out model for MPX

Peter Hutterer peter.hutterer at who-t.net
Sun Dec 21 15:48:53 PST 2008


Below is the description of the focus model for multiple keyboards. I have
implemented and tested all but the PointerRoot/None cases and so far it seems
to work, but then again, there's tricky bits in the details.

Two things make the focus model trickier than the enter/leave model:
NotifyPointer events and the ability to set the focus to PointerRoot/None.
Both of these may cause unbalanced events.

Assumptions for this document: A is the current focus window. B is the window
the focus is about to change to.  W is the window we want to deliver events to.
P is the pointer window, i.e. the window the mouse pointer is on at the
moment.

== Identical cases to the Enter/Leave model ==

   1. A and B are above W
   2. A is above W, B is W
   3. A is above W, B is below W
   4. A is W, B is above W
   5. A and B are W
   6. A is W, B is below W
   7. A is below W, B is above W
   8. A is below W, B is W
   9. A is below W, B is below W

Each of these cases exists in six varieties, P is below A, P is A, P is above
A, P is below B, P is B and P is above B. The only exception are
PointerRoot/None cases which only have three varieties of P. The
implementation of all cases that do not include None or PointerRoot is
equivalent to the enter/leave model described in
http://lists.freedesktop.org/archives/xorg/2008-August/037606.html (provided
NotifyPointer is ignored)

=== Special NotifyPointer handling ===

NotifyPointer handling is different each case, and in the MPX model
NotifyPointer handling depends on the local treatment of an event.  Since the
model allows for changing the detail field, the NotifyPointer handling must be
adjusted to fit the core protocol description for the new state of the detail
field. For example, if a window receives a FocusIn(NotifyInferior) and P is
below B but not A or below A, FocusIn(NotifyPointer) must be sent between B
and including P. If B has a descendant with a focus, supress all events.

The core protocol spec define that a NotifyPointer may be sent to the
descendants of a window 

    * receiving a FocusIn(NotifyInferior), if they are not descendants of the source window.
    * receiving a FocusOut(NotifyInferior), if they are not descendants of the target window.  
    * receiving a FocusOut(NotifyNonlinear).
    * receiving a FocusIn(NotifyNonlinear).

The various PointerRoot/None cases are ignored for now.

The problem we face with these cases is the presence of multiple pointers.
Given pointers P1 and P2, paired with keyboards K1 and K2, respectively, may
provide the case where NotifyPointer events are sent to windows based on
location of P2. Could this be a problem?
Even worse, changes in the detail field for local adjustments may result in a
client receiving to sets of NotifyPointer events - this cannot happen in the
core model.  The solution to this would be to tie NotifyPointer events to the
keyboard that focus events are being sent for. For example, the detail is
changed to NotifyInferior because of the keyboard K's focus in a descendant of
W. Only send NotifyPointer events for the pointer paired with K (if
appropriate).

== Focus cases that do not exist in the enter/leave model ==

Case 10:
  A is W, B is PointerRoot or None and W is NOT a root window.
  Core: FocusOut(Nonlinear) to W
  MPX: 10.a: if W has another focus, suppress the event
       10.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W switches to this descendant. FocusOut(Inferior)
             to W
       10.c: otherwise, FocusOut(Nonlinear) to W

Case 11:
  A is W, B is PointerRoot or None and W is a root window
  Core: FocusOut(Nonlinear) to W, then FocusIn(PointerRoot or None) to W
  MPX: 11.a: if W has another focus, suppress the event
       11.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W switches to this descendant. FocusOut(Inferior)
             to W
       11.c: otherwise, FocusOut(Nonlinear) to W, then FocusIn(PointerRoot
             or None) to W

Case 12:
  A is below W, B is PointerRoot or None and W is NOT a root window
  Core: FocusOut(NonlinearVirtual) to W
  MPX: 12.a: if W has another focus, suppress the event,
       12.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W does not change. suppress the event.
       12.c: otherwise, FocusOut(NonlinearVirtual) to W

Case 13:
  A is below W, B is PointerRoot or None and W is a root window
  Core: FocusOut(NonlinearVirtual) to W, then FocusIn(PointerRoot or None) to W
  MPX: 13.a: if W has another focus, suppress the event
       13.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W does not change. suppress the event.
       13.c: otherwise, FocusOut(NonlinearVirtual) to W, then
             FocusIn(PointerRoot or None)

Case 14:
  A is PointerRoot or None, B is W and W is NOT a root window
  Core: FocusIn(Nonlinear) to W
  MPX: 14.a: if W has another focus, suppress the event
       14.b: otherwise, if a descendant of W has another focus, the focus
             as seen by W changes from the descendant to W.
             FocusIn(Inferior) to W
       14.c: otherwise, FocusIn(Nonlinear) to W

Case 15:
  A is PointerRoot or None, B is W and W is a root window
  Core: FocusOut(PointerRoot or None) to W, then FocusIn(Nonlinear) to W
  MPX: 15.a: if W has another focus, suppress the event
       15.b: otherwise, if a descendant of W has another focus, the focus
             as seen by W changes from the descendant to W.
             FocusIn(Inferior) to W
       15.c: otherwise, FocusOut(PointerRoot or None) to W, then
             FocusIn(Nonlinear) to W

Case 16:
  A is PointerRoot or None, B is below W and W is NOT a root window
  Core: FocusIn(NonlinearVirtual) to W
  MPX: 16.a: if W has another focus, suppress the event
       16.b: otherwise, if a descendant of W has another focus, the focus as
             seen by W does not change. suppress the event.
       16.c: otherwise, FocusIn(NonlinearVirtual) to W

Case 17:
  A is PointerRoot or None, B is below W and W is a root window
  Core: FocusOut(PointerRoot or None) to W, then FocusIn(NonlinearWirtual) to W
  MPX: 17.a: if W has another focus, suppress the event
       17.b: otherwise, if a descendant of W has another focus, the focus as seen
             by W does not change. suppress the event.
       17.c: otherwise, FocusOut(PointerRoot or None) to W, then
             FocusIn(NonlinearVirtual) to W

Case 18:
  A is PointerRoot or None, B is None or PointerRoot and W is NOT a root window
  Core: No event to W
  MPX: No event to W

Case 19:
  A is PointerRoot or None, B is None or PointerRoot and W is a root window
  Core: FocusOut(PointerRoot or None) to W, then FocusIn(None or PointerRoot) to W
  MPX: 19.a: if W has another focus, suppress the event.
       19.b: otherwise, if a descendant of W has another focus, the focus window as
             seen by W does not change. suppress the event
       19.c: otherwise, FocusOut(PointerRoot or None) to W, then FocusIn(None
             or PointerRoot) to W

Case 20:
  A is not a descendant of W, B is PointerRoot or None and W is a root window
  Core: FocusIn(PointerRoot or None) to W
  MPX: 20.a: if W has another focus, suppress the event
       20.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W does not change. suppress the event.
       20.c: otherwise, FocusIn(PointerRoot or None) to W

Case 21:
  A is PointerRoot or None, B is NOT below W and W is a root window.
  Core: FocusOut(PointerRoot or None) to W
  MPX: 21.a: if W has another focus, suppress the event
       21.b: otherwise, if a descendant of W has another focus, the focus
             window as seen by W does not change. suppress the event.
       21.c: otherwise, FocusOut(PointerRoot or None) to W

Problems: 
Unbalanced PointerRoot or None events. Assuming F1 and F2 are two foci.  F1
and F2 are in window A. F1 changes to PointerRoot. No event is sent to the
root window.  F2 changes to window B on a different screen.
FocusOut(NonlinearVirtual) is sent to the root window. If F1 now changes back
from PointerRoot to A, the root window would receive an unbalanced
FocusOut(PointerRoot).

NotifyPointer in the PointerRoot or None cases may be sent:
    * to the descendants of a window receiving a FocusOut(Nonlinear)
    * to the descendants of a window receiving a FocusIn(Nonlinear)
    * to the descendants of a root window receiving FocusIn(PointerRoot)
    * to the descendants of a root window receiving FocusOut(PointerRoot)

These cases suffer from the same problems as the other cases, but also from
the following requirement:
     "If the old focus is PointerRoot, FocusOut with detail Pointer is
      generated on each window from P up to and including P’s root (in order)"
                                                [X protocol spec, p 81]

The "old focus" would need to be determined by running through all keyboards,
checking the focus and eliminating those root windows with an explicitly
focused keyboard. Nonetheless, it leaves the case where some root windows have
a perceived PointerRoot/None state and others a NotifyInferior/Nonlinear.

Comments?

Cheers,
  Peter



More information about the xorg mailing list