Edit

IABSD.fr/xenocara/xserver/xfixes/cursor.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2012-06-10 13:21:05
    Hash : e60da745
    Message : Update to xserver 1.12.2. tested by naddy@, krw@, mpi@.

  • xserver/xfixes/cursor.c
  • /*
     * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
     * Copyright 2010 Red Hat, Inc.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice (including the next
     * paragraph) shall be included in all copies or substantial portions of the
     * Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     * DEALINGS IN THE SOFTWARE.
     *
     * Copyright © 2002 Keith Packard
     *
     * Permission to use, copy, modify, distribute, and sell this software and its
     * documentation for any purpose is hereby granted without fee, provided that
     * the above copyright notice appear in all copies and that both that
     * copyright notice and this permission notice appear in supporting
     * documentation, and that the name of Keith Packard not be used in
     * advertising or publicity pertaining to distribution of the software without
     * specific, written prior permission.  Keith Packard makes no
     * representations about the suitability of this software for any purpose.  It
     * is provided "as is" without express or implied warranty.
     *
     * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     * PERFORMANCE OF THIS SOFTWARE.
     */
    
    #ifdef HAVE_DIX_CONFIG_H
    #include <dix-config.h>
    #endif
    
    #include "xfixesint.h"
    #include "scrnintstr.h"
    #include "cursorstr.h"
    #include "dixevents.h"
    #include "servermd.h"
    #include "mipointer.h"
    #include "inputstr.h"
    #include "windowstr.h"
    #include "xace.h"
    #include "list.h"
    
    static RESTYPE CursorClientType;
    static RESTYPE CursorHideCountType;
    static RESTYPE CursorWindowType;
    RESTYPE PointerBarrierType;
    static CursorPtr CursorCurrent[MAXDEVICES];
    
    static DevPrivateKeyRec CursorScreenPrivateKeyRec;
    
    #define CursorScreenPrivateKey (&CursorScreenPrivateKeyRec)
    
    static void deleteCursorHideCountsForScreen(ScreenPtr pScreen);
    
    #define VERIFY_CURSOR(pCursor, cursor, client, access)			\
        do {								\
    	int err;							\
    	err = dixLookupResourceByType((pointer *) &pCursor, cursor,	\
    				      RT_CURSOR, client, access);	\
    	if (err != Success) {						\
    	    client->errorValue = cursor;				\
    	    return err;							\
    	}								\
        } while (0)
    
    /*
     * There is a global list of windows selecting for cursor events
     */
    
    typedef struct _CursorEvent *CursorEventPtr;
    
    typedef struct _CursorEvent {
        CursorEventPtr next;
        CARD32 eventMask;
        ClientPtr pClient;
        WindowPtr pWindow;
        XID clientResource;
    } CursorEventRec;
    
    static CursorEventPtr cursorEvents;
    
    /*
     * Each screen has a list of clients which have requested
     * that the cursor be hid, and the number of times each
     * client has requested.
    */
    
    typedef struct _CursorHideCountRec *CursorHideCountPtr;
    
    typedef struct _CursorHideCountRec {
        CursorHideCountPtr pNext;
        ClientPtr pClient;
        ScreenPtr pScreen;
        int hideCount;
        XID resource;
    } CursorHideCountRec;
    
    typedef struct PointerBarrierClient *PointerBarrierClientPtr;
    
    struct PointerBarrierClient {
        ScreenPtr screen;
        struct PointerBarrier barrier;
        struct xorg_list entry;
    };
    
    /*
     * Wrap DisplayCursor to catch cursor change events
     */
    
    typedef struct _CursorScreen {
        DisplayCursorProcPtr DisplayCursor;
        CloseScreenProcPtr CloseScreen;
        ConstrainCursorHarderProcPtr ConstrainCursorHarder;
        CursorHideCountPtr pCursorHideCounts;
        struct xorg_list barriers;
    } CursorScreenRec, *CursorScreenPtr;
    
    #define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey))
    #define GetCursorScreenIfSet(s) GetCursorScreen(s)
    #define SetCursorScreen(s,p) dixSetPrivate(&(s)->devPrivates, CursorScreenPrivateKey, p)
    #define Wrap(as,s,elt,func)	(((as)->elt = (s)->elt), (s)->elt = func)
    #define Unwrap(as,s,elt,backup)	(((backup) = (s)->elt), (s)->elt = (as)->elt)
    
    /* The cursor doesn't show up until the first XDefineCursor() */
    static Bool CursorVisible = FALSE;
    
    Bool EnableCursor = TRUE;
    
    static Bool
    CursorDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        Bool ret;
        DisplayCursorProcPtr backupProc;
    
        Unwrap(cs, pScreen, DisplayCursor, backupProc);
    
        /*
         * Have to check ConnectionInfo to distinguish client requests from
         * initial root window setup.  Not a great way to do it, I admit.
         */
        if (ConnectionInfo)
            CursorVisible = EnableCursor;
    
        if (cs->pCursorHideCounts != NULL || !CursorVisible) {
            ret = (*pScreen->DisplayCursor) (pDev, pScreen, NullCursor);
        }
        else {
            ret = (*pScreen->DisplayCursor) (pDev, pScreen, pCursor);
        }
    
        if (pCursor != CursorCurrent[pDev->id]) {
            CursorEventPtr e;
    
            CursorCurrent[pDev->id] = pCursor;
            for (e = cursorEvents; e; e = e->next) {
                if ((e->eventMask & XFixesDisplayCursorNotifyMask)) {
                    xXFixesCursorNotifyEvent ev;
    
                    ev.type = XFixesEventBase + XFixesCursorNotify;
                    ev.subtype = XFixesDisplayCursorNotify;
                    ev.window = e->pWindow->drawable.id;
                    ev.cursorSerial = pCursor ? pCursor->serialNumber : 0;
                    ev.timestamp = currentTime.milliseconds;
                    ev.name = pCursor ? pCursor->name : None;
                    WriteEventsToClient(e->pClient, 1, (xEvent *) &ev);
                }
            }
        }
        Wrap(cs, pScreen, DisplayCursor, backupProc);
    
        return ret;
    }
    
    static Bool
    CursorCloseScreen(int index, ScreenPtr pScreen)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        Bool ret;
        _X_UNUSED CloseScreenProcPtr close_proc;
        _X_UNUSED DisplayCursorProcPtr display_proc;
        ConstrainCursorHarderProcPtr constrain_proc;
    
        Unwrap(cs, pScreen, CloseScreen, close_proc);
        Unwrap(cs, pScreen, DisplayCursor, display_proc);
        Unwrap(cs, pScreen, ConstrainCursorHarder, constrain_proc);
        deleteCursorHideCountsForScreen(pScreen);
        ret = (*pScreen->CloseScreen) (index, pScreen);
        free(cs);
        return ret;
    }
    
    #define CursorAllEvents (XFixesDisplayCursorNotifyMask)
    
    static int
    XFixesSelectCursorInput(ClientPtr pClient, WindowPtr pWindow, CARD32 eventMask)
    {
        CursorEventPtr *prev, e;
        pointer val;
        int rc;
    
        for (prev = &cursorEvents; (e = *prev); prev = &e->next) {
            if (e->pClient == pClient && e->pWindow == pWindow) {
                break;
            }
        }
        if (!eventMask) {
            if (e) {
                FreeResource(e->clientResource, 0);
            }
            return Success;
        }
        if (!e) {
            e = (CursorEventPtr) malloc(sizeof(CursorEventRec));
            if (!e)
                return BadAlloc;
    
            e->next = 0;
            e->pClient = pClient;
            e->pWindow = pWindow;
            e->clientResource = FakeClientID(pClient->index);
    
            /*
             * Add a resource hanging from the window to
             * catch window destroy
             */
            rc = dixLookupResourceByType(&val, pWindow->drawable.id,
                                         CursorWindowType, serverClient,
                                         DixGetAttrAccess);
            if (rc != Success)
                if (!AddResource(pWindow->drawable.id, CursorWindowType,
                                 (pointer) pWindow)) {
                    free(e);
                    return BadAlloc;
                }
    
            if (!AddResource(e->clientResource, CursorClientType, (pointer) e))
                return BadAlloc;
    
            *prev = e;
        }
        e->eventMask = eventMask;
        return Success;
    }
    
    int
    ProcXFixesSelectCursorInput(ClientPtr client)
    {
        REQUEST(xXFixesSelectCursorInputReq);
        WindowPtr pWin;
        int rc;
    
        REQUEST_SIZE_MATCH(xXFixesSelectCursorInputReq);
        rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
        if (rc != Success)
            return rc;
        if (stuff->eventMask & ~CursorAllEvents) {
            client->errorValue = stuff->eventMask;
            return BadValue;
        }
        return XFixesSelectCursorInput(client, pWin, stuff->eventMask);
    }
    
    static int
    GetBit(unsigned char *line, int x)
    {
        unsigned char mask;
    
        if (screenInfo.bitmapBitOrder == LSBFirst)
            mask = (1 << (x & 7));
        else
            mask = (0x80 >> (x & 7));
        /* XXX assumes byte order is host byte order */
        line += (x >> 3);
        if (*line & mask)
            return 1;
        return 0;
    }
    
    int
    SProcXFixesSelectCursorInput(ClientPtr client)
    {
        REQUEST(xXFixesSelectCursorInputReq);
    
        swaps(&stuff->length);
        swapl(&stuff->window);
        swapl(&stuff->eventMask);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    void
    SXFixesCursorNotifyEvent(xXFixesCursorNotifyEvent * from,
                             xXFixesCursorNotifyEvent * to)
    {
        to->type = from->type;
        cpswaps(from->sequenceNumber, to->sequenceNumber);
        cpswapl(from->window, to->window);
        cpswapl(from->cursorSerial, to->cursorSerial);
        cpswapl(from->timestamp, to->timestamp);
        cpswapl(from->name, to->name);
    }
    
    static void
    CopyCursorToImage(CursorPtr pCursor, CARD32 *image)
    {
        int width = pCursor->bits->width;
        int height = pCursor->bits->height;
        int npixels = width * height;
    
    #ifdef ARGB_CURSOR
        if (pCursor->bits->argb)
            memcpy(image, pCursor->bits->argb, npixels * sizeof(CARD32));
        else
    #endif
        {
            unsigned char *srcLine = pCursor->bits->source;
            unsigned char *mskLine = pCursor->bits->mask;
            int stride = BitmapBytePad(width);
            int x, y;
            CARD32 fg, bg;
    
            fg = (0xff000000 |
                  ((pCursor->foreRed & 0xff00) << 8) |
                  (pCursor->foreGreen & 0xff00) | (pCursor->foreBlue >> 8));
            bg = (0xff000000 |
                  ((pCursor->backRed & 0xff00) << 8) |
                  (pCursor->backGreen & 0xff00) | (pCursor->backBlue >> 8));
            for (y = 0; y < height; y++) {
                for (x = 0; x < width; x++) {
                    if (GetBit(mskLine, x)) {
                        if (GetBit(srcLine, x))
                            *image++ = fg;
                        else
                            *image++ = bg;
                    }
                    else
                        *image++ = 0;
                }
                srcLine += stride;
                mskLine += stride;
            }
        }
    }
    
    int
    ProcXFixesGetCursorImage(ClientPtr client)
    {
    /*    REQUEST(xXFixesGetCursorImageReq); */
        xXFixesGetCursorImageReply *rep;
        CursorPtr pCursor;
        CARD32 *image;
        int npixels, width, height, rc, x, y;
    
        REQUEST_SIZE_MATCH(xXFixesGetCursorImageReq);
        pCursor = CursorCurrent[PickPointer(client)->id];
        if (!pCursor)
            return BadCursor;
        rc = XaceHook(XACE_RESOURCE_ACCESS, client, pCursor->id, RT_CURSOR,
                      pCursor, RT_NONE, NULL, DixReadAccess);
        if (rc != Success)
            return rc;
        GetSpritePosition(PickPointer(client), &x, &y);
        width = pCursor->bits->width;
        height = pCursor->bits->height;
        npixels = width * height;
        rep = malloc(sizeof(xXFixesGetCursorImageReply) + npixels * sizeof(CARD32));
        if (!rep)
            return BadAlloc;
    
        rep->type = X_Reply;
        rep->sequenceNumber = client->sequence;
        rep->length = npixels;
        rep->width = width;
        rep->height = height;
        rep->x = x;
        rep->y = y;
        rep->xhot = pCursor->bits->xhot;
        rep->yhot = pCursor->bits->yhot;
        rep->cursorSerial = pCursor->serialNumber;
    
        image = (CARD32 *) (rep + 1);
        CopyCursorToImage(pCursor, image);
        if (client->swapped) {
            swaps(&rep->sequenceNumber);
            swapl(&rep->length);
            swaps(&rep->x);
            swaps(&rep->y);
            swaps(&rep->width);
            swaps(&rep->height);
            swaps(&rep->xhot);
            swaps(&rep->yhot);
            swapl(&rep->cursorSerial);
            SwapLongs(image, npixels);
        }
        WriteToClient(client, sizeof(xXFixesGetCursorImageReply) +
                      (npixels << 2), (char *) rep);
        free(rep);
        return Success;
    }
    
    int
    SProcXFixesGetCursorImage(ClientPtr client)
    {
        REQUEST(xXFixesGetCursorImageReq);
        swaps(&stuff->length);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    int
    ProcXFixesSetCursorName(ClientPtr client)
    {
        CursorPtr pCursor;
        char *tchar;
    
        REQUEST(xXFixesSetCursorNameReq);
        Atom atom;
    
        REQUEST_AT_LEAST_SIZE(xXFixesSetCursorNameReq);
        VERIFY_CURSOR(pCursor, stuff->cursor, client, DixSetAttrAccess);
        tchar = (char *) &stuff[1];
        atom = MakeAtom(tchar, stuff->nbytes, TRUE);
        if (atom == BAD_RESOURCE)
            return BadAlloc;
    
        pCursor->name = atom;
        return Success;
    }
    
    int
    SProcXFixesSetCursorName(ClientPtr client)
    {
        REQUEST(xXFixesSetCursorNameReq);
    
        swaps(&stuff->length);
        REQUEST_AT_LEAST_SIZE(xXFixesSetCursorNameReq);
        swapl(&stuff->cursor);
        swaps(&stuff->nbytes);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    int
    ProcXFixesGetCursorName(ClientPtr client)
    {
        CursorPtr pCursor;
        xXFixesGetCursorNameReply reply;
    
        REQUEST(xXFixesGetCursorNameReq);
        const char *str;
        int len;
    
        REQUEST_SIZE_MATCH(xXFixesGetCursorNameReq);
        VERIFY_CURSOR(pCursor, stuff->cursor, client, DixGetAttrAccess);
        if (pCursor->name)
            str = NameForAtom(pCursor->name);
        else
            str = "";
        len = strlen(str);
    
        reply.type = X_Reply;
        reply.length = bytes_to_int32(len);
        reply.sequenceNumber = client->sequence;
        reply.atom = pCursor->name;
        reply.nbytes = len;
        if (client->swapped) {
            swaps(&reply.sequenceNumber);
            swapl(&reply.length);
            swapl(&reply.atom);
            swaps(&reply.nbytes);
        }
        WriteReplyToClient(client, sizeof(xXFixesGetCursorNameReply), &reply);
        WriteToClient(client, len, str);
    
        return Success;
    }
    
    int
    SProcXFixesGetCursorName(ClientPtr client)
    {
        REQUEST(xXFixesGetCursorNameReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesGetCursorNameReq);
        swapl(&stuff->cursor);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    int
    ProcXFixesGetCursorImageAndName(ClientPtr client)
    {
    /*    REQUEST(xXFixesGetCursorImageAndNameReq); */
        xXFixesGetCursorImageAndNameReply *rep;
        CursorPtr pCursor;
        CARD32 *image;
        int npixels;
        const char *name;
        int nbytes, nbytesRound;
        int width, height;
        int rc, x, y;
    
        REQUEST_SIZE_MATCH(xXFixesGetCursorImageAndNameReq);
        pCursor = CursorCurrent[PickPointer(client)->id];
        if (!pCursor)
            return BadCursor;
        rc = XaceHook(XACE_RESOURCE_ACCESS, client, pCursor->id, RT_CURSOR,
                      pCursor, RT_NONE, NULL, DixReadAccess | DixGetAttrAccess);
        if (rc != Success)
            return rc;
        GetSpritePosition(PickPointer(client), &x, &y);
        width = pCursor->bits->width;
        height = pCursor->bits->height;
        npixels = width * height;
        name = pCursor->name ? NameForAtom(pCursor->name) : "";
        nbytes = strlen(name);
        nbytesRound = pad_to_int32(nbytes);
        rep = malloc(sizeof(xXFixesGetCursorImageAndNameReply) +
                     npixels * sizeof(CARD32) + nbytesRound);
        if (!rep)
            return BadAlloc;
    
        rep->type = X_Reply;
        rep->sequenceNumber = client->sequence;
        rep->length = npixels + bytes_to_int32(nbytesRound);
        rep->width = width;
        rep->height = height;
        rep->x = x;
        rep->y = y;
        rep->xhot = pCursor->bits->xhot;
        rep->yhot = pCursor->bits->yhot;
        rep->cursorSerial = pCursor->serialNumber;
        rep->cursorName = pCursor->name;
        rep->nbytes = nbytes;
    
        image = (CARD32 *) (rep + 1);
        CopyCursorToImage(pCursor, image);
        memcpy((image + npixels), name, nbytes);
        if (client->swapped) {
            swaps(&rep->sequenceNumber);
            swapl(&rep->length);
            swaps(&rep->x);
            swaps(&rep->y);
            swaps(&rep->width);
            swaps(&rep->height);
            swaps(&rep->xhot);
            swaps(&rep->yhot);
            swapl(&rep->cursorSerial);
            swapl(&rep->cursorName);
            swaps(&rep->nbytes);
            SwapLongs(image, npixels);
        }
        WriteToClient(client, sizeof(xXFixesGetCursorImageAndNameReply) +
                      (npixels << 2) + nbytesRound, (char *) rep);
        free(rep);
        return Success;
    }
    
    int
    SProcXFixesGetCursorImageAndName(ClientPtr client)
    {
        REQUEST(xXFixesGetCursorImageAndNameReq);
        swaps(&stuff->length);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    /*
     * Find every cursor reference in the system, ask testCursor
     * whether it should be replaced with a reference to pCursor.
     */
    
    typedef Bool (*TestCursorFunc) (CursorPtr pOld, pointer closure);
    
    typedef struct {
        RESTYPE type;
        TestCursorFunc testCursor;
        CursorPtr pNew;
        pointer closure;
    } ReplaceCursorLookupRec, *ReplaceCursorLookupPtr;
    
    static const RESTYPE CursorRestypes[] = {
        RT_WINDOW, RT_PASSIVEGRAB, RT_CURSOR
    };
    
    #define NUM_CURSOR_RESTYPES (sizeof (CursorRestypes) / sizeof (CursorRestypes[0]))
    
    static Bool
    ReplaceCursorLookup(pointer value, XID id, pointer closure)
    {
        ReplaceCursorLookupPtr rcl = (ReplaceCursorLookupPtr) closure;
        WindowPtr pWin;
        GrabPtr pGrab;
        CursorPtr pCursor = 0, *pCursorRef = 0;
        XID cursor = 0;
    
        switch (rcl->type) {
        case RT_WINDOW:
            pWin = (WindowPtr) value;
            if (pWin->optional) {
                pCursorRef = &pWin->optional->cursor;
                pCursor = *pCursorRef;
            }
            break;
        case RT_PASSIVEGRAB:
            pGrab = (GrabPtr) value;
            pCursorRef = &pGrab->cursor;
            pCursor = *pCursorRef;
            break;
        case RT_CURSOR:
            pCursorRef = 0;
            pCursor = (CursorPtr) value;
            cursor = id;
            break;
        }
        if (pCursor && pCursor != rcl->pNew) {
            if ((*rcl->testCursor) (pCursor, rcl->closure)) {
                rcl->pNew->refcnt++;
                /* either redirect reference or update resource database */
                if (pCursorRef)
                    *pCursorRef = rcl->pNew;
                else
                    ChangeResourceValue(id, RT_CURSOR, rcl->pNew);
                FreeCursor(pCursor, cursor);
            }
        }
        return FALSE;               /* keep walking */
    }
    
    static void
    ReplaceCursor(CursorPtr pCursor, TestCursorFunc testCursor, pointer closure)
    {
        int clientIndex;
        int resIndex;
        ReplaceCursorLookupRec rcl;
    
        /* 
         * Cursors exist only in the resource database, windows and grabs.
         * All of these are always pointed at by the resource database.  Walk
         * the whole thing looking for cursors
         */
        rcl.testCursor = testCursor;
        rcl.pNew = pCursor;
        rcl.closure = closure;
    
        /* for each client */
        for (clientIndex = 0; clientIndex < currentMaxClients; clientIndex++) {
            if (!clients[clientIndex])
                continue;
            for (resIndex = 0; resIndex < NUM_CURSOR_RESTYPES; resIndex++) {
                rcl.type = CursorRestypes[resIndex];
                /*
                 * This function walks the entire client resource database
                 */
                LookupClientResourceComplex(clients[clientIndex],
                                            rcl.type,
                                            ReplaceCursorLookup, (pointer) &rcl);
            }
        }
        /* this "knows" that WindowHasNewCursor doesn't depend on it's argument */
        WindowHasNewCursor(screenInfo.screens[0]->root);
    }
    
    static Bool
    TestForCursor(CursorPtr pCursor, pointer closure)
    {
        return (pCursor == (CursorPtr) closure);
    }
    
    int
    ProcXFixesChangeCursor(ClientPtr client)
    {
        CursorPtr pSource, pDestination;
    
        REQUEST(xXFixesChangeCursorReq);
    
        REQUEST_SIZE_MATCH(xXFixesChangeCursorReq);
        VERIFY_CURSOR(pSource, stuff->source, client,
                      DixReadAccess | DixGetAttrAccess);
        VERIFY_CURSOR(pDestination, stuff->destination, client,
                      DixWriteAccess | DixSetAttrAccess);
    
        ReplaceCursor(pSource, TestForCursor, (pointer) pDestination);
        return Success;
    }
    
    int
    SProcXFixesChangeCursor(ClientPtr client)
    {
        REQUEST(xXFixesChangeCursorReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesChangeCursorReq);
        swapl(&stuff->source);
        swapl(&stuff->destination);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    static Bool
    TestForCursorName(CursorPtr pCursor, pointer closure)
    {
        Atom *pName = closure;
    
        return pCursor->name == *pName;
    }
    
    int
    ProcXFixesChangeCursorByName(ClientPtr client)
    {
        CursorPtr pSource;
        Atom name;
        char *tchar;
    
        REQUEST(xXFixesChangeCursorByNameReq);
    
        REQUEST_FIXED_SIZE(xXFixesChangeCursorByNameReq, stuff->nbytes);
        VERIFY_CURSOR(pSource, stuff->source, client,
                      DixReadAccess | DixGetAttrAccess);
        tchar = (char *) &stuff[1];
        name = MakeAtom(tchar, stuff->nbytes, FALSE);
        if (name)
            ReplaceCursor(pSource, TestForCursorName, &name);
        return Success;
    }
    
    int
    SProcXFixesChangeCursorByName(ClientPtr client)
    {
        REQUEST(xXFixesChangeCursorByNameReq);
    
        swaps(&stuff->length);
        REQUEST_AT_LEAST_SIZE(xXFixesChangeCursorByNameReq);
        swapl(&stuff->source);
        swaps(&stuff->nbytes);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    /*
     * Routines for manipulating the per-screen hide counts list.
     * This list indicates which clients have requested cursor hiding 
     * for that screen.
     */
    
    /* Return the screen's hide-counts list element for the given client */
    static CursorHideCountPtr
    findCursorHideCount(ClientPtr pClient, ScreenPtr pScreen)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        CursorHideCountPtr pChc;
    
        for (pChc = cs->pCursorHideCounts; pChc != NULL; pChc = pChc->pNext) {
            if (pChc->pClient == pClient) {
                return pChc;
            }
        }
    
        return NULL;
    }
    
    static int
    createCursorHideCount(ClientPtr pClient, ScreenPtr pScreen)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        CursorHideCountPtr pChc;
    
        pChc = (CursorHideCountPtr) malloc(sizeof(CursorHideCountRec));
        if (pChc == NULL) {
            return BadAlloc;
        }
        pChc->pClient = pClient;
        pChc->pScreen = pScreen;
        pChc->hideCount = 1;
        pChc->resource = FakeClientID(pClient->index);
        pChc->pNext = cs->pCursorHideCounts;
        cs->pCursorHideCounts = pChc;
    
        /* 
         * Create a resource for this element so it can be deleted
         * when the client goes away.
         */
        if (!AddResource(pChc->resource, CursorHideCountType, (pointer) pChc)) {
            free(pChc);
            return BadAlloc;
        }
    
        return Success;
    }
    
    /* 
     * Delete the given hide-counts list element from its screen list.
     */
    static void
    deleteCursorHideCount(CursorHideCountPtr pChcToDel, ScreenPtr pScreen)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        CursorHideCountPtr pChc, pNext;
        CursorHideCountPtr pChcLast = NULL;
    
        pChc = cs->pCursorHideCounts;
        while (pChc != NULL) {
            pNext = pChc->pNext;
            if (pChc == pChcToDel) {
                free(pChc);
                if (pChcLast == NULL) {
                    cs->pCursorHideCounts = pNext;
                }
                else {
                    pChcLast->pNext = pNext;
                }
                return;
            }
            pChcLast = pChc;
            pChc = pNext;
        }
    }
    
    /* 
     * Delete all the hide-counts list elements for this screen.
     */
    static void
    deleteCursorHideCountsForScreen(ScreenPtr pScreen)
    {
        CursorScreenPtr cs = GetCursorScreen(pScreen);
        CursorHideCountPtr pChc, pTmp;
    
        pChc = cs->pCursorHideCounts;
        while (pChc != NULL) {
            pTmp = pChc->pNext;
            FreeResource(pChc->resource, 0);
            pChc = pTmp;
        }
        cs->pCursorHideCounts = NULL;
    }
    
    int
    ProcXFixesHideCursor(ClientPtr client)
    {
        WindowPtr pWin;
        CursorHideCountPtr pChc;
    
        REQUEST(xXFixesHideCursorReq);
        int ret;
    
        REQUEST_SIZE_MATCH(xXFixesHideCursorReq);
    
        ret = dixLookupResourceByType((pointer *) &pWin, stuff->window, RT_WINDOW,
                                      client, DixGetAttrAccess);
        if (ret != Success) {
            client->errorValue = stuff->window;
            return ret;
        }
    
        /* 
         * Has client hidden the cursor before on this screen? 
         * If so, just increment the count. 
         */
    
        pChc = findCursorHideCount(client, pWin->drawable.pScreen);
        if (pChc != NULL) {
            pChc->hideCount++;
            return Success;
        }
    
        /* 
         * This is the first time this client has hid the cursor 
         * for this screen.
         */
        ret = XaceHook(XACE_SCREEN_ACCESS, client, pWin->drawable.pScreen,
                       DixHideAccess);
        if (ret != Success)
            return ret;
    
        ret = createCursorHideCount(client, pWin->drawable.pScreen);
    
        if (ret == Success) {
            DeviceIntPtr dev;
    
            for (dev = inputInfo.devices; dev; dev = dev->next) {
                if (IsMaster(dev) && IsPointerDevice(dev))
                    CursorDisplayCursor(dev, pWin->drawable.pScreen,
                                        CursorCurrent[dev->id]);
            }
        }
    
        return ret;
    }
    
    int
    SProcXFixesHideCursor(ClientPtr client)
    {
        REQUEST(xXFixesHideCursorReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesHideCursorReq);
        swapl(&stuff->window);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    int
    ProcXFixesShowCursor(ClientPtr client)
    {
        WindowPtr pWin;
        CursorHideCountPtr pChc;
        int rc;
    
        REQUEST(xXFixesShowCursorReq);
    
        REQUEST_SIZE_MATCH(xXFixesShowCursorReq);
    
        rc = dixLookupResourceByType((pointer *) &pWin, stuff->window, RT_WINDOW,
                                     client, DixGetAttrAccess);
        if (rc != Success) {
            client->errorValue = stuff->window;
            return rc;
        }
    
        /* 
         * Has client hidden the cursor on this screen?
         * If not, generate an error.
         */
        pChc = findCursorHideCount(client, pWin->drawable.pScreen);
        if (pChc == NULL) {
            return BadMatch;
        }
    
        rc = XaceHook(XACE_SCREEN_ACCESS, client, pWin->drawable.pScreen,
                      DixShowAccess);
        if (rc != Success)
            return rc;
    
        pChc->hideCount--;
        if (pChc->hideCount <= 0) {
            FreeResource(pChc->resource, 0);
        }
    
        return Success;
    }
    
    int
    SProcXFixesShowCursor(ClientPtr client)
    {
        REQUEST(xXFixesShowCursorReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesShowCursorReq);
        swapl(&stuff->window);
        return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
    }
    
    static int
    CursorFreeClient(pointer data, XID id)
    {
        CursorEventPtr old = (CursorEventPtr) data;
        CursorEventPtr *prev, e;
    
        for (prev = &cursorEvents; (e = *prev); prev = &e->next) {
            if (e == old) {
                *prev = e->next;
                free(e);
                break;
            }
        }
        return 1;
    }
    
    static int
    CursorFreeHideCount(pointer data, XID id)
    {
        CursorHideCountPtr pChc = (CursorHideCountPtr) data;
        ScreenPtr pScreen = pChc->pScreen;
        DeviceIntPtr dev;
    
        deleteCursorHideCount(pChc, pChc->pScreen);
        for (dev = inputInfo.devices; dev; dev = dev->next) {
            if (IsMaster(dev) && IsPointerDevice(dev))
                CursorDisplayCursor(dev, pScreen, CursorCurrent[dev->id]);
        }
    
        return 1;
    }
    
    static int
    CursorFreeWindow(pointer data, XID id)
    {
        WindowPtr pWindow = (WindowPtr) data;
        CursorEventPtr e, next;
    
        for (e = cursorEvents; e; e = next) {
            next = e->next;
            if (e->pWindow == pWindow) {
                FreeResource(e->clientResource, 0);
            }
        }
        return 1;
    }
    
    static BOOL
    barrier_is_horizontal(const struct PointerBarrier *barrier)
    {
        return barrier->y1 == barrier->y2;
    }
    
    static BOOL
    barrier_is_vertical(const struct PointerBarrier *barrier)
    {
        return barrier->x1 == barrier->x2;
    }
    
    /**
     * @return The set of barrier movement directions the movement vector
     * x1/y1 → x2/y2 represents.
     */
    int
    barrier_get_direction(int x1, int y1, int x2, int y2)
    {
        int direction = 0;
    
        /* which way are we trying to go */
        if (x2 > x1)
            direction |= BarrierPositiveX;
        if (x2 < x1)
            direction |= BarrierNegativeX;
        if (y2 > y1)
            direction |= BarrierPositiveY;
        if (y2 < y1)
            direction |= BarrierNegativeY;
    
        return direction;
    }
    
    /**
     * Test if the barrier may block movement in the direction defined by
     * x1/y1 → x2/y2. This function only tests whether the directions could be
     * blocked, it does not test if the barrier actually blocks the movement.
     *
     * @return TRUE if the barrier blocks the direction of movement or FALSE
     * otherwise.
     */
    BOOL
    barrier_is_blocking_direction(const struct PointerBarrier * barrier,
                                  int direction)
    {
        /* Barriers define which way is ok, not which way is blocking */
        return (barrier->directions & direction) != direction;
    }
    
    /**
     * Test if the movement vector x1/y1 → x2/y2 is intersecting with the
     * barrier. A movement vector with the startpoint or endpoint adjacent to
     * the barrier itself counts as intersecting.
     *
     * @param x1 X start coordinate of movement vector
     * @param y1 Y start coordinate of movement vector
     * @param x2 X end coordinate of movement vector
     * @param y2 Y end coordinate of movement vector
     * @param[out] distance The distance between the start point and the
     * intersection with the barrier (if applicable).
     * @return TRUE if the barrier intersects with the given vector
     */
    BOOL
    barrier_is_blocking(const struct PointerBarrier * barrier,
                        int x1, int y1, int x2, int y2, double *distance)
    {
        BOOL rc = FALSE;
        float ua, ub, ud;
        int dir = barrier_get_direction(x1, y1, x2, y2);
    
        /* Algorithm below doesn't handle edge cases well, hence the extra
         * checks. */
        if (barrier_is_vertical(barrier)) {
            /* handle immediate barrier adjacency, moving away */
            if (dir & BarrierPositiveX && x1 == barrier->x1)
                return FALSE;
            if (dir & BarrierNegativeX && x1 == (barrier->x1 - 1))
                return FALSE;
            /* startpoint adjacent to barrier, moving towards -> block */
            if (x1 == barrier->x1 && y1 >= barrier->y1 && y1 <= barrier->y2) {
                *distance = 0;
                return TRUE;
            }
        }
        else {
            /* handle immediate barrier adjacency, moving away */
            if (dir & BarrierPositiveY && y1 == barrier->y1)
                return FALSE;
            if (dir & BarrierNegativeY && y1 == (barrier->y1 - 1))
                return FALSE;
            /* startpoint adjacent to barrier, moving towards -> block */
            if (y1 == barrier->y1 && x1 >= barrier->x1 && x1 <= barrier->x2) {
                *distance = 0;
                return TRUE;
            }
        }
    
        /* not an edge case, compute distance */
        ua = 0;
        ud = (barrier->y2 - barrier->y1) * (x2 - x1) - (barrier->x2 -
                                                        barrier->x1) * (y2 - y1);
        if (ud != 0) {
            ua = ((barrier->x2 - barrier->x1) * (y1 - barrier->y1) -
                  (barrier->y2 - barrier->y1) * (x1 - barrier->x1)) / ud;
            ub = ((x2 - x1) * (y1 - barrier->y1) -
                  (y2 - y1) * (x1 - barrier->x1)) / ud;
            if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
                ua = 0;
        }
    
        if (ua > 0 && ua <= 1) {
            double ix = barrier->x1 + ua * (barrier->x2 - barrier->x1);
            double iy = barrier->y1 + ua * (barrier->y2 - barrier->y1);
    
            *distance = sqrt(pow(x1 - ix, 2) + pow(y1 - iy, 2));
            rc = TRUE;
        }
    
        return rc;
    }
    
    /**
     * Find the nearest barrier that is blocking movement from x1/y1 to x2/y2.
     *
     * @param dir Only barriers blocking movement in direction dir are checked
     * @param x1 X start coordinate of movement vector
     * @param y1 Y start coordinate of movement vector
     * @param x2 X end coordinate of movement vector
     * @param y2 Y end coordinate of movement vector
     * @return The barrier nearest to the movement origin that blocks this movement.
     */
    static struct PointerBarrier *
    barrier_find_nearest(CursorScreenPtr cs, int dir,
                         int x1, int y1, int x2, int y2)
    {
        struct PointerBarrierClient *c;
        struct PointerBarrier *nearest = NULL;
        double min_distance = INT_MAX;      /* can't get higher than that in X anyway */
    
        xorg_list_for_each_entry(c, &cs->barriers, entry) {
            struct PointerBarrier *b = &c->barrier;
            double distance;
    
            if (!barrier_is_blocking_direction(b, dir))
                continue;
    
            if (barrier_is_blocking(b, x1, y1, x2, y2, &distance)) {
                if (min_distance > distance) {
                    min_distance = distance;
                    nearest = b;
                }
            }
        }
    
        return nearest;
    }
    
    /**
     * Clamp to the given barrier given the movement direction specified in dir.
     *
     * @param barrier The barrier to clamp to
     * @param dir The movement direction
     * @param[out] x The clamped x coordinate.
     * @param[out] y The clamped x coordinate.
     */
    void
    barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x,
                             int *y)
    {
        if (barrier_is_vertical(barrier)) {
            if ((dir & BarrierNegativeX) & ~barrier->directions)
                *x = barrier->x1;
            if ((dir & BarrierPositiveX) & ~barrier->directions)
                *x = barrier->x1 - 1;
        }
        if (barrier_is_horizontal(barrier)) {
            if ((dir & BarrierNegativeY) & ~barrier->directions)
                *y = barrier->y1;
            if ((dir & BarrierPositiveY) & ~barrier->directions)
                *y = barrier->y1 - 1;
        }
    }
    
    static void
    CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode,
                                int *x, int *y)
    {
        CursorScreenPtr cs = GetCursorScreen(screen);
    
        if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev) &&
            mode == Relative) {
            int ox, oy;
            int dir;
            struct PointerBarrier *nearest = NULL;
    
            /* where are we coming from */
            miPointerGetPosition(dev, &ox, &oy);
    
            /* How this works:
             * Given the origin and the movement vector, get the nearest barrier
             * to the origin that is blocking the movement.
             * Clamp to that barrier.
             * Then, check from the clamped intersection to the original
             * destination, again finding the nearest barrier and clamping.
             */
            dir = barrier_get_direction(ox, oy, *x, *y);
    
            nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
            if (nearest) {
                barrier_clamp_to_barrier(nearest, dir, x, y);
    
                if (barrier_is_vertical(nearest)) {
                    dir &= ~(BarrierNegativeX | BarrierPositiveX);
                    ox = *x;
                }
                else if (barrier_is_horizontal(nearest)) {
                    dir &= ~(BarrierNegativeY | BarrierPositiveY);
                    oy = *y;
                }
    
                nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
                if (nearest) {
                    barrier_clamp_to_barrier(nearest, dir, x, y);
                }
            }
        }
    
        if (cs->ConstrainCursorHarder) {
            screen->ConstrainCursorHarder = cs->ConstrainCursorHarder;
            screen->ConstrainCursorHarder(dev, screen, mode, x, y);
            screen->ConstrainCursorHarder = CursorConstrainCursorHarder;
        }
    }
    
    static struct PointerBarrierClient *
    CreatePointerBarrierClient(ScreenPtr screen, ClientPtr client,
                               xXFixesCreatePointerBarrierReq * stuff)
    {
        CursorScreenPtr cs = GetCursorScreen(screen);
        struct PointerBarrierClient *ret = malloc(sizeof(*ret));
    
        if (ret) {
            ret->screen = screen;
            ret->barrier.x1 = min(stuff->x1, stuff->x2);
            ret->barrier.x2 = max(stuff->x1, stuff->x2);
            ret->barrier.y1 = min(stuff->y1, stuff->y2);
            ret->barrier.y2 = max(stuff->y1, stuff->y2);
            ret->barrier.directions = stuff->directions & 0x0f;
            if (barrier_is_horizontal(&ret->barrier))
                ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
            if (barrier_is_vertical(&ret->barrier))
                ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
            xorg_list_add(&ret->entry, &cs->barriers);
        }
    
        return ret;
    }
    
    int
    ProcXFixesCreatePointerBarrier(ClientPtr client)
    {
        int err;
        WindowPtr pWin;
        struct PointerBarrierClient *barrier;
        struct PointerBarrier b;
    
        REQUEST(xXFixesCreatePointerBarrierReq);
    
        REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
        LEGAL_NEW_RESOURCE(stuff->barrier, client);
    
        err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
        if (err != Success) {
            client->errorValue = stuff->window;
            return err;
        }
    
        /* This sure does need fixing. */
        if (stuff->num_devices)
            return BadImplementation;
    
        b.x1 = stuff->x1;
        b.x2 = stuff->x2;
        b.y1 = stuff->y1;
        b.y2 = stuff->y2;
    
        if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
            return BadValue;
    
        /* no 0-sized barriers */
        if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
            return BadValue;
    
        if (!(barrier = CreatePointerBarrierClient(pWin->drawable.pScreen,
                                                   client, stuff)))
            return BadAlloc;
    
        if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
            return BadAlloc;
    
        return Success;
    }
    
    int
    SProcXFixesCreatePointerBarrier(ClientPtr client)
    {
        REQUEST(xXFixesCreatePointerBarrierReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
        swapl(&stuff->barrier);
        swapl(&stuff->window);
        swaps(&stuff->x1);
        swaps(&stuff->y1);
        swaps(&stuff->x2);
        swaps(&stuff->y2);
        swapl(&stuff->directions);
        return ProcXFixesVector[stuff->xfixesReqType] (client);
    }
    
    static int
    CursorFreeBarrier(void *data, XID id)
    {
        struct PointerBarrierClient *b = NULL, *barrier;
        ScreenPtr screen;
        CursorScreenPtr cs;
    
        barrier = container_of(data, struct PointerBarrierClient, barrier);
    
        screen = barrier->screen;
        cs = GetCursorScreen(screen);
    
        /* find and unlink from the screen private */
        xorg_list_for_each_entry(b, &cs->barriers, entry) {
            if (b == barrier) {
                xorg_list_del(&b->entry);
                break;
            }
        }
    
        free(barrier);
        return Success;
    }
    
    int
    ProcXFixesDestroyPointerBarrier(ClientPtr client)
    {
        int err;
        void *barrier;
    
        REQUEST(xXFixesDestroyPointerBarrierReq);
    
        REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
    
        err = dixLookupResourceByType((void **) &barrier, stuff->barrier,
                                      PointerBarrierType, client, DixDestroyAccess);
        if (err != Success) {
            client->errorValue = stuff->barrier;
            return err;
        }
    
        FreeResource(stuff->barrier, RT_NONE);
        return Success;
    }
    
    int
    SProcXFixesDestroyPointerBarrier(ClientPtr client)
    {
        REQUEST(xXFixesDestroyPointerBarrierReq);
    
        swaps(&stuff->length);
        REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
        swapl(&stuff->barrier);
        return ProcXFixesVector[stuff->xfixesReqType] (client);
    }
    
    Bool
    XFixesCursorInit(void)
    {
        int i;
    
        if (party_like_its_1989)
            CursorVisible = EnableCursor;
    
        if (!dixRegisterPrivateKey(&CursorScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
            return FALSE;
    
        for (i = 0; i < screenInfo.numScreens; i++) {
            ScreenPtr pScreen = screenInfo.screens[i];
            CursorScreenPtr cs;
    
            cs = (CursorScreenPtr) calloc(1, sizeof(CursorScreenRec));
            if (!cs)
                return FALSE;
            xorg_list_init(&cs->barriers);
            Wrap(cs, pScreen, CloseScreen, CursorCloseScreen);
            Wrap(cs, pScreen, DisplayCursor, CursorDisplayCursor);
            Wrap(cs, pScreen, ConstrainCursorHarder, CursorConstrainCursorHarder);
            cs->pCursorHideCounts = NULL;
            SetCursorScreen(pScreen, cs);
        }
        CursorClientType = CreateNewResourceType(CursorFreeClient,
                                                 "XFixesCursorClient");
        CursorHideCountType = CreateNewResourceType(CursorFreeHideCount,
                                                    "XFixesCursorHideCount");
        CursorWindowType = CreateNewResourceType(CursorFreeWindow,
                                                 "XFixesCursorWindow");
        PointerBarrierType = CreateNewResourceType(CursorFreeBarrier,
                                                   "XFixesPointerBarrier");
    
        return CursorClientType && CursorHideCountType && CursorWindowType &&
            PointerBarrierType;
    }