You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
преди 18 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * libcucul Canvas for ultrafast compositing of Unicode letters
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This library is free software. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. /*
  15. * This file contains the main functions used by \e libcucul applications
  16. * to initialise a drawing context.
  17. */
  18. #include "config.h"
  19. #if !defined(__KERNEL__)
  20. # include <stdio.h>
  21. # include <stdlib.h>
  22. # include <string.h>
  23. # include <time.h>
  24. # include <sys/types.h>
  25. # if defined(HAVE_UNISTD_H)
  26. # include <unistd.h>
  27. # endif
  28. #endif
  29. #include "cucul.h"
  30. #include "cucul_internals.h"
  31. static int cucul_resize(cucul_canvas_t *, unsigned int, unsigned int);
  32. /** \brief Initialise a \e libcucul canvas.
  33. *
  34. * Initialise internal \e libcucul structures and the backend that will
  35. * be used for subsequent graphical operations. It must be the first
  36. * \e libcucul function to be called in a function. cucul_free_canvas()
  37. * should be called at the end of the program to free all allocated resources.
  38. *
  39. * Both the cursor and the canvas' handle are initialised at the top-left
  40. * corner.
  41. *
  42. * If an error occurs, NULL is returned and \b errno is set accordingly:
  43. * - \c ENOMEM Not enough memory for the requested canvas size.
  44. *
  45. * \param width The desired canvas width
  46. * \param height The desired canvas height
  47. * \return A libcucul canvas handle upon success, NULL if an error occurred.
  48. */
  49. cucul_canvas_t * cucul_create_canvas(unsigned int width, unsigned int height)
  50. {
  51. cucul_canvas_t *cv = malloc(sizeof(cucul_canvas_t));
  52. if(!cv)
  53. goto nomem;
  54. cv->refcount = 0;
  55. cv->autoinc = 0;
  56. cv->resize_callback = NULL;
  57. cv->resize_data = NULL;
  58. cv->frame = 0;
  59. cv->framecount = 1;
  60. cv->frames = malloc(sizeof(struct cucul_frame));
  61. if(!cv->frames)
  62. {
  63. free(cv);
  64. goto nomem;
  65. }
  66. cv->frames[0].width = cv->frames[0].height = 0;
  67. cv->frames[0].chars = NULL;
  68. cv->frames[0].attrs = NULL;
  69. cv->frames[0].x = cv->frames[0].y = 0;
  70. cv->frames[0].handlex = cv->frames[0].handley = 0;
  71. cv->frames[0].curattr = 0;
  72. cv->frames[0].name = strdup("frame#00000000");
  73. _cucul_load_frame_info(cv);
  74. cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
  75. cv->ff = NULL;
  76. if(cucul_resize(cv, width, height) < 0)
  77. {
  78. int saved_errno = geterrno();
  79. free(cv->frames[0].name);
  80. free(cv->frames);
  81. free(cv);
  82. seterrno(saved_errno);
  83. return NULL;
  84. }
  85. return cv;
  86. nomem:
  87. seterrno(ENOMEM);
  88. return NULL;
  89. }
  90. /** \brief Manage a canvas.
  91. *
  92. * Lock a canvas to prevent it from being resized. If non-NULL,
  93. * the \e callback function pointer will be called upon each
  94. * \e cucul_set_canvas_size call and if the returned value is zero, the
  95. * canvas resize request will be denied.
  96. *
  97. * This function is only useful for display drivers such as the \e libcaca
  98. * library.
  99. *
  100. * If an error occurs, -1 is returned and \b errno is set accordingly:
  101. * - \c EBUSY The canvas is already being managed.
  102. *
  103. * \param cv A libcucul canvas.
  104. * \param callback An optional callback function pointer.
  105. * \param p The argument to be passed to \e callback.
  106. * \return 0 in case of success, -1 if an error occurred.
  107. */
  108. int cucul_manage_canvas(cucul_canvas_t *cv, int (*callback)(void *), void *p)
  109. {
  110. if(cv->refcount)
  111. {
  112. seterrno(EBUSY);
  113. return -1;
  114. }
  115. cv->resize_callback = callback;
  116. cv->resize_data = p;
  117. cv->refcount = 1;
  118. return 0;
  119. }
  120. /** \brief Unmanage a canvas.
  121. *
  122. * Unlock a canvas previously locked by cucul_manage_canvas(). For safety
  123. * reasons, the callback and callback data arguments must be the same as for
  124. * the cucul_manage_canvas() call.
  125. *
  126. * This function is only useful for display drivers such as the \e libcaca
  127. * library.
  128. *
  129. * If an error occurs, -1 is returned and \b errno is set accordingly:
  130. * - \c EINVAL The canvas is not managed, or the callback arguments do
  131. * not match.
  132. *
  133. * \param cv A libcucul canvas.
  134. * \param callback The \e callback argument previously passed to
  135. cucul_manage_canvas().
  136. * \param p The \e p argument previously passed to cucul_manage_canvas().
  137. * \return 0 in case of success, -1 if an error occurred.
  138. */
  139. int cucul_unmanage_canvas(cucul_canvas_t *cv, int (*callback)(void *), void *p)
  140. {
  141. if(!cv->refcount
  142. || cv->resize_callback != callback || cv->resize_data != p)
  143. {
  144. seterrno(EINVAL);
  145. return -1;
  146. }
  147. cv->refcount = 0;
  148. return 0;
  149. }
  150. /** \brief Resize a canvas.
  151. *
  152. * Set the canvas' width and height, in character cells.
  153. *
  154. * The contents of the canvas are preserved to the extent of the new
  155. * canvas size. Newly allocated character cells at the right and/or at
  156. * the bottom of the canvas are filled with spaces.
  157. *
  158. * If as a result of the resize the cursor coordinates fall outside the
  159. * new canvas boundaries, they are readjusted. For instance, if the
  160. * current X cursor coordinate is 11 and the requested width is 10, the
  161. * new X cursor coordinate will be 10.
  162. *
  163. * It is an error to try to resize the canvas if an output driver has
  164. * been attached to the canvas using caca_create_display(). You need to
  165. * remove the output driver using caca_free_display() before you can change
  166. * the canvas size again. However, the caca output driver can cause a
  167. * canvas resize through user interaction. See the caca_event() documentation
  168. * for more about this.
  169. *
  170. * If an error occurs, -1 is returned and \b errno is set accordingly:
  171. * - \c EBUSY The canvas is in use by a display driver and cannot be resized.
  172. * - \c ENOMEM Not enough memory for the requested canvas size. If this
  173. * happens, the canvas handle becomes invalid and should not be used.
  174. *
  175. * \param cv A libcucul canvas.
  176. * \param width The desired canvas width.
  177. * \param height The desired canvas height.
  178. * \return 0 in case of success, -1 if an error occurred.
  179. */
  180. int cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
  181. unsigned int height)
  182. {
  183. if(cv->refcount && cv->resize_callback
  184. && !cv->resize_callback(cv->resize_data))
  185. {
  186. seterrno(EBUSY);
  187. return -1;
  188. }
  189. return cucul_resize(cv, width, height);
  190. }
  191. /** \brief Get the canvas width.
  192. *
  193. * Return the current canvas' width, in character cells.
  194. *
  195. * This function never fails.
  196. *
  197. * \param cv A libcucul canvas.
  198. * \return The canvas width.
  199. */
  200. unsigned int cucul_get_canvas_width(cucul_canvas_t const *cv)
  201. {
  202. return cv->width;
  203. }
  204. /** \brief Get the canvas height.
  205. *
  206. * Returns the current canvas' height, in character cells.
  207. *
  208. * This function never fails.
  209. *
  210. * \param cv A libcucul canvas.
  211. * \return The canvas height.
  212. */
  213. unsigned int cucul_get_canvas_height(cucul_canvas_t const *cv)
  214. {
  215. return cv->height;
  216. }
  217. /** \brief Get the canvas character array.
  218. *
  219. * Return the current canvas' internal character array. The array elements
  220. * consist in native endian 32-bit Unicode values as returned by
  221. * cucul_get_char().
  222. *
  223. * This function is only useful for display drivers such as the \e libcaca
  224. * library.
  225. *
  226. * This function never fails.
  227. *
  228. * \param cv A libcucul canvas.
  229. * \return The canvas character array.
  230. */
  231. unsigned char const * cucul_get_canvas_chars(cucul_canvas_t const *cv)
  232. {
  233. return (unsigned char const *)cv->chars;
  234. }
  235. /** \brief Get the canvas attribute array.
  236. *
  237. * Returns the current canvas' internal attribute array. The array elements
  238. * consist in native endian 32-bit attribute values as returned by
  239. * cucul_get_attr().
  240. *
  241. * This function is only useful for display drivers such as the \e libcaca
  242. * library.
  243. *
  244. * This function never fails.
  245. *
  246. * \param cv A libcucul canvas.
  247. * \return The canvas attribute array.
  248. */
  249. unsigned char const * cucul_get_canvas_attrs(cucul_canvas_t const *cv)
  250. {
  251. return (unsigned char const *)cv->attrs;
  252. }
  253. /** \brief Uninitialise \e libcucul.
  254. *
  255. * Free all resources allocated by cucul_create_canvas(). After
  256. * this function has been called, no other \e libcucul functions may be
  257. * used unless a new call to cucul_create_canvas() is done.
  258. *
  259. * If an error occurs, -1 is returned and \b errno is set accordingly:
  260. * - \c EBUSY The canvas is in use by a display driver and cannot be freed.
  261. *
  262. * \param cv A libcucul canvas.
  263. * \return 0 in case of success, -1 if an error occurred.
  264. */
  265. int cucul_free_canvas(cucul_canvas_t *cv)
  266. {
  267. unsigned int f;
  268. if(cv->refcount)
  269. {
  270. seterrno(EBUSY);
  271. return -1;
  272. }
  273. for(f = 0; f < cv->framecount; f++)
  274. {
  275. free(cv->frames[f].chars);
  276. free(cv->frames[f].attrs);
  277. free(cv->frames[f].name);
  278. }
  279. cucul_canvas_set_figfont(cv, NULL);
  280. free(cv->frames);
  281. free(cv);
  282. return 0;
  283. }
  284. /** \brief Generate a random integer within a range.
  285. *
  286. * Generate a random integer within the given range.
  287. *
  288. * This function never fails.
  289. *
  290. * \param min The lower bound of the integer range.
  291. * \param max The upper bound of the integer range.
  292. * \return A random integer comprised between \p min and \p max - 1
  293. * (inclusive).
  294. */
  295. int cucul_rand(int min, int max)
  296. {
  297. static int need_init = 1;
  298. if(need_init)
  299. {
  300. srand(getpid() + time(NULL));
  301. need_init = 0;
  302. }
  303. return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
  304. }
  305. /** \brief Return the \e libcucul version.
  306. *
  307. * Return a read-only string with the \e libcucul version information.
  308. *
  309. * This function never fails.
  310. *
  311. * \return The \e libcucul version information.
  312. */
  313. char const * cucul_get_version(void)
  314. {
  315. return VERSION;
  316. }
  317. /*
  318. * XXX: The following functions are local.
  319. */
  320. int cucul_resize(cucul_canvas_t *cv, unsigned int width, unsigned int height)
  321. {
  322. unsigned int x, y, f, old_width, old_height, new_size, old_size;
  323. old_width = cv->width;
  324. old_height = cv->height;
  325. old_size = old_width * old_height;
  326. _cucul_save_frame_info(cv);
  327. cv->width = width;
  328. cv->height = height;
  329. new_size = width * height;
  330. /* Step 1: if new area is bigger, resize the memory area now. */
  331. if(new_size > old_size)
  332. {
  333. for(f = 0; f < cv->framecount; f++)
  334. {
  335. cv->frames[f].chars = realloc(cv->frames[f].chars,
  336. new_size * sizeof(uint32_t));
  337. cv->frames[f].attrs = realloc(cv->frames[f].attrs,
  338. new_size * sizeof(uint32_t));
  339. if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
  340. {
  341. seterrno(ENOMEM);
  342. return -1;
  343. }
  344. }
  345. }
  346. /* Step 2: move line data if necessary. */
  347. if(width == old_width)
  348. {
  349. /* Width did not change, which means we do not need to move data. */
  350. ;
  351. }
  352. else if(width > old_width)
  353. {
  354. /* New width is bigger than old width, which means we need to
  355. * copy lines starting from the bottom of the screen otherwise
  356. * we will overwrite information. */
  357. for(f = 0; f < cv->framecount; f++)
  358. {
  359. uint32_t *chars = cv->frames[f].chars;
  360. uint32_t *attrs = cv->frames[f].attrs;
  361. for(y = height < old_height ? height : old_height; y--; )
  362. {
  363. uint32_t attr = cv->frames[f].curattr;
  364. for(x = old_width; x--; )
  365. {
  366. chars[y * width + x] = chars[y * old_width + x];
  367. attrs[y * width + x] = attrs[y * old_width + x];
  368. }
  369. /* Zero the end of the line */
  370. for(x = width - old_width; x--; )
  371. {
  372. chars[y * width + old_width + x] = (uint32_t)' ';
  373. attrs[y * width + old_width + x] = attr;
  374. }
  375. }
  376. }
  377. }
  378. else
  379. {
  380. /* New width is smaller. Copy as many lines as possible. Ignore
  381. * the first line, it is already in place. */
  382. unsigned int lines = height < old_height ? height : old_height;
  383. for(f = 0; f < cv->framecount; f++)
  384. {
  385. uint32_t *chars = cv->frames[f].chars;
  386. uint32_t *attrs = cv->frames[f].attrs;
  387. for(y = 1; y < lines; y++)
  388. {
  389. for(x = 0; x < width; x++)
  390. {
  391. chars[y * width + x] = chars[y * old_width + x];
  392. attrs[y * width + x] = attrs[y * old_width + x];
  393. }
  394. }
  395. }
  396. }
  397. /* Step 3: fill the bottom of the new screen if necessary. */
  398. if(height > old_height)
  399. {
  400. for(f = 0; f < cv->framecount; f++)
  401. {
  402. uint32_t *chars = cv->frames[f].chars;
  403. uint32_t *attrs = cv->frames[f].attrs;
  404. uint32_t attr = cv->frames[f].curattr;
  405. /* Zero the bottom of the screen */
  406. for(x = (height - old_height) * width; x--; )
  407. {
  408. chars[old_height * width + x] = (uint32_t)' ';
  409. attrs[old_height * width + x] = attr;
  410. }
  411. }
  412. }
  413. /* Step 4: if new area is smaller, resize memory area now. */
  414. if(new_size < old_size)
  415. {
  416. for(f = 0; f < cv->framecount; f++)
  417. {
  418. cv->frames[f].chars = realloc(cv->frames[f].chars,
  419. new_size * sizeof(uint32_t));
  420. cv->frames[f].attrs = realloc(cv->frames[f].attrs,
  421. new_size * sizeof(uint32_t));
  422. if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
  423. {
  424. seterrno(ENOMEM);
  425. return -1;
  426. }
  427. }
  428. }
  429. /* Set new size */
  430. for(f = 0; f < cv->framecount; f++)
  431. {
  432. if(cv->frames[f].x > (int)width)
  433. cv->frames[f].x = width;
  434. if(cv->frames[f].y > (int)height)
  435. cv->frames[f].y = height;
  436. cv->frames[f].width = width;
  437. cv->frames[f].height = height;
  438. }
  439. /* Reset the current frame shortcuts */
  440. _cucul_load_frame_info(cv);
  441. return 0;
  442. }