mipointer.c revision eee80088
1/*
2
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24*/
25
26/**
27 * @file
28 * This file contains functions to move the pointer on the screen and/or
29 * restrict its movement. These functions are divided into two sets:
30 * Screen-specific functions that are used as function pointers from other
31 * parts of the server (and end up heavily wrapped by e.g. animcur and
32 * xfixes):
33 *      miPointerConstrainCursor
34 *      miPointerCursorLimits
35 *      miPointerDisplayCursor
36 *      miPointerRealizeCursor
37 *      miPointerUnrealizeCursor
38 *      miPointerSetCursorPosition
39 *      miRecolorCursor
40 *      miPointerDeviceInitialize
41 *      miPointerDeviceCleanup
42 * If wrapped, these are the last element in the wrapping chain. They may
43 * call into sprite-specific code through further function pointers though.
44 *
45 * The second type of functions are those that are directly called by the
46 * DIX, DDX and some drivers.
47 */
48
49#ifdef HAVE_DIX_CONFIG_H
50#include <dix-config.h>
51#endif
52
53#include   <X11/X.h>
54#include   <X11/Xmd.h>
55#include   <X11/Xproto.h>
56#include   "misc.h"
57#include   "windowstr.h"
58#include   "pixmapstr.h"
59#include   "mi.h"
60#include   "scrnintstr.h"
61#include   "mipointrst.h"
62#include   "cursorstr.h"
63#include   "dixstruct.h"
64#include   "inputstr.h"
65#include   "inpututils.h"
66#include   "eventstr.h"
67
68typedef struct {
69    ScreenPtr pScreen;          /* current screen */
70    ScreenPtr pSpriteScreen;    /* screen containing current sprite */
71    CursorPtr pCursor;          /* current cursor */
72    CursorPtr pSpriteCursor;    /* cursor on screen */
73    BoxRec limits;              /* current constraints */
74    Bool confined;              /* pointer can't change screens */
75    int x, y;                   /* hot spot location */
76    int devx, devy;             /* sprite position */
77    Bool generateEvent;         /* generate an event during warping? */
78} miPointerRec, *miPointerPtr;
79
80DevPrivateKeyRec miPointerScreenKeyRec;
81
82#define GetScreenPrivate(s) ((miPointerScreenPtr) \
83    dixLookupPrivate(&(s)->devPrivates, miPointerScreenKey))
84#define SetupScreen(s)	miPointerScreenPtr  pScreenPriv = GetScreenPrivate(s)
85
86DevPrivateKeyRec miPointerPrivKeyRec;
87
88#define MIPOINTER(dev) \
89    (IsFloating(dev) ? \
90        (miPointerPtr)dixLookupPrivate(&(dev)->devPrivates, miPointerPrivKey): \
91        (miPointerPtr)dixLookupPrivate(&(GetMaster(dev, MASTER_POINTER))->devPrivates, miPointerPrivKey))
92
93static Bool miPointerRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen,
94                                   CursorPtr pCursor);
95static Bool miPointerUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen,
96                                     CursorPtr pCursor);
97static Bool miPointerDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen,
98                                   CursorPtr pCursor);
99static void miPointerConstrainCursor(DeviceIntPtr pDev, ScreenPtr pScreen,
100                                     BoxPtr pBox);
101static void miPointerCursorLimits(DeviceIntPtr pDev, ScreenPtr pScreen,
102                                  CursorPtr pCursor, BoxPtr pHotBox,
103                                  BoxPtr pTopLeftBox);
104static Bool miPointerSetCursorPosition(DeviceIntPtr pDev, ScreenPtr pScreen,
105                                       int x, int y, Bool generateEvent);
106static Bool miPointerCloseScreen(ScreenPtr pScreen);
107static void miPointerMove(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y);
108static Bool miPointerDeviceInitialize(DeviceIntPtr pDev, ScreenPtr pScreen);
109static void miPointerDeviceCleanup(DeviceIntPtr pDev, ScreenPtr pScreen);
110static void miPointerMoveNoEvent(DeviceIntPtr pDev, ScreenPtr pScreen, int x,
111                                 int y);
112
113static InternalEvent *mipointermove_events;   /* for WarpPointer MotionNotifies */
114
115Bool
116miPointerInitialize(ScreenPtr pScreen,
117                    miPointerSpriteFuncPtr spriteFuncs,
118                    miPointerScreenFuncPtr screenFuncs, Bool waitForUpdate)
119{
120    miPointerScreenPtr pScreenPriv;
121
122    if (!dixRegisterPrivateKey(&miPointerScreenKeyRec, PRIVATE_SCREEN, 0))
123        return FALSE;
124
125    if (!dixRegisterPrivateKey(&miPointerPrivKeyRec, PRIVATE_DEVICE, 0))
126        return FALSE;
127
128    pScreenPriv = malloc(sizeof(miPointerScreenRec));
129    if (!pScreenPriv)
130        return FALSE;
131    pScreenPriv->spriteFuncs = spriteFuncs;
132    pScreenPriv->screenFuncs = screenFuncs;
133    pScreenPriv->waitForUpdate = waitForUpdate;
134    pScreenPriv->showTransparent = FALSE;
135    pScreenPriv->CloseScreen = pScreen->CloseScreen;
136    pScreen->CloseScreen = miPointerCloseScreen;
137    dixSetPrivate(&pScreen->devPrivates, miPointerScreenKey, pScreenPriv);
138    /*
139     * set up screen cursor method table
140     */
141    pScreen->ConstrainCursor = miPointerConstrainCursor;
142    pScreen->CursorLimits = miPointerCursorLimits;
143    pScreen->DisplayCursor = miPointerDisplayCursor;
144    pScreen->RealizeCursor = miPointerRealizeCursor;
145    pScreen->UnrealizeCursor = miPointerUnrealizeCursor;
146    pScreen->SetCursorPosition = miPointerSetCursorPosition;
147    pScreen->RecolorCursor = miRecolorCursor;
148    pScreen->DeviceCursorInitialize = miPointerDeviceInitialize;
149    pScreen->DeviceCursorCleanup = miPointerDeviceCleanup;
150
151    mipointermove_events = NULL;
152    return TRUE;
153}
154
155/**
156 * Destroy screen-specific information.
157 *
158 * @param index Screen index of the screen in screenInfo.screens[]
159 * @param pScreen The actual screen pointer
160 */
161static Bool
162miPointerCloseScreen(ScreenPtr pScreen)
163{
164    SetupScreen(pScreen);
165
166    pScreen->CloseScreen = pScreenPriv->CloseScreen;
167    free((void *) pScreenPriv);
168    FreeEventList(mipointermove_events, GetMaximumEventsNum());
169    mipointermove_events = NULL;
170    return (*pScreen->CloseScreen) (pScreen);
171}
172
173/*
174 * DIX/DDX interface routines
175 */
176
177static Bool
178miPointerRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
179{
180    SetupScreen(pScreen);
181    return (*pScreenPriv->spriteFuncs->RealizeCursor) (pDev, pScreen, pCursor);
182}
183
184static Bool
185miPointerUnrealizeCursor(DeviceIntPtr pDev,
186                         ScreenPtr pScreen, CursorPtr pCursor)
187{
188    SetupScreen(pScreen);
189    return (*pScreenPriv->spriteFuncs->UnrealizeCursor) (pDev, pScreen,
190                                                         pCursor);
191}
192
193static Bool
194miPointerDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
195{
196    miPointerPtr pPointer;
197
198    /* return for keyboards */
199    if (!IsPointerDevice(pDev))
200        return FALSE;
201
202    pPointer = MIPOINTER(pDev);
203
204    pPointer->pCursor = pCursor;
205    pPointer->pScreen = pScreen;
206    miPointerUpdateSprite(pDev);
207    return TRUE;
208}
209
210/**
211 * Set up the constraints for the given device. This function does not
212 * actually constrain the cursor but merely copies the given box to the
213 * internal constraint storage.
214 *
215 * @param pDev The device to constrain to the box
216 * @param pBox The rectangle to constrain the cursor to
217 * @param pScreen Used for copying screen confinement
218 */
219static void
220miPointerConstrainCursor(DeviceIntPtr pDev, ScreenPtr pScreen, BoxPtr pBox)
221{
222    miPointerPtr pPointer;
223
224    pPointer = MIPOINTER(pDev);
225
226    pPointer->limits = *pBox;
227    pPointer->confined = PointerConfinedToScreen(pDev);
228}
229
230/**
231 * Should calculate the box for the given cursor, based on screen and the
232 * confinement given. But we assume that whatever box is passed in is valid
233 * anyway.
234 *
235 * @param pDev The device to calculate the cursor limits for
236 * @param pScreen The screen the confinement happens on
237 * @param pCursor The screen the confinement happens on
238 * @param pHotBox The confinement box for the cursor
239 * @param[out] pTopLeftBox The new confinement box, always *pHotBox.
240 */
241static void
242miPointerCursorLimits(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor,
243                      BoxPtr pHotBox, BoxPtr pTopLeftBox)
244{
245    *pTopLeftBox = *pHotBox;
246}
247
248/**
249 * Set the device's cursor position to the x/y position on the given screen.
250 * Generates and event if required.
251 *
252 * This function is called from:
253 *    - sprite init code to place onto initial position
254 *    - the various WarpPointer implementations (core, XI, Xinerama, dmx,…)
255 *    - during the cursor update path in CheckMotion
256 *    - in the Xinerama part of NewCurrentScreen
257 *    - when a RandR/RandR1.2 mode was applied (it may have moved the pointer, so
258 *      it's set back to the original pos)
259 *
260 * @param pDev The device to move
261 * @param pScreen The screen the device is on
262 * @param x The x coordinate in per-screen coordinates
263 * @param y The y coordinate in per-screen coordinates
264 * @param generateEvent True if the pointer movement should generate an
265 * event.
266 *
267 * @return TRUE in all cases
268 */
269static Bool
270miPointerSetCursorPosition(DeviceIntPtr pDev, ScreenPtr pScreen,
271                           int x, int y, Bool generateEvent)
272{
273    SetupScreen(pScreen);
274    miPointerPtr pPointer = MIPOINTER(pDev);
275
276    pPointer->generateEvent = generateEvent;
277
278    if (pScreen->ConstrainCursorHarder)
279        pScreen->ConstrainCursorHarder(pDev, pScreen, Absolute, &x, &y);
280
281    /* device dependent - must pend signal and call miPointerWarpCursor */
282    (*pScreenPriv->screenFuncs->WarpCursor) (pDev, pScreen, x, y);
283    if (!generateEvent)
284        miPointerUpdateSprite(pDev);
285    return TRUE;
286}
287
288void
289miRecolorCursor(DeviceIntPtr pDev, ScreenPtr pScr,
290                CursorPtr pCurs, Bool displayed)
291{
292    /*
293     * This is guaranteed to correct any color-dependent state which may have
294     * been bound up in private state created by RealizeCursor
295     */
296    pScr->UnrealizeCursor(pDev, pScr, pCurs);
297    pScr->RealizeCursor(pDev, pScr, pCurs);
298    if (displayed)
299        pScr->DisplayCursor(pDev, pScr, pCurs);
300}
301
302/**
303 * Set up sprite information for the device.
304 * This function will be called once for each device after it is initialized
305 * in the DIX.
306 *
307 * @param pDev The newly created device
308 * @param pScreen The initial sprite scree.
309 */
310static Bool
311miPointerDeviceInitialize(DeviceIntPtr pDev, ScreenPtr pScreen)
312{
313    miPointerPtr pPointer;
314
315    SetupScreen(pScreen);
316
317    pPointer = malloc(sizeof(miPointerRec));
318    if (!pPointer)
319        return FALSE;
320
321    pPointer->pScreen = NULL;
322    pPointer->pSpriteScreen = NULL;
323    pPointer->pCursor = NULL;
324    pPointer->pSpriteCursor = NULL;
325    pPointer->limits.x1 = 0;
326    pPointer->limits.x2 = 32767;
327    pPointer->limits.y1 = 0;
328    pPointer->limits.y2 = 32767;
329    pPointer->confined = FALSE;
330    pPointer->x = 0;
331    pPointer->y = 0;
332    pPointer->generateEvent = FALSE;
333
334    if (!((*pScreenPriv->spriteFuncs->DeviceCursorInitialize) (pDev, pScreen))) {
335        free(pPointer);
336        return FALSE;
337    }
338
339    dixSetPrivate(&pDev->devPrivates, miPointerPrivKey, pPointer);
340    return TRUE;
341}
342
343/**
344 * Clean up after device.
345 * This function will be called once before the device is freed in the DIX
346 *
347 * @param pDev The device to be removed from the server
348 * @param pScreen Current screen of the device
349 */
350static void
351miPointerDeviceCleanup(DeviceIntPtr pDev, ScreenPtr pScreen)
352{
353    SetupScreen(pScreen);
354
355    if (!IsMaster(pDev) && !IsFloating(pDev))
356        return;
357
358    (*pScreenPriv->spriteFuncs->DeviceCursorCleanup) (pDev, pScreen);
359    free(dixLookupPrivate(&pDev->devPrivates, miPointerPrivKey));
360    dixSetPrivate(&pDev->devPrivates, miPointerPrivKey, NULL);
361}
362
363/**
364 * Warp the pointer to the given position on the given screen. May generate
365 * an event, depending on whether we're coming from miPointerSetPosition.
366 *
367 * Once signals are ignored, the WarpCursor function can call this
368 *
369 * @param pDev The device to warp
370 * @param pScreen Screen to warp on
371 * @param x The x coordinate in per-screen coordinates
372 * @param y The y coordinate in per-screen coordinates
373 */
374
375void
376miPointerWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
377{
378    miPointerPtr pPointer;
379    BOOL changedScreen = FALSE;
380
381    pPointer = MIPOINTER(pDev);
382
383    if (pPointer->pScreen != pScreen) {
384        mieqSwitchScreen(pDev, pScreen, TRUE);
385        changedScreen = TRUE;
386    }
387
388    if (pPointer->generateEvent)
389        miPointerMove(pDev, pScreen, x, y);
390    else
391        miPointerMoveNoEvent(pDev, pScreen, x, y);
392
393    /* Don't call USFS if we use Xinerama, otherwise the root window is
394     * updated to the second screen, and we never receive any events.
395     * (FDO bug #18668) */
396    if (changedScreen
397#ifdef PANORAMIX
398        && noPanoramiXExtension
399#endif
400        ) {
401            DeviceIntPtr master = GetMaster(pDev, MASTER_POINTER);
402            /* Hack for CVE-2023-5380: if we're moving
403             * screens PointerWindows[] keeps referring to the
404             * old window. If that gets destroyed we have a UAF
405             * bug later. Only happens when jumping from a window
406             * to the root window on the other screen.
407             * Enter/Leave events are incorrect for that case but
408             * too niche to fix.
409             */
410            LeaveWindow(pDev);
411            if (master)
412                LeaveWindow(master);
413            UpdateSpriteForScreen(pDev, pScreen);
414    }
415}
416
417/**
418 * Synchronize the sprite with the cursor.
419 *
420 * @param pDev The device to sync
421 */
422void
423miPointerUpdateSprite(DeviceIntPtr pDev)
424{
425    ScreenPtr pScreen;
426    miPointerScreenPtr pScreenPriv;
427    CursorPtr pCursor;
428    int x, y, devx, devy;
429    miPointerPtr pPointer;
430
431    if (!pDev || !pDev->coreEvents)
432        return;
433
434    pPointer = MIPOINTER(pDev);
435
436    if (!pPointer)
437        return;
438
439    pScreen = pPointer->pScreen;
440    if (!pScreen)
441        return;
442
443    x = pPointer->x;
444    y = pPointer->y;
445    devx = pPointer->devx;
446    devy = pPointer->devy;
447
448    pScreenPriv = GetScreenPrivate(pScreen);
449    /*
450     * if the cursor has switched screens, disable the sprite
451     * on the old screen
452     */
453    if (pScreen != pPointer->pSpriteScreen) {
454        if (pPointer->pSpriteScreen) {
455            miPointerScreenPtr pOldPriv;
456
457            pOldPriv = GetScreenPrivate(pPointer->pSpriteScreen);
458            if (pPointer->pCursor) {
459                (*pOldPriv->spriteFuncs->SetCursor)
460                    (pDev, pPointer->pSpriteScreen, NullCursor, 0, 0);
461            }
462            (*pOldPriv->screenFuncs->CrossScreen) (pPointer->pSpriteScreen,
463                                                   FALSE);
464        }
465        (*pScreenPriv->screenFuncs->CrossScreen) (pScreen, TRUE);
466        (*pScreenPriv->spriteFuncs->SetCursor)
467            (pDev, pScreen, pPointer->pCursor, x, y);
468        pPointer->devx = x;
469        pPointer->devy = y;
470        pPointer->pSpriteCursor = pPointer->pCursor;
471        pPointer->pSpriteScreen = pScreen;
472    }
473    /*
474     * if the cursor has changed, display the new one
475     */
476    else if (pPointer->pCursor != pPointer->pSpriteCursor) {
477        pCursor = pPointer->pCursor;
478        if (!pCursor ||
479            (pCursor->bits->emptyMask && !pScreenPriv->showTransparent))
480            pCursor = NullCursor;
481        (*pScreenPriv->spriteFuncs->SetCursor) (pDev, pScreen, pCursor, x, y);
482
483        pPointer->devx = x;
484        pPointer->devy = y;
485        pPointer->pSpriteCursor = pPointer->pCursor;
486    }
487    else if (x != devx || y != devy) {
488        pPointer->devx = x;
489        pPointer->devy = y;
490        if (pPointer->pCursor && !pPointer->pCursor->bits->emptyMask)
491            (*pScreenPriv->spriteFuncs->MoveCursor) (pDev, pScreen, x, y);
492    }
493}
494
495/**
496 * Invalidate the current sprite and force it to be reloaded on next cursor setting
497 * operation
498 *
499 * @param pDev The device to invalidate the sprite fore
500 */
501void
502miPointerInvalidateSprite(DeviceIntPtr pDev)
503{
504    miPointerPtr pPointer;
505
506    pPointer = MIPOINTER(pDev);
507    pPointer->pSpriteCursor = (CursorPtr) 1;
508}
509
510/**
511 * Set the device to the coordinates on the given screen.
512 *
513 * @param pDev The device to move
514 * @param screen_no Index of the screen to move to
515 * @param x The x coordinate in per-screen coordinates
516 * @param y The y coordinate in per-screen coordinates
517 */
518void
519miPointerSetScreen(DeviceIntPtr pDev, int screen_no, int x, int y)
520{
521    ScreenPtr pScreen;
522    miPointerPtr pPointer;
523
524    pPointer = MIPOINTER(pDev);
525
526    pScreen = screenInfo.screens[screen_no];
527    mieqSwitchScreen(pDev, pScreen, FALSE);
528    NewCurrentScreen(pDev, pScreen, x, y);
529
530    pPointer->limits.x2 = pScreen->width;
531    pPointer->limits.y2 = pScreen->height;
532}
533
534/**
535 * @return The current screen of the given device or NULL.
536 */
537ScreenPtr
538miPointerGetScreen(DeviceIntPtr pDev)
539{
540    miPointerPtr pPointer = MIPOINTER(pDev);
541
542    return (pPointer) ? pPointer->pScreen : NULL;
543}
544
545/* Controls whether the cursor image should be updated immediately when
546   moved (FALSE) or if something else will be responsible for updating
547   it later (TRUE).  Returns current setting.
548   Caller is responsible for calling OsBlockSignal first.
549*/
550Bool
551miPointerSetWaitForUpdate(ScreenPtr pScreen, Bool wait)
552{
553    SetupScreen(pScreen);
554    Bool prevWait = pScreenPriv->waitForUpdate;
555
556    pScreenPriv->waitForUpdate = wait;
557    return prevWait;
558}
559
560/* Move the pointer on the current screen,  and update the sprite. */
561static void
562miPointerMoveNoEvent(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
563{
564    miPointerPtr pPointer;
565
566    SetupScreen(pScreen);
567
568    pPointer = MIPOINTER(pDev);
569
570    /* Hack: We mustn't call into ->MoveCursor for anything but the
571     * VCP, as this may cause a non-HW rendered cursor to be rendered while
572     * not holding the input lock. This would race with building the command
573     * buffer for other rendering.
574     */
575    if (GetMaster(pDev, MASTER_POINTER) == inputInfo.pointer
576        &&!pScreenPriv->waitForUpdate && pScreen == pPointer->pSpriteScreen) {
577        pPointer->devx = x;
578        pPointer->devy = y;
579        if (pPointer->pCursor && !pPointer->pCursor->bits->emptyMask)
580            (*pScreenPriv->spriteFuncs->MoveCursor) (pDev, pScreen, x, y);
581    }
582
583    pPointer->x = x;
584    pPointer->y = y;
585    pPointer->pScreen = pScreen;
586}
587
588/**
589 * Set the devices' cursor position to the given x/y position.
590 *
591 * This function is called during the pointer update path in
592 * GetPointerEvents and friends (and the same in the xwin DDX).
593 *
594 * The coordinates provided are always absolute. The parameter mode whether
595 * it was relative or absolute movement that landed us at those coordinates.
596 *
597 * If the cursor was constrained by a barrier, ET_Barrier* events may be
598 * generated and appended to the InternalEvent list provided.
599 *
600 * @param pDev The device to move
601 * @param mode Movement mode (Absolute or Relative)
602 * @param[in,out] screenx The x coordinate in desktop coordinates
603 * @param[in,out] screeny The y coordinate in desktop coordinates
604 * @param[in,out] nevents The number of events in events (before/after)
605 * @param[in,out] events The list of events before/after being constrained
606 */
607ScreenPtr
608miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx,
609                     double *screeny,
610                     int *nevents, InternalEvent* events)
611{
612    miPointerScreenPtr pScreenPriv;
613    ScreenPtr pScreen;
614    ScreenPtr newScreen;
615    int x, y;
616    Bool switch_screen = FALSE;
617    Bool should_constrain_barriers = FALSE;
618    int i;
619
620    miPointerPtr pPointer;
621
622    pPointer = MIPOINTER(pDev);
623    pScreen = pPointer->pScreen;
624
625    x = trunc(*screenx);
626    y = trunc(*screeny);
627
628    switch_screen = !point_on_screen(pScreen, x, y);
629
630    /* Switch to per-screen coordinates for CursorOffScreen and
631     * Pointer->limits */
632    x -= pScreen->x;
633    y -= pScreen->y;
634
635    should_constrain_barriers = (mode == Relative);
636
637    if (should_constrain_barriers) {
638        /* coordinates after clamped to a barrier */
639        int constrained_x, constrained_y;
640        int current_x, current_y; /* current position in per-screen coord */
641
642        current_x = MIPOINTER(pDev)->x - pScreen->x;
643        current_y = MIPOINTER(pDev)->y - pScreen->y;
644
645        input_constrain_cursor(pDev, pScreen,
646                               current_x, current_y, x, y,
647                               &constrained_x, &constrained_y,
648                               nevents, events);
649
650        x = constrained_x;
651        y = constrained_y;
652    }
653
654    if (switch_screen) {
655        pScreenPriv = GetScreenPrivate(pScreen);
656        if (!pPointer->confined) {
657            newScreen = pScreen;
658            (*pScreenPriv->screenFuncs->CursorOffScreen) (&newScreen, &x, &y);
659            if (newScreen != pScreen) {
660                pScreen = newScreen;
661                mieqSwitchScreen(pDev, pScreen, FALSE);
662                /* Smash the confine to the new screen */
663                pPointer->limits.x2 = pScreen->width;
664                pPointer->limits.y2 = pScreen->height;
665            }
666        }
667    }
668    /* Constrain the sprite to the current limits. */
669    if (x < pPointer->limits.x1)
670        x = pPointer->limits.x1;
671    if (x >= pPointer->limits.x2)
672        x = pPointer->limits.x2 - 1;
673    if (y < pPointer->limits.y1)
674        y = pPointer->limits.y1;
675    if (y >= pPointer->limits.y2)
676        y = pPointer->limits.y2 - 1;
677
678    if (pScreen->ConstrainCursorHarder)
679        pScreen->ConstrainCursorHarder(pDev, pScreen, mode, &x, &y);
680
681    if (pPointer->x != x || pPointer->y != y || pPointer->pScreen != pScreen)
682        miPointerMoveNoEvent(pDev, pScreen, x, y);
683
684    /* check if we generated any barrier events and if so, update root x/y
685     * to the fully constrained coords */
686    if (should_constrain_barriers) {
687        for (i = 0; i < *nevents; i++) {
688            if (events[i].any.type == ET_BarrierHit ||
689                events[i].any.type == ET_BarrierLeave) {
690                events[i].barrier_event.root_x = x;
691                events[i].barrier_event.root_y = y;
692            }
693        }
694    }
695
696    /* Convert to desktop coordinates again */
697    x += pScreen->x;
698    y += pScreen->y;
699
700    /* In the event we actually change screen or we get confined, we just
701     * drop the float component on the floor
702     * FIXME: only drop remainder for ConstrainCursorHarder, not for screen
703     * crossings */
704    if (x != trunc(*screenx))
705        *screenx = x;
706    if (y != trunc(*screeny))
707        *screeny = y;
708
709    return pScreen;
710}
711
712/**
713 * Get the current position of the device in desktop coordinates.
714 *
715 * @param x Return value for the current x coordinate in desktop coordinates.
716 * @param y Return value for the current y coordinate in desktop coordinates.
717 */
718void
719miPointerGetPosition(DeviceIntPtr pDev, int *x, int *y)
720{
721    *x = MIPOINTER(pDev)->x;
722    *y = MIPOINTER(pDev)->y;
723}
724
725/**
726 * Move the device's pointer to the x/y coordinates on the given screen.
727 * This function generates and enqueues pointer events.
728 *
729 * @param pDev The device to move
730 * @param pScreen The screen the device is on
731 * @param x The x coordinate in per-screen coordinates
732 * @param y The y coordinate in per-screen coordinates
733 */
734void
735miPointerMove(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
736{
737    int i, nevents;
738    int valuators[2];
739    ValuatorMask mask;
740
741    miPointerMoveNoEvent(pDev, pScreen, x, y);
742
743    /* generate motion notify */
744    valuators[0] = x;
745    valuators[1] = y;
746
747    if (!mipointermove_events) {
748        mipointermove_events = InitEventList(GetMaximumEventsNum());
749
750        if (!mipointermove_events) {
751            FatalError("Could not allocate event store.\n");
752            return;
753        }
754    }
755
756    valuator_mask_set_range(&mask, 0, 2, valuators);
757    nevents = GetPointerEvents(mipointermove_events, pDev, MotionNotify, 0,
758                               POINTER_SCREEN | POINTER_ABSOLUTE |
759                               POINTER_NORAW, &mask);
760
761    input_lock();
762    for (i = 0; i < nevents; i++)
763        mieqEnqueue(pDev, &mipointermove_events[i]);
764    input_unlock();
765}
766