/* * 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 #endif #include #include "windowstr.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 * 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 * * 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. */ static WindowPtr PointerWindows[MAXDEVICES]; /** * Return TRUE if @win has a pointer within its boundaries, excluding child * window. */ static BOOL HasPointer(WindowPtr win) { int i; for (i = 0; i < MAXDEVICES; i++) if (PointerWindows[i] == win) return TRUE; return FALSE; } /** * 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; } /** * 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. */ static void LeaveWindow(DeviceIntPtr dev, WindowPtr win, int mode) { PointerWindows[dev->id] = NULL; } /** * @return The window that is the first ancestor of both a and b. */ 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. * If @core is TRUE, core events are sent, otherwise XI events will be sent. */ static void DeviceEnterNotifies(DeviceIntPtr dev, WindowPtr ancestor, WindowPtr child, int mode, int detail) { WindowPtr parent = child->parent; if (ancestor == parent) return; DeviceEnterNotifies(dev, ancestor, parent, mode, detail); DeviceEnterLeaveEvent(dev, DeviceEnterNotify, 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(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(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, 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, DeviceLeaveNotify, 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(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(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(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(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(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(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 (!dev->isMaster) return; LeaveWindow(dev, from, mode); 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, WindowPtr from, WindowPtr to, int mode) { if (IsParent(from, to)) { DeviceEnterLeaveEvent(dev, DeviceLeaveNotify, mode, NotifyInferior, from, None); DeviceEnterNotifies(dev, from, to, mode, NotifyVirtual); DeviceEnterLeaveEvent(dev, DeviceEnterNotify, mode, NotifyAncestor, to, None); } else if (IsParent(to, from)) { DeviceEnterLeaveEvent(dev, DeviceLeaveNotify, mode, NotifyAncestor, from, None); DeviceLeaveNotifies(dev, from, to, mode, NotifyVirtual); DeviceEnterLeaveEvent(dev, DeviceEnterNotify, 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, DeviceLeaveNotify, mode, NotifyNonlinear, from, None); DeviceLeaveNotifies(dev, from, common, mode, NotifyNonlinearVirtual); DeviceEnterNotifies(dev, common, to, mode, NotifyNonlinearVirtual); DeviceEnterLeaveEvent(dev, DeviceEnterNotify, 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, WindowPtr fromWin, WindowPtr toWin, int mode) { if (!IsPointerDevice(pDev)) return; if (fromWin == toWin) return; CoreEnterLeaveEvents(pDev, fromWin, toWin, mode); DeviceEnterLeaveEvents(pDev, fromWin, toWin, mode); }