library.c revision e6d5e4e0
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 "~/.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 int 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)); 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 f = XcursorScanTheme (i, name); 264 if (inherits != NULL) 265 free (inherits); 266 return f; 267} 268 269XcursorImage * 270XcursorLibraryLoadImage (const char *file, const char *theme, int size) 271{ 272 FILE *f = NULL; 273 XcursorImage *image = NULL; 274 275 if (!file) 276 return NULL; 277 278 if (theme) 279 f = XcursorScanTheme (theme, file); 280 if (!f) 281 f = XcursorScanTheme ("default", file); 282 if (f == XCURSOR_SCAN_CORE) 283 return NULL; 284 if (f) 285 { 286 image = XcursorFileLoadImage (f, size); 287 fclose (f); 288 } 289 return image; 290} 291 292XcursorImages * 293XcursorLibraryLoadImages (const char *file, const char *theme, int size) 294{ 295 FILE *f = NULL; 296 XcursorImages *images = NULL; 297 298 if (!file) 299 return NULL; 300 301 if (theme) 302 f = XcursorScanTheme (theme, file); 303 if (!f) 304 f = XcursorScanTheme ("default", file); 305 if (f == XCURSOR_SCAN_CORE) 306 return NULL; 307 if (f) 308 { 309 images = XcursorFileLoadImages (f, size); 310 if (images) 311 XcursorImagesSetName (images, file); 312 fclose (f); 313 } 314 return images; 315} 316 317Cursor 318XcursorLibraryLoadCursor (Display *dpy, const char *file) 319{ 320 int size = XcursorGetDefaultSize (dpy); 321 char *theme = XcursorGetTheme (dpy); 322 XcursorImages *images = XcursorLibraryLoadImages (file, theme, size); 323 Cursor cursor; 324 325 if (!file) 326 return 0; 327 328 if (!images) 329 { 330 int id = XcursorLibraryShape (file); 331 332 if (id >= 0) 333 return _XcursorCreateFontCursor (dpy, id); 334 else 335 return 0; 336 } 337 cursor = XcursorImagesLoadCursor (dpy, images); 338 XcursorImagesDestroy (images); 339#if defined HAVE_XFIXES && XFIXES_MAJOR >= 2 340 XFixesSetCursorName (dpy, cursor, file); 341#endif 342 return cursor; 343} 344 345XcursorCursors * 346XcursorLibraryLoadCursors (Display *dpy, const char *file) 347{ 348 int size = XcursorGetDefaultSize (dpy); 349 char *theme = XcursorGetTheme (dpy); 350 XcursorImages *images = XcursorLibraryLoadImages (file, theme, size); 351 XcursorCursors *cursors; 352 353 if (!file) 354 return NULL; 355 356 if (!images) 357 { 358 int id = XcursorLibraryShape (file); 359 360 if (id >= 0) 361 { 362 cursors = XcursorCursorsCreate (dpy, 1); 363 if (cursors) 364 { 365 cursors->cursors[0] = _XcursorCreateFontCursor (dpy, id); 366 if (cursors->cursors[0] == None) 367 { 368 XcursorCursorsDestroy (cursors); 369 cursors = NULL; 370 } 371 else 372 cursors->ncursor = 1; 373 } 374 } 375 else 376 cursors = NULL; 377 } 378 else 379 { 380 cursors = XcursorImagesLoadCursors (dpy, images); 381 XcursorImagesDestroy (images); 382 } 383 return cursors; 384} 385 386static const char _XcursorStandardNames[] = 387 "X_cursor\0" 388 "arrow\0" 389 "based_arrow_down\0" 390 "based_arrow_up\0" 391 "boat\0" 392 "bogosity\0" 393 "bottom_left_corner\0" 394 "bottom_right_corner\0" 395 "bottom_side\0" 396 "bottom_tee\0" 397 "box_spiral\0" 398 "center_ptr\0" 399 "circle\0" 400 "clock\0" 401 "coffee_mug\0" 402 "cross\0" 403 "cross_reverse\0" 404 "crosshair\0" 405 "diamond_cross\0" 406 "dot\0" 407 "dotbox\0" 408 "double_arrow\0" 409 "draft_large\0" 410 "draft_small\0" 411 "draped_box\0" 412 "exchange\0" 413 "fleur\0" 414 "gobbler\0" 415 "gumby\0" 416 "hand1\0" 417 "hand2\0" 418 "heart\0" 419 "icon\0" 420 "iron_cross\0" 421 "left_ptr\0" 422 "left_side\0" 423 "left_tee\0" 424 "leftbutton\0" 425 "ll_angle\0" 426 "lr_angle\0" 427 "man\0" 428 "middlebutton\0" 429 "mouse\0" 430 "pencil\0" 431 "pirate\0" 432 "plus\0" 433 "question_arrow\0" 434 "right_ptr\0" 435 "right_side\0" 436 "right_tee\0" 437 "rightbutton\0" 438 "rtl_logo\0" 439 "sailboat\0" 440 "sb_down_arrow\0" 441 "sb_h_double_arrow\0" 442 "sb_left_arrow\0" 443 "sb_right_arrow\0" 444 "sb_up_arrow\0" 445 "sb_v_double_arrow\0" 446 "shuttle\0" 447 "sizing\0" 448 "spider\0" 449 "spraycan\0" 450 "star\0" 451 "target\0" 452 "tcross\0" 453 "top_left_arrow\0" 454 "top_left_corner\0" 455 "top_right_corner\0" 456 "top_side\0" 457 "top_tee\0" 458 "trek\0" 459 "ul_angle\0" 460 "umbrella\0" 461 "ur_angle\0" 462 "watch\0" 463 "xterm"; 464 465static const unsigned short _XcursorStandardNameOffsets[] = { 466 0, 9, 15, 32, 47, 52, 61, 80, 100, 112, 123, 134, 145, 152, 158, 467 169, 175, 189, 199, 213, 217, 224, 237, 249, 261, 272, 281, 287, 468 295, 301, 307, 313, 319, 324, 335, 344, 354, 363, 374, 383, 392, 469 396, 409, 415, 422, 429, 434, 449, 459, 470, 480, 492, 501, 510, 470 524, 542, 556, 571, 583, 601, 609, 616, 623, 632, 637, 644, 651, 471 666, 682, 699, 708, 716, 721, 730, 739, 748, 754 472}; 473 474#define NUM_STANDARD_NAMES (sizeof _XcursorStandardNameOffsets / sizeof _XcursorStandardNameOffsets[0]) 475 476#define STANDARD_NAME(id) \ 477 _XcursorStandardNames + _XcursorStandardNameOffsets[id] 478 479XcursorImage * 480XcursorShapeLoadImage (unsigned int shape, const char *theme, int size) 481{ 482 unsigned int id = shape >> 1; 483 484 if (id < NUM_STANDARD_NAMES) 485 return XcursorLibraryLoadImage (STANDARD_NAME (id), theme, size); 486 else 487 return NULL; 488} 489 490XcursorImages * 491XcursorShapeLoadImages (unsigned int shape, const char *theme, int size) 492{ 493 unsigned int id = shape >> 1; 494 495 if (id < NUM_STANDARD_NAMES) 496 return XcursorLibraryLoadImages (STANDARD_NAME (id), theme, size); 497 else 498 return NULL; 499} 500 501Cursor 502XcursorShapeLoadCursor (Display *dpy, unsigned int shape) 503{ 504 unsigned int id = shape >> 1; 505 506 if (id < NUM_STANDARD_NAMES) 507 return XcursorLibraryLoadCursor (dpy, STANDARD_NAME (id)); 508 else 509 return 0; 510} 511 512XcursorCursors * 513XcursorShapeLoadCursors (Display *dpy, unsigned int shape) 514{ 515 unsigned int id = shape >> 1; 516 517 if (id < NUM_STANDARD_NAMES) 518 return XcursorLibraryLoadCursors (dpy, STANDARD_NAME (id)); 519 else 520 return NULL; 521} 522 523int 524XcursorLibraryShape (const char *library) 525{ 526 int low, high; 527 int mid; 528 int c; 529 530 low = 0; 531 high = NUM_STANDARD_NAMES - 1; 532 while (low < high - 1) 533 { 534 mid = (low + high) >> 1; 535 c = strcmp (library, STANDARD_NAME (mid)); 536 if (c == 0) 537 return (mid << 1); 538 if (c > 0) 539 low = mid; 540 else 541 high = mid; 542 } 543 while (low <= high) 544 { 545 if (!strcmp (library, STANDARD_NAME (low))) 546 return (low << 1); 547 low++; 548 } 549 return -1; 550} 551