enterleave.c revision 875c6e4f
14642e01fSmrg/*
24642e01fSmrg * Copyright © 2008 Red Hat, Inc.
34642e01fSmrg *
44642e01fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
54642e01fSmrg * copy of this software and associated documentation files (the "Software"),
64642e01fSmrg * to deal in the Software without restriction, including without limitation
74642e01fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
84642e01fSmrg * and/or sell copies of the Software, and to permit persons to whom the
94642e01fSmrg * Software is furnished to do so, subject to the following conditions:
104642e01fSmrg *
114642e01fSmrg * The above copyright notice and this permission notice (including the next
124642e01fSmrg * paragraph) shall be included in all copies or substantial portions of the
134642e01fSmrg * Software.
144642e01fSmrg *
154642e01fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
164642e01fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
174642e01fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
184642e01fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
194642e01fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
204642e01fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
214642e01fSmrg * DEALINGS IN THE SOFTWARE.
224642e01fSmrg *
234642e01fSmrg * Authors: Peter Hutterer
244642e01fSmrg *
254642e01fSmrg */
264642e01fSmrg
274642e01fSmrg#ifdef HAVE_DIX_CONFIG_H
284642e01fSmrg#include <dix-config.h>
294642e01fSmrg#endif
304642e01fSmrg
314642e01fSmrg#include <X11/X.h>
324202a189Smrg#include <X11/extensions/XI2.h>
33f7df2e56Smrg#include <X11/extensions/XIproto.h>
34f7df2e56Smrg#include <X11/extensions/XI2proto.h>
354202a189Smrg#include "inputstr.h"
364642e01fSmrg#include "windowstr.h"
374642e01fSmrg#include "scrnintstr.h"
384642e01fSmrg#include "exglobals.h"
394642e01fSmrg#include "enterleave.h"
40f7df2e56Smrg#include "eventconvert.h"
41f7df2e56Smrg#include "xkbsrv.h"
42f7df2e56Smrg#include "inpututils.h"
434642e01fSmrg
444202a189Smrg/**
454202a189Smrg * @file
464202a189Smrg * This file describes the model for sending core enter/leave events and
474202a189Smrg * focus in/out in the case of multiple pointers/keyboard foci.
484202a189Smrg *
494642e01fSmrg * Since we can't send more than one Enter or Leave/Focus in or out event per
504642e01fSmrg * window to a core client without confusing it, this is a rather complicated
514642e01fSmrg * approach.
524642e01fSmrg *
534642e01fSmrg * For a full description of the enter/leave model from a window's
544642e01fSmrg * perspective, see
554642e01fSmrg * http://lists.freedesktop.org/archives/xorg/2008-August/037606.html
564642e01fSmrg *
574642e01fSmrg * For a full description of the focus in/out model from a window's
584642e01fSmrg * perspective, see
595a112b11Smrg * https://lists.freedesktop.org/archives/xorg/2008-December/041684.html
604642e01fSmrg *
614642e01fSmrg * Additional notes:
624202a189Smrg * - The core protocol spec says that "In a LeaveNotify event, if a child of the
634642e01fSmrg * event window contains the initial position of the pointer, then the child
644642e01fSmrg * component is set to that child. Otherwise, it is None.  For an EnterNotify
654642e01fSmrg * event, if a child of the event window contains the final pointer position,
664642e01fSmrg * then the child component is set to that child. Otherwise, it is None."
674642e01fSmrg *
684642e01fSmrg * By inference, this means that only NotifyVirtual or NotifyNonlinearVirtual
694642e01fSmrg * events may have a subwindow set to other than None.
704642e01fSmrg *
714202a189Smrg * - NotifyPointer events may be sent if the focus changes from window A to
724642e01fSmrg * B. The assumption used in this model is that NotifyPointer events are only
734642e01fSmrg * sent for the pointer paired with the keyboard that is involved in the focus
744642e01fSmrg * events. For example, if F(W) changes because of keyboard 2, then
754642e01fSmrg * NotifyPointer events are only sent for pointer 2.
764642e01fSmrg */
774642e01fSmrg
784642e01fSmrgstatic WindowPtr PointerWindows[MAXDEVICES];
794642e01fSmrgstatic WindowPtr FocusWindows[MAXDEVICES];
804642e01fSmrg
814642e01fSmrg/**
824202a189Smrg * Return TRUE if 'win' has a pointer within its boundaries, excluding child
834642e01fSmrg * window.
844642e01fSmrg */
854642e01fSmrgstatic BOOL
864202a189SmrgHasPointer(DeviceIntPtr dev, WindowPtr win)
874642e01fSmrg{
884642e01fSmrg    int i;
894642e01fSmrg
904202a189Smrg    /* FIXME: The enter/leave model does not cater for grabbed devices. For
914202a189Smrg     * now, a quickfix: if the device about to send an enter/leave event to
924202a189Smrg     * a window is grabbed, assume there is no pointer in that window.
934202a189Smrg     * Fixes fdo 27804.
944202a189Smrg     * There isn't enough beer in my fridge to fix this properly.
954202a189Smrg     */
964202a189Smrg    if (dev->deviceGrab.grab)
974202a189Smrg        return FALSE;
984202a189Smrg
994642e01fSmrg    for (i = 0; i < MAXDEVICES; i++)
1004642e01fSmrg        if (PointerWindows[i] == win)
1014642e01fSmrg            return TRUE;
1024642e01fSmrg
1034642e01fSmrg    return FALSE;
1044642e01fSmrg}
1054642e01fSmrg
1064642e01fSmrg/**
1074202a189Smrg * Return TRUE if at least one keyboard focus is set to 'win' (excluding
1084642e01fSmrg * descendants of win).
1094642e01fSmrg */
1104642e01fSmrgstatic BOOL
1114642e01fSmrgHasFocus(WindowPtr win)
1124642e01fSmrg{
1134642e01fSmrg    int i;
114f7df2e56Smrg
1154642e01fSmrg    for (i = 0; i < MAXDEVICES; i++)
1164642e01fSmrg        if (FocusWindows[i] == win)
1174642e01fSmrg            return TRUE;
1184642e01fSmrg
1194642e01fSmrg    return FALSE;
1204642e01fSmrg}
1214642e01fSmrg
1224642e01fSmrg/**
1234202a189Smrg * Return the window the device dev is currently on.
1244642e01fSmrg */
1254642e01fSmrgstatic WindowPtr
1264642e01fSmrgPointerWin(DeviceIntPtr dev)
1274642e01fSmrg{
1284642e01fSmrg    return PointerWindows[dev->id];
1294642e01fSmrg}
1304642e01fSmrg
1314642e01fSmrg/**
1324202a189Smrg * Search for the first window below 'win' that has a pointer directly within
1335a112b11Smrg * its boundaries (excluding boundaries of its own descendants).
1344642e01fSmrg *
1354642e01fSmrg * @return The child window that has the pointer within its boundaries or
1364642e01fSmrg *         NULL.
1374642e01fSmrg */
1384642e01fSmrgstatic WindowPtr
1394642e01fSmrgFirstPointerChild(WindowPtr win)
1404642e01fSmrg{
1414642e01fSmrg    int i;
142f7df2e56Smrg
143f7df2e56Smrg    for (i = 0; i < MAXDEVICES; i++) {
1444642e01fSmrg        if (PointerWindows[i] && IsParent(win, PointerWindows[i]))
1454642e01fSmrg            return PointerWindows[i];
1464642e01fSmrg    }
1474642e01fSmrg
1484642e01fSmrg    return NULL;
1494642e01fSmrg}
1504642e01fSmrg
1514642e01fSmrg/**
1524202a189Smrg * Search for the first window below 'win' that has a focus directly within
1535a112b11Smrg * its boundaries (excluding boundaries of its own descendants).
1544642e01fSmrg *
1554642e01fSmrg * @return The child window that has the pointer within its boundaries or
1564642e01fSmrg *         NULL.
1574642e01fSmrg */
1584642e01fSmrgstatic WindowPtr
1594642e01fSmrgFirstFocusChild(WindowPtr win)
1604642e01fSmrg{
1614642e01fSmrg    int i;
162f7df2e56Smrg
163f7df2e56Smrg    for (i = 0; i < MAXDEVICES; i++) {
1644642e01fSmrg        if (FocusWindows[i] && FocusWindows[i] != PointerRootWin &&
1654642e01fSmrg            IsParent(win, FocusWindows[i]))
1664642e01fSmrg            return FocusWindows[i];
1674642e01fSmrg    }
1684642e01fSmrg
1694642e01fSmrg    return NULL;
1704642e01fSmrg}
1714642e01fSmrg
1724642e01fSmrg/**
1734202a189Smrg * Set the presence flag for dev to mark that it is now in 'win'.
1744642e01fSmrg */
1754642e01fSmrgvoid
1764642e01fSmrgEnterWindow(DeviceIntPtr dev, WindowPtr win, int mode)
1774642e01fSmrg{
1784642e01fSmrg    PointerWindows[dev->id] = win;
1794642e01fSmrg}
1804642e01fSmrg
1814642e01fSmrg/**
1824202a189Smrg * Unset the presence flag for dev to mark that it is not in 'win' anymore.
1834642e01fSmrg */
1844202a189Smrgvoid
1854202a189SmrgLeaveWindow(DeviceIntPtr dev)
1864642e01fSmrg{
1874642e01fSmrg    PointerWindows[dev->id] = NULL;
1884642e01fSmrg}
1894642e01fSmrg
1904642e01fSmrg/**
1914202a189Smrg * Set the presence flag for dev to mark that it is now in 'win'.
1924642e01fSmrg */
1934642e01fSmrgvoid
1944642e01fSmrgSetFocusIn(DeviceIntPtr dev, WindowPtr win)
1954642e01fSmrg{
1964642e01fSmrg    FocusWindows[dev->id] = win;
1974642e01fSmrg}
1984642e01fSmrg
1994642e01fSmrg/**
2004202a189Smrg * Unset the presence flag for dev to mark that it is not in 'win' anymore.
2014642e01fSmrg */
2024642e01fSmrgvoid
2034202a189SmrgSetFocusOut(DeviceIntPtr dev)
2044642e01fSmrg{
2054642e01fSmrg    FocusWindows[dev->id] = NULL;
2064642e01fSmrg}
2074642e01fSmrg
2084642e01fSmrg/**
2094202a189Smrg * Return the common ancestor of 'a' and 'b' (if one exists).
2104202a189Smrg * @param a A window with the same ancestor as b.
2114202a189Smrg * @param b A window with the same ancestor as a.
2124202a189Smrg * @return The window that is the first ancestor of both 'a' and 'b', or the
2134202a189Smrg *         NullWindow if they do not have a common ancestor.
2144642e01fSmrg */
215f7df2e56Smrgstatic WindowPtr
216f7df2e56SmrgCommonAncestor(WindowPtr a, WindowPtr b)
2174642e01fSmrg{
2184642e01fSmrg    for (b = b->parent; b; b = b->parent)
219f7df2e56Smrg        if (IsParent(b, a))
220f7df2e56Smrg            return b;
2214642e01fSmrg    return NullWindow;
2224642e01fSmrg}
2234642e01fSmrg
2244642e01fSmrg/**
2254202a189Smrg * Send enter notifies to all windows between 'ancestor' and 'child' (excluding
2264642e01fSmrg * both). Events are sent running up the window hierarchy. This function
2274642e01fSmrg * recurses.
2284642e01fSmrg */
2294642e01fSmrgstatic void
2304642e01fSmrgDeviceEnterNotifies(DeviceIntPtr dev,
231f7df2e56Smrg                    int sourceid,
232f7df2e56Smrg                    WindowPtr ancestor, WindowPtr child, int mode, int detail)
2334642e01fSmrg{
234f7df2e56Smrg    WindowPtr parent = child->parent;
2354642e01fSmrg
2364642e01fSmrg    if (ancestor == parent)
237f7df2e56Smrg        return;
2384202a189Smrg    DeviceEnterNotifies(dev, sourceid, ancestor, parent, mode, detail);
2394202a189Smrg    DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, detail, parent,
2404642e01fSmrg                          child->drawable.id);
2414642e01fSmrg}
2424642e01fSmrg
2434642e01fSmrg/**
2444202a189Smrg * Send enter notifies to all windows between 'ancestor' and 'child' (excluding
2454642e01fSmrg * both). Events are sent running down the window hierarchy. This function
2464642e01fSmrg * recurses.
2474642e01fSmrg */
2484642e01fSmrgstatic void
2494642e01fSmrgCoreEnterNotifies(DeviceIntPtr dev,
250f7df2e56Smrg                  WindowPtr ancestor, WindowPtr child, int mode, int detail)
2514642e01fSmrg{
252f7df2e56Smrg    WindowPtr parent = child->parent;
253f7df2e56Smrg
2544642e01fSmrg    if (ancestor == parent)
255f7df2e56Smrg        return;
2564642e01fSmrg    CoreEnterNotifies(dev, ancestor, parent, mode, detail);
2574642e01fSmrg
2584642e01fSmrg    /* Case 3:
259f7df2e56Smrg       A is above W, B is a descendant
260f7df2e56Smrg
261f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
262f7df2e56Smrg       Virtual or NonlinearVirtual
263f7df2e56Smrg
264f7df2e56Smrg       MPX:
265f7df2e56Smrg       Case 3A: There is at least one other pointer on W itself
266f7df2e56Smrg       P(W) doesn't change, so the event should be suppressed
267f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other pointer in a
268f7df2e56Smrg       descendant
269f7df2e56Smrg       P(W) stays on the same descendant, or changes to a different
270f7df2e56Smrg       descendant. The event should be suppressed.
271f7df2e56Smrg       Case 3C: Otherwise:
272f7df2e56Smrg       P(W) moves from a window above W to a descendant. The subwindow
273f7df2e56Smrg       field is set to the child containing the descendant. The detail
274f7df2e56Smrg       may need to be changed from Virtual to NonlinearVirtual depending
275f7df2e56Smrg       on the previous P(W). */
2764642e01fSmrg
2774202a189Smrg    if (!HasPointer(dev, parent) && !FirstPointerChild(parent))
278f7df2e56Smrg        CoreEnterLeaveEvent(dev, EnterNotify, mode, detail, parent,
279f7df2e56Smrg                            child->drawable.id);
2804642e01fSmrg}
2814642e01fSmrg
2824642e01fSmrgstatic void
2834642e01fSmrgCoreLeaveNotifies(DeviceIntPtr dev,
284f7df2e56Smrg                  WindowPtr child, WindowPtr ancestor, int mode, int detail)
2854642e01fSmrg{
286f7df2e56Smrg    WindowPtr win;
2874642e01fSmrg
2884642e01fSmrg    if (ancestor == child)
2894642e01fSmrg        return;
2904642e01fSmrg
291f7df2e56Smrg    for (win = child->parent; win != ancestor; win = win->parent) {
2924642e01fSmrg        /*Case 7:
293f7df2e56Smrg           A is a descendant of W, B is above W
294f7df2e56Smrg
295f7df2e56Smrg           Classically: A LeaveNotify is generated on W with a detail of Virtual
296f7df2e56Smrg           or NonlinearVirtual.
297f7df2e56Smrg
298f7df2e56Smrg           MPX:
299f7df2e56Smrg           Case 3A: There is at least one other pointer on W itself
300f7df2e56Smrg           P(W) doesn't change, the event should be suppressed.
301f7df2e56Smrg           Case 3B: Otherwise, if there is at least one other pointer in a
302f7df2e56Smrg           descendant
303f7df2e56Smrg           P(W) stays on the same descendant, or changes to a different
304f7df2e56Smrg           descendant. The event should be suppressed.
305f7df2e56Smrg           Case 3C: Otherwise:
306f7df2e56Smrg           P(W) changes from the descendant of W to a window above W.
307f7df2e56Smrg           The detail may need to be changed from Virtual to NonlinearVirtual
308f7df2e56Smrg           or vice-versa depending on the new P(W). */
3094642e01fSmrg
3104642e01fSmrg        /* If one window has a pointer or a child with a pointer, skip some
3114642e01fSmrg         * work and exit. */
3124202a189Smrg        if (HasPointer(dev, win) || FirstPointerChild(win))
3134642e01fSmrg            return;
3144642e01fSmrg
315f7df2e56Smrg        CoreEnterLeaveEvent(dev, LeaveNotify, mode, detail, win,
316f7df2e56Smrg                            child->drawable.id);
3174642e01fSmrg
3184642e01fSmrg        child = win;
3194642e01fSmrg    }
3204642e01fSmrg}
3214642e01fSmrg
3224642e01fSmrg/**
3234202a189Smrg * Send leave notifies to all windows between 'child' and 'ancestor'.
3244642e01fSmrg * Events are sent running up the hierarchy.
3254642e01fSmrg */
3264642e01fSmrgstatic void
3274642e01fSmrgDeviceLeaveNotifies(DeviceIntPtr dev,
328f7df2e56Smrg                    int sourceid,
329f7df2e56Smrg                    WindowPtr child, WindowPtr ancestor, int mode, int detail)
3304642e01fSmrg{
331f7df2e56Smrg    WindowPtr win;
3324642e01fSmrg
3334642e01fSmrg    if (ancestor == child)
334f7df2e56Smrg        return;
335f7df2e56Smrg    for (win = child->parent; win != ancestor; win = win->parent) {
3364202a189Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, detail, win,
337f7df2e56Smrg                              child->drawable.id);
3384642e01fSmrg        child = win;
3394642e01fSmrg    }
3404642e01fSmrg}
3414642e01fSmrg
3424642e01fSmrg/**
3434202a189Smrg * Pointer dev moves from A to B and A neither a descendant of B nor is
3444202a189Smrg * B a descendant of A.
3454642e01fSmrg */
3464642e01fSmrgstatic void
347f7df2e56SmrgCoreEnterLeaveNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
3484642e01fSmrg{
3494642e01fSmrg    WindowPtr X = CommonAncestor(A, B);
350f7df2e56Smrg
3514642e01fSmrg    /* Case 4:
352f7df2e56Smrg       A is W, B is above W
3534642e01fSmrg
354f7df2e56Smrg       Classically: The move generates a LeaveNotify on W with a detail of
3554642e01fSmrg       Ancestor or Nonlinear
3564642e01fSmrg
357f7df2e56Smrg       MPX:
358f7df2e56Smrg       Case 3A: There is at least one other pointer on W itself
359f7df2e56Smrg       P(W) doesn't change, the event should be suppressed
360f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other pointer in a
361f7df2e56Smrg       descendant of W
362f7df2e56Smrg       P(W) changes from W to a descendant of W. The subwindow field
363f7df2e56Smrg       is set to the child containing the new P(W), the detail field
364f7df2e56Smrg       is set to Inferior
365f7df2e56Smrg       Case 3C: Otherwise:
366f7df2e56Smrg       The pointer window moves from W to a window above W.
367f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
368f7df2e56Smrg       vice versa depending on the the new P(W)
3694642e01fSmrg     */
3704642e01fSmrg
371f7df2e56Smrg    if (!HasPointer(dev, A)) {
3724642e01fSmrg        WindowPtr child = FirstPointerChild(A);
373f7df2e56Smrg
3744642e01fSmrg        if (child)
375f7df2e56Smrg            CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A,
376f7df2e56Smrg                                None);
3774642e01fSmrg        else
378f7df2e56Smrg            CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyNonlinear, A,
379f7df2e56Smrg                                None);
3804642e01fSmrg    }
3814642e01fSmrg
3824642e01fSmrg    CoreLeaveNotifies(dev, A, X, mode, NotifyNonlinearVirtual);
3834642e01fSmrg
3844642e01fSmrg    /*
385f7df2e56Smrg       Case 9:
386f7df2e56Smrg       A is a descendant of W, B is a descendant of W
3874642e01fSmrg
388f7df2e56Smrg       Classically: No events are generated on W
389f7df2e56Smrg       MPX: The pointer window stays the same or moves to a different
390f7df2e56Smrg       descendant of W. No events should be generated on W.
3914642e01fSmrg
3924642e01fSmrg       Therefore, no event to X.
393f7df2e56Smrg     */
3944642e01fSmrg
3954642e01fSmrg    CoreEnterNotifies(dev, X, B, mode, NotifyNonlinearVirtual);
3964642e01fSmrg
3974642e01fSmrg    /* Case 2:
398f7df2e56Smrg       A is above W, B=W
399f7df2e56Smrg
400f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
401f7df2e56Smrg       Ancestor or Nonlinear
402f7df2e56Smrg
403f7df2e56Smrg       MPX:
404f7df2e56Smrg       Case 2A: There is at least one other pointer on W itself
405f7df2e56Smrg       P(W) doesn't change, so the event should be suppressed
406f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other pointer in a
407f7df2e56Smrg       descendant
408f7df2e56Smrg       P(W) moves from a descendant to W. detail is changed to Inferior,
409f7df2e56Smrg       subwindow is set to the child containing the previous P(W)
410f7df2e56Smrg       Case 2C: Otherwise:
411f7df2e56Smrg       P(W) changes from a window above W to W itself.
412f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
413f7df2e56Smrg       or vice-versa depending on the previous P(W). */
414f7df2e56Smrg
415f7df2e56Smrg    if (!HasPointer(dev, B)) {
416f7df2e56Smrg        WindowPtr child = FirstPointerChild(B);
417f7df2e56Smrg
418f7df2e56Smrg        if (child)
419f7df2e56Smrg            CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B,
420f7df2e56Smrg                                None);
421f7df2e56Smrg        else
422f7df2e56Smrg            CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyNonlinear, B,
423f7df2e56Smrg                                None);
424f7df2e56Smrg    }
4254642e01fSmrg}
4264642e01fSmrg
4274642e01fSmrg/**
4284202a189Smrg * Pointer dev moves from A to B and A is a descendant of B.
4294642e01fSmrg */
4304642e01fSmrgstatic void
431f7df2e56SmrgCoreEnterLeaveToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
4324642e01fSmrg{
4334642e01fSmrg    /* Case 4:
434f7df2e56Smrg       A is W, B is above W
4354642e01fSmrg
436f7df2e56Smrg       Classically: The move generates a LeaveNotify on W with a detail of
4374642e01fSmrg       Ancestor or Nonlinear
4384642e01fSmrg
439f7df2e56Smrg       MPX:
440f7df2e56Smrg       Case 3A: There is at least one other pointer on W itself
441f7df2e56Smrg       P(W) doesn't change, the event should be suppressed
442f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other pointer in a
443f7df2e56Smrg       descendant of W
444f7df2e56Smrg       P(W) changes from W to a descendant of W. The subwindow field
445f7df2e56Smrg       is set to the child containing the new P(W), the detail field
446f7df2e56Smrg       is set to Inferior
447f7df2e56Smrg       Case 3C: Otherwise:
448f7df2e56Smrg       The pointer window moves from W to a window above W.
449f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
450f7df2e56Smrg       vice versa depending on the the new P(W)
4514642e01fSmrg     */
452f7df2e56Smrg    if (!HasPointer(dev, A)) {
4534642e01fSmrg        WindowPtr child = FirstPointerChild(A);
454f7df2e56Smrg
4554642e01fSmrg        if (child)
456f7df2e56Smrg            CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A,
457f7df2e56Smrg                                None);
4584642e01fSmrg        else
459f7df2e56Smrg            CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyAncestor, A,
460f7df2e56Smrg                                None);
4614642e01fSmrg    }
4624642e01fSmrg
4634642e01fSmrg    CoreLeaveNotifies(dev, A, B, mode, NotifyVirtual);
4644642e01fSmrg
4654642e01fSmrg    /* Case 8:
466f7df2e56Smrg       A is a descendant of W, B is W
4674642e01fSmrg
468f7df2e56Smrg       Classically: A EnterNotify is generated on W with a detail of
469f7df2e56Smrg       NotifyInferior
4704642e01fSmrg
471f7df2e56Smrg       MPX:
472f7df2e56Smrg       Case 3A: There is at least one other pointer on W itself
473f7df2e56Smrg       P(W) doesn't change, the event should be suppressed
474f7df2e56Smrg       Case 3B: Otherwise:
475f7df2e56Smrg       P(W) changes from a descendant to W itself. The subwindow
476f7df2e56Smrg       field should be set to the child containing the old P(W) <<< WRONG */
4774642e01fSmrg
4784202a189Smrg    if (!HasPointer(dev, B))
4794642e01fSmrg        CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B, None);
4804642e01fSmrg
4814642e01fSmrg}
4824642e01fSmrg
4834642e01fSmrg/**
4844202a189Smrg * Pointer dev moves from A to B and B is a descendant of A.
4854642e01fSmrg */
4864642e01fSmrgstatic void
487f7df2e56SmrgCoreEnterLeaveToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
4884642e01fSmrg{
4894642e01fSmrg    /* Case 6:
490f7df2e56Smrg       A is W, B is a descendant of W
4914642e01fSmrg
492f7df2e56Smrg       Classically: A LeaveNotify is generated on W with a detail of
4934642e01fSmrg       NotifyInferior
4944642e01fSmrg
495f7df2e56Smrg       MPX:
496f7df2e56Smrg       Case 3A: There is at least one other pointer on W itself
497f7df2e56Smrg       P(W) doesn't change, the event should be suppressed
498f7df2e56Smrg       Case 3B: Otherwise:
499f7df2e56Smrg       P(W) changes from W to a descendant of W. The subwindow field
500f7df2e56Smrg       is set to the child containing the new P(W) <<< THIS IS WRONG */
5014642e01fSmrg
5024202a189Smrg    if (!HasPointer(dev, A))
5034642e01fSmrg        CoreEnterLeaveEvent(dev, LeaveNotify, mode, NotifyInferior, A, None);
5044642e01fSmrg
5054642e01fSmrg    CoreEnterNotifies(dev, A, B, mode, NotifyVirtual);
5064642e01fSmrg
5074642e01fSmrg    /* Case 2:
508f7df2e56Smrg       A is above W, B=W
509f7df2e56Smrg
510f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
511f7df2e56Smrg       Ancestor or Nonlinear
512f7df2e56Smrg
513f7df2e56Smrg       MPX:
514f7df2e56Smrg       Case 2A: There is at least one other pointer on W itself
515f7df2e56Smrg       P(W) doesn't change, so the event should be suppressed
516f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other pointer in a
517f7df2e56Smrg       descendant
518f7df2e56Smrg       P(W) moves from a descendant to W. detail is changed to Inferior,
519f7df2e56Smrg       subwindow is set to the child containing the previous P(W)
520f7df2e56Smrg       Case 2C: Otherwise:
521f7df2e56Smrg       P(W) changes from a window above W to W itself.
522f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
523f7df2e56Smrg       or vice-versa depending on the previous P(W). */
524f7df2e56Smrg
525f7df2e56Smrg    if (!HasPointer(dev, B)) {
526f7df2e56Smrg        WindowPtr child = FirstPointerChild(B);
527f7df2e56Smrg
528f7df2e56Smrg        if (child)
529f7df2e56Smrg            CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyInferior, B,
530f7df2e56Smrg                                None);
531f7df2e56Smrg        else
532f7df2e56Smrg            CoreEnterLeaveEvent(dev, EnterNotify, mode, NotifyAncestor, B,
533f7df2e56Smrg                                None);
534f7df2e56Smrg    }
5354642e01fSmrg}
5364642e01fSmrg
5374642e01fSmrgstatic void
538f7df2e56SmrgCoreEnterLeaveEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
5394642e01fSmrg{
5404202a189Smrg    if (!IsMaster(dev))
5414642e01fSmrg        return;
5424642e01fSmrg
5434202a189Smrg    LeaveWindow(dev);
5444642e01fSmrg
5454642e01fSmrg    if (IsParent(from, to))
5464642e01fSmrg        CoreEnterLeaveToDescendant(dev, from, to, mode);
5474642e01fSmrg    else if (IsParent(to, from))
5484642e01fSmrg        CoreEnterLeaveToAncestor(dev, from, to, mode);
5494642e01fSmrg    else
5504642e01fSmrg        CoreEnterLeaveNonLinear(dev, from, to, mode);
5514642e01fSmrg
5524642e01fSmrg    EnterWindow(dev, to, mode);
5534642e01fSmrg}
5544642e01fSmrg
5554642e01fSmrgstatic void
5564642e01fSmrgDeviceEnterLeaveEvents(DeviceIntPtr dev,
557f7df2e56Smrg                       int sourceid, WindowPtr from, WindowPtr to, int mode)
5584642e01fSmrg{
559f7df2e56Smrg    if (IsParent(from, to)) {
560f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyInferior,
561f7df2e56Smrg                              from, None);
5624202a189Smrg        DeviceEnterNotifies(dev, sourceid, from, to, mode, NotifyVirtual);
563f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyAncestor, to,
564f7df2e56Smrg                              None);
5654642e01fSmrg    }
566f7df2e56Smrg    else if (IsParent(to, from)) {
567f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyAncestor,
568f7df2e56Smrg                              from, None);
569f7df2e56Smrg        DeviceLeaveNotifies(dev, sourceid, from, to, mode, NotifyVirtual);
570f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyInferior, to,
571f7df2e56Smrg                              None);
5724642e01fSmrg    }
573f7df2e56Smrg    else {                      /* neither from nor to is descendent of the other */
574f7df2e56Smrg        WindowPtr common = CommonAncestor(to, from);
575f7df2e56Smrg
576f7df2e56Smrg        /* common == NullWindow ==> different screens */
577f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Leave, mode, NotifyNonlinear,
578f7df2e56Smrg                              from, None);
579f7df2e56Smrg        DeviceLeaveNotifies(dev, sourceid, from, common, mode,
580f7df2e56Smrg                            NotifyNonlinearVirtual);
581f7df2e56Smrg        DeviceEnterNotifies(dev, sourceid, common, to, mode,
582f7df2e56Smrg                            NotifyNonlinearVirtual);
583f7df2e56Smrg        DeviceEnterLeaveEvent(dev, sourceid, XI_Enter, mode, NotifyNonlinear,
584f7df2e56Smrg                              to, None);
5854642e01fSmrg    }
5864642e01fSmrg}
5874642e01fSmrg
5884642e01fSmrg/**
5894642e01fSmrg * Figure out if enter/leave events are necessary and send them to the
5904642e01fSmrg * appropriate windows.
5914642e01fSmrg *
5924642e01fSmrg * @param fromWin Window the sprite moved out of.
5934642e01fSmrg * @param toWin Window the sprite moved into.
5944642e01fSmrg */
5954642e01fSmrgvoid
5964642e01fSmrgDoEnterLeaveEvents(DeviceIntPtr pDev,
597f7df2e56Smrg                   int sourceid, WindowPtr fromWin, WindowPtr toWin, int mode)
5984642e01fSmrg{
5994642e01fSmrg    if (!IsPointerDevice(pDev))
6004642e01fSmrg        return;
6014642e01fSmrg
6024642e01fSmrg    if (fromWin == toWin)
603f7df2e56Smrg        return;
6044642e01fSmrg
6054202a189Smrg    if (mode != XINotifyPassiveGrab && mode != XINotifyPassiveUngrab)
6064202a189Smrg        CoreEnterLeaveEvents(pDev, fromWin, toWin, mode);
6074202a189Smrg    DeviceEnterLeaveEvents(pDev, sourceid, fromWin, toWin, mode);
6084642e01fSmrg}
6094642e01fSmrg
610f7df2e56Smrgstatic void
611f7df2e56SmrgFixDeviceValuator(DeviceIntPtr dev, deviceValuator * ev, ValuatorClassPtr v,
612f7df2e56Smrg                  int first)
613f7df2e56Smrg{
614f7df2e56Smrg    int nval = v->numAxes - first;
615f7df2e56Smrg
616f7df2e56Smrg    ev->type = DeviceValuator;
617f7df2e56Smrg    ev->deviceid = dev->id;
618875c6e4fSmrg    ev->num_valuators = nval < 6 ? nval : 6;
619f7df2e56Smrg    ev->first_valuator = first;
620f7df2e56Smrg    switch (ev->num_valuators) {
621875c6e4fSmrg    case 6:
622875c6e4fSmrg        ev->valuator2 = v->axisVal[first + 5];
623875c6e4fSmrg    case 5:
624875c6e4fSmrg        ev->valuator2 = v->axisVal[first + 4];
625875c6e4fSmrg    case 4:
626875c6e4fSmrg        ev->valuator2 = v->axisVal[first + 3];
627f7df2e56Smrg    case 3:
628f7df2e56Smrg        ev->valuator2 = v->axisVal[first + 2];
629f7df2e56Smrg    case 2:
630f7df2e56Smrg        ev->valuator1 = v->axisVal[first + 1];
631f7df2e56Smrg    case 1:
632f7df2e56Smrg        ev->valuator0 = v->axisVal[first];
633f7df2e56Smrg        break;
634f7df2e56Smrg    }
635f7df2e56Smrg}
636f7df2e56Smrg
637f7df2e56Smrgstatic void
638f7df2e56SmrgFixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
639f7df2e56Smrg                     ButtonClassPtr b, ValuatorClassPtr v, int first)
640f7df2e56Smrg{
641f7df2e56Smrg    ev->type = DeviceStateNotify;
642f7df2e56Smrg    ev->deviceid = dev->id;
643f7df2e56Smrg    ev->time = currentTime.milliseconds;
644f7df2e56Smrg    ev->classes_reported = 0;
645f7df2e56Smrg    ev->num_keys = 0;
646f7df2e56Smrg    ev->num_buttons = 0;
647f7df2e56Smrg    ev->num_valuators = 0;
648f7df2e56Smrg
649f7df2e56Smrg    if (b) {
650f7df2e56Smrg        ev->classes_reported |= (1 << ButtonClass);
651f7df2e56Smrg        ev->num_buttons = b->numButtons;
652f7df2e56Smrg        memcpy((char *) ev->buttons, (char *) b->down, 4);
653f7df2e56Smrg    }
654875c6e4fSmrg    if (k) {
655f7df2e56Smrg        ev->classes_reported |= (1 << KeyClass);
656f7df2e56Smrg        ev->num_keys = k->xkbInfo->desc->max_key_code -
657f7df2e56Smrg            k->xkbInfo->desc->min_key_code;
658f7df2e56Smrg        memmove((char *) &ev->keys[0], (char *) k->down, 4);
659f7df2e56Smrg    }
660f7df2e56Smrg    if (v) {
661f7df2e56Smrg        int nval = v->numAxes - first;
662f7df2e56Smrg
663f7df2e56Smrg        ev->classes_reported |= (1 << ValuatorClass);
664f7df2e56Smrg        ev->classes_reported |= valuator_get_mode(dev, 0) << ModeBitsShift;
665f7df2e56Smrg        ev->num_valuators = nval < 3 ? nval : 3;
666f7df2e56Smrg        switch (ev->num_valuators) {
667f7df2e56Smrg        case 3:
668f7df2e56Smrg            ev->valuator2 = v->axisVal[first + 2];
669f7df2e56Smrg        case 2:
670f7df2e56Smrg            ev->valuator1 = v->axisVal[first + 1];
671f7df2e56Smrg        case 1:
672f7df2e56Smrg            ev->valuator0 = v->axisVal[first];
673f7df2e56Smrg            break;
674f7df2e56Smrg        }
675f7df2e56Smrg    }
676f7df2e56Smrg}
677f7df2e56Smrg
678875c6e4fSmrg/**
679875c6e4fSmrg * The device state notify event is split across multiple 32-byte events.
680875c6e4fSmrg * The first one contains the first 32 button state bits, the first 32
681875c6e4fSmrg * key state bits, and the first 3 valuator values.
682875c6e4fSmrg *
683875c6e4fSmrg * If a device has more than that, the server sends out:
684875c6e4fSmrg * - one deviceButtonStateNotify for buttons 32 and above
685875c6e4fSmrg * - one deviceKeyStateNotify for keys 32 and above
686875c6e4fSmrg * - one deviceValuator event per 6 valuators above valuator 4
687875c6e4fSmrg *
688875c6e4fSmrg * All events but the last one have the deviceid binary ORed with MORE_EVENTS,
689875c6e4fSmrg */
690f7df2e56Smrgstatic void
691f7df2e56SmrgDeliverStateNotifyEvent(DeviceIntPtr dev, WindowPtr win)
692f7df2e56Smrg{
693875c6e4fSmrg    /* deviceStateNotify, deviceKeyStateNotify, deviceButtonStateNotify
694875c6e4fSmrg     * and one deviceValuator for each 6 valuators */
695875c6e4fSmrg    deviceStateNotify sev[3 + (MAX_VALUATORS + 6)/6];
696f7df2e56Smrg    int evcount = 1;
697875c6e4fSmrg    deviceStateNotify *ev = sev;
698f7df2e56Smrg
699f7df2e56Smrg    KeyClassPtr k;
700f7df2e56Smrg    ButtonClassPtr b;
701f7df2e56Smrg    ValuatorClassPtr v;
702f7df2e56Smrg    int nval = 0, nkeys = 0, nbuttons = 0, first = 0;
703f7df2e56Smrg
704f7df2e56Smrg    if (!(wOtherInputMasks(win)) ||
705f7df2e56Smrg        !(wOtherInputMasks(win)->inputEvents[dev->id] & DeviceStateNotifyMask))
706f7df2e56Smrg        return;
707f7df2e56Smrg
708f7df2e56Smrg    if ((b = dev->button) != NULL) {
709f7df2e56Smrg        nbuttons = b->numButtons;
710875c6e4fSmrg        if (nbuttons > 32) /* first 32 are encoded in deviceStateNotify */
711f7df2e56Smrg            evcount++;
712f7df2e56Smrg    }
713f7df2e56Smrg    if ((k = dev->key) != NULL) {
714f7df2e56Smrg        nkeys = k->xkbInfo->desc->max_key_code - k->xkbInfo->desc->min_key_code;
715875c6e4fSmrg        if (nkeys > 32) /* first 32 are encoded in deviceStateNotify */
716f7df2e56Smrg            evcount++;
717f7df2e56Smrg    }
718f7df2e56Smrg    if ((v = dev->valuator) != NULL) {
719f7df2e56Smrg        nval = v->numAxes;
720875c6e4fSmrg        /* first three are encoded in deviceStateNotify, then
721875c6e4fSmrg         * it's 6 per deviceValuator event */
722875c6e4fSmrg        evcount += ((nval - 3) + 6)/6;
723f7df2e56Smrg    }
724f7df2e56Smrg
725875c6e4fSmrg    BUG_RETURN(evcount <= ARRAY_SIZE(sev));
726875c6e4fSmrg
727875c6e4fSmrg    FixDeviceStateNotify(dev, ev, k, b, v, first);
728875c6e4fSmrg
729875c6e4fSmrg    if (b != NULL && nbuttons > 32) {
730875c6e4fSmrg        deviceButtonStateNotify *bev = (deviceButtonStateNotify *) ++ev;
731875c6e4fSmrg        (ev - 1)->deviceid |= MORE_EVENTS;
732875c6e4fSmrg        bev->type = DeviceButtonStateNotify;
733875c6e4fSmrg        bev->deviceid = dev->id;
734875c6e4fSmrg        memcpy((char *) &bev->buttons[4], (char *) &b->down[4],
735875c6e4fSmrg               DOWN_LENGTH - 4);
736f7df2e56Smrg    }
737f7df2e56Smrg
738875c6e4fSmrg    if (k != NULL && nkeys > 32) {
739875c6e4fSmrg        deviceKeyStateNotify *kev = (deviceKeyStateNotify *) ++ev;
740875c6e4fSmrg        (ev - 1)->deviceid |= MORE_EVENTS;
741875c6e4fSmrg        kev->type = DeviceKeyStateNotify;
742875c6e4fSmrg        kev->deviceid = dev->id;
743875c6e4fSmrg        memmove((char *) &kev->keys[0], (char *) &k->down[4], 28);
744f7df2e56Smrg    }
745f7df2e56Smrg
746875c6e4fSmrg    first = 3;
747875c6e4fSmrg    nval -= 3;
748f7df2e56Smrg    while (nval > 0) {
749875c6e4fSmrg        ev->deviceid |= MORE_EVENTS;
750875c6e4fSmrg        FixDeviceValuator(dev, (deviceValuator *) ++ev, v, first);
751875c6e4fSmrg        first += 6;
752875c6e4fSmrg        nval -= 6;
753f7df2e56Smrg    }
754f7df2e56Smrg
755f7df2e56Smrg    DeliverEventsToWindow(dev, win, (xEvent *) sev, evcount,
756f7df2e56Smrg                          DeviceStateNotifyMask, NullGrab);
757f7df2e56Smrg}
758f7df2e56Smrg
759f7df2e56Smrgvoid
760f7df2e56SmrgDeviceFocusEvent(DeviceIntPtr dev, int type, int mode, int detail,
761f7df2e56Smrg                 WindowPtr pWin)
762f7df2e56Smrg{
763f7df2e56Smrg    deviceFocus event;
764f7df2e56Smrg    xXIFocusInEvent *xi2event;
765f7df2e56Smrg    DeviceIntPtr mouse;
766f7df2e56Smrg    int btlen, len, i;
767f7df2e56Smrg
768f7df2e56Smrg    mouse = IsFloating(dev) ? dev : GetMaster(dev, MASTER_POINTER);
769f7df2e56Smrg
770875c6e4fSmrg    /* XI 2 event contains the logical button map - maps are CARD8
771875c6e4fSmrg     * so we need 256 bits for the possibly maximum mapping */
772875c6e4fSmrg    btlen = (mouse->button) ? bits_to_bytes(256) : 0;
773f7df2e56Smrg    btlen = bytes_to_int32(btlen);
774f7df2e56Smrg    len = sizeof(xXIFocusInEvent) + btlen * 4;
775f7df2e56Smrg
776f7df2e56Smrg    xi2event = calloc(1, len);
777f7df2e56Smrg    xi2event->type = GenericEvent;
778f7df2e56Smrg    xi2event->extension = IReqCode;
779f7df2e56Smrg    xi2event->evtype = type;
780f7df2e56Smrg    xi2event->length = bytes_to_int32(len - sizeof(xEvent));
781f7df2e56Smrg    xi2event->buttons_len = btlen;
782f7df2e56Smrg    xi2event->detail = detail;
783f7df2e56Smrg    xi2event->time = currentTime.milliseconds;
784f7df2e56Smrg    xi2event->deviceid = dev->id;
785f7df2e56Smrg    xi2event->sourceid = dev->id;       /* a device doesn't change focus by itself */
786f7df2e56Smrg    xi2event->mode = mode;
787f7df2e56Smrg    xi2event->root_x = double_to_fp1616(mouse->spriteInfo->sprite->hot.x);
788f7df2e56Smrg    xi2event->root_y = double_to_fp1616(mouse->spriteInfo->sprite->hot.y);
789f7df2e56Smrg
790f7df2e56Smrg    for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++)
791f7df2e56Smrg        if (BitIsOn(mouse->button->down, i))
792f7df2e56Smrg            SetBit(&xi2event[1], mouse->button->map[i]);
793f7df2e56Smrg
794f7df2e56Smrg    if (dev->key) {
795f7df2e56Smrg        xi2event->mods.base_mods = dev->key->xkbInfo->state.base_mods;
796f7df2e56Smrg        xi2event->mods.latched_mods = dev->key->xkbInfo->state.latched_mods;
797f7df2e56Smrg        xi2event->mods.locked_mods = dev->key->xkbInfo->state.locked_mods;
798f7df2e56Smrg        xi2event->mods.effective_mods = dev->key->xkbInfo->state.mods;
799f7df2e56Smrg
800f7df2e56Smrg        xi2event->group.base_group = dev->key->xkbInfo->state.base_group;
801f7df2e56Smrg        xi2event->group.latched_group = dev->key->xkbInfo->state.latched_group;
802f7df2e56Smrg        xi2event->group.locked_group = dev->key->xkbInfo->state.locked_group;
803f7df2e56Smrg        xi2event->group.effective_group = dev->key->xkbInfo->state.group;
804f7df2e56Smrg    }
805f7df2e56Smrg
806f7df2e56Smrg    FixUpEventFromWindow(dev->spriteInfo->sprite, (xEvent *) xi2event, pWin,
807f7df2e56Smrg                         None, FALSE);
808f7df2e56Smrg
809f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) xi2event, 1,
810f7df2e56Smrg                          GetEventFilter(dev, (xEvent *) xi2event), NullGrab);
811f7df2e56Smrg
812f7df2e56Smrg    free(xi2event);
813f7df2e56Smrg
814f7df2e56Smrg    /* XI 1.x event */
815f7df2e56Smrg    event = (deviceFocus) {
816f7df2e56Smrg        .deviceid = dev->id,
817f7df2e56Smrg        .mode = mode,
818f7df2e56Smrg        .type = (type == XI_FocusIn) ? DeviceFocusIn : DeviceFocusOut,
819f7df2e56Smrg        .detail = detail,
820f7df2e56Smrg        .window = pWin->drawable.id,
821f7df2e56Smrg        .time = currentTime.milliseconds
822f7df2e56Smrg    };
823f7df2e56Smrg
824f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) &event, 1,
825f7df2e56Smrg                          DeviceFocusChangeMask, NullGrab);
826f7df2e56Smrg
827f7df2e56Smrg    if (event.type == DeviceFocusIn)
828f7df2e56Smrg        DeliverStateNotifyEvent(dev, pWin);
829f7df2e56Smrg}
830f7df2e56Smrg
8314642e01fSmrg/**
8324202a189Smrg * Send focus out events to all windows between 'child' and 'ancestor'.
8334642e01fSmrg * Events are sent running up the hierarchy.
8344642e01fSmrg */
8354642e01fSmrgstatic void
8364642e01fSmrgDeviceFocusOutEvents(DeviceIntPtr dev,
837f7df2e56Smrg                     WindowPtr child, WindowPtr ancestor, int mode, int detail)
8384642e01fSmrg{
839f7df2e56Smrg    WindowPtr win;
8404642e01fSmrg
8414642e01fSmrg    if (ancestor == child)
842f7df2e56Smrg        return;
8434642e01fSmrg    for (win = child->parent; win != ancestor; win = win->parent)
8444202a189Smrg        DeviceFocusEvent(dev, XI_FocusOut, mode, detail, win);
8454642e01fSmrg}
8464642e01fSmrg
8474642e01fSmrg/**
8484202a189Smrg * Send enter notifies to all windows between 'ancestor' and 'child' (excluding
8494642e01fSmrg * both). Events are sent running up the window hierarchy. This function
8504642e01fSmrg * recurses.
8514642e01fSmrg */
8524642e01fSmrgstatic void
8534642e01fSmrgDeviceFocusInEvents(DeviceIntPtr dev,
854f7df2e56Smrg                    WindowPtr ancestor, WindowPtr child, int mode, int detail)
8554642e01fSmrg{
856f7df2e56Smrg    WindowPtr parent = child->parent;
8574642e01fSmrg
8584642e01fSmrg    if (ancestor == parent || !parent)
859f7df2e56Smrg        return;
8604642e01fSmrg    DeviceFocusInEvents(dev, ancestor, parent, mode, detail);
8614202a189Smrg    DeviceFocusEvent(dev, XI_FocusIn, mode, detail, parent);
8624642e01fSmrg}
8634642e01fSmrg
8644642e01fSmrg/**
8654202a189Smrg * Send FocusIn events to all windows between 'ancestor' and 'child' (excluding
8664642e01fSmrg * both). Events are sent running down the window hierarchy. This function
8674642e01fSmrg * recurses.
8684642e01fSmrg */
8694642e01fSmrgstatic void
8704642e01fSmrgCoreFocusInEvents(DeviceIntPtr dev,
871f7df2e56Smrg                  WindowPtr ancestor, WindowPtr child, int mode, int detail)
8724642e01fSmrg{
873f7df2e56Smrg    WindowPtr parent = child->parent;
874f7df2e56Smrg
8754642e01fSmrg    if (ancestor == parent)
876f7df2e56Smrg        return;
8774642e01fSmrg    CoreFocusInEvents(dev, ancestor, parent, mode, detail);
8784642e01fSmrg
8794642e01fSmrg    /* Case 3:
880f7df2e56Smrg       A is above W, B is a descendant
881f7df2e56Smrg
882f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
883f7df2e56Smrg       Virtual or NonlinearVirtual
884f7df2e56Smrg
885f7df2e56Smrg       MPX:
886f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
887f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
888f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
889f7df2e56Smrg       descendant
890f7df2e56Smrg       F(W) stays on the same descendant, or changes to a different
891f7df2e56Smrg       descendant. The event should be suppressed.
892f7df2e56Smrg       Case 3C: Otherwise:
893f7df2e56Smrg       F(W) moves from a window above W to a descendant. The detail may
894f7df2e56Smrg       need to be changed from Virtual to NonlinearVirtual depending
895f7df2e56Smrg       on the previous F(W). */
8964642e01fSmrg
8974642e01fSmrg    if (!HasFocus(parent) && !FirstFocusChild(parent))
898f7df2e56Smrg        CoreFocusEvent(dev, FocusIn, mode, detail, parent);
8994642e01fSmrg}
9004642e01fSmrg
9014642e01fSmrgstatic void
9024642e01fSmrgCoreFocusOutEvents(DeviceIntPtr dev,
903f7df2e56Smrg                   WindowPtr child, WindowPtr ancestor, int mode, int detail)
9044642e01fSmrg{
905f7df2e56Smrg    WindowPtr win;
9064642e01fSmrg
9074642e01fSmrg    if (ancestor == child)
9084642e01fSmrg        return;
9094642e01fSmrg
910f7df2e56Smrg    for (win = child->parent; win != ancestor; win = win->parent) {
9114642e01fSmrg        /*Case 7:
912f7df2e56Smrg           A is a descendant of W, B is above W
913f7df2e56Smrg
914f7df2e56Smrg           Classically: A FocusOut is generated on W with a detail of Virtual
915f7df2e56Smrg           or NonlinearVirtual.
916f7df2e56Smrg
917f7df2e56Smrg           MPX:
918f7df2e56Smrg           Case 3A: There is at least one other focus on W itself
919f7df2e56Smrg           F(W) doesn't change, the event should be suppressed.
920f7df2e56Smrg           Case 3B: Otherwise, if there is at least one other focus in a
921f7df2e56Smrg           descendant
922f7df2e56Smrg           F(W) stays on the same descendant, or changes to a different
923f7df2e56Smrg           descendant. The event should be suppressed.
924f7df2e56Smrg           Case 3C: Otherwise:
925f7df2e56Smrg           F(W) changes from the descendant of W to a window above W.
926f7df2e56Smrg           The detail may need to be changed from Virtual to NonlinearVirtual
927f7df2e56Smrg           or vice-versa depending on the new P(W). */
9284642e01fSmrg
9294642e01fSmrg        /* If one window has a focus or a child with a focuspointer, skip some
9304642e01fSmrg         * work and exit. */
9314642e01fSmrg        if (HasFocus(win) || FirstFocusChild(win))
9324642e01fSmrg            return;
9334642e01fSmrg
9344642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, detail, win);
9354642e01fSmrg    }
9364642e01fSmrg}
9374642e01fSmrg
9384642e01fSmrg/**
9394642e01fSmrg * Send FocusOut(NotifyPointer) events from the current pointer window (which
9404202a189Smrg * is a descendant of pwin_parent) up to (excluding) pwin_parent.
9414642e01fSmrg *
9424202a189Smrg * NotifyPointer events are only sent for the device paired with dev.
9434642e01fSmrg *
9444202a189Smrg * If the current pointer window is a descendant of 'exclude' or an ancestor of
9454202a189Smrg * 'exclude', no events are sent. If the current pointer IS 'exclude', events
9464202a189Smrg * are sent!
9474642e01fSmrg */
9484642e01fSmrgstatic void
9494642e01fSmrgCoreFocusOutNotifyPointerEvents(DeviceIntPtr dev,
9504642e01fSmrg                                WindowPtr pwin_parent,
951f7df2e56Smrg                                WindowPtr exclude, int mode, int inclusive)
9524642e01fSmrg{
9534642e01fSmrg    WindowPtr P, stopAt;
9544642e01fSmrg
955f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
9564642e01fSmrg
9574642e01fSmrg    if (!P)
9584642e01fSmrg        return;
9594642e01fSmrg    if (!IsParent(pwin_parent, P))
9604642e01fSmrg        if (!(pwin_parent == P && inclusive))
9614642e01fSmrg            return;
9624642e01fSmrg
9634642e01fSmrg    if (exclude != None && exclude != PointerRootWin &&
9644642e01fSmrg        (IsParent(exclude, P) || IsParent(P, exclude)))
9654642e01fSmrg        return;
9664642e01fSmrg
9674642e01fSmrg    stopAt = (inclusive) ? pwin_parent->parent : pwin_parent;
9684642e01fSmrg
9694642e01fSmrg    for (; P && P != stopAt; P = P->parent)
9704642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyPointer, P);
9714642e01fSmrg}
9724642e01fSmrg
9734642e01fSmrg/**
9744642e01fSmrg * DO NOT CALL DIRECTLY.
9754642e01fSmrg * Recursion helper for CoreFocusInNotifyPointerEvents.
9764642e01fSmrg */
9774642e01fSmrgstatic void
9784642e01fSmrgCoreFocusInRecurse(DeviceIntPtr dev,
979f7df2e56Smrg                   WindowPtr win, WindowPtr stopAt, int mode, int inclusive)
9804642e01fSmrg{
9814642e01fSmrg    if ((!inclusive && win == stopAt) || !win)
9824642e01fSmrg        return;
9834642e01fSmrg
9844642e01fSmrg    CoreFocusInRecurse(dev, win->parent, stopAt, mode, inclusive);
9854642e01fSmrg    CoreFocusEvent(dev, FocusIn, mode, NotifyPointer, win);
9864642e01fSmrg}
9874642e01fSmrg
9884642e01fSmrg/**
9894202a189Smrg * Send FocusIn(NotifyPointer) events from pwin_parent down to
9904202a189Smrg * including the current pointer window (which is a descendant of pwin_parent).
9914642e01fSmrg *
9924202a189Smrg * @param pwin The pointer window.
9934202a189Smrg * @param exclude If the pointer window is a child of 'exclude', no events are
9944202a189Smrg *                sent.
9954202a189Smrg * @param inclusive If TRUE, pwin_parent will receive the event too.
9964642e01fSmrg */
9974642e01fSmrgstatic void
9984642e01fSmrgCoreFocusInNotifyPointerEvents(DeviceIntPtr dev,
9994642e01fSmrg                               WindowPtr pwin_parent,
1000f7df2e56Smrg                               WindowPtr exclude, int mode, int inclusive)
10014642e01fSmrg{
10024642e01fSmrg    WindowPtr P;
10034642e01fSmrg
1004f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
10054642e01fSmrg
10064642e01fSmrg    if (!P || P == exclude || (pwin_parent != P && !IsParent(pwin_parent, P)))
10074642e01fSmrg        return;
10084642e01fSmrg
10094642e01fSmrg    if (exclude != None && (IsParent(exclude, P) || IsParent(P, exclude)))
10104642e01fSmrg        return;
10114642e01fSmrg
10124642e01fSmrg    CoreFocusInRecurse(dev, P, pwin_parent, mode, inclusive);
10134642e01fSmrg}
10144642e01fSmrg
10154642e01fSmrg/**
10164202a189Smrg * Focus of dev moves from A to B and A neither a descendant of B nor is
10174202a189Smrg * B a descendant of A.
10184642e01fSmrg */
10194642e01fSmrgstatic void
1020f7df2e56SmrgCoreFocusNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
10214642e01fSmrg{
10224642e01fSmrg    WindowPtr X = CommonAncestor(A, B);
10234642e01fSmrg
10244642e01fSmrg    /* Case 4:
1025f7df2e56Smrg       A is W, B is above W
10264642e01fSmrg
1027f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
10284642e01fSmrg       Ancestor or Nonlinear
10294642e01fSmrg
1030f7df2e56Smrg       MPX:
1031f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1032f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1033f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1034f7df2e56Smrg       descendant of W
1035f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1036f7df2e56Smrg       is set to Inferior
1037f7df2e56Smrg       Case 3C: Otherwise:
1038f7df2e56Smrg       The focus window moves from W to a window above W.
1039f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1040f7df2e56Smrg       vice versa depending on the the new F(W)
10414642e01fSmrg     */
10424642e01fSmrg
1043f7df2e56Smrg    if (!HasFocus(A)) {
10444642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1045f7df2e56Smrg
1046f7df2e56Smrg        if (child) {
1047f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
10484642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
10494642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1050f7df2e56Smrg        }
1051f7df2e56Smrg        else {
10524642e01fSmrg            /* NotifyPointer P-A */
10534642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
10544642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
10554642e01fSmrg        }
10564642e01fSmrg    }
10574642e01fSmrg
10584642e01fSmrg    CoreFocusOutEvents(dev, A, X, mode, NotifyNonlinearVirtual);
10594642e01fSmrg
10604642e01fSmrg    /*
1061f7df2e56Smrg       Case 9:
1062f7df2e56Smrg       A is a descendant of W, B is a descendant of W
10634642e01fSmrg
1064f7df2e56Smrg       Classically: No events are generated on W
1065f7df2e56Smrg       MPX: The focus window stays the same or moves to a different
1066f7df2e56Smrg       descendant of W. No events should be generated on W.
10674642e01fSmrg
10684642e01fSmrg       Therefore, no event to X.
1069f7df2e56Smrg     */
10704642e01fSmrg
10714642e01fSmrg    CoreFocusInEvents(dev, X, B, mode, NotifyNonlinearVirtual);
10724642e01fSmrg
10734642e01fSmrg    /* Case 2:
1074f7df2e56Smrg       A is above W, B=W
10754642e01fSmrg
1076f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
1077f7df2e56Smrg       Ancestor or Nonlinear
1078f7df2e56Smrg
1079f7df2e56Smrg       MPX:
1080f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1081f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1082f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1083f7df2e56Smrg       descendant
1084f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1085f7df2e56Smrg       Case 2C: Otherwise:
1086f7df2e56Smrg       F(W) changes from a window above W to W itself.
1087f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1088f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1089f7df2e56Smrg
1090f7df2e56Smrg    if (!HasFocus(B)) {
1091f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1092f7df2e56Smrg
1093f7df2e56Smrg        if (child) {
1094f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1095f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1096f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1097f7df2e56Smrg        }
1098f7df2e56Smrg        else {
1099f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
1100f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1101f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
1102f7df2e56Smrg        }
1103f7df2e56Smrg    }
1104f7df2e56Smrg}
11054642e01fSmrg
11064642e01fSmrg/**
11074202a189Smrg * Focus of dev moves from A to B and A is a descendant of B.
11084642e01fSmrg */
11094642e01fSmrgstatic void
1110f7df2e56SmrgCoreFocusToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11114642e01fSmrg{
11124642e01fSmrg    /* Case 4:
1113f7df2e56Smrg       A is W, B is above W
11144642e01fSmrg
1115f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
11164642e01fSmrg       Ancestor or Nonlinear
11174642e01fSmrg
1118f7df2e56Smrg       MPX:
1119f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1120f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1121f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1122f7df2e56Smrg       descendant of W
1123f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1124f7df2e56Smrg       is set to Inferior
1125f7df2e56Smrg       Case 3C: Otherwise:
1126f7df2e56Smrg       The focus window moves from W to a window above W.
1127f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1128f7df2e56Smrg       vice versa depending on the the new F(W)
11294642e01fSmrg     */
1130f7df2e56Smrg    if (!HasFocus(A)) {
11314642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1132f7df2e56Smrg
1133f7df2e56Smrg        if (child) {
1134f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
11354642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
11364642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1137f7df2e56Smrg        }
1138f7df2e56Smrg        else
11394642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyAncestor, A);
11404642e01fSmrg    }
11414642e01fSmrg
11424642e01fSmrg    CoreFocusOutEvents(dev, A, B, mode, NotifyVirtual);
11434642e01fSmrg
11444642e01fSmrg    /* Case 8:
1145f7df2e56Smrg       A is a descendant of W, B is W
11464642e01fSmrg
1147f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
1148f7df2e56Smrg       NotifyInferior
11494642e01fSmrg
1150f7df2e56Smrg       MPX:
1151f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1152f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1153f7df2e56Smrg       Case 3B: Otherwise:
1154f7df2e56Smrg       F(W) changes from a descendant to W itself. */
11554642e01fSmrg
1156f7df2e56Smrg    if (!HasFocus(B)) {
11574642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
11584642e01fSmrg        /* NotifyPointer B-P unless P is A or below. */
11594642e01fSmrg        CoreFocusInNotifyPointerEvents(dev, B, A, mode, FALSE);
11604642e01fSmrg    }
11614642e01fSmrg}
11624642e01fSmrg
11634642e01fSmrg/**
11644202a189Smrg * Focus of dev moves from A to B and B is a descendant of A.
11654642e01fSmrg */
11664642e01fSmrgstatic void
1167f7df2e56SmrgCoreFocusToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11684642e01fSmrg{
11694642e01fSmrg    /* Case 6:
1170f7df2e56Smrg       A is W, B is a descendant of W
11714642e01fSmrg
1172f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
11734642e01fSmrg       NotifyInferior
11744642e01fSmrg
1175f7df2e56Smrg       MPX:
1176f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1177f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1178f7df2e56Smrg       Case 3B: Otherwise:
1179f7df2e56Smrg       F(W) changes from W to a descendant of W. */
11804642e01fSmrg
1181f7df2e56Smrg    if (!HasFocus(A)) {
1182f7df2e56Smrg        /* NotifyPointer P-A unless P is B or below */
11834642e01fSmrg        CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
11844642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
11854642e01fSmrg    }
11864642e01fSmrg
11874642e01fSmrg    CoreFocusInEvents(dev, A, B, mode, NotifyVirtual);
11884642e01fSmrg
11894642e01fSmrg    /* Case 2:
1190f7df2e56Smrg       A is above W, B=W
1191f7df2e56Smrg
1192f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
1193f7df2e56Smrg       Ancestor or Nonlinear
1194f7df2e56Smrg
1195f7df2e56Smrg       MPX:
1196f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1197f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1198f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1199f7df2e56Smrg       descendant
1200f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1201f7df2e56Smrg       Case 2C: Otherwise:
1202f7df2e56Smrg       F(W) changes from a window above W to W itself.
1203f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1204f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1205f7df2e56Smrg
1206f7df2e56Smrg    if (!HasFocus(B)) {
1207f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1208f7df2e56Smrg
1209f7df2e56Smrg        if (child) {
1210f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1211f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1212f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1213f7df2e56Smrg        }
1214f7df2e56Smrg        else
1215f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyAncestor, B);
1216f7df2e56Smrg    }
12174642e01fSmrg}
12184642e01fSmrg
12194642e01fSmrgstatic BOOL
12204642e01fSmrgHasOtherPointer(WindowPtr win, DeviceIntPtr exclude)
12214642e01fSmrg{
12224642e01fSmrg    int i;
12234642e01fSmrg
12244642e01fSmrg    for (i = 0; i < MAXDEVICES; i++)
12254642e01fSmrg        if (i != exclude->id && PointerWindows[i] == win)
12264642e01fSmrg            return TRUE;
12274642e01fSmrg
12284642e01fSmrg    return FALSE;
12294642e01fSmrg}
12304642e01fSmrg
12314642e01fSmrg/**
12324642e01fSmrg * Focus moves from PointerRoot to None or from None to PointerRoot.
12334642e01fSmrg * Assumption: Neither A nor B are valid windows.
12344642e01fSmrg */
12354642e01fSmrgstatic void
12364642e01fSmrgCoreFocusPointerRootNoneSwitch(DeviceIntPtr dev,
1237f7df2e56Smrg                               WindowPtr A,     /* PointerRootWin or NoneWin */
1238f7df2e56Smrg                               WindowPtr B,     /* NoneWin or PointerRootWin */
12394642e01fSmrg                               int mode)
12404642e01fSmrg{
12414642e01fSmrg    WindowPtr root;
12424642e01fSmrg    int i;
12434642e01fSmrg    int nscreens = screenInfo.numScreens;
12444642e01fSmrg
12454642e01fSmrg#ifdef PANORAMIX
12464642e01fSmrg    if (!noPanoramiXExtension)
12474642e01fSmrg        nscreens = 1;
12484642e01fSmrg#endif
12494642e01fSmrg
1250f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
12514202a189Smrg        root = screenInfo.screens[i]->root;
1252f7df2e56Smrg        if (!HasOtherPointer(root, GetMaster(dev, POINTER_OR_FLOAT)) &&
1253f7df2e56Smrg            !FirstFocusChild(root)) {
12544642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
12554202a189Smrg             * the pointer paired with dev is below the current root window,
12564642e01fSmrg             * do a NotifyPointer run. */
12574642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1258f7df2e56Smrg                B != PointerRootWin) {
1259f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1260f7df2e56Smrg
12614642e01fSmrg                if (ptrwin && IsParent(root, ptrwin))
1262f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1263f7df2e56Smrg                                                    TRUE);
12644642e01fSmrg            }
1265f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1266f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
1267f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1268f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
12694642e01fSmrg            if (B == PointerRootWin)
12704642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
12714642e01fSmrg        }
12724642e01fSmrg
12734642e01fSmrg    }
12744642e01fSmrg}
12754642e01fSmrg
12764642e01fSmrg/**
12774202a189Smrg * Focus moves from window A to PointerRoot or to None.
12784202a189Smrg * Assumption: A is a valid window and not PointerRoot or None.
12794642e01fSmrg */
12804642e01fSmrgstatic void
1281f7df2e56SmrgCoreFocusToPointerRootOrNone(DeviceIntPtr dev, WindowPtr A,
1282f7df2e56Smrg                             WindowPtr B,        /* PointerRootWin or NoneWin */
1283f7df2e56Smrg                             int mode)
12844642e01fSmrg{
12854642e01fSmrg    WindowPtr root;
12864642e01fSmrg    int i;
12874642e01fSmrg    int nscreens = screenInfo.numScreens;
12884642e01fSmrg
12894642e01fSmrg#ifdef PANORAMIX
12904642e01fSmrg    if (!noPanoramiXExtension)
12914642e01fSmrg        nscreens = 1;
12924642e01fSmrg#endif
12934642e01fSmrg
1294f7df2e56Smrg    if (!HasFocus(A)) {
12954642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1296f7df2e56Smrg
1297f7df2e56Smrg        if (child) {
1298f7df2e56Smrg            /* NotifyPointer P-A unless P is B or below */
12994642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
13004642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1301f7df2e56Smrg        }
1302f7df2e56Smrg        else {
13034642e01fSmrg            /* NotifyPointer P-A */
13044642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
13054642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
13064642e01fSmrg        }
13074642e01fSmrg    }
13084642e01fSmrg
13094642e01fSmrg    /* NullWindow means we include the root window */
13104642e01fSmrg    CoreFocusOutEvents(dev, A, NullWindow, mode, NotifyNonlinearVirtual);
13114642e01fSmrg
1312f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13134202a189Smrg        root = screenInfo.screens[i]->root;
1314f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
1315f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1316f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
13174642e01fSmrg            if (B == PointerRootWin)
13184642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
13194642e01fSmrg        }
13204642e01fSmrg    }
13214642e01fSmrg}
13224642e01fSmrg
13234642e01fSmrg/**
13244202a189Smrg * Focus moves from PointerRoot or None to a window B.
13254202a189Smrg * Assumption: B is a valid window and not PointerRoot or None.
13264642e01fSmrg */
13274642e01fSmrgstatic void
13284642e01fSmrgCoreFocusFromPointerRootOrNone(DeviceIntPtr dev,
1329f7df2e56Smrg                               WindowPtr A,   /* PointerRootWin or NoneWin */
1330f7df2e56Smrg                               WindowPtr B, int mode)
13314642e01fSmrg{
13324642e01fSmrg    WindowPtr root;
13334642e01fSmrg    int i;
13344642e01fSmrg    int nscreens = screenInfo.numScreens;
13354642e01fSmrg
13364642e01fSmrg#ifdef PANORAMIX
13374642e01fSmrg    if (!noPanoramiXExtension)
13384642e01fSmrg        nscreens = 1;
13394642e01fSmrg#endif
13404642e01fSmrg
1341f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13424202a189Smrg        root = screenInfo.screens[i]->root;
1343f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
13444642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
13454202a189Smrg             * the pointer paired with dev is below the current root window,
13464642e01fSmrg             * do a NotifyPointer run. */
13474642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1348f7df2e56Smrg                B != PointerRootWin) {
1349f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1350f7df2e56Smrg
13514642e01fSmrg                if (ptrwin)
1352f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1353f7df2e56Smrg                                                    TRUE);
13544642e01fSmrg            }
1355f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1356f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
13574642e01fSmrg        }
13584642e01fSmrg    }
13594642e01fSmrg
1360f7df2e56Smrg    root = B;                   /* get B's root window */
1361f7df2e56Smrg    while (root->parent)
13624642e01fSmrg        root = root->parent;
13634642e01fSmrg
1364f7df2e56Smrg    if (B != root) {
13654642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinearVirtual, root);
13664642e01fSmrg        CoreFocusInEvents(dev, root, B, mode, NotifyNonlinearVirtual);
13674642e01fSmrg    }
13684642e01fSmrg
1369f7df2e56Smrg    if (!HasFocus(B)) {
13704642e01fSmrg        WindowPtr child = FirstFocusChild(B);
1371f7df2e56Smrg
1372f7df2e56Smrg        if (child) {
1373f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1374f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1375f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1376f7df2e56Smrg        }
1377f7df2e56Smrg        else {
13784642e01fSmrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
13794642e01fSmrg            /* NotifyPointer B-P unless P is child or below. */
13804642e01fSmrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
13814642e01fSmrg        }
13824642e01fSmrg    }
13834642e01fSmrg
13844642e01fSmrg}
13854642e01fSmrg
13864642e01fSmrgstatic void
1387f7df2e56SmrgCoreFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
13884642e01fSmrg{
13894202a189Smrg    if (!IsMaster(dev))
13904642e01fSmrg        return;
13914642e01fSmrg
13924202a189Smrg    SetFocusOut(dev);
13934642e01fSmrg
13944642e01fSmrg    if (((to == NullWindow) || (to == PointerRootWin)) &&
13954642e01fSmrg        ((from == NullWindow) || (from == PointerRootWin)))
13964642e01fSmrg        CoreFocusPointerRootNoneSwitch(dev, from, to, mode);
13974642e01fSmrg    else if ((to == NullWindow) || (to == PointerRootWin))
13984642e01fSmrg        CoreFocusToPointerRootOrNone(dev, from, to, mode);
13994642e01fSmrg    else if ((from == NullWindow) || (from == PointerRootWin))
14004642e01fSmrg        CoreFocusFromPointerRootOrNone(dev, from, to, mode);
14014642e01fSmrg    else if (IsParent(from, to))
14024642e01fSmrg        CoreFocusToDescendant(dev, from, to, mode);
14034642e01fSmrg    else if (IsParent(to, from))
14044642e01fSmrg        CoreFocusToAncestor(dev, from, to, mode);
14054642e01fSmrg    else
14064642e01fSmrg        CoreFocusNonLinear(dev, from, to, mode);
14074642e01fSmrg
14084642e01fSmrg    SetFocusIn(dev, to);
14094642e01fSmrg}
14104642e01fSmrg
14114642e01fSmrgstatic void
1412f7df2e56SmrgDeviceFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
14134642e01fSmrg{
1414f7df2e56Smrg    int out, in;                /* for holding details for to/from
1415f7df2e56Smrg                                   PointerRoot/None */
14164642e01fSmrg    int i;
14174642e01fSmrg    int nscreens = screenInfo.numScreens;
1418f7df2e56Smrg    SpritePtr sprite = dev->spriteInfo->sprite;
14194642e01fSmrg
14204642e01fSmrg    if (from == to)
14214642e01fSmrg        return;
14224642e01fSmrg    out = (from == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14234642e01fSmrg    in = (to == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14244642e01fSmrg    /* wrong values if neither, but then not referenced */
14254642e01fSmrg
14264642e01fSmrg#ifdef PANORAMIX
14274642e01fSmrg    if (!noPanoramiXExtension)
14284642e01fSmrg        nscreens = 1;
14294642e01fSmrg#endif
14304642e01fSmrg
1431f7df2e56Smrg    if ((to == NullWindow) || (to == PointerRootWin)) {
1432f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1433f7df2e56Smrg            if (from == PointerRootWin) {
1434f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1435f7df2e56Smrg                                 sprite->win);
1436f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1437f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1438f7df2e56Smrg                                     NotifyPointer);
1439f7df2e56Smrg            }
14404642e01fSmrg            /* Notify all the roots */
14414642e01fSmrg            for (i = 0; i < nscreens; i++)
1442f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1443f7df2e56Smrg                                 screenInfo.screens[i]->root);
14444642e01fSmrg        }
1445f7df2e56Smrg        else {
1446f7df2e56Smrg            if (IsParent(from, sprite->win)) {
1447f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1448f7df2e56Smrg                                 sprite->win);
14494642e01fSmrg                DeviceFocusOutEvents(dev, sprite->win, from, mode,
1450f7df2e56Smrg                                     NotifyPointer);
1451f7df2e56Smrg            }
14524202a189Smrg            DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
14534642e01fSmrg            /* next call catches the root too, if the screen changed */
1454f7df2e56Smrg            DeviceFocusOutEvents(dev, from, NullWindow, mode,
1455f7df2e56Smrg                                 NotifyNonlinearVirtual);
14564642e01fSmrg        }
14574642e01fSmrg        /* Notify all the roots */
14584642e01fSmrg        for (i = 0; i < nscreens; i++)
1459f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, in,
1460f7df2e56Smrg                             screenInfo.screens[i]->root);
1461f7df2e56Smrg        if (to == PointerRootWin) {
1462f7df2e56Smrg            DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), sprite->win,
1463f7df2e56Smrg                                mode, NotifyPointer);
1464f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyPointer, sprite->win);
1465f7df2e56Smrg        }
14664642e01fSmrg    }
1467f7df2e56Smrg    else {
1468f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1469f7df2e56Smrg            if (from == PointerRootWin) {
1470f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1471f7df2e56Smrg                                 sprite->win);
1472f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1473f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1474f7df2e56Smrg                                     NotifyPointer);
1475f7df2e56Smrg            }
14764642e01fSmrg            for (i = 0; i < nscreens; i++)
1477f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1478f7df2e56Smrg                                 screenInfo.screens[i]->root);
14794642e01fSmrg            if (to->parent != NullWindow)
1480f7df2e56Smrg                DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), to, mode,
1481f7df2e56Smrg                                    NotifyNonlinearVirtual);
14824202a189Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
14834642e01fSmrg            if (IsParent(to, sprite->win))
14844642e01fSmrg                DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer);
14854642e01fSmrg        }
1486f7df2e56Smrg        else {
1487f7df2e56Smrg            if (IsParent(to, from)) {
14884202a189Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyAncestor, from);
1489f7df2e56Smrg                DeviceFocusOutEvents(dev, from, to, mode, NotifyVirtual);
14904202a189Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyInferior, to);
14914642e01fSmrg                if ((IsParent(to, sprite->win)) &&
1492f7df2e56Smrg                    (sprite->win != from) &&
1493f7df2e56Smrg                    (!IsParent(from, sprite->win)) &&
1494f7df2e56Smrg                    (!IsParent(sprite->win, from)))
1495f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1496f7df2e56Smrg                                        NotifyPointer);
14974642e01fSmrg            }
1498f7df2e56Smrg            else if (IsParent(from, to)) {
1499f7df2e56Smrg                if ((IsParent(from, sprite->win)) &&
1500f7df2e56Smrg                    (sprite->win != from) &&
1501f7df2e56Smrg                    (!IsParent(to, sprite->win)) &&
1502f7df2e56Smrg                    (!IsParent(sprite->win, to))) {
1503f7df2e56Smrg                    DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1504f7df2e56Smrg                                     sprite->win);
1505f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1506f7df2e56Smrg                                         NotifyPointer);
15074642e01fSmrg                }
1508f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyInferior, from);
1509f7df2e56Smrg                DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual);
1510f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyAncestor, to);
1511f7df2e56Smrg            }
1512f7df2e56Smrg            else {
1513f7df2e56Smrg                /* neither from or to is child of other */
1514f7df2e56Smrg                WindowPtr common = CommonAncestor(to, from);
1515f7df2e56Smrg
1516f7df2e56Smrg                /* common == NullWindow ==> different screens */
1517f7df2e56Smrg                if (IsParent(from, sprite->win))
1518f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1519f7df2e56Smrg                                         NotifyPointer);
1520f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
1521f7df2e56Smrg                if (from->parent != NullWindow)
1522f7df2e56Smrg                    DeviceFocusOutEvents(dev, from, common, mode,
1523f7df2e56Smrg                                         NotifyNonlinearVirtual);
1524f7df2e56Smrg                if (to->parent != NullWindow)
1525f7df2e56Smrg                    DeviceFocusInEvents(dev, common, to, mode,
1526f7df2e56Smrg                                        NotifyNonlinearVirtual);
1527f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
1528f7df2e56Smrg                if (IsParent(to, sprite->win))
1529f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1530f7df2e56Smrg                                        NotifyPointer);
1531f7df2e56Smrg            }
15324642e01fSmrg        }
15334642e01fSmrg    }
15344642e01fSmrg}
15354642e01fSmrg
15364642e01fSmrg/**
15374642e01fSmrg * Figure out if focus events are necessary and send them to the
15384642e01fSmrg * appropriate windows.
15394642e01fSmrg *
15404642e01fSmrg * @param from Window the focus moved out of.
15414642e01fSmrg * @param to Window the focus moved into.
15424642e01fSmrg */
15434642e01fSmrgvoid
1544f7df2e56SmrgDoFocusEvents(DeviceIntPtr pDev, WindowPtr from, WindowPtr to, int mode)
15454642e01fSmrg{
15464642e01fSmrg    if (!IsKeyboardDevice(pDev))
15474642e01fSmrg        return;
15484642e01fSmrg
15497e31ba66Smrg    if (from == to && mode != NotifyGrab && mode != NotifyUngrab)
1550f7df2e56Smrg        return;
15514642e01fSmrg
15524642e01fSmrg    CoreFocusEvents(pDev, from, to, mode);
15534642e01fSmrg    DeviceFocusEvents(pDev, from, to, mode);
15544642e01fSmrg}
1555