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