Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

668 wiersze
20 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  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 libcaca 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 "caca.h"
  30. #include "caca_internals.h"
  31. static int caca_resize(caca_canvas_t *, int, int);
  32. /** \brief Initialise a \e libcaca canvas.
  33. *
  34. * Initialise internal \e libcaca structures and the backend that will
  35. * be used for subsequent graphical operations. It must be the first
  36. * \e libcaca function to be called in a function. caca_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 EINVAL Specified width or height is invalid.
  44. * - \c ENOMEM Not enough memory for the requested canvas size.
  45. *
  46. * \param width The desired canvas width
  47. * \param height The desired canvas height
  48. * \return A libcaca canvas handle upon success, NULL if an error occurred.
  49. */
  50. caca_canvas_t * caca_create_canvas(int width, int height)
  51. {
  52. caca_canvas_t *cv;
  53. if(width < 0 || height < 0)
  54. {
  55. seterrno(EINVAL);
  56. return NULL;
  57. }
  58. cv = malloc(sizeof(caca_canvas_t));
  59. if(!cv)
  60. goto nomem;
  61. cv->refcount = 0;
  62. cv->autoinc = 0;
  63. cv->resize_callback = NULL;
  64. cv->resize_data = NULL;
  65. cv->frame = 0;
  66. cv->framecount = 1;
  67. cv->frames = malloc(sizeof(struct caca_frame));
  68. if(!cv->frames)
  69. {
  70. free(cv);
  71. goto nomem;
  72. }
  73. cv->frames[0].width = cv->frames[0].height = 0;
  74. cv->frames[0].chars = NULL;
  75. cv->frames[0].attrs = NULL;
  76. cv->frames[0].x = cv->frames[0].y = 0;
  77. cv->frames[0].handlex = cv->frames[0].handley = 0;
  78. cv->frames[0].curattr = 0;
  79. cv->frames[0].name = strdup("frame#00000000");
  80. _caca_load_frame_info(cv);
  81. caca_set_color_ansi(cv, CACA_DEFAULT, CACA_TRANSPARENT);
  82. cv->dirty_xmin = 0;
  83. cv->dirty_xmax = -1;
  84. cv->dirty_ymin = 0;
  85. cv->dirty_ymax = -1;
  86. cv->ff = NULL;
  87. if(caca_resize(cv, width, height) < 0)
  88. {
  89. int saved_errno = geterrno();
  90. free(cv->frames[0].name);
  91. free(cv->frames);
  92. free(cv);
  93. seterrno(saved_errno);
  94. return NULL;
  95. }
  96. return cv;
  97. nomem:
  98. seterrno(ENOMEM);
  99. return NULL;
  100. }
  101. /** \brief Manage a canvas.
  102. *
  103. * Lock a canvas to prevent it from being resized. If non-NULL,
  104. * the \e callback function pointer will be called upon each
  105. * \e caca_set_canvas_size call and if the returned value is zero, the
  106. * canvas resize request will be denied.
  107. *
  108. * This function is only useful for display drivers such as the \e libcaca
  109. * library.
  110. *
  111. * If an error occurs, -1 is returned and \b errno is set accordingly:
  112. * - \c EBUSY The canvas is already being managed.
  113. *
  114. * \param cv A libcaca canvas.
  115. * \param callback An optional callback function pointer.
  116. * \param p The argument to be passed to \e callback.
  117. * \return 0 in case of success, -1 if an error occurred.
  118. */
  119. int caca_manage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
  120. {
  121. if(cv->refcount)
  122. {
  123. seterrno(EBUSY);
  124. return -1;
  125. }
  126. cv->resize_callback = callback;
  127. cv->resize_data = p;
  128. cv->refcount = 1;
  129. return 0;
  130. }
  131. /** \brief unmanage a canvas.
  132. *
  133. * unlock a canvas previously locked by caca_manage_canvas(). for safety
  134. * reasons, the callback and callback data arguments must be the same as for
  135. * the caca_manage_canvas() call.
  136. *
  137. * this function is only useful for display drivers such as the \e libcaca
  138. * library.
  139. *
  140. * if an error occurs, -1 is returned and \b errno is set accordingly:
  141. * - \c einval the canvas is not managed, or the callback arguments do
  142. * not match.
  143. *
  144. * \param cv a libcaca canvas.
  145. * \param callback the \e callback argument previously passed to
  146. * caca_manage_canvas().
  147. * \param p the \e p argument previously passed to caca_manage_canvas().
  148. * \return 0 in case of success, -1 if an error occurred.
  149. */
  150. int caca_unmanage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
  151. {
  152. if(!cv->refcount
  153. || cv->resize_callback != callback || cv->resize_data != p)
  154. {
  155. seterrno(EINVAL);
  156. return -1;
  157. }
  158. cv->refcount = 0;
  159. return 0;
  160. }
  161. /** \brief Resize a canvas.
  162. *
  163. * Set the canvas' width and height, in character cells.
  164. *
  165. * The contents of the canvas are preserved to the extent of the new
  166. * canvas size. Newly allocated character cells at the right and/or at
  167. * the bottom of the canvas are filled with spaces.
  168. *
  169. * If as a result of the resize the cursor coordinates fall outside the
  170. * new canvas boundaries, they are readjusted. For instance, if the
  171. * current X cursor coordinate is 11 and the requested width is 10, the
  172. * new X cursor coordinate will be 10.
  173. *
  174. * It is an error to try to resize the canvas if an output driver has
  175. * been attached to the canvas using caca_create_display(). You need to
  176. * remove the output driver using caca_free_display() before you can change
  177. * the canvas size again. However, the caca output driver can cause a
  178. * canvas resize through user interaction. See the caca_event() documentation
  179. * for more about this.
  180. *
  181. * If an error occurs, -1 is returned and \b errno is set accordingly:
  182. * - \c EINVAL Specified width or height is invalid.
  183. * - \c EBUSY The canvas is in use by a display driver and cannot be resized.
  184. * - \c ENOMEM Not enough memory for the requested canvas size. If this
  185. * happens, the canvas handle becomes invalid and should not be used.
  186. *
  187. * \param cv A libcaca canvas.
  188. * \param width The desired canvas width.
  189. * \param height The desired canvas height.
  190. * \return 0 in case of success, -1 if an error occurred.
  191. */
  192. int caca_set_canvas_size(caca_canvas_t *cv, int width, int height)
  193. {
  194. if(width < 0 || height < 0)
  195. {
  196. seterrno(EINVAL);
  197. return -1;
  198. }
  199. if(cv->refcount && cv->resize_callback
  200. && !cv->resize_callback(cv->resize_data))
  201. {
  202. seterrno(EBUSY);
  203. return -1;
  204. }
  205. return caca_resize(cv, width, height);
  206. }
  207. /** \brief Get the canvas width.
  208. *
  209. * Return the current canvas' width, in character cells.
  210. *
  211. * This function never fails.
  212. *
  213. * \param cv A libcaca canvas.
  214. * \return The canvas width.
  215. */
  216. int caca_get_canvas_width(caca_canvas_t const *cv)
  217. {
  218. return cv->width;
  219. }
  220. /** \brief Get the canvas height.
  221. *
  222. * Returns the current canvas' height, in character cells.
  223. *
  224. * This function never fails.
  225. *
  226. * \param cv A libcaca canvas.
  227. * \return The canvas height.
  228. */
  229. int caca_get_canvas_height(caca_canvas_t const *cv)
  230. {
  231. return cv->height;
  232. }
  233. /** \brief Get the canvas character array.
  234. *
  235. * Return the current canvas' internal character array. The array elements
  236. * consist in native endian 32-bit Unicode values as returned by
  237. * caca_get_char().
  238. *
  239. * This function is only useful for display drivers such as the \e libcaca
  240. * library.
  241. *
  242. * This function never fails.
  243. *
  244. * \param cv A libcaca canvas.
  245. * \return The canvas character array.
  246. */
  247. uint8_t const * caca_get_canvas_chars(caca_canvas_t const *cv)
  248. {
  249. return (uint8_t const *)cv->chars;
  250. }
  251. /** \brief Get the canvas attribute array.
  252. *
  253. * Returns the current canvas' internal attribute array. The array elements
  254. * consist in native endian 32-bit attribute values as returned by
  255. * caca_get_attr().
  256. *
  257. * This function is only useful for display drivers such as the \e libcaca
  258. * library.
  259. *
  260. * This function never fails.
  261. *
  262. * \param cv A libcaca canvas.
  263. * \return The canvas attribute array.
  264. */
  265. uint8_t const * caca_get_canvas_attrs(caca_canvas_t const *cv)
  266. {
  267. return (uint8_t const *)cv->attrs;
  268. }
  269. /** \brief Get a canvas's dirty rectangle.
  270. *
  271. * Get the canvas's dirty rectangle coordinates. The dirty rectangle is
  272. * the smallest area containing all the cells that have changed since it
  273. * was last reset.
  274. *
  275. * The dirty rectangle is used internally by display drivers to optimise
  276. * rendering by avoiding to redraw the whole screen. Once the display driver
  277. * has rendered the canvas, it resets the dirty rectangle.
  278. *
  279. * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
  280. * the dirty rectangle is empty. It means that the canvas's contents have
  281. * not changed since the dirty rectangle was last reset.
  282. *
  283. * FIXME: having only one dirty rectangle instead of a list of rectangles
  284. * is a severe limitation, but the potential gain does not yet look to be
  285. * worth the implementation complexity of a multiple-rectangle scheme.
  286. *
  287. * This function never fails.
  288. *
  289. * \param cv A libcaca canvas.
  290. * \param xmin A pointer to an integer where the leftmost edge of the
  291. * dirty rectangle will be stored.
  292. * \param ymin A pointer to an integer where the topmost edge of the
  293. * dirty rectangle will be stored.
  294. * \param xmax A pointer to an integer where the rightmost edge of the
  295. * dirty rectangle will be stored.
  296. * \param ymax A pointer to an integer where the bottommost edge of the
  297. * dirty rectangle will be stored.
  298. * \return This function always returns 0.
  299. */
  300. int caca_get_dirty_rectangle(caca_canvas_t *cv, int *xmin, int *ymin,
  301. int *xmax, int *ymax)
  302. {
  303. *xmin = cv->dirty_xmin;
  304. *xmax = cv->dirty_xmax;
  305. *ymin = cv->dirty_ymin;
  306. *ymax = cv->dirty_ymax;
  307. return 0;
  308. }
  309. /** \brief Add a dirty rectangle to the canvas's dirty rectangle.
  310. *
  311. * Add an invalidating zone to the canvas's dirty rectangle. For more
  312. * information about the dirty rectangle, see caca_get_dirty_rectangle().
  313. *
  314. * This function may be useful to force refresh of a given zone of the
  315. * canvas even if the dirty rectangle tracking indicates that it is
  316. * unchanged.
  317. *
  318. * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
  319. * the dirty rectangle is empty. They will be silently ignored.
  320. *
  321. * This function never fails.
  322. *
  323. * \param cv A libcaca canvas.
  324. * \param xmin The leftmost edge of the additional dirty rectangle.
  325. * \param ymin The topmost edge of the additional dirty rectangle.
  326. * \param xmax The rightmost edge of the additional dirty rectangle.
  327. * \param ymax The bottommost edge of the additional dirty rectangle.
  328. * \return This function always returns 0.
  329. */
  330. int caca_add_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin,
  331. int xmax, int ymax)
  332. {
  333. /* Ignore empty rectangles. */
  334. if(xmin > xmax || ymin > ymax)
  335. return 0;
  336. /* Ignore out-of-bounds rectangles. */
  337. if(xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height)
  338. return 0;
  339. if(xmin < cv->dirty_xmin)
  340. cv->dirty_xmin = xmin;
  341. if(xmax > cv->dirty_xmax)
  342. cv->dirty_xmax = xmax;
  343. if(ymin < cv->dirty_ymin)
  344. cv->dirty_ymin = ymin;
  345. if(ymax > cv->dirty_ymax)
  346. cv->dirty_ymax = ymax;
  347. return 0;
  348. }
  349. /** \brief Set a canvas's dirty rectangle.
  350. *
  351. * Set the canvas's dirty rectangle coordinates. For more information
  352. * about the dirty rectangle, see caca_get_dirty_rectangle().
  353. *
  354. * Values such that \b xmin > \b xmax or \b ymin > \b ymax indicate that
  355. * the dirty rectangle is empty.
  356. *
  357. * This function never fails.
  358. *
  359. * \param cv A libcaca canvas.
  360. * \param xmin The leftmost edge of the desired dirty rectangle.
  361. * \param ymin The topmost edge of the desired dirty rectangle.
  362. * \param xmax The rightmost edge of the desired dirty rectangle.
  363. * \param ymax The bottommost edge of the desired dirty rectangle.
  364. * \return This function always returns 0.
  365. */
  366. int caca_set_dirty_rectangle(caca_canvas_t *cv, int xmin, int ymin,
  367. int xmax, int ymax)
  368. {
  369. /* Normalise values indicating an empty or out-of-bounds rectangle. */
  370. if(xmin > xmax || ymin > ymax ||
  371. xmax < 0 || xmin >= cv->width || ymax < 0 || ymin >= cv->height)
  372. {
  373. xmin = cv->width;
  374. xmax = -1;
  375. ymin = cv->height;
  376. ymax = -1;
  377. }
  378. cv->dirty_xmin = xmin;
  379. cv->dirty_xmax = xmax;
  380. cv->dirty_ymin = ymin;
  381. cv->dirty_ymax = ymax;
  382. return 0;
  383. }
  384. /** \brief Free a \e libcaca canvas.
  385. *
  386. * Free all resources allocated by caca_create_canvas(). The canvas
  387. * pointer becomes invalid and must no longer be used unless a new call
  388. * to caca_create_canvas() is made.
  389. *
  390. * If an error occurs, -1 is returned and \b errno is set accordingly:
  391. * - \c EBUSY The canvas is in use by a display driver and cannot be freed.
  392. *
  393. * \param cv A libcaca canvas.
  394. * \return 0 in case of success, -1 if an error occurred.
  395. */
  396. int caca_free_canvas(caca_canvas_t *cv)
  397. {
  398. int f;
  399. if(cv->refcount)
  400. {
  401. seterrno(EBUSY);
  402. return -1;
  403. }
  404. for(f = 0; f < cv->framecount; f++)
  405. {
  406. free(cv->frames[f].chars);
  407. free(cv->frames[f].attrs);
  408. free(cv->frames[f].name);
  409. }
  410. caca_canvas_set_figfont(cv, NULL);
  411. free(cv->frames);
  412. free(cv);
  413. return 0;
  414. }
  415. /** \brief Generate a random integer within a range.
  416. *
  417. * Generate a random integer within the given range.
  418. *
  419. * This function never fails.
  420. *
  421. * \param min The lower bound of the integer range.
  422. * \param max The upper bound of the integer range.
  423. * \return A random integer comprised between \p min and \p max - 1
  424. * (inclusive).
  425. */
  426. int caca_rand(int min, int max)
  427. {
  428. static int need_init = 1;
  429. if(need_init)
  430. {
  431. srand(getpid() + time(NULL));
  432. need_init = 0;
  433. }
  434. return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
  435. }
  436. /*
  437. * XXX: The following functions are local.
  438. */
  439. int caca_resize(caca_canvas_t *cv, int width, int height)
  440. {
  441. int x, y, f, old_width, old_height, new_size, old_size;
  442. old_width = cv->width;
  443. old_height = cv->height;
  444. old_size = old_width * old_height;
  445. _caca_save_frame_info(cv);
  446. cv->width = width;
  447. cv->height = height;
  448. new_size = width * height;
  449. /* Step 1: if new area is bigger, resize the memory area now. */
  450. if(new_size > old_size)
  451. {
  452. for(f = 0; f < cv->framecount; f++)
  453. {
  454. cv->frames[f].chars = realloc(cv->frames[f].chars,
  455. new_size * sizeof(uint32_t));
  456. cv->frames[f].attrs = realloc(cv->frames[f].attrs,
  457. new_size * sizeof(uint32_t));
  458. if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
  459. {
  460. seterrno(ENOMEM);
  461. return -1;
  462. }
  463. }
  464. }
  465. /* Step 2: move line data if necessary. */
  466. if(width == old_width)
  467. {
  468. /* Width did not change, which means we do not need to move data. */
  469. ;
  470. }
  471. else if(width > old_width)
  472. {
  473. /* New width is bigger than old width, which means we need to
  474. * copy lines starting from the bottom of the screen otherwise
  475. * we will overwrite information. */
  476. for(f = 0; f < cv->framecount; f++)
  477. {
  478. uint32_t *chars = cv->frames[f].chars;
  479. uint32_t *attrs = cv->frames[f].attrs;
  480. for(y = height < old_height ? height : old_height; y--; )
  481. {
  482. uint32_t attr = cv->frames[f].curattr;
  483. for(x = old_width; x--; )
  484. {
  485. chars[y * width + x] = chars[y * old_width + x];
  486. attrs[y * width + x] = attrs[y * old_width + x];
  487. }
  488. /* Zero the end of the line */
  489. for(x = width - old_width; x--; )
  490. {
  491. chars[y * width + old_width + x] = (uint32_t)' ';
  492. attrs[y * width + old_width + x] = attr;
  493. }
  494. }
  495. }
  496. caca_add_dirty_rectangle(cv, old_width, 0, width - 1, old_height - 1);
  497. }
  498. else
  499. {
  500. /* New width is smaller. Copy as many lines as possible. Ignore
  501. * the first line, it is already in place. */
  502. int lines = height < old_height ? height : old_height;
  503. for(f = 0; f < cv->framecount; f++)
  504. {
  505. uint32_t *chars = cv->frames[f].chars;
  506. uint32_t *attrs = cv->frames[f].attrs;
  507. for(y = 1; y < lines; y++)
  508. {
  509. for(x = 0; x < width; x++)
  510. {
  511. chars[y * width + x] = chars[y * old_width + x];
  512. attrs[y * width + x] = attrs[y * old_width + x];
  513. }
  514. }
  515. }
  516. }
  517. /* Step 3: fill the bottom of the new screen if necessary. */
  518. if(height > old_height)
  519. {
  520. for(f = 0; f < cv->framecount; f++)
  521. {
  522. uint32_t *chars = cv->frames[f].chars;
  523. uint32_t *attrs = cv->frames[f].attrs;
  524. uint32_t attr = cv->frames[f].curattr;
  525. /* Zero the bottom of the screen */
  526. for(x = (height - old_height) * width; x--; )
  527. {
  528. chars[old_height * width + x] = (uint32_t)' ';
  529. attrs[old_height * width + x] = attr;
  530. }
  531. }
  532. caca_add_dirty_rectangle(cv, 0, old_height, old_width - 1, height - 1);
  533. }
  534. /* XXX: technically we should not worry about the dirty rectangle in
  535. * the bottom-right corner, because we only handle one dirty rectangle,
  536. * but in case the API changes later, we make sure this is handled. */
  537. if(width > old_width && height > old_height)
  538. caca_add_dirty_rectangle(cv, old_width, old_height,
  539. width - 1, height - 1);
  540. /* Step 4: if new area is smaller, resize memory area now. */
  541. if(new_size < old_size)
  542. {
  543. for(f = 0; f < cv->framecount; f++)
  544. {
  545. cv->frames[f].chars = realloc(cv->frames[f].chars,
  546. new_size * sizeof(uint32_t));
  547. cv->frames[f].attrs = realloc(cv->frames[f].attrs,
  548. new_size * sizeof(uint32_t));
  549. if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
  550. {
  551. seterrno(ENOMEM);
  552. return -1;
  553. }
  554. }
  555. }
  556. /* Set new size */
  557. for(f = 0; f < cv->framecount; f++)
  558. {
  559. if(cv->frames[f].x > (int)width)
  560. cv->frames[f].x = width;
  561. if(cv->frames[f].y > (int)height)
  562. cv->frames[f].y = height;
  563. cv->frames[f].width = width;
  564. cv->frames[f].height = height;
  565. }
  566. /* Reset the current frame shortcuts */
  567. _caca_load_frame_info(cv);
  568. return 0;
  569. }
  570. /*
  571. * XXX: The following functions are aliases.
  572. */
  573. cucul_canvas_t * cucul_create_canvas(int, int) CACA_ALIAS(caca_create_canvas);
  574. int cucul_manage_canvas(cucul_canvas_t *, int (*)(void *), void *)
  575. CACA_ALIAS(caca_manage_canvas);
  576. int cucul_unmanage_canvas(cucul_canvas_t *, int (*)(void *), void *)
  577. CACA_ALIAS(caca_unmanage_canvas);
  578. int cucul_set_canvas_size(cucul_canvas_t *, int, int)
  579. CACA_ALIAS(caca_set_canvas_size);
  580. int cucul_get_canvas_width(cucul_canvas_t const *)
  581. CACA_ALIAS(caca_get_canvas_width);
  582. int cucul_get_canvas_height(cucul_canvas_t const *)
  583. CACA_ALIAS(caca_get_canvas_height);
  584. uint8_t const * cucul_get_canvas_chars(cucul_canvas_t const *)
  585. CACA_ALIAS(caca_get_canvas_chars);
  586. uint8_t const * cucul_get_canvas_attrs(cucul_canvas_t const *)
  587. CACA_ALIAS(caca_get_canvas_attrs);
  588. int cucul_free_canvas(cucul_canvas_t *) CACA_ALIAS(caca_free_canvas);
  589. int cucul_rand(int, int) CACA_ALIAS(caca_rand);