library.c revision 7914d74b
1/*
2 * $Id: library.c,v 1.1.1.1 2008/07/30 02:45:53 mrg Exp $
3 *
4 * Copyright © 2002 Keith Packard
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  Keith Packard makes no
13 * representations about the suitability of this software for any purpose.  It
14 * is provided "as is" without express or implied warranty.
15 *
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25#include "xcursorint.h"
26#include <stdlib.h>
27#include <string.h>
28
29#ifndef ICONDIR
30#define ICONDIR "/usr/X11R6/lib/X11/icons"
31#endif
32
33#ifndef XCURSORPATH
34#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR
35#endif
36
37const char *
38XcursorLibraryPath (void)
39{
40    static const char	*path;
41
42    if (!path)
43    {
44	path = getenv ("XCURSOR_PATH");
45	if (!path)
46	    path = XCURSORPATH;
47    }
48    return path;
49}
50
51static  void
52_XcursorAddPathElt (char *path, const char *elt, int len)
53{
54    int	    pathlen = strlen (path);
55
56    /* append / if the path doesn't currently have one */
57    if (path[0] == '\0' || path[pathlen - 1] != '/')
58    {
59	strcat (path, "/");
60	pathlen++;
61    }
62    if (len == -1)
63	len = strlen (elt);
64    /* strip leading slashes */
65    while (len && elt[0] == '/')
66    {
67	elt++;
68	len--;
69    }
70    strncpy (path + pathlen, elt, len);
71    path[pathlen + len] = '\0';
72}
73
74static char *
75_XcursorBuildThemeDir (const char *dir, const char *theme)
76{
77    const char	    *colon;
78    const char	    *tcolon;
79    char	    *full;
80    char	    *home;
81    int		    dirlen;
82    int		    homelen;
83    int		    themelen;
84    int		    len;
85
86    if (!dir || !theme)
87        return NULL;
88
89    colon = strchr (dir, ':');
90    if (!colon)
91	colon = dir + strlen (dir);
92
93    dirlen = colon - dir;
94
95    tcolon = strchr (theme, ':');
96    if (!tcolon)
97	tcolon = theme + strlen (theme);
98
99    themelen = tcolon - theme;
100
101    home = NULL;
102    homelen = 0;
103    if (*dir == '~')
104    {
105	home = getenv ("HOME");
106	if (!home)
107	    return NULL;
108	homelen = strlen (home);
109	dir++;
110	dirlen--;
111    }
112
113    /*
114     * add space for any needed directory separators, one per component,
115     * and one for the trailing null
116     */
117    len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
118
119    full = malloc (len);
120    if (!full)
121	return NULL;
122    full[0] = '\0';
123
124    if (home)
125	_XcursorAddPathElt (full, home, -1);
126    _XcursorAddPathElt (full, dir, dirlen);
127    _XcursorAddPathElt (full, theme, themelen);
128    return full;
129}
130
131static char *
132_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
133{
134    char    *full;
135
136    if (!dir || !subdir || !file)
137        return NULL;
138
139    full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
140    if (!full)
141	return NULL;
142    full[0] = '\0';
143    _XcursorAddPathElt (full, dir, -1);
144    _XcursorAddPathElt (full, subdir, -1);
145    _XcursorAddPathElt (full, file, -1);
146    return full;
147}
148
149static const char *
150_XcursorNextPath (const char *path)
151{
152    char    *colon = strchr (path, ':');
153
154    if (!colon)
155	return NULL;
156    return colon + 1;
157}
158
159#define XcursorWhite(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
160#define XcursorSep(c) ((c) == ';' || (c) == ',')
161
162static char *
163_XcursorThemeInherits (const char *full)
164{
165    char    line[8192];
166    char    *result = NULL;
167    FILE    *f;
168
169    if (!full)
170        return NULL;
171
172    f = fopen (full, "r");
173    if (f)
174    {
175	while (fgets (line, sizeof (line), f))
176	{
177	    if (!strncmp (line, "Inherits", 8))
178	    {
179		char    *l = line + 8;
180		char    *r;
181		while (*l == ' ') l++;
182		if (*l != '=') continue;
183		l++;
184		while (*l == ' ') l++;
185		result = malloc (strlen (l));
186		if (result)
187		{
188		    r = result;
189		    while (*l)
190		    {
191			while (XcursorSep(*l) || XcursorWhite (*l)) l++;
192			if (!*l)
193			    break;
194			if (r != result)
195			    *r++ = ':';
196			while (*l && !XcursorWhite(*l) &&
197			       !XcursorSep(*l))
198			    *r++ = *l++;
199		    }
200		    *r++ = '\0';
201		}
202		break;
203	    }
204	}
205	fclose (f);
206    }
207    return result;
208}
209
210#define XCURSOR_SCAN_CORE   ((FILE *) 1)
211
212static FILE *
213XcursorScanTheme (const char *theme, const char *name)
214{
215    FILE	*f = NULL;
216    char	*full;
217    char	*dir;
218    const char  *path;
219    char	*inherits = NULL;
220    const char	*i;
221
222    if (!theme || !name)
223        return NULL;
224
225    /*
226     * XCURSOR_CORE_THEME is a magic name; cursors from the core set
227     * are never found in any directory.  Instead, a magic value is
228     * returned which truncates any search so that overlying functions
229     * can switch to equivalent core cursors
230     */
231    if (!strcmp (theme, XCURSOR_CORE_THEME) && XcursorLibraryShape (name) >= 0)
232	return XCURSOR_SCAN_CORE;
233    /*
234     * Scan this theme
235     */
236    for (path = XcursorLibraryPath ();
237	 path && f == 0;
238	 path = _XcursorNextPath (path))
239    {
240	dir = _XcursorBuildThemeDir (path, theme);
241	if (dir)
242	{
243	    full = _XcursorBuildFullname (dir, "cursors", name);
244	    if (full)
245	    {
246		f = fopen (full, "r");
247		free (full);
248	    }
249	    if (!f && !inherits)
250	    {
251		full = _XcursorBuildFullname (dir, "", "index.theme");
252		if (full)
253		{
254		    inherits = _XcursorThemeInherits (full);
255		    free (full);
256		}
257	    }
258	    free (dir);
259	}
260    }
261    /*
262     * Recurse to scan inherited themes
263     */
264    for (i = inherits; i && f == 0; i = _XcursorNextPath (i))
265	f = XcursorScanTheme (i, name);
266    if (inherits != NULL)
267	free (inherits);
268    return f;
269}
270
271XcursorImage *
272XcursorLibraryLoadImage (const char *file, const char *theme, int size)
273{
274    FILE	    *f = NULL;
275    XcursorImage    *image = NULL;
276
277    if (!file)
278        return NULL;
279
280    if (theme)
281	f = XcursorScanTheme (theme, file);
282    if (!f)
283	f = XcursorScanTheme ("default", file);
284    if (f == XCURSOR_SCAN_CORE)
285	return NULL;
286    if (f)
287    {
288	image = XcursorFileLoadImage (f, size);
289	fclose (f);
290    }
291    return image;
292}
293
294XcursorImages *
295XcursorLibraryLoadImages (const char *file, const char *theme, int size)
296{
297    FILE	    *f = NULL;
298    XcursorImages   *images = NULL;
299
300    if (!file)
301        return NULL;
302
303    if (theme)
304	f = XcursorScanTheme (theme, file);
305    if (!f)
306	f = XcursorScanTheme ("default", file);
307    if (f == XCURSOR_SCAN_CORE)
308	return NULL;
309    if (f)
310    {
311	images = XcursorFileLoadImages (f, size);
312	if (images)
313	    XcursorImagesSetName (images, file);
314	fclose (f);
315    }
316    return images;
317}
318
319Cursor
320XcursorLibraryLoadCursor (Display *dpy, const char *file)
321{
322    int		    size = XcursorGetDefaultSize (dpy);
323    char	    *theme = XcursorGetTheme (dpy);
324    XcursorImages   *images = XcursorLibraryLoadImages (file, theme, size);
325    Cursor	    cursor;
326
327    if (!file)
328        return 0;
329
330    if (!images)
331    {
332	int id = XcursorLibraryShape (file);
333
334	if (id >= 0)
335	    return _XcursorCreateFontCursor (dpy, id);
336	else
337	    return 0;
338    }
339    cursor = XcursorImagesLoadCursor (dpy, images);
340    XcursorImagesDestroy (images);
341#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2
342    XFixesSetCursorName (dpy, cursor, file);
343#endif
344    return cursor;
345}
346
347XcursorCursors *
348XcursorLibraryLoadCursors (Display *dpy, const char *file)
349{
350    int		    size = XcursorGetDefaultSize (dpy);
351    char	    *theme = XcursorGetTheme (dpy);
352    XcursorImages   *images = XcursorLibraryLoadImages (file, theme, size);
353    XcursorCursors  *cursors;
354
355    if (!file)
356        return NULL;
357
358    if (!images)
359    {
360	int id = XcursorLibraryShape (file);
361
362	if (id >= 0)
363	{
364	    cursors = XcursorCursorsCreate (dpy, 1);
365	    if (cursors)
366	    {
367		cursors->cursors[0] = _XcursorCreateFontCursor (dpy, id);
368		if (cursors->cursors[0] == None)
369		{
370		    XcursorCursorsDestroy (cursors);
371		    cursors = NULL;
372		}
373		else
374		    cursors->ncursor = 1;
375	    }
376	}
377	else
378	    cursors = NULL;
379    }
380    else
381    {
382	cursors = XcursorImagesLoadCursors (dpy, images);
383	XcursorImagesDestroy (images);
384    }
385    return cursors;
386}
387
388static const char _XcursorStandardNames[] =
389	"X_cursor\0"
390	"arrow\0"
391	"based_arrow_down\0"
392	"based_arrow_up\0"
393	"boat\0"
394	"bogosity\0"
395	"bottom_left_corner\0"
396	"bottom_right_corner\0"
397	"bottom_side\0"
398	"bottom_tee\0"
399	"box_spiral\0"
400	"center_ptr\0"
401	"circle\0"
402	"clock\0"
403	"coffee_mug\0"
404	"cross\0"
405	"cross_reverse\0"
406	"crosshair\0"
407	"diamond_cross\0"
408	"dot\0"
409	"dotbox\0"
410	"double_arrow\0"
411	"draft_large\0"
412	"draft_small\0"
413	"draped_box\0"
414	"exchange\0"
415	"fleur\0"
416	"gobbler\0"
417	"gumby\0"
418	"hand1\0"
419	"hand2\0"
420	"heart\0"
421	"icon\0"
422	"iron_cross\0"
423	"left_ptr\0"
424	"left_side\0"
425	"left_tee\0"
426	"leftbutton\0"
427	"ll_angle\0"
428	"lr_angle\0"
429	"man\0"
430	"middlebutton\0"
431	"mouse\0"
432	"pencil\0"
433	"pirate\0"
434	"plus\0"
435	"question_arrow\0"
436	"right_ptr\0"
437	"right_side\0"
438	"right_tee\0"
439	"rightbutton\0"
440	"rtl_logo\0"
441	"sailboat\0"
442	"sb_down_arrow\0"
443	"sb_h_double_arrow\0"
444	"sb_left_arrow\0"
445	"sb_right_arrow\0"
446	"sb_up_arrow\0"
447	"sb_v_double_arrow\0"
448	"shuttle\0"
449	"sizing\0"
450	"spider\0"
451	"spraycan\0"
452	"star\0"
453	"target\0"
454	"tcross\0"
455	"top_left_arrow\0"
456	"top_left_corner\0"
457	"top_right_corner\0"
458	"top_side\0"
459	"top_tee\0"
460	"trek\0"
461	"ul_angle\0"
462	"umbrella\0"
463	"ur_angle\0"
464	"watch\0"
465	"xterm";
466
467static const unsigned short _XcursorStandardNameOffsets[] = {
468	0, 9, 15, 32, 47, 52, 61, 80, 100, 112, 123, 134, 145, 152, 158,
469	169, 175, 189, 199, 213, 217, 224, 237, 249, 261, 272, 281, 287,
470	295, 301, 307, 313, 319, 324, 335, 344, 354, 363, 374, 383, 392,
471	396, 409, 415, 422, 429, 434, 449, 459, 470, 480, 492, 501, 510,
472	524, 542, 556, 571, 583, 601, 609, 616, 623, 632, 637, 644, 651,
473	666, 682, 699, 708, 716, 721, 730, 739, 748, 754
474};
475
476#define NUM_STANDARD_NAMES  (sizeof _XcursorStandardNameOffsets / sizeof _XcursorStandardNameOffsets[0])
477
478#define STANDARD_NAME(id) \
479    _XcursorStandardNames + _XcursorStandardNameOffsets[id]
480
481XcursorImage *
482XcursorShapeLoadImage (unsigned int shape, const char *theme, int size)
483{
484    unsigned int    id = shape >> 1;
485
486    if (id < NUM_STANDARD_NAMES)
487	return XcursorLibraryLoadImage (STANDARD_NAME (id), theme, size);
488    else
489	return NULL;
490}
491
492XcursorImages *
493XcursorShapeLoadImages (unsigned int shape, const char *theme, int size)
494{
495    unsigned int    id = shape >> 1;
496
497    if (id < NUM_STANDARD_NAMES)
498	return XcursorLibraryLoadImages (STANDARD_NAME (id), theme, size);
499    else
500	return NULL;
501}
502
503Cursor
504XcursorShapeLoadCursor (Display *dpy, unsigned int shape)
505{
506    unsigned int    id = shape >> 1;
507
508    if (id < NUM_STANDARD_NAMES)
509	return XcursorLibraryLoadCursor (dpy, STANDARD_NAME (id));
510    else
511	return 0;
512}
513
514XcursorCursors *
515XcursorShapeLoadCursors (Display *dpy, unsigned int shape)
516{
517    unsigned int    id = shape >> 1;
518
519    if (id < NUM_STANDARD_NAMES)
520	return XcursorLibraryLoadCursors (dpy, STANDARD_NAME (id));
521    else
522	return NULL;
523}
524
525int
526XcursorLibraryShape (const char *library)
527{
528    int	low, high;
529    int	mid;
530    int	c;
531
532    low = 0;
533    high = NUM_STANDARD_NAMES - 1;
534    while (low < high - 1)
535    {
536	mid = (low + high) >> 1;
537	c = strcmp (library, STANDARD_NAME (mid));
538	if (c == 0)
539	    return (mid << 1);
540	if (c > 0)
541	    low = mid;
542	else
543	    high = mid;
544    }
545    while (low <= high)
546    {
547	if (!strcmp (library, STANDARD_NAME (low)))
548	    return (low << 1);
549	low++;
550    }
551    return -1;
552}
553