diff options
author | Keith Packard <keithp@keithp.com> | 2009-01-11 15:56:28 -0800 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2009-01-11 15:56:28 -0800 |
commit | 02c059ea99a791b9e3643b4fb131af01306b1c23 (patch) | |
tree | ccf76ef02f72d79cb4507116c79858378c76655e | |
parent | c75445754e283dc2a306ef1cf2bbd94b40423640 (diff) |
dix: move focus handling into enterleave.c.server-1.6-enterleave
This commit moves the focus handling from events.c into enterleave.c and
implements a model similar to the core enter/leave model.
For a full description of the model, see:
http://lists.freedesktop.org/archives/xorg/2008-December/041740.html
This commit also gets rid of the focusinout array in the WindowRec, ditching
it in favour of a local array that keeps the current focus window for each
device.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
(cherry picked from commit eb2d7b3d700952ba88c77deacf687b251300e660)
Conflicts:
dix/events.c
include/input.h
Signed-off-by: Keith Packard <keithp@keithp.com>
-rw-r--r-- | dix/enterleave.c | 800 | ||||
-rw-r--r-- | dix/enterleave.h | 25 | ||||
-rw-r--r-- | dix/events.c | 284 | ||||
-rw-r--r-- | dix/window.c | 2 | ||||
-rw-r--r-- | include/input.h | 2 | ||||
-rw-r--r-- | include/windowstr.h | 5 |
6 files changed, 837 insertions, 281 deletions
diff --git a/dix/enterleave.c b/dix/enterleave.c index 659ae052f..8511dea6f 100644 --- a/dix/enterleave.c +++ b/dix/enterleave.c @@ -30,19 +30,24 @@ #include <X11/X.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 in - * the case of multiple pointers. - * Since we can't send more than one Enter or Leave event per window - * to a core client without confusing it, this is a rather complicated +/* @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 @@ -52,6 +57,12 @@ * * 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]; @@ -73,6 +84,10 @@ HasPointer(WindowPtr win) return FALSE; } +/** + * Return TRUE if at least one keyboard focus is set to @win (excluding + * descendants of win). + */ static BOOL HasFocus(WindowPtr win) { @@ -85,6 +100,15 @@ HasFocus(WindowPtr win) } /** + * 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). * @@ -105,7 +129,7 @@ FirstPointerChild(WindowPtr win) } /** - * Search for the first window below @win that has a pointer directly within + * 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 @@ -125,8 +149,6 @@ FirstFocusChild(WindowPtr win) return NULL; } - - /** * Set the presence flag for @dev to mark that it is now in @win. */ @@ -589,3 +611,767 @@ DoEnterLeaveEvents(DeviceIntPtr pDev, DeviceEnterLeaveEvents(pDev, fromWin, toWin, mode); #endif } + +/** + * 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, DeviceFocusOut, 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, DeviceFocusIn, 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 descendat of @exclude or an ancestor of + * @exclude, no events are sent. Note: 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(GetPairedDevice(dev)); + + 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). + * If @inclusive is TRUE, @pwin_parent will receive the event too. + * + * @pwin is the pointer window. + * + * If the current pointer window is a child of @exclude, no events are sent. + */ +static void +CoreFocusInNotifyPointerEvents(DeviceIntPtr dev, + WindowPtr pwin_parent, + WindowPtr exclude, + int mode, + int inclusive) +{ + WindowPtr P; + + P = PointerWin(GetPairedDevice(dev)); + + 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 = WindowTable[i]; + if (!HasOtherPointer(root, GetPairedDevice(dev)) && !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(GetPairedDevice(dev)); + if (ptrwin && IsParent(root, ptrwin)) + CoreFocusOutNotifyPointerEvents(dev, root, None, mode, TRUE); + } + CoreFocusEvent(dev, FocusOut, mode, ((int)A) ? NotifyPointerRoot : NotifyDetailNone, root); + CoreFocusEvent(dev, FocusIn, mode, ((int)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 = WindowTable[i]; + if (!HasFocus(root) && !FirstFocusChild(root)) + { + CoreFocusEvent(dev, FocusIn, mode, ((int)B) ? NotifyPointerRoot : NotifyDetailNone, root); + if (B == PointerRootWin) + CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE); + } + } +} + +/** + * Focus moves from PointerRoot or None to a window @to. + * Assumption: @to 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 = WindowTable[i]; + 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(GetPairedDevice(dev)); + if (ptrwin) + CoreFocusOutNotifyPointerEvents(dev, root, None, mode, TRUE); + } + CoreFocusEvent(dev, FocusOut, mode, ((int)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 (!dev->isMaster) + return; + + SetFocusOut(dev, from); + + 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); +} + +#define RootWindow(dev) dev->spriteInfo->sprite->spriteTrace[0] + +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, RootWindow(dev), mode, + NotifyPointer); + /* Notify all the roots */ + for (i = 0; i < nscreens; i++) + DeviceFocusEvent(dev, FocusOut, mode, out, WindowTable[i]); + } + else + { + if (IsParent(from, sprite->win)) + DeviceFocusOutEvents(dev, sprite->win, from, mode, + NotifyPointer); + DeviceFocusEvent(dev, FocusOut, mode, NotifyNonlinear, from); + /* next call catches the root too, if the screen changed */ + DeviceFocusOutEvents(dev, from->parent, NullWindow, mode, + NotifyNonlinearVirtual); + } + /* Notify all the roots */ + for (i = 0; i < nscreens; i++) + DeviceFocusEvent(dev, FocusIn, mode, in, WindowTable[i]); + if (to == PointerRootWin) + DeviceFocusInEvents(dev, RootWindow(dev), sprite->win, mode, NotifyPointer); + } + else + { + if ((from == NullWindow) || (from == PointerRootWin)) + { + if (from == PointerRootWin) + DeviceFocusOutEvents(dev, sprite->win, RootWindow(dev), mode, + NotifyPointer); + for (i = 0; i < nscreens; i++) + DeviceFocusEvent(dev, FocusOut, mode, out, WindowTable[i]); + if (to->parent != NullWindow) + DeviceFocusInEvents(dev, RootWindow(dev), to, mode, NotifyNonlinearVirtual); + DeviceFocusEvent(dev, FocusIn, mode, NotifyNonlinear, to); + if (IsParent(to, sprite->win)) + DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer); + } + else + { + if (IsParent(to, from)) + { + DeviceFocusEvent(dev, FocusOut, mode, NotifyAncestor, from); + DeviceFocusOutEvents(dev, from->parent, to, mode, + NotifyVirtual); + DeviceFocusEvent(dev, 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, FocusOut, mode, NotifyInferior, from); + DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual); + DeviceFocusEvent(dev, 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, FocusOut, mode, NotifyNonlinear, from); + if (from->parent != NullWindow) + DeviceFocusOutEvents(dev, from->parent, common, mode, + NotifyNonlinearVirtual); + if (to->parent != NullWindow) + DeviceFocusInEvents(dev, common, to, mode, NotifyNonlinearVirtual); + DeviceFocusEvent(dev, 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); +} diff --git a/dix/enterleave.h b/dix/enterleave.h index 99e2e46c9..edca38664 100644 --- a/dix/enterleave.h +++ b/dix/enterleave.h @@ -38,6 +38,13 @@ extern void DoEnterLeaveEvents( int mode ); +extern void DoFocusEvents( + DeviceIntPtr pDev, + WindowPtr fromWin, + WindowPtr toWin, + int mode +); + extern void EnterLeaveEvent( DeviceIntPtr mouse, int type, @@ -67,4 +74,22 @@ extern void EnterWindow(DeviceIntPtr dev, WindowPtr win, int mode); + +extern void CoreFocusEvent(DeviceIntPtr kbd, + int type, + int mode, + int detail, + WindowPtr pWin); + +extern void DeviceFocusEvent(DeviceIntPtr kbd, + int type, + int mode, + int detail, + WindowPtr pWin); + +extern void SetFocusIn(DeviceIntPtr kbd, + WindowPtr win); + +extern void SetFocusOut(DeviceIntPtr dev, + WindowPtr win); #endif /* _ENTERLEAVE_H_ */ diff --git a/dix/events.c b/dix/events.c index d7618c230..5530c012f 100644 --- a/dix/events.c +++ b/dix/events.c @@ -4185,261 +4185,30 @@ CoreEnterLeaveEvent( } } -static void -FocusEvent(DeviceIntPtr dev, int type, int mode, int detail, WindowPtr pWin) +void +CoreFocusEvent(DeviceIntPtr dev, int type, int mode, int detail, WindowPtr pWin) { xEvent event; - int numFoci; /* zero if no device has focus on window */ - Bool sendevent = FALSE; - - if (dev != inputInfo.keyboard) - DeviceFocusEvent(dev, type, mode, detail, pWin); - - /* - * Same procedure as for Enter/Leave events. - * - * Sending multiple core FocusIn/Out events to the same window may confuse - * the client. - * We can send multiple events that have detail NotifyVirtual, - * NotifyNonlinearVirtual, NotifyPointerRoot, NotifyDetailNone or - * NotifyPointer however. - * - * For standard events (NotifyAncestor, NotifyInferior, NotifyNonlinear) - * we only send an FocusIn event for the first kbd to set the focus. A - * FocusOut event is sent for the last kbd to set the focus away from the - * window. - * - * For events with Virtual detail, we send them only to a window that does - * not have a focus from another keyboard. - * - * For a window tree in the form of - * - * A -> Bf -> C -> D - * \ (where B and E have focus) - * -> Ef - * - * If the focus changes from E into D, a FocusOut is sent to E, a - * FocusIn is sent to D, a FocusIn with detail - * NotifyNonlinearVirtual to C and nothing to B. - */ - if (dev->isMaster && type == FocusOut && - (detail != NotifyVirtual && - detail != NotifyNonlinearVirtual && - detail != NotifyPointer && - detail != NotifyPointerRoot && - detail != NotifyDetailNone)) - FOCUS_SEMAPHORE_UNSET(pWin, dev); - - numFoci = FocusSemaphoresIsset(pWin); - - if (!numFoci) - sendevent = TRUE; - else if (mode == NotifyUngrab && FOCUS_SEMAPHORE_ISSET(pWin, dev)) - sendevent = TRUE; - - if (sendevent) - { - event.u.focus.mode = mode; - event.u.u.type = type; - event.u.u.detail = detail; - event.u.focus.window = pWin->drawable.id; - (void)DeliverEventsToWindow(dev, pWin, &event, 1, - filters[dev->id][type], NullGrab, 0); - if ((type == FocusIn) && - ((pWin->eventMask | wOtherEventMasks(pWin)) & KeymapStateMask)) - { - xKeymapEvent ke; - ClientPtr client = clients[CLIENT_ID(pWin->drawable.id)]; - if (XaceHook(XACE_DEVICE_ACCESS, client, dev, FALSE)) - memmove((char *)&ke.map[0], (char *)&dev->key->down[1], 31); - else - bzero((char *)&ke.map[0], 31); - - ke.type = KeymapNotify; - (void)DeliverEventsToWindow(dev, pWin, (xEvent *)&ke, 1, - KeymapStateMask, NullGrab, 0); - } - } - - if (dev->isMaster && type == FocusIn && - (detail != NotifyVirtual && - detail != NotifyNonlinearVirtual && - detail != NotifyPointer && - detail != NotifyPointerRoot && - detail != NotifyDetailNone)) - FOCUS_SEMAPHORE_SET(pWin, dev); -} - - /* - * recursive because it is easier - * no-op if child not descended from ancestor - */ -static Bool -FocusInEvents( - DeviceIntPtr dev, - WindowPtr ancestor, WindowPtr child, WindowPtr skipChild, - int mode, int detail, - Bool doAncestor) -{ - if (child == NullWindow) - return ancestor == NullWindow; - if (ancestor == child) - { - if (doAncestor) - FocusEvent(dev, FocusIn, mode, detail, child); - return TRUE; - } - if (FocusInEvents(dev, ancestor, child->parent, skipChild, mode, detail, - doAncestor)) + event.u.focus.mode = mode; + event.u.u.type = type; + event.u.u.detail = detail; + event.u.focus.window = pWin->drawable.id; + (void)DeliverEventsToWindow(dev, pWin, &event, 1, + filters[dev->id][type], NullGrab, 0); + if ((type == FocusIn) && + ((pWin->eventMask | wOtherEventMasks(pWin)) & KeymapStateMask)) { - if (child != skipChild) - FocusEvent(dev, FocusIn, mode, detail, child); - return TRUE; - } - return FALSE; -} - -/* dies horribly if ancestor is not an ancestor of child */ -static void -FocusOutEvents( - DeviceIntPtr dev, - WindowPtr child, WindowPtr ancestor, - int mode, int detail, - Bool doAncestor) -{ - WindowPtr pWin; - - for (pWin = child; pWin != ancestor; pWin = pWin->parent) - FocusEvent(dev, FocusOut, mode, detail, pWin); - if (doAncestor) - FocusEvent(dev, FocusOut, mode, detail, ancestor); -} - -void -DoFocusEvents(DeviceIntPtr dev, WindowPtr fromWin, WindowPtr toWin, int mode) -{ - int out, in; /* for holding details for to/from - PointerRoot/None */ - int i; - SpritePtr pSprite = dev->spriteInfo->sprite; - - if (fromWin == toWin) - return; - out = (fromWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; - in = (toWin == NoneWin) ? NotifyDetailNone : NotifyPointerRoot; - /* wrong values if neither, but then not referenced */ + xKeymapEvent ke; + ClientPtr client = clients[CLIENT_ID(pWin->drawable.id)]; + if (XaceHook(XACE_DEVICE_ACCESS, client, dev, FALSE)) + memmove((char *)&ke.map[0], (char *)&dev->key->down[1], 31); + else + bzero((char *)&ke.map[0], 31); - if ((toWin == NullWindow) || (toWin == PointerRootWin)) - { - if ((fromWin == NullWindow) || (fromWin == PointerRootWin)) - { - if (fromWin == PointerRootWin) - FocusOutEvents(dev, pSprite->win, RootWindow(dev), mode, - NotifyPointer, TRUE); - /* Notify all the roots */ -#ifdef PANORAMIX - if ( !noPanoramiXExtension ) - FocusEvent(dev, FocusOut, mode, out, WindowTable[0]); - else -#endif - for (i=0; i<screenInfo.numScreens; i++) - FocusEvent(dev, FocusOut, mode, out, WindowTable[i]); - } - else - { - if (IsParent(fromWin, pSprite->win)) - FocusOutEvents(dev, pSprite->win, fromWin, mode, NotifyPointer, - FALSE); - FocusEvent(dev, FocusOut, mode, NotifyNonlinear, fromWin); - /* next call catches the root too, if the screen changed */ - FocusOutEvents(dev, fromWin->parent, NullWindow, mode, - NotifyNonlinearVirtual, FALSE); - } - /* Notify all the roots */ -#ifdef PANORAMIX - if ( !noPanoramiXExtension ) - FocusEvent(dev, FocusIn, mode, in, WindowTable[0]); - else -#endif - for (i=0; i<screenInfo.numScreens; i++) - FocusEvent(dev, FocusIn, mode, in, WindowTable[i]); - if (toWin == PointerRootWin) - (void)FocusInEvents(dev, RootWindow(dev), pSprite->win, - NullWindow, mode, NotifyPointer, TRUE); - } - else - { - if ((fromWin == NullWindow) || (fromWin == PointerRootWin)) - { - if (fromWin == PointerRootWin) - FocusOutEvents(dev, pSprite->win, RootWindow(dev), mode, - NotifyPointer, TRUE); -#ifdef PANORAMIX - if ( !noPanoramiXExtension ) - FocusEvent(dev, FocusOut, mode, out, WindowTable[0]); - else -#endif - for (i=0; i<screenInfo.numScreens; i++) - FocusEvent(dev, FocusOut, mode, out, WindowTable[i]); - if (toWin->parent != NullWindow) - (void)FocusInEvents(dev, RootWindow(dev), toWin, toWin, mode, - NotifyNonlinearVirtual, TRUE); - FocusEvent(dev, FocusIn, mode, NotifyNonlinear, toWin); - if (IsParent(toWin, pSprite->win)) - (void)FocusInEvents(dev, toWin, pSprite->win, NullWindow, mode, - NotifyPointer, FALSE); - } - else - { - if (IsParent(toWin, fromWin)) - { - FocusEvent(dev, FocusOut, mode, NotifyAncestor, fromWin); - FocusOutEvents(dev, fromWin->parent, toWin, mode, - NotifyVirtual, FALSE); - FocusEvent(dev, FocusIn, mode, NotifyInferior, toWin); - if ((IsParent(toWin, pSprite->win)) && - (pSprite->win != fromWin) && - (!IsParent(fromWin, pSprite->win)) && - (!IsParent(pSprite->win, fromWin))) - (void)FocusInEvents(dev, toWin, pSprite->win, NullWindow, - mode, NotifyPointer, FALSE); - } - else - if (IsParent(fromWin, toWin)) - { - if ((IsParent(fromWin, pSprite->win)) && - (pSprite->win != fromWin) && - (!IsParent(toWin, pSprite->win)) && - (!IsParent(pSprite->win, toWin))) - FocusOutEvents(dev, pSprite->win, fromWin, mode, - NotifyPointer, FALSE); - FocusEvent(dev, FocusOut, mode, NotifyInferior, fromWin); - (void)FocusInEvents(dev, fromWin, toWin, toWin, mode, - NotifyVirtual, FALSE); - FocusEvent(dev, FocusIn, mode, NotifyAncestor, toWin); - } - else - { - /* neither fromWin or toWin is child of other */ - WindowPtr common = CommonAncestor(toWin, fromWin); - /* common == NullWindow ==> different screens */ - if (IsParent(fromWin, pSprite->win)) - FocusOutEvents(dev, pSprite->win, fromWin, mode, - NotifyPointer, FALSE); - FocusEvent(dev, FocusOut, mode, NotifyNonlinear, fromWin); - if (fromWin->parent != NullWindow) - FocusOutEvents(dev, fromWin->parent, common, mode, - NotifyNonlinearVirtual, FALSE); - if (toWin->parent != NullWindow) - (void)FocusInEvents(dev, common, toWin, toWin, mode, - NotifyNonlinearVirtual, FALSE); - FocusEvent(dev, FocusIn, mode, NotifyNonlinear, toWin); - if (IsParent(toWin, pSprite->win)) - (void)FocusInEvents(dev, toWin, pSprite->win, NullWindow, - mode, NotifyPointer, FALSE); - } - } + ke.type = KeymapNotify; + (void)DeliverEventsToWindow(dev, pWin, (xEvent *)&ke, 1, + KeymapStateMask, NullGrab, 0); } } @@ -5987,18 +5756,3 @@ ExtGrabDevice(ClientPtr client, return GrabSuccess; } -/* - * @return Zero if no devices has focus on the window, non-zero otherwise. - */ -int -FocusSemaphoresIsset(WindowPtr win) -{ - int set = 0; - int i; - - for (i = 0; i < (MAXDEVICES + 7)/8; i++) - set += win->focusinout[i]; - - return set; -} - diff --git a/dix/window.c b/dix/window.c index 36c52ceba..c19990ac1 100644 --- a/dix/window.c +++ b/dix/window.c @@ -300,8 +300,6 @@ SetWindowToDefaults(WindowPtr pWin) pWin->redirectDraw = RedirectDrawNone; pWin->forcedBG = FALSE; - memset(pWin->focusinout, 0, sizeof(pWin->focusinout)); - #ifdef ROOTLESS pWin->rootlessUnhittable = FALSE; #endif diff --git a/include/input.h b/include/input.h index 82ee2360c..cf7f82407 100644 --- a/include/input.h +++ b/include/input.h @@ -509,8 +509,6 @@ extern int AllocMasterDevice(ClientPtr client, extern void DeepCopyDeviceClasses(DeviceIntPtr from, DeviceIntPtr to); -extern int FocusSemaphoresIsset(WindowPtr win); - /* Implemented by the DDX. */ extern int NewInputDeviceRequest( InputOption *options, diff --git a/include/windowstr.h b/include/windowstr.h index c8f35ee3e..a31fdc1df 100644 --- a/include/windowstr.h +++ b/include/windowstr.h @@ -188,11 +188,6 @@ typedef struct _Window { #ifdef ROOTLESS unsigned rootlessUnhittable:1; /* doesn't hit-test */ #endif - /* Used to maintain semantics of core protocol for Enter/LeaveNotifies and - * FocusIn/Out events for multiple pointers/keyboards. Each device ID - * corresponds to one bit. If set, the device is in the window/has focus. - */ - char focusinout[(MAXDEVICES + 7)/8]; } WindowRec; /* |