Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

545 рядки
15 KiB

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