enterleave.c revision 5a112b11
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;
618f7df2e56Smrg    ev->num_valuators = nval < 3 ? nval : 3;
619f7df2e56Smrg    ev->first_valuator = first;
620f7df2e56Smrg    switch (ev->num_valuators) {
621f7df2e56Smrg    case 3:
622f7df2e56Smrg        ev->valuator2 = v->axisVal[first + 2];
623f7df2e56Smrg    case 2:
624f7df2e56Smrg        ev->valuator1 = v->axisVal[first + 1];
625f7df2e56Smrg    case 1:
626f7df2e56Smrg        ev->valuator0 = v->axisVal[first];
627f7df2e56Smrg        break;
628f7df2e56Smrg    }
629f7df2e56Smrg    first += ev->num_valuators;
630f7df2e56Smrg}
631f7df2e56Smrg
632f7df2e56Smrgstatic void
633f7df2e56SmrgFixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
634f7df2e56Smrg                     ButtonClassPtr b, ValuatorClassPtr v, int first)
635f7df2e56Smrg{
636f7df2e56Smrg    ev->type = DeviceStateNotify;
637f7df2e56Smrg    ev->deviceid = dev->id;
638f7df2e56Smrg    ev->time = currentTime.milliseconds;
639f7df2e56Smrg    ev->classes_reported = 0;
640f7df2e56Smrg    ev->num_keys = 0;
641f7df2e56Smrg    ev->num_buttons = 0;
642f7df2e56Smrg    ev->num_valuators = 0;
643f7df2e56Smrg
644f7df2e56Smrg    if (b) {
645f7df2e56Smrg        ev->classes_reported |= (1 << ButtonClass);
646f7df2e56Smrg        ev->num_buttons = b->numButtons;
647f7df2e56Smrg        memcpy((char *) ev->buttons, (char *) b->down, 4);
648f7df2e56Smrg    }
649f7df2e56Smrg    else if (k) {
650f7df2e56Smrg        ev->classes_reported |= (1 << KeyClass);
651f7df2e56Smrg        ev->num_keys = k->xkbInfo->desc->max_key_code -
652f7df2e56Smrg            k->xkbInfo->desc->min_key_code;
653f7df2e56Smrg        memmove((char *) &ev->keys[0], (char *) k->down, 4);
654f7df2e56Smrg    }
655f7df2e56Smrg    if (v) {
656f7df2e56Smrg        int nval = v->numAxes - first;
657f7df2e56Smrg
658f7df2e56Smrg        ev->classes_reported |= (1 << ValuatorClass);
659f7df2e56Smrg        ev->classes_reported |= valuator_get_mode(dev, 0) << ModeBitsShift;
660f7df2e56Smrg        ev->num_valuators = nval < 3 ? nval : 3;
661f7df2e56Smrg        switch (ev->num_valuators) {
662f7df2e56Smrg        case 3:
663f7df2e56Smrg            ev->valuator2 = v->axisVal[first + 2];
664f7df2e56Smrg        case 2:
665f7df2e56Smrg            ev->valuator1 = v->axisVal[first + 1];
666f7df2e56Smrg        case 1:
667f7df2e56Smrg            ev->valuator0 = v->axisVal[first];
668f7df2e56Smrg            break;
669f7df2e56Smrg        }
670f7df2e56Smrg    }
671f7df2e56Smrg}
672f7df2e56Smrg
673f7df2e56Smrg
674f7df2e56Smrgstatic void
675f7df2e56SmrgDeliverStateNotifyEvent(DeviceIntPtr dev, WindowPtr win)
676f7df2e56Smrg{
677f7df2e56Smrg    int evcount = 1;
678f7df2e56Smrg    deviceStateNotify *ev, *sev;
679f7df2e56Smrg    deviceKeyStateNotify *kev;
680f7df2e56Smrg    deviceButtonStateNotify *bev;
681f7df2e56Smrg
682f7df2e56Smrg    KeyClassPtr k;
683f7df2e56Smrg    ButtonClassPtr b;
684f7df2e56Smrg    ValuatorClassPtr v;
685f7df2e56Smrg    int nval = 0, nkeys = 0, nbuttons = 0, first = 0;
686f7df2e56Smrg
687f7df2e56Smrg    if (!(wOtherInputMasks(win)) ||
688f7df2e56Smrg        !(wOtherInputMasks(win)->inputEvents[dev->id] & DeviceStateNotifyMask))
689f7df2e56Smrg        return;
690f7df2e56Smrg
691f7df2e56Smrg    if ((b = dev->button) != NULL) {
692f7df2e56Smrg        nbuttons = b->numButtons;
693f7df2e56Smrg        if (nbuttons > 32)
694f7df2e56Smrg            evcount++;
695f7df2e56Smrg    }
696f7df2e56Smrg    if ((k = dev->key) != NULL) {
697f7df2e56Smrg        nkeys = k->xkbInfo->desc->max_key_code - k->xkbInfo->desc->min_key_code;
698f7df2e56Smrg        if (nkeys > 32)
699f7df2e56Smrg            evcount++;
700f7df2e56Smrg        if (nbuttons > 0) {
701f7df2e56Smrg            evcount++;
702f7df2e56Smrg        }
703f7df2e56Smrg    }
704f7df2e56Smrg    if ((v = dev->valuator) != NULL) {
705f7df2e56Smrg        nval = v->numAxes;
706f7df2e56Smrg
707f7df2e56Smrg        if (nval > 3)
708f7df2e56Smrg            evcount++;
709f7df2e56Smrg        if (nval > 6) {
710f7df2e56Smrg            if (!(k && b))
711f7df2e56Smrg                evcount++;
712f7df2e56Smrg            if (nval > 9)
713f7df2e56Smrg                evcount += ((nval - 7) / 3);
714f7df2e56Smrg        }
715f7df2e56Smrg    }
716f7df2e56Smrg
717f7df2e56Smrg    sev = ev = xallocarray(evcount, sizeof(xEvent));
718f7df2e56Smrg    FixDeviceStateNotify(dev, ev, NULL, NULL, NULL, first);
719f7df2e56Smrg
720f7df2e56Smrg    if (b != NULL) {
721f7df2e56Smrg        FixDeviceStateNotify(dev, ev++, NULL, b, v, first);
722f7df2e56Smrg        first += 3;
723f7df2e56Smrg        nval -= 3;
724f7df2e56Smrg        if (nbuttons > 32) {
725f7df2e56Smrg            (ev - 1)->deviceid |= MORE_EVENTS;
726f7df2e56Smrg            bev = (deviceButtonStateNotify *) ev++;
727f7df2e56Smrg            bev->type = DeviceButtonStateNotify;
728f7df2e56Smrg            bev->deviceid = dev->id;
729f7df2e56Smrg            memcpy((char *) &bev->buttons[4], (char *) &b->down[4],
730f7df2e56Smrg                   DOWN_LENGTH - 4);
731f7df2e56Smrg        }
732f7df2e56Smrg        if (nval > 0) {
733f7df2e56Smrg            (ev - 1)->deviceid |= MORE_EVENTS;
734f7df2e56Smrg            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
735f7df2e56Smrg            first += 3;
736f7df2e56Smrg            nval -= 3;
737f7df2e56Smrg        }
738f7df2e56Smrg    }
739f7df2e56Smrg
740f7df2e56Smrg    if (k != NULL) {
741f7df2e56Smrg        FixDeviceStateNotify(dev, ev++, k, NULL, v, first);
742f7df2e56Smrg        first += 3;
743f7df2e56Smrg        nval -= 3;
744f7df2e56Smrg        if (nkeys > 32) {
745f7df2e56Smrg            (ev - 1)->deviceid |= MORE_EVENTS;
746f7df2e56Smrg            kev = (deviceKeyStateNotify *) ev++;
747f7df2e56Smrg            kev->type = DeviceKeyStateNotify;
748f7df2e56Smrg            kev->deviceid = dev->id;
749f7df2e56Smrg            memmove((char *) &kev->keys[0], (char *) &k->down[4], 28);
750f7df2e56Smrg        }
751f7df2e56Smrg        if (nval > 0) {
752f7df2e56Smrg            (ev - 1)->deviceid |= MORE_EVENTS;
753f7df2e56Smrg            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
754f7df2e56Smrg            first += 3;
755f7df2e56Smrg            nval -= 3;
756f7df2e56Smrg        }
757f7df2e56Smrg    }
758f7df2e56Smrg
759f7df2e56Smrg    while (nval > 0) {
760f7df2e56Smrg        FixDeviceStateNotify(dev, ev++, NULL, NULL, v, first);
761f7df2e56Smrg        first += 3;
762f7df2e56Smrg        nval -= 3;
763f7df2e56Smrg        if (nval > 0) {
764f7df2e56Smrg            (ev - 1)->deviceid |= MORE_EVENTS;
765f7df2e56Smrg            FixDeviceValuator(dev, (deviceValuator *) ev++, v, first);
766f7df2e56Smrg            first += 3;
767f7df2e56Smrg            nval -= 3;
768f7df2e56Smrg        }
769f7df2e56Smrg    }
770f7df2e56Smrg
771f7df2e56Smrg    DeliverEventsToWindow(dev, win, (xEvent *) sev, evcount,
772f7df2e56Smrg                          DeviceStateNotifyMask, NullGrab);
773f7df2e56Smrg    free(sev);
774f7df2e56Smrg}
775f7df2e56Smrg
776f7df2e56Smrgvoid
777f7df2e56SmrgDeviceFocusEvent(DeviceIntPtr dev, int type, int mode, int detail,
778f7df2e56Smrg                 WindowPtr pWin)
779f7df2e56Smrg{
780f7df2e56Smrg    deviceFocus event;
781f7df2e56Smrg    xXIFocusInEvent *xi2event;
782f7df2e56Smrg    DeviceIntPtr mouse;
783f7df2e56Smrg    int btlen, len, i;
784f7df2e56Smrg
785f7df2e56Smrg    mouse = IsFloating(dev) ? dev : GetMaster(dev, MASTER_POINTER);
786f7df2e56Smrg
787f7df2e56Smrg    /* XI 2 event */
788f7df2e56Smrg    btlen = (mouse->button) ? bits_to_bytes(mouse->button->numButtons) : 0;
789f7df2e56Smrg    btlen = bytes_to_int32(btlen);
790f7df2e56Smrg    len = sizeof(xXIFocusInEvent) + btlen * 4;
791f7df2e56Smrg
792f7df2e56Smrg    xi2event = calloc(1, len);
793f7df2e56Smrg    xi2event->type = GenericEvent;
794f7df2e56Smrg    xi2event->extension = IReqCode;
795f7df2e56Smrg    xi2event->evtype = type;
796f7df2e56Smrg    xi2event->length = bytes_to_int32(len - sizeof(xEvent));
797f7df2e56Smrg    xi2event->buttons_len = btlen;
798f7df2e56Smrg    xi2event->detail = detail;
799f7df2e56Smrg    xi2event->time = currentTime.milliseconds;
800f7df2e56Smrg    xi2event->deviceid = dev->id;
801f7df2e56Smrg    xi2event->sourceid = dev->id;       /* a device doesn't change focus by itself */
802f7df2e56Smrg    xi2event->mode = mode;
803f7df2e56Smrg    xi2event->root_x = double_to_fp1616(mouse->spriteInfo->sprite->hot.x);
804f7df2e56Smrg    xi2event->root_y = double_to_fp1616(mouse->spriteInfo->sprite->hot.y);
805f7df2e56Smrg
806f7df2e56Smrg    for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++)
807f7df2e56Smrg        if (BitIsOn(mouse->button->down, i))
808f7df2e56Smrg            SetBit(&xi2event[1], mouse->button->map[i]);
809f7df2e56Smrg
810f7df2e56Smrg    if (dev->key) {
811f7df2e56Smrg        xi2event->mods.base_mods = dev->key->xkbInfo->state.base_mods;
812f7df2e56Smrg        xi2event->mods.latched_mods = dev->key->xkbInfo->state.latched_mods;
813f7df2e56Smrg        xi2event->mods.locked_mods = dev->key->xkbInfo->state.locked_mods;
814f7df2e56Smrg        xi2event->mods.effective_mods = dev->key->xkbInfo->state.mods;
815f7df2e56Smrg
816f7df2e56Smrg        xi2event->group.base_group = dev->key->xkbInfo->state.base_group;
817f7df2e56Smrg        xi2event->group.latched_group = dev->key->xkbInfo->state.latched_group;
818f7df2e56Smrg        xi2event->group.locked_group = dev->key->xkbInfo->state.locked_group;
819f7df2e56Smrg        xi2event->group.effective_group = dev->key->xkbInfo->state.group;
820f7df2e56Smrg    }
821f7df2e56Smrg
822f7df2e56Smrg    FixUpEventFromWindow(dev->spriteInfo->sprite, (xEvent *) xi2event, pWin,
823f7df2e56Smrg                         None, FALSE);
824f7df2e56Smrg
825f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) xi2event, 1,
826f7df2e56Smrg                          GetEventFilter(dev, (xEvent *) xi2event), NullGrab);
827f7df2e56Smrg
828f7df2e56Smrg    free(xi2event);
829f7df2e56Smrg
830f7df2e56Smrg    /* XI 1.x event */
831f7df2e56Smrg    event = (deviceFocus) {
832f7df2e56Smrg        .deviceid = dev->id,
833f7df2e56Smrg        .mode = mode,
834f7df2e56Smrg        .type = (type == XI_FocusIn) ? DeviceFocusIn : DeviceFocusOut,
835f7df2e56Smrg        .detail = detail,
836f7df2e56Smrg        .window = pWin->drawable.id,
837f7df2e56Smrg        .time = currentTime.milliseconds
838f7df2e56Smrg    };
839f7df2e56Smrg
840f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) &event, 1,
841f7df2e56Smrg                          DeviceFocusChangeMask, NullGrab);
842f7df2e56Smrg
843f7df2e56Smrg    if (event.type == DeviceFocusIn)
844f7df2e56Smrg        DeliverStateNotifyEvent(dev, pWin);
845f7df2e56Smrg}
846f7df2e56Smrg
8474642e01fSmrg/**
8484202a189Smrg * Send focus out events to all windows between 'child' and 'ancestor'.
8494642e01fSmrg * Events are sent running up the hierarchy.
8504642e01fSmrg */
8514642e01fSmrgstatic void
8524642e01fSmrgDeviceFocusOutEvents(DeviceIntPtr dev,
853f7df2e56Smrg                     WindowPtr child, WindowPtr ancestor, int mode, int detail)
8544642e01fSmrg{
855f7df2e56Smrg    WindowPtr win;
8564642e01fSmrg
8574642e01fSmrg    if (ancestor == child)
858f7df2e56Smrg        return;
8594642e01fSmrg    for (win = child->parent; win != ancestor; win = win->parent)
8604202a189Smrg        DeviceFocusEvent(dev, XI_FocusOut, mode, detail, win);
8614642e01fSmrg}
8624642e01fSmrg
8634642e01fSmrg/**
8644202a189Smrg * Send enter notifies to all windows between 'ancestor' and 'child' (excluding
8654642e01fSmrg * both). Events are sent running up the window hierarchy. This function
8664642e01fSmrg * recurses.
8674642e01fSmrg */
8684642e01fSmrgstatic void
8694642e01fSmrgDeviceFocusInEvents(DeviceIntPtr dev,
870f7df2e56Smrg                    WindowPtr ancestor, WindowPtr child, int mode, int detail)
8714642e01fSmrg{
872f7df2e56Smrg    WindowPtr parent = child->parent;
8734642e01fSmrg
8744642e01fSmrg    if (ancestor == parent || !parent)
875f7df2e56Smrg        return;
8764642e01fSmrg    DeviceFocusInEvents(dev, ancestor, parent, mode, detail);
8774202a189Smrg    DeviceFocusEvent(dev, XI_FocusIn, mode, detail, parent);
8784642e01fSmrg}
8794642e01fSmrg
8804642e01fSmrg/**
8814202a189Smrg * Send FocusIn events to all windows between 'ancestor' and 'child' (excluding
8824642e01fSmrg * both). Events are sent running down the window hierarchy. This function
8834642e01fSmrg * recurses.
8844642e01fSmrg */
8854642e01fSmrgstatic void
8864642e01fSmrgCoreFocusInEvents(DeviceIntPtr dev,
887f7df2e56Smrg                  WindowPtr ancestor, WindowPtr child, int mode, int detail)
8884642e01fSmrg{
889f7df2e56Smrg    WindowPtr parent = child->parent;
890f7df2e56Smrg
8914642e01fSmrg    if (ancestor == parent)
892f7df2e56Smrg        return;
8934642e01fSmrg    CoreFocusInEvents(dev, ancestor, parent, mode, detail);
8944642e01fSmrg
8954642e01fSmrg    /* Case 3:
896f7df2e56Smrg       A is above W, B is a descendant
897f7df2e56Smrg
898f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
899f7df2e56Smrg       Virtual or NonlinearVirtual
900f7df2e56Smrg
901f7df2e56Smrg       MPX:
902f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
903f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
904f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
905f7df2e56Smrg       descendant
906f7df2e56Smrg       F(W) stays on the same descendant, or changes to a different
907f7df2e56Smrg       descendant. The event should be suppressed.
908f7df2e56Smrg       Case 3C: Otherwise:
909f7df2e56Smrg       F(W) moves from a window above W to a descendant. The detail may
910f7df2e56Smrg       need to be changed from Virtual to NonlinearVirtual depending
911f7df2e56Smrg       on the previous F(W). */
9124642e01fSmrg
9134642e01fSmrg    if (!HasFocus(parent) && !FirstFocusChild(parent))
914f7df2e56Smrg        CoreFocusEvent(dev, FocusIn, mode, detail, parent);
9154642e01fSmrg}
9164642e01fSmrg
9174642e01fSmrgstatic void
9184642e01fSmrgCoreFocusOutEvents(DeviceIntPtr dev,
919f7df2e56Smrg                   WindowPtr child, WindowPtr ancestor, int mode, int detail)
9204642e01fSmrg{
921f7df2e56Smrg    WindowPtr win;
9224642e01fSmrg
9234642e01fSmrg    if (ancestor == child)
9244642e01fSmrg        return;
9254642e01fSmrg
926f7df2e56Smrg    for (win = child->parent; win != ancestor; win = win->parent) {
9274642e01fSmrg        /*Case 7:
928f7df2e56Smrg           A is a descendant of W, B is above W
929f7df2e56Smrg
930f7df2e56Smrg           Classically: A FocusOut is generated on W with a detail of Virtual
931f7df2e56Smrg           or NonlinearVirtual.
932f7df2e56Smrg
933f7df2e56Smrg           MPX:
934f7df2e56Smrg           Case 3A: There is at least one other focus on W itself
935f7df2e56Smrg           F(W) doesn't change, the event should be suppressed.
936f7df2e56Smrg           Case 3B: Otherwise, if there is at least one other focus in a
937f7df2e56Smrg           descendant
938f7df2e56Smrg           F(W) stays on the same descendant, or changes to a different
939f7df2e56Smrg           descendant. The event should be suppressed.
940f7df2e56Smrg           Case 3C: Otherwise:
941f7df2e56Smrg           F(W) changes from the descendant of W to a window above W.
942f7df2e56Smrg           The detail may need to be changed from Virtual to NonlinearVirtual
943f7df2e56Smrg           or vice-versa depending on the new P(W). */
9444642e01fSmrg
9454642e01fSmrg        /* If one window has a focus or a child with a focuspointer, skip some
9464642e01fSmrg         * work and exit. */
9474642e01fSmrg        if (HasFocus(win) || FirstFocusChild(win))
9484642e01fSmrg            return;
9494642e01fSmrg
9504642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, detail, win);
9514642e01fSmrg    }
9524642e01fSmrg}
9534642e01fSmrg
9544642e01fSmrg/**
9554642e01fSmrg * Send FocusOut(NotifyPointer) events from the current pointer window (which
9564202a189Smrg * is a descendant of pwin_parent) up to (excluding) pwin_parent.
9574642e01fSmrg *
9584202a189Smrg * NotifyPointer events are only sent for the device paired with dev.
9594642e01fSmrg *
9604202a189Smrg * If the current pointer window is a descendant of 'exclude' or an ancestor of
9614202a189Smrg * 'exclude', no events are sent. If the current pointer IS 'exclude', events
9624202a189Smrg * are sent!
9634642e01fSmrg */
9644642e01fSmrgstatic void
9654642e01fSmrgCoreFocusOutNotifyPointerEvents(DeviceIntPtr dev,
9664642e01fSmrg                                WindowPtr pwin_parent,
967f7df2e56Smrg                                WindowPtr exclude, int mode, int inclusive)
9684642e01fSmrg{
9694642e01fSmrg    WindowPtr P, stopAt;
9704642e01fSmrg
971f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
9724642e01fSmrg
9734642e01fSmrg    if (!P)
9744642e01fSmrg        return;
9754642e01fSmrg    if (!IsParent(pwin_parent, P))
9764642e01fSmrg        if (!(pwin_parent == P && inclusive))
9774642e01fSmrg            return;
9784642e01fSmrg
9794642e01fSmrg    if (exclude != None && exclude != PointerRootWin &&
9804642e01fSmrg        (IsParent(exclude, P) || IsParent(P, exclude)))
9814642e01fSmrg        return;
9824642e01fSmrg
9834642e01fSmrg    stopAt = (inclusive) ? pwin_parent->parent : pwin_parent;
9844642e01fSmrg
9854642e01fSmrg    for (; P && P != stopAt; P = P->parent)
9864642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyPointer, P);
9874642e01fSmrg}
9884642e01fSmrg
9894642e01fSmrg/**
9904642e01fSmrg * DO NOT CALL DIRECTLY.
9914642e01fSmrg * Recursion helper for CoreFocusInNotifyPointerEvents.
9924642e01fSmrg */
9934642e01fSmrgstatic void
9944642e01fSmrgCoreFocusInRecurse(DeviceIntPtr dev,
995f7df2e56Smrg                   WindowPtr win, WindowPtr stopAt, int mode, int inclusive)
9964642e01fSmrg{
9974642e01fSmrg    if ((!inclusive && win == stopAt) || !win)
9984642e01fSmrg        return;
9994642e01fSmrg
10004642e01fSmrg    CoreFocusInRecurse(dev, win->parent, stopAt, mode, inclusive);
10014642e01fSmrg    CoreFocusEvent(dev, FocusIn, mode, NotifyPointer, win);
10024642e01fSmrg}
10034642e01fSmrg
10044642e01fSmrg/**
10054202a189Smrg * Send FocusIn(NotifyPointer) events from pwin_parent down to
10064202a189Smrg * including the current pointer window (which is a descendant of pwin_parent).
10074642e01fSmrg *
10084202a189Smrg * @param pwin The pointer window.
10094202a189Smrg * @param exclude If the pointer window is a child of 'exclude', no events are
10104202a189Smrg *                sent.
10114202a189Smrg * @param inclusive If TRUE, pwin_parent will receive the event too.
10124642e01fSmrg */
10134642e01fSmrgstatic void
10144642e01fSmrgCoreFocusInNotifyPointerEvents(DeviceIntPtr dev,
10154642e01fSmrg                               WindowPtr pwin_parent,
1016f7df2e56Smrg                               WindowPtr exclude, int mode, int inclusive)
10174642e01fSmrg{
10184642e01fSmrg    WindowPtr P;
10194642e01fSmrg
1020f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
10214642e01fSmrg
10224642e01fSmrg    if (!P || P == exclude || (pwin_parent != P && !IsParent(pwin_parent, P)))
10234642e01fSmrg        return;
10244642e01fSmrg
10254642e01fSmrg    if (exclude != None && (IsParent(exclude, P) || IsParent(P, exclude)))
10264642e01fSmrg        return;
10274642e01fSmrg
10284642e01fSmrg    CoreFocusInRecurse(dev, P, pwin_parent, mode, inclusive);
10294642e01fSmrg}
10304642e01fSmrg
10314642e01fSmrg/**
10324202a189Smrg * Focus of dev moves from A to B and A neither a descendant of B nor is
10334202a189Smrg * B a descendant of A.
10344642e01fSmrg */
10354642e01fSmrgstatic void
1036f7df2e56SmrgCoreFocusNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
10374642e01fSmrg{
10384642e01fSmrg    WindowPtr X = CommonAncestor(A, B);
10394642e01fSmrg
10404642e01fSmrg    /* Case 4:
1041f7df2e56Smrg       A is W, B is above W
10424642e01fSmrg
1043f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
10444642e01fSmrg       Ancestor or Nonlinear
10454642e01fSmrg
1046f7df2e56Smrg       MPX:
1047f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1048f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1049f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1050f7df2e56Smrg       descendant of W
1051f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1052f7df2e56Smrg       is set to Inferior
1053f7df2e56Smrg       Case 3C: Otherwise:
1054f7df2e56Smrg       The focus window moves from W to a window above W.
1055f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1056f7df2e56Smrg       vice versa depending on the the new F(W)
10574642e01fSmrg     */
10584642e01fSmrg
1059f7df2e56Smrg    if (!HasFocus(A)) {
10604642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1061f7df2e56Smrg
1062f7df2e56Smrg        if (child) {
1063f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
10644642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
10654642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1066f7df2e56Smrg        }
1067f7df2e56Smrg        else {
10684642e01fSmrg            /* NotifyPointer P-A */
10694642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
10704642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
10714642e01fSmrg        }
10724642e01fSmrg    }
10734642e01fSmrg
10744642e01fSmrg    CoreFocusOutEvents(dev, A, X, mode, NotifyNonlinearVirtual);
10754642e01fSmrg
10764642e01fSmrg    /*
1077f7df2e56Smrg       Case 9:
1078f7df2e56Smrg       A is a descendant of W, B is a descendant of W
10794642e01fSmrg
1080f7df2e56Smrg       Classically: No events are generated on W
1081f7df2e56Smrg       MPX: The focus window stays the same or moves to a different
1082f7df2e56Smrg       descendant of W. No events should be generated on W.
10834642e01fSmrg
10844642e01fSmrg       Therefore, no event to X.
1085f7df2e56Smrg     */
10864642e01fSmrg
10874642e01fSmrg    CoreFocusInEvents(dev, X, B, mode, NotifyNonlinearVirtual);
10884642e01fSmrg
10894642e01fSmrg    /* Case 2:
1090f7df2e56Smrg       A is above W, B=W
10914642e01fSmrg
1092f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
1093f7df2e56Smrg       Ancestor or Nonlinear
1094f7df2e56Smrg
1095f7df2e56Smrg       MPX:
1096f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1097f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1098f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1099f7df2e56Smrg       descendant
1100f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1101f7df2e56Smrg       Case 2C: Otherwise:
1102f7df2e56Smrg       F(W) changes from a window above W to W itself.
1103f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1104f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1105f7df2e56Smrg
1106f7df2e56Smrg    if (!HasFocus(B)) {
1107f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1108f7df2e56Smrg
1109f7df2e56Smrg        if (child) {
1110f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1111f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1112f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1113f7df2e56Smrg        }
1114f7df2e56Smrg        else {
1115f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
1116f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1117f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
1118f7df2e56Smrg        }
1119f7df2e56Smrg    }
1120f7df2e56Smrg}
11214642e01fSmrg
11224642e01fSmrg/**
11234202a189Smrg * Focus of dev moves from A to B and A is a descendant of B.
11244642e01fSmrg */
11254642e01fSmrgstatic void
1126f7df2e56SmrgCoreFocusToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11274642e01fSmrg{
11284642e01fSmrg    /* Case 4:
1129f7df2e56Smrg       A is W, B is above W
11304642e01fSmrg
1131f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
11324642e01fSmrg       Ancestor or Nonlinear
11334642e01fSmrg
1134f7df2e56Smrg       MPX:
1135f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1136f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1137f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1138f7df2e56Smrg       descendant of W
1139f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1140f7df2e56Smrg       is set to Inferior
1141f7df2e56Smrg       Case 3C: Otherwise:
1142f7df2e56Smrg       The focus window moves from W to a window above W.
1143f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1144f7df2e56Smrg       vice versa depending on the the new F(W)
11454642e01fSmrg     */
1146f7df2e56Smrg    if (!HasFocus(A)) {
11474642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1148f7df2e56Smrg
1149f7df2e56Smrg        if (child) {
1150f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
11514642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
11524642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1153f7df2e56Smrg        }
1154f7df2e56Smrg        else
11554642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyAncestor, A);
11564642e01fSmrg    }
11574642e01fSmrg
11584642e01fSmrg    CoreFocusOutEvents(dev, A, B, mode, NotifyVirtual);
11594642e01fSmrg
11604642e01fSmrg    /* Case 8:
1161f7df2e56Smrg       A is a descendant of W, B is W
11624642e01fSmrg
1163f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
1164f7df2e56Smrg       NotifyInferior
11654642e01fSmrg
1166f7df2e56Smrg       MPX:
1167f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1168f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1169f7df2e56Smrg       Case 3B: Otherwise:
1170f7df2e56Smrg       F(W) changes from a descendant to W itself. */
11714642e01fSmrg
1172f7df2e56Smrg    if (!HasFocus(B)) {
11734642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
11744642e01fSmrg        /* NotifyPointer B-P unless P is A or below. */
11754642e01fSmrg        CoreFocusInNotifyPointerEvents(dev, B, A, mode, FALSE);
11764642e01fSmrg    }
11774642e01fSmrg}
11784642e01fSmrg
11794642e01fSmrg/**
11804202a189Smrg * Focus of dev moves from A to B and B is a descendant of A.
11814642e01fSmrg */
11824642e01fSmrgstatic void
1183f7df2e56SmrgCoreFocusToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11844642e01fSmrg{
11854642e01fSmrg    /* Case 6:
1186f7df2e56Smrg       A is W, B is a descendant of W
11874642e01fSmrg
1188f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
11894642e01fSmrg       NotifyInferior
11904642e01fSmrg
1191f7df2e56Smrg       MPX:
1192f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1193f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1194f7df2e56Smrg       Case 3B: Otherwise:
1195f7df2e56Smrg       F(W) changes from W to a descendant of W. */
11964642e01fSmrg
1197f7df2e56Smrg    if (!HasFocus(A)) {
1198f7df2e56Smrg        /* NotifyPointer P-A unless P is B or below */
11994642e01fSmrg        CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
12004642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
12014642e01fSmrg    }
12024642e01fSmrg
12034642e01fSmrg    CoreFocusInEvents(dev, A, B, mode, NotifyVirtual);
12044642e01fSmrg
12054642e01fSmrg    /* Case 2:
1206f7df2e56Smrg       A is above W, B=W
1207f7df2e56Smrg
1208f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
1209f7df2e56Smrg       Ancestor or Nonlinear
1210f7df2e56Smrg
1211f7df2e56Smrg       MPX:
1212f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1213f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1214f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1215f7df2e56Smrg       descendant
1216f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1217f7df2e56Smrg       Case 2C: Otherwise:
1218f7df2e56Smrg       F(W) changes from a window above W to W itself.
1219f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1220f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1221f7df2e56Smrg
1222f7df2e56Smrg    if (!HasFocus(B)) {
1223f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1224f7df2e56Smrg
1225f7df2e56Smrg        if (child) {
1226f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1227f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1228f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1229f7df2e56Smrg        }
1230f7df2e56Smrg        else
1231f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyAncestor, B);
1232f7df2e56Smrg    }
12334642e01fSmrg}
12344642e01fSmrg
12354642e01fSmrgstatic BOOL
12364642e01fSmrgHasOtherPointer(WindowPtr win, DeviceIntPtr exclude)
12374642e01fSmrg{
12384642e01fSmrg    int i;
12394642e01fSmrg
12404642e01fSmrg    for (i = 0; i < MAXDEVICES; i++)
12414642e01fSmrg        if (i != exclude->id && PointerWindows[i] == win)
12424642e01fSmrg            return TRUE;
12434642e01fSmrg
12444642e01fSmrg    return FALSE;
12454642e01fSmrg}
12464642e01fSmrg
12474642e01fSmrg/**
12484642e01fSmrg * Focus moves from PointerRoot to None or from None to PointerRoot.
12494642e01fSmrg * Assumption: Neither A nor B are valid windows.
12504642e01fSmrg */
12514642e01fSmrgstatic void
12524642e01fSmrgCoreFocusPointerRootNoneSwitch(DeviceIntPtr dev,
1253f7df2e56Smrg                               WindowPtr A,     /* PointerRootWin or NoneWin */
1254f7df2e56Smrg                               WindowPtr B,     /* NoneWin or PointerRootWin */
12554642e01fSmrg                               int mode)
12564642e01fSmrg{
12574642e01fSmrg    WindowPtr root;
12584642e01fSmrg    int i;
12594642e01fSmrg    int nscreens = screenInfo.numScreens;
12604642e01fSmrg
12614642e01fSmrg#ifdef PANORAMIX
12624642e01fSmrg    if (!noPanoramiXExtension)
12634642e01fSmrg        nscreens = 1;
12644642e01fSmrg#endif
12654642e01fSmrg
1266f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
12674202a189Smrg        root = screenInfo.screens[i]->root;
1268f7df2e56Smrg        if (!HasOtherPointer(root, GetMaster(dev, POINTER_OR_FLOAT)) &&
1269f7df2e56Smrg            !FirstFocusChild(root)) {
12704642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
12714202a189Smrg             * the pointer paired with dev is below the current root window,
12724642e01fSmrg             * do a NotifyPointer run. */
12734642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1274f7df2e56Smrg                B != PointerRootWin) {
1275f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1276f7df2e56Smrg
12774642e01fSmrg                if (ptrwin && IsParent(root, ptrwin))
1278f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1279f7df2e56Smrg                                                    TRUE);
12804642e01fSmrg            }
1281f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1282f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
1283f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1284f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
12854642e01fSmrg            if (B == PointerRootWin)
12864642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
12874642e01fSmrg        }
12884642e01fSmrg
12894642e01fSmrg    }
12904642e01fSmrg}
12914642e01fSmrg
12924642e01fSmrg/**
12934202a189Smrg * Focus moves from window A to PointerRoot or to None.
12944202a189Smrg * Assumption: A is a valid window and not PointerRoot or None.
12954642e01fSmrg */
12964642e01fSmrgstatic void
1297f7df2e56SmrgCoreFocusToPointerRootOrNone(DeviceIntPtr dev, WindowPtr A,
1298f7df2e56Smrg                             WindowPtr B,        /* PointerRootWin or NoneWin */
1299f7df2e56Smrg                             int mode)
13004642e01fSmrg{
13014642e01fSmrg    WindowPtr root;
13024642e01fSmrg    int i;
13034642e01fSmrg    int nscreens = screenInfo.numScreens;
13044642e01fSmrg
13054642e01fSmrg#ifdef PANORAMIX
13064642e01fSmrg    if (!noPanoramiXExtension)
13074642e01fSmrg        nscreens = 1;
13084642e01fSmrg#endif
13094642e01fSmrg
1310f7df2e56Smrg    if (!HasFocus(A)) {
13114642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1312f7df2e56Smrg
1313f7df2e56Smrg        if (child) {
1314f7df2e56Smrg            /* NotifyPointer P-A unless P is B or below */
13154642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
13164642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1317f7df2e56Smrg        }
1318f7df2e56Smrg        else {
13194642e01fSmrg            /* NotifyPointer P-A */
13204642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
13214642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
13224642e01fSmrg        }
13234642e01fSmrg    }
13244642e01fSmrg
13254642e01fSmrg    /* NullWindow means we include the root window */
13264642e01fSmrg    CoreFocusOutEvents(dev, A, NullWindow, mode, NotifyNonlinearVirtual);
13274642e01fSmrg
1328f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13294202a189Smrg        root = screenInfo.screens[i]->root;
1330f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
1331f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1332f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
13334642e01fSmrg            if (B == PointerRootWin)
13344642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
13354642e01fSmrg        }
13364642e01fSmrg    }
13374642e01fSmrg}
13384642e01fSmrg
13394642e01fSmrg/**
13404202a189Smrg * Focus moves from PointerRoot or None to a window B.
13414202a189Smrg * Assumption: B is a valid window and not PointerRoot or None.
13424642e01fSmrg */
13434642e01fSmrgstatic void
13444642e01fSmrgCoreFocusFromPointerRootOrNone(DeviceIntPtr dev,
1345f7df2e56Smrg                               WindowPtr A,   /* PointerRootWin or NoneWin */
1346f7df2e56Smrg                               WindowPtr B, int mode)
13474642e01fSmrg{
13484642e01fSmrg    WindowPtr root;
13494642e01fSmrg    int i;
13504642e01fSmrg    int nscreens = screenInfo.numScreens;
13514642e01fSmrg
13524642e01fSmrg#ifdef PANORAMIX
13534642e01fSmrg    if (!noPanoramiXExtension)
13544642e01fSmrg        nscreens = 1;
13554642e01fSmrg#endif
13564642e01fSmrg
1357f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13584202a189Smrg        root = screenInfo.screens[i]->root;
1359f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
13604642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
13614202a189Smrg             * the pointer paired with dev is below the current root window,
13624642e01fSmrg             * do a NotifyPointer run. */
13634642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1364f7df2e56Smrg                B != PointerRootWin) {
1365f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1366f7df2e56Smrg
13674642e01fSmrg                if (ptrwin)
1368f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1369f7df2e56Smrg                                                    TRUE);
13704642e01fSmrg            }
1371f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1372f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
13734642e01fSmrg        }
13744642e01fSmrg    }
13754642e01fSmrg
1376f7df2e56Smrg    root = B;                   /* get B's root window */
1377f7df2e56Smrg    while (root->parent)
13784642e01fSmrg        root = root->parent;
13794642e01fSmrg
1380f7df2e56Smrg    if (B != root) {
13814642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinearVirtual, root);
13824642e01fSmrg        CoreFocusInEvents(dev, root, B, mode, NotifyNonlinearVirtual);
13834642e01fSmrg    }
13844642e01fSmrg
1385f7df2e56Smrg    if (!HasFocus(B)) {
13864642e01fSmrg        WindowPtr child = FirstFocusChild(B);
1387f7df2e56Smrg
1388f7df2e56Smrg        if (child) {
1389f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1390f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1391f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1392f7df2e56Smrg        }
1393f7df2e56Smrg        else {
13944642e01fSmrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
13954642e01fSmrg            /* NotifyPointer B-P unless P is child or below. */
13964642e01fSmrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
13974642e01fSmrg        }
13984642e01fSmrg    }
13994642e01fSmrg
14004642e01fSmrg}
14014642e01fSmrg
14024642e01fSmrgstatic void
1403f7df2e56SmrgCoreFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
14044642e01fSmrg{
14054202a189Smrg    if (!IsMaster(dev))
14064642e01fSmrg        return;
14074642e01fSmrg
14084202a189Smrg    SetFocusOut(dev);
14094642e01fSmrg
14104642e01fSmrg    if (((to == NullWindow) || (to == PointerRootWin)) &&
14114642e01fSmrg        ((from == NullWindow) || (from == PointerRootWin)))
14124642e01fSmrg        CoreFocusPointerRootNoneSwitch(dev, from, to, mode);
14134642e01fSmrg    else if ((to == NullWindow) || (to == PointerRootWin))
14144642e01fSmrg        CoreFocusToPointerRootOrNone(dev, from, to, mode);
14154642e01fSmrg    else if ((from == NullWindow) || (from == PointerRootWin))
14164642e01fSmrg        CoreFocusFromPointerRootOrNone(dev, from, to, mode);
14174642e01fSmrg    else if (IsParent(from, to))
14184642e01fSmrg        CoreFocusToDescendant(dev, from, to, mode);
14194642e01fSmrg    else if (IsParent(to, from))
14204642e01fSmrg        CoreFocusToAncestor(dev, from, to, mode);
14214642e01fSmrg    else
14224642e01fSmrg        CoreFocusNonLinear(dev, from, to, mode);
14234642e01fSmrg
14244642e01fSmrg    SetFocusIn(dev, to);
14254642e01fSmrg}
14264642e01fSmrg
14274642e01fSmrgstatic void
1428f7df2e56SmrgDeviceFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
14294642e01fSmrg{
1430f7df2e56Smrg    int out, in;                /* for holding details for to/from
1431f7df2e56Smrg                                   PointerRoot/None */
14324642e01fSmrg    int i;
14334642e01fSmrg    int nscreens = screenInfo.numScreens;
1434f7df2e56Smrg    SpritePtr sprite = dev->spriteInfo->sprite;
14354642e01fSmrg
14364642e01fSmrg    if (from == to)
14374642e01fSmrg        return;
14384642e01fSmrg    out = (from == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14394642e01fSmrg    in = (to == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14404642e01fSmrg    /* wrong values if neither, but then not referenced */
14414642e01fSmrg
14424642e01fSmrg#ifdef PANORAMIX
14434642e01fSmrg    if (!noPanoramiXExtension)
14444642e01fSmrg        nscreens = 1;
14454642e01fSmrg#endif
14464642e01fSmrg
1447f7df2e56Smrg    if ((to == NullWindow) || (to == PointerRootWin)) {
1448f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1449f7df2e56Smrg            if (from == PointerRootWin) {
1450f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1451f7df2e56Smrg                                 sprite->win);
1452f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1453f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1454f7df2e56Smrg                                     NotifyPointer);
1455f7df2e56Smrg            }
14564642e01fSmrg            /* Notify all the roots */
14574642e01fSmrg            for (i = 0; i < nscreens; i++)
1458f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1459f7df2e56Smrg                                 screenInfo.screens[i]->root);
14604642e01fSmrg        }
1461f7df2e56Smrg        else {
1462f7df2e56Smrg            if (IsParent(from, sprite->win)) {
1463f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1464f7df2e56Smrg                                 sprite->win);
14654642e01fSmrg                DeviceFocusOutEvents(dev, sprite->win, from, mode,
1466f7df2e56Smrg                                     NotifyPointer);
1467f7df2e56Smrg            }
14684202a189Smrg            DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
14694642e01fSmrg            /* next call catches the root too, if the screen changed */
1470f7df2e56Smrg            DeviceFocusOutEvents(dev, from, NullWindow, mode,
1471f7df2e56Smrg                                 NotifyNonlinearVirtual);
14724642e01fSmrg        }
14734642e01fSmrg        /* Notify all the roots */
14744642e01fSmrg        for (i = 0; i < nscreens; i++)
1475f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, in,
1476f7df2e56Smrg                             screenInfo.screens[i]->root);
1477f7df2e56Smrg        if (to == PointerRootWin) {
1478f7df2e56Smrg            DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), sprite->win,
1479f7df2e56Smrg                                mode, NotifyPointer);
1480f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyPointer, sprite->win);
1481f7df2e56Smrg        }
14824642e01fSmrg    }
1483f7df2e56Smrg    else {
1484f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1485f7df2e56Smrg            if (from == PointerRootWin) {
1486f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1487f7df2e56Smrg                                 sprite->win);
1488f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1489f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1490f7df2e56Smrg                                     NotifyPointer);
1491f7df2e56Smrg            }
14924642e01fSmrg            for (i = 0; i < nscreens; i++)
1493f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1494f7df2e56Smrg                                 screenInfo.screens[i]->root);
14954642e01fSmrg            if (to->parent != NullWindow)
1496f7df2e56Smrg                DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), to, mode,
1497f7df2e56Smrg                                    NotifyNonlinearVirtual);
14984202a189Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
14994642e01fSmrg            if (IsParent(to, sprite->win))
15004642e01fSmrg                DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer);
15014642e01fSmrg        }
1502f7df2e56Smrg        else {
1503f7df2e56Smrg            if (IsParent(to, from)) {
15044202a189Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyAncestor, from);
1505f7df2e56Smrg                DeviceFocusOutEvents(dev, from, to, mode, NotifyVirtual);
15064202a189Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyInferior, to);
15074642e01fSmrg                if ((IsParent(to, sprite->win)) &&
1508f7df2e56Smrg                    (sprite->win != from) &&
1509f7df2e56Smrg                    (!IsParent(from, sprite->win)) &&
1510f7df2e56Smrg                    (!IsParent(sprite->win, from)))
1511f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1512f7df2e56Smrg                                        NotifyPointer);
15134642e01fSmrg            }
1514f7df2e56Smrg            else if (IsParent(from, to)) {
1515f7df2e56Smrg                if ((IsParent(from, sprite->win)) &&
1516f7df2e56Smrg                    (sprite->win != from) &&
1517f7df2e56Smrg                    (!IsParent(to, sprite->win)) &&
1518f7df2e56Smrg                    (!IsParent(sprite->win, to))) {
1519f7df2e56Smrg                    DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1520f7df2e56Smrg                                     sprite->win);
1521f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1522f7df2e56Smrg                                         NotifyPointer);
15234642e01fSmrg                }
1524f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyInferior, from);
1525f7df2e56Smrg                DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual);
1526f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyAncestor, to);
1527f7df2e56Smrg            }
1528f7df2e56Smrg            else {
1529f7df2e56Smrg                /* neither from or to is child of other */
1530f7df2e56Smrg                WindowPtr common = CommonAncestor(to, from);
1531f7df2e56Smrg
1532f7df2e56Smrg                /* common == NullWindow ==> different screens */
1533f7df2e56Smrg                if (IsParent(from, sprite->win))
1534f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1535f7df2e56Smrg                                         NotifyPointer);
1536f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
1537f7df2e56Smrg                if (from->parent != NullWindow)
1538f7df2e56Smrg                    DeviceFocusOutEvents(dev, from, common, mode,
1539f7df2e56Smrg                                         NotifyNonlinearVirtual);
1540f7df2e56Smrg                if (to->parent != NullWindow)
1541f7df2e56Smrg                    DeviceFocusInEvents(dev, common, to, mode,
1542f7df2e56Smrg                                        NotifyNonlinearVirtual);
1543f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
1544f7df2e56Smrg                if (IsParent(to, sprite->win))
1545f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1546f7df2e56Smrg                                        NotifyPointer);
1547f7df2e56Smrg            }
15484642e01fSmrg        }
15494642e01fSmrg    }
15504642e01fSmrg}
15514642e01fSmrg
15524642e01fSmrg/**
15534642e01fSmrg * Figure out if focus events are necessary and send them to the
15544642e01fSmrg * appropriate windows.
15554642e01fSmrg *
15564642e01fSmrg * @param from Window the focus moved out of.
15574642e01fSmrg * @param to Window the focus moved into.
15584642e01fSmrg */
15594642e01fSmrgvoid
1560f7df2e56SmrgDoFocusEvents(DeviceIntPtr pDev, WindowPtr from, WindowPtr to, int mode)
15614642e01fSmrg{
15624642e01fSmrg    if (!IsKeyboardDevice(pDev))
15634642e01fSmrg        return;
15644642e01fSmrg
15657e31ba66Smrg    if (from == to && mode != NotifyGrab && mode != NotifyUngrab)
1566f7df2e56Smrg        return;
15674642e01fSmrg
15684642e01fSmrg    CoreFocusEvents(pDev, from, to, mode);
15694642e01fSmrg    DeviceFocusEvents(pDev, from, to, mode);
15704642e01fSmrg}
1571