cursor.c revision 9ace9065
1/*
2 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Copyright © 2002 Keith Packard
24 *
25 * Permission to use, copy, modify, distribute, and sell this software and its
26 * documentation for any purpose is hereby granted without fee, provided that
27 * the above copyright notice appear in all copies and that both that
28 * copyright notice and this permission notice appear in supporting
29 * documentation, and that the name of Keith Packard not be used in
30 * advertising or publicity pertaining to distribution of the software without
31 * specific, written prior permission.  Keith Packard makes no
32 * representations about the suitability of this software for any purpose.  It
33 * is provided "as is" without express or implied warranty.
34 *
35 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
36 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
37 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
39 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
40 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41 * PERFORMANCE OF THIS SOFTWARE.
42 */
43
44#ifdef HAVE_DIX_CONFIG_H
45#include <dix-config.h>
46#endif
47
48#include "xfixesint.h"
49#include "scrnintstr.h"
50#include "cursorstr.h"
51#include "dixevents.h"
52#include "servermd.h"
53#include "inputstr.h"
54#include "windowstr.h"
55#include "xace.h"
56
57static RESTYPE		CursorClientType;
58static RESTYPE		CursorHideCountType;
59static RESTYPE		CursorWindowType;
60static CursorPtr	CursorCurrent[MAXDEVICES];
61
62static DevPrivateKeyRec CursorScreenPrivateKeyRec;
63#define CursorScreenPrivateKey (&CursorScreenPrivateKeyRec)
64
65static void deleteCursorHideCountsForScreen (ScreenPtr pScreen);
66
67#define VERIFY_CURSOR(pCursor, cursor, client, access)			\
68    do {								\
69	int err;							\
70	err = dixLookupResourceByType((pointer *) &pCursor, cursor,	\
71				      RT_CURSOR, client, access);	\
72	if (err != Success) {						\
73	    client->errorValue = cursor;				\
74	    return err;							\
75	}								\
76    } while (0)
77
78/*
79 * There is a global list of windows selecting for cursor events
80 */
81
82typedef struct _CursorEvent *CursorEventPtr;
83
84typedef struct _CursorEvent {
85    CursorEventPtr	next;
86    CARD32		eventMask;
87    ClientPtr		pClient;
88    WindowPtr		pWindow;
89    XID			clientResource;
90} CursorEventRec;
91
92static CursorEventPtr	    cursorEvents;
93
94/*
95 * Each screen has a list of clients which have requested
96 * that the cursor be hid, and the number of times each
97 * client has requested.
98*/
99
100typedef struct _CursorHideCountRec *CursorHideCountPtr;
101
102typedef struct _CursorHideCountRec {
103    CursorHideCountPtr   pNext;
104    ClientPtr            pClient;
105    ScreenPtr            pScreen;
106    int                  hideCount;
107    XID			 resource;
108} CursorHideCountRec;
109
110/*
111 * Wrap DisplayCursor to catch cursor change events
112 */
113
114typedef struct _CursorScreen {
115    DisplayCursorProcPtr	DisplayCursor;
116    CloseScreenProcPtr		CloseScreen;
117    CursorHideCountPtr          pCursorHideCounts;
118} CursorScreenRec, *CursorScreenPtr;
119
120#define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey))
121#define GetCursorScreenIfSet(s) GetCursorScreen(s)
122#define SetCursorScreen(s,p) dixSetPrivate(&(s)->devPrivates, CursorScreenPrivateKey, p)
123#define Wrap(as,s,elt,func)	(((as)->elt = (s)->elt), (s)->elt = func)
124#define Unwrap(as,s,elt,backup)	(((backup) = (s)->elt), (s)->elt = (as)->elt)
125
126/* The cursor doesn't show up until the first XDefineCursor() */
127static Bool CursorVisible = FALSE;
128
129Bool EnableCursor = TRUE;
130
131static Bool
132CursorDisplayCursor (DeviceIntPtr pDev,
133                     ScreenPtr pScreen,
134		     CursorPtr pCursor)
135{
136    CursorScreenPtr	cs = GetCursorScreen(pScreen);
137    Bool		ret;
138    DisplayCursorProcPtr backupProc;
139
140    Unwrap (cs, pScreen, DisplayCursor, backupProc);
141
142    /*
143     * Have to check ConnectionInfo to distinguish client requests from
144     * initial root window setup.  Not a great way to do it, I admit.
145     */
146    if (ConnectionInfo)
147	CursorVisible = EnableCursor;
148
149    if (cs->pCursorHideCounts != NULL || !CursorVisible) {
150	ret = (*pScreen->DisplayCursor) (pDev, pScreen, NullCursor);
151    } else {
152	ret = (*pScreen->DisplayCursor) (pDev, pScreen, pCursor);
153    }
154
155    if (pCursor != CursorCurrent[pDev->id])
156    {
157	CursorEventPtr	e;
158
159	CursorCurrent[pDev->id] = pCursor;
160	for (e = cursorEvents; e; e = e->next)
161	{
162	    if ((e->eventMask & XFixesDisplayCursorNotifyMask))
163	    {
164		xXFixesCursorNotifyEvent	ev;
165		ev.type = XFixesEventBase + XFixesCursorNotify;
166		ev.subtype = XFixesDisplayCursorNotify;
167		ev.window = e->pWindow->drawable.id;
168		ev.cursorSerial = pCursor->serialNumber;
169		ev.timestamp = currentTime.milliseconds;
170		ev.name = pCursor->name;
171		WriteEventsToClient (e->pClient, 1, (xEvent *) &ev);
172	    }
173	}
174    }
175    Wrap (cs, pScreen, DisplayCursor, backupProc);
176
177    return ret;
178}
179
180static Bool
181CursorCloseScreen (int index, ScreenPtr pScreen)
182{
183    CursorScreenPtr	cs = GetCursorScreen (pScreen);
184    Bool		ret;
185    CloseScreenProcPtr	close_proc;
186    DisplayCursorProcPtr display_proc;
187
188    Unwrap (cs, pScreen, CloseScreen, close_proc);
189    Unwrap (cs, pScreen, DisplayCursor, display_proc);
190    deleteCursorHideCountsForScreen(pScreen);
191    ret = (*pScreen->CloseScreen) (index, pScreen);
192    free(cs);
193    return ret;
194}
195
196#define CursorAllEvents (XFixesDisplayCursorNotifyMask)
197
198static int
199XFixesSelectCursorInput (ClientPtr	pClient,
200			 WindowPtr	pWindow,
201			 CARD32		eventMask)
202{
203    CursorEventPtr	*prev, e;
204    pointer val;
205    int rc;
206
207    for (prev = &cursorEvents; (e = *prev); prev = &e->next)
208    {
209	if (e->pClient == pClient &&
210	    e->pWindow == pWindow)
211	{
212	    break;
213	}
214    }
215    if (!eventMask)
216    {
217	if (e)
218	{
219	    FreeResource (e->clientResource, 0);
220	}
221	return Success;
222    }
223    if (!e)
224    {
225	e = (CursorEventPtr) malloc(sizeof (CursorEventRec));
226	if (!e)
227	    return BadAlloc;
228
229	e->next = 0;
230	e->pClient = pClient;
231	e->pWindow = pWindow;
232	e->clientResource = FakeClientID(pClient->index);
233
234	/*
235	 * Add a resource hanging from the window to
236	 * catch window destroy
237	 */
238	rc = dixLookupResourceByType( &val, pWindow->drawable.id,
239				      CursorWindowType, serverClient,
240				      DixGetAttrAccess);
241	if (rc != Success)
242	    if (!AddResource (pWindow->drawable.id, CursorWindowType,
243			      (pointer) pWindow))
244	    {
245		free(e);
246		return BadAlloc;
247	    }
248
249	if (!AddResource (e->clientResource, CursorClientType, (pointer) e))
250	    return BadAlloc;
251
252	*prev = e;
253    }
254    e->eventMask = eventMask;
255    return Success;
256}
257
258int
259ProcXFixesSelectCursorInput (ClientPtr client)
260{
261    REQUEST (xXFixesSelectCursorInputReq);
262    WindowPtr	pWin;
263    int		rc;
264
265    REQUEST_SIZE_MATCH (xXFixesSelectCursorInputReq);
266    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
267    if (rc != Success)
268        return rc;
269    if (stuff->eventMask & ~CursorAllEvents)
270    {
271	client->errorValue = stuff->eventMask;
272	return BadValue;
273    }
274    return XFixesSelectCursorInput (client, pWin, stuff->eventMask);
275}
276
277static int
278GetBit (unsigned char *line, int x)
279{
280    unsigned char   mask;
281
282    if (screenInfo.bitmapBitOrder == LSBFirst)
283	mask = (1 << (x & 7));
284    else
285	mask = (0x80 >> (x & 7));
286    /* XXX assumes byte order is host byte order */
287    line += (x >> 3);
288    if (*line & mask)
289	return 1;
290    return 0;
291}
292
293int
294SProcXFixesSelectCursorInput (ClientPtr client)
295{
296    register int n;
297    REQUEST(xXFixesSelectCursorInputReq);
298
299    swaps(&stuff->length, n);
300    swapl(&stuff->window, n);
301    swapl(&stuff->eventMask, n);
302    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
303}
304
305void
306SXFixesCursorNotifyEvent (xXFixesCursorNotifyEvent *from,
307			  xXFixesCursorNotifyEvent *to)
308{
309    to->type = from->type;
310    cpswaps (from->sequenceNumber, to->sequenceNumber);
311    cpswapl (from->window, to->window);
312    cpswapl (from->cursorSerial, to->cursorSerial);
313    cpswapl (from->timestamp, to->timestamp);
314    cpswapl (from->name, to->name);
315}
316
317static void
318CopyCursorToImage (CursorPtr pCursor, CARD32 *image)
319{
320    int width = pCursor->bits->width;
321    int height = pCursor->bits->height;
322    int npixels = width * height;
323
324#ifdef ARGB_CURSOR
325    if (pCursor->bits->argb)
326	memcpy (image, pCursor->bits->argb, npixels * sizeof (CARD32));
327    else
328#endif
329    {
330	unsigned char	*srcLine = pCursor->bits->source;
331	unsigned char	*mskLine = pCursor->bits->mask;
332	int		stride = BitmapBytePad (width);
333	int		x, y;
334	CARD32		fg, bg;
335
336	fg = (0xff000000 |
337	      ((pCursor->foreRed & 0xff00) << 8) |
338	      (pCursor->foreGreen & 0xff00) |
339	      (pCursor->foreBlue >> 8));
340	bg = (0xff000000 |
341	      ((pCursor->backRed & 0xff00) << 8) |
342	      (pCursor->backGreen & 0xff00) |
343	      (pCursor->backBlue >> 8));
344	for (y = 0; y < height; y++)
345	{
346	    for (x = 0; x < width; x++)
347	    {
348		if (GetBit (mskLine, x))
349		{
350		    if (GetBit (srcLine, x))
351			*image++ = fg;
352		    else
353			*image++ = bg;
354		}
355		else
356		    *image++ = 0;
357	    }
358	    srcLine += stride;
359	    mskLine += stride;
360	}
361    }
362}
363
364int
365ProcXFixesGetCursorImage (ClientPtr client)
366{
367/*    REQUEST(xXFixesGetCursorImageReq); */
368    xXFixesGetCursorImageReply	*rep;
369    CursorPtr			pCursor;
370    CARD32			*image;
371    int				npixels, width, height, rc, x, y;
372
373    REQUEST_SIZE_MATCH(xXFixesGetCursorImageReq);
374    pCursor = CursorCurrent[PickPointer(client)->id];
375    if (!pCursor)
376	return BadCursor;
377    rc = XaceHook(XACE_RESOURCE_ACCESS, client, pCursor->id, RT_CURSOR,
378		  pCursor, RT_NONE, NULL, DixReadAccess);
379    if (rc != Success)
380	return rc;
381    GetSpritePosition (PickPointer(client), &x, &y);
382    width = pCursor->bits->width;
383    height = pCursor->bits->height;
384    npixels = width * height;
385    rep = malloc(sizeof (xXFixesGetCursorImageReply) +
386		  npixels * sizeof (CARD32));
387    if (!rep)
388	return BadAlloc;
389
390    rep->type = X_Reply;
391    rep->sequenceNumber = client->sequence;
392    rep->length = npixels;
393    rep->width = width;
394    rep->height = height;
395    rep->x = x;
396    rep->y = y;
397    rep->xhot = pCursor->bits->xhot;
398    rep->yhot = pCursor->bits->yhot;
399    rep->cursorSerial = pCursor->serialNumber;
400
401    image = (CARD32 *) (rep + 1);
402    CopyCursorToImage (pCursor, image);
403    if (client->swapped)
404    {
405	int n;
406	swaps (&rep->sequenceNumber, n);
407	swapl (&rep->length, n);
408	swaps (&rep->x, n);
409	swaps (&rep->y, n);
410	swaps (&rep->width, n);
411	swaps (&rep->height, n);
412	swaps (&rep->xhot, n);
413	swaps (&rep->yhot, n);
414	swapl (&rep->cursorSerial, n);
415	SwapLongs (image, npixels);
416    }
417    WriteToClient(client, sizeof (xXFixesGetCursorImageReply) +
418			 (npixels << 2), (char *) rep);
419    free(rep);
420    return Success;
421}
422
423int
424SProcXFixesGetCursorImage (ClientPtr client)
425{
426    int n;
427    REQUEST(xXFixesGetCursorImageReq);
428    swaps (&stuff->length, n);
429    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
430}
431
432int
433ProcXFixesSetCursorName (ClientPtr client)
434{
435    CursorPtr pCursor;
436    char *tchar;
437    REQUEST(xXFixesSetCursorNameReq);
438    Atom atom;
439
440    REQUEST_AT_LEAST_SIZE(xXFixesSetCursorNameReq);
441    VERIFY_CURSOR(pCursor, stuff->cursor, client, DixSetAttrAccess);
442    tchar = (char *) &stuff[1];
443    atom = MakeAtom (tchar, stuff->nbytes, TRUE);
444    if (atom == BAD_RESOURCE)
445	return BadAlloc;
446
447    pCursor->name = atom;
448    return Success;
449}
450
451int
452SProcXFixesSetCursorName (ClientPtr client)
453{
454    int n;
455    REQUEST(xXFixesSetCursorNameReq);
456
457    swaps (&stuff->length, n);
458    REQUEST_AT_LEAST_SIZE(xXFixesSetCursorNameReq);
459    swapl (&stuff->cursor, n);
460    swaps (&stuff->nbytes, n);
461    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
462}
463
464int
465ProcXFixesGetCursorName (ClientPtr client)
466{
467    CursorPtr			pCursor;
468    xXFixesGetCursorNameReply	reply;
469    REQUEST(xXFixesGetCursorNameReq);
470    const char *str;
471    int len;
472
473    REQUEST_SIZE_MATCH(xXFixesGetCursorNameReq);
474    VERIFY_CURSOR(pCursor, stuff->cursor, client, DixGetAttrAccess);
475    if (pCursor->name)
476	str = NameForAtom (pCursor->name);
477    else
478	str = "";
479    len = strlen (str);
480
481    reply.type = X_Reply;
482    reply.length = bytes_to_int32(len);
483    reply.sequenceNumber = client->sequence;
484    reply.atom = pCursor->name;
485    reply.nbytes = len;
486    if (client->swapped)
487    {
488	int n;
489	swaps (&reply.sequenceNumber, n);
490	swapl (&reply.length, n);
491	swapl (&reply.atom, n);
492	swaps (&reply.nbytes, n);
493    }
494    WriteReplyToClient(client, sizeof(xXFixesGetCursorNameReply), &reply);
495    WriteToClient(client, len, str);
496
497    return Success;
498}
499
500int
501SProcXFixesGetCursorName (ClientPtr client)
502{
503    int n;
504    REQUEST(xXFixesGetCursorNameReq);
505
506    swaps (&stuff->length, n);
507    REQUEST_SIZE_MATCH(xXFixesGetCursorNameReq);
508    swapl (&stuff->cursor, n);
509    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
510}
511
512int
513ProcXFixesGetCursorImageAndName (ClientPtr client)
514{
515/*    REQUEST(xXFixesGetCursorImageAndNameReq); */
516    xXFixesGetCursorImageAndNameReply	*rep;
517    CursorPtr			pCursor;
518    CARD32			*image;
519    int				npixels;
520    const char			*name;
521    int				nbytes, nbytesRound;
522    int				width, height;
523    int				rc, x, y;
524
525    REQUEST_SIZE_MATCH(xXFixesGetCursorImageAndNameReq);
526    pCursor = CursorCurrent[PickPointer(client)->id];
527    if (!pCursor)
528	return BadCursor;
529    rc = XaceHook(XACE_RESOURCE_ACCESS, client, pCursor->id, RT_CURSOR,
530		  pCursor, RT_NONE, NULL, DixReadAccess|DixGetAttrAccess);
531    if (rc != Success)
532	return rc;
533    GetSpritePosition (PickPointer(client), &x, &y);
534    width = pCursor->bits->width;
535    height = pCursor->bits->height;
536    npixels = width * height;
537    name = pCursor->name ? NameForAtom (pCursor->name) : "";
538    nbytes = strlen (name);
539    nbytesRound = pad_to_int32(nbytes);
540    rep = malloc(sizeof (xXFixesGetCursorImageAndNameReply) +
541		  npixels * sizeof (CARD32) + nbytesRound);
542    if (!rep)
543	return BadAlloc;
544
545    rep->type = X_Reply;
546    rep->sequenceNumber = client->sequence;
547    rep->length = npixels + bytes_to_int32(nbytesRound);
548    rep->width = width;
549    rep->height = height;
550    rep->x = x;
551    rep->y = y;
552    rep->xhot = pCursor->bits->xhot;
553    rep->yhot = pCursor->bits->yhot;
554    rep->cursorSerial = pCursor->serialNumber;
555    rep->cursorName = pCursor->name;
556    rep->nbytes = nbytes;
557
558    image = (CARD32 *) (rep + 1);
559    CopyCursorToImage (pCursor, image);
560    memcpy ((image + npixels), name, nbytes);
561    if (client->swapped)
562    {
563	int n;
564	swaps (&rep->sequenceNumber, n);
565	swapl (&rep->length, n);
566	swaps (&rep->x, n);
567	swaps (&rep->y, n);
568	swaps (&rep->width, n);
569	swaps (&rep->height, n);
570	swaps (&rep->xhot, n);
571	swaps (&rep->yhot, n);
572	swapl (&rep->cursorSerial, n);
573	swapl (&rep->cursorName, n);
574	swaps (&rep->nbytes, n);
575	SwapLongs (image, npixels);
576    }
577    WriteToClient(client, sizeof (xXFixesGetCursorImageAndNameReply) +
578			 (npixels << 2) + nbytesRound, (char *) rep);
579    free(rep);
580    return Success;
581}
582
583int
584SProcXFixesGetCursorImageAndName (ClientPtr client)
585{
586    int n;
587    REQUEST(xXFixesGetCursorImageAndNameReq);
588    swaps (&stuff->length, n);
589    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
590}
591
592/*
593 * Find every cursor reference in the system, ask testCursor
594 * whether it should be replaced with a reference to pCursor.
595 */
596
597typedef Bool (*TestCursorFunc) (CursorPtr pOld, pointer closure);
598
599typedef struct {
600    RESTYPE type;
601    TestCursorFunc testCursor;
602    CursorPtr pNew;
603    pointer closure;
604} ReplaceCursorLookupRec, *ReplaceCursorLookupPtr;
605
606static const RESTYPE    CursorRestypes[] = {
607    RT_WINDOW, RT_PASSIVEGRAB, RT_CURSOR
608};
609
610#define NUM_CURSOR_RESTYPES (sizeof (CursorRestypes) / sizeof (CursorRestypes[0]))
611
612static Bool
613ReplaceCursorLookup (pointer value, XID id, pointer closure)
614{
615    ReplaceCursorLookupPtr  rcl = (ReplaceCursorLookupPtr) closure;
616    WindowPtr		    pWin;
617    GrabPtr		    pGrab;
618    CursorPtr		    pCursor = 0, *pCursorRef = 0;
619    XID			    cursor = 0;
620
621    switch (rcl->type) {
622    case RT_WINDOW:
623	pWin = (WindowPtr) value;
624	if (pWin->optional)
625	{
626	    pCursorRef = &pWin->optional->cursor;
627	    pCursor = *pCursorRef;
628	}
629	break;
630    case RT_PASSIVEGRAB:
631	pGrab = (GrabPtr) value;
632	pCursorRef = &pGrab->cursor;
633	pCursor = *pCursorRef;
634	break;
635    case RT_CURSOR:
636	pCursorRef = 0;
637	pCursor = (CursorPtr) value;
638	cursor = id;
639	break;
640    }
641    if (pCursor && pCursor != rcl->pNew)
642    {
643	if ((*rcl->testCursor) (pCursor, rcl->closure))
644	{
645	    rcl->pNew->refcnt++;
646	    /* either redirect reference or update resource database */
647	    if (pCursorRef)
648		*pCursorRef = rcl->pNew;
649	    else
650		ChangeResourceValue (id, RT_CURSOR, rcl->pNew);
651	    FreeCursor (pCursor, cursor);
652	}
653    }
654    return FALSE;   /* keep walking */
655}
656
657static void
658ReplaceCursor (CursorPtr pCursor,
659	       TestCursorFunc testCursor,
660	       pointer closure)
661{
662    int	clientIndex;
663    int resIndex;
664    ReplaceCursorLookupRec  rcl;
665
666    /*
667     * Cursors exist only in the resource database, windows and grabs.
668     * All of these are always pointed at by the resource database.  Walk
669     * the whole thing looking for cursors
670     */
671    rcl.testCursor = testCursor;
672    rcl.pNew = pCursor;
673    rcl.closure = closure;
674
675    /* for each client */
676    for (clientIndex = 0; clientIndex < currentMaxClients; clientIndex++)
677    {
678	if (!clients[clientIndex])
679	    continue;
680	for (resIndex = 0; resIndex < NUM_CURSOR_RESTYPES; resIndex++)
681	{
682	    rcl.type = CursorRestypes[resIndex];
683	    /*
684	     * This function walks the entire client resource database
685	     */
686	    LookupClientResourceComplex (clients[clientIndex],
687					 rcl.type,
688					 ReplaceCursorLookup,
689					 (pointer) &rcl);
690	}
691    }
692    /* this "knows" that WindowHasNewCursor doesn't depend on it's argument */
693    WindowHasNewCursor (screenInfo.screens[0]->root);
694}
695
696static Bool
697TestForCursor (CursorPtr pCursor, pointer closure)
698{
699    return (pCursor == (CursorPtr) closure);
700}
701
702int
703ProcXFixesChangeCursor (ClientPtr client)
704{
705    CursorPtr	pSource, pDestination;
706    REQUEST(xXFixesChangeCursorReq);
707
708    REQUEST_SIZE_MATCH(xXFixesChangeCursorReq);
709    VERIFY_CURSOR (pSource, stuff->source, client,
710		   DixReadAccess|DixGetAttrAccess);
711    VERIFY_CURSOR (pDestination, stuff->destination, client,
712		   DixWriteAccess|DixSetAttrAccess);
713
714    ReplaceCursor (pSource, TestForCursor, (pointer) pDestination);
715    return Success;
716}
717
718int
719SProcXFixesChangeCursor (ClientPtr client)
720{
721    int n;
722    REQUEST(xXFixesChangeCursorReq);
723
724    swaps (&stuff->length, n);
725    REQUEST_SIZE_MATCH(xXFixesChangeCursorReq);
726    swapl (&stuff->source, n);
727    swapl (&stuff->destination, n);
728    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
729}
730
731static Bool
732TestForCursorName (CursorPtr pCursor, pointer closure)
733{
734    Atom *pName = closure;
735    return pCursor->name == *pName;
736}
737
738int
739ProcXFixesChangeCursorByName (ClientPtr client)
740{
741    CursorPtr	pSource;
742    Atom	name;
743    char	*tchar;
744    REQUEST(xXFixesChangeCursorByNameReq);
745
746    REQUEST_FIXED_SIZE(xXFixesChangeCursorByNameReq, stuff->nbytes);
747    VERIFY_CURSOR(pSource, stuff->source, client,
748		  DixReadAccess|DixGetAttrAccess);
749    tchar = (char *) &stuff[1];
750    name = MakeAtom (tchar, stuff->nbytes, FALSE);
751    if (name)
752	ReplaceCursor (pSource, TestForCursorName, &name);
753    return Success;
754}
755
756int
757SProcXFixesChangeCursorByName (ClientPtr client)
758{
759    int n;
760    REQUEST(xXFixesChangeCursorByNameReq);
761
762    swaps (&stuff->length, n);
763    REQUEST_AT_LEAST_SIZE (xXFixesChangeCursorByNameReq);
764    swapl (&stuff->source, n);
765    swaps (&stuff->nbytes, n);
766    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
767}
768
769/*
770 * Routines for manipulating the per-screen hide counts list.
771 * This list indicates which clients have requested cursor hiding
772 * for that screen.
773 */
774
775/* Return the screen's hide-counts list element for the given client */
776static CursorHideCountPtr
777findCursorHideCount (ClientPtr pClient, ScreenPtr pScreen)
778{
779    CursorScreenPtr    cs = GetCursorScreen(pScreen);
780    CursorHideCountPtr pChc;
781
782    for (pChc = cs->pCursorHideCounts; pChc != NULL; pChc = pChc->pNext) {
783	if (pChc->pClient == pClient) {
784	    return pChc;
785	}
786    }
787
788    return NULL;
789}
790
791static int
792createCursorHideCount (ClientPtr pClient, ScreenPtr pScreen)
793{
794    CursorScreenPtr    cs = GetCursorScreen(pScreen);
795    CursorHideCountPtr pChc;
796
797    pChc = (CursorHideCountPtr) malloc(sizeof(CursorHideCountRec));
798    if (pChc == NULL) {
799	return BadAlloc;
800    }
801    pChc->pClient = pClient;
802    pChc->pScreen = pScreen;
803    pChc->hideCount = 1;
804    pChc->resource = FakeClientID(pClient->index);
805    pChc->pNext = cs->pCursorHideCounts;
806    cs->pCursorHideCounts = pChc;
807
808    /*
809     * Create a resource for this element so it can be deleted
810     * when the client goes away.
811     */
812    if (!AddResource (pChc->resource, CursorHideCountType,
813		      (pointer) pChc)) {
814	free(pChc);
815	return BadAlloc;
816    }
817
818    return Success;
819}
820
821/*
822 * Delete the given hide-counts list element from its screen list.
823 */
824static void
825deleteCursorHideCount (CursorHideCountPtr pChcToDel, ScreenPtr pScreen)
826{
827    CursorScreenPtr    cs = GetCursorScreen(pScreen);
828    CursorHideCountPtr pChc, pNext;
829    CursorHideCountPtr pChcLast = NULL;
830
831    pChc = cs->pCursorHideCounts;
832    while (pChc != NULL) {
833	pNext = pChc->pNext;
834	if (pChc == pChcToDel) {
835	    free(pChc);
836	    if (pChcLast == NULL) {
837		cs->pCursorHideCounts = pNext;
838	    } else {
839		pChcLast->pNext = pNext;
840	    }
841	    return;
842	}
843	pChcLast = pChc;
844	pChc = pNext;
845    }
846}
847
848/*
849 * Delete all the hide-counts list elements for this screen.
850 */
851static void
852deleteCursorHideCountsForScreen (ScreenPtr pScreen)
853{
854    CursorScreenPtr    cs = GetCursorScreen(pScreen);
855    CursorHideCountPtr pChc, pTmp;
856
857    pChc = cs->pCursorHideCounts;
858    while (pChc != NULL) {
859	pTmp = pChc->pNext;
860	FreeResource(pChc->resource, 0);
861	pChc = pTmp;
862    }
863    cs->pCursorHideCounts = NULL;
864}
865
866int
867ProcXFixesHideCursor (ClientPtr client)
868{
869    WindowPtr pWin;
870    CursorHideCountPtr pChc;
871    REQUEST(xXFixesHideCursorReq);
872    int ret;
873
874    REQUEST_SIZE_MATCH (xXFixesHideCursorReq);
875
876    ret = dixLookupResourceByType((pointer *)&pWin, stuff->window, RT_WINDOW,
877			    client, DixGetAttrAccess);
878    if (ret != Success) {
879	client->errorValue = stuff->window;
880	return ret;
881    }
882
883    /*
884     * Has client hidden the cursor before on this screen?
885     * If so, just increment the count.
886     */
887
888    pChc = findCursorHideCount(client, pWin->drawable.pScreen);
889    if (pChc != NULL) {
890	pChc->hideCount++;
891	return Success;
892    }
893
894    /*
895     * This is the first time this client has hid the cursor
896     * for this screen.
897     */
898    ret = XaceHook(XACE_SCREEN_ACCESS, client, pWin->drawable.pScreen,
899		   DixHideAccess);
900    if (ret != Success)
901	return ret;
902
903    ret = createCursorHideCount(client, pWin->drawable.pScreen);
904
905    if (ret == Success) {
906	DeviceIntPtr dev;
907	for (dev = inputInfo.devices; dev; dev = dev->next)
908	{
909	    if (IsMaster(dev) && IsPointerDevice(dev))
910		CursorDisplayCursor(dev, pWin->drawable.pScreen, CursorCurrent[dev->id]);
911	}
912    }
913
914    return ret;
915}
916
917int
918SProcXFixesHideCursor (ClientPtr client)
919{
920    int n;
921    REQUEST(xXFixesHideCursorReq);
922
923    swaps (&stuff->length, n);
924    REQUEST_SIZE_MATCH (xXFixesHideCursorReq);
925    swapl (&stuff->window, n);
926    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
927}
928
929int
930ProcXFixesShowCursor (ClientPtr client)
931{
932    WindowPtr pWin;
933    CursorHideCountPtr pChc;
934    int rc;
935    REQUEST(xXFixesShowCursorReq);
936
937    REQUEST_SIZE_MATCH (xXFixesShowCursorReq);
938
939    rc = dixLookupResourceByType((pointer *)&pWin, stuff->window, RT_WINDOW,
940			   client, DixGetAttrAccess);
941    if (rc != Success) {
942	client->errorValue = stuff->window;
943	return rc;
944    }
945
946    /*
947     * Has client hidden the cursor on this screen?
948     * If not, generate an error.
949     */
950    pChc = findCursorHideCount(client, pWin->drawable.pScreen);
951    if (pChc == NULL) {
952	return BadMatch;
953    }
954
955    rc = XaceHook(XACE_SCREEN_ACCESS, client, pWin->drawable.pScreen,
956		  DixShowAccess);
957    if (rc != Success)
958	return rc;
959
960    pChc->hideCount--;
961    if (pChc->hideCount <= 0) {
962	FreeResource(pChc->resource, 0);
963    }
964
965    return Success;
966}
967
968int
969SProcXFixesShowCursor (ClientPtr client)
970{
971    int n;
972    REQUEST(xXFixesShowCursorReq);
973
974    swaps (&stuff->length, n);
975    REQUEST_SIZE_MATCH (xXFixesShowCursorReq);
976    swapl (&stuff->window, n);
977    return (*ProcXFixesVector[stuff->xfixesReqType]) (client);
978}
979
980static int
981CursorFreeClient (pointer data, XID id)
982{
983    CursorEventPtr	old = (CursorEventPtr) data;
984    CursorEventPtr	*prev, e;
985
986    for (prev = &cursorEvents; (e = *prev); prev = &e->next)
987    {
988	if (e == old)
989	{
990	    *prev = e->next;
991	    free(e);
992	    break;
993	}
994    }
995    return 1;
996}
997
998static int
999CursorFreeHideCount (pointer data, XID id)
1000{
1001    CursorHideCountPtr pChc = (CursorHideCountPtr) data;
1002    ScreenPtr pScreen = pChc->pScreen;
1003    DeviceIntPtr dev;
1004
1005    deleteCursorHideCount(pChc, pChc->pScreen);
1006    for (dev = inputInfo.devices; dev; dev = dev->next)
1007    {
1008        if (IsMaster(dev) && IsPointerDevice(dev))
1009            CursorDisplayCursor(dev, pScreen, CursorCurrent[dev->id]);
1010    }
1011
1012    return 1;
1013}
1014
1015static int
1016CursorFreeWindow (pointer data, XID id)
1017{
1018    WindowPtr		pWindow = (WindowPtr) data;
1019    CursorEventPtr	e, next;
1020
1021    for (e = cursorEvents; e; e = next)
1022    {
1023	next = e->next;
1024	if (e->pWindow == pWindow)
1025	{
1026	    FreeResource (e->clientResource, 0);
1027	}
1028    }
1029    return 1;
1030}
1031
1032Bool
1033XFixesCursorInit (void)
1034{
1035    int	i;
1036
1037    if (party_like_its_1989)
1038	CursorVisible = EnableCursor;
1039
1040    if (!dixRegisterPrivateKey(&CursorScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1041	return FALSE;
1042
1043    for (i = 0; i < screenInfo.numScreens; i++)
1044    {
1045	ScreenPtr	pScreen = screenInfo.screens[i];
1046	CursorScreenPtr	cs;
1047
1048	cs = (CursorScreenPtr) calloc(1, sizeof (CursorScreenRec));
1049	if (!cs)
1050	    return FALSE;
1051	Wrap (cs, pScreen, CloseScreen, CursorCloseScreen);
1052	Wrap (cs, pScreen, DisplayCursor, CursorDisplayCursor);
1053	cs->pCursorHideCounts = NULL;
1054	SetCursorScreen (pScreen, cs);
1055    }
1056    CursorClientType = CreateNewResourceType(CursorFreeClient,
1057					     "XFixesCursorClient");
1058    CursorHideCountType = CreateNewResourceType(CursorFreeHideCount,
1059						"XFixesCursorHideCount");
1060    CursorWindowType = CreateNewResourceType(CursorFreeWindow,
1061					     "XFixesCursorWindow");
1062
1063    return CursorClientType && CursorHideCountType && CursorWindowType;
1064}
1065
1066