logo

xcursorgen-nox

xcursorgen(1) without libX11/libXcursor dependency

xcursorgen.c (10631B)


  1. /*
  2. * xcursorgen.c
  3. *
  4. * Copyright (C) 2002 Manish Singh
  5. *
  6. * SPDX-License-Identifier: MIT
  7. *
  8. * Permission to use, copy, modify, distribute, and sell this software and its
  9. * documentation for any purpose is hereby granted without fee, provided that
  10. * the above copyright notice appear in all copies and that both that
  11. * copyright notice and this permission notice appear in supporting
  12. * documentation, and that the name of Manish Singh not be used in
  13. * advertising or publicity pertaining to distribution of the software without
  14. * specific, written prior permission. Manish Singh makes no
  15. * representations about the suitability of this software for any purpose. It
  16. * is provided "as is" without express or implied warranty.
  17. *
  18. * MANISH SINGH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  19. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  20. * EVENT SHALL MANISH SINGH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  21. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  22. * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  23. * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  24. * PERFORMANCE OF THIS SOFTWARE.
  25. */
  26. #define _POSIX_C_SOURCE 200809L
  27. #include "Xcursor/Xcursor.h"
  28. #include "config.h"
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <png.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. static const char *ProgramName;
  36. struct flist
  37. {
  38. int size;
  39. int xhot, yhot;
  40. int delay;
  41. char *pngfile;
  42. struct flist *next;
  43. };
  44. static void
  45. usage(const char *name)
  46. {
  47. fprintf(stderr,
  48. "usage: %s [-V] [--version] [-?] [--help] [-p <dir>] [--prefix <dir>] [CONFIG [OUT]]\n%s",
  49. name,
  50. "Generate an Xcursor file from a series of PNG images\n"
  51. "\n"
  52. " -V, --version display the version number and exit\n"
  53. " -?, --help display this message and exit\n"
  54. " -p, --prefix <dir> find cursor images in <dir>\n"
  55. "\n"
  56. "With no CONFIG, or when CONFIG is -, read standard input. "
  57. "Same with OUT and\n"
  58. "standard output.\n");
  59. }
  60. static int
  61. read_config_file(const char *config, struct flist **list)
  62. {
  63. FILE *fp;
  64. char line[4096], pngfile[4000];
  65. int size, xhot, yhot, delay;
  66. struct flist *start = NULL, *end = NULL, *curr;
  67. int count = 0;
  68. if(strcmp(config, "-") != 0)
  69. {
  70. fp = fopen(config, "r");
  71. if(!fp)
  72. {
  73. *list = NULL;
  74. return 0;
  75. }
  76. }
  77. else
  78. fp = stdin;
  79. while(fgets(line, sizeof(line), fp) != NULL)
  80. {
  81. if(line[0] == '#') continue;
  82. switch(sscanf(line, "%d %d %d %3999s %d", &size, &xhot, &yhot, pngfile, &delay))
  83. {
  84. case 4:
  85. delay = 50;
  86. break;
  87. case 5:
  88. break;
  89. default:
  90. {
  91. fprintf(stderr,
  92. "%s: Bad config file data on line %d of %s\n",
  93. ProgramName,
  94. count + 1,
  95. strcmp(config, "-") ? config : "stdin");
  96. fclose(fp);
  97. return 0;
  98. }
  99. }
  100. curr = malloc(sizeof(struct flist));
  101. if(curr == NULL)
  102. {
  103. fprintf(stderr, "%s: malloc() failed: %s\n", ProgramName, strerror(errno));
  104. fclose(fp);
  105. return 0;
  106. }
  107. curr->size = size;
  108. curr->xhot = xhot;
  109. curr->yhot = yhot;
  110. curr->delay = delay;
  111. curr->pngfile = strdup(pngfile);
  112. if(curr->pngfile == NULL)
  113. {
  114. fprintf(stderr, "%s: strdup() failed: %s\n", ProgramName, strerror(errno));
  115. fclose(fp);
  116. free(curr);
  117. return 0;
  118. }
  119. curr->next = NULL;
  120. if(start)
  121. {
  122. end->next = curr;
  123. end = curr;
  124. }
  125. else
  126. {
  127. start = curr;
  128. end = curr;
  129. }
  130. count++;
  131. }
  132. fclose(fp);
  133. *list = start;
  134. return count;
  135. }
  136. #define div_255(x) (((x) + 0x80 + (((x) + 0x80) >> 8)) >> 8)
  137. static void
  138. premultiply_data(png_structp png, png_row_infop row_info, png_bytep data)
  139. {
  140. png_size_t i;
  141. for(i = 0; i < row_info->rowbytes; i += 4)
  142. {
  143. unsigned char *base = &data[i];
  144. unsigned char blue = base[0];
  145. unsigned char green = base[1];
  146. unsigned char red = base[2];
  147. unsigned char alpha = base[3];
  148. XcursorPixel p;
  149. red = (unsigned char)div_255((unsigned)red * (unsigned)alpha);
  150. green = (unsigned char)div_255((unsigned)green * (unsigned)alpha);
  151. blue = (unsigned char)div_255((unsigned)blue * (unsigned)alpha);
  152. p = ((unsigned int)alpha << 24) | ((unsigned int)red << 16) | ((unsigned int)green << 8) |
  153. ((unsigned int)blue << 0);
  154. memcpy(base, &p, sizeof(XcursorPixel));
  155. }
  156. }
  157. static XcursorImage *
  158. load_image(struct flist *list, const char *prefix)
  159. {
  160. XcursorImage *image;
  161. png_structp png;
  162. png_infop info;
  163. png_bytepp rows;
  164. FILE *fp;
  165. png_uint_32 i;
  166. png_uint_32 width, height;
  167. int depth, color, interlace;
  168. char *file;
  169. png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  170. if(png == NULL) return NULL;
  171. info = png_create_info_struct(png);
  172. if(info == NULL)
  173. {
  174. png_destroy_read_struct(&png, NULL, NULL);
  175. return NULL;
  176. }
  177. if(setjmp(png_jmpbuf(png)))
  178. {
  179. png_destroy_read_struct(&png, &info, NULL);
  180. return NULL;
  181. }
  182. if(prefix)
  183. {
  184. #ifdef HAVE_ASPRINTF
  185. if(asprintf(&file, "%s/%s", prefix, list->pngfile) == -1)
  186. {
  187. fprintf(stderr, "%s: asprintf() failed: %s\n", ProgramName, strerror(errno));
  188. png_destroy_read_struct(&png, &info, NULL);
  189. return NULL;
  190. }
  191. #else
  192. file = malloc(strlen(prefix) + 1 + strlen(list->pngfile) + 1);
  193. if(file == NULL)
  194. {
  195. fprintf(stderr, "%s: malloc() failed: %s\n", ProgramName, strerror(errno));
  196. png_destroy_read_struct(&png, &info, NULL);
  197. return NULL;
  198. }
  199. strcpy(file, prefix);
  200. strcat(file, "/");
  201. strcat(file, list->pngfile);
  202. #endif
  203. }
  204. else
  205. file = list->pngfile;
  206. fp = fopen(file, "rb");
  207. if(prefix) free(file);
  208. if(fp == NULL)
  209. {
  210. png_destroy_read_struct(&png, &info, NULL);
  211. return NULL;
  212. }
  213. png_init_io(png, fp);
  214. png_read_info(png, info);
  215. png_get_IHDR(png, info, &width, &height, &depth, &color, &interlace, NULL, NULL);
  216. /* TODO: More needs to be done here maybe */
  217. if(color == PNG_COLOR_TYPE_PALETTE && depth <= 8) png_set_expand(png);
  218. if(color == PNG_COLOR_TYPE_GRAY && depth < 8) png_set_expand(png);
  219. if(png_get_valid(png, info, PNG_INFO_tRNS)) png_set_expand(png);
  220. if(depth == 16) png_set_strip_16(png);
  221. if(depth < 8) png_set_packing(png);
  222. if(color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png);
  223. if(interlace != PNG_INTERLACE_NONE) png_set_interlace_handling(png);
  224. png_set_bgr(png);
  225. png_set_filler(png, 255, PNG_FILLER_AFTER);
  226. png_set_read_user_transform_fn(png, premultiply_data);
  227. png_read_update_info(png, info);
  228. image = XcursorImageCreate((int)width, (int)height);
  229. if(image == NULL)
  230. {
  231. fprintf(stderr,
  232. "%s: XcursorImageCreate() failed to create %u x %u image\n"
  233. " for file %s\n",
  234. ProgramName,
  235. width,
  236. height,
  237. list->pngfile);
  238. fclose(fp);
  239. png_destroy_read_struct(&png, &info, NULL);
  240. return NULL;
  241. }
  242. image->size = (XcursorDim)list->size;
  243. image->xhot = (XcursorDim)list->xhot;
  244. image->yhot = (XcursorDim)list->yhot;
  245. image->delay = (XcursorUInt)list->delay;
  246. rows = malloc(sizeof(png_bytep) * height);
  247. if(rows == NULL)
  248. {
  249. fclose(fp);
  250. png_destroy_read_struct(&png, &info, NULL);
  251. return NULL;
  252. }
  253. for(i = 0; i < height; i++)
  254. rows[i] = (png_bytep)(image->pixels + i * width);
  255. png_read_image(png, rows);
  256. png_read_end(png, info);
  257. free(rows);
  258. fclose(fp);
  259. png_destroy_read_struct(&png, &info, NULL);
  260. return image;
  261. }
  262. static int
  263. write_cursors(int count, struct flist *list, const char *filename, const char *prefix)
  264. {
  265. XcursorImages *cimages;
  266. XcursorImage *image;
  267. int i;
  268. FILE *fp;
  269. int ret;
  270. if(strcmp(filename, "-") != 0)
  271. {
  272. fp = fopen(filename, "wb");
  273. if(!fp) return 1;
  274. }
  275. else
  276. fp = stdout;
  277. cimages = XcursorImagesCreate(count);
  278. cimages->nimage = count;
  279. for(i = 0; i < count; i++, list = list->next)
  280. {
  281. image = load_image(list, prefix);
  282. if(image == NULL)
  283. {
  284. fprintf(stderr, "%s: PNG error while reading %s\n", ProgramName, list->pngfile);
  285. fclose(fp);
  286. return 1;
  287. }
  288. cimages->images[i] = image;
  289. }
  290. ret = XcursorFileSaveImages(fp, cimages);
  291. fclose(fp);
  292. return ret ? 0 : 1;
  293. }
  294. #if 0
  295. static int
  296. check_image(char *image)
  297. {
  298. unsigned int width, height;
  299. unsigned char *data;
  300. int x_hot, y_hot;
  301. unsigned char hash[XCURSOR_BITMAP_HASH_SIZE];
  302. int i;
  303. if (XReadBitmapFileData(image, &width, &height, &data, &x_hot, &y_hot)
  304. != BitmapSuccess) {
  305. fprintf(stderr, "%s: Can't open bitmap file \"%s\"\n", ProgramName,
  306. image);
  307. return 1;
  308. }
  309. else {
  310. XImage ximage = {
  311. .width = (int) width,
  312. .height = (int) height,
  313. .depth = 1,
  314. .bits_per_pixel = 1,
  315. .xoffset = 0,
  316. .format = XYPixmap,
  317. .data = (char *) data,
  318. .byte_order = LSBFirst,
  319. .bitmap_unit = 8,
  320. .bitmap_bit_order = LSBFirst,
  321. .bitmap_pad = 8,
  322. .bytes_per_line = (width + 7) / 8
  323. };
  324. XcursorImageHash(&ximage, hash);
  325. }
  326. printf("%s: ", image);
  327. for (i = 0; i < XCURSOR_BITMAP_HASH_SIZE; i++)
  328. printf("%02x", hash[i]);
  329. printf("\n");
  330. return 0;
  331. }
  332. #endif
  333. int
  334. main(int argc, char *argv[])
  335. {
  336. struct flist *list;
  337. int count;
  338. const char *in = NULL, *out = NULL;
  339. const char *prefix = NULL;
  340. int i;
  341. ProgramName = argv[0];
  342. for(i = 1; i < argc; i++)
  343. {
  344. if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0)
  345. {
  346. printf("xcursorgen version %s\n", PACKAGE_VERSION);
  347. return 0;
  348. }
  349. if(strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "--help") == 0)
  350. {
  351. usage(argv[0]);
  352. return 0;
  353. }
  354. #if 0
  355. if (strcmp(argv[i], "-image") == 0) {
  356. int ret = 0;
  357. while (argv[++i]) {
  358. if (check_image(argv[i]))
  359. ret = i;
  360. }
  361. return ret;
  362. }
  363. #endif
  364. if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--prefix") == 0)
  365. {
  366. i++;
  367. if(argv[i] == NULL)
  368. {
  369. fprintf(stderr, "%s: %s requires an argument\n", argv[0], argv[i - 1]);
  370. usage(argv[0]);
  371. return 1;
  372. }
  373. prefix = argv[i];
  374. continue;
  375. }
  376. if(!in)
  377. in = argv[i];
  378. else if(!out)
  379. out = argv[i];
  380. else
  381. {
  382. fprintf(stderr, "%s: unexpected argument '%s'\n", argv[0], argv[i]);
  383. usage(argv[0]);
  384. return 1;
  385. }
  386. }
  387. if(!in) in = "-";
  388. if(!out) out = "-";
  389. count = read_config_file(in, &list);
  390. if(count == 0)
  391. {
  392. fprintf(
  393. stderr, "%s: Error reading config file %s\n", argv[0], strcmp(in, "-") ? in : "from stdin");
  394. return 1;
  395. }
  396. return write_cursors(count, list, out, prefix);
  397. }