1/*
2 * Copyright © 2024 Thomas E. Dickey
3 * Copyright © 2002 Keith Packard
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Keith Packard not be used in
10 * advertising or publicity pertaining to distribution of the software without
11 * specific, written prior permission.  Keith Packard makes no
12 * representations about the suitability of this software for any purpose.  It
13 * is provided "as is" without express or implied warranty.
14 *
15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 * PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#include "xcursorint.h"
25#include <X11/Xlibint.h>
26#include <X11/Xutil.h>
27
28XcursorCursors *
29XcursorCursorsCreate (Display *dpy, int size)
30{
31    XcursorCursors  *cursors;
32
33    cursors = malloc (sizeof (XcursorCursors) +
34		      (size_t) size * sizeof (Cursor));
35    if (!cursors)
36	return NULL;
37    cursors->ref = 1;
38    cursors->dpy = dpy;
39    cursors->ncursor = 0;
40    cursors->cursors = (Cursor *) (cursors + 1);
41    return cursors;
42}
43
44void
45XcursorCursorsDestroy (XcursorCursors *cursors)
46{
47    int	    n;
48
49    if (!cursors)
50      return;
51
52    --cursors->ref;
53    if (cursors->ref > 0)
54	return;
55
56    for (n = 0; n < cursors->ncursor; n++)
57	XFreeCursor (cursors->dpy, cursors->cursors[n]);
58    free (cursors);
59}
60
61XcursorAnimate *
62XcursorAnimateCreate (XcursorCursors *cursors)
63{
64    XcursorAnimate  *animate;
65
66    animate = malloc (sizeof (XcursorAnimate));
67    if (!animate)
68	return NULL;
69    animate->cursors = cursors;
70    cursors->ref++;
71    animate->sequence = 0;
72    return animate;
73}
74
75void
76XcursorAnimateDestroy (XcursorAnimate *animate)
77{
78    if (!animate)
79      return;
80
81    XcursorCursorsDestroy (animate->cursors);
82    free (animate);
83}
84
85Cursor
86XcursorAnimateNext (XcursorAnimate *animate)
87{
88    Cursor  cursor = animate->cursors->cursors[animate->sequence++];
89
90    if (animate->sequence >= animate->cursors->ncursor)
91	animate->sequence = 0;
92    return cursor;
93}
94
95#if RENDER_MAJOR > 0 || RENDER_MINOR >= 5
96static int
97nativeByteOrder (void)
98{
99    int	x = 1;
100
101    return (*((char *) &x) == 1) ? LSBFirst : MSBFirst;
102}
103#endif
104
105static XcursorUInt
106_XcursorPixelBrightness (XcursorPixel p)
107{
108    XcursorPixel    alpha = p >> 24;
109    XcursorPixel    r, g, b;
110
111    if (!alpha)
112	return 0;
113    r = ((p >> 8) & 0xff00) / alpha;
114    if (r > 0xff) r = 0xff;
115    g = ((p >> 0) & 0xff00) / alpha;
116    if (g > 0xff) g = 0xff;
117    b = ((p << 8) & 0xff00) / alpha;
118    if (b > 0xff) b = 0xff;
119    return (r * 153 + g * 301 + b * 58) >> 9;
120}
121
122static unsigned short
123_XcursorDivideAlpha (XcursorUInt value, XcursorUInt alpha)
124{
125    if (!alpha)
126	return 0;
127    value = value * 255 / alpha;
128    if (value > 255)
129	value = 255;
130    return (unsigned short) (value | (value << 8));
131}
132
133static void
134_XcursorPixelToColor (XcursorPixel p, XColor *color)
135{
136    XcursorPixel    alpha = p >> 24;
137
138    color->pixel = 0;
139    color->red =   _XcursorDivideAlpha ((p >> 16) & 0xff, alpha);
140    color->green = _XcursorDivideAlpha ((p >>  8) & 0xff, alpha);
141    color->blue =  _XcursorDivideAlpha ((p >>  0) & 0xff, alpha);
142    color->flags = DoRed|DoGreen|DoBlue;
143}
144
145#undef DEBUG_IMAGE
146#ifdef DEBUG_IMAGE
147static void
148_XcursorDumpImage (XImage *image)
149{
150    FILE    *f = fopen ("/tmp/images", "a" FOPEN_CLOEXEC);
151    int	    x, y;
152    if (!f)
153	return;
154    fprintf (f, "%d x %x\n", image->width, image->height);
155    for (y = 0; y < image->height; y++)
156    {
157	for (x = 0; x < image->width; x++)
158	    fprintf (f, "%c", XGetPixel (image, x, y) ? '*' : ' ');
159	fprintf (f, "\n");
160    }
161    fflush (f);
162    fclose (f);
163}
164
165static void
166_XcursorDumpColor (XColor *color, char *name)
167{
168    FILE    *f = fopen ("/tmp/images", "a" FOPEN_CLOEXEC);
169    fprintf (f, "%s: %x %x %x\n", name,
170	     color->red, color->green, color->blue);
171    fflush (f);
172    fclose (f);
173}
174#endif
175
176static int
177_XcursorCompareRed (const void *a, const void *b)
178{
179    const XcursorPixel    *ap = a, *bp = b;
180
181    return (int) (((*ap >> 16) & 0xff) - ((*bp >> 16) & 0xff));
182}
183
184static int
185_XcursorCompareGreen (const void *a, const void *b)
186{
187    const XcursorPixel    *ap = a, *bp = b;
188
189    return (int) (((*ap >> 8) & 0xff) - ((*bp >> 8) & 0xff));
190}
191
192static int
193_XcursorCompareBlue (const void *a, const void *b)
194{
195    const XcursorPixel    *ap = a, *bp = b;
196
197    return (int) (((*ap >> 0) & 0xff) - ((*bp >> 0) & 0xff));
198}
199
200#define ScaledPixels(c,n) ((c)/(XcursorPixel)(n))
201
202static XcursorPixel
203_XcursorAverageColor (XcursorPixel *pixels, int npixels)
204{
205    XcursorPixel    p;
206    XcursorPixel    red, green, blue;
207    int		    n = npixels;
208
209    if (n < 1)
210	return 0;
211
212    blue = green = red = 0;
213    while (n--)
214    {
215	p = *pixels++;
216	red += (p >> 16) & 0xff;
217	green += (p >> 8) & 0xff;
218	blue += (p >> 0) & 0xff;
219    }
220    return (0xffU << 24)
221	    | (ScaledPixels(red, npixels) << 16)
222	    | (ScaledPixels(green, npixels) << 8)
223	    |  ScaledPixels(blue, npixels);
224}
225
226typedef struct XcursorCoreCursor {
227    XImage  *src_image;
228    XImage  *msk_image;
229    XColor  on_color;
230    XColor  off_color;
231} XcursorCoreCursor;
232
233static Bool
234_XcursorHeckbertMedianCut (const XcursorImage *image, XcursorCoreCursor *core)
235{
236    XImage	    *src_image = core->src_image, *msk_image = core->msk_image;
237    unsigned int    npixels = image->width * image->height;
238    int		    ncolors;
239    int		    n;
240    XcursorPixel    *po, *pn, *pc;
241    XcursorPixel    p;
242    XcursorPixel    red, green, blue, alpha;
243    XcursorPixel    max_red, min_red, max_green, min_green, max_blue, min_blue;
244    XcursorPixel    *temp, *pixels, *colors;
245    int		    split;
246    XcursorPixel    leftColor, centerColor, rightColor;
247    int		    (*compare) (const void *, const void *);
248    int		    x, y;
249
250    /*
251     * Temp space for converted image and converted colors
252     */
253    temp = malloc (npixels * sizeof (XcursorPixel) * 2);
254    if (!temp)
255	return False;
256
257    pixels = temp;
258    colors = pixels + npixels;
259
260    /*
261     * Convert to 2-value alpha and build
262     * array of opaque color values and an
263     */
264    po = image->pixels;
265    pn = pixels;
266    pc = colors;
267    max_blue = max_green = max_red = 0;
268    min_blue = min_green = min_red = 255;
269    n = (int) npixels;
270    while (n--)
271    {
272	p = *po++;
273	alpha = (p >> 24) & 0xff;
274	red = (p >> 16) & 0xff;
275	green = (p >> 8) & 0xff;
276	blue = (p >> 0) & 0xff;
277	if (alpha >= 0x80)
278	{
279	    red = red * 255 / alpha;
280	    green = green * 255 / alpha;
281	    blue = blue * 255 / alpha;
282	    if (red < min_red) min_red = red;
283	    if (red > max_red) max_red = red;
284	    if (green < min_green) min_green = green;
285	    if (green > max_green) max_green = green;
286	    if (blue < min_blue) min_blue = blue;
287	    if (blue > max_blue) max_blue = blue;
288	    p = ((0xffU << 24) | (red << 16) |
289		 (green << 8) | (blue << 0));
290	    *pc++ = p;
291	}
292	else
293	    p = 0;
294	*pn++ = p;
295    }
296    ncolors = (int) (pc - colors);
297
298    /*
299     * Compute longest dimension and sort
300     */
301    if ((max_green - min_green) >= (max_red - min_red) &&
302	(max_green - min_green) >= (max_blue - min_blue))
303	compare = _XcursorCompareGreen;
304    else if ((max_red - min_red) >= (max_blue - min_blue))
305	compare = _XcursorCompareRed;
306    else
307	compare = _XcursorCompareBlue;
308    qsort (colors, (size_t) ncolors, sizeof (XcursorPixel), compare);
309    /*
310     * Compute average colors on both sides of the cut
311     */
312    split = ncolors >> 1;
313    leftColor  = _XcursorAverageColor (colors, split);
314    centerColor = colors[split];
315    rightColor = _XcursorAverageColor (colors + split, ncolors - split);
316    /*
317     * Select best color for each pixel
318     */
319    pn = pixels;
320    for (y = 0; (unsigned) y < image->height; y++)
321	for (x = 0; (unsigned) x < image->width; x++)
322	{
323	    p = *pn++;
324	    if (p & 0xff000000)
325	    {
326		XPutPixel (msk_image, x, y, 1);
327		if ((*compare) (&p, &centerColor) >= 0)
328		    XPutPixel (src_image, x, y, 0);
329		else
330		    XPutPixel (src_image, x, y, 1);
331	    }
332	    else
333	    {
334		XPutPixel (msk_image, x, y, 0);
335		XPutPixel (src_image, x, y, 0);
336	    }
337	}
338    free (temp);
339    _XcursorPixelToColor (rightColor, &core->off_color);
340    _XcursorPixelToColor (leftColor, &core->on_color);
341    return True;
342}
343
344#if 0
345#define DITHER_DIM  4
346static XcursorPixel orderedDither[4][4] = {
347    {  1,  9,  3, 11 },
348    { 13,  5, 15,  7 },
349    {  4, 12,  2, 10 },
350    { 16,  8, 14,  6 }
351};
352#else
353#define DITHER_DIM 2
354static XcursorPixel orderedDither[2][2] = {
355    {  1,  3,  },
356    {  4,  2,  },
357};
358#endif
359
360#define DITHER_SIZE  ((sizeof orderedDither / sizeof orderedDither[0][0]) + 1)
361
362static Bool
363_XcursorBayerOrderedDither (const XcursorImage *image, XcursorCoreCursor *core)
364{
365    int		    x, y;
366    XcursorPixel    *pixel, p;
367    XcursorPixel    a, i, d;
368
369    pixel = image->pixels;
370    for (y = 0; (unsigned) y < image->height; y++)
371	for (x = 0; (unsigned) x < image->width; x++)
372	{
373	    p = *pixel++;
374	    a = (XcursorPixel) (((p >> 24) * DITHER_SIZE + 127) / 255);
375	    i = (XcursorPixel) ((_XcursorPixelBrightness (p) * DITHER_SIZE + 127) / 255);
376	    d = orderedDither[y&(DITHER_DIM-1)][x&(DITHER_DIM-1)];
377	    if (a > d)
378	    {
379		XPutPixel (core->msk_image, x, y, 1);
380		if (i > d)
381		    XPutPixel (core->src_image, x, y, 0);   /* white */
382		else
383		    XPutPixel (core->src_image, x, y, 1);   /* black */
384	    }
385	    else
386	    {
387		XPutPixel (core->msk_image, x, y, 0);
388		XPutPixel (core->src_image, x, y, 0);
389	    }
390	}
391    core->on_color.red = 0;
392    core->on_color.green = 0;
393    core->on_color.blue = 0;
394    core->off_color.red = 0xffff;
395    core->off_color.green = 0xffff;
396    core->off_color.blue = 0xffff;
397    return True;
398}
399
400static Bool
401_XcursorFloydSteinberg (const XcursorImage *image, XcursorCoreCursor *core)
402{
403    int		    *aPicture, *iPicture, *aP, *iP;
404    XcursorPixel    *pixel, p;
405    int		    aR, iR, aA, iA;
406    unsigned int    npixels = image->width * image->height;
407    int		    n;
408    int		    right = 1;
409    int		    belowLeft = (int) (image->width - 1);
410    int		    below = (int) image->width;
411    int		    belowRight = (int) (image->width + 1);
412    int		    iError, aError;
413    int		    iErrorRight, aErrorRight;
414    int		    iErrorBelowLeft, aErrorBelowLeft;
415    int		    iErrorBelow, aErrorBelow;
416    int		    iErrorBelowRight, aErrorBelowRight;
417    int		    x, y;
418    int		    max_inten, min_inten, mean_inten;
419
420    iPicture = malloc (npixels * sizeof (int) * 2);
421    if (!iPicture)
422	return False;
423    aPicture = iPicture + npixels;
424
425    /*
426     * Compute raw gray and alpha arrays
427     */
428    pixel = image->pixels;
429    iP = iPicture;
430    aP = aPicture;
431    n = (int) npixels;
432    max_inten = 0;
433    min_inten = 0xff;
434    while (n-- > 0)
435    {
436	p = *pixel++;
437	*aP++ = (int) (p >> 24);
438	iR = (int) _XcursorPixelBrightness (p);
439	if (iR > max_inten) max_inten = iR;
440	if (iR < min_inten) min_inten = iR;
441	*iP++ = iR;
442    }
443    /*
444     * Draw the image while diffusing the error
445     */
446    iP = iPicture;
447    aP = aPicture;
448    mean_inten = (max_inten + min_inten + 1) >> 1;
449    for (y = 0; (unsigned) y < image->height; y++)
450	for (x = 0; (unsigned) x < image->width; x++)
451	{
452	    aR = *aP;
453	    iR = *iP;
454	    if (aR >= 0x80)
455	    {
456		XPutPixel (core->msk_image, x, y, 1);
457		aA = 0xff;
458	    }
459	    else
460	    {
461		XPutPixel (core->msk_image, x, y, 0);
462		aA = 0x00;
463	    }
464	    if (iR >= mean_inten)
465	    {
466		XPutPixel (core->src_image, x, y, 0);
467		iA = max_inten;
468	    }
469	    else
470	    {
471		XPutPixel (core->src_image, x, y, 1);
472		iA = min_inten;
473	    }
474	    iError = iR - iA;
475	    aError = aR - aA;
476	    iErrorRight = (iError * 7) >> 4;
477	    iErrorBelowLeft = (iError * 3) >> 4;
478	    iErrorBelow = (iError * 5) >> 4;
479	    iErrorBelowRight = (iError - iErrorRight -
480				iErrorBelowLeft - iErrorBelow);
481	    aErrorRight = (aError * 7) >> 4;
482	    aErrorBelowLeft = (aError * 3) >> 4;
483	    aErrorBelow = (aError * 5) >> 4;
484	    aErrorBelowRight = (aError - aErrorRight -
485				aErrorBelowLeft - aErrorBelow);
486	    if (x < ((int)image->width - 1))
487	    {
488		iP[right] += iErrorRight;
489		aP[right] += aErrorRight;
490	    }
491	    if (y < ((int)image->height - 1))
492	    {
493		if (x)
494		{
495		    iP[belowLeft] += iErrorBelowLeft;
496		    aP[belowLeft] += aErrorBelowLeft;
497		}
498		iP[below] += iErrorBelow;
499		aP[below] += aErrorBelow;
500		if (x < ((int)image->width - 1))
501		{
502		    iP[belowRight] += iErrorBelowRight;
503		    aP[belowRight] += aErrorBelowRight;
504		}
505	    }
506	    aP++;
507	    iP++;
508	}
509    free (iPicture);
510    core->on_color.red =
511    core->on_color.green =
512    core->on_color.blue = (unsigned short) (min_inten | min_inten << 8);
513    core->off_color.red =
514    core->off_color.green =
515    core->off_color.blue = (unsigned short) (max_inten | max_inten << 8);
516    return True;
517}
518
519static Bool
520_XcursorThreshold (const XcursorImage *image, XcursorCoreCursor *core)
521{
522    XcursorPixel    *pixel, p;
523    int		    x, y;
524
525    /*
526     * Draw the image, picking black for dark pixels and white for light
527     */
528    pixel = image->pixels;
529    for (y = 0; (unsigned) y < image->height; y++)
530	for (x = 0; (unsigned) x < image->width; x++)
531	{
532	    p = *pixel++;
533	    if ((p >> 24) >= 0x80)
534	    {
535		XPutPixel (core->msk_image, x, y, 1);
536		if (_XcursorPixelBrightness (p) > 0x80)
537		    XPutPixel (core->src_image, x, y, 0);
538		else
539		    XPutPixel (core->src_image, x, y, 1);
540	    }
541	    else
542	    {
543		XPutPixel (core->msk_image, x, y, 0);
544		XPutPixel (core->src_image, x, y, 0);
545	    }
546	}
547    core->on_color.red =
548    core->on_color.green =
549    core->on_color.blue = 0;
550    core->off_color.red =
551    core->off_color.green =
552    core->off_color.blue = 0xffff;
553    return True;
554}
555
556Cursor
557XcursorImageLoadCursor (Display *dpy, const XcursorImage *image)
558{
559    Cursor  cursor;
560
561#if RENDER_MAJOR > 0 || RENDER_MINOR >= 5
562    if (XcursorSupportsARGB (dpy))
563    {
564	XImage		    ximage;
565	int		    screen = DefaultScreen (dpy);
566	Pixmap		    pixmap;
567	Picture		    picture;
568	GC		    gc;
569	XRenderPictFormat   *format;
570
571	ximage.width = (int) image->width;
572	ximage.height = (int) image->height;
573	ximage.xoffset = 0;
574	ximage.format = ZPixmap;
575	ximage.data = (char *) image->pixels;
576	ximage.byte_order = nativeByteOrder ();
577	ximage.bitmap_unit = 32;
578	ximage.bitmap_bit_order = ximage.byte_order;
579	ximage.bitmap_pad = 32;
580	ximage.depth = 32;
581	ximage.bits_per_pixel = 32;
582	ximage.bytes_per_line = (int) (image->width * 4);
583	ximage.red_mask = 0xff0000;
584	ximage.green_mask = 0x00ff00;
585	ximage.blue_mask = 0x0000ff;
586	ximage.obdata = NULL;
587	if (!XInitImage (&ximage))
588	    return None;
589	pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
590				image->width, image->height, 32);
591	gc = XCreateGC (dpy, pixmap, 0, NULL);
592	XPutImage (dpy, pixmap, gc, &ximage,
593		   0, 0, 0, 0, image->width, image->height);
594	XFreeGC (dpy, gc);
595	format = XRenderFindStandardFormat (dpy, PictStandardARGB32);
596	picture = XRenderCreatePicture (dpy, pixmap, format, 0, NULL);
597	XFreePixmap (dpy, pixmap);
598	cursor = XRenderCreateCursor (dpy, picture,
599				      image->xhot, image->yhot);
600	XRenderFreePicture (dpy, picture);
601    }
602    else
603#endif
604    {
605	XcursorDisplayInfo  *info = _XcursorGetDisplayInfo (dpy);
606	int		    screen = DefaultScreen (dpy);
607	XcursorCoreCursor   core;
608	Pixmap		    src_pixmap, msk_pixmap;
609	GC		    gc;
610	XGCValues	    gcv;
611
612	if (!info)
613	    return 0;
614
615	core.src_image = XCreateImage (dpy, NULL, 1, ZPixmap,
616				       0, NULL, image->width, image->height,
617				       32, 0);
618	core.src_image->data = Xmalloc (image->height *
619					(unsigned) core.src_image->bytes_per_line);
620	core.msk_image = XCreateImage (dpy, NULL, 1, ZPixmap,
621				       0, NULL, image->width, image->height,
622				       32, 0);
623	core.msk_image->data = Xmalloc (image->height *
624					(unsigned) core.msk_image->bytes_per_line);
625
626	switch (info->dither) {
627	case XcursorDitherThreshold:
628	    if (!_XcursorThreshold (image, &core))
629		return 0;
630	    break;
631	case XcursorDitherMedian:
632	    if (!_XcursorHeckbertMedianCut (image, &core))
633		return 0;
634	    break;
635	case XcursorDitherOrdered:
636	    if (!_XcursorBayerOrderedDither (image, &core))
637		return 0;
638	    break;
639	case XcursorDitherDiffuse:
640	    if (!_XcursorFloydSteinberg (image, &core))
641		return 0;
642	    break;
643	default:
644	    return 0;
645	}
646
647	/*
648	 * Create the cursor
649	 */
650	src_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
651				    image->width, image->height, 1);
652	msk_pixmap = XCreatePixmap (dpy, RootWindow (dpy, screen),
653				    image->width, image->height, 1);
654	gcv.foreground = 1;
655	gcv.background = 0;
656	gc = XCreateGC (dpy, src_pixmap,
657			GCForeground|GCBackground,
658			&gcv);
659	XPutImage (dpy, src_pixmap, gc, core.src_image,
660		   0, 0, 0, 0, image->width, image->height);
661
662	XPutImage (dpy, msk_pixmap, gc, core.msk_image,
663		   0, 0, 0, 0, image->width, image->height);
664	XFreeGC (dpy, gc);
665
666#ifdef DEBUG_IMAGE
667	_XcursorDumpColor (&core.on_color, "on_color");
668	_XcursorDumpColor (&core.off_color, "off_color");
669	_XcursorDumpImage (core.src_image);
670	_XcursorDumpImage (core.msk_image);
671#endif
672	XDestroyImage (core.src_image);
673	XDestroyImage (core.msk_image);
674
675	cursor = XCreatePixmapCursor (dpy, src_pixmap, msk_pixmap,
676				      &core.on_color, &core.off_color,
677				      image->xhot, image->yhot);
678	XFreePixmap (dpy, src_pixmap);
679	XFreePixmap (dpy, msk_pixmap);
680    }
681    return cursor;
682}
683
684XcursorCursors *
685XcursorImagesLoadCursors (Display *dpy, const XcursorImages *images)
686{
687    XcursorCursors  *cursors = XcursorCursorsCreate (dpy, images->nimage);
688    int		    n;
689
690    if (!cursors)
691	return NULL;
692    for (n = 0; n < images->nimage; n++)
693    {
694	cursors->cursors[n] = XcursorImageLoadCursor (dpy, images->images[n]);
695	if (!cursors->cursors[n])
696	{
697	    XcursorCursorsDestroy (cursors);
698	    return NULL;
699	}
700	cursors->ncursor++;
701    }
702    return cursors;
703}
704
705Cursor
706XcursorImagesLoadCursor (Display *dpy, const XcursorImages *images)
707{
708    Cursor  cursor;
709    if (images->nimage == 1 || !XcursorSupportsAnim (dpy))
710	cursor = XcursorImageLoadCursor (dpy, images->images[0]);
711    else
712    {
713	XcursorCursors	*cursors = XcursorImagesLoadCursors (dpy, images);
714	XAnimCursor	*anim;
715	int		n;
716
717	if (!cursors)
718	    return 0;
719	anim = malloc ((size_t) cursors->ncursor * sizeof (XAnimCursor));
720	if (!anim)
721	{
722	    XcursorCursorsDestroy (cursors);
723	    return 0;
724	}
725	for (n = 0; n < cursors->ncursor; n++)
726	{
727	    anim[n].cursor = cursors->cursors[n];
728	    anim[n].delay = images->images[n]->delay;
729	}
730	cursor = XRenderCreateAnimCursor (dpy, cursors->ncursor, anim);
731	XcursorCursorsDestroy(cursors);
732	free (anim);
733    }
734#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2
735    if (images->name)
736	XFixesSetCursorName (dpy, cursor, images->name);
737#endif
738    return cursor;
739}
740
741
742Cursor
743XcursorFilenameLoadCursor (Display *dpy, const char *file)
744{
745    int		    size = XcursorGetDefaultSize (dpy);
746    XcursorBool     resize = XcursorGetResizable (dpy);
747    XcursorImages   *images;
748    Cursor	    cursor;
749
750    images = _XcursorFilenameLoadImages (file, size, resize);
751    if (!images)
752	return None;
753    cursor = XcursorImagesLoadCursor (dpy, images);
754    XcursorImagesDestroy (images);
755    return cursor;
756}
757
758XcursorCursors *
759XcursorFilenameLoadCursors (Display *dpy, const char *file)
760{
761    int		    size = XcursorGetDefaultSize (dpy);
762    XcursorBool     resize = XcursorGetResizable (dpy);
763    XcursorImages   *images;
764    XcursorCursors  *cursors;
765
766    images = _XcursorFilenameLoadImages (file, size, resize);
767    if (!images)
768	return NULL;
769    cursors = XcursorImagesLoadCursors (dpy, images);
770    XcursorImagesDestroy (images);
771    return cursors;
772}
773
774/*
775 * Stolen from XCreateGlyphCursor (which we cruelly override)
776 */
777
778Cursor
779_XcursorCreateGlyphCursor(Display	    *dpy,
780			  Font		    source_font,
781			  Font		    mask_font,
782			  unsigned int	    source_char,
783			  unsigned int	    mask_char,
784			  XColor _Xconst    *foreground,
785			  XColor _Xconst    *background)
786{
787    Cursor cid;
788    register xCreateGlyphCursorReq *req;
789
790    LockDisplay(dpy);
791    GetReq(CreateGlyphCursor, req);
792    cid = req->cid = (CARD32) XAllocID(dpy);
793    req->source = (CARD32) source_font;
794    req->mask = (CARD32) mask_font;
795    req->sourceChar = (CARD16) source_char;
796    req->maskChar = (CARD16) mask_char;
797    req->foreRed = foreground->red;
798    req->foreGreen = foreground->green;
799    req->foreBlue = foreground->blue;
800    req->backRed = background->red;
801    req->backGreen = background->green;
802    req->backBlue = background->blue;
803    UnlockDisplay(dpy);
804    SyncHandle();
805    return (cid);
806}
807
808/*
809 * Stolen from XCreateFontCursor (which we cruelly override)
810 */
811
812Cursor
813_XcursorCreateFontCursor (Display *dpy, unsigned int shape)
814{
815#define DATA(c) { 0UL, c, c, c, 0, 0 }
816    static XColor _Xconst foreground = DATA(0);  /* black */
817    static XColor _Xconst background = DATA(65535);  /* white */
818#undef DATA
819
820    /*
821     * the cursor font contains the shape glyph followed by the mask
822     * glyph; so character position 0 contains a shape, 1 the mask for 0,
823     * 2 a shape, etc.  <X11/cursorfont.h> contains hash define names
824     * for all of these.
825     */
826
827    if (dpy->cursor_font == None)
828    {
829	dpy->cursor_font = XLoadFont (dpy, CURSORFONT);
830	if (dpy->cursor_font == None)
831	    return None;
832    }
833
834    return _XcursorCreateGlyphCursor (dpy, dpy->cursor_font, dpy->cursor_font,
835				      shape, shape + 1, &foreground, &background);
836}
837
838