xserver-multidpi/dix/enterleave.c
Keith Packard 9838b7032e Introduce a consistent coding style
This is strictly the application of the script 'x-indent-all.sh'
from util/modular. Compared to the patch that Daniel posted in
January, I've added a few indent flags:

	-bap
	-psl
	-T PrivatePtr
	-T pmWait
	-T _XFUNCPROTOBEGIN
	-T _XFUNCPROTOEND
	-T _X_EXPORT

The typedefs were needed to make the output of sdksyms.sh match the
previous output, otherwise, the code is formatted badly enough that
sdksyms.sh generates incorrect output.

The generated code was compared with the previous version and found to
be essentially identical -- "assert" line numbers and BUILD_TIME were
the only differences found.

The comparison was done with this script:

dir1=$1
dir2=$2

for dir in $dir1 $dir2; do
	(cd $dir && find . -name '*.o' | while read file; do
		dir=`dirname $file`
		base=`basename $file .o`
		dump=$dir/$base.dump
		objdump -d $file > $dump
	done)
done

find $dir1 -name '*.dump' | while read dump; do
	otherdump=`echo $dump | sed "s;$dir1;$dir2;"`
	diff -u $dump $otherdump
done

Signed-off-by: Keith Packard <keithp@keithp.com>
Acked-by: Daniel Stone <daniel@fooishbar.org>
Acked-by: Alan Coopersmith <alan.coopersmith@oracle.com>
2012-03-21 13:54:42 -07:00

1314 lines
43 KiB
C

/*
* Copyright © 2008 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors: Peter Hutterer
*
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <X11/X.h>
#include <X11/extensions/XI2.h>
#include "inputstr.h"
#include "windowstr.h"
#include "scrnintstr.h"
#include "exglobals.h"
#include "enterleave.h"
/**
* @file
* This file describes the model for sending core enter/leave events and
* focus in/out in the case of multiple pointers/keyboard foci.
*
* Since we can't send more than one Enter or Leave/Focus in or out event per
* window to a core client without confusing it, this is a rather complicated
* approach.
*
* For a full description of the enter/leave model from a window's
* perspective, see
* http://lists.freedesktop.org/archives/xorg/2008-August/037606.html
*
* For a full description of the focus in/out model from a window's
* perspective, see
* http://lists.freedesktop.org/archives/xorg/2008-December/041740.html
*
* Additional notes:
* - The core protocol spec says that "In a LeaveNotify event, if a child of the
* event window contains the initial position of the pointer, then the child
* component is set to that child. Otherwise, it is None. For an EnterNotify
* event, if a child of the event window contains the final pointer position,
* then the child component is set to that child. Otherwise, it is None."
*
* By inference, this means that only NotifyVirtual or NotifyNonlinearVirtual
* events may have a subwindow set to other than None.
*
* - NotifyPointer events may be sent if the focus changes from window A to
* B. The assumption used in this model is that NotifyPointer events are only
* sent for the pointer paired with the keyboard that is involved in the focus
* events. For example, if F(W) changes because of keyboard 2, then
* NotifyPointer events are only sent for pointer 2.
*/
static WindowPtr PointerWindows[MAXDEVICES];
static WindowPtr FocusWindows[MAXDEVICES];
/**
* Return TRUE if 'win' has a pointer within its boundaries, excluding child
* window.
*/
static BOOL
HasPointer(DeviceIntPtr dev, WindowPtr win)
{
int i;
/* FIXME: The enter/leave model does not cater for grabbed devices. For
* now, a quickfix: if the device about to send an enter/leave event to
* a window is grabbed, assume there is no pointer in that window.
* Fixes fdo 27804.
* There isn't enough beer in my fridge to fix this properly.
*/
if (dev->deviceGrab.grab)
return FALSE;
for (i = 0; i < MAXDEVICES; i++)
if (PointerWindows[i] == win)
return TRUE;
return FALSE;
}
/**
* Return TRUE if at least one keyboard focus is set to 'win' (excluding
* descendants of win).
*/
static BOOL
HasFocus(WindowPtr win)
{
int i;
for (i = 0; i < MAXDEVICES; i++)
if (FocusWindows[i] == win)
return TRUE;
return FALSE;
}
/**
* Return the window the device dev is currently on.
*/
static WindowPtr
PointerWin(DeviceIntPtr dev)
{
return PointerWindows[dev->id];
}
/**
* Search for the first window below 'win' that has a pointer directly within
* it's boundaries (excluding boundaries of its own descendants).
*
* @return The child window that has the pointer within its boundaries or
* NULL.
*/
static WindowPtr
FirstPointerChild(WindowPtr win)
{
int i;
for (i = 0; i < MAXDEVICES; i++) {
if (PointerWindows[i] && IsParent(win, PointerWindows[i]))
return PointerWindows[i];
}
return NULL;
}
/**
* Search for the first window below 'win' that has a focus directly within
* it's boundaries (excluding boundaries of its own descendants).
*
* @return The child window that has the pointer within its boundaries or
* NULL.
*/
static WindowPtr
FirstFocusChild(WindowPtr win)
{
int i;
for (i = 0; i < MAXDEVICES; i++) {
if (FocusWindows[i] && FocusWindows[i] != PointerRootWin &&
IsParent(win, FocusWindows[i]))
return FocusWindows[i];
}
return NULL;
}
/**
* Set the presence flag for dev to mark that it is now in 'win'.
*/
void
EnterWindow(DeviceIntPtr dev, WindowPtr win, int mode)
{
PointerWindows[dev->id] = win;
}
/**
* Unset the presence flag for dev to mark that it is not in 'win' anymore.
*/
void
LeaveWindow(DeviceIntPtr dev)
{
PointerWindows[dev->id] = NULL;
}
/**
* Set the presence flag for dev to mark that it is now in 'win'.
*/
void
SetFocusIn(DeviceIntPtr dev, WindowPtr win)
{
FocusWindows[dev->id] = win;
}
/**
* Unset the presence flag for dev to mark that it is not in 'win' anymore.
*/
void
SetFocusOut(DeviceIntPtr dev)
{
FocusWindows[dev->id] = NULL;
}
/**
* Return the common ancestor of 'a' and 'b' (if one exists).
* @param a A window with the same ancestor as b.
* @param b A window with the same ancestor as a.
* @return The window that is the first ancestor of both 'a' and 'b', or the
* NullWindow if they do not have a common ancestor.
*/
WindowPtr
CommonAncestor(WindowPtr a, WindowPtr b)
{
for (b = b->parent; b; b = b->parent)
if (IsParent(b, a))
return b;
return NullWindow;
}
/**
* Send enter notifies to all windows between 'ancestor' and 'child' (excluding
* both). Events are sent running up the window hierarchy. This function
* recurses.
*/
static void
DeviceEnterNotifies(DeviceIntPtr dev,
int sourceid,
WindowPtr ancestor, WindowPtr child, int mode, int detail)
{
WindowPtr parent = child->parent;
if (ancestor == parent)
return;
DeviceEnterNotifies(dev, sourceid, ancestor, parent, mode, detail);
DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, detail, parent,
child->drawable.id);
}
/**
* Send enter notifies to all windows between 'ancestor' and 'child' (excluding
* both). Events are sent running down the window hierarchy. This function
* recurses.
*/
static void
CoreEnterNotifies(DeviceIntPtr dev,
WindowPtr ancestor, WindowPtr child, int mode, int detail)
{
WindowPtr parent = child->parent;
if (ancestor == parent)
return;
CoreEnterNotifies(dev, ancestor, parent, mode, detail);
/* Case 3:
A is above W, B is a descendant
Classically: The move generates an EnterNotify on W with a detail of
Virtual or NonlinearVirtual
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, so the event should be suppressed
Case 3B: Otherwise, if there is at least one other pointer in a
descendant
P(W) stays on the same descendant, or changes to a different
descendant. The event should be suppressed.
Case 3C: Otherwise:
P(W) moves from a window above W to a descendant. The subwindow
field is set to the child containing the descendant. The detail
may need to be changed from Virtual to NonlinearVirtual depending
on the previous P(W). */
if (!HasPointer(dev, parent) && !FirstPointerChild(parent))
CoreEnterLeaveEvent(dev, EnterNotify, mode, detail, parent,
child->drawable.id);
}
static void
CoreLeaveNotifies(DeviceIntPtr dev,
WindowPtr child, WindowPtr ancestor, int mode, int detail)
{
WindowPtr win;
if (ancestor == child)
return;
for (win = child->parent; win != ancestor; win = win->parent) {
/*Case 7:
A is a descendant of W, B is above W
Classically: A LeaveNotify is generated on W with a detail of Virtual
or NonlinearVirtual.
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, the event should be suppressed.
Case 3B: Otherwise, if there is at least one other pointer in a
descendant
P(W) stays on the same descendant, or changes to a different
descendant. The event should be suppressed.
Case 3C: Otherwise:
P(W) changes from the descendant of W to a window above W.
The detail may need to be changed from Virtual to NonlinearVirtual
or vice-versa depending on the new P(W). */
/* If one window has a pointer or a child with a pointer, skip some
* work and exit. */
if (HasPointer(dev, win) || FirstPointerChild(win))
return;
CoreEnterLeaveEvent(dev, LeaveNotify, mode, detail, win,
child->drawable.id);
child = win;
}
}
/**
* Send leave notifies to all windows between 'child' and 'ancestor'.
* Events are sent running up the hierarchy.
*/
static void
DeviceLeaveNotifies(DeviceIntPtr dev,
int sourceid,
WindowPtr child, WindowPtr ancestor, int mode, int detail)
{
WindowPtr win;
if (ancestor == child)
return;
for (win = child->parent; win != ancestor; win = win->parent) {
DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, detail, win,
child->drawable.id);
child = win;
}
}
/**
* Pointer dev moves from A to B and A neither a descendant of B nor is
* B a descendant of A.
*/
static void
CoreEnterLeaveNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
WindowPtr X = CommonAncestor(A, B);
/* Case 4:
A is W, B is above W
Classically: The move generates a LeaveNotify on W with a detail of
Ancestor or Nonlinear
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, the event should be suppressed
Case 3B: Otherwise, if there is at least one other pointer in a
descendant of W
P(W) changes from W to a descendant of W. The subwindow field
is set to the child containing the new P(W), the detail field
is set to Inferior
Case 3C: Otherwise:
The pointer window moves from W to a window above W.
The detail may need to be changed from Ancestor to Nonlinear or
vice versa depending on the the new P(W)
*/
if (!HasPointer(dev, A)) {
WindowPtr child = FirstPointerChild(A);
if (child)
CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A,
None);
else
CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyNonlinear, A,
None);
}
CoreLeaveNotifies(dev, A, X, mode, NotifyNonlinearVirtual);
/*
Case 9:
A is a descendant of W, B is a descendant of W
Classically: No events are generated on W
MPX: The pointer window stays the same or moves to a different
descendant of W. No events should be generated on W.
Therefore, no event to X.
*/
CoreEnterNotifies(dev, X, B, mode, NotifyNonlinearVirtual);
/* Case 2:
A is above W, B=W
Classically: The move generates an EnterNotify on W with a detail of
Ancestor or Nonlinear
MPX:
Case 2A: There is at least one other pointer on W itself
P(W) doesn't change, so the event should be suppressed
Case 2B: Otherwise, if there is at least one other pointer in a
descendant
P(W) moves from a descendant to W. detail is changed to Inferior,
subwindow is set to the child containing the previous P(W)
Case 2C: Otherwise:
P(W) changes from a window above W to W itself.
The detail may need to be changed from Ancestor to Nonlinear
or vice-versa depending on the previous P(W). */
if (!HasPointer(dev, B)) {
WindowPtr child = FirstPointerChild(B);
if (child)
CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B,
None);
else
CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyNonlinear, B,
None);
}
}
/**
* Pointer dev moves from A to B and A is a descendant of B.
*/
static void
CoreEnterLeaveToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
/* Case 4:
A is W, B is above W
Classically: The move generates a LeaveNotify on W with a detail of
Ancestor or Nonlinear
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, the event should be suppressed
Case 3B: Otherwise, if there is at least one other pointer in a
descendant of W
P(W) changes from W to a descendant of W. The subwindow field
is set to the child containing the new P(W), the detail field
is set to Inferior
Case 3C: Otherwise:
The pointer window moves from W to a window above W.
The detail may need to be changed from Ancestor to Nonlinear or
vice versa depending on the the new P(W)
*/
if (!HasPointer(dev, A)) {
WindowPtr child = FirstPointerChild(A);
if (child)
CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A,
None);
else
CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyAncestor, A,
None);
}
CoreLeaveNotifies(dev, A, B, mode, NotifyVirtual);
/* Case 8:
A is a descendant of W, B is W
Classically: A EnterNotify is generated on W with a detail of
NotifyInferior
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, the event should be suppressed
Case 3B: Otherwise:
P(W) changes from a descendant to W itself. The subwindow
field should be set to the child containing the old P(W) <<< WRONG */
if (!HasPointer(dev, B))
CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B, None);
}
/**
* Pointer dev moves from A to B and B is a descendant of A.
*/
static void
CoreEnterLeaveToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
/* Case 6:
A is W, B is a descendant of W
Classically: A LeaveNotify is generated on W with a detail of
NotifyInferior
MPX:
Case 3A: There is at least one other pointer on W itself
P(W) doesn't change, the event should be suppressed
Case 3B: Otherwise:
P(W) changes from W to a descendant of W. The subwindow field
is set to the child containing the new P(W) <<< THIS IS WRONG */
if (!HasPointer(dev, A))
CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A, None);
CoreEnterNotifies(dev, A, B, mode, NotifyVirtual);
/* Case 2:
A is above W, B=W
Classically: The move generates an EnterNotify on W with a detail of
Ancestor or Nonlinear
MPX:
Case 2A: There is at least one other pointer on W itself
P(W) doesn't change, so the event should be suppressed
Case 2B: Otherwise, if there is at least one other pointer in a
descendant
P(W) moves from a descendant to W. detail is changed to Inferior,
subwindow is set to the child containing the previous P(W)
Case 2C: Otherwise:
P(W) changes from a window above W to W itself.
The detail may need to be changed from Ancestor to Nonlinear
or vice-versa depending on the previous P(W). */
if (!HasPointer(dev, B)) {
WindowPtr child = FirstPointerChild(B);
if (child)
CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B,
None);
else
CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyAncestor, B,
None);
}
}
static void
CoreEnterLeaveEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
{
if (!IsMaster(dev))
return;
LeaveWindow(dev);
if (IsParent(from, to))
CoreEnterLeaveToDescendant(dev, from, to, mode);
else if (IsParent(to, from))
CoreEnterLeaveToAncestor(dev, from, to, mode);
else
CoreEnterLeaveNonLinear(dev, from, to, mode);
EnterWindow(dev, to, mode);
}
static void
DeviceEnterLeaveEvents(DeviceIntPtr dev,
int sourceid, WindowPtr from, WindowPtr to, int mode)
{
if (IsParent(from, to)) {
DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyInferior,
from, None);
DeviceEnterNotifies(dev, sourceid, from, to, mode, NotifyVirtual);
DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyAncestor, to,
None);
}
else if (IsParent(to, from)) {
DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyAncestor,
from, None);
DeviceLeaveNotifies(dev, sourceid, from, to, mode, NotifyVirtual);
DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyInferior, to,
None);
}
else { /* neither from nor to is descendent of the other */
WindowPtr common = CommonAncestor(to, from);
/* common == NullWindow ==> different screens */
DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyNonlinear,
from, None);
DeviceLeaveNotifies(dev, sourceid, from, common, mode,
NotifyNonlinearVirtual);
DeviceEnterNotifies(dev, sourceid, common, to, mode,
NotifyNonlinearVirtual);
DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyNonlinear,
to, None);
}
}
/**
* Figure out if enter/leave events are necessary and send them to the
* appropriate windows.
*
* @param fromWin Window the sprite moved out of.
* @param toWin Window the sprite moved into.
*/
void
DoEnterLeaveEvents(DeviceIntPtr pDev,
int sourceid, WindowPtr fromWin, WindowPtr toWin, int mode)
{
if (!IsPointerDevice(pDev))
return;
if (fromWin == toWin)
return;
if (mode != XINotifyPassiveGrab && mode != XINotifyPassiveUngrab)
CoreEnterLeaveEvents(pDev, fromWin, toWin, mode);
DeviceEnterLeaveEvents(pDev, sourceid, fromWin, toWin, mode);
}
/**
* Send focus out events to all windows between 'child' and 'ancestor'.
* Events are sent running up the hierarchy.
*/
static void
DeviceFocusOutEvents(DeviceIntPtr dev,
WindowPtr child, WindowPtr ancestor, int mode, int detail)
{
WindowPtr win;
if (ancestor == child)
return;
for (win = child->parent; win != ancestor; win = win->parent)
DeviceFocusEvent(dev, XI_FocusOut, mode, detail, win);
}
/**
* Send enter notifies to all windows between 'ancestor' and 'child' (excluding
* both). Events are sent running up the window hierarchy. This function
* recurses.
*/
static void
DeviceFocusInEvents(DeviceIntPtr dev,
WindowPtr ancestor, WindowPtr child, int mode, int detail)
{
WindowPtr parent = child->parent;
if (ancestor == parent || !parent)
return;
DeviceFocusInEvents(dev, ancestor, parent, mode, detail);
DeviceFocusEvent(dev, XI_FocusIn, mode, detail, parent);
}
/**
* Send FocusIn events to all windows between 'ancestor' and 'child' (excluding
* both). Events are sent running down the window hierarchy. This function
* recurses.
*/
static void
CoreFocusInEvents(DeviceIntPtr dev,
WindowPtr ancestor, WindowPtr child, int mode, int detail)
{
WindowPtr parent = child->parent;
if (ancestor == parent)
return;
CoreFocusInEvents(dev, ancestor, parent, mode, detail);
/* Case 3:
A is above W, B is a descendant
Classically: The move generates an FocusIn on W with a detail of
Virtual or NonlinearVirtual
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, so the event should be suppressed
Case 3B: Otherwise, if there is at least one other focus in a
descendant
F(W) stays on the same descendant, or changes to a different
descendant. The event should be suppressed.
Case 3C: Otherwise:
F(W) moves from a window above W to a descendant. The detail may
need to be changed from Virtual to NonlinearVirtual depending
on the previous F(W). */
if (!HasFocus(parent) && !FirstFocusChild(parent))
CoreFocusEvent(dev, FocusIn, mode, detail, parent);
}
static void
CoreFocusOutEvents(DeviceIntPtr dev,
WindowPtr child, WindowPtr ancestor, int mode, int detail)
{
WindowPtr win;
if (ancestor == child)
return;
for (win = child->parent; win != ancestor; win = win->parent) {
/*Case 7:
A is a descendant of W, B is above W
Classically: A FocusOut is generated on W with a detail of Virtual
or NonlinearVirtual.
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, the event should be suppressed.
Case 3B: Otherwise, if there is at least one other focus in a
descendant
F(W) stays on the same descendant, or changes to a different
descendant. The event should be suppressed.
Case 3C: Otherwise:
F(W) changes from the descendant of W to a window above W.
The detail may need to be changed from Virtual to NonlinearVirtual
or vice-versa depending on the new P(W). */
/* If one window has a focus or a child with a focuspointer, skip some
* work and exit. */
if (HasFocus(win) || FirstFocusChild(win))
return;
CoreFocusEvent(dev, FocusOut, mode, detail, win);
}
}
/**
* Send FocusOut(NotifyPointer) events from the current pointer window (which
* is a descendant of pwin_parent) up to (excluding) pwin_parent.
*
* NotifyPointer events are only sent for the device paired with dev.
*
* If the current pointer window is a descendant of 'exclude' or an ancestor of
* 'exclude', no events are sent. If the current pointer IS 'exclude', events
* are sent!
*/
static void
CoreFocusOutNotifyPointerEvents(DeviceIntPtr dev,
WindowPtr pwin_parent,
WindowPtr exclude, int mode, int inclusive)
{
WindowPtr P, stopAt;
P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
if (!P)
return;
if (!IsParent(pwin_parent, P))
if (!(pwin_parent == P && inclusive))
return;
if (exclude != None && exclude != PointerRootWin &&
(IsParent(exclude, P) || IsParent(P, exclude)))
return;
stopAt = (inclusive) ? pwin_parent->parent : pwin_parent;
for (; P && P != stopAt; P = P->parent)
CoreFocusEvent(dev, FocusOut, mode, NotifyPointer, P);
}
/**
* DO NOT CALL DIRECTLY.
* Recursion helper for CoreFocusInNotifyPointerEvents.
*/
static void
CoreFocusInRecurse(DeviceIntPtr dev,
WindowPtr win, WindowPtr stopAt, int mode, int inclusive)
{
if ((!inclusive && win == stopAt) || !win)
return;
CoreFocusInRecurse(dev, win->parent, stopAt, mode, inclusive);
CoreFocusEvent(dev, FocusIn, mode, NotifyPointer, win);
}
/**
* Send FocusIn(NotifyPointer) events from pwin_parent down to
* including the current pointer window (which is a descendant of pwin_parent).
*
* @param pwin The pointer window.
* @param exclude If the pointer window is a child of 'exclude', no events are
* sent.
* @param inclusive If TRUE, pwin_parent will receive the event too.
*/
static void
CoreFocusInNotifyPointerEvents(DeviceIntPtr dev,
WindowPtr pwin_parent,
WindowPtr exclude, int mode, int inclusive)
{
WindowPtr P;
P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
if (!P || P == exclude || (pwin_parent != P && !IsParent(pwin_parent, P)))
return;
if (exclude != None && (IsParent(exclude, P) || IsParent(P, exclude)))
return;
CoreFocusInRecurse(dev, P, pwin_parent, mode, inclusive);
}
/**
* Focus of dev moves from A to B and A neither a descendant of B nor is
* B a descendant of A.
*/
static void
CoreFocusNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
WindowPtr X = CommonAncestor(A, B);
/* Case 4:
A is W, B is above W
Classically: The change generates a FocusOut on W with a detail of
Ancestor or Nonlinear
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, the event should be suppressed
Case 3B: Otherwise, if there is at least one other focus in a
descendant of W
F(W) changes from W to a descendant of W. The detail field
is set to Inferior
Case 3C: Otherwise:
The focus window moves from W to a window above W.
The detail may need to be changed from Ancestor to Nonlinear or
vice versa depending on the the new F(W)
*/
if (!HasFocus(A)) {
WindowPtr child = FirstFocusChild(A);
if (child) {
/* NotifyPointer P-A unless P is child or below */
CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
}
else {
/* NotifyPointer P-A */
CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
}
}
CoreFocusOutEvents(dev, A, X, mode, NotifyNonlinearVirtual);
/*
Case 9:
A is a descendant of W, B is a descendant of W
Classically: No events are generated on W
MPX: The focus window stays the same or moves to a different
descendant of W. No events should be generated on W.
Therefore, no event to X.
*/
CoreFocusInEvents(dev, X, B, mode, NotifyNonlinearVirtual);
/* Case 2:
A is above W, B=W
Classically: The move generates an EnterNotify on W with a detail of
Ancestor or Nonlinear
MPX:
Case 2A: There is at least one other focus on W itself
F(W) doesn't change, so the event should be suppressed
Case 2B: Otherwise, if there is at least one other focus in a
descendant
F(W) moves from a descendant to W. detail is changed to Inferior.
Case 2C: Otherwise:
F(W) changes from a window above W to W itself.
The detail may need to be changed from Ancestor to Nonlinear
or vice-versa depending on the previous F(W). */
if (!HasFocus(B)) {
WindowPtr child = FirstFocusChild(B);
if (child) {
CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
/* NotifyPointer B-P unless P is child or below. */
CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
}
else {
CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
/* NotifyPointer B-P unless P is child or below. */
CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
}
}
}
/**
* Focus of dev moves from A to B and A is a descendant of B.
*/
static void
CoreFocusToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
/* Case 4:
A is W, B is above W
Classically: The change generates a FocusOut on W with a detail of
Ancestor or Nonlinear
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, the event should be suppressed
Case 3B: Otherwise, if there is at least one other focus in a
descendant of W
F(W) changes from W to a descendant of W. The detail field
is set to Inferior
Case 3C: Otherwise:
The focus window moves from W to a window above W.
The detail may need to be changed from Ancestor to Nonlinear or
vice versa depending on the the new F(W)
*/
if (!HasFocus(A)) {
WindowPtr child = FirstFocusChild(A);
if (child) {
/* NotifyPointer P-A unless P is child or below */
CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
}
else
CoreFocusEvent(dev, FocusOut, mode, NotifyAncestor, A);
}
CoreFocusOutEvents(dev, A, B, mode, NotifyVirtual);
/* Case 8:
A is a descendant of W, B is W
Classically: A FocusOut is generated on W with a detail of
NotifyInferior
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, the event should be suppressed
Case 3B: Otherwise:
F(W) changes from a descendant to W itself. */
if (!HasFocus(B)) {
CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
/* NotifyPointer B-P unless P is A or below. */
CoreFocusInNotifyPointerEvents(dev, B, A, mode, FALSE);
}
}
/**
* Focus of dev moves from A to B and B is a descendant of A.
*/
static void
CoreFocusToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
{
/* Case 6:
A is W, B is a descendant of W
Classically: A FocusOut is generated on W with a detail of
NotifyInferior
MPX:
Case 3A: There is at least one other focus on W itself
F(W) doesn't change, the event should be suppressed
Case 3B: Otherwise:
F(W) changes from W to a descendant of W. */
if (!HasFocus(A)) {
/* NotifyPointer P-A unless P is B or below */
CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
}
CoreFocusInEvents(dev, A, B, mode, NotifyVirtual);
/* Case 2:
A is above W, B=W
Classically: The move generates an FocusIn on W with a detail of
Ancestor or Nonlinear
MPX:
Case 2A: There is at least one other focus on W itself
F(W) doesn't change, so the event should be suppressed
Case 2B: Otherwise, if there is at least one other focus in a
descendant
F(W) moves from a descendant to W. detail is changed to Inferior.
Case 2C: Otherwise:
F(W) changes from a window above W to W itself.
The detail may need to be changed from Ancestor to Nonlinear
or vice-versa depending on the previous F(W). */
if (!HasFocus(B)) {
WindowPtr child = FirstFocusChild(B);
if (child) {
CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
/* NotifyPointer B-P unless P is child or below. */
CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
}
else
CoreFocusEvent(dev, FocusIn, mode, NotifyAncestor, B);
}
}
static BOOL
HasOtherPointer(WindowPtr win, DeviceIntPtr exclude)
{
int i;
for (i = 0; i < MAXDEVICES; i++)
if (i != exclude->id && PointerWindows[i] == win)
return TRUE;
return FALSE;
}
/**
* Focus moves from PointerRoot to None or from None to PointerRoot.
* Assumption: Neither A nor B are valid windows.
*/
static void
CoreFocusPointerRootNoneSwitch(DeviceIntPtr dev, WindowPtr A, /* PointerRootWin or NoneWin */
WindowPtr B, /* NoneWin or PointerRootWin */
int mode)
{
WindowPtr root;
int i;
int nscreens = screenInfo.numScreens;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
nscreens = 1;
#endif
for (i = 0; i < nscreens; i++) {
root = screenInfo.screens[i]->root;
if (!HasOtherPointer(root, GetMaster(dev, POINTER_OR_FLOAT)) &&
!FirstFocusChild(root)) {
/* If pointer was on PointerRootWin and changes to NoneWin, and
* the pointer paired with dev is below the current root window,
* do a NotifyPointer run. */
if (dev->focus && dev->focus->win == PointerRootWin &&
B != PointerRootWin) {
WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
if (ptrwin && IsParent(root, ptrwin))
CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
TRUE);
}
CoreFocusEvent(dev, FocusOut, mode,
A ? NotifyPointerRoot : NotifyDetailNone, root);
CoreFocusEvent(dev, FocusIn, mode,
B ? NotifyPointerRoot : NotifyDetailNone, root);
if (B == PointerRootWin)
CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
}
}
}
/**
* Focus moves from window A to PointerRoot or to None.
* Assumption: A is a valid window and not PointerRoot or None.
*/
static void
CoreFocusToPointerRootOrNone(DeviceIntPtr dev, WindowPtr A, WindowPtr B, /* PointerRootWin or NoneWin */
int mode)
{
WindowPtr root;
int i;
int nscreens = screenInfo.numScreens;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
nscreens = 1;
#endif
if (!HasFocus(A)) {
WindowPtr child = FirstFocusChild(A);
if (child) {
/* NotifyPointer P-A unless P is B or below */
CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
}
else {
/* NotifyPointer P-A */
CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
}
}
/* NullWindow means we include the root window */
CoreFocusOutEvents(dev, A, NullWindow, mode, NotifyNonlinearVirtual);
for (i = 0; i < nscreens; i++) {
root = screenInfo.screens[i]->root;
if (!HasFocus(root) && !FirstFocusChild(root)) {
CoreFocusEvent(dev, FocusIn, mode,
B ? NotifyPointerRoot : NotifyDetailNone, root);
if (B == PointerRootWin)
CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
}
}
}
/**
* Focus moves from PointerRoot or None to a window B.
* Assumption: B is a valid window and not PointerRoot or None.
*/
static void
CoreFocusFromPointerRootOrNone(DeviceIntPtr dev, WindowPtr A, /* PointerRootWin or NoneWin */
WindowPtr B, int mode)
{
WindowPtr root;
int i;
int nscreens = screenInfo.numScreens;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
nscreens = 1;
#endif
for (i = 0; i < nscreens; i++) {
root = screenInfo.screens[i]->root;
if (!HasFocus(root) && !FirstFocusChild(root)) {
/* If pointer was on PointerRootWin and changes to NoneWin, and
* the pointer paired with dev is below the current root window,
* do a NotifyPointer run. */
if (dev->focus && dev->focus->win == PointerRootWin &&
B != PointerRootWin) {
WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
if (ptrwin)
CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
TRUE);
}
CoreFocusEvent(dev, FocusOut, mode,
A ? NotifyPointerRoot : NotifyDetailNone, root);
}
}
root = B; /* get B's root window */
while (root->parent)
root = root->parent;
if (B != root) {
CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinearVirtual, root);
CoreFocusInEvents(dev, root, B, mode, NotifyNonlinearVirtual);
}
if (!HasFocus(B)) {
WindowPtr child = FirstFocusChild(B);
if (child) {
CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
/* NotifyPointer B-P unless P is child or below. */
CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
}
else {
CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
/* NotifyPointer B-P unless P is child or below. */
CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
}
}
}
static void
CoreFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
{
if (!IsMaster(dev))
return;
SetFocusOut(dev);
if (((to == NullWindow) || (to == PointerRootWin)) &&
((from == NullWindow) || (from == PointerRootWin)))
CoreFocusPointerRootNoneSwitch(dev, from, to, mode);
else if ((to == NullWindow) || (to == PointerRootWin))
CoreFocusToPointerRootOrNone(dev, from, to, mode);
else if ((from == NullWindow) || (from == PointerRootWin))
CoreFocusFromPointerRootOrNone(dev, from, to, mode);
else if (IsParent(from, to))
CoreFocusToDescendant(dev, from, to, mode);
else if (IsParent(to, from))
CoreFocusToAncestor(dev, from, to, mode);
else
CoreFocusNonLinear(dev, from, to, mode);
SetFocusIn(dev, to);
}
static void
DeviceFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
{
int out, in; /* for holding details for to/from
PointerRoot/None */
int i;
int nscreens = screenInfo.numScreens;
SpritePtr sprite = dev->spriteInfo->sprite;
if (from == to)
return;
out = (from == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
in = (to == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
/* wrong values if neither, but then not referenced */
#ifdef PANORAMIX
if (!noPanoramiXExtension)
nscreens = 1;
#endif
if ((to == NullWindow) || (to == PointerRootWin)) {
if ((from == NullWindow) || (from == PointerRootWin)) {
if (from == PointerRootWin)
DeviceFocusOutEvents(dev, sprite->win,
GetCurrentRootWindow(dev), mode,
NotifyPointer);
/* Notify all the roots */
for (i = 0; i < nscreens; i++)
DeviceFocusEvent(dev, XI_FocusOut, mode, out,
screenInfo.screens[i]->root);
}
else {
if (IsParent(from, sprite->win))
DeviceFocusOutEvents(dev, sprite->win, from, mode,
NotifyPointer);
DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
/* next call catches the root too, if the screen changed */
DeviceFocusOutEvents(dev, from, NullWindow, mode,
NotifyNonlinearVirtual);
}
/* Notify all the roots */
for (i = 0; i < nscreens; i++)
DeviceFocusEvent(dev, XI_FocusIn, mode, in,
screenInfo.screens[i]->root);
if (to == PointerRootWin) {
DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), sprite->win,
mode, NotifyPointer);
DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyPointer, sprite->win);
}
}
else {
if ((from == NullWindow) || (from == PointerRootWin)) {
if (from == PointerRootWin)
DeviceFocusOutEvents(dev, sprite->win,
GetCurrentRootWindow(dev), mode,
NotifyPointer);
for (i = 0; i < nscreens; i++)
DeviceFocusEvent(dev, XI_FocusOut, mode, out,
screenInfo.screens[i]->root);
if (to->parent != NullWindow)
DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), to, mode,
NotifyNonlinearVirtual);
DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
if (IsParent(to, sprite->win))
DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer);
}
else {
if (IsParent(to, from)) {
DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyAncestor, from);
DeviceFocusOutEvents(dev, from, to, mode, NotifyVirtual);
DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyInferior, to);
if ((IsParent(to, sprite->win)) &&
(sprite->win != from) &&
(!IsParent(from, sprite->win)) &&
(!IsParent(sprite->win, from)))
DeviceFocusInEvents(dev, to, sprite->win, mode,
NotifyPointer);
}
else if (IsParent(from, to)) {
if ((IsParent(from, sprite->win)) &&
(sprite->win != from) &&
(!IsParent(to, sprite->win)) &&
(!IsParent(sprite->win, to)))
DeviceFocusOutEvents(dev, sprite->win, from, mode,
NotifyPointer);
DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyInferior, from);
DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual);
DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyAncestor, to);
}
else {
/* neither from or to is child of other */
WindowPtr common = CommonAncestor(to, from);
/* common == NullWindow ==> different screens */
if (IsParent(from, sprite->win))
DeviceFocusOutEvents(dev, sprite->win, from, mode,
NotifyPointer);
DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
if (from->parent != NullWindow)
DeviceFocusOutEvents(dev, from, common, mode,
NotifyNonlinearVirtual);
if (to->parent != NullWindow)
DeviceFocusInEvents(dev, common, to, mode,
NotifyNonlinearVirtual);
DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
if (IsParent(to, sprite->win))
DeviceFocusInEvents(dev, to, sprite->win, mode,
NotifyPointer);
}
}
}
}
/**
* Figure out if focus events are necessary and send them to the
* appropriate windows.
*
* @param from Window the focus moved out of.
* @param to Window the focus moved into.
*/
void
DoFocusEvents(DeviceIntPtr pDev, WindowPtr from, WindowPtr to, int mode)
{
if (!IsKeyboardDevice(pDev))
return;
if (from == to)
return;
CoreFocusEvents(pDev, from, to, mode);
DeviceFocusEvents(pDev, from, to, mode);
}