display.c revision c63293b5
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Keith Packard makes no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "xcursorint.h"
24#include <X11/Xlibint.h>
25#include <ctype.h>
26
27static XcursorDisplayInfo *_XcursorDisplayInfo;
28
29static void
30_XcursorFreeDisplayInfo (XcursorDisplayInfo *info)
31{
32    if (info->theme)
33	free (info->theme);
34
35    if (info->theme_from_config)
36	free (info->theme_from_config);
37
38    while (info->fonts)
39    {
40      XcursorFontInfo *fi = info->fonts;
41      info->fonts = fi->next;
42      free (fi);
43    }
44
45    free (info);
46}
47
48static int
49_XcursorCloseDisplay (Display *dpy, XExtCodes *codes)
50{
51    XcursorDisplayInfo  *info, **prev;
52
53    /*
54     * Unhook from the global list
55     */
56    _XLockMutex (_Xglobal_lock);
57    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
58	if (info->display == dpy)
59	{
60            *prev = info->next;
61	    break;
62	}
63    _XUnlockMutex (_Xglobal_lock);
64
65    if (info)
66	_XcursorFreeDisplayInfo (info);
67    return 0;
68}
69
70static int
71_XcursorDefaultParseBool (char *v)
72{
73    char    c0, c1;
74
75    c0 = *v;
76    if (isupper ((int)c0))
77	c0 = tolower (c0);
78    if (c0 == 't' || c0 == 'y' || c0 == '1')
79	return 1;
80    if (c0 == 'f' || c0 == 'n' || c0 == '0')
81	return 0;
82    if (c0 == 'o')
83    {
84	c1 = v[1];
85	if (isupper ((int)c1))
86	    c1 = tolower (c1);
87	if (c1 == 'n')
88	    return 1;
89	if (c1 == 'f')
90	    return 0;
91    }
92    return -1;
93}
94
95XcursorDisplayInfo *
96_XcursorGetDisplayInfo (Display *dpy)
97{
98    XcursorDisplayInfo	*info, **prev, *old;
99    int			event_base, error_base;
100    int			major, minor;
101    char		*v;
102    int			i;
103
104    _XLockMutex (_Xglobal_lock);
105    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
106    {
107	if (info->display == dpy)
108	{
109	    /*
110	     * MRU the list
111	     */
112	    if (prev != &_XcursorDisplayInfo)
113	    {
114		*prev = info->next;
115		info->next = _XcursorDisplayInfo;
116		_XcursorDisplayInfo = info;
117	    }
118	    break;
119	}
120    }
121    _XUnlockMutex (_Xglobal_lock);
122    if (info)
123        return info;
124    info = (XcursorDisplayInfo *) malloc (sizeof (XcursorDisplayInfo));
125    if (!info)
126	return NULL;
127    info->next = NULL;
128    info->display = dpy;
129
130    info->codes = XAddExtension (dpy);
131    if (!info->codes)
132    {
133	free (info);
134	return NULL;
135    }
136    (void) XESetCloseDisplay (dpy, info->codes->extension, _XcursorCloseDisplay);
137
138    /*
139     * Check whether the display supports the Render CreateCursor request
140     */
141    info->has_render_cursor = XcursorFalse;
142    info->has_anim_cursor = XcursorFalse;
143    if (XRenderQueryExtension (dpy, &event_base, &error_base) &&
144	XRenderQueryVersion (dpy, &major, &minor))
145    {
146	if (major > 0 || minor >= 5)
147	{
148	    info->has_render_cursor = XcursorTrue;
149	    v = getenv ("XCURSOR_CORE");
150	    if (!v)
151		v = XGetDefault (dpy, "Xcursor", "core");
152	    if (v && _XcursorDefaultParseBool (v) == 1)
153		info->has_render_cursor = XcursorFalse;
154	}
155	if (info->has_render_cursor && (major > 0 || minor >= 8))
156	{
157	    info->has_anim_cursor = XcursorTrue;
158	    v = getenv ("XCURSOR_ANIM");
159	    if (!v)
160		v = XGetDefault (dpy, "Xcursor", "anim");
161	    if (v && _XcursorDefaultParseBool (v) == 0)
162		info->has_anim_cursor = XcursorFalse;
163	}
164    }
165
166    info->size = 0;
167
168    /*
169     * Get desired cursor size
170     */
171    v = getenv ("XCURSOR_SIZE");
172    if (!v)
173	v = XGetDefault (dpy, "Xcursor", "size");
174    if (v)
175	info->size = atoi (v);
176
177    /*
178     * Use the Xft size to guess a size; make cursors 16 "points" tall
179     */
180    if (info->size == 0)
181    {
182	int dpi = 0;
183	v = XGetDefault (dpy, "Xft", "dpi");
184	if (v)
185	    dpi = atoi (v);
186	if (dpi)
187	    info->size = dpi * 16 / 72;
188    }
189
190    /*
191     * Use display size to guess a size
192     */
193    if (info->size == 0)
194    {
195	int dim;
196
197	if (DisplayHeight (dpy, DefaultScreen (dpy)) <
198	    DisplayWidth (dpy, DefaultScreen (dpy)))
199	    dim = DisplayHeight (dpy, DefaultScreen (dpy));
200	else
201	    dim = DisplayWidth (dpy, DefaultScreen (dpy));
202	/*
203	 * 16 pixels on a display of dimension 768
204	 */
205	info->size = dim / 48;
206    }
207
208    info->theme = NULL;
209    info->theme_from_config = NULL;
210
211    /*
212     * Get the desired theme
213     */
214    v = getenv ("XCURSOR_THEME");
215    if (!v)
216	v = XGetDefault (dpy, "Xcursor", "theme");
217    if (v)
218    {
219	int len;
220
221	len = strlen (v) + 1;
222
223	info->theme = malloc (len);
224	if (info->theme)
225	    strcpy (info->theme, v);
226
227	info->theme_from_config = malloc (len);
228	if (info->theme_from_config)
229	    strcpy (info->theme_from_config, v);
230    }
231
232    /*
233     * Get the desired dither
234     */
235    info->dither = XcursorDitherThreshold;
236    v = getenv ("XCURSOR_DITHER");
237    if (!v)
238	v = XGetDefault (dpy, "Xcursor", "dither");
239    if (v)
240    {
241	if (!strcmp (v, "threshold"))
242	    info->dither = XcursorDitherThreshold;
243	if (!strcmp (v, "median"))
244	    info->dither = XcursorDitherMedian;
245	if (!strcmp (v, "ordered"))
246	    info->dither = XcursorDitherOrdered;
247	if (!strcmp (v, "diffuse"))
248	    info->dither = XcursorDitherDiffuse;
249    }
250
251    info->theme_core = False;
252    /*
253     * Find out if core cursors should
254     * be themed
255     */
256    v = getenv ("XCURSOR_THEME_CORE");
257    if (!v)
258	v = XGetDefault (dpy, "Xcursor", "theme_core");
259    if (v)
260    {
261	i = _XcursorDefaultParseBool (v);
262	if (i >= 0)
263	    info->theme_core = i;
264    }
265
266    info->fonts = NULL;
267    for (i = 0; i < NUM_BITMAPS; i++)
268	info->bitmaps[i].bitmap = None;
269
270    /*
271     * Link new info info list, making sure another
272     * thread hasn't inserted something into the list while
273     * this one was busy setting up the data
274     */
275    _XLockMutex (_Xglobal_lock);
276    for (old = _XcursorDisplayInfo; old; old = old->next)
277	if (old->display == dpy)
278	    break;
279    if (old)
280    {
281	_XcursorFreeDisplayInfo (info);
282	info = old;
283    }
284    else
285    {
286	info->next = _XcursorDisplayInfo;
287	_XcursorDisplayInfo = info;
288    }
289    _XUnlockMutex (_Xglobal_lock);
290
291    return info;
292}
293
294XcursorBool
295XcursorSupportsARGB (Display *dpy)
296{
297    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
298
299    return info && info->has_render_cursor;
300}
301
302XcursorBool
303XcursorSupportsAnim (Display *dpy)
304{
305    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
306
307    return info && info->has_anim_cursor;
308}
309
310XcursorBool
311XcursorSetDefaultSize (Display *dpy, int size)
312{
313    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
314
315    if (!info)
316	return XcursorFalse;
317    info->size = size;
318    return XcursorTrue;
319}
320
321int
322XcursorGetDefaultSize (Display *dpy)
323{
324    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
325
326    if (!info)
327	return 0;
328    return info->size;
329}
330
331XcursorBool
332XcursorSetTheme (Display *dpy, const char *theme)
333{
334    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
335    char		*copy;
336
337    if (!info)
338	return XcursorFalse;
339
340    if (!theme)
341	theme = info->theme_from_config;
342
343    if (theme)
344    {
345	copy = malloc (strlen (theme) + 1);
346	if (!copy)
347	    return XcursorFalse;
348	strcpy (copy, theme);
349    }
350    else
351	copy = NULL;
352    if (info->theme)
353	free (info->theme);
354    info->theme = copy;
355    return XcursorTrue;
356}
357
358char *
359XcursorGetTheme (Display *dpy)
360{
361    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
362
363    if (!info)
364	return NULL;
365    return info->theme;
366}
367
368XcursorBool
369XcursorGetThemeCore (Display *dpy)
370{
371    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
372
373    if (!info)
374	return XcursorFalse;
375    return info->theme_core;
376
377}
378
379XcursorBool
380XcursorSetThemeCore (Display *dpy, XcursorBool theme_core)
381{
382    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
383
384    if (!info)
385	return XcursorFalse;
386    info->theme_core = theme_core;
387    return XcursorTrue;
388}
389