library.c revision cdce750a
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 <stdlib.h> 25#include <string.h> 26 27#ifndef ICONDIR 28#define ICONDIR "/usr/X11R6/lib/X11/icons" 29#endif 30 31#ifndef XCURSORPATH 32#define XCURSORPATH "~/.local/share/icons:~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR 33#endif 34 35const char * 36XcursorLibraryPath (void) 37{ 38 static const char *path; 39 40 if (!path) 41 { 42 path = getenv ("XCURSOR_PATH"); 43 if (!path) 44 path = XCURSORPATH; 45 } 46 return path; 47} 48 49static void 50_XcursorAddPathElt (char *path, const char *elt, int len) 51{ 52 size_t pathlen = strlen (path); 53 54 /* append / if the path doesn't currently have one */ 55 if (path[0] == '\0' || path[pathlen - 1] != '/') 56 { 57 strcat (path, "/"); 58 pathlen++; 59 } 60 if (len == -1) 61 len = strlen (elt); 62 /* strip leading slashes */ 63 while (len && elt[0] == '/') 64 { 65 elt++; 66 len--; 67 } 68 strncpy (path + pathlen, elt, len); 69 path[pathlen + len] = '\0'; 70} 71 72static char * 73_XcursorBuildThemeDir (const char *dir, const char *theme) 74{ 75 const char *colon; 76 const char *tcolon; 77 char *full; 78 char *home; 79 int dirlen; 80 int homelen; 81 int themelen; 82 int len; 83 84 if (!dir || !theme) 85 return NULL; 86 87 colon = strchr (dir, ':'); 88 if (!colon) 89 colon = dir + strlen (dir); 90 91 dirlen = colon - dir; 92 93 tcolon = strchr (theme, ':'); 94 if (!tcolon) 95 tcolon = theme + strlen (theme); 96 97 themelen = tcolon - theme; 98 99 home = NULL; 100 homelen = 0; 101 if (*dir == '~') 102 { 103 home = getenv ("HOME"); 104 if (!home) 105 return NULL; 106 homelen = strlen (home); 107 dir++; 108 dirlen--; 109 } 110 111 /* 112 * add space for any needed directory separators, one per component, 113 * and one for the trailing null 114 */ 115 len = 1 + homelen + 1 + dirlen + 1 + themelen + 1; 116 117 full = malloc (len); 118 if (!full) 119 return NULL; 120 full[0] = '\0'; 121 122 if (home) 123 _XcursorAddPathElt (full, home, -1); 124 _XcursorAddPathElt (full, dir, dirlen); 125 _XcursorAddPathElt (full, theme, themelen); 126 return full; 127} 128 129static char * 130_XcursorBuildFullname (const char *dir, const char *subdir, const char *file) 131{ 132 char *full; 133 134 if (!dir || !subdir || !file) 135 return NULL; 136 137 full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1); 138 if (!full) 139 return NULL; 140 full[0] = '\0'; 141 _XcursorAddPathElt (full, dir, -1); 142 _XcursorAddPathElt (full, subdir, -1); 143 _XcursorAddPathElt (full, file, -1); 144 return full; 145} 146 147static const char * 148_XcursorNextPath (const char *path) 149{ 150 char *colon = strchr (path, ':'); 151 152 if (!colon) 153 return NULL; 154 return colon + 1; 155} 156 157#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') 158#define XcursorSep(c) ((c) == ';' || (c) == ',') 159 160static char * 161_XcursorThemeInherits (const char *full) 162{ 163 char line[8192]; 164 char *result = NULL; 165 FILE *f; 166 167 if (!full) 168 return NULL; 169 170 f = fopen (full, "r"); 171 if (f) 172 { 173 while (fgets (line, sizeof (line), f)) 174 { 175 if (!strncmp (line, "Inherits", 8)) 176 { 177 char *l = line + 8; 178 char *r; 179 while (*l == ' ') l++; 180 if (*l != '=') continue; 181 l++; 182 while (*l == ' ') l++; 183 result = malloc (strlen (l) + 1); 184 if (result) 185 { 186 r = result; 187 while (*l) 188 { 189 while (XcursorSep(*l) || XcursorWhite (*l)) l++; 190 if (!*l) 191 break; 192 if (r != result) 193 *r++ = ':'; 194 while (*l && !XcursorWhite(*l) && 195 !XcursorSep(*l)) 196 *r++ = *l++; 197 } 198 *r++ = '\0'; 199 } 200 break; 201 } 202 } 203 fclose (f); 204 } 205 return result; 206} 207 208#define XCURSOR_SCAN_CORE ((FILE *) 1) 209 210static FILE * 211XcursorScanTheme (const char *theme, const char *name) 212{ 213 FILE *f = NULL; 214 char *full; 215 char *dir; 216 const char *path; 217 char *inherits = NULL; 218 const char *i; 219 220 if (!theme || !name) 221 return NULL; 222 223 /* 224 * XCURSOR_CORE_THEME is a magic name; cursors from the core set 225 * are never found in any directory. Instead, a magic value is 226 * returned which truncates any search so that overlying functions 227 * can switch to equivalent core cursors 228 */ 229 if (!strcmp (theme, XCURSOR_CORE_THEME) && XcursorLibraryShape (name) >= 0) 230 return XCURSOR_SCAN_CORE; 231 /* 232 * Scan this theme 233 */ 234 for (path = XcursorLibraryPath (); 235 path && f == NULL; 236 path = _XcursorNextPath (path)) 237 { 238 dir = _XcursorBuildThemeDir (path, theme); 239 if (dir) 240 { 241 full = _XcursorBuildFullname (dir, "cursors", name); 242 if (full) 243 { 244 f = fopen (full, "r"); 245 free (full); 246 } 247 if (!f && !inherits) 248 { 249 full = _XcursorBuildFullname (dir, "", "index.theme"); 250 if (full) 251 { 252 inherits = _XcursorThemeInherits (full); 253 free (full); 254 } 255 } 256 free (dir); 257 } 258 } 259 /* 260 * Recurse to scan inherited themes 261 */ 262 for (i = inherits; i && f == NULL; i = _XcursorNextPath (i)) 263 { 264 if (strcmp(i, theme) != 0) 265 f = XcursorScanTheme (i, name); 266 else 267 printf("Not calling XcursorScanTheme because of circular dependency: %s. %s", i, name); 268 } 269 if (inherits != NULL) 270 free (inherits); 271 return f; 272} 273 274XcursorImage * 275XcursorLibraryLoadImage (const char *file, const char *theme, int size) 276{ 277 FILE *f = NULL; 278 XcursorImage *image = NULL; 279 280 if (!file) 281 return NULL; 282 283 if (theme) 284 f = XcursorScanTheme (theme, file); 285 if (!f) 286 f = XcursorScanTheme ("default", file); 287 if (f == XCURSOR_SCAN_CORE) 288 return NULL; 289 if (f) 290 { 291 image = XcursorFileLoadImage (f, size); 292 fclose (f); 293 } 294 return image; 295} 296 297XcursorImages * 298XcursorLibraryLoadImages (const char *file, const char *theme, int size) 299{ 300 FILE *f = NULL; 301 XcursorImages *images = NULL; 302 303 if (!file) 304 return NULL; 305 306 if (theme) 307 f = XcursorScanTheme (theme, file); 308 if (!f) 309 f = XcursorScanTheme ("default", file); 310 if (f == XCURSOR_SCAN_CORE) 311 return NULL; 312 if (f) 313 { 314 images = XcursorFileLoadImages (f, size); 315 if (images) 316 XcursorImagesSetName (images, file); 317 fclose (f); 318 } 319 return images; 320} 321 322Cursor 323XcursorLibraryLoadCursor (Display *dpy, const char *file) 324{ 325 int size = XcursorGetDefaultSize (dpy); 326 char *theme = XcursorGetTheme (dpy); 327 XcursorImages *images = XcursorLibraryLoadImages (file, theme, size); 328 Cursor cursor; 329 330 if (!file) 331 return 0; 332 333 if (!images) 334 { 335 int id = XcursorLibraryShape (file); 336 337 if (id >= 0) 338 return _XcursorCreateFontCursor (dpy, id); 339 else 340 return 0; 341 } 342 cursor = XcursorImagesLoadCursor (dpy, images); 343 XcursorImagesDestroy (images); 344#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2 345 XFixesSetCursorName (dpy, cursor, file); 346#endif 347 return cursor; 348} 349 350XcursorCursors * 351XcursorLibraryLoadCursors (Display *dpy, const char *file) 352{ 353 int size = XcursorGetDefaultSize (dpy); 354 char *theme = XcursorGetTheme (dpy); 355 XcursorImages *images = XcursorLibraryLoadImages (file, theme, size); 356 XcursorCursors *cursors; 357 358 if (!file) 359 return NULL; 360 361 if (!images) 362 { 363 int id = XcursorLibraryShape (file); 364 365 if (id >= 0) 366 { 367 cursors = XcursorCursorsCreate (dpy, 1); 368 if (cursors) 369 { 370 cursors->cursors[0] = _XcursorCreateFontCursor (dpy, id); 371 if (cursors->cursors[0] == None) 372 { 373 XcursorCursorsDestroy (cursors); 374 cursors = NULL; 375 } 376 else 377 cursors->ncursor = 1; 378 } 379 } 380 else 381 cursors = NULL; 382 } 383 else 384 { 385 cursors = XcursorImagesLoadCursors (dpy, images); 386 XcursorImagesDestroy (images); 387 } 388 return cursors; 389} 390 391static const char _XcursorStandardNames[] = 392 "X_cursor\0" 393 "arrow\0" 394 "based_arrow_down\0" 395 "based_arrow_up\0" 396 "boat\0" 397 "bogosity\0" 398 "bottom_left_corner\0" 399 "bottom_right_corner\0" 400 "bottom_side\0" 401 "bottom_tee\0" 402 "box_spiral\0" 403 "center_ptr\0" 404 "circle\0" 405 "clock\0" 406 "coffee_mug\0" 407 "cross\0" 408 "cross_reverse\0" 409 "crosshair\0" 410 "diamond_cross\0" 411 "dot\0" 412 "dotbox\0" 413 "double_arrow\0" 414 "draft_large\0" 415 "draft_small\0" 416 "draped_box\0" 417 "exchange\0" 418 "fleur\0" 419 "gobbler\0" 420 "gumby\0" 421 "hand1\0" 422 "hand2\0" 423 "heart\0" 424 "icon\0" 425 "iron_cross\0" 426 "left_ptr\0" 427 "left_side\0" 428 "left_tee\0" 429 "leftbutton\0" 430 "ll_angle\0" 431 "lr_angle\0" 432 "man\0" 433 "middlebutton\0" 434 "mouse\0" 435 "pencil\0" 436 "pirate\0" 437 "plus\0" 438 "question_arrow\0" 439 "right_ptr\0" 440 "right_side\0" 441 "right_tee\0" 442 "rightbutton\0" 443 "rtl_logo\0" 444 "sailboat\0" 445 "sb_down_arrow\0" 446 "sb_h_double_arrow\0" 447 "sb_left_arrow\0" 448 "sb_right_arrow\0" 449 "sb_up_arrow\0" 450 "sb_v_double_arrow\0" 451 "shuttle\0" 452 "sizing\0" 453 "spider\0" 454 "spraycan\0" 455 "star\0" 456 "target\0" 457 "tcross\0" 458 "top_left_arrow\0" 459 "top_left_corner\0" 460 "top_right_corner\0" 461 "top_side\0" 462 "top_tee\0" 463 "trek\0" 464 "ul_angle\0" 465 "umbrella\0" 466 "ur_angle\0" 467 "watch\0" 468 "xterm"; 469 470static const unsigned short _XcursorStandardNameOffsets[] = { 471 0, 9, 15, 32, 47, 52, 61, 80, 100, 112, 123, 134, 145, 152, 158, 472 169, 175, 189, 199, 213, 217, 224, 237, 249, 261, 272, 281, 287, 473 295, 301, 307, 313, 319, 324, 335, 344, 354, 363, 374, 383, 392, 474 396, 409, 415, 422, 429, 434, 449, 459, 470, 480, 492, 501, 510, 475 524, 542, 556, 571, 583, 601, 609, 616, 623, 632, 637, 644, 651, 476 666, 682, 699, 708, 716, 721, 730, 739, 748, 754 477}; 478 479#define NUM_STANDARD_NAMES (sizeof _XcursorStandardNameOffsets / sizeof _XcursorStandardNameOffsets[0]) 480 481#define STANDARD_NAME(id) \ 482 _XcursorStandardNames + _XcursorStandardNameOffsets[id] 483 484XcursorImage * 485XcursorShapeLoadImage (unsigned int shape, const char *theme, int size) 486{ 487 unsigned int id = shape >> 1; 488 489 if (id < NUM_STANDARD_NAMES) 490 return XcursorLibraryLoadImage (STANDARD_NAME (id), theme, size); 491 else 492 return NULL; 493} 494 495XcursorImages * 496XcursorShapeLoadImages (unsigned int shape, const char *theme, int size) 497{ 498 unsigned int id = shape >> 1; 499 500 if (id < NUM_STANDARD_NAMES) 501 return XcursorLibraryLoadImages (STANDARD_NAME (id), theme, size); 502 else 503 return NULL; 504} 505 506Cursor 507XcursorShapeLoadCursor (Display *dpy, unsigned int shape) 508{ 509 unsigned int id = shape >> 1; 510 511 if (id < NUM_STANDARD_NAMES) 512 return XcursorLibraryLoadCursor (dpy, STANDARD_NAME (id)); 513 else 514 return 0; 515} 516 517XcursorCursors * 518XcursorShapeLoadCursors (Display *dpy, unsigned int shape) 519{ 520 unsigned int id = shape >> 1; 521 522 if (id < NUM_STANDARD_NAMES) 523 return XcursorLibraryLoadCursors (dpy, STANDARD_NAME (id)); 524 else 525 return NULL; 526} 527 528int 529XcursorLibraryShape (const char *library) 530{ 531 int low, high; 532 int mid; 533 int c; 534 535 low = 0; 536 high = NUM_STANDARD_NAMES - 1; 537 while (low < high - 1) 538 { 539 mid = (low + high) >> 1; 540 c = strcmp (library, STANDARD_NAME (mid)); 541 if (c == 0) 542 return (mid << 1); 543 if (c > 0) 544 low = mid; 545 else 546 high = mid; 547 } 548 while (low <= high) 549 { 550 if (!strcmp (library, STANDARD_NAME (low))) 551 return (low << 1); 552 low++; 553 } 554 return -1; 555} 556