/* * 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 #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 list entry; }; /* * Wrap DisplayCursor to catch cursor change events */ typedef struct _CursorScreen { DisplayCursorProcPtr DisplayCursor; CloseScreenProcPtr CloseScreen; ConstrainCursorHarderProcPtr ConstrainCursorHarder; CursorHideCountPtr pCursorHideCounts; struct 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) { register int n; REQUEST(xXFixesSelectCursorInputReq); swaps(&stuff->length, n); swapl(&stuff->window, n); swapl(&stuff->eventMask, n); 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) { int n; swaps (&rep->sequenceNumber, n); swapl (&rep->length, n); swaps (&rep->x, n); swaps (&rep->y, n); swaps (&rep->width, n); swaps (&rep->height, n); swaps (&rep->xhot, n); swaps (&rep->yhot, n); swapl (&rep->cursorSerial, n); SwapLongs (image, npixels); } WriteToClient(client, sizeof (xXFixesGetCursorImageReply) + (npixels << 2), (char *) rep); free(rep); return Success; } int SProcXFixesGetCursorImage (ClientPtr client) { int n; REQUEST(xXFixesGetCursorImageReq); swaps (&stuff->length, n); 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) { int n; REQUEST(xXFixesSetCursorNameReq); swaps (&stuff->length, n); REQUEST_AT_LEAST_SIZE(xXFixesSetCursorNameReq); swapl (&stuff->cursor, n); swaps (&stuff->nbytes, n); 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) { int n; swaps (&reply.sequenceNumber, n); swapl (&reply.length, n); swapl (&reply.atom, n); swaps (&reply.nbytes, n); } WriteReplyToClient(client, sizeof(xXFixesGetCursorNameReply), &reply); WriteToClient(client, len, str); return Success; } int SProcXFixesGetCursorName (ClientPtr client) { int n; REQUEST(xXFixesGetCursorNameReq); swaps (&stuff->length, n); REQUEST_SIZE_MATCH(xXFixesGetCursorNameReq); swapl (&stuff->cursor, n); 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) { int n; swaps (&rep->sequenceNumber, n); swapl (&rep->length, n); swaps (&rep->x, n); swaps (&rep->y, n); swaps (&rep->width, n); swaps (&rep->height, n); swaps (&rep->xhot, n); swaps (&rep->yhot, n); swapl (&rep->cursorSerial, n); swapl (&rep->cursorName, n); swaps (&rep->nbytes, n); SwapLongs (image, npixels); } WriteToClient(client, sizeof (xXFixesGetCursorImageAndNameReply) + (npixels << 2) + nbytesRound, (char *) rep); free(rep); return Success; } int SProcXFixesGetCursorImageAndName (ClientPtr client) { int n; REQUEST(xXFixesGetCursorImageAndNameReq); swaps (&stuff->length, n); 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) { int n; REQUEST(xXFixesChangeCursorReq); swaps (&stuff->length, n); REQUEST_SIZE_MATCH(xXFixesChangeCursorReq); swapl (&stuff->source, n); swapl (&stuff->destination, n); 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) { int n; REQUEST(xXFixesChangeCursorByNameReq); swaps (&stuff->length, n); REQUEST_AT_LEAST_SIZE (xXFixesChangeCursorByNameReq); swapl (&stuff->source, n); swaps (&stuff->nbytes, n); 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) { int n; REQUEST(xXFixesHideCursorReq); swaps (&stuff->length, n); REQUEST_SIZE_MATCH (xXFixesHideCursorReq); swapl (&stuff->window, n); 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) { int n; REQUEST(xXFixesShowCursorReq); swaps (&stuff->length, n); REQUEST_SIZE_MATCH (xXFixesShowCursorReq); swapl (&stuff->window, n); 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 */ 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 (!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); 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) { int n; REQUEST(xXFixesCreatePointerBarrierReq); swaps(&stuff->length, n); REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq); swapl(&stuff->barrier, n); swapl(&stuff->window, n); swaps(&stuff->x1, n); swaps(&stuff->y1, n); swaps(&stuff->x2, n); swaps(&stuff->y2, n); swapl(&stuff->directions, n); 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 */ list_for_each_entry(b, &cs->barriers, entry) { if (b == barrier) { 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) { int n; REQUEST(xXFixesDestroyPointerBarrierReq); swaps(&stuff->length, n); REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq); swapl(&stuff->barrier, n); 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; 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; }