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:
62259ca590cSmrg        ev->valuator5 = v->axisVal[first + 5];
62359ca590cSmrg        /* fallthrough */
624875c6e4fSmrg    case 5:
62559ca590cSmrg        ev->valuator4 = v->axisVal[first + 4];
62659ca590cSmrg        /* fallthrough */
627875c6e4fSmrg    case 4:
62859ca590cSmrg        ev->valuator3 = v->axisVal[first + 3];
62959ca590cSmrg        /* fallthrough */
630f7df2e56Smrg    case 3:
631f7df2e56Smrg        ev->valuator2 = v->axisVal[first + 2];
63259ca590cSmrg        /* fallthrough */
633f7df2e56Smrg    case 2:
634f7df2e56Smrg        ev->valuator1 = v->axisVal[first + 1];
63559ca590cSmrg        /* fallthrough */
636f7df2e56Smrg    case 1:
637f7df2e56Smrg        ev->valuator0 = v->axisVal[first];
638f7df2e56Smrg        break;
639f7df2e56Smrg    }
640f7df2e56Smrg}
641f7df2e56Smrg
642f7df2e56Smrgstatic void
643f7df2e56SmrgFixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
644f7df2e56Smrg                     ButtonClassPtr b, ValuatorClassPtr v, int first)
645f7df2e56Smrg{
646f7df2e56Smrg    ev->type = DeviceStateNotify;
647f7df2e56Smrg    ev->deviceid = dev->id;
648f7df2e56Smrg    ev->time = currentTime.milliseconds;
649f7df2e56Smrg    ev->classes_reported = 0;
650f7df2e56Smrg    ev->num_keys = 0;
651f7df2e56Smrg    ev->num_buttons = 0;
652f7df2e56Smrg    ev->num_valuators = 0;
653f7df2e56Smrg
654f7df2e56Smrg    if (b) {
655f7df2e56Smrg        ev->classes_reported |= (1 << ButtonClass);
656f7df2e56Smrg        ev->num_buttons = b->numButtons;
657f7df2e56Smrg        memcpy((char *) ev->buttons, (char *) b->down, 4);
658f7df2e56Smrg    }
659875c6e4fSmrg    if (k) {
660f7df2e56Smrg        ev->classes_reported |= (1 << KeyClass);
661f7df2e56Smrg        ev->num_keys = k->xkbInfo->desc->max_key_code -
662f7df2e56Smrg            k->xkbInfo->desc->min_key_code;
663f7df2e56Smrg        memmove((char *) &ev->keys[0], (char *) k->down, 4);
664f7df2e56Smrg    }
665f7df2e56Smrg    if (v) {
666f7df2e56Smrg        int nval = v->numAxes - first;
667f7df2e56Smrg
668f7df2e56Smrg        ev->classes_reported |= (1 << ValuatorClass);
669f7df2e56Smrg        ev->classes_reported |= valuator_get_mode(dev, 0) << ModeBitsShift;
670f7df2e56Smrg        ev->num_valuators = nval < 3 ? nval : 3;
671f7df2e56Smrg        switch (ev->num_valuators) {
672f7df2e56Smrg        case 3:
673f7df2e56Smrg            ev->valuator2 = v->axisVal[first + 2];
67459ca590cSmrg            /* fallthrough */
675f7df2e56Smrg        case 2:
676f7df2e56Smrg            ev->valuator1 = v->axisVal[first + 1];
67759ca590cSmrg            /* fallthrough */
678f7df2e56Smrg        case 1:
679f7df2e56Smrg            ev->valuator0 = v->axisVal[first];
680f7df2e56Smrg            break;
681f7df2e56Smrg        }
682f7df2e56Smrg    }
683f7df2e56Smrg}
684f7df2e56Smrg
685875c6e4fSmrg/**
686875c6e4fSmrg * The device state notify event is split across multiple 32-byte events.
687875c6e4fSmrg * The first one contains the first 32 button state bits, the first 32
688875c6e4fSmrg * key state bits, and the first 3 valuator values.
689875c6e4fSmrg *
690875c6e4fSmrg * If a device has more than that, the server sends out:
691875c6e4fSmrg * - one deviceButtonStateNotify for buttons 32 and above
692875c6e4fSmrg * - one deviceKeyStateNotify for keys 32 and above
693875c6e4fSmrg * - one deviceValuator event per 6 valuators above valuator 4
694875c6e4fSmrg *
695875c6e4fSmrg * All events but the last one have the deviceid binary ORed with MORE_EVENTS,
696875c6e4fSmrg */
697f7df2e56Smrgstatic void
698f7df2e56SmrgDeliverStateNotifyEvent(DeviceIntPtr dev, WindowPtr win)
699f7df2e56Smrg{
700875c6e4fSmrg    /* deviceStateNotify, deviceKeyStateNotify, deviceButtonStateNotify
701875c6e4fSmrg     * and one deviceValuator for each 6 valuators */
702875c6e4fSmrg    deviceStateNotify sev[3 + (MAX_VALUATORS + 6)/6];
703f7df2e56Smrg    int evcount = 1;
704875c6e4fSmrg    deviceStateNotify *ev = sev;
705f7df2e56Smrg
706f7df2e56Smrg    KeyClassPtr k;
707f7df2e56Smrg    ButtonClassPtr b;
708f7df2e56Smrg    ValuatorClassPtr v;
709f7df2e56Smrg    int nval = 0, nkeys = 0, nbuttons = 0, first = 0;
710f7df2e56Smrg
711f7df2e56Smrg    if (!(wOtherInputMasks(win)) ||
712f7df2e56Smrg        !(wOtherInputMasks(win)->inputEvents[dev->id] & DeviceStateNotifyMask))
713f7df2e56Smrg        return;
714f7df2e56Smrg
715f7df2e56Smrg    if ((b = dev->button) != NULL) {
716f7df2e56Smrg        nbuttons = b->numButtons;
717875c6e4fSmrg        if (nbuttons > 32) /* first 32 are encoded in deviceStateNotify */
718f7df2e56Smrg            evcount++;
719f7df2e56Smrg    }
720f7df2e56Smrg    if ((k = dev->key) != NULL) {
721f7df2e56Smrg        nkeys = k->xkbInfo->desc->max_key_code - k->xkbInfo->desc->min_key_code;
722875c6e4fSmrg        if (nkeys > 32) /* first 32 are encoded in deviceStateNotify */
723f7df2e56Smrg            evcount++;
724f7df2e56Smrg    }
725f7df2e56Smrg    if ((v = dev->valuator) != NULL) {
726f7df2e56Smrg        nval = v->numAxes;
727875c6e4fSmrg        /* first three are encoded in deviceStateNotify, then
728875c6e4fSmrg         * it's 6 per deviceValuator event */
729875c6e4fSmrg        evcount += ((nval - 3) + 6)/6;
730f7df2e56Smrg    }
731f7df2e56Smrg
7322f76b07dSmrg    BUG_RETURN(evcount > ARRAY_SIZE(sev));
733875c6e4fSmrg
734875c6e4fSmrg    FixDeviceStateNotify(dev, ev, k, b, v, first);
735875c6e4fSmrg
736875c6e4fSmrg    if (b != NULL && nbuttons > 32) {
737875c6e4fSmrg        deviceButtonStateNotify *bev = (deviceButtonStateNotify *) ++ev;
738875c6e4fSmrg        (ev - 1)->deviceid |= MORE_EVENTS;
739875c6e4fSmrg        bev->type = DeviceButtonStateNotify;
740875c6e4fSmrg        bev->deviceid = dev->id;
741d566a54bSmrg        memcpy((char *) &bev->buttons[0], (char *) &b->down[4],
742875c6e4fSmrg               DOWN_LENGTH - 4);
743f7df2e56Smrg    }
744f7df2e56Smrg
745875c6e4fSmrg    if (k != NULL && nkeys > 32) {
746875c6e4fSmrg        deviceKeyStateNotify *kev = (deviceKeyStateNotify *) ++ev;
747875c6e4fSmrg        (ev - 1)->deviceid |= MORE_EVENTS;
748875c6e4fSmrg        kev->type = DeviceKeyStateNotify;
749875c6e4fSmrg        kev->deviceid = dev->id;
750875c6e4fSmrg        memmove((char *) &kev->keys[0], (char *) &k->down[4], 28);
751f7df2e56Smrg    }
752f7df2e56Smrg
753875c6e4fSmrg    first = 3;
754875c6e4fSmrg    nval -= 3;
755f7df2e56Smrg    while (nval > 0) {
756875c6e4fSmrg        ev->deviceid |= MORE_EVENTS;
757875c6e4fSmrg        FixDeviceValuator(dev, (deviceValuator *) ++ev, v, first);
758875c6e4fSmrg        first += 6;
759875c6e4fSmrg        nval -= 6;
760f7df2e56Smrg    }
761f7df2e56Smrg
762f7df2e56Smrg    DeliverEventsToWindow(dev, win, (xEvent *) sev, evcount,
763f7df2e56Smrg                          DeviceStateNotifyMask, NullGrab);
764f7df2e56Smrg}
765f7df2e56Smrg
766f7df2e56Smrgvoid
767f7df2e56SmrgDeviceFocusEvent(DeviceIntPtr dev, int type, int mode, int detail,
768f7df2e56Smrg                 WindowPtr pWin)
769f7df2e56Smrg{
770f7df2e56Smrg    deviceFocus event;
771f7df2e56Smrg    xXIFocusInEvent *xi2event;
772f7df2e56Smrg    DeviceIntPtr mouse;
773f7df2e56Smrg    int btlen, len, i;
774f7df2e56Smrg
775f7df2e56Smrg    mouse = IsFloating(dev) ? dev : GetMaster(dev, MASTER_POINTER);
776f7df2e56Smrg
777875c6e4fSmrg    /* XI 2 event contains the logical button map - maps are CARD8
778875c6e4fSmrg     * so we need 256 bits for the possibly maximum mapping */
779875c6e4fSmrg    btlen = (mouse->button) ? bits_to_bytes(256) : 0;
780f7df2e56Smrg    btlen = bytes_to_int32(btlen);
781f7df2e56Smrg    len = sizeof(xXIFocusInEvent) + btlen * 4;
782f7df2e56Smrg
783f7df2e56Smrg    xi2event = calloc(1, len);
784f7df2e56Smrg    xi2event->type = GenericEvent;
785f7df2e56Smrg    xi2event->extension = IReqCode;
786f7df2e56Smrg    xi2event->evtype = type;
787f7df2e56Smrg    xi2event->length = bytes_to_int32(len - sizeof(xEvent));
788f7df2e56Smrg    xi2event->buttons_len = btlen;
789f7df2e56Smrg    xi2event->detail = detail;
790f7df2e56Smrg    xi2event->time = currentTime.milliseconds;
791f7df2e56Smrg    xi2event->deviceid = dev->id;
792f7df2e56Smrg    xi2event->sourceid = dev->id;       /* a device doesn't change focus by itself */
793f7df2e56Smrg    xi2event->mode = mode;
794f7df2e56Smrg    xi2event->root_x = double_to_fp1616(mouse->spriteInfo->sprite->hot.x);
795f7df2e56Smrg    xi2event->root_y = double_to_fp1616(mouse->spriteInfo->sprite->hot.y);
796f7df2e56Smrg
797f7df2e56Smrg    for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++)
798f7df2e56Smrg        if (BitIsOn(mouse->button->down, i))
799f7df2e56Smrg            SetBit(&xi2event[1], mouse->button->map[i]);
800f7df2e56Smrg
801f7df2e56Smrg    if (dev->key) {
802f7df2e56Smrg        xi2event->mods.base_mods = dev->key->xkbInfo->state.base_mods;
803f7df2e56Smrg        xi2event->mods.latched_mods = dev->key->xkbInfo->state.latched_mods;
804f7df2e56Smrg        xi2event->mods.locked_mods = dev->key->xkbInfo->state.locked_mods;
805f7df2e56Smrg        xi2event->mods.effective_mods = dev->key->xkbInfo->state.mods;
806f7df2e56Smrg
807f7df2e56Smrg        xi2event->group.base_group = dev->key->xkbInfo->state.base_group;
808f7df2e56Smrg        xi2event->group.latched_group = dev->key->xkbInfo->state.latched_group;
809f7df2e56Smrg        xi2event->group.locked_group = dev->key->xkbInfo->state.locked_group;
810f7df2e56Smrg        xi2event->group.effective_group = dev->key->xkbInfo->state.group;
811f7df2e56Smrg    }
812f7df2e56Smrg
813f7df2e56Smrg    FixUpEventFromWindow(dev->spriteInfo->sprite, (xEvent *) xi2event, pWin,
814f7df2e56Smrg                         None, FALSE);
815f7df2e56Smrg
816f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) xi2event, 1,
817f7df2e56Smrg                          GetEventFilter(dev, (xEvent *) xi2event), NullGrab);
818f7df2e56Smrg
819f7df2e56Smrg    free(xi2event);
820f7df2e56Smrg
821f7df2e56Smrg    /* XI 1.x event */
822f7df2e56Smrg    event = (deviceFocus) {
823f7df2e56Smrg        .deviceid = dev->id,
824f7df2e56Smrg        .mode = mode,
825f7df2e56Smrg        .type = (type == XI_FocusIn) ? DeviceFocusIn : DeviceFocusOut,
826f7df2e56Smrg        .detail = detail,
827f7df2e56Smrg        .window = pWin->drawable.id,
828f7df2e56Smrg        .time = currentTime.milliseconds
829f7df2e56Smrg    };
830f7df2e56Smrg
831f7df2e56Smrg    DeliverEventsToWindow(dev, pWin, (xEvent *) &event, 1,
832f7df2e56Smrg                          DeviceFocusChangeMask, NullGrab);
833f7df2e56Smrg
834f7df2e56Smrg    if (event.type == DeviceFocusIn)
835f7df2e56Smrg        DeliverStateNotifyEvent(dev, pWin);
836f7df2e56Smrg}
837f7df2e56Smrg
8384642e01fSmrg/**
8394202a189Smrg * Send focus out events to all windows between 'child' and 'ancestor'.
8404642e01fSmrg * Events are sent running up the hierarchy.
8414642e01fSmrg */
8424642e01fSmrgstatic void
8434642e01fSmrgDeviceFocusOutEvents(DeviceIntPtr dev,
844f7df2e56Smrg                     WindowPtr child, WindowPtr ancestor, int mode, int detail)
8454642e01fSmrg{
846f7df2e56Smrg    WindowPtr win;
8474642e01fSmrg
8484642e01fSmrg    if (ancestor == child)
849f7df2e56Smrg        return;
8504642e01fSmrg    for (win = child->parent; win != ancestor; win = win->parent)
8514202a189Smrg        DeviceFocusEvent(dev, XI_FocusOut, mode, detail, win);
8524642e01fSmrg}
8534642e01fSmrg
8544642e01fSmrg/**
8554202a189Smrg * Send enter notifies to all windows between 'ancestor' and 'child' (excluding
8564642e01fSmrg * both). Events are sent running up the window hierarchy. This function
8574642e01fSmrg * recurses.
8584642e01fSmrg */
8594642e01fSmrgstatic void
8604642e01fSmrgDeviceFocusInEvents(DeviceIntPtr dev,
861f7df2e56Smrg                    WindowPtr ancestor, WindowPtr child, int mode, int detail)
8624642e01fSmrg{
863f7df2e56Smrg    WindowPtr parent = child->parent;
8644642e01fSmrg
8654642e01fSmrg    if (ancestor == parent || !parent)
866f7df2e56Smrg        return;
8674642e01fSmrg    DeviceFocusInEvents(dev, ancestor, parent, mode, detail);
8684202a189Smrg    DeviceFocusEvent(dev, XI_FocusIn, mode, detail, parent);
8694642e01fSmrg}
8704642e01fSmrg
8714642e01fSmrg/**
8724202a189Smrg * Send FocusIn events to all windows between 'ancestor' and 'child' (excluding
8734642e01fSmrg * both). Events are sent running down the window hierarchy. This function
8744642e01fSmrg * recurses.
8754642e01fSmrg */
8764642e01fSmrgstatic void
8774642e01fSmrgCoreFocusInEvents(DeviceIntPtr dev,
878f7df2e56Smrg                  WindowPtr ancestor, WindowPtr child, int mode, int detail)
8794642e01fSmrg{
880f7df2e56Smrg    WindowPtr parent = child->parent;
881f7df2e56Smrg
8824642e01fSmrg    if (ancestor == parent)
883f7df2e56Smrg        return;
8844642e01fSmrg    CoreFocusInEvents(dev, ancestor, parent, mode, detail);
8854642e01fSmrg
8864642e01fSmrg    /* Case 3:
887f7df2e56Smrg       A is above W, B is a descendant
888f7df2e56Smrg
889f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
890f7df2e56Smrg       Virtual or NonlinearVirtual
891f7df2e56Smrg
892f7df2e56Smrg       MPX:
893f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
894f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
895f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
896f7df2e56Smrg       descendant
897f7df2e56Smrg       F(W) stays on the same descendant, or changes to a different
898f7df2e56Smrg       descendant. The event should be suppressed.
899f7df2e56Smrg       Case 3C: Otherwise:
900f7df2e56Smrg       F(W) moves from a window above W to a descendant. The detail may
901f7df2e56Smrg       need to be changed from Virtual to NonlinearVirtual depending
902f7df2e56Smrg       on the previous F(W). */
9034642e01fSmrg
9044642e01fSmrg    if (!HasFocus(parent) && !FirstFocusChild(parent))
905f7df2e56Smrg        CoreFocusEvent(dev, FocusIn, mode, detail, parent);
9064642e01fSmrg}
9074642e01fSmrg
9084642e01fSmrgstatic void
9094642e01fSmrgCoreFocusOutEvents(DeviceIntPtr dev,
910f7df2e56Smrg                   WindowPtr child, WindowPtr ancestor, int mode, int detail)
9114642e01fSmrg{
912f7df2e56Smrg    WindowPtr win;
9134642e01fSmrg
9144642e01fSmrg    if (ancestor == child)
9154642e01fSmrg        return;
9164642e01fSmrg
917f7df2e56Smrg    for (win = child->parent; win != ancestor; win = win->parent) {
9184642e01fSmrg        /*Case 7:
919f7df2e56Smrg           A is a descendant of W, B is above W
920f7df2e56Smrg
921f7df2e56Smrg           Classically: A FocusOut is generated on W with a detail of Virtual
922f7df2e56Smrg           or NonlinearVirtual.
923f7df2e56Smrg
924f7df2e56Smrg           MPX:
925f7df2e56Smrg           Case 3A: There is at least one other focus on W itself
926f7df2e56Smrg           F(W) doesn't change, the event should be suppressed.
927f7df2e56Smrg           Case 3B: Otherwise, if there is at least one other focus in a
928f7df2e56Smrg           descendant
929f7df2e56Smrg           F(W) stays on the same descendant, or changes to a different
930f7df2e56Smrg           descendant. The event should be suppressed.
931f7df2e56Smrg           Case 3C: Otherwise:
932f7df2e56Smrg           F(W) changes from the descendant of W to a window above W.
933f7df2e56Smrg           The detail may need to be changed from Virtual to NonlinearVirtual
934f7df2e56Smrg           or vice-versa depending on the new P(W). */
9354642e01fSmrg
9364642e01fSmrg        /* If one window has a focus or a child with a focuspointer, skip some
9374642e01fSmrg         * work and exit. */
9384642e01fSmrg        if (HasFocus(win) || FirstFocusChild(win))
9394642e01fSmrg            return;
9404642e01fSmrg
9414642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, detail, win);
9424642e01fSmrg    }
9434642e01fSmrg}
9444642e01fSmrg
9454642e01fSmrg/**
9464642e01fSmrg * Send FocusOut(NotifyPointer) events from the current pointer window (which
9474202a189Smrg * is a descendant of pwin_parent) up to (excluding) pwin_parent.
9484642e01fSmrg *
9494202a189Smrg * NotifyPointer events are only sent for the device paired with dev.
9504642e01fSmrg *
9514202a189Smrg * If the current pointer window is a descendant of 'exclude' or an ancestor of
9524202a189Smrg * 'exclude', no events are sent. If the current pointer IS 'exclude', events
9534202a189Smrg * are sent!
9544642e01fSmrg */
9554642e01fSmrgstatic void
9564642e01fSmrgCoreFocusOutNotifyPointerEvents(DeviceIntPtr dev,
9574642e01fSmrg                                WindowPtr pwin_parent,
958f7df2e56Smrg                                WindowPtr exclude, int mode, int inclusive)
9594642e01fSmrg{
9604642e01fSmrg    WindowPtr P, stopAt;
9614642e01fSmrg
962f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
9634642e01fSmrg
9644642e01fSmrg    if (!P)
9654642e01fSmrg        return;
9664642e01fSmrg    if (!IsParent(pwin_parent, P))
9674642e01fSmrg        if (!(pwin_parent == P && inclusive))
9684642e01fSmrg            return;
9694642e01fSmrg
9704642e01fSmrg    if (exclude != None && exclude != PointerRootWin &&
9714642e01fSmrg        (IsParent(exclude, P) || IsParent(P, exclude)))
9724642e01fSmrg        return;
9734642e01fSmrg
9744642e01fSmrg    stopAt = (inclusive) ? pwin_parent->parent : pwin_parent;
9754642e01fSmrg
9764642e01fSmrg    for (; P && P != stopAt; P = P->parent)
9774642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyPointer, P);
9784642e01fSmrg}
9794642e01fSmrg
9804642e01fSmrg/**
9814642e01fSmrg * DO NOT CALL DIRECTLY.
9824642e01fSmrg * Recursion helper for CoreFocusInNotifyPointerEvents.
9834642e01fSmrg */
9844642e01fSmrgstatic void
9854642e01fSmrgCoreFocusInRecurse(DeviceIntPtr dev,
986f7df2e56Smrg                   WindowPtr win, WindowPtr stopAt, int mode, int inclusive)
9874642e01fSmrg{
9884642e01fSmrg    if ((!inclusive && win == stopAt) || !win)
9894642e01fSmrg        return;
9904642e01fSmrg
9914642e01fSmrg    CoreFocusInRecurse(dev, win->parent, stopAt, mode, inclusive);
9924642e01fSmrg    CoreFocusEvent(dev, FocusIn, mode, NotifyPointer, win);
9934642e01fSmrg}
9944642e01fSmrg
9954642e01fSmrg/**
9964202a189Smrg * Send FocusIn(NotifyPointer) events from pwin_parent down to
9974202a189Smrg * including the current pointer window (which is a descendant of pwin_parent).
9984642e01fSmrg *
9994202a189Smrg * @param pwin The pointer window.
10004202a189Smrg * @param exclude If the pointer window is a child of 'exclude', no events are
10014202a189Smrg *                sent.
10024202a189Smrg * @param inclusive If TRUE, pwin_parent will receive the event too.
10034642e01fSmrg */
10044642e01fSmrgstatic void
10054642e01fSmrgCoreFocusInNotifyPointerEvents(DeviceIntPtr dev,
10064642e01fSmrg                               WindowPtr pwin_parent,
1007f7df2e56Smrg                               WindowPtr exclude, int mode, int inclusive)
10084642e01fSmrg{
10094642e01fSmrg    WindowPtr P;
10104642e01fSmrg
1011f7df2e56Smrg    P = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
10124642e01fSmrg
10134642e01fSmrg    if (!P || P == exclude || (pwin_parent != P && !IsParent(pwin_parent, P)))
10144642e01fSmrg        return;
10154642e01fSmrg
10164642e01fSmrg    if (exclude != None && (IsParent(exclude, P) || IsParent(P, exclude)))
10174642e01fSmrg        return;
10184642e01fSmrg
10194642e01fSmrg    CoreFocusInRecurse(dev, P, pwin_parent, mode, inclusive);
10204642e01fSmrg}
10214642e01fSmrg
10224642e01fSmrg/**
10234202a189Smrg * Focus of dev moves from A to B and A neither a descendant of B nor is
10244202a189Smrg * B a descendant of A.
10254642e01fSmrg */
10264642e01fSmrgstatic void
1027f7df2e56SmrgCoreFocusNonLinear(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
10284642e01fSmrg{
10294642e01fSmrg    WindowPtr X = CommonAncestor(A, B);
10304642e01fSmrg
10314642e01fSmrg    /* Case 4:
1032f7df2e56Smrg       A is W, B is above W
10334642e01fSmrg
1034f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
10354642e01fSmrg       Ancestor or Nonlinear
10364642e01fSmrg
1037f7df2e56Smrg       MPX:
1038f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1039f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1040f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1041f7df2e56Smrg       descendant of W
1042f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1043f7df2e56Smrg       is set to Inferior
1044f7df2e56Smrg       Case 3C: Otherwise:
1045f7df2e56Smrg       The focus window moves from W to a window above W.
1046f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1047f7df2e56Smrg       vice versa depending on the the new F(W)
10484642e01fSmrg     */
10494642e01fSmrg
1050f7df2e56Smrg    if (!HasFocus(A)) {
10514642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1052f7df2e56Smrg
1053f7df2e56Smrg        if (child) {
1054f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
10554642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
10564642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1057f7df2e56Smrg        }
1058f7df2e56Smrg        else {
10594642e01fSmrg            /* NotifyPointer P-A */
10604642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
10614642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
10624642e01fSmrg        }
10634642e01fSmrg    }
10644642e01fSmrg
10654642e01fSmrg    CoreFocusOutEvents(dev, A, X, mode, NotifyNonlinearVirtual);
10664642e01fSmrg
10674642e01fSmrg    /*
1068f7df2e56Smrg       Case 9:
1069f7df2e56Smrg       A is a descendant of W, B is a descendant of W
10704642e01fSmrg
1071f7df2e56Smrg       Classically: No events are generated on W
1072f7df2e56Smrg       MPX: The focus window stays the same or moves to a different
1073f7df2e56Smrg       descendant of W. No events should be generated on W.
10744642e01fSmrg
10754642e01fSmrg       Therefore, no event to X.
1076f7df2e56Smrg     */
10774642e01fSmrg
10784642e01fSmrg    CoreFocusInEvents(dev, X, B, mode, NotifyNonlinearVirtual);
10794642e01fSmrg
10804642e01fSmrg    /* Case 2:
1081f7df2e56Smrg       A is above W, B=W
10824642e01fSmrg
1083f7df2e56Smrg       Classically: The move generates an EnterNotify on W with a detail of
1084f7df2e56Smrg       Ancestor or Nonlinear
1085f7df2e56Smrg
1086f7df2e56Smrg       MPX:
1087f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1088f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1089f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1090f7df2e56Smrg       descendant
1091f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1092f7df2e56Smrg       Case 2C: Otherwise:
1093f7df2e56Smrg       F(W) changes from a window above W to W itself.
1094f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1095f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1096f7df2e56Smrg
1097f7df2e56Smrg    if (!HasFocus(B)) {
1098f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1099f7df2e56Smrg
1100f7df2e56Smrg        if (child) {
1101f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1102f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1103f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1104f7df2e56Smrg        }
1105f7df2e56Smrg        else {
1106f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
1107f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1108f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
1109f7df2e56Smrg        }
1110f7df2e56Smrg    }
1111f7df2e56Smrg}
11124642e01fSmrg
11134642e01fSmrg/**
11144202a189Smrg * Focus of dev moves from A to B and A is a descendant of B.
11154642e01fSmrg */
11164642e01fSmrgstatic void
1117f7df2e56SmrgCoreFocusToAncestor(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11184642e01fSmrg{
11194642e01fSmrg    /* Case 4:
1120f7df2e56Smrg       A is W, B is above W
11214642e01fSmrg
1122f7df2e56Smrg       Classically: The change generates a FocusOut on W with a detail of
11234642e01fSmrg       Ancestor or Nonlinear
11244642e01fSmrg
1125f7df2e56Smrg       MPX:
1126f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1127f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1128f7df2e56Smrg       Case 3B: Otherwise, if there is at least one other focus in a
1129f7df2e56Smrg       descendant of W
1130f7df2e56Smrg       F(W) changes from W to a descendant of W. The detail field
1131f7df2e56Smrg       is set to Inferior
1132f7df2e56Smrg       Case 3C: Otherwise:
1133f7df2e56Smrg       The focus window moves from W to a window above W.
1134f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear or
1135f7df2e56Smrg       vice versa depending on the the new F(W)
11364642e01fSmrg     */
1137f7df2e56Smrg    if (!HasFocus(A)) {
11384642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1139f7df2e56Smrg
1140f7df2e56Smrg        if (child) {
1141f7df2e56Smrg            /* NotifyPointer P-A unless P is child or below */
11424642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, child, mode, FALSE);
11434642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1144f7df2e56Smrg        }
1145f7df2e56Smrg        else
11464642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyAncestor, A);
11474642e01fSmrg    }
11484642e01fSmrg
11494642e01fSmrg    CoreFocusOutEvents(dev, A, B, mode, NotifyVirtual);
11504642e01fSmrg
11514642e01fSmrg    /* Case 8:
1152f7df2e56Smrg       A is a descendant of W, B is W
11534642e01fSmrg
1154f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
1155f7df2e56Smrg       NotifyInferior
11564642e01fSmrg
1157f7df2e56Smrg       MPX:
1158f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1159f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1160f7df2e56Smrg       Case 3B: Otherwise:
1161f7df2e56Smrg       F(W) changes from a descendant to W itself. */
11624642e01fSmrg
1163f7df2e56Smrg    if (!HasFocus(B)) {
11644642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
11654642e01fSmrg        /* NotifyPointer B-P unless P is A or below. */
11664642e01fSmrg        CoreFocusInNotifyPointerEvents(dev, B, A, mode, FALSE);
11674642e01fSmrg    }
11684642e01fSmrg}
11694642e01fSmrg
11704642e01fSmrg/**
11714202a189Smrg * Focus of dev moves from A to B and B is a descendant of A.
11724642e01fSmrg */
11734642e01fSmrgstatic void
1174f7df2e56SmrgCoreFocusToDescendant(DeviceIntPtr dev, WindowPtr A, WindowPtr B, int mode)
11754642e01fSmrg{
11764642e01fSmrg    /* Case 6:
1177f7df2e56Smrg       A is W, B is a descendant of W
11784642e01fSmrg
1179f7df2e56Smrg       Classically: A FocusOut is generated on W with a detail of
11804642e01fSmrg       NotifyInferior
11814642e01fSmrg
1182f7df2e56Smrg       MPX:
1183f7df2e56Smrg       Case 3A: There is at least one other focus on W itself
1184f7df2e56Smrg       F(W) doesn't change, the event should be suppressed
1185f7df2e56Smrg       Case 3B: Otherwise:
1186f7df2e56Smrg       F(W) changes from W to a descendant of W. */
11874642e01fSmrg
1188f7df2e56Smrg    if (!HasFocus(A)) {
1189f7df2e56Smrg        /* NotifyPointer P-A unless P is B or below */
11904642e01fSmrg        CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
11914642e01fSmrg        CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
11924642e01fSmrg    }
11934642e01fSmrg
11944642e01fSmrg    CoreFocusInEvents(dev, A, B, mode, NotifyVirtual);
11954642e01fSmrg
11964642e01fSmrg    /* Case 2:
1197f7df2e56Smrg       A is above W, B=W
1198f7df2e56Smrg
1199f7df2e56Smrg       Classically: The move generates an FocusIn on W with a detail of
1200f7df2e56Smrg       Ancestor or Nonlinear
1201f7df2e56Smrg
1202f7df2e56Smrg       MPX:
1203f7df2e56Smrg       Case 2A: There is at least one other focus on W itself
1204f7df2e56Smrg       F(W) doesn't change, so the event should be suppressed
1205f7df2e56Smrg       Case 2B: Otherwise, if there is at least one other focus in a
1206f7df2e56Smrg       descendant
1207f7df2e56Smrg       F(W) moves from a descendant to W. detail is changed to Inferior.
1208f7df2e56Smrg       Case 2C: Otherwise:
1209f7df2e56Smrg       F(W) changes from a window above W to W itself.
1210f7df2e56Smrg       The detail may need to be changed from Ancestor to Nonlinear
1211f7df2e56Smrg       or vice-versa depending on the previous F(W). */
1212f7df2e56Smrg
1213f7df2e56Smrg    if (!HasFocus(B)) {
1214f7df2e56Smrg        WindowPtr child = FirstFocusChild(B);
1215f7df2e56Smrg
1216f7df2e56Smrg        if (child) {
1217f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1218f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1219f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1220f7df2e56Smrg        }
1221f7df2e56Smrg        else
1222f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyAncestor, B);
1223f7df2e56Smrg    }
12244642e01fSmrg}
12254642e01fSmrg
12264642e01fSmrgstatic BOOL
12274642e01fSmrgHasOtherPointer(WindowPtr win, DeviceIntPtr exclude)
12284642e01fSmrg{
12294642e01fSmrg    int i;
12304642e01fSmrg
12314642e01fSmrg    for (i = 0; i < MAXDEVICES; i++)
12324642e01fSmrg        if (i != exclude->id && PointerWindows[i] == win)
12334642e01fSmrg            return TRUE;
12344642e01fSmrg
12354642e01fSmrg    return FALSE;
12364642e01fSmrg}
12374642e01fSmrg
12384642e01fSmrg/**
12394642e01fSmrg * Focus moves from PointerRoot to None or from None to PointerRoot.
12404642e01fSmrg * Assumption: Neither A nor B are valid windows.
12414642e01fSmrg */
12424642e01fSmrgstatic void
12434642e01fSmrgCoreFocusPointerRootNoneSwitch(DeviceIntPtr dev,
1244f7df2e56Smrg                               WindowPtr A,     /* PointerRootWin or NoneWin */
1245f7df2e56Smrg                               WindowPtr B,     /* NoneWin or PointerRootWin */
12464642e01fSmrg                               int mode)
12474642e01fSmrg{
12484642e01fSmrg    WindowPtr root;
12494642e01fSmrg    int i;
12504642e01fSmrg    int nscreens = screenInfo.numScreens;
12514642e01fSmrg
12524642e01fSmrg#ifdef PANORAMIX
12534642e01fSmrg    if (!noPanoramiXExtension)
12544642e01fSmrg        nscreens = 1;
12554642e01fSmrg#endif
12564642e01fSmrg
1257f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
12584202a189Smrg        root = screenInfo.screens[i]->root;
1259f7df2e56Smrg        if (!HasOtherPointer(root, GetMaster(dev, POINTER_OR_FLOAT)) &&
1260f7df2e56Smrg            !FirstFocusChild(root)) {
12614642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
12624202a189Smrg             * the pointer paired with dev is below the current root window,
12634642e01fSmrg             * do a NotifyPointer run. */
12644642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1265f7df2e56Smrg                B != PointerRootWin) {
1266f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1267f7df2e56Smrg
12684642e01fSmrg                if (ptrwin && IsParent(root, ptrwin))
1269f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1270f7df2e56Smrg                                                    TRUE);
12714642e01fSmrg            }
1272f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1273f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
1274f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1275f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
12764642e01fSmrg            if (B == PointerRootWin)
12774642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
12784642e01fSmrg        }
12794642e01fSmrg
12804642e01fSmrg    }
12814642e01fSmrg}
12824642e01fSmrg
12834642e01fSmrg/**
12844202a189Smrg * Focus moves from window A to PointerRoot or to None.
12854202a189Smrg * Assumption: A is a valid window and not PointerRoot or None.
12864642e01fSmrg */
12874642e01fSmrgstatic void
1288f7df2e56SmrgCoreFocusToPointerRootOrNone(DeviceIntPtr dev, WindowPtr A,
1289f7df2e56Smrg                             WindowPtr B,        /* PointerRootWin or NoneWin */
1290f7df2e56Smrg                             int mode)
12914642e01fSmrg{
12924642e01fSmrg    WindowPtr root;
12934642e01fSmrg    int i;
12944642e01fSmrg    int nscreens = screenInfo.numScreens;
12954642e01fSmrg
12964642e01fSmrg#ifdef PANORAMIX
12974642e01fSmrg    if (!noPanoramiXExtension)
12984642e01fSmrg        nscreens = 1;
12994642e01fSmrg#endif
13004642e01fSmrg
1301f7df2e56Smrg    if (!HasFocus(A)) {
13024642e01fSmrg        WindowPtr child = FirstFocusChild(A);
1303f7df2e56Smrg
1304f7df2e56Smrg        if (child) {
1305f7df2e56Smrg            /* NotifyPointer P-A unless P is B or below */
13064642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, B, mode, FALSE);
13074642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyInferior, A);
1308f7df2e56Smrg        }
1309f7df2e56Smrg        else {
13104642e01fSmrg            /* NotifyPointer P-A */
13114642e01fSmrg            CoreFocusOutNotifyPointerEvents(dev, A, None, mode, FALSE);
13124642e01fSmrg            CoreFocusEvent(dev, FocusOut, mode, NotifyNonlinear, A);
13134642e01fSmrg        }
13144642e01fSmrg    }
13154642e01fSmrg
13164642e01fSmrg    /* NullWindow means we include the root window */
13174642e01fSmrg    CoreFocusOutEvents(dev, A, NullWindow, mode, NotifyNonlinearVirtual);
13184642e01fSmrg
1319f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13204202a189Smrg        root = screenInfo.screens[i]->root;
1321f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
1322f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode,
1323f7df2e56Smrg                           B ? NotifyPointerRoot : NotifyDetailNone, root);
13244642e01fSmrg            if (B == PointerRootWin)
13254642e01fSmrg                CoreFocusInNotifyPointerEvents(dev, root, None, mode, TRUE);
13264642e01fSmrg        }
13274642e01fSmrg    }
13284642e01fSmrg}
13294642e01fSmrg
13304642e01fSmrg/**
13314202a189Smrg * Focus moves from PointerRoot or None to a window B.
13324202a189Smrg * Assumption: B is a valid window and not PointerRoot or None.
13334642e01fSmrg */
13344642e01fSmrgstatic void
13354642e01fSmrgCoreFocusFromPointerRootOrNone(DeviceIntPtr dev,
1336f7df2e56Smrg                               WindowPtr A,   /* PointerRootWin or NoneWin */
1337f7df2e56Smrg                               WindowPtr B, int mode)
13384642e01fSmrg{
13394642e01fSmrg    WindowPtr root;
13404642e01fSmrg    int i;
13414642e01fSmrg    int nscreens = screenInfo.numScreens;
13424642e01fSmrg
13434642e01fSmrg#ifdef PANORAMIX
13444642e01fSmrg    if (!noPanoramiXExtension)
13454642e01fSmrg        nscreens = 1;
13464642e01fSmrg#endif
13474642e01fSmrg
1348f7df2e56Smrg    for (i = 0; i < nscreens; i++) {
13494202a189Smrg        root = screenInfo.screens[i]->root;
1350f7df2e56Smrg        if (!HasFocus(root) && !FirstFocusChild(root)) {
13514642e01fSmrg            /* If pointer was on PointerRootWin and changes to NoneWin, and
13524202a189Smrg             * the pointer paired with dev is below the current root window,
13534642e01fSmrg             * do a NotifyPointer run. */
13544642e01fSmrg            if (dev->focus && dev->focus->win == PointerRootWin &&
1355f7df2e56Smrg                B != PointerRootWin) {
1356f7df2e56Smrg                WindowPtr ptrwin = PointerWin(GetMaster(dev, POINTER_OR_FLOAT));
1357f7df2e56Smrg
13584642e01fSmrg                if (ptrwin)
1359f7df2e56Smrg                    CoreFocusOutNotifyPointerEvents(dev, root, None, mode,
1360f7df2e56Smrg                                                    TRUE);
13614642e01fSmrg            }
1362f7df2e56Smrg            CoreFocusEvent(dev, FocusOut, mode,
1363f7df2e56Smrg                           A ? NotifyPointerRoot : NotifyDetailNone, root);
13644642e01fSmrg        }
13654642e01fSmrg    }
13664642e01fSmrg
1367f7df2e56Smrg    root = B;                   /* get B's root window */
1368f7df2e56Smrg    while (root->parent)
13694642e01fSmrg        root = root->parent;
13704642e01fSmrg
1371f7df2e56Smrg    if (B != root) {
13724642e01fSmrg        CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinearVirtual, root);
13734642e01fSmrg        CoreFocusInEvents(dev, root, B, mode, NotifyNonlinearVirtual);
13744642e01fSmrg    }
13754642e01fSmrg
1376f7df2e56Smrg    if (!HasFocus(B)) {
13774642e01fSmrg        WindowPtr child = FirstFocusChild(B);
1378f7df2e56Smrg
1379f7df2e56Smrg        if (child) {
1380f7df2e56Smrg            CoreFocusEvent(dev, FocusIn, mode, NotifyInferior, B);
1381f7df2e56Smrg            /* NotifyPointer B-P unless P is child or below. */
1382f7df2e56Smrg            CoreFocusInNotifyPointerEvents(dev, B, child, mode, FALSE);
1383f7df2e56Smrg        }
1384f7df2e56Smrg        else {
13854642e01fSmrg            CoreFocusEvent(dev, FocusIn, mode, NotifyNonlinear, B);
13864642e01fSmrg            /* NotifyPointer B-P unless P is child or below. */
13874642e01fSmrg            CoreFocusInNotifyPointerEvents(dev, B, None, mode, FALSE);
13884642e01fSmrg        }
13894642e01fSmrg    }
13904642e01fSmrg
13914642e01fSmrg}
13924642e01fSmrg
13934642e01fSmrgstatic void
1394f7df2e56SmrgCoreFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
13954642e01fSmrg{
13964202a189Smrg    if (!IsMaster(dev))
13974642e01fSmrg        return;
13984642e01fSmrg
13994202a189Smrg    SetFocusOut(dev);
14004642e01fSmrg
14014642e01fSmrg    if (((to == NullWindow) || (to == PointerRootWin)) &&
14024642e01fSmrg        ((from == NullWindow) || (from == PointerRootWin)))
14034642e01fSmrg        CoreFocusPointerRootNoneSwitch(dev, from, to, mode);
14044642e01fSmrg    else if ((to == NullWindow) || (to == PointerRootWin))
14054642e01fSmrg        CoreFocusToPointerRootOrNone(dev, from, to, mode);
14064642e01fSmrg    else if ((from == NullWindow) || (from == PointerRootWin))
14074642e01fSmrg        CoreFocusFromPointerRootOrNone(dev, from, to, mode);
14084642e01fSmrg    else if (IsParent(from, to))
14094642e01fSmrg        CoreFocusToDescendant(dev, from, to, mode);
14104642e01fSmrg    else if (IsParent(to, from))
14114642e01fSmrg        CoreFocusToAncestor(dev, from, to, mode);
14124642e01fSmrg    else
14134642e01fSmrg        CoreFocusNonLinear(dev, from, to, mode);
14144642e01fSmrg
14154642e01fSmrg    SetFocusIn(dev, to);
14164642e01fSmrg}
14174642e01fSmrg
14184642e01fSmrgstatic void
1419f7df2e56SmrgDeviceFocusEvents(DeviceIntPtr dev, WindowPtr from, WindowPtr to, int mode)
14204642e01fSmrg{
1421f7df2e56Smrg    int out, in;                /* for holding details for to/from
1422f7df2e56Smrg                                   PointerRoot/None */
14234642e01fSmrg    int i;
14244642e01fSmrg    int nscreens = screenInfo.numScreens;
1425f7df2e56Smrg    SpritePtr sprite = dev->spriteInfo->sprite;
14264642e01fSmrg
14274642e01fSmrg    if (from == to)
14284642e01fSmrg        return;
14294642e01fSmrg    out = (from == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14304642e01fSmrg    in = (to == NoneWin) ? NotifyDetailNone : NotifyPointerRoot;
14314642e01fSmrg    /* wrong values if neither, but then not referenced */
14324642e01fSmrg
14334642e01fSmrg#ifdef PANORAMIX
14344642e01fSmrg    if (!noPanoramiXExtension)
14354642e01fSmrg        nscreens = 1;
14364642e01fSmrg#endif
14374642e01fSmrg
1438f7df2e56Smrg    if ((to == NullWindow) || (to == PointerRootWin)) {
1439f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1440f7df2e56Smrg            if (from == PointerRootWin) {
1441f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1442f7df2e56Smrg                                 sprite->win);
1443f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1444f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1445f7df2e56Smrg                                     NotifyPointer);
1446f7df2e56Smrg            }
14474642e01fSmrg            /* Notify all the roots */
14484642e01fSmrg            for (i = 0; i < nscreens; i++)
1449f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1450f7df2e56Smrg                                 screenInfo.screens[i]->root);
14514642e01fSmrg        }
1452f7df2e56Smrg        else {
1453f7df2e56Smrg            if (IsParent(from, sprite->win)) {
1454f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1455f7df2e56Smrg                                 sprite->win);
14564642e01fSmrg                DeviceFocusOutEvents(dev, sprite->win, from, mode,
1457f7df2e56Smrg                                     NotifyPointer);
1458f7df2e56Smrg            }
14594202a189Smrg            DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
14604642e01fSmrg            /* next call catches the root too, if the screen changed */
1461f7df2e56Smrg            DeviceFocusOutEvents(dev, from, NullWindow, mode,
1462f7df2e56Smrg                                 NotifyNonlinearVirtual);
14634642e01fSmrg        }
14644642e01fSmrg        /* Notify all the roots */
14654642e01fSmrg        for (i = 0; i < nscreens; i++)
1466f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, in,
1467f7df2e56Smrg                             screenInfo.screens[i]->root);
1468f7df2e56Smrg        if (to == PointerRootWin) {
1469f7df2e56Smrg            DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), sprite->win,
1470f7df2e56Smrg                                mode, NotifyPointer);
1471f7df2e56Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyPointer, sprite->win);
1472f7df2e56Smrg        }
14734642e01fSmrg    }
1474f7df2e56Smrg    else {
1475f7df2e56Smrg        if ((from == NullWindow) || (from == PointerRootWin)) {
1476f7df2e56Smrg            if (from == PointerRootWin) {
1477f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1478f7df2e56Smrg                                 sprite->win);
1479f7df2e56Smrg                DeviceFocusOutEvents(dev, sprite->win,
1480f7df2e56Smrg                                     GetCurrentRootWindow(dev), mode,
1481f7df2e56Smrg                                     NotifyPointer);
1482f7df2e56Smrg            }
14834642e01fSmrg            for (i = 0; i < nscreens; i++)
1484f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, out,
1485f7df2e56Smrg                                 screenInfo.screens[i]->root);
14864642e01fSmrg            if (to->parent != NullWindow)
1487f7df2e56Smrg                DeviceFocusInEvents(dev, GetCurrentRootWindow(dev), to, mode,
1488f7df2e56Smrg                                    NotifyNonlinearVirtual);
14894202a189Smrg            DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
14904642e01fSmrg            if (IsParent(to, sprite->win))
14914642e01fSmrg                DeviceFocusInEvents(dev, to, sprite->win, mode, NotifyPointer);
14924642e01fSmrg        }
1493f7df2e56Smrg        else {
1494f7df2e56Smrg            if (IsParent(to, from)) {
14954202a189Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyAncestor, from);
1496f7df2e56Smrg                DeviceFocusOutEvents(dev, from, to, mode, NotifyVirtual);
14974202a189Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyInferior, to);
14984642e01fSmrg                if ((IsParent(to, sprite->win)) &&
1499f7df2e56Smrg                    (sprite->win != from) &&
1500f7df2e56Smrg                    (!IsParent(from, sprite->win)) &&
1501f7df2e56Smrg                    (!IsParent(sprite->win, from)))
1502f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1503f7df2e56Smrg                                        NotifyPointer);
15044642e01fSmrg            }
1505f7df2e56Smrg            else if (IsParent(from, to)) {
1506f7df2e56Smrg                if ((IsParent(from, sprite->win)) &&
1507f7df2e56Smrg                    (sprite->win != from) &&
1508f7df2e56Smrg                    (!IsParent(to, sprite->win)) &&
1509f7df2e56Smrg                    (!IsParent(sprite->win, to))) {
1510f7df2e56Smrg                    DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyPointer,
1511f7df2e56Smrg                                     sprite->win);
1512f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1513f7df2e56Smrg                                         NotifyPointer);
15144642e01fSmrg                }
1515f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyInferior, from);
1516f7df2e56Smrg                DeviceFocusInEvents(dev, from, to, mode, NotifyVirtual);
1517f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyAncestor, to);
1518f7df2e56Smrg            }
1519f7df2e56Smrg            else {
1520f7df2e56Smrg                /* neither from or to is child of other */
1521f7df2e56Smrg                WindowPtr common = CommonAncestor(to, from);
1522f7df2e56Smrg
1523f7df2e56Smrg                /* common == NullWindow ==> different screens */
1524f7df2e56Smrg                if (IsParent(from, sprite->win))
1525f7df2e56Smrg                    DeviceFocusOutEvents(dev, sprite->win, from, mode,
1526f7df2e56Smrg                                         NotifyPointer);
1527f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusOut, mode, NotifyNonlinear, from);
1528f7df2e56Smrg                if (from->parent != NullWindow)
1529f7df2e56Smrg                    DeviceFocusOutEvents(dev, from, common, mode,
1530f7df2e56Smrg                                         NotifyNonlinearVirtual);
1531f7df2e56Smrg                if (to->parent != NullWindow)
1532f7df2e56Smrg                    DeviceFocusInEvents(dev, common, to, mode,
1533f7df2e56Smrg                                        NotifyNonlinearVirtual);
1534f7df2e56Smrg                DeviceFocusEvent(dev, XI_FocusIn, mode, NotifyNonlinear, to);
1535f7df2e56Smrg                if (IsParent(to, sprite->win))
1536f7df2e56Smrg                    DeviceFocusInEvents(dev, to, sprite->win, mode,
1537f7df2e56Smrg                                        NotifyPointer);
1538f7df2e56Smrg            }
15394642e01fSmrg        }
15404642e01fSmrg    }
15414642e01fSmrg}
15424642e01fSmrg
15434642e01fSmrg/**
15444642e01fSmrg * Figure out if focus events are necessary and send them to the
15454642e01fSmrg * appropriate windows.
15464642e01fSmrg *
15474642e01fSmrg * @param from Window the focus moved out of.
15484642e01fSmrg * @param to Window the focus moved into.
15494642e01fSmrg */
15504642e01fSmrgvoid
1551f7df2e56SmrgDoFocusEvents(DeviceIntPtr pDev, WindowPtr from, WindowPtr to, int mode)
15524642e01fSmrg{
15534642e01fSmrg    if (!IsKeyboardDevice(pDev))
15544642e01fSmrg        return;
15554642e01fSmrg
15567e31ba66Smrg    if (from == to && mode != NotifyGrab && mode != NotifyUngrab)
1557f7df2e56Smrg        return;
15584642e01fSmrg
15594642e01fSmrg    CoreFocusEvents(pDev, from, to, mode);
15604642e01fSmrg    DeviceFocusEvents(pDev, from, to, mode);
15614642e01fSmrg}
1562