display.c revision e169010a
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    free (info);
39}
40
41static int
42_XcursorCloseDisplay (Display *dpy, XExtCodes *codes)
43{
44    XcursorDisplayInfo  *info, **prev;
45
46    /*
47     * Unhook from the global list
48     */
49    _XLockMutex (_Xglobal_lock);
50    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
51	if (info->display == dpy)
52	{
53            *prev = info->next;
54	    break;
55	}
56    _XUnlockMutex (_Xglobal_lock);
57
58    if (info)
59	_XcursorFreeDisplayInfo (info);
60    return 0;
61}
62
63static int
64_XcursorDefaultParseBool (char *v)
65{
66    char    c0, c1;
67
68    c0 = *v;
69    if (isupper ((int)c0))
70	c0 = tolower (c0);
71    if (c0 == 't' || c0 == 'y' || c0 == '1')
72	return 1;
73    if (c0 == 'f' || c0 == 'n' || c0 == '0')
74	return 0;
75    if (c0 == 'o')
76    {
77	c1 = v[1];
78	if (isupper ((int)c1))
79	    c1 = tolower (c1);
80	if (c1 == 'n')
81	    return 1;
82	if (c1 == 'f')
83	    return 0;
84    }
85    return -1;
86}
87
88XcursorDisplayInfo *
89_XcursorGetDisplayInfo (Display *dpy)
90{
91    XcursorDisplayInfo	*info, **prev, *old;
92    int			event_base, error_base;
93    int			major, minor;
94    char		*v;
95    int			i;
96
97    _XLockMutex (_Xglobal_lock);
98    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
99    {
100	if (info->display == dpy)
101	{
102	    /*
103	     * MRU the list
104	     */
105	    if (prev != &_XcursorDisplayInfo)
106	    {
107		*prev = info->next;
108		info->next = _XcursorDisplayInfo;
109		_XcursorDisplayInfo = info;
110	    }
111	    break;
112	}
113    }
114    _XUnlockMutex (_Xglobal_lock);
115    if (info)
116        return info;
117    info = (XcursorDisplayInfo *) malloc (sizeof (XcursorDisplayInfo));
118    if (!info)
119	return NULL;
120    info->next = NULL;
121    info->display = dpy;
122
123    info->codes = XAddExtension (dpy);
124    if (!info->codes)
125    {
126	free (info);
127	return NULL;
128    }
129    (void) XESetCloseDisplay (dpy, info->codes->extension, _XcursorCloseDisplay);
130
131    /*
132     * Check whether the display supports the Render CreateCursor request
133     */
134    info->has_render_cursor = XcursorFalse;
135    info->has_anim_cursor = XcursorFalse;
136    if (XRenderQueryExtension (dpy, &event_base, &error_base) &&
137	XRenderQueryVersion (dpy, &major, &minor))
138    {
139	if (major > 0 || minor >= 5)
140	{
141	    info->has_render_cursor = XcursorTrue;
142	    v = getenv ("XCURSOR_CORE");
143	    if (!v)
144		v = XGetDefault (dpy, "Xcursor", "core");
145	    if (v && _XcursorDefaultParseBool (v) == 1)
146		info->has_render_cursor = XcursorFalse;
147	}
148	if (info->has_render_cursor && (major > 0 || minor >= 8))
149	{
150	    info->has_anim_cursor = XcursorTrue;
151	    v = getenv ("XCURSOR_ANIM");
152	    if (!v)
153		v = XGetDefault (dpy, "Xcursor", "anim");
154	    if (v && _XcursorDefaultParseBool (v) == 0)
155		info->has_anim_cursor = XcursorFalse;
156	}
157    }
158
159    info->size = 0;
160
161    /*
162     * Get desired cursor size
163     */
164    v = getenv ("XCURSOR_SIZE");
165    if (!v)
166	v = XGetDefault (dpy, "Xcursor", "size");
167    if (v)
168	info->size = atoi (v);
169
170    /*
171     * Use the Xft size to guess a size; make cursors 16 "points" tall
172     */
173    if (info->size == 0)
174    {
175	int dpi = 0;
176	v = XGetDefault (dpy, "Xft", "dpi");
177	if (v)
178	    dpi = atoi (v);
179	if (dpi)
180	    info->size = dpi * 16 / 72;
181    }
182
183    /*
184     * Use display size to guess a size
185     */
186    if (info->size == 0)
187    {
188	int dim;
189
190	if (DisplayHeight (dpy, DefaultScreen (dpy)) <
191	    DisplayWidth (dpy, DefaultScreen (dpy)))
192	    dim = DisplayHeight (dpy, DefaultScreen (dpy));
193	else
194	    dim = DisplayWidth (dpy, DefaultScreen (dpy));
195	/*
196	 * 16 pixels on a display of dimension 768
197	 */
198	info->size = dim / 48;
199    }
200
201    info->theme = NULL;
202    info->theme_from_config = NULL;
203
204    /*
205     * Get the desired theme
206     */
207    v = getenv ("XCURSOR_THEME");
208    if (!v)
209	v = XGetDefault (dpy, "Xcursor", "theme");
210    if (v)
211    {
212	int len;
213
214	len = strlen (v) + 1;
215
216	info->theme = malloc (len);
217	if (info->theme)
218	    strcpy (info->theme, v);
219
220	info->theme_from_config = malloc (len);
221	if (info->theme_from_config)
222	    strcpy (info->theme_from_config, 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 = malloc (strlen (theme) + 1);
339	if (!copy)
340	    return XcursorFalse;
341	strcpy (copy, theme);
342    }
343    else
344	copy = NULL;
345    if (info->theme)
346	free (info->theme);
347    info->theme = copy;
348    return XcursorTrue;
349}
350
351char *
352XcursorGetTheme (Display *dpy)
353{
354    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
355
356    if (!info)
357	return NULL;
358    return info->theme;
359}
360
361XcursorBool
362XcursorGetThemeCore (Display *dpy)
363{
364    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
365
366    if (!info)
367	return XcursorFalse;
368    return info->theme_core;
369
370}
371
372XcursorBool
373XcursorSetThemeCore (Display *dpy, XcursorBool theme_core)
374{
375    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
376
377    if (!info)
378	return XcursorFalse;
379    info->theme_core = theme_core;
380    return XcursorTrue;
381}
382