17914d74bSmrg/*
26b7436aeSmrg * Copyright © 2024 Thomas E. Dickey
37914d74bSmrg * Copyright © 2002 Keith Packard
47914d74bSmrg *
57914d74bSmrg * Permission to use, copy, modify, distribute, and sell this software and its
67914d74bSmrg * documentation for any purpose is hereby granted without fee, provided that
77914d74bSmrg * the above copyright notice appear in all copies and that both that
87914d74bSmrg * copyright notice and this permission notice appear in supporting
97914d74bSmrg * documentation, and that the name of Keith Packard not be used in
107914d74bSmrg * advertising or publicity pertaining to distribution of the software without
117914d74bSmrg * specific, written prior permission.  Keith Packard makes no
127914d74bSmrg * representations about the suitability of this software for any purpose.  It
137914d74bSmrg * is provided "as is" without express or implied warranty.
147914d74bSmrg *
157914d74bSmrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
167914d74bSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
177914d74bSmrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
187914d74bSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
197914d74bSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
207914d74bSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
217914d74bSmrg * PERFORMANCE OF THIS SOFTWARE.
227914d74bSmrg */
237914d74bSmrg
247914d74bSmrg#include "xcursorint.h"
257914d74bSmrg#include <stdlib.h>
267914d74bSmrg#include <string.h>
277914d74bSmrg
287914d74bSmrg#ifndef ICONDIR
297914d74bSmrg#define ICONDIR "/usr/X11R6/lib/X11/icons"
307914d74bSmrg#endif
317914d74bSmrg
327914d74bSmrg#ifndef XCURSORPATH
33cdce750aSmrg#define XCURSORPATH "~/.local/share/icons:~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR
347914d74bSmrg#endif
357914d74bSmrg
364d939ec7Smrgtypedef struct XcursorInherit {
374d939ec7Smrg    char	*line;
384d939ec7Smrg    const char	*theme;
394d939ec7Smrg} XcursorInherit;
404d939ec7Smrg
417914d74bSmrgconst char *
427914d74bSmrgXcursorLibraryPath (void)
437914d74bSmrg{
447914d74bSmrg    static const char	*path;
457914d74bSmrg
467914d74bSmrg    if (!path)
477914d74bSmrg    {
487914d74bSmrg	path = getenv ("XCURSOR_PATH");
497914d74bSmrg	if (!path)
507914d74bSmrg	    path = XCURSORPATH;
516b7436aeSmrg	traceOpts((T_OPTION(XCURSOR_PATH) ": %s\n", NonNull(path)));
527914d74bSmrg    }
537914d74bSmrg    return path;
547914d74bSmrg}
557914d74bSmrg
567914d74bSmrgstatic  void
577914d74bSmrg_XcursorAddPathElt (char *path, const char *elt, int len)
587914d74bSmrg{
599d0ccd10Smrg    size_t    pathlen = strlen (path);
60e6d5e4e0Smrg
617914d74bSmrg    /* append / if the path doesn't currently have one */
627914d74bSmrg    if (path[0] == '\0' || path[pathlen - 1] != '/')
637914d74bSmrg    {
647914d74bSmrg	strcat (path, "/");
657914d74bSmrg	pathlen++;
667914d74bSmrg    }
677914d74bSmrg    if (len == -1)
684d939ec7Smrg	len = (int) strlen (elt);
697914d74bSmrg    /* strip leading slashes */
707914d74bSmrg    while (len && elt[0] == '/')
717914d74bSmrg    {
727914d74bSmrg	elt++;
737914d74bSmrg	len--;
747914d74bSmrg    }
754d939ec7Smrg    strncpy (path + pathlen, elt, (size_t) len);
764d939ec7Smrg    path[pathlen + (size_t) len] = '\0';
777914d74bSmrg}
787914d74bSmrg
797914d74bSmrgstatic char *
807914d74bSmrg_XcursorBuildThemeDir (const char *dir, const char *theme)
817914d74bSmrg{
827914d74bSmrg    const char	    *colon;
837914d74bSmrg    const char	    *tcolon;
847914d74bSmrg    char	    *full;
857914d74bSmrg    char	    *home;
867914d74bSmrg    int		    dirlen;
877914d74bSmrg    int		    homelen;
887914d74bSmrg    int		    themelen;
897914d74bSmrg    int		    len;
907914d74bSmrg
917914d74bSmrg    if (!dir || !theme)
924d939ec7Smrg	return NULL;
93e6d5e4e0Smrg
947914d74bSmrg    colon = strchr (dir, ':');
957914d74bSmrg    if (!colon)
967914d74bSmrg	colon = dir + strlen (dir);
97e6d5e4e0Smrg
984d939ec7Smrg    dirlen = (int) (colon - dir);
997914d74bSmrg
1007914d74bSmrg    tcolon = strchr (theme, ':');
1017914d74bSmrg    if (!tcolon)
1027914d74bSmrg	tcolon = theme + strlen (theme);
1037914d74bSmrg
1044d939ec7Smrg    themelen = (int) (tcolon - theme);
105e6d5e4e0Smrg
1067914d74bSmrg    home = NULL;
1077914d74bSmrg    homelen = 0;
1087914d74bSmrg    if (*dir == '~')
1097914d74bSmrg    {
1107914d74bSmrg	home = getenv ("HOME");
1117914d74bSmrg	if (!home)
1127914d74bSmrg	    return NULL;
1134d939ec7Smrg	homelen = (int) strlen (home);
1147914d74bSmrg	dir++;
1157914d74bSmrg	dirlen--;
1167914d74bSmrg    }
1177914d74bSmrg
1187914d74bSmrg    /*
1197914d74bSmrg     * add space for any needed directory separators, one per component,
1207914d74bSmrg     * and one for the trailing null
1217914d74bSmrg     */
1227914d74bSmrg    len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
123e6d5e4e0Smrg
1244d939ec7Smrg    full = malloc ((size_t)len);
1257914d74bSmrg    if (!full)
1267914d74bSmrg	return NULL;
1277914d74bSmrg    full[0] = '\0';
1287914d74bSmrg
1297914d74bSmrg    if (home)
1307914d74bSmrg	_XcursorAddPathElt (full, home, -1);
1317914d74bSmrg    _XcursorAddPathElt (full, dir, dirlen);
1327914d74bSmrg    _XcursorAddPathElt (full, theme, themelen);
1337914d74bSmrg    return full;
1347914d74bSmrg}
1357914d74bSmrg
1367914d74bSmrgstatic char *
1377914d74bSmrg_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
1387914d74bSmrg{
1397914d74bSmrg    char    *full;
1407914d74bSmrg
1417914d74bSmrg    if (!dir || !subdir || !file)
1424d939ec7Smrg	return NULL;
1437914d74bSmrg
1447914d74bSmrg    full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
1457914d74bSmrg    if (!full)
1467914d74bSmrg	return NULL;
1477914d74bSmrg    full[0] = '\0';
1487914d74bSmrg    _XcursorAddPathElt (full, dir, -1);
1497914d74bSmrg    _XcursorAddPathElt (full, subdir, -1);
1507914d74bSmrg    _XcursorAddPathElt (full, file, -1);
1517914d74bSmrg    return full;
1527914d74bSmrg}
1537914d74bSmrg
1547914d74bSmrgstatic const char *
1557914d74bSmrg_XcursorNextPath (const char *path)
1567914d74bSmrg{
1577914d74bSmrg    char    *colon = strchr (path, ':');
1587914d74bSmrg
1597914d74bSmrg    if (!colon)
1607914d74bSmrg	return NULL;
1617914d74bSmrg    return colon + 1;
1627914d74bSmrg}
1637914d74bSmrg
1640ea508b1Smrg/*
1650ea508b1Smrg * _XcursorThemeInherits, XcursorWhite, & XcursorSep are copied in
1660ea508b1Smrg * libxcb-cursor/cursor/load_cursor.c.  Please update that copy to
1670ea508b1Smrg * include any changes made to the code for those here.
1680ea508b1Smrg */
1690ea508b1Smrg
1707914d74bSmrg#define XcursorWhite(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
1717914d74bSmrg#define XcursorSep(c) ((c) == ';' || (c) == ',')
1727914d74bSmrg
1737914d74bSmrgstatic char *
1747914d74bSmrg_XcursorThemeInherits (const char *full)
1757914d74bSmrg{
1767914d74bSmrg    char    line[8192];
1777914d74bSmrg    char    *result = NULL;
1787914d74bSmrg    FILE    *f;
1797914d74bSmrg
1807914d74bSmrg    if (!full)
1814d939ec7Smrg	return NULL;
1827914d74bSmrg
1830ea508b1Smrg    f = fopen (full, "r" FOPEN_CLOEXEC);
1847914d74bSmrg    if (f)
1857914d74bSmrg    {
1867914d74bSmrg	while (fgets (line, sizeof (line), f))
1877914d74bSmrg	{
1887914d74bSmrg	    if (!strncmp (line, "Inherits", 8))
1897914d74bSmrg	    {
1907914d74bSmrg		char    *l = line + 8;
1917914d74bSmrg		while (*l == ' ') l++;
1927914d74bSmrg		if (*l != '=') continue;
1937914d74bSmrg		l++;
1947914d74bSmrg		while (*l == ' ') l++;
1959d0ccd10Smrg		result = malloc (strlen (l) + 1);
1967914d74bSmrg		if (result)
1977914d74bSmrg		{
1984d939ec7Smrg		    char *r = result;
199e6d5e4e0Smrg		    while (*l)
2007914d74bSmrg		    {
2017914d74bSmrg			while (XcursorSep(*l) || XcursorWhite (*l)) l++;
2027914d74bSmrg			if (!*l)
2037914d74bSmrg			    break;
2047914d74bSmrg			if (r != result)
2057914d74bSmrg			    *r++ = ':';
206e6d5e4e0Smrg			while (*l && !XcursorWhite(*l) &&
2077914d74bSmrg			       !XcursorSep(*l))
2087914d74bSmrg			    *r++ = *l++;
2097914d74bSmrg		    }
2107914d74bSmrg		    *r++ = '\0';
2117914d74bSmrg		}
2127914d74bSmrg		break;
2137914d74bSmrg	    }
2147914d74bSmrg	}
2157914d74bSmrg	fclose (f);
2167914d74bSmrg    }
2177914d74bSmrg    return result;
2187914d74bSmrg}
2197914d74bSmrg
2207914d74bSmrg#define XCURSOR_SCAN_CORE   ((FILE *) 1)
2214d939ec7Smrg#define MAX_INHERITS_DEPTH  32
2227914d74bSmrg
2237914d74bSmrgstatic FILE *
2247914d74bSmrgXcursorScanTheme (const char *theme, const char *name)
2257914d74bSmrg{
2264d939ec7Smrg    FILE		*f = NULL;
2274d939ec7Smrg    char		*full;
2284d939ec7Smrg    char		*dir;
2294d939ec7Smrg    const char		*path;
2304d939ec7Smrg    XcursorInherit	 inherits[MAX_INHERITS_DEPTH + 1];
2314d939ec7Smrg    int			 d;
2327914d74bSmrg
2337914d74bSmrg    if (!theme || !name)
2344d939ec7Smrg	return NULL;
2357914d74bSmrg
2367914d74bSmrg    /*
2377914d74bSmrg     * XCURSOR_CORE_THEME is a magic name; cursors from the core set
2387914d74bSmrg     * are never found in any directory.  Instead, a magic value is
2397914d74bSmrg     * returned which truncates any search so that overlying functions
2407914d74bSmrg     * can switch to equivalent core cursors
2417914d74bSmrg     */
2427914d74bSmrg    if (!strcmp (theme, XCURSOR_CORE_THEME) && XcursorLibraryShape (name) >= 0)
2437914d74bSmrg	return XCURSOR_SCAN_CORE;
2444d939ec7Smrg
2454d939ec7Smrg    memset (inherits, 0, sizeof (inherits));
2464d939ec7Smrg
2474d939ec7Smrg    d = 0;
2484d939ec7Smrg    inherits[d].theme = theme;
2494d939ec7Smrg
2504d939ec7Smrg    while (f == NULL && d >= 0 && inherits[d].theme != NULL)
2517914d74bSmrg    {
2524d939ec7Smrg	/*
2534d939ec7Smrg	 * Scan this theme
2544d939ec7Smrg	 */
2554d939ec7Smrg	for (path = XcursorLibraryPath ();
2564d939ec7Smrg	     path && f == NULL;
2574d939ec7Smrg	     path = _XcursorNextPath (path))
2587914d74bSmrg	{
2594d939ec7Smrg	    dir = _XcursorBuildThemeDir (path, inherits[d].theme);
2604d939ec7Smrg	    if (dir)
2617914d74bSmrg	    {
2624d939ec7Smrg		full = _XcursorBuildFullname (dir, "cursors", name);
2637914d74bSmrg		if (full)
2647914d74bSmrg		{
2650ea508b1Smrg		    f = fopen (full, "r" FOPEN_CLOEXEC);
2667914d74bSmrg		    free (full);
2677914d74bSmrg		}
2684d939ec7Smrg		if (!f && inherits[d + 1].line == NULL)
2694d939ec7Smrg		{
2704d939ec7Smrg		    if (d + 1 >= MAX_INHERITS_DEPTH)
2714d939ec7Smrg		    {
2724d939ec7Smrg			free (dir);
2734d939ec7Smrg			goto finish;
2744d939ec7Smrg		    }
2754d939ec7Smrg		    full = _XcursorBuildFullname (dir, "", "index.theme");
2764d939ec7Smrg		    if (full)
2774d939ec7Smrg		    {
2784d939ec7Smrg			inherits[d + 1].line = _XcursorThemeInherits (full);
2794d939ec7Smrg			inherits[d + 1].theme = inherits[d + 1].line;
2804d939ec7Smrg			free (full);
2814d939ec7Smrg		    }
2824d939ec7Smrg		}
2834d939ec7Smrg		free (dir);
2847914d74bSmrg	    }
2857914d74bSmrg	}
2864d939ec7Smrg
2874d939ec7Smrg	d++;
2884d939ec7Smrg	while (d > 0 && inherits[d].theme == NULL)
2894d939ec7Smrg	{
2904d939ec7Smrg	    free (inherits[d].line);
2914d939ec7Smrg	    inherits[d].line = NULL;
2924d939ec7Smrg
2934d939ec7Smrg	    if (--d == 0)
2944d939ec7Smrg		inherits[d].theme = NULL;
2954d939ec7Smrg	    else
2964d939ec7Smrg		inherits[d].theme = _XcursorNextPath (inherits[d].theme);
2974d939ec7Smrg	}
2984d939ec7Smrg
2994d939ec7Smrg	/*
3004d939ec7Smrg	 * Detect and break self reference loop early on.
3014d939ec7Smrg	 */
3024d939ec7Smrg	if (inherits[d].theme != NULL && strcmp (inherits[d].theme, theme) == 0)
3034d939ec7Smrg	    break;
3047914d74bSmrg    }
3054d939ec7Smrg
3064d939ec7Smrgfinish:
3074d939ec7Smrg    for (d = 1; d <= MAX_INHERITS_DEPTH; d++)
3084d939ec7Smrg	free (inherits[d].line);
3094d939ec7Smrg
3107914d74bSmrg    return f;
3117914d74bSmrg}
3127914d74bSmrg
3137914d74bSmrgXcursorImage *
3147914d74bSmrgXcursorLibraryLoadImage (const char *file, const char *theme, int size)
3157914d74bSmrg{
3167914d74bSmrg    FILE	    *f = NULL;
3177914d74bSmrg    XcursorImage    *image = NULL;
3187914d74bSmrg
3196b7436aeSmrg    enterFunc((T_CALLED(XcursorLibraryLoadImage) "(\"%s\",\"%s\", %d)\n",
3206b7436aeSmrg	      NonNull(file), NonNull(theme), size));
3216b7436aeSmrg
3227914d74bSmrg    if (!file)
3236b7436aeSmrg	returnAddr(NULL);
3247914d74bSmrg
3257914d74bSmrg    if (theme)
3267914d74bSmrg	f = XcursorScanTheme (theme, file);
3277914d74bSmrg    if (!f)
3287914d74bSmrg	f = XcursorScanTheme ("default", file);
3294d939ec7Smrg    if (f != NULL && f != XCURSOR_SCAN_CORE)
3307914d74bSmrg    {
3317914d74bSmrg	image = XcursorFileLoadImage (f, size);
3327914d74bSmrg	fclose (f);
3337914d74bSmrg    }
3346b7436aeSmrg    returnAddr(image);
3356b7436aeSmrg}
3366b7436aeSmrg
3376b7436aeSmrgstatic XcursorImages *
3386b7436aeSmrg_XcursorLibraryLoadImages (Display *dpy, const char *file)
3396b7436aeSmrg{
3406b7436aeSmrg    int		    size = XcursorGetDefaultSize (dpy);
3416b7436aeSmrg    char	    *theme = XcursorGetTheme (dpy);
3426b7436aeSmrg    XcursorBool     resized = XcursorGetResizable (dpy);
3436b7436aeSmrg    FILE	    *f = NULL;
3446b7436aeSmrg    XcursorImages   *images = NULL;
3456b7436aeSmrg
3466b7436aeSmrg    if (!file)
3476b7436aeSmrg	return NULL;
3486b7436aeSmrg
3496b7436aeSmrg    if (theme)
3506b7436aeSmrg	f = XcursorScanTheme (theme, file);
3516b7436aeSmrg    if (!f)
3526b7436aeSmrg	f = XcursorScanTheme ("default", file);
3536b7436aeSmrg    if (f != NULL && f != XCURSOR_SCAN_CORE)
3546b7436aeSmrg    {
3556b7436aeSmrg	images = _XcursorFileLoadImages (f, size, resized);
3566b7436aeSmrg	if (images)
3576b7436aeSmrg	    XcursorImagesSetName (images, file);
3586b7436aeSmrg	fclose (f);
3596b7436aeSmrg    }
3606b7436aeSmrg    return images;
3617914d74bSmrg}
3627914d74bSmrg
3637914d74bSmrgXcursorImages *
3647914d74bSmrgXcursorLibraryLoadImages (const char *file, const char *theme, int size)
3657914d74bSmrg{
3667914d74bSmrg    FILE	    *f = NULL;
3677914d74bSmrg    XcursorImages   *images = NULL;
3687914d74bSmrg
3696b7436aeSmrg    enterFunc((T_CALLED(XcursorLibraryLoadImages) "(\"%s\", \"%s\", %d)\n",
3706b7436aeSmrg	      NonNull(file), NonNull(theme), size));
3716b7436aeSmrg
3727914d74bSmrg    if (!file)
3736b7436aeSmrg	returnAddr(NULL);
3747914d74bSmrg
3757914d74bSmrg    if (theme)
3767914d74bSmrg	f = XcursorScanTheme (theme, file);
3777914d74bSmrg    if (!f)
3787914d74bSmrg	f = XcursorScanTheme ("default", file);
3794d939ec7Smrg    if (f != NULL && f != XCURSOR_SCAN_CORE)
3807914d74bSmrg    {
3817914d74bSmrg	images = XcursorFileLoadImages (f, size);
3827914d74bSmrg	if (images)
3837914d74bSmrg	    XcursorImagesSetName (images, file);
3847914d74bSmrg	fclose (f);
3857914d74bSmrg    }
3866b7436aeSmrg    returnAddr(images);
3877914d74bSmrg}
3887914d74bSmrg
3897914d74bSmrgCursor
3907914d74bSmrgXcursorLibraryLoadCursor (Display *dpy, const char *file)
3917914d74bSmrg{
3926b7436aeSmrg    XcursorImages   *images;
3936b7436aeSmrg    Cursor	    cursor = 0;
3946b7436aeSmrg
3956b7436aeSmrg    enterFunc((T_CALLED(XcursorLibraryLoadCursor) "(%p, \"%s\")\n",
3966b7436aeSmrg	      (void*)dpy, NonNull(file)));
3977914d74bSmrg
3987914d74bSmrg    if (!file)
3996b7436aeSmrg	returnLong(cursor);
400e6d5e4e0Smrg
4016b7436aeSmrg    images = _XcursorLibraryLoadImages (dpy, file);
4027914d74bSmrg    if (!images)
4037914d74bSmrg    {
4047914d74bSmrg	int id = XcursorLibraryShape (file);
4057914d74bSmrg
4067914d74bSmrg	if (id >= 0)
4076b7436aeSmrg	    cursor = _XcursorCreateFontCursor (dpy, (unsigned) id);
4087914d74bSmrg    }
4096b7436aeSmrg    else
4106b7436aeSmrg    {
4116b7436aeSmrg	cursor = XcursorImagesLoadCursor (dpy, images);
4126b7436aeSmrg	XcursorImagesDestroy (images);
4137914d74bSmrg#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2
4146b7436aeSmrg	XFixesSetCursorName (dpy, cursor, file);
4157914d74bSmrg#endif
4166b7436aeSmrg    }
4176b7436aeSmrg    returnLong(cursor);
4187914d74bSmrg}
4197914d74bSmrg
4207914d74bSmrgXcursorCursors *
4217914d74bSmrgXcursorLibraryLoadCursors (Display *dpy, const char *file)
4227914d74bSmrg{
4236b7436aeSmrg    XcursorImages   *images;
4247914d74bSmrg    XcursorCursors  *cursors;
425e6d5e4e0Smrg
4266b7436aeSmrg    enterFunc((T_CALLED(XcursorLibraryLoadCursors) "(%p, \"%s\")\n",
4276b7436aeSmrg	      (void*)dpy, NonNull(file)));
4286b7436aeSmrg
4297914d74bSmrg    if (!file)
4306b7436aeSmrg	returnAddr(NULL);
431e6d5e4e0Smrg
4326b7436aeSmrg    images = _XcursorLibraryLoadImages (dpy, file);
4337914d74bSmrg    if (!images)
4347914d74bSmrg    {
4357914d74bSmrg	int id = XcursorLibraryShape (file);
4367914d74bSmrg
4377914d74bSmrg	if (id >= 0)
4387914d74bSmrg	{
4397914d74bSmrg	    cursors = XcursorCursorsCreate (dpy, 1);
4407914d74bSmrg	    if (cursors)
4417914d74bSmrg	    {
4424d939ec7Smrg		cursors->cursors[0] = _XcursorCreateFontCursor (dpy, (unsigned) id);
4437914d74bSmrg		if (cursors->cursors[0] == None)
4447914d74bSmrg		{
4457914d74bSmrg		    XcursorCursorsDestroy (cursors);
4467914d74bSmrg		    cursors = NULL;
4477914d74bSmrg		}
4487914d74bSmrg		else
4497914d74bSmrg		    cursors->ncursor = 1;
4507914d74bSmrg	    }
4517914d74bSmrg	}
4527914d74bSmrg	else
4537914d74bSmrg	    cursors = NULL;
4547914d74bSmrg    }
4557914d74bSmrg    else
4567914d74bSmrg    {
4577914d74bSmrg	cursors = XcursorImagesLoadCursors (dpy, images);
4587914d74bSmrg	XcursorImagesDestroy (images);
4597914d74bSmrg    }
4606b7436aeSmrg    returnAddr(cursors);
4617914d74bSmrg}
4627914d74bSmrg
4637914d74bSmrgstatic const char _XcursorStandardNames[] =
4647914d74bSmrg	"X_cursor\0"
4657914d74bSmrg	"arrow\0"
4667914d74bSmrg	"based_arrow_down\0"
4677914d74bSmrg	"based_arrow_up\0"
4687914d74bSmrg	"boat\0"
4697914d74bSmrg	"bogosity\0"
4707914d74bSmrg	"bottom_left_corner\0"
4717914d74bSmrg	"bottom_right_corner\0"
4727914d74bSmrg	"bottom_side\0"
4737914d74bSmrg	"bottom_tee\0"
4747914d74bSmrg	"box_spiral\0"
4757914d74bSmrg	"center_ptr\0"
4767914d74bSmrg	"circle\0"
4777914d74bSmrg	"clock\0"
4787914d74bSmrg	"coffee_mug\0"
4797914d74bSmrg	"cross\0"
4807914d74bSmrg	"cross_reverse\0"
4817914d74bSmrg	"crosshair\0"
4827914d74bSmrg	"diamond_cross\0"
4837914d74bSmrg	"dot\0"
4847914d74bSmrg	"dotbox\0"
4857914d74bSmrg	"double_arrow\0"
4867914d74bSmrg	"draft_large\0"
4877914d74bSmrg	"draft_small\0"
4887914d74bSmrg	"draped_box\0"
4897914d74bSmrg	"exchange\0"
4907914d74bSmrg	"fleur\0"
4917914d74bSmrg	"gobbler\0"
4927914d74bSmrg	"gumby\0"
4937914d74bSmrg	"hand1\0"
4947914d74bSmrg	"hand2\0"
4957914d74bSmrg	"heart\0"
4967914d74bSmrg	"icon\0"
4977914d74bSmrg	"iron_cross\0"
4987914d74bSmrg	"left_ptr\0"
4997914d74bSmrg	"left_side\0"
5007914d74bSmrg	"left_tee\0"
5017914d74bSmrg	"leftbutton\0"
5027914d74bSmrg	"ll_angle\0"
5037914d74bSmrg	"lr_angle\0"
5047914d74bSmrg	"man\0"
5057914d74bSmrg	"middlebutton\0"
5067914d74bSmrg	"mouse\0"
5077914d74bSmrg	"pencil\0"
5087914d74bSmrg	"pirate\0"
5097914d74bSmrg	"plus\0"
5107914d74bSmrg	"question_arrow\0"
5117914d74bSmrg	"right_ptr\0"
5127914d74bSmrg	"right_side\0"
5137914d74bSmrg	"right_tee\0"
5147914d74bSmrg	"rightbutton\0"
5157914d74bSmrg	"rtl_logo\0"
5167914d74bSmrg	"sailboat\0"
5177914d74bSmrg	"sb_down_arrow\0"
5187914d74bSmrg	"sb_h_double_arrow\0"
5197914d74bSmrg	"sb_left_arrow\0"
5207914d74bSmrg	"sb_right_arrow\0"
5217914d74bSmrg	"sb_up_arrow\0"
5227914d74bSmrg	"sb_v_double_arrow\0"
5237914d74bSmrg	"shuttle\0"
5247914d74bSmrg	"sizing\0"
5257914d74bSmrg	"spider\0"
5267914d74bSmrg	"spraycan\0"
5277914d74bSmrg	"star\0"
5287914d74bSmrg	"target\0"
5297914d74bSmrg	"tcross\0"
5307914d74bSmrg	"top_left_arrow\0"
5317914d74bSmrg	"top_left_corner\0"
5327914d74bSmrg	"top_right_corner\0"
5337914d74bSmrg	"top_side\0"
5347914d74bSmrg	"top_tee\0"
5357914d74bSmrg	"trek\0"
5367914d74bSmrg	"ul_angle\0"
5377914d74bSmrg	"umbrella\0"
5387914d74bSmrg	"ur_angle\0"
5397914d74bSmrg	"watch\0"
5407914d74bSmrg	"xterm";
5417914d74bSmrg
5427914d74bSmrgstatic const unsigned short _XcursorStandardNameOffsets[] = {
5437914d74bSmrg	0, 9, 15, 32, 47, 52, 61, 80, 100, 112, 123, 134, 145, 152, 158,
5447914d74bSmrg	169, 175, 189, 199, 213, 217, 224, 237, 249, 261, 272, 281, 287,
5457914d74bSmrg	295, 301, 307, 313, 319, 324, 335, 344, 354, 363, 374, 383, 392,
5467914d74bSmrg	396, 409, 415, 422, 429, 434, 449, 459, 470, 480, 492, 501, 510,
5477914d74bSmrg	524, 542, 556, 571, 583, 601, 609, 616, 623, 632, 637, 644, 651,
5487914d74bSmrg	666, 682, 699, 708, 716, 721, 730, 739, 748, 754
5497914d74bSmrg};
5507914d74bSmrg
5517914d74bSmrg#define NUM_STANDARD_NAMES  (sizeof _XcursorStandardNameOffsets / sizeof _XcursorStandardNameOffsets[0])
5527914d74bSmrg
5537914d74bSmrg#define STANDARD_NAME(id) \
5547914d74bSmrg    _XcursorStandardNames + _XcursorStandardNameOffsets[id]
5557914d74bSmrg
5567914d74bSmrgXcursorImage *
5577914d74bSmrgXcursorShapeLoadImage (unsigned int shape, const char *theme, int size)
5587914d74bSmrg{
5597914d74bSmrg    unsigned int    id = shape >> 1;
5606b7436aeSmrg    XcursorImage   *result = NULL;
5616b7436aeSmrg
5626b7436aeSmrg    enterFunc((T_CALLED(XcursorShapeLoadImage) "(%u, \"%s\", %d)\n",
5636b7436aeSmrg	      shape, NonNull(theme), size));
5647914d74bSmrg
5657914d74bSmrg    if (id < NUM_STANDARD_NAMES)
5666b7436aeSmrg	result = XcursorLibraryLoadImage (STANDARD_NAME (id), theme, size);
5676b7436aeSmrg
5686b7436aeSmrg    returnAddr(result);
5696b7436aeSmrg}
5706b7436aeSmrg
5716b7436aeSmrgXcursorImages *
5726b7436aeSmrg_XcursorShapeLoadImages (Display *dpy, unsigned int shape)
5736b7436aeSmrg{
5746b7436aeSmrg    unsigned int    id = shape >> 1;
5756b7436aeSmrg    XcursorImages  *result = NULL;
5766b7436aeSmrg
5776b7436aeSmrg    enterFunc((T_CALLED(_XcursorShapeLoadImages) "(%p, %u)\n",
5786b7436aeSmrg	      (void*)dpy, shape));
5796b7436aeSmrg
5806b7436aeSmrg    if (id < NUM_STANDARD_NAMES)
5816b7436aeSmrg	result = _XcursorLibraryLoadImages (dpy, STANDARD_NAME (id));
5826b7436aeSmrg
5836b7436aeSmrg    returnAddr(result);
5847914d74bSmrg}
5857914d74bSmrg
5867914d74bSmrgXcursorImages *
5877914d74bSmrgXcursorShapeLoadImages (unsigned int shape, const char *theme, int size)
5887914d74bSmrg{
5897914d74bSmrg    unsigned int    id = shape >> 1;
5906b7436aeSmrg    XcursorImages  *result = NULL;
5916b7436aeSmrg
5926b7436aeSmrg    enterFunc((T_CALLED(XcursorShapeLoadImages) "(%u, \"%s\", %d)\n",
5936b7436aeSmrg	      shape, NonNull(theme), size));
5947914d74bSmrg
5957914d74bSmrg    if (id < NUM_STANDARD_NAMES)
5966b7436aeSmrg	result = XcursorLibraryLoadImages (STANDARD_NAME (id), theme, size);
5976b7436aeSmrg
5986b7436aeSmrg    returnAddr(result);
5997914d74bSmrg}
6007914d74bSmrg
6017914d74bSmrgCursor
6027914d74bSmrgXcursorShapeLoadCursor (Display *dpy, unsigned int shape)
6037914d74bSmrg{
6047914d74bSmrg    unsigned int    id = shape >> 1;
6056b7436aeSmrg    Cursor          result = None;
6066b7436aeSmrg
6076b7436aeSmrg    enterFunc((T_CALLED(XcursorShapeLoadCursor) "(%p, %u)\n",
6086b7436aeSmrg	      (void*)dpy, shape));
6097914d74bSmrg
6107914d74bSmrg    if (id < NUM_STANDARD_NAMES)
6116b7436aeSmrg	result = XcursorLibraryLoadCursor (dpy, STANDARD_NAME (id));
6126b7436aeSmrg
6136b7436aeSmrg    returnLong(result);
6147914d74bSmrg}
6157914d74bSmrg
6167914d74bSmrgXcursorCursors *
6177914d74bSmrgXcursorShapeLoadCursors (Display *dpy, unsigned int shape)
6187914d74bSmrg{
6197914d74bSmrg    unsigned int    id = shape >> 1;
6206b7436aeSmrg    XcursorCursors *result = NULL;
6216b7436aeSmrg
6226b7436aeSmrg    enterFunc((T_CALLED(XcursorShapeLoadCursors) "(%p, %u)\n",
6236b7436aeSmrg	      (void*)dpy, shape));
6247914d74bSmrg
6257914d74bSmrg    if (id < NUM_STANDARD_NAMES)
6266b7436aeSmrg	result = XcursorLibraryLoadCursors (dpy, STANDARD_NAME (id));
6276b7436aeSmrg
6286b7436aeSmrg    returnAddr(result);
6297914d74bSmrg}
6307914d74bSmrg
6317914d74bSmrgint
6327914d74bSmrgXcursorLibraryShape (const char *library)
6337914d74bSmrg{
6347914d74bSmrg    int	low, high;
6357914d74bSmrg
6366b7436aeSmrg    enterFunc((T_CALLED(XcursorLibraryShape) "(%s)\n", NonNull(library)));
6376b7436aeSmrg
6387914d74bSmrg    low = 0;
6397914d74bSmrg    high = NUM_STANDARD_NAMES - 1;
6407914d74bSmrg    while (low < high - 1)
6417914d74bSmrg    {
6424d939ec7Smrg	int mid = (low + high) >> 1;
6434d939ec7Smrg	int c = strcmp (library, STANDARD_NAME (mid));
6447914d74bSmrg	if (c == 0)
6456b7436aeSmrg	    returnCode(mid << 1);
6467914d74bSmrg	if (c > 0)
6477914d74bSmrg	    low = mid;
6487914d74bSmrg	else
6497914d74bSmrg	    high = mid;
6507914d74bSmrg    }
6517914d74bSmrg    while (low <= high)
6527914d74bSmrg    {
6537914d74bSmrg	if (!strcmp (library, STANDARD_NAME (low)))
6547914d74bSmrg	    return (low << 1);
6557914d74bSmrg	low++;
6567914d74bSmrg    }
6576b7436aeSmrg    returnCode(-1);
6587914d74bSmrg}
659