1/* 2 * Copyright © 2024 Thomas E. Dickey 3 * Copyright © 2002 Keith Packard 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Keith Packard not be used in 10 * advertising or publicity pertaining to distribution of the software without 11 * specific, written prior permission. Keith Packard makes no 12 * representations about the suitability of this software for any purpose. It 13 * is provided "as is" without express or implied warranty. 14 * 15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24#include "xcursorint.h" 25#include <X11/Xlibint.h> 26#include <ctype.h> 27#include <unistd.h> /* for getpid */ 28 29static XcursorDisplayInfo *_XcursorDisplayInfo; 30 31static void 32_XcursorFreeDisplayInfo (XcursorDisplayInfo *info) 33{ 34 if (info->theme) 35 free (info->theme); 36 37 if (info->theme_from_config) 38 free (info->theme_from_config); 39 40 while (info->fonts) 41 { 42 XcursorFontInfo *fi = info->fonts; 43 info->fonts = fi->next; 44 free (fi); 45 } 46 47 free (info); 48} 49 50static int 51_XcursorCloseDisplay (Display *dpy, XExtCodes *codes) 52{ 53 XcursorDisplayInfo *info, **prev; 54 55 (void) codes; /* UNUSED */ 56 57 /* 58 * Unhook from the global list 59 */ 60 _XLockMutex (_Xglobal_lock); 61 for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next) 62 if (info->display == dpy) 63 { 64 *prev = info->next; 65 break; 66 } 67 _XUnlockMutex (_Xglobal_lock); 68 69 if (info) 70 _XcursorFreeDisplayInfo (info); 71 return 0; 72} 73 74static int 75_XcursorDefaultParseBool (char *v) 76{ 77 char c0; 78 79 c0 = *v; 80 if (isupper ((int)c0)) 81 c0 = (char) tolower (c0); 82 if (c0 == 't' || c0 == 'y' || c0 == '1') 83 return 1; 84 if (c0 == 'f' || c0 == 'n' || c0 == '0') 85 return 0; 86 if (c0 == 'o') 87 { 88 char c1 = v[1]; 89 if (isupper ((int)c1)) 90 c1 = (char) tolower (c1); 91 if (c1 == 'n') 92 return 1; 93 if (c1 == 'f') 94 return 0; 95 } 96 return -1; 97} 98 99XcursorDisplayInfo * 100_XcursorGetDisplayInfo (Display *dpy) 101{ 102 XcursorDisplayInfo *info, **prev, *old; 103 int event_base, error_base; 104 int major, minor; 105 char *v; 106 int i; 107 108 _XLockMutex (_Xglobal_lock); 109 for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next) 110 { 111 if (info->display == dpy) 112 { 113 /* 114 * MRU the list 115 */ 116 if (prev != &_XcursorDisplayInfo) 117 { 118 *prev = info->next; 119 info->next = _XcursorDisplayInfo; 120 _XcursorDisplayInfo = info; 121 } 122 break; 123 } 124 } 125 _XUnlockMutex (_Xglobal_lock); 126 if (info) 127 return info; 128 info = (XcursorDisplayInfo *) malloc (sizeof (XcursorDisplayInfo)); 129 if (!info) 130 return NULL; 131 info->next = NULL; 132 info->display = dpy; 133 134 info->codes = XAddExtension (dpy); 135 if (!info->codes) 136 { 137 free (info); 138 return NULL; 139 } 140 (void) XESetCloseDisplay (dpy, info->codes->extension, _XcursorCloseDisplay); 141 142 /* 143 * The debugging-trace for new info-blocks begins here. 144 * As a reminder that multiple processes/threads use this library, 145 * the current process-id is logged. 146 */ 147 traceOpts((T_CALLED(_XcursorGetDisplayInfo) " info %p, pid %d\n", 148 (void*)info, getpid())); 149 150 /* 151 * Check whether the display supports the Render CreateCursor request 152 */ 153 info->has_render_cursor = XcursorFalse; 154 info->has_anim_cursor = XcursorFalse; 155 if (XRenderQueryExtension (dpy, &event_base, &error_base) && 156 XRenderQueryVersion (dpy, &major, &minor)) 157 { 158 if (major > 0 || minor >= 5) 159 { 160 info->has_render_cursor = XcursorTrue; 161 v = getenv ("XCURSOR_CORE"); 162 if (!v) 163 v = XGetDefault (dpy, "Xcursor", "core"); 164 if (v && _XcursorDefaultParseBool (v) == 1) 165 info->has_render_cursor = XcursorFalse; 166 traceOpts((T_OPTION(XCURSOR_CORE) ": %d\n", info->has_render_cursor)); 167 } 168 if (info->has_render_cursor && (major > 0 || minor >= 8)) 169 { 170 info->has_anim_cursor = XcursorTrue; 171 v = getenv ("XCURSOR_ANIM"); 172 if (!v) 173 v = XGetDefault (dpy, "Xcursor", "anim"); 174 if (v && _XcursorDefaultParseBool (v) == 0) 175 info->has_anim_cursor = XcursorFalse; 176 traceOpts((T_OPTION(XCURSOR_ANIM) ": %d\n", info->has_anim_cursor)); 177 } 178 } 179 180 info->size = 0; 181 182 /* 183 * Get desired cursor size 184 */ 185 v = getenv ("XCURSOR_SIZE"); 186 if (!v) 187 v = XGetDefault (dpy, "Xcursor", "size"); 188 if (v) 189 info->size = atoi (v); 190 traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size)); 191 192 /* 193 * Use the Xft size to guess a size; make cursors 16 "points" tall 194 */ 195 if (info->size == 0) 196 { 197 int dpi = 0; 198 v = XGetDefault (dpy, "Xft", "dpi"); 199 if (v) 200 dpi = atoi (v); 201 if (dpi) 202 info->size = dpi * 16 / 72; 203 traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size)); 204 } 205 206 /* 207 * Use display size to guess a size 208 */ 209 if (info->size == 0) 210 { 211 int dim; 212 213 if (DisplayHeight (dpy, DefaultScreen (dpy)) < 214 DisplayWidth (dpy, DefaultScreen (dpy))) 215 dim = DisplayHeight (dpy, DefaultScreen (dpy)); 216 else 217 dim = DisplayWidth (dpy, DefaultScreen (dpy)); 218 /* 219 * 16 pixels on a display of dimension 768 220 */ 221 info->size = dim / 48; 222 traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size)); 223 } 224 225 info->theme = NULL; 226 info->theme_from_config = NULL; 227 228 /* 229 * Provide for making cursors resized to match the requested size 230 */ 231 info->resized_cursors = XcursorFalse; 232 v = getenv ("XCURSOR_RESIZED"); 233 if (!v) 234 v = XGetDefault (dpy, "Xcursor", "resized"); 235 if (v) 236 { 237 i = _XcursorDefaultParseBool (v); 238 if (i >= 0) 239 info->resized_cursors = i; 240 } 241 traceOpts((T_OPTION(XCURSOR_RESIZED) ": %d\n", info->resized_cursors)); 242 243 /* 244 * Get the desired theme 245 */ 246 v = getenv ("XCURSOR_THEME"); 247 if (!v) 248 v = XGetDefault (dpy, "Xcursor", "theme"); 249 if (v) 250 { 251 info->theme = strdup (v); 252 info->theme_from_config = strdup (v); 253 } 254 traceOpts((T_OPTION(XCURSOR_THEME) ": %s\n", NonNull(info->theme))); 255 256 /* 257 * Get the desired dither 258 */ 259 info->dither = XcursorDitherThreshold; 260 v = getenv ("XCURSOR_DITHER"); 261 if (!v) 262 v = XGetDefault (dpy, "Xcursor", "dither"); 263 if (v) 264 { 265 if (!strcmp (v, "threshold")) 266 info->dither = XcursorDitherThreshold; 267 if (!strcmp (v, "median")) 268 info->dither = XcursorDitherMedian; 269 if (!strcmp (v, "ordered")) 270 info->dither = XcursorDitherOrdered; 271 if (!strcmp (v, "diffuse")) 272 info->dither = XcursorDitherDiffuse; 273 } 274 traceOpts((T_OPTION(XCURSOR_DITHER) ": %d\n", info->dither)); 275 276 info->theme_core = False; 277 /* 278 * Find out if core cursors should 279 * be themed 280 */ 281 v = getenv ("XCURSOR_THEME_CORE"); 282 if (!v) 283 v = XGetDefault (dpy, "Xcursor", "theme_core"); 284 if (v) 285 { 286 i = _XcursorDefaultParseBool (v); 287 if (i >= 0) 288 info->theme_core = i; 289 } 290 traceOpts((T_OPTION(XCURSOR_THEME_CORE) ": %d\n", info->theme_core)); 291 292 info->fonts = NULL; 293 for (i = 0; i < NUM_BITMAPS; i++) 294 info->bitmaps[i].bitmap = None; 295 296 /* 297 * Link new info info list, making sure another 298 * thread hasn't inserted something into the list while 299 * this one was busy setting up the data 300 */ 301 _XLockMutex (_Xglobal_lock); 302 for (old = _XcursorDisplayInfo; old; old = old->next) 303 if (old->display == dpy) 304 break; 305 if (old) 306 { 307 _XcursorFreeDisplayInfo (info); 308 info = old; 309 } 310 else 311 { 312 info->next = _XcursorDisplayInfo; 313 _XcursorDisplayInfo = info; 314 } 315 _XUnlockMutex (_Xglobal_lock); 316 317 returnAddr(info); 318} 319 320XcursorBool 321XcursorSupportsARGB (Display *dpy) 322{ 323 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 324 325 return info && info->has_render_cursor; 326} 327 328XcursorBool 329XcursorSupportsAnim (Display *dpy) 330{ 331 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 332 333 return info && info->has_anim_cursor; 334} 335 336XcursorBool 337XcursorSetDefaultSize (Display *dpy, int size) 338{ 339 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 340 341 if (!info) 342 return XcursorFalse; 343 info->size = size; 344 return XcursorTrue; 345} 346 347int 348XcursorGetDefaultSize (Display *dpy) 349{ 350 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 351 352 if (!info) 353 return 0; 354 return info->size; 355} 356 357XcursorBool 358XcursorSetResizable (Display *dpy, XcursorBool flag) 359{ 360 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 361 362 if (!info) 363 return XcursorFalse; 364 info->resized_cursors = flag; 365 return XcursorTrue; 366} 367 368XcursorBool 369XcursorGetResizable (Display *dpy) 370{ 371 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 372 373 if (!info) 374 return 0; 375 return info->resized_cursors; 376} 377 378XcursorBool 379XcursorSetTheme (Display *dpy, const char *theme) 380{ 381 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 382 char *copy; 383 384 if (!info) 385 return XcursorFalse; 386 387 if (!theme) 388 theme = info->theme_from_config; 389 390 if (theme) 391 { 392 copy = strdup (theme); 393 if (!copy) 394 return XcursorFalse; 395 } 396 else 397 copy = NULL; 398 if (info->theme) 399 free (info->theme); 400 info->theme = copy; 401 return XcursorTrue; 402} 403 404char * 405XcursorGetTheme (Display *dpy) 406{ 407 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 408 409 if (!info) 410 return NULL; 411 return info->theme; 412} 413 414XcursorBool 415XcursorGetThemeCore (Display *dpy) 416{ 417 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 418 419 if (!info) 420 return XcursorFalse; 421 return info->theme_core; 422} 423 424XcursorBool 425XcursorSetThemeCore (Display *dpy, XcursorBool theme_core) 426{ 427 XcursorDisplayInfo *info = _XcursorGetDisplayInfo (dpy); 428 429 if (!info) 430 return XcursorFalse; 431 info->theme_core = theme_core; 432 return XcursorTrue; 433} 434