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