xcursor.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. /*
  2. * Copyright © 2002 Keith Packard
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining
  5. * a copy of this software and associated documentation files (the
  6. * "Software"), to deal in the Software without restriction, including
  7. * without limitation the rights to use, copy, modify, merge, publish,
  8. * distribute, sublicense, and/or sell copies of the Software, and to
  9. * permit persons to whom the Software is furnished to do so, subject to
  10. * the following conditions:
  11. *
  12. * The above copyright notice and this permission notice (including the
  13. * next paragraph) shall be included in all copies or substantial
  14. * portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  20. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  21. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE.
  24. */
  25. #define _GNU_SOURCE
  26. #include "xcursor.h"
  27. #include <stdbool.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <dirent.h>
  32. /*
  33. * Cursor files start with a header. The header
  34. * contains a magic number, a version number and a
  35. * table of contents which has type and offset information
  36. * for the remaining tables in the file.
  37. *
  38. * File minor versions increment for compatible changes
  39. * File major versions increment for incompatible changes (never, we hope)
  40. *
  41. * Chunks of the same type are always upward compatible. Incompatible
  42. * changes are made with new chunk types; the old data can remain under
  43. * the old type. Upward compatible changes can add header data as the
  44. * header lengths are specified in the file.
  45. *
  46. * File:
  47. * FileHeader
  48. * LISTofChunk
  49. *
  50. * FileHeader:
  51. * CARD32 magic magic number
  52. * CARD32 header bytes in file header
  53. * CARD32 version file version
  54. * CARD32 ntoc number of toc entries
  55. * LISTofFileToc toc table of contents
  56. *
  57. * FileToc:
  58. * CARD32 type entry type
  59. * CARD32 subtype entry subtype (size for images)
  60. * CARD32 position absolute file position
  61. */
  62. #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
  63. /*
  64. * This version number is stored in cursor files; changes to the
  65. * file format require updating this version number
  66. */
  67. #define XCURSOR_FILE_MAJOR 1
  68. #define XCURSOR_FILE_MINOR 0
  69. #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
  70. #define XCURSOR_FILE_HEADER_LEN (4 * 4)
  71. #define XCURSOR_FILE_TOC_LEN (3 * 4)
  72. struct xcursor_file_toc {
  73. uint32_t type; /* chunk type */
  74. uint32_t subtype; /* subtype (size for images) */
  75. uint32_t position; /* absolute position in file */
  76. };
  77. struct xcursor_file_header {
  78. uint32_t magic; /* magic number */
  79. uint32_t header; /* byte length of header */
  80. uint32_t version; /* file version number */
  81. uint32_t ntoc; /* number of toc entries */
  82. struct xcursor_file_toc *tocs; /* table of contents */
  83. };
  84. /*
  85. * The rest of the file is a list of chunks, each tagged by type
  86. * and version.
  87. *
  88. * Chunk:
  89. * ChunkHeader
  90. * <extra type-specific header fields>
  91. * <type-specific data>
  92. *
  93. * ChunkHeader:
  94. * CARD32 header bytes in chunk header + type header
  95. * CARD32 type chunk type
  96. * CARD32 subtype chunk subtype
  97. * CARD32 version chunk type version
  98. */
  99. #define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
  100. struct xcursor_chunk_header {
  101. uint32_t header; /* bytes in chunk header */
  102. uint32_t type; /* chunk type */
  103. uint32_t subtype; /* chunk subtype (size for images) */
  104. uint32_t version; /* version of this type */
  105. };
  106. /*
  107. * Each cursor image occupies a separate image chunk.
  108. * The length of the image header follows the chunk header
  109. * so that future versions can extend the header without
  110. * breaking older applications
  111. *
  112. * Image:
  113. * ChunkHeader header chunk header
  114. * CARD32 width actual width
  115. * CARD32 height actual height
  116. * CARD32 xhot hot spot x
  117. * CARD32 yhot hot spot y
  118. * CARD32 delay animation delay
  119. * LISTofCARD32 pixels ARGB pixels
  120. */
  121. #define XCURSOR_IMAGE_TYPE 0xfffd0002
  122. #define XCURSOR_IMAGE_VERSION 1
  123. #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
  124. #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
  125. /*
  126. * From libXcursor/src/file.c
  127. */
  128. static struct xcursor_image *
  129. xcursor_image_create(int width, int height)
  130. {
  131. struct xcursor_image *image;
  132. if (width < 0 || height < 0)
  133. return NULL;
  134. if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE)
  135. return NULL;
  136. image = malloc(sizeof(struct xcursor_image) +
  137. width * height * sizeof(uint32_t));
  138. if (!image)
  139. return NULL;
  140. image->version = XCURSOR_IMAGE_VERSION;
  141. image->pixels = (uint32_t *) (image + 1);
  142. image->size = width > height ? width : height;
  143. image->width = width;
  144. image->height = height;
  145. image->delay = 0;
  146. return image;
  147. }
  148. static void
  149. xcursor_image_destroy(struct xcursor_image *image)
  150. {
  151. free(image);
  152. }
  153. static struct xcursor_images *
  154. xcursor_images_create(int size)
  155. {
  156. struct xcursor_images *images;
  157. images = malloc(sizeof(struct xcursor_images) +
  158. size * sizeof(struct xcursor_image *));
  159. if (!images)
  160. return NULL;
  161. images->nimage = 0;
  162. images->images = (struct xcursor_image **) (images + 1);
  163. images->name = NULL;
  164. return images;
  165. }
  166. void
  167. xcursor_images_destroy(struct xcursor_images *images)
  168. {
  169. int n;
  170. if (!images)
  171. return;
  172. for (n = 0; n < images->nimage; n++)
  173. xcursor_image_destroy(images->images[n]);
  174. free(images->name);
  175. free(images);
  176. }
  177. static bool
  178. xcursor_read_uint(FILE *file, uint32_t *u)
  179. {
  180. unsigned char bytes[4];
  181. if (!file || !u)
  182. return false;
  183. if (fread(bytes, 1, 4, file) != 4)
  184. return false;
  185. *u = ((uint32_t)(bytes[0]) << 0) |
  186. ((uint32_t)(bytes[1]) << 8) |
  187. ((uint32_t)(bytes[2]) << 16) |
  188. ((uint32_t)(bytes[3]) << 24);
  189. return true;
  190. }
  191. static void
  192. xcursor_file_header_destroy(struct xcursor_file_header *file_header)
  193. {
  194. free(file_header);
  195. }
  196. static struct xcursor_file_header *
  197. xcursor_file_header_create(uint32_t ntoc)
  198. {
  199. struct xcursor_file_header *file_header;
  200. if (ntoc > 0x10000)
  201. return NULL;
  202. file_header = malloc(sizeof(struct xcursor_file_header) +
  203. ntoc * sizeof(struct xcursor_file_toc));
  204. if (!file_header)
  205. return NULL;
  206. file_header->magic = XCURSOR_MAGIC;
  207. file_header->header = XCURSOR_FILE_HEADER_LEN;
  208. file_header->version = XCURSOR_FILE_VERSION;
  209. file_header->ntoc = ntoc;
  210. file_header->tocs = (struct xcursor_file_toc *) (file_header + 1);
  211. return file_header;
  212. }
  213. static struct xcursor_file_header *
  214. xcursor_read_file_header(FILE *file)
  215. {
  216. struct xcursor_file_header head, *file_header;
  217. uint32_t skip;
  218. unsigned int n;
  219. if (!file)
  220. return NULL;
  221. if (!xcursor_read_uint(file, &head.magic))
  222. return NULL;
  223. if (head.magic != XCURSOR_MAGIC)
  224. return NULL;
  225. if (!xcursor_read_uint(file, &head.header))
  226. return NULL;
  227. if (!xcursor_read_uint(file, &head.version))
  228. return NULL;
  229. if (!xcursor_read_uint(file, &head.ntoc))
  230. return NULL;
  231. if (head.header < XCURSOR_FILE_HEADER_LEN)
  232. return NULL;
  233. skip = head.header - XCURSOR_FILE_HEADER_LEN;
  234. if (skip)
  235. if (fseek(file, skip, SEEK_CUR) == EOF)
  236. return NULL;
  237. file_header = xcursor_file_header_create(head.ntoc);
  238. if (!file_header)
  239. return NULL;
  240. file_header->magic = head.magic;
  241. file_header->header = head.header;
  242. file_header->version = head.version;
  243. file_header->ntoc = head.ntoc;
  244. for (n = 0; n < file_header->ntoc; n++) {
  245. if (!xcursor_read_uint(file, &file_header->tocs[n].type))
  246. break;
  247. if (!xcursor_read_uint(file, &file_header->tocs[n].subtype))
  248. break;
  249. if (!xcursor_read_uint(file, &file_header->tocs[n].position))
  250. break;
  251. }
  252. if (n != file_header->ntoc) {
  253. xcursor_file_header_destroy(file_header);
  254. return NULL;
  255. }
  256. return file_header;
  257. }
  258. static bool
  259. xcursor_seek_to_toc(FILE *file,
  260. struct xcursor_file_header *file_header,
  261. int toc)
  262. {
  263. if (!file || !file_header ||
  264. fseek(file, file_header->tocs[toc].position, SEEK_SET) == EOF)
  265. return false;
  266. return true;
  267. }
  268. static bool
  269. xcursor_file_read_chunk_header(FILE *file,
  270. struct xcursor_file_header *file_header,
  271. int toc,
  272. struct xcursor_chunk_header *chunk_header)
  273. {
  274. if (!file || !file_header || !chunk_header)
  275. return false;
  276. if (!xcursor_seek_to_toc(file, file_header, toc))
  277. return false;
  278. if (!xcursor_read_uint(file, &chunk_header->header))
  279. return false;
  280. if (!xcursor_read_uint(file, &chunk_header->type))
  281. return false;
  282. if (!xcursor_read_uint(file, &chunk_header->subtype))
  283. return false;
  284. if (!xcursor_read_uint(file, &chunk_header->version))
  285. return false;
  286. /* sanity check */
  287. if (chunk_header->type != file_header->tocs[toc].type ||
  288. chunk_header->subtype != file_header->tocs[toc].subtype)
  289. return false;
  290. return true;
  291. }
  292. static uint32_t
  293. dist(uint32_t a, uint32_t b)
  294. {
  295. return a > b ? a - b : b - a;
  296. }
  297. static uint32_t
  298. xcursor_file_best_size(struct xcursor_file_header *file_header,
  299. uint32_t size, int *nsizesp)
  300. {
  301. unsigned int n;
  302. int nsizes = 0;
  303. uint32_t best_size = 0;
  304. uint32_t this_size;
  305. if (!file_header || !nsizesp)
  306. return 0;
  307. for (n = 0; n < file_header->ntoc; n++) {
  308. if (file_header->tocs[n].type != XCURSOR_IMAGE_TYPE)
  309. continue;
  310. this_size = file_header->tocs[n].subtype;
  311. if (!best_size || dist(this_size, size) < dist(best_size, size)) {
  312. best_size = this_size;
  313. nsizes = 1;
  314. } else if (this_size == best_size) {
  315. nsizes++;
  316. }
  317. }
  318. *nsizesp = nsizes;
  319. return best_size;
  320. }
  321. static int
  322. xcursor_find_image_toc(struct xcursor_file_header *file_header,
  323. uint32_t size, int count)
  324. {
  325. unsigned int toc;
  326. uint32_t this_size;
  327. if (!file_header)
  328. return 0;
  329. for (toc = 0; toc < file_header->ntoc; toc++) {
  330. if (file_header->tocs[toc].type != XCURSOR_IMAGE_TYPE)
  331. continue;
  332. this_size = file_header->tocs[toc].subtype;
  333. if (this_size != size)
  334. continue;
  335. if (!count)
  336. break;
  337. count--;
  338. }
  339. if (toc == file_header->ntoc)
  340. return -1;
  341. return toc;
  342. }
  343. static struct xcursor_image *
  344. xcursor_read_image(FILE *file,
  345. struct xcursor_file_header *file_header,
  346. int toc)
  347. {
  348. struct xcursor_chunk_header chunk_header;
  349. struct xcursor_image head;
  350. struct xcursor_image *image;
  351. int n;
  352. uint32_t *p;
  353. if (!file || !file_header)
  354. return NULL;
  355. if (!xcursor_file_read_chunk_header(file, file_header, toc, &chunk_header))
  356. return NULL;
  357. if (!xcursor_read_uint(file, &head.width))
  358. return NULL;
  359. if (!xcursor_read_uint(file, &head.height))
  360. return NULL;
  361. if (!xcursor_read_uint(file, &head.xhot))
  362. return NULL;
  363. if (!xcursor_read_uint(file, &head.yhot))
  364. return NULL;
  365. if (!xcursor_read_uint(file, &head.delay))
  366. return NULL;
  367. /* sanity check data */
  368. if (head.width > XCURSOR_IMAGE_MAX_SIZE ||
  369. head.height > XCURSOR_IMAGE_MAX_SIZE)
  370. return NULL;
  371. if (head.width == 0 || head.height == 0)
  372. return NULL;
  373. if (head.xhot > head.width || head.yhot > head.height)
  374. return NULL;
  375. /* Create the image and initialize it */
  376. image = xcursor_image_create(head.width, head.height);
  377. if (image == NULL)
  378. return NULL;
  379. if (chunk_header.version < image->version)
  380. image->version = chunk_header.version;
  381. image->size = chunk_header.subtype;
  382. image->xhot = head.xhot;
  383. image->yhot = head.yhot;
  384. image->delay = head.delay;
  385. n = image->width * image->height;
  386. p = image->pixels;
  387. while (n--) {
  388. if (!xcursor_read_uint(file, p)) {
  389. xcursor_image_destroy(image);
  390. return NULL;
  391. }
  392. p++;
  393. }
  394. return image;
  395. }
  396. static struct xcursor_images *
  397. xcursor_xc_file_load_images(FILE *file, int size)
  398. {
  399. struct xcursor_file_header *file_header;
  400. uint32_t best_size;
  401. int nsize;
  402. struct xcursor_images *images;
  403. int n;
  404. int toc;
  405. if (!file || size < 0)
  406. return NULL;
  407. file_header = xcursor_read_file_header(file);
  408. if (!file_header)
  409. return NULL;
  410. best_size = xcursor_file_best_size(file_header, (uint32_t) size, &nsize);
  411. if (!best_size) {
  412. xcursor_file_header_destroy(file_header);
  413. return NULL;
  414. }
  415. images = xcursor_images_create(nsize);
  416. if (!images) {
  417. xcursor_file_header_destroy(file_header);
  418. return NULL;
  419. }
  420. for (n = 0; n < nsize; n++) {
  421. toc = xcursor_find_image_toc(file_header, best_size, n);
  422. if (toc < 0)
  423. break;
  424. images->images[images->nimage] = xcursor_read_image(file, file_header,
  425. toc);
  426. if (!images->images[images->nimage])
  427. break;
  428. images->nimage++;
  429. }
  430. xcursor_file_header_destroy(file_header);
  431. if (images->nimage != nsize) {
  432. xcursor_images_destroy(images);
  433. images = NULL;
  434. }
  435. return images;
  436. }
  437. /*
  438. * From libXcursor/src/library.c
  439. */
  440. #ifndef ICONDIR
  441. #define ICONDIR "/usr/X11R6/lib/X11/icons"
  442. #endif
  443. #ifndef XCURSORPATH
  444. #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
  445. #endif
  446. #define XDG_DATA_HOME_FALLBACK "~/.local/share"
  447. #define CURSORDIR "/icons"
  448. /** Get search path for cursor themes
  449. *
  450. * This function builds the list of directories to look for cursor
  451. * themes in. The format is PATH-like: directories are separated by
  452. * colons.
  453. *
  454. * The memory block returned by this function is allocated on the heap
  455. * and must be freed by the caller.
  456. */
  457. static char *
  458. xcursor_library_path(void)
  459. {
  460. const char *env_var, *suffix;
  461. char *path;
  462. size_t path_size;
  463. env_var = getenv("XCURSOR_PATH");
  464. if (env_var)
  465. return strdup(env_var);
  466. env_var = getenv("XDG_DATA_HOME");
  467. if (!env_var || env_var[0] != '/')
  468. env_var = XDG_DATA_HOME_FALLBACK;
  469. suffix = CURSORDIR ":" XCURSORPATH;
  470. path_size = strlen(env_var) + strlen(suffix) + 1;
  471. path = malloc(path_size);
  472. if (!path)
  473. return NULL;
  474. snprintf(path, path_size, "%s%s", env_var, suffix);
  475. return path;
  476. }
  477. static char *
  478. xcursor_build_theme_dir(const char *dir, const char *theme)
  479. {
  480. const char *colon;
  481. const char *tcolon;
  482. char *full;
  483. const char *home, *homesep;
  484. int dirlen;
  485. int homelen;
  486. int themelen;
  487. size_t full_size;
  488. if (!dir || !theme)
  489. return NULL;
  490. colon = strchr(dir, ':');
  491. if (!colon)
  492. colon = dir + strlen(dir);
  493. dirlen = colon - dir;
  494. tcolon = strchr(theme, ':');
  495. if (!tcolon)
  496. tcolon = theme + strlen(theme);
  497. themelen = tcolon - theme;
  498. home = "";
  499. homelen = 0;
  500. homesep = "";
  501. if (*dir == '~') {
  502. home = getenv("HOME");
  503. if (!home)
  504. return NULL;
  505. homelen = strlen(home);
  506. homesep = "/";
  507. dir++;
  508. dirlen--;
  509. }
  510. /*
  511. * add space for any needed directory separators, one per component,
  512. * and one for the trailing null
  513. */
  514. full_size = (size_t) 1 + homelen + 1 + dirlen + 1 + themelen + 1;
  515. full = malloc(full_size);
  516. if (!full)
  517. return NULL;
  518. snprintf(full, full_size, "%s%s%.*s/%.*s", home, homesep,
  519. dirlen, dir, themelen, theme);
  520. return full;
  521. }
  522. static char *
  523. xcursor_build_fullname(const char *dir, const char *subdir, const char *file)
  524. {
  525. char *full;
  526. size_t full_size;
  527. int ret;
  528. if (!dir || !subdir || !file)
  529. return NULL;
  530. full_size = strlen(dir) + 1 + strlen(subdir) + 1 + strlen(file) + 1;
  531. full = malloc(full_size);
  532. if (!full)
  533. return NULL;
  534. ret = snprintf(full, full_size, "%s/%s/%s", dir, subdir, file);
  535. if (ret < 0) {
  536. free(full);
  537. return NULL;
  538. }
  539. return full;
  540. }
  541. static const char *
  542. xcursor_next_path(const char *path)
  543. {
  544. const char *colon = strchr(path, ':');
  545. if (!colon)
  546. return NULL;
  547. return colon + 1;
  548. }
  549. static bool
  550. xcursor_white(char c)
  551. {
  552. return c == ' ' || c == '\t' || c == '\n';
  553. }
  554. static bool
  555. xcursor_sep(char c)
  556. {
  557. return c == ';' || c == ',';
  558. }
  559. static char *
  560. xcursor_theme_inherits(const char *full)
  561. {
  562. char *line = NULL;
  563. size_t line_size = 0;
  564. char *result = NULL;
  565. FILE *f;
  566. if (!full)
  567. return NULL;
  568. f = fopen(full, "r");
  569. if (!f)
  570. return NULL;
  571. while (getline(&line, &line_size, f) >= 0) {
  572. const char *l;
  573. char *r;
  574. if (strncmp(line, "Inherits", 8))
  575. continue;
  576. l = line + 8;
  577. while (*l == ' ')
  578. l++;
  579. if (*l != '=')
  580. continue;
  581. l++;
  582. while (*l == ' ')
  583. l++;
  584. result = malloc(strlen(l) + 1);
  585. if (!result)
  586. break;
  587. r = result;
  588. while (*l) {
  589. while (xcursor_sep(*l) || xcursor_white(*l))
  590. l++;
  591. if (!*l)
  592. break;
  593. if (r != result)
  594. *r++ = ':';
  595. while (*l && !xcursor_white(*l) && !xcursor_sep(*l))
  596. *r++ = *l++;
  597. }
  598. *r++ = '\0';
  599. break;
  600. }
  601. fclose(f);
  602. free(line);
  603. return result;
  604. }
  605. static void
  606. load_all_cursors_from_dir(const char *path, int size,
  607. void (*load_callback)(struct xcursor_images *, void *),
  608. void *user_data)
  609. {
  610. FILE *f;
  611. DIR *dir;
  612. struct dirent *ent;
  613. char *full;
  614. struct xcursor_images *images;
  615. if (!path)
  616. return;
  617. dir = opendir(path);
  618. if (!dir)
  619. return;
  620. for (ent = readdir(dir); ent; ent = readdir(dir)) {
  621. #ifdef _DIRENT_HAVE_D_TYPE
  622. if (ent->d_type != DT_UNKNOWN &&
  623. ent->d_type != DT_REG &&
  624. ent->d_type != DT_LNK)
  625. continue;
  626. #endif
  627. full = xcursor_build_fullname(path, "", ent->d_name);
  628. if (!full)
  629. continue;
  630. f = fopen(full, "r");
  631. if (!f) {
  632. free(full);
  633. continue;
  634. }
  635. images = xcursor_xc_file_load_images(f, size);
  636. if (images) {
  637. images->name = strdup(ent->d_name);
  638. load_callback(images, user_data);
  639. }
  640. fclose(f);
  641. free(full);
  642. }
  643. closedir(dir);
  644. }
  645. struct xcursor_nodelist {
  646. size_t nodelen;
  647. const char *node;
  648. struct xcursor_nodelist *next;
  649. };
  650. static bool
  651. nodelist_contains(struct xcursor_nodelist *nodelist, const char *s, size_t ss)
  652. {
  653. struct xcursor_nodelist *vi;
  654. for (vi = nodelist; vi && vi->node; vi = vi->next) {
  655. if (vi->nodelen == ss && !strncmp(s, vi->node, vi->nodelen))
  656. return true;
  657. }
  658. return false;
  659. }
  660. static void
  661. xcursor_load_theme_protected(const char *theme, int size,
  662. void (*load_callback)(struct xcursor_images *, void *),
  663. void *user_data,
  664. struct xcursor_nodelist *visited_nodes)
  665. {
  666. char *full, *dir;
  667. char *inherits = NULL;
  668. const char *path, *i;
  669. char *xcursor_path;
  670. size_t si;
  671. struct xcursor_nodelist current_node;
  672. if (!theme)
  673. theme = "default";
  674. current_node.next = visited_nodes;
  675. current_node.node = theme;
  676. current_node.nodelen = strlen(theme);
  677. visited_nodes = &current_node;
  678. xcursor_path = xcursor_library_path();
  679. for (path = xcursor_path;
  680. path;
  681. path = xcursor_next_path(path)) {
  682. dir = xcursor_build_theme_dir(path, theme);
  683. if (!dir)
  684. continue;
  685. full = xcursor_build_fullname(dir, "cursors", "");
  686. load_all_cursors_from_dir(full, size, load_callback,
  687. user_data);
  688. free(full);
  689. if (!inherits) {
  690. full = xcursor_build_fullname(dir, "", "index.theme");
  691. inherits = xcursor_theme_inherits(full);
  692. free(full);
  693. }
  694. free(dir);
  695. }
  696. for (i = inherits; i; i = xcursor_next_path(i)) {
  697. si = strlen(i);
  698. if (nodelist_contains(visited_nodes, i, si))
  699. continue;
  700. xcursor_load_theme_protected(i, size, load_callback, user_data, visited_nodes);
  701. }
  702. free(inherits);
  703. free(xcursor_path);
  704. }
  705. /** Load all the cursors of a theme
  706. *
  707. * This function loads all the cursor images of a given theme and its
  708. * inherited themes. Each cursor is loaded into a struct xcursor_images object
  709. * which is passed to the caller's load callback. If a cursor appears
  710. * more than once across all the inherited themes, the load callback
  711. * will be called multiple times, with possibly different struct xcursor_images
  712. * objects which have the same name. The user is expected to destroy the
  713. * struct xcursor_images objects passed to the callback with
  714. * xcursor_images_destroy().
  715. *
  716. * \param theme The name of theme that should be loaded
  717. * \param size The desired size of the cursor images
  718. * \param load_callback A callback function that will be called
  719. * for each cursor loaded. The first parameter is the struct xcursor_images
  720. * object representing the loaded cursor and the second is a pointer
  721. * to data provided by the user.
  722. * \param user_data The data that should be passed to the load callback
  723. */
  724. void
  725. xcursor_load_theme(const char *theme, int size,
  726. void (*load_callback)(struct xcursor_images *, void *),
  727. void *user_data)
  728. {
  729. xcursor_load_theme_protected(theme,
  730. size,
  731. load_callback,
  732. user_data,
  733. NULL);
  734. }