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 
27 static XcursorDisplayInfo *_XcursorDisplayInfo;
28 
29 static void
_XcursorFreeDisplayInfo(XcursorDisplayInfo * info)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 
48 static int
_XcursorCloseDisplay(Display * dpy,XExtCodes * codes)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 
70 static int
_XcursorDefaultParseBool(char * v)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 
95 XcursorDisplayInfo *
_XcursorGetDisplayInfo(Display * dpy)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 
285 XcursorBool
XcursorSupportsARGB(Display * dpy)286 XcursorSupportsARGB (Display *dpy)
287 {
288     XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
289 
290     return info && info->has_render_cursor;
291 }
292 
293 XcursorBool
XcursorSupportsAnim(Display * dpy)294 XcursorSupportsAnim (Display *dpy)
295 {
296     XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
297 
298     return info && info->has_anim_cursor;
299 }
300 
301 XcursorBool
XcursorSetDefaultSize(Display * dpy,int size)302 XcursorSetDefaultSize (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 
312 int
XcursorGetDefaultSize(Display * dpy)313 XcursorGetDefaultSize (Display *dpy)
314 {
315     XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
316 
317     if (!info)
318 	return 0;
319     return info->size;
320 }
321 
322 XcursorBool
XcursorSetTheme(Display * dpy,const char * theme)323 XcursorSetTheme (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 
348 char *
XcursorGetTheme(Display * dpy)349 XcursorGetTheme (Display *dpy)
350 {
351     XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
352 
353     if (!info)
354 	return NULL;
355     return info->theme;
356 }
357 
358 XcursorBool
XcursorGetThemeCore(Display * dpy)359 XcursorGetThemeCore (Display *dpy)
360 {
361     XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
362 
363     if (!info)
364 	return XcursorFalse;
365     return info->theme_core;
366 
367 }
368 
369 XcursorBool
XcursorSetThemeCore(Display * dpy,XcursorBool theme_core)370 XcursorSetThemeCore (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