display.c revision 4d939ec7
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    (void) codes;	/* UNUSED */
54
55    /*
56     * Unhook from the global list
57     */
58    _XLockMutex (_Xglobal_lock);
59    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
60	if (info->display == dpy)
61	{
62            *prev = info->next;
63	    break;
64	}
65    _XUnlockMutex (_Xglobal_lock);
66
67    if (info)
68	_XcursorFreeDisplayInfo (info);
69    return 0;
70}
71
72static int
73_XcursorDefaultParseBool (char *v)
74{
75    char    c0;
76
77    c0 = *v;
78    if (isupper ((int)c0))
79	c0 = (char) tolower (c0);
80    if (c0 == 't' || c0 == 'y' || c0 == '1')
81	return 1;
82    if (c0 == 'f' || c0 == 'n' || c0 == '0')
83	return 0;
84    if (c0 == 'o')
85    {
86	char c1 = v[1];
87	if (isupper ((int)c1))
88	    c1 = (char) tolower (c1);
89	if (c1 == 'n')
90	    return 1;
91	if (c1 == 'f')
92	    return 0;
93    }
94    return -1;
95}
96
97XcursorDisplayInfo *
98_XcursorGetDisplayInfo (Display *dpy)
99{
100    XcursorDisplayInfo	*info, **prev, *old;
101    int			event_base, error_base;
102    int			major, minor;
103    char		*v;
104    int			i;
105
106    _XLockMutex (_Xglobal_lock);
107    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
108    {
109	if (info->display == dpy)
110	{
111	    /*
112	     * MRU the list
113	     */
114	    if (prev != &_XcursorDisplayInfo)
115	    {
116		*prev = info->next;
117		info->next = _XcursorDisplayInfo;
118		_XcursorDisplayInfo = info;
119	    }
120	    break;
121	}
122    }
123    _XUnlockMutex (_Xglobal_lock);
124    if (info)
125        return info;
126    info = (XcursorDisplayInfo *) malloc (sizeof (XcursorDisplayInfo));
127    if (!info)
128	return NULL;
129    info->next = NULL;
130    info->display = dpy;
131
132    info->codes = XAddExtension (dpy);
133    if (!info->codes)
134    {
135	free (info);
136	return NULL;
137    }
138    (void) XESetCloseDisplay (dpy, info->codes->extension, _XcursorCloseDisplay);
139
140    /*
141     * Check whether the display supports the Render CreateCursor request
142     */
143    info->has_render_cursor = XcursorFalse;
144    info->has_anim_cursor = XcursorFalse;
145    if (XRenderQueryExtension (dpy, &event_base, &error_base) &&
146	XRenderQueryVersion (dpy, &major, &minor))
147    {
148	if (major > 0 || minor >= 5)
149	{
150	    info->has_render_cursor = XcursorTrue;
151	    v = getenv ("XCURSOR_CORE");
152	    if (!v)
153		v = XGetDefault (dpy, "Xcursor", "core");
154	    if (v && _XcursorDefaultParseBool (v) == 1)
155		info->has_render_cursor = XcursorFalse;
156	}
157	if (info->has_render_cursor && (major > 0 || minor >= 8))
158	{
159	    info->has_anim_cursor = XcursorTrue;
160	    v = getenv ("XCURSOR_ANIM");
161	    if (!v)
162		v = XGetDefault (dpy, "Xcursor", "anim");
163	    if (v && _XcursorDefaultParseBool (v) == 0)
164		info->has_anim_cursor = XcursorFalse;
165	}
166    }
167
168    info->size = 0;
169
170    /*
171     * Get desired cursor size
172     */
173    v = getenv ("XCURSOR_SIZE");
174    if (!v)
175	v = XGetDefault (dpy, "Xcursor", "size");
176    if (v)
177	info->size = atoi (v);
178
179    /*
180     * Use the Xft size to guess a size; make cursors 16 "points" tall
181     */
182    if (info->size == 0)
183    {
184	int dpi = 0;
185	v = XGetDefault (dpy, "Xft", "dpi");
186	if (v)
187	    dpi = atoi (v);
188	if (dpi)
189	    info->size = dpi * 16 / 72;
190    }
191
192    /*
193     * Use display size to guess a size
194     */
195    if (info->size == 0)
196    {
197	int dim;
198
199	if (DisplayHeight (dpy, DefaultScreen (dpy)) <
200	    DisplayWidth (dpy, DefaultScreen (dpy)))
201	    dim = DisplayHeight (dpy, DefaultScreen (dpy));
202	else
203	    dim = DisplayWidth (dpy, DefaultScreen (dpy));
204	/*
205	 * 16 pixels on a display of dimension 768
206	 */
207	info->size = dim / 48;
208    }
209
210    info->theme = NULL;
211    info->theme_from_config = NULL;
212
213    /*
214     * Get the desired theme
215     */
216    v = getenv ("XCURSOR_THEME");
217    if (!v)
218	v = XGetDefault (dpy, "Xcursor", "theme");
219    if (v)
220    {
221	info->theme = strdup (v);
222	info->theme_from_config = strdup (v);
223    }
224
225    /*
226     * Get the desired dither
227     */
228    info->dither = XcursorDitherThreshold;
229    v = getenv ("XCURSOR_DITHER");
230    if (!v)
231	v = XGetDefault (dpy, "Xcursor", "dither");
232    if (v)
233    {
234	if (!strcmp (v, "threshold"))
235	    info->dither = XcursorDitherThreshold;
236	if (!strcmp (v, "median"))
237	    info->dither = XcursorDitherMedian;
238	if (!strcmp (v, "ordered"))
239	    info->dither = XcursorDitherOrdered;
240	if (!strcmp (v, "diffuse"))
241	    info->dither = XcursorDitherDiffuse;
242    }
243
244    info->theme_core = False;
245    /*
246     * Find out if core cursors should
247     * be themed
248     */
249    v = getenv ("XCURSOR_THEME_CORE");
250    if (!v)
251	v = XGetDefault (dpy, "Xcursor", "theme_core");
252    if (v)
253    {
254	i = _XcursorDefaultParseBool (v);
255	if (i >= 0)
256	    info->theme_core = i;
257    }
258
259    info->fonts = NULL;
260    for (i = 0; i < NUM_BITMAPS; i++)
261	info->bitmaps[i].bitmap = None;
262
263    /*
264     * Link new info info list, making sure another
265     * thread hasn't inserted something into the list while
266     * this one was busy setting up the data
267     */
268    _XLockMutex (_Xglobal_lock);
269    for (old = _XcursorDisplayInfo; old; old = old->next)
270	if (old->display == dpy)
271	    break;
272    if (old)
273    {
274	_XcursorFreeDisplayInfo (info);
275	info = old;
276    }
277    else
278    {
279	info->next = _XcursorDisplayInfo;
280	_XcursorDisplayInfo = info;
281    }
282    _XUnlockMutex (_Xglobal_lock);
283
284    return info;
285}
286
287XcursorBool
288XcursorSupportsARGB (Display *dpy)
289{
290    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
291
292    return info && info->has_render_cursor;
293}
294
295XcursorBool
296XcursorSupportsAnim (Display *dpy)
297{
298    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
299
300    return info && info->has_anim_cursor;
301}
302
303XcursorBool
304XcursorSetDefaultSize (Display *dpy, int size)
305{
306    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
307
308    if (!info)
309	return XcursorFalse;
310    info->size = size;
311    return XcursorTrue;
312}
313
314int
315XcursorGetDefaultSize (Display *dpy)
316{
317    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
318
319    if (!info)
320	return 0;
321    return info->size;
322}
323
324XcursorBool
325XcursorSetTheme (Display *dpy, const char *theme)
326{
327    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
328    char		*copy;
329
330    if (!info)
331	return XcursorFalse;
332
333    if (!theme)
334	theme = info->theme_from_config;
335
336    if (theme)
337    {
338	copy = strdup (theme);
339	if (!copy)
340	    return XcursorFalse;
341    }
342    else
343	copy = NULL;
344    if (info->theme)
345	free (info->theme);
346    info->theme = copy;
347    return XcursorTrue;
348}
349
350char *
351XcursorGetTheme (Display *dpy)
352{
353    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
354
355    if (!info)
356	return NULL;
357    return info->theme;
358}
359
360XcursorBool
361XcursorGetThemeCore (Display *dpy)
362{
363    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
364
365    if (!info)
366	return XcursorFalse;
367    return info->theme_core;
368
369}
370
371XcursorBool
372XcursorSetThemeCore (Display *dpy, XcursorBool theme_core)
373{
374    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
375
376    if (!info)
377	return XcursorFalse;
378    info->theme_core = theme_core;
379    return XcursorTrue;
380}
381