Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

1224 linhas
35 KiB

  1. /*
  2. * libcaca ASCII-Art library
  3. * Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  19. * 02111-1307 USA
  20. */
  21. /** \file graphics.c
  22. * \version \$Id$
  23. * \author Sam Hocevar <sam@zoy.org>
  24. * \brief Character drawing
  25. *
  26. * This file contains character and string drawing functions.
  27. */
  28. #include "config.h"
  29. #if defined(USE_SLANG)
  30. # if defined(HAVE_SLANG_SLANG_H)
  31. # include <slang/slang.h>
  32. # else
  33. # include <slang.h>
  34. # endif
  35. #endif
  36. #if defined(USE_NCURSES)
  37. # include <curses.h>
  38. #endif
  39. #if defined(USE_CONIO)
  40. # include <conio.h>
  41. # if defined(SCREENUPDATE_IN_PC_H)
  42. # include <pc.h>
  43. # endif
  44. #endif
  45. #if defined(USE_X11)
  46. # include <X11/Xlib.h>
  47. # if defined(HAVE_X11_XKBLIB_H)
  48. # include <X11/XKBlib.h>
  49. # endif
  50. #endif
  51. #if defined(USE_WIN32)
  52. # include <windows.h>
  53. #endif
  54. #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
  55. # include <inttypes.h>
  56. #else
  57. typedef unsigned char uint8_t;
  58. #endif
  59. #include <stdio.h> /* BUFSIZ */
  60. #include <string.h>
  61. #include <stdlib.h>
  62. #include <unistd.h>
  63. #include <stdarg.h>
  64. #include "caca.h"
  65. #include "caca_internals.h"
  66. /*
  67. * Global variables
  68. */
  69. #if !defined(_DOXYGEN_SKIP_ME)
  70. unsigned int _caca_width = 0;
  71. unsigned int _caca_height = 0;
  72. #endif
  73. /*
  74. * Local variables
  75. */
  76. #if defined(USE_NCURSES)
  77. static int ncurses_attr[16*16];
  78. #endif
  79. #if defined(USE_SLANG)
  80. /* Tables generated by test/optipal.c */
  81. static int const slang_palette[2*16*16] =
  82. {
  83. 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
  84. 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 8,
  85. 8, 7, 7, 8, 15, 7, 7, 15, 15, 9, 9, 15, 1, 9, 9, 1,
  86. 7, 9, 9, 7, 8, 1, 1, 8, 0, 1, 15, 10, 10, 15, 2, 10,
  87. 10, 2, 7, 10, 10, 7, 8, 2, 2, 8, 0, 2, 15, 11, 11, 15,
  88. 3, 11, 11, 3, 7, 11, 11, 7, 8, 3, 3, 8, 0, 3, 15, 12,
  89. 12, 15, 4, 12, 12, 4, 7, 12, 12, 7, 8, 4, 4, 8, 0, 4,
  90. 15, 13, 13, 15, 5, 13, 13, 5, 7, 13, 13, 7, 8, 5, 5, 8,
  91. 0, 5, 15, 14, 14, 15, 6, 14, 14, 6, 7, 14, 14, 7, 8, 6,
  92. 6, 8, 0, 6, 4, 6, 6, 4, 12, 14, 14, 12, 6, 2, 2, 6,
  93. 14, 10, 10, 14, 2, 3, 3, 2, 10, 11, 11, 10, 3, 1, 1, 3,
  94. 11, 9, 9, 11, 1, 5, 5, 1, 9, 13, 13, 9, 5, 4, 4, 5,
  95. 13, 12, 12, 13, 4, 14, 6, 12, 12, 6, 14, 4, 6, 10, 2, 14,
  96. 14, 2, 10, 6, 2, 11, 3, 10, 10, 3, 11, 2, 3, 9, 1, 11,
  97. 11, 1, 9, 3, 1, 13, 5, 9, 9, 5, 13, 1, 5, 12, 4, 13,
  98. 13, 4, 12, 5, 0, 7, 0, 15, 15, 8, 8, 15, 15, 1, 7, 1,
  99. 1, 6, 2, 5, 3, 4, 4, 3, 5, 2, 6, 1, 0, 0, 1, 1,
  100. 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 2, 2, 3, 3,
  101. 4, 4, 5, 5, 6, 6, 7, 7, 14, 9, 1, 15, 8, 9, 8, 8,
  102. 9, 9, 1, 7, 0, 9, 9, 8, 6, 9, 13, 10, 2, 15, 8, 10,
  103. 7, 2, 15, 2, 2, 7, 0, 10, 10, 8, 5, 10, 12, 11, 3, 15,
  104. 8, 11, 7, 3, 15, 3, 3, 7, 0, 11, 11, 8, 4, 11, 11, 12,
  105. 4, 15, 8, 12, 7, 4, 15, 4, 4, 7, 0, 12, 12, 8, 3, 12,
  106. 10, 13, 5, 15, 8, 13, 7, 5, 15, 5, 5, 7, 0, 13, 13, 8,
  107. 2, 13, 9, 14, 6, 15, 8, 14, 7, 6, 15, 6, 6, 7, 0, 14,
  108. 14, 8, 1, 14, 5, 6, 2, 4, 13, 14, 10, 12, 4, 2, 3, 6,
  109. 12, 10, 11, 14, 6, 3, 1, 2, 14, 11, 9, 10, 2, 1, 5, 3,
  110. 10, 9, 13, 11, 3, 5, 4, 1, 11, 13, 12, 9, 1, 4, 6, 5,
  111. 9, 12, 14, 13, 5, 14, 2, 12, 13, 6, 10, 4, 4, 10, 3, 14,
  112. 12, 2, 11, 6, 6, 11, 1, 10, 14, 3, 9, 2, 2, 9, 5, 11,
  113. 10, 1, 13, 3, 3, 13, 4, 9, 11, 5, 12, 1, 1, 12, 6, 13,
  114. 9, 4, 14, 5, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
  115. };
  116. static int const slang_assoc[16*16] =
  117. {
  118. 134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  119. 28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
  120. 37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
  121. 46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
  122. 55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
  123. 64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
  124. 73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
  125. 122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
  126. 15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
  127. 154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
  128. 163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
  129. 172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
  130. 181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
  131. 190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
  132. 199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
  133. 123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
  134. };
  135. #endif
  136. #if defined(USE_CONIO)
  137. static struct text_info conio_ti;
  138. static char *conio_screen;
  139. #endif
  140. #if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
  141. Display *x11_dpy;
  142. Window x11_window;
  143. int x11_font_width, x11_font_height;
  144. static GC x11_gc;
  145. static Pixmap x11_pixmap;
  146. static uint8_t *x11_char, *x11_attr;
  147. static int x11_colors[16];
  148. static Font x11_font;
  149. static XFontStruct *x11_font_struct;
  150. static int x11_font_offset;
  151. #if defined(HAVE_X11_XKBLIB_H)
  152. static Bool x11_detect_autorepeat;
  153. #endif
  154. #endif
  155. #if defined(USE_WIN32)
  156. static uint8_t *win32_char, *win32_attr;
  157. HANDLE win32_hin, win32_hout;
  158. static HANDLE win32_front, win32_back;
  159. static CHAR_INFO *win32_buffer;
  160. static int const win32_fg_palette[] =
  161. {
  162. 0,
  163. FOREGROUND_BLUE,
  164. FOREGROUND_GREEN,
  165. FOREGROUND_GREEN | FOREGROUND_BLUE,
  166. FOREGROUND_RED,
  167. FOREGROUND_RED | FOREGROUND_BLUE,
  168. FOREGROUND_RED | FOREGROUND_GREEN,
  169. FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
  170. FOREGROUND_INTENSITY,
  171. FOREGROUND_INTENSITY | FOREGROUND_BLUE,
  172. FOREGROUND_INTENSITY | FOREGROUND_GREEN,
  173. FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
  174. FOREGROUND_INTENSITY | FOREGROUND_RED,
  175. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
  176. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
  177. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  178. };
  179. static int const win32_bg_palette[] =
  180. {
  181. 0,
  182. BACKGROUND_BLUE,
  183. BACKGROUND_GREEN,
  184. BACKGROUND_GREEN | BACKGROUND_BLUE,
  185. BACKGROUND_RED,
  186. BACKGROUND_RED | BACKGROUND_BLUE,
  187. BACKGROUND_RED | BACKGROUND_GREEN,
  188. BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
  189. BACKGROUND_INTENSITY,
  190. BACKGROUND_INTENSITY | BACKGROUND_BLUE,
  191. BACKGROUND_INTENSITY | BACKGROUND_GREEN,
  192. BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
  193. BACKGROUND_INTENSITY | BACKGROUND_RED,
  194. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
  195. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
  196. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
  197. };
  198. #endif
  199. static char *_caca_empty_line;
  200. static char *_caca_scratch_line;
  201. static unsigned int _caca_delay;
  202. static unsigned int _caca_rendertime;
  203. #if defined(OPTIMISE_SLANG_PALETTE)
  204. static int _caca_fgisbg = 0;
  205. #endif
  206. static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
  207. static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
  208. /*
  209. * Local functions
  210. */
  211. #if defined(USE_SLANG)
  212. static void slang_init_palette(void);
  213. #endif
  214. #if defined(USE_X11)
  215. static int x11_error_handler(Display *, XErrorEvent *);
  216. #endif
  217. /** \brief Set the default colour pair.
  218. *
  219. * This function sets the default colour pair. String functions such as
  220. * caca_printf() and graphical primitive functions such as caca_draw_line()
  221. * will use these colour pairs.
  222. *
  223. * \param fgcolor The requested foreground colour.
  224. * \param bgcolor The requested background colour.
  225. */
  226. void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
  227. {
  228. if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
  229. return;
  230. _caca_fgcolor = fgcolor;
  231. _caca_bgcolor = bgcolor;
  232. switch(_caca_driver)
  233. {
  234. #if defined(USE_SLANG)
  235. case CACA_DRIVER_SLANG:
  236. #if defined(OPTIMISE_SLANG_PALETTE)
  237. /* If foreground == background, discard this colour pair. Functions
  238. * such as caca_putchar will print spaces instead of characters */
  239. if(fgcolor != bgcolor)
  240. _caca_fgisbg = 0;
  241. else
  242. {
  243. _caca_fgisbg = 1;
  244. if(fgcolor == CACA_COLOR_BLACK)
  245. fgcolor = CACA_COLOR_WHITE;
  246. else if(fgcolor == CACA_COLOR_WHITE
  247. || fgcolor <= CACA_COLOR_LIGHTGRAY)
  248. fgcolor = CACA_COLOR_BLACK;
  249. else
  250. fgcolor = CACA_COLOR_WHITE;
  251. }
  252. #endif
  253. #if defined(OPTIMISE_SLANG_PALETTE)
  254. SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
  255. #else
  256. SLsmg_set_color(fgcolor + 16 * bgcolor);
  257. #endif
  258. break;
  259. #endif
  260. #if defined(USE_NCURSES)
  261. case CACA_DRIVER_NCURSES:
  262. attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
  263. break;
  264. #endif
  265. #if defined(USE_CONIO)
  266. case CACA_DRIVER_CONIO:
  267. textbackground(bgcolor);
  268. textcolor(fgcolor);
  269. break;
  270. #endif
  271. #if defined(USE_X11)
  272. case CACA_DRIVER_X11:
  273. /* Nothing to do */
  274. break;
  275. #endif
  276. #if defined(USE_WIN32)
  277. case CACA_DRIVER_WIN32:
  278. /* Nothing to do */
  279. break;
  280. #endif
  281. default:
  282. break;
  283. }
  284. }
  285. /** \brief Get the current foreground colour.
  286. *
  287. * This function returns the current foreground colour that was set with
  288. * caca_set_color().
  289. *
  290. * \return The current foreground colour.
  291. */
  292. enum caca_color caca_get_fg_color(void)
  293. {
  294. return _caca_fgcolor;
  295. }
  296. /** \brief Get the current background colour.
  297. *
  298. * This function returns the current background colour that was set with
  299. * caca_set_color().
  300. *
  301. * \return The current background colour.
  302. */
  303. enum caca_color caca_get_bg_color(void)
  304. {
  305. return _caca_bgcolor;
  306. }
  307. /** \brief Print a character.
  308. *
  309. * This function prints a character at the given coordinates, using the
  310. * default foreground and background values. If the coordinates are outside
  311. * the screen boundaries, nothing is printed.
  312. *
  313. * \param x X coordinate.
  314. * \param y Y coordinate.
  315. * \param c The character to print.
  316. */
  317. void caca_putchar(int x, int y, char c)
  318. {
  319. #if defined(USE_CONIO)
  320. char *data;
  321. #endif
  322. if(x < 0 || x >= (int)_caca_width ||
  323. y < 0 || y >= (int)_caca_height)
  324. return;
  325. switch(_caca_driver)
  326. {
  327. #if defined(USE_SLANG)
  328. case CACA_DRIVER_SLANG:
  329. SLsmg_gotorc(y, x);
  330. #if defined(OPTIMISE_SLANG_PALETTE)
  331. if(_caca_fgisbg)
  332. SLsmg_write_char(' ');
  333. else
  334. #endif
  335. SLsmg_write_char(c);
  336. break;
  337. #endif
  338. #if defined(USE_NCURSES)
  339. case CACA_DRIVER_NCURSES:
  340. move(y, x);
  341. addch(c);
  342. break;
  343. #endif
  344. #if defined(USE_CONIO)
  345. case CACA_DRIVER_CONIO:
  346. data = conio_screen + 2 * (x + y * _caca_width);
  347. data[0] = c;
  348. data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
  349. break;
  350. #endif
  351. #if defined(USE_X11)
  352. case CACA_DRIVER_X11:
  353. x11_char[x + y * _caca_width] = c;
  354. x11_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
  355. break;
  356. #endif
  357. #if defined(USE_WIN32)
  358. case CACA_DRIVER_WIN32:
  359. win32_char[x + y * _caca_width] = c;
  360. win32_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
  361. break;
  362. #endif
  363. default:
  364. break;
  365. }
  366. }
  367. /** \brief Print a string.
  368. *
  369. * This function prints a string at the given coordinates, using the
  370. * default foreground and background values. The coordinates may be outside
  371. * the screen boundaries (eg. a negative Y coordinate) and the string will
  372. * be cropped accordingly if it is too long.
  373. *
  374. * \param x X coordinate.
  375. * \param y Y coordinate.
  376. * \param s The string to print.
  377. */
  378. void caca_putstr(int x, int y, char const *s)
  379. {
  380. #if defined(USE_CONIO) | defined(USE_X11) | defined(USE_WIN32)
  381. char *charbuf;
  382. #endif
  383. #if defined(USE_X11) | defined(USE_WIN32)
  384. char *attrbuf;
  385. #endif
  386. unsigned int len;
  387. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  388. return;
  389. len = strlen(s);
  390. if(x < 0)
  391. {
  392. len -= -x;
  393. if(len < 0)
  394. return;
  395. s += -x;
  396. x = 0;
  397. }
  398. if(x + len >= _caca_width)
  399. {
  400. len = _caca_width - x;
  401. memcpy(_caca_scratch_line, s, len);
  402. _caca_scratch_line[len] = '\0';
  403. s = _caca_scratch_line;
  404. }
  405. switch(_caca_driver)
  406. {
  407. #if defined(USE_SLANG)
  408. case CACA_DRIVER_SLANG:
  409. SLsmg_gotorc(y, x);
  410. #if defined(OPTIMISE_SLANG_PALETTE)
  411. if(_caca_fgisbg)
  412. SLsmg_write_string(_caca_empty_line + _caca_width - len);
  413. else
  414. #endif
  415. SLsmg_write_string((char *)(intptr_t)s);
  416. break;
  417. #endif
  418. #if defined(USE_NCURSES)
  419. case CACA_DRIVER_NCURSES:
  420. move(y, x);
  421. addstr(s);
  422. break;
  423. #endif
  424. #if defined(USE_CONIO)
  425. case CACA_DRIVER_CONIO:
  426. charbuf = conio_screen + 2 * (x + y * _caca_width);
  427. while(*s)
  428. {
  429. *charbuf++ = *s++;
  430. *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  431. }
  432. break;
  433. #endif
  434. #if defined(USE_X11)
  435. case CACA_DRIVER_X11:
  436. charbuf = x11_char + x + y * _caca_width;
  437. attrbuf = x11_attr + x + y * _caca_width;
  438. while(*s)
  439. {
  440. *charbuf++ = *s++;
  441. *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  442. }
  443. break;
  444. #endif
  445. #if defined(USE_WIN32)
  446. case CACA_DRIVER_WIN32:
  447. charbuf = win32_char + x + y * _caca_width;
  448. attrbuf = win32_attr + x + y * _caca_width;
  449. while(*s)
  450. {
  451. *charbuf++ = *s++;
  452. *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  453. }
  454. break;
  455. #endif
  456. default:
  457. break;
  458. }
  459. }
  460. /** \brief Format a string.
  461. *
  462. * This function formats a string at the given coordinates, using the
  463. * default foreground and background values. The coordinates may be outside
  464. * the screen boundaries (eg. a negative Y coordinate) and the string will
  465. * be cropped accordingly if it is too long. The syntax of the format
  466. * string is the same as for the C printf() function.
  467. *
  468. * \param x X coordinate.
  469. * \param y Y coordinate.
  470. * \param format The format string to print.
  471. * \param ... Arguments to the format string.
  472. */
  473. void caca_printf(int x, int y, char const *format, ...)
  474. {
  475. char tmp[BUFSIZ];
  476. char *buf = tmp;
  477. va_list args;
  478. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  479. return;
  480. if(_caca_width - x + 1 > BUFSIZ)
  481. buf = malloc(_caca_width - x + 1);
  482. va_start(args, format);
  483. #if defined(HAVE_VSNPRINTF)
  484. vsnprintf(buf, _caca_width - x + 1, format, args);
  485. #else
  486. vsprintf(buf, format, args);
  487. #endif
  488. buf[_caca_width - x] = '\0';
  489. va_end(args);
  490. caca_putstr(x, y, buf);
  491. if(buf != tmp)
  492. free(buf);
  493. }
  494. /** \brief Clear the screen.
  495. *
  496. * This function clears the screen using a black background.
  497. */
  498. void caca_clear(void)
  499. {
  500. enum caca_color oldfg = caca_get_fg_color();
  501. enum caca_color oldbg = caca_get_bg_color();
  502. int y = _caca_height;
  503. caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
  504. /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
  505. while(y--)
  506. caca_putstr(0, y, _caca_empty_line);
  507. caca_set_color(oldfg, oldbg);
  508. }
  509. #if !defined(_DOXYGEN_SKIP_ME)
  510. int _caca_init_graphics(void)
  511. {
  512. #if defined(USE_SLANG)
  513. if(_caca_driver == CACA_DRIVER_SLANG)
  514. {
  515. slang_init_palette();
  516. /* Disable alt charset support so that we get a chance to have all
  517. * 256 colour pairs */
  518. SLtt_Has_Alt_Charset = 0;
  519. _caca_width = SLtt_Screen_Cols;
  520. _caca_height = SLtt_Screen_Rows;
  521. }
  522. else
  523. #endif
  524. #if defined(USE_NCURSES)
  525. if(_caca_driver == CACA_DRIVER_NCURSES)
  526. {
  527. static int curses_colors[] =
  528. {
  529. /* Standard curses colours */
  530. COLOR_BLACK,
  531. COLOR_BLUE,
  532. COLOR_GREEN,
  533. COLOR_CYAN,
  534. COLOR_RED,
  535. COLOR_MAGENTA,
  536. COLOR_YELLOW,
  537. COLOR_WHITE,
  538. /* Extra values for xterm-16color */
  539. COLOR_BLACK + 8,
  540. COLOR_BLUE + 8,
  541. COLOR_GREEN + 8,
  542. COLOR_CYAN + 8,
  543. COLOR_RED + 8,
  544. COLOR_MAGENTA + 8,
  545. COLOR_YELLOW + 8,
  546. COLOR_WHITE + 8
  547. };
  548. int fg, bg, max;
  549. /* Activate colour */
  550. start_color();
  551. /* If COLORS == 16, it means the terminal supports full bright colours
  552. * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
  553. * we can build 16*16 colour pairs.
  554. * If COLORS == 8, it means the terminal does not know about bright
  555. * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
  556. * and \e[5m). We can only build 8*8 colour pairs. */
  557. max = COLORS >= 16 ? 16 : 8;
  558. for(bg = 0; bg < max; bg++)
  559. for(fg = 0; fg < max; fg++)
  560. {
  561. /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
  562. * is light gray on black, since some terminals don't like
  563. * this colour pair to be redefined. */
  564. int col = ((max + 7 - fg) % max) + max * bg;
  565. init_pair(col, curses_colors[fg], curses_colors[bg]);
  566. ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
  567. if(max == 8)
  568. {
  569. /* Bright fg on simple bg */
  570. ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
  571. /* Simple fg on bright bg */
  572. ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
  573. | COLOR_PAIR(col);
  574. /* Bright fg on bright bg */
  575. ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
  576. | COLOR_PAIR(col);
  577. }
  578. }
  579. _caca_width = COLS;
  580. _caca_height = LINES;
  581. }
  582. else
  583. #endif
  584. #if defined(USE_CONIO)
  585. if(_caca_driver == CACA_DRIVER_CONIO)
  586. {
  587. gettextinfo(&conio_ti);
  588. conio_screen = malloc(2 * conio_ti.screenwidth
  589. * conio_ti.screenheight * sizeof(char));
  590. if(conio_screen == NULL)
  591. return -1;
  592. # if defined(SCREENUPDATE_IN_PC_H)
  593. ScreenRetrieve(conio_screen);
  594. # else
  595. /* FIXME */
  596. # endif
  597. _caca_width = conio_ti.screenwidth;
  598. _caca_height = conio_ti.screenheight;
  599. }
  600. else
  601. #endif
  602. #if defined(USE_X11)
  603. if(_caca_driver == CACA_DRIVER_X11)
  604. {
  605. static int x11_palette[] =
  606. {
  607. /* Standard curses colours */
  608. 0x0, 0x0, 0x0,
  609. 0x0, 0x0, 0x8000,
  610. 0x0, 0x8000, 0x0,
  611. 0x0, 0x8000, 0x8000,
  612. 0x8000, 0x0, 0x0,
  613. 0x8000, 0x0, 0x8000,
  614. 0x8000, 0x8000, 0x0,
  615. 0x8000, 0x8000, 0x8000,
  616. /* Extra values for xterm-16color */
  617. 0x4000, 0x4000, 0x4000,
  618. 0x4000, 0x4000, 0xffff,
  619. 0x4000, 0xffff, 0x4000,
  620. 0x4000, 0xffff, 0xffff,
  621. 0xffff, 0x4000, 0x4000,
  622. 0xffff, 0x4000, 0xffff,
  623. 0xffff, 0xffff, 0x4000,
  624. 0xffff, 0xffff, 0xffff,
  625. };
  626. Colormap colormap;
  627. XSetWindowAttributes x11_winattr;
  628. int (*old_error_handler)(Display *, XErrorEvent *);
  629. char const *font_name = "8x13bold";
  630. int i;
  631. if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
  632. sscanf(getenv("CACA_GEOMETRY"),
  633. "%ux%u", &_caca_width, &_caca_height);
  634. if(!_caca_width)
  635. _caca_width = 80;
  636. if(!_caca_height)
  637. _caca_height = 32;
  638. x11_char = malloc(_caca_width * _caca_height * sizeof(int));
  639. if(x11_char == NULL)
  640. return -1;
  641. x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
  642. if(x11_attr == NULL)
  643. {
  644. free(x11_char);
  645. return -1;
  646. }
  647. memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
  648. memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
  649. x11_dpy = XOpenDisplay(NULL);
  650. if(x11_dpy == NULL)
  651. {
  652. free(x11_char);
  653. free(x11_attr);
  654. return -1;
  655. }
  656. if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
  657. font_name = getenv("CACA_FONT");
  658. /* Ignore font errors */
  659. old_error_handler = XSetErrorHandler(x11_error_handler);
  660. x11_font = XLoadFont(x11_dpy, font_name);
  661. if(!x11_font)
  662. {
  663. XCloseDisplay(x11_dpy);
  664. free(x11_char);
  665. free(x11_attr);
  666. return -1;
  667. }
  668. x11_font_struct = XQueryFont(x11_dpy, x11_font);
  669. if(!x11_font_struct)
  670. {
  671. XUnloadFont(x11_dpy, x11_font);
  672. XCloseDisplay(x11_dpy);
  673. free(x11_char);
  674. free(x11_attr);
  675. return -1;
  676. }
  677. /* Reset the default X11 error handler */
  678. XSetErrorHandler(old_error_handler);
  679. x11_font_width = x11_font_struct->max_bounds.width;
  680. x11_font_height = x11_font_struct->max_bounds.ascent
  681. + x11_font_struct->max_bounds.descent;
  682. x11_font_offset = x11_font_struct->max_bounds.descent;
  683. colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
  684. for(i = 0; i < 16; i++)
  685. {
  686. XColor color;
  687. color.red = x11_palette[i * 3];
  688. color.green = x11_palette[i * 3 + 1];
  689. color.blue = x11_palette[i * 3 + 2];
  690. XAllocColor(x11_dpy, colormap, &color);
  691. x11_colors[i] = color.pixel;
  692. }
  693. x11_winattr.backing_store = Always;
  694. x11_winattr.background_pixel = x11_colors[0];
  695. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  696. x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
  697. _caca_width * x11_font_width,
  698. _caca_height * x11_font_height,
  699. 0, 0, InputOutput, 0,
  700. CWBackingStore | CWBackPixel | CWEventMask,
  701. &x11_winattr);
  702. XStoreName(x11_dpy, x11_window, "caca for X");
  703. XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
  704. XMapWindow(x11_dpy, x11_window);
  705. x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
  706. XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
  707. XSetFont(x11_dpy, x11_gc, x11_font);
  708. for(;;)
  709. {
  710. XEvent event;
  711. XNextEvent(x11_dpy, &event);
  712. if (event.type == MapNotify)
  713. break;
  714. }
  715. /* Disable autorepeat */
  716. #if defined(HAVE_X11_XKBLIB_H)
  717. XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
  718. if(!x11_detect_autorepeat)
  719. XAutoRepeatOff(x11_dpy);
  720. #endif
  721. XSelectInput(x11_dpy, x11_window,
  722. KeyPressMask | KeyReleaseMask | ButtonPressMask
  723. | ButtonReleaseMask | PointerMotionMask);
  724. XSync(x11_dpy, False);
  725. x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
  726. _caca_width * x11_font_width,
  727. _caca_height * x11_font_height,
  728. DefaultDepth(x11_dpy,
  729. DefaultScreen(x11_dpy)));
  730. }
  731. else
  732. #endif
  733. #if defined(USE_WIN32)
  734. if(_caca_driver == CACA_DRIVER_WIN32)
  735. {
  736. CONSOLE_CURSOR_INFO cci;
  737. CONSOLE_SCREEN_BUFFER_INFO csbi;
  738. COORD size;
  739. win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  740. 0, NULL,
  741. CONSOLE_TEXTMODE_BUFFER, NULL);
  742. if(!win32_front)
  743. return -1;
  744. win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  745. 0, NULL,
  746. CONSOLE_TEXTMODE_BUFFER, NULL);
  747. if(!win32_back)
  748. return -1;
  749. if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
  750. return -1;
  751. _caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  752. _caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  753. size.X = _caca_width;
  754. size.Y = _caca_height;
  755. SetConsoleScreenBufferSize(win32_front, size);
  756. SetConsoleScreenBufferSize(win32_back, size);
  757. SetConsoleMode(win32_front, 0);
  758. SetConsoleMode(win32_back, 0);
  759. GetConsoleCursorInfo(win32_front, &cci);
  760. cci.bVisible = FALSE;
  761. SetConsoleCursorInfo(win32_front, &cci);
  762. SetConsoleCursorInfo(win32_back, &cci);
  763. SetConsoleActiveScreenBuffer(win32_front);
  764. win32_char = malloc(_caca_width * _caca_height * sizeof(int));
  765. if(win32_char == NULL)
  766. return -1;
  767. win32_attr = malloc(_caca_width * _caca_height * sizeof(int));
  768. if(win32_attr == NULL)
  769. {
  770. free(win32_char);
  771. return -1;
  772. }
  773. win32_buffer = malloc(sizeof(CHAR_INFO) * _caca_width * _caca_height);
  774. memset(win32_char, 0, _caca_width * _caca_height * sizeof(int));
  775. memset(win32_attr, 0, _caca_width * _caca_height * sizeof(int));
  776. }
  777. else
  778. #endif
  779. {
  780. /* Dummy */
  781. }
  782. _caca_empty_line = malloc(_caca_width + 1);
  783. memset(_caca_empty_line, ' ', _caca_width);
  784. _caca_empty_line[_caca_width] = '\0';
  785. _caca_scratch_line = malloc(_caca_width + 1);
  786. _caca_delay = 0;
  787. _caca_rendertime = 0;
  788. return 0;
  789. }
  790. int _caca_end_graphics(void)
  791. {
  792. #if defined(USE_SLANG)
  793. /* Nothing to do */
  794. #endif
  795. #if defined(USE_NCURSES)
  796. /* Nothing to do */
  797. #endif
  798. #if defined(USE_CONIO)
  799. if(_caca_driver == CACA_DRIVER_CONIO)
  800. {
  801. free(conio_screen);
  802. }
  803. else
  804. #endif
  805. #if defined(USE_X11)
  806. if(_caca_driver == CACA_DRIVER_X11)
  807. {
  808. XSync(x11_dpy, False);
  809. #if defined(HAVE_X11_XKBLIB_H)
  810. if(!x11_detect_autorepeat)
  811. XAutoRepeatOn(x11_dpy);
  812. #endif
  813. XFreePixmap(x11_dpy, x11_pixmap);
  814. XFreeFont(x11_dpy, x11_font_struct);
  815. XFreeGC(x11_dpy, x11_gc);
  816. XUnmapWindow(x11_dpy, x11_window);
  817. XDestroyWindow(x11_dpy, x11_window);
  818. XCloseDisplay(x11_dpy);
  819. free(x11_char);
  820. free(x11_attr);
  821. }
  822. else
  823. #endif
  824. #if defined(USE_WIN32)
  825. if(_caca_driver == CACA_DRIVER_WIN32)
  826. {
  827. SetConsoleActiveScreenBuffer(win32_hout);
  828. CloseHandle(win32_back);
  829. CloseHandle(win32_front);
  830. free(win32_char);
  831. free(win32_attr);
  832. }
  833. else
  834. #endif
  835. {
  836. /* Dummy */
  837. }
  838. free(_caca_empty_line);
  839. return 0;
  840. }
  841. #endif /* _DOXYGEN_SKIP_ME */
  842. /** \brief Set the window title.
  843. *
  844. * If libcaca runs in a window, try to change its title. This works with
  845. * the X11 and Win32 drivers.
  846. *
  847. * \param title The desired window title.
  848. * \return 0 upon success, a non-zero value if an error occurs.
  849. */
  850. int caca_set_title(char const *title)
  851. {
  852. #if defined(USE_X11)
  853. if(_caca_driver == CACA_DRIVER_X11)
  854. {
  855. XStoreName(x11_dpy, x11_window, title);
  856. }
  857. else
  858. #endif
  859. #if defined(USE_WIN32)
  860. if(_caca_driver == CACA_DRIVER_WIN32)
  861. {
  862. SetConsoleTitle(title);
  863. }
  864. else
  865. #endif
  866. {
  867. /* Not supported */
  868. return -1;
  869. }
  870. return 0;
  871. }
  872. /** \brief Set the refresh delay.
  873. *
  874. * This function sets the refresh delay in microseconds. The refresh delay
  875. * is used by caca_refresh() to achieve constant framerate. See the
  876. * caca_refresh() documentation for more details.
  877. *
  878. * If the argument is zero, constant framerate is disabled. This is the
  879. * default behaviour.
  880. *
  881. * \param usec The refresh delay in microseconds.
  882. */
  883. void caca_set_delay(unsigned int usec)
  884. {
  885. _caca_delay = usec;
  886. }
  887. /** \brief Get the average rendering time.
  888. *
  889. * This function returns the average rendering time, which is the average
  890. * measured time between two caca_refresh() calls, in microseconds. If
  891. * constant framerate was activated by calling caca_set_delay(), the average
  892. * rendering time will not be considerably shorter than the requested delay
  893. * even if the real rendering time was shorter.
  894. *
  895. * \return The render time in microseconds.
  896. */
  897. unsigned int caca_get_rendertime(void)
  898. {
  899. return _caca_rendertime;
  900. }
  901. /** \brief Flush pending changes and redraw the screen.
  902. *
  903. * This function flushes all graphical operations and prints them to the
  904. * screen. Nothing will show on the screen until caca_refresh() is
  905. * called.
  906. *
  907. * If caca_set_delay() was called with a non-zero value, caca_refresh()
  908. * will use that value to achieve constant framerate: if two consecutive
  909. * calls to caca_refresh() are within a time range shorter than the value
  910. * set with caca_set_delay(), the second call will wait a bit before
  911. * performing the screen refresh.
  912. */
  913. void caca_refresh(void)
  914. {
  915. #if !defined(_DOXYGEN_SKIP_ME)
  916. #define IDLE_USEC 10000
  917. #endif
  918. static struct caca_timer timer = CACA_TIMER_INITIALIZER;
  919. static int lastticks = 0;
  920. int ticks = lastticks + _caca_getticks(&timer);
  921. #if defined(USE_SLANG)
  922. if(_caca_driver == CACA_DRIVER_SLANG)
  923. {
  924. SLsmg_refresh();
  925. }
  926. else
  927. #endif
  928. #if defined(USE_NCURSES)
  929. if(_caca_driver == CACA_DRIVER_NCURSES)
  930. {
  931. refresh();
  932. }
  933. else
  934. #endif
  935. #if defined(USE_CONIO)
  936. if(_caca_driver == CACA_DRIVER_CONIO)
  937. {
  938. # if defined(SCREENUPDATE_IN_PC_H)
  939. ScreenUpdate(conio_screen);
  940. # else
  941. /* FIXME */
  942. # endif
  943. }
  944. else
  945. #endif
  946. #if defined(USE_X11)
  947. if(_caca_driver == CACA_DRIVER_X11)
  948. {
  949. unsigned int x, y, len;
  950. /* First draw the background colours. Splitting the process in two
  951. * loops like this is actually slightly faster. */
  952. for(y = 0; y < _caca_height; y++)
  953. {
  954. for(x = 0; x < _caca_width; x += len)
  955. {
  956. unsigned char *attr = x11_attr + x + y * _caca_width;
  957. len = 1;
  958. while(x + len < _caca_width
  959. && (attr[len] >> 4) == (attr[0] >> 4))
  960. len++;
  961. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
  962. XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
  963. x * x11_font_width, y * x11_font_height,
  964. len * x11_font_width, x11_font_height);
  965. }
  966. }
  967. /* Then print the foreground characters */
  968. for(y = 0; y < _caca_height; y++)
  969. {
  970. for(x = 0; x < _caca_width; x += len)
  971. {
  972. unsigned char *attr = x11_attr + x + y * _caca_width;
  973. len = 1;
  974. /* Skip spaces */
  975. if(x11_char[x + y * _caca_width] == ' ')
  976. continue;
  977. while(x + len < _caca_width
  978. && (attr[len] & 0xf) == (attr[0] & 0xf))
  979. len++;
  980. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
  981. XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
  982. (y + 1) * x11_font_height - x11_font_offset,
  983. x11_char + x + y * _caca_width, len);
  984. }
  985. }
  986. XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
  987. _caca_width * x11_font_width, _caca_height * x11_font_height,
  988. 0, 0);
  989. XFlush(x11_dpy);
  990. }
  991. else
  992. #endif
  993. #if defined(USE_WIN32)
  994. if(_caca_driver == CACA_DRIVER_WIN32)
  995. {
  996. COORD size, pos;
  997. SMALL_RECT rect;
  998. DWORD dummy;
  999. unsigned int x, y, len;
  1000. /* Render everything to our back buffer */
  1001. for(y = 0; y < _caca_height; y++)
  1002. {
  1003. pos.X = 0;
  1004. pos.Y = y;
  1005. SetConsoleCursorPosition(win32_back, pos);
  1006. for(x = 0; x < _caca_width; x += len)
  1007. {
  1008. unsigned char *attr = win32_attr + x + y * _caca_width;
  1009. len = 1;
  1010. while(x + len < _caca_width && attr[len] == attr[0])
  1011. len++;
  1012. SetConsoleTextAttribute(win32_back,
  1013. win32_fg_palette[attr[0] & 0xf]
  1014. | win32_bg_palette[attr[0] >> 4]);
  1015. WriteConsole(win32_back, win32_char + x + y * _caca_width,
  1016. len, &dummy, NULL);
  1017. }
  1018. }
  1019. /* Blit the back buffer to the front buffer */
  1020. size.X = _caca_width;
  1021. size.Y = _caca_height;
  1022. pos.X = pos.Y = 0;
  1023. rect.Left = rect.Top = 0;
  1024. rect.Right = _caca_width - 1;
  1025. rect.Bottom = _caca_height - 1;
  1026. ReadConsoleOutput(win32_back, win32_buffer, size, pos, &rect);
  1027. WriteConsoleOutput(win32_front, win32_buffer, size, pos, &rect);
  1028. }
  1029. else
  1030. #endif
  1031. {
  1032. /* Dummy */
  1033. }
  1034. /* Wait until _caca_delay + time of last call */
  1035. ticks += _caca_getticks(&timer);
  1036. for(ticks += _caca_getticks(&timer);
  1037. ticks + IDLE_USEC < (int)_caca_delay;
  1038. ticks += _caca_getticks(&timer))
  1039. {
  1040. _caca_sleep(IDLE_USEC);
  1041. }
  1042. /* Update the sliding mean of the render time */
  1043. _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
  1044. lastticks = ticks - _caca_delay;
  1045. /* If we drifted too much, it's bad, bad, bad. */
  1046. if(lastticks > (int)_caca_delay)
  1047. lastticks = 0;
  1048. }
  1049. #if defined(USE_SLANG)
  1050. static void slang_init_palette(void)
  1051. {
  1052. /* See SLang ref., 5.4.4. */
  1053. static char *slang_colors[16] =
  1054. {
  1055. /* Standard colours */
  1056. "black",
  1057. "blue",
  1058. "green",
  1059. "cyan",
  1060. "red",
  1061. "magenta",
  1062. "brown",
  1063. "lightgray",
  1064. /* Bright colours */
  1065. "gray",
  1066. "brightblue",
  1067. "brightgreen",
  1068. "brightcyan",
  1069. "brightred",
  1070. "brightmagenta",
  1071. "yellow",
  1072. "white",
  1073. };
  1074. #if defined(OPTIMISE_SLANG_PALETTE)
  1075. int i;
  1076. for(i = 0; i < 16 * 16; i++)
  1077. SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
  1078. slang_colors[slang_palette[i * 2 + 1]]);
  1079. #else
  1080. int fg, bg;
  1081. for(bg = 0; bg < 16; bg++)
  1082. for(fg = 0; fg < 16; fg++)
  1083. {
  1084. int i = fg + 16 * bg;
  1085. SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
  1086. }
  1087. #endif
  1088. }
  1089. #endif /* USE_SLANG */
  1090. #if defined(USE_X11)
  1091. static int x11_error_handler(Display *dpy, XErrorEvent *event)
  1092. {
  1093. return 0;
  1094. }
  1095. #endif