display.c revision 9d0ccd10
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	info->theme = strdup (v);
220	info->theme_from_config = strdup (v);
221    }
222
223    /*
224     * Get the desired dither
225     */
226    info->dither = XcursorDitherThreshold;
227    v = getenv ("XCURSOR_DITHER");
228    if (!v)
229	v = XGetDefault (dpy, "Xcursor", "dither");
230    if (v)
231    {
232	if (!strcmp (v, "threshold"))
233	    info->dither = XcursorDitherThreshold;
234	if (!strcmp (v, "median"))
235	    info->dither = XcursorDitherMedian;
236	if (!strcmp (v, "ordered"))
237	    info->dither = XcursorDitherOrdered;
238	if (!strcmp (v, "diffuse"))
239	    info->dither = XcursorDitherDiffuse;
240    }
241
242    info->theme_core = False;
243    /*
244     * Find out if core cursors should
245     * be themed
246     */
247    v = getenv ("XCURSOR_THEME_CORE");
248    if (!v)
249	v = XGetDefault (dpy, "Xcursor", "theme_core");
250    if (v)
251    {
252	i = _XcursorDefaultParseBool (v);
253	if (i >= 0)
254	    info->theme_core = i;
255    }
256
257    info->fonts = NULL;
258    for (i = 0; i < NUM_BITMAPS; i++)
259	info->bitmaps[i].bitmap = None;
260
261    /*
262     * Link new info info list, making sure another
263     * thread hasn't inserted something into the list while
264     * this one was busy setting up the data
265     */
266    _XLockMutex (_Xglobal_lock);
267    for (old = _XcursorDisplayInfo; old; old = old->next)
268	if (old->display == dpy)
269	    break;
270    if (old)
271    {
272	_XcursorFreeDisplayInfo (info);
273	info = old;
274    }
275    else
276    {
277	info->next = _XcursorDisplayInfo;
278	_XcursorDisplayInfo = info;
279    }
280    _XUnlockMutex (_Xglobal_lock);
281
282    return info;
283}
284
285XcursorBool
286XcursorSupportsARGB (Display *dpy)
287{
288    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
289
290    return info && info->has_render_cursor;
291}
292
293XcursorBool
294XcursorSupportsAnim (Display *dpy)
295{
296    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
297
298    return info && info->has_anim_cursor;
299}
300
301XcursorBool
302XcursorSetDefaultSize (Display *dpy, int size)
303{
304    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
305
306    if (!info)
307	return XcursorFalse;
308    info->size = size;
309    return XcursorTrue;
310}
311
312int
313XcursorGetDefaultSize (Display *dpy)
314{
315    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
316
317    if (!info)
318	return 0;
319    return info->size;
320}
321
322XcursorBool
323XcursorSetTheme (Display *dpy, const char *theme)
324{
325    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
326    char		*copy;
327
328    if (!info)
329	return XcursorFalse;
330
331    if (!theme)
332	theme = info->theme_from_config;
333
334    if (theme)
335    {
336	copy = strdup (theme);
337	if (!copy)
338	    return XcursorFalse;
339    }
340    else
341	copy = NULL;
342    if (info->theme)
343	free (info->theme);
344    info->theme = copy;
345    return XcursorTrue;
346}
347
348char *
349XcursorGetTheme (Display *dpy)
350{
351    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
352
353    if (!info)
354	return NULL;
355    return info->theme;
356}
357
358XcursorBool
359XcursorGetThemeCore (Display *dpy)
360{
361    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
362
363    if (!info)
364	return XcursorFalse;
365    return info->theme_core;
366
367}
368
369XcursorBool
370XcursorSetThemeCore (Display *dpy, XcursorBool theme_core)
371{
372    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
373
374    if (!info)
375	return XcursorFalse;
376    info->theme_core = theme_core;
377    return XcursorTrue;
378}
379