1027 lines
29 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(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
  52. # include <inttypes.h>
  53. #else
  54. typedef unsigned char uint8_t;
  55. #endif
  56. #include <stdio.h> /* BUFSIZ */
  57. #include <string.h>
  58. #include <stdlib.h>
  59. #include <unistd.h>
  60. #include <stdarg.h>
  61. #include <sys/time.h>
  62. #include <time.h>
  63. #include "caca.h"
  64. #include "caca_internals.h"
  65. /*
  66. * Global variables
  67. */
  68. #if !defined(_DOXYGEN_SKIP_ME)
  69. unsigned int _caca_width = 0;
  70. unsigned int _caca_height = 0;
  71. #endif
  72. /*
  73. * Local variables
  74. */
  75. #if defined(USE_NCURSES)
  76. static int ncurses_attr[16*16];
  77. #endif
  78. #if defined(USE_SLANG)
  79. /* Tables generated by test/optipal.c */
  80. static int const slang_palette[2*16*16] =
  81. {
  82. 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
  83. 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 8,
  84. 8, 7, 7, 8, 15, 7, 7, 15, 15, 9, 9, 15, 1, 9, 9, 1,
  85. 7, 9, 9, 7, 8, 1, 1, 8, 0, 1, 15, 10, 10, 15, 2, 10,
  86. 10, 2, 7, 10, 10, 7, 8, 2, 2, 8, 0, 2, 15, 11, 11, 15,
  87. 3, 11, 11, 3, 7, 11, 11, 7, 8, 3, 3, 8, 0, 3, 15, 12,
  88. 12, 15, 4, 12, 12, 4, 7, 12, 12, 7, 8, 4, 4, 8, 0, 4,
  89. 15, 13, 13, 15, 5, 13, 13, 5, 7, 13, 13, 7, 8, 5, 5, 8,
  90. 0, 5, 15, 14, 14, 15, 6, 14, 14, 6, 7, 14, 14, 7, 8, 6,
  91. 6, 8, 0, 6, 4, 6, 6, 4, 12, 14, 14, 12, 6, 2, 2, 6,
  92. 14, 10, 10, 14, 2, 3, 3, 2, 10, 11, 11, 10, 3, 1, 1, 3,
  93. 11, 9, 9, 11, 1, 5, 5, 1, 9, 13, 13, 9, 5, 4, 4, 5,
  94. 13, 12, 12, 13, 4, 14, 6, 12, 12, 6, 14, 4, 6, 10, 2, 14,
  95. 14, 2, 10, 6, 2, 11, 3, 10, 10, 3, 11, 2, 3, 9, 1, 11,
  96. 11, 1, 9, 3, 1, 13, 5, 9, 9, 5, 13, 1, 5, 12, 4, 13,
  97. 13, 4, 12, 5, 0, 7, 0, 15, 15, 8, 8, 15, 15, 1, 7, 1,
  98. 1, 6, 2, 5, 3, 4, 4, 3, 5, 2, 6, 1, 0, 0, 1, 1,
  99. 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 2, 2, 3, 3,
  100. 4, 4, 5, 5, 6, 6, 7, 7, 14, 9, 1, 15, 8, 9, 8, 8,
  101. 9, 9, 1, 7, 0, 9, 9, 8, 6, 9, 13, 10, 2, 15, 8, 10,
  102. 7, 2, 15, 2, 2, 7, 0, 10, 10, 8, 5, 10, 12, 11, 3, 15,
  103. 8, 11, 7, 3, 15, 3, 3, 7, 0, 11, 11, 8, 4, 11, 11, 12,
  104. 4, 15, 8, 12, 7, 4, 15, 4, 4, 7, 0, 12, 12, 8, 3, 12,
  105. 10, 13, 5, 15, 8, 13, 7, 5, 15, 5, 5, 7, 0, 13, 13, 8,
  106. 2, 13, 9, 14, 6, 15, 8, 14, 7, 6, 15, 6, 6, 7, 0, 14,
  107. 14, 8, 1, 14, 5, 6, 2, 4, 13, 14, 10, 12, 4, 2, 3, 6,
  108. 12, 10, 11, 14, 6, 3, 1, 2, 14, 11, 9, 10, 2, 1, 5, 3,
  109. 10, 9, 13, 11, 3, 5, 4, 1, 11, 13, 12, 9, 1, 4, 6, 5,
  110. 9, 12, 14, 13, 5, 14, 2, 12, 13, 6, 10, 4, 4, 10, 3, 14,
  111. 12, 2, 11, 6, 6, 11, 1, 10, 14, 3, 9, 2, 2, 9, 5, 11,
  112. 10, 1, 13, 3, 3, 13, 4, 9, 11, 5, 12, 1, 1, 12, 6, 13,
  113. 9, 4, 14, 5, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
  114. };
  115. static int const slang_assoc[16*16] =
  116. {
  117. 134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  118. 28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
  119. 37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
  120. 46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
  121. 55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
  122. 64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
  123. 73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
  124. 122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
  125. 15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
  126. 154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
  127. 163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
  128. 172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
  129. 181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
  130. 190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
  131. 199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
  132. 123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
  133. };
  134. #endif
  135. #if defined(USE_CONIO)
  136. static struct text_info conio_ti;
  137. static char *conio_screen;
  138. #endif
  139. #if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
  140. Display *x11_dpy;
  141. Window x11_window;
  142. int x11_font_width, x11_font_height;
  143. static GC x11_gc;
  144. static Pixmap x11_pixmap;
  145. static uint8_t *x11_char, *x11_attr;
  146. static int x11_colors[16];
  147. static Font x11_font;
  148. static XFontStruct *x11_font_struct;
  149. static int x11_font_offset;
  150. #if defined(HAVE_X11_XKBLIB_H)
  151. static Bool x11_detect_autorepeat;
  152. #endif
  153. #endif
  154. static char *_caca_empty_line;
  155. static char *_caca_scratch_line;
  156. static unsigned int _caca_delay;
  157. static unsigned int _caca_rendertime;
  158. #if defined(OPTIMISE_SLANG_PALETTE)
  159. static int _caca_fgisbg = 0;
  160. #endif
  161. static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
  162. static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
  163. /*
  164. * Local functions
  165. */
  166. #if defined(USE_SLANG)
  167. static void slang_init_palette(void);
  168. #endif
  169. #if defined(USE_X11)
  170. static int x11_error_handler(Display *, XErrorEvent *);
  171. #endif
  172. static unsigned int _caca_getticks(void);
  173. /** \brief Set the default colour pair.
  174. *
  175. * This function sets the default colour pair. String functions such as
  176. * caca_printf() and graphical primitive functions such as caca_draw_line()
  177. * will use these colour pairs.
  178. *
  179. * \param fgcolor The requested foreground colour.
  180. * \param bgcolor The requested background colour.
  181. */
  182. void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
  183. {
  184. if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
  185. return;
  186. _caca_fgcolor = fgcolor;
  187. _caca_bgcolor = bgcolor;
  188. switch(_caca_driver)
  189. {
  190. #if defined(USE_SLANG)
  191. case CACA_DRIVER_SLANG:
  192. #if defined(OPTIMISE_SLANG_PALETTE)
  193. /* If foreground == background, discard this colour pair. Functions
  194. * such as caca_putchar will print spaces instead of characters */
  195. if(fgcolor != bgcolor)
  196. _caca_fgisbg = 0;
  197. else
  198. {
  199. _caca_fgisbg = 1;
  200. if(fgcolor == CACA_COLOR_BLACK)
  201. fgcolor = CACA_COLOR_WHITE;
  202. else if(fgcolor == CACA_COLOR_WHITE
  203. || fgcolor <= CACA_COLOR_LIGHTGRAY)
  204. fgcolor = CACA_COLOR_BLACK;
  205. else
  206. fgcolor = CACA_COLOR_WHITE;
  207. }
  208. #endif
  209. #if defined(OPTIMISE_SLANG_PALETTE)
  210. SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
  211. #else
  212. SLsmg_set_color(fgcolor + 16 * bgcolor);
  213. #endif
  214. break;
  215. #endif
  216. #if defined(USE_NCURSES)
  217. case CACA_DRIVER_NCURSES:
  218. attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
  219. break;
  220. #endif
  221. #if defined(USE_CONIO)
  222. case CACA_DRIVER_CONIO:
  223. textbackground(bgcolor);
  224. textcolor(fgcolor);
  225. break;
  226. #endif
  227. #if defined(USE_X11)
  228. case CACA_DRIVER_X11:
  229. /* FIXME */
  230. break;
  231. #endif
  232. default:
  233. break;
  234. }
  235. }
  236. /** \brief Get the current foreground colour.
  237. *
  238. * This function returns the current foreground colour that was set with
  239. * caca_set_color().
  240. *
  241. * \return The current foreground colour.
  242. */
  243. enum caca_color caca_get_fg_color(void)
  244. {
  245. return _caca_fgcolor;
  246. }
  247. /** \brief Get the current background colour.
  248. *
  249. * This function returns the current background colour that was set with
  250. * caca_set_color().
  251. *
  252. * \return The current background colour.
  253. */
  254. enum caca_color caca_get_bg_color(void)
  255. {
  256. return _caca_bgcolor;
  257. }
  258. /** \brief Print a character.
  259. *
  260. * This function prints a character at the given coordinates, using the
  261. * default foreground and background values. If the coordinates are outside
  262. * the screen boundaries, nothing is printed.
  263. *
  264. * \param x X coordinate.
  265. * \param y Y coordinate.
  266. * \param c The character to print.
  267. */
  268. void caca_putchar(int x, int y, char c)
  269. {
  270. #if defined(USE_CONIO)
  271. char *data;
  272. #endif
  273. if(x < 0 || x >= (int)_caca_width ||
  274. y < 0 || y >= (int)_caca_height)
  275. return;
  276. switch(_caca_driver)
  277. {
  278. #if defined(USE_SLANG)
  279. case CACA_DRIVER_SLANG:
  280. SLsmg_gotorc(y, x);
  281. #if defined(OPTIMISE_SLANG_PALETTE)
  282. if(_caca_fgisbg)
  283. SLsmg_write_char(' ');
  284. else
  285. #endif
  286. SLsmg_write_char(c);
  287. break;
  288. #endif
  289. #if defined(USE_NCURSES)
  290. case CACA_DRIVER_NCURSES:
  291. move(y, x);
  292. addch(c);
  293. break;
  294. #endif
  295. #if defined(USE_CONIO)
  296. case CACA_DRIVER_CONIO:
  297. data = conio_screen + 2 * (x + y * _caca_width);
  298. data[0] = c;
  299. data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
  300. break;
  301. #endif
  302. #if defined(USE_X11)
  303. case CACA_DRIVER_X11:
  304. x11_char[x + y * _caca_width] = c;
  305. x11_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
  306. break;
  307. #endif
  308. default:
  309. break;
  310. }
  311. }
  312. /** \brief Print a string.
  313. *
  314. * This function prints a string at the given coordinates, using the
  315. * default foreground and background values. The coordinates may be outside
  316. * the screen boundaries (eg. a negative Y coordinate) and the string will
  317. * be cropped accordingly if it is too long.
  318. *
  319. * \param x X coordinate.
  320. * \param y Y coordinate.
  321. * \param s The string to print.
  322. */
  323. void caca_putstr(int x, int y, char const *s)
  324. {
  325. #if defined(USE_CONIO) | defined(USE_X11)
  326. char *charbuf;
  327. #endif
  328. #if defined(USE_X11)
  329. char *attrbuf;
  330. #endif
  331. unsigned int len;
  332. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  333. return;
  334. len = strlen(s);
  335. if(x < 0)
  336. {
  337. len -= -x;
  338. if(len < 0)
  339. return;
  340. s += -x;
  341. x = 0;
  342. }
  343. if(x + len >= _caca_width)
  344. {
  345. len = _caca_width - x;
  346. memcpy(_caca_scratch_line, s, len);
  347. _caca_scratch_line[len] = '\0';
  348. s = _caca_scratch_line;
  349. }
  350. switch(_caca_driver)
  351. {
  352. #if defined(USE_SLANG)
  353. case CACA_DRIVER_SLANG:
  354. SLsmg_gotorc(y, x);
  355. #if defined(OPTIMISE_SLANG_PALETTE)
  356. if(_caca_fgisbg)
  357. SLsmg_write_string(_caca_empty_line + _caca_width - len);
  358. else
  359. #endif
  360. SLsmg_write_string((char *)(intptr_t)s);
  361. break;
  362. #endif
  363. #if defined(USE_NCURSES)
  364. case CACA_DRIVER_NCURSES:
  365. move(y, x);
  366. addstr(s);
  367. break;
  368. #endif
  369. #if defined(USE_CONIO)
  370. case CACA_DRIVER_CONIO:
  371. charbuf = conio_screen + 2 * (x + y * _caca_width);
  372. while(*s)
  373. {
  374. *charbuf++ = *s++;
  375. *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  376. }
  377. break;
  378. #endif
  379. #if defined(USE_X11)
  380. case CACA_DRIVER_X11:
  381. charbuf = x11_char + x + y * _caca_width;
  382. attrbuf = x11_attr + x + y * _caca_width;
  383. while(*s)
  384. {
  385. *charbuf++ = *s++;
  386. *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  387. }
  388. break;
  389. #endif
  390. default:
  391. break;
  392. }
  393. }
  394. /** \brief Format a string.
  395. *
  396. * This function formats a string at the given coordinates, using the
  397. * default foreground and background values. The coordinates may be outside
  398. * the screen boundaries (eg. a negative Y coordinate) and the string will
  399. * be cropped accordingly if it is too long. The syntax of the format
  400. * string is the same as for the C printf() function.
  401. *
  402. * \param x X coordinate.
  403. * \param y Y coordinate.
  404. * \param format The format string to print.
  405. * \param ... Arguments to the format string.
  406. */
  407. void caca_printf(int x, int y, char const *format, ...)
  408. {
  409. char tmp[BUFSIZ];
  410. char *buf = tmp;
  411. va_list args;
  412. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  413. return;
  414. if(_caca_width - x + 1 > BUFSIZ)
  415. buf = malloc(_caca_width - x + 1);
  416. va_start(args, format);
  417. #if defined(HAVE_VSNPRINTF)
  418. vsnprintf(buf, _caca_width - x + 1, format, args);
  419. #else
  420. vsprintf(buf, format, args);
  421. #endif
  422. buf[_caca_width - x] = '\0';
  423. va_end(args);
  424. caca_putstr(x, y, buf);
  425. if(buf != tmp)
  426. free(buf);
  427. }
  428. /** \brief Clear the screen.
  429. *
  430. * This function clears the screen using a black background.
  431. */
  432. void caca_clear(void)
  433. {
  434. enum caca_color oldfg = caca_get_fg_color();
  435. enum caca_color oldbg = caca_get_bg_color();
  436. int y = _caca_height;
  437. caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
  438. /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
  439. while(y--)
  440. caca_putstr(0, y, _caca_empty_line);
  441. caca_set_color(oldfg, oldbg);
  442. }
  443. #if !defined(_DOXYGEN_SKIP_ME)
  444. int _caca_init_graphics(void)
  445. {
  446. #if defined(USE_SLANG)
  447. if(_caca_driver == CACA_DRIVER_SLANG)
  448. {
  449. slang_init_palette();
  450. /* Disable alt charset support so that we get a chance to have all
  451. * 256 colour pairs */
  452. SLtt_Has_Alt_Charset = 0;
  453. _caca_width = SLtt_Screen_Cols;
  454. _caca_height = SLtt_Screen_Rows;
  455. }
  456. else
  457. #endif
  458. #if defined(USE_NCURSES)
  459. if(_caca_driver == CACA_DRIVER_NCURSES)
  460. {
  461. static int curses_colors[] =
  462. {
  463. /* Standard curses colours */
  464. COLOR_BLACK,
  465. COLOR_BLUE,
  466. COLOR_GREEN,
  467. COLOR_CYAN,
  468. COLOR_RED,
  469. COLOR_MAGENTA,
  470. COLOR_YELLOW,
  471. COLOR_WHITE,
  472. /* Extra values for xterm-16color */
  473. COLOR_BLACK + 8,
  474. COLOR_BLUE + 8,
  475. COLOR_GREEN + 8,
  476. COLOR_CYAN + 8,
  477. COLOR_RED + 8,
  478. COLOR_MAGENTA + 8,
  479. COLOR_YELLOW + 8,
  480. COLOR_WHITE + 8
  481. };
  482. int fg, bg, max;
  483. /* Activate colour */
  484. start_color();
  485. /* If COLORS == 16, it means the terminal supports full bright colours
  486. * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
  487. * we can build 16*16 colour pairs.
  488. * If COLORS == 8, it means the terminal does not know about bright
  489. * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
  490. * and \e[5m). We can only build 8*8 colour pairs. */
  491. max = COLORS >= 16 ? 16 : 8;
  492. for(bg = 0; bg < max; bg++)
  493. for(fg = 0; fg < max; fg++)
  494. {
  495. /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
  496. * is light gray on black, since some terminals don't like
  497. * this colour pair to be redefined. */
  498. int col = ((max + 7 - fg) % max) + max * bg;
  499. init_pair(col, curses_colors[fg], curses_colors[bg]);
  500. ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
  501. if(max == 8)
  502. {
  503. /* Bright fg on simple bg */
  504. ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
  505. /* Simple fg on bright bg */
  506. ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
  507. | COLOR_PAIR(col);
  508. /* Bright fg on bright bg */
  509. ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
  510. | COLOR_PAIR(col);
  511. }
  512. }
  513. _caca_width = COLS;
  514. _caca_height = LINES;
  515. }
  516. else
  517. #endif
  518. #if defined(USE_CONIO)
  519. if(_caca_driver == CACA_DRIVER_CONIO)
  520. {
  521. gettextinfo(&conio_ti);
  522. conio_screen = malloc(2 * conio_ti.screenwidth
  523. * conio_ti.screenheight * sizeof(char));
  524. if(conio_screen == NULL)
  525. return -1;
  526. # if defined(SCREENUPDATE_IN_PC_H)
  527. ScreenRetrieve(conio_screen);
  528. # else
  529. /* FIXME */
  530. # endif
  531. _caca_width = conio_ti.screenwidth;
  532. _caca_height = conio_ti.screenheight;
  533. }
  534. else
  535. #endif
  536. #if defined(USE_X11)
  537. if(_caca_driver == CACA_DRIVER_X11)
  538. {
  539. static int x11_palette[] =
  540. {
  541. /* Standard curses colours */
  542. 0x0, 0x0, 0x0,
  543. 0x0, 0x0, 0x8000,
  544. 0x0, 0x8000, 0x0,
  545. 0x0, 0x8000, 0x8000,
  546. 0x8000, 0x0, 0x0,
  547. 0x8000, 0x0, 0x8000,
  548. 0x8000, 0x8000, 0x0,
  549. 0x8000, 0x8000, 0x8000,
  550. /* Extra values for xterm-16color */
  551. 0x4000, 0x4000, 0x4000,
  552. 0x4000, 0x4000, 0xffff,
  553. 0x4000, 0xffff, 0x4000,
  554. 0x4000, 0xffff, 0xffff,
  555. 0xffff, 0x4000, 0x4000,
  556. 0xffff, 0x4000, 0xffff,
  557. 0xffff, 0xffff, 0x4000,
  558. 0xffff, 0xffff, 0xffff,
  559. };
  560. Colormap colormap;
  561. XSetWindowAttributes x11_winattr;
  562. int (*old_error_handler)(Display *, XErrorEvent *);
  563. char const *font_name = "8x13bold";
  564. int i;
  565. if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
  566. sscanf(getenv("CACA_GEOMETRY"),
  567. "%ux%u", &_caca_width, &_caca_height);
  568. if(!_caca_width)
  569. _caca_width = 80;
  570. if(!_caca_height)
  571. _caca_height = 32;
  572. x11_char = malloc(_caca_width * _caca_height * sizeof(int));
  573. if(x11_char == NULL)
  574. return -1;
  575. x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
  576. if(x11_attr == NULL)
  577. {
  578. free(x11_char);
  579. return -1;
  580. }
  581. x11_dpy = XOpenDisplay(NULL);
  582. if(x11_dpy == NULL)
  583. {
  584. free(x11_char);
  585. free(x11_attr);
  586. return -1;
  587. }
  588. if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
  589. font_name = getenv("CACA_FONT");
  590. /* Ignore font errors */
  591. old_error_handler = XSetErrorHandler(x11_error_handler);
  592. x11_font = XLoadFont(x11_dpy, font_name);
  593. if(!x11_font)
  594. {
  595. XCloseDisplay(x11_dpy);
  596. free(x11_char);
  597. free(x11_attr);
  598. return -1;
  599. }
  600. x11_font_struct = XQueryFont(x11_dpy, x11_font);
  601. if(!x11_font_struct)
  602. {
  603. XUnloadFont(x11_dpy, x11_font);
  604. XCloseDisplay(x11_dpy);
  605. free(x11_char);
  606. free(x11_attr);
  607. return -1;
  608. }
  609. /* Reset the default X11 error handler */
  610. XSetErrorHandler(old_error_handler);
  611. x11_font_width = x11_font_struct->max_bounds.width;
  612. x11_font_height = x11_font_struct->max_bounds.ascent
  613. + x11_font_struct->max_bounds.descent;
  614. x11_font_offset = x11_font_struct->max_bounds.descent;
  615. colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
  616. for(i = 0; i < 16; i++)
  617. {
  618. XColor color;
  619. color.red = x11_palette[i * 3];
  620. color.green = x11_palette[i * 3 + 1];
  621. color.blue = x11_palette[i * 3 + 2];
  622. XAllocColor(x11_dpy, colormap, &color);
  623. x11_colors[i] = color.pixel;
  624. }
  625. x11_winattr.backing_store = Always;
  626. x11_winattr.background_pixel = x11_colors[0];
  627. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  628. x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
  629. _caca_width * x11_font_width,
  630. _caca_height * x11_font_height,
  631. 0, 0, InputOutput, 0,
  632. CWBackingStore | CWBackPixel | CWEventMask,
  633. &x11_winattr);
  634. XStoreName(x11_dpy, x11_window, "caca for X");
  635. XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
  636. XMapWindow(x11_dpy, x11_window);
  637. x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
  638. XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
  639. XSetFont(x11_dpy, x11_gc, x11_font);
  640. for(;;)
  641. {
  642. XEvent event;
  643. XNextEvent(x11_dpy, &event);
  644. if (event.type == MapNotify)
  645. break;
  646. }
  647. /* Disable autorepeat */
  648. #if defined(HAVE_X11_XKBLIB_H)
  649. XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
  650. if(!x11_detect_autorepeat)
  651. XAutoRepeatOff(x11_dpy);
  652. #endif
  653. XSelectInput(x11_dpy, x11_window,
  654. KeyPressMask | KeyReleaseMask | ButtonPressMask
  655. | ButtonReleaseMask | PointerMotionMask);
  656. XSync(x11_dpy, False);
  657. x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
  658. _caca_width * x11_font_width,
  659. _caca_height * x11_font_height,
  660. DefaultDepth(x11_dpy,
  661. DefaultScreen(x11_dpy)));
  662. }
  663. else
  664. #endif
  665. {
  666. /* Dummy */
  667. }
  668. _caca_empty_line = malloc(_caca_width + 1);
  669. memset(_caca_empty_line, ' ', _caca_width);
  670. _caca_empty_line[_caca_width] = '\0';
  671. _caca_scratch_line = malloc(_caca_width + 1);
  672. _caca_delay = 0;
  673. _caca_rendertime = 0;
  674. return 0;
  675. }
  676. int _caca_end_graphics(void)
  677. {
  678. #if defined(USE_SLANG)
  679. /* Nothing to do */
  680. #endif
  681. #if defined(USE_NCURSES)
  682. /* Nothing to do */
  683. #endif
  684. #if defined(USE_CONIO)
  685. if(_caca_driver == CACA_DRIVER_CONIO)
  686. {
  687. free(conio_screen);
  688. }
  689. else
  690. #endif
  691. #if defined(USE_X11)
  692. if(_caca_driver == CACA_DRIVER_X11)
  693. {
  694. XSync(x11_dpy, False);
  695. #if defined(HAVE_X11_XKBLIB_H)
  696. if(!x11_detect_autorepeat)
  697. XAutoRepeatOn(x11_dpy);
  698. #endif
  699. XFreePixmap(x11_dpy, x11_pixmap);
  700. XFreeFont(x11_dpy, x11_font_struct);
  701. XFreeGC(x11_dpy, x11_gc);
  702. XUnmapWindow(x11_dpy, x11_window);
  703. XDestroyWindow(x11_dpy, x11_window);
  704. XCloseDisplay(x11_dpy);
  705. free(x11_char);
  706. free(x11_attr);
  707. }
  708. else
  709. #endif
  710. {
  711. /* Dummy */
  712. }
  713. free(_caca_empty_line);
  714. return 0;
  715. }
  716. #endif /* _DOXYGEN_SKIP_ME */
  717. /** \brief Set the refresh delay.
  718. *
  719. * This function sets the refresh delay in microseconds. The refresh delay
  720. * is used by caca_refresh() to achieve constant framerate. See the
  721. * caca_refresh() documentation for more details.
  722. *
  723. * If the argument is zero, constant framerate is disabled. This is the
  724. * default behaviour.
  725. *
  726. * \param usec The refresh delay in microseconds.
  727. */
  728. void caca_set_delay(unsigned int usec)
  729. {
  730. _caca_delay = usec;
  731. }
  732. /** \brief Get the average rendering time.
  733. *
  734. * This function returns the average rendering time, which is the average
  735. * measured time between two caca_refresh() calls, in microseconds. If
  736. * constant framerate was activated by calling caca_set_delay(), the average
  737. * rendering time will not be considerably shorter than the requested delay
  738. * even if the real rendering time was shorter.
  739. *
  740. * \return The render time in microseconds.
  741. */
  742. unsigned int caca_get_rendertime(void)
  743. {
  744. return _caca_rendertime;
  745. }
  746. static unsigned int _caca_getticks(void)
  747. {
  748. static int last_sec = 0, last_usec = 0;
  749. struct timeval tv;
  750. unsigned int ticks = 0;
  751. gettimeofday(&tv, NULL);
  752. if(last_sec != 0)
  753. {
  754. /* If the delay was greater than 60 seconds, return 10 seconds
  755. * otherwise we may overflow our ticks counter. */
  756. if(tv.tv_sec >= last_sec + 60)
  757. ticks = 60 * 1000000;
  758. else
  759. {
  760. ticks = (tv.tv_sec - last_sec) * 1000000;
  761. ticks += tv.tv_usec;
  762. ticks -= last_usec;
  763. }
  764. }
  765. last_sec = tv.tv_sec;
  766. last_usec = tv.tv_usec;
  767. return ticks;
  768. }
  769. /** \brief Flush pending changes and redraw the screen.
  770. *
  771. * This function flushes all graphical operations and prints them to the
  772. * screen. Nothing will show on the screen caca_refresh() is not called.
  773. *
  774. * If caca_set_delay() was called with a non-zero value, caca_refresh()
  775. * will use that value to achieve constant framerate: if two consecutive
  776. * calls to caca_refresh() are within a time range shorter than the value
  777. * set with caca_set_delay(), the second call will wait a bit before
  778. * performing the screen refresh.
  779. */
  780. void caca_refresh(void)
  781. {
  782. #if !defined(_DOXYGEN_SKIP_ME)
  783. #define IDLE_USEC 10000
  784. #endif
  785. static int lastticks = 0;
  786. int ticks = lastticks + _caca_getticks();
  787. #if defined(USE_SLANG)
  788. if(_caca_driver == CACA_DRIVER_SLANG)
  789. {
  790. SLsmg_refresh();
  791. }
  792. else
  793. #endif
  794. #if defined(USE_NCURSES)
  795. if(_caca_driver == CACA_DRIVER_NCURSES)
  796. {
  797. refresh();
  798. }
  799. else
  800. #endif
  801. #if defined(USE_CONIO)
  802. if(_caca_driver == CACA_DRIVER_CONIO)
  803. {
  804. # if defined(SCREENUPDATE_IN_PC_H)
  805. ScreenUpdate(conio_screen);
  806. # else
  807. /* FIXME */
  808. # endif
  809. }
  810. else
  811. #endif
  812. #if defined(USE_X11)
  813. if(_caca_driver == CACA_DRIVER_X11)
  814. {
  815. unsigned int x, y, len;
  816. /* First draw the background colours. Splitting the process in two
  817. * loops like this is actually slightly faster. */
  818. for(y = 0; y < _caca_height; y++)
  819. {
  820. for(x = 0; x < _caca_width; x += len)
  821. {
  822. unsigned char *attr = x11_attr + x + y * _caca_width;
  823. len = 1;
  824. while(x + len < _caca_width
  825. && (attr[len] >> 4) == (attr[0] >> 4))
  826. len++;
  827. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
  828. XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
  829. x * x11_font_width, y * x11_font_height,
  830. len * x11_font_width, x11_font_height);
  831. }
  832. }
  833. /* Then print the foreground characters */
  834. for(y = 0; y < _caca_height; y++)
  835. {
  836. for(x = 0; x < _caca_width; x += len)
  837. {
  838. unsigned char *attr = x11_attr + x + y * _caca_width;
  839. len = 1;
  840. /* Skip spaces */
  841. if(x11_char[x + y * _caca_width] == ' ')
  842. continue;
  843. while(x + len < _caca_width
  844. && (attr[len] & 0xf) == (attr[0] & 0xf))
  845. len++;
  846. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
  847. XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
  848. (y + 1) * x11_font_height - x11_font_offset,
  849. x11_char + x + y * _caca_width, len);
  850. }
  851. }
  852. XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
  853. _caca_width * x11_font_width, _caca_height * x11_font_height,
  854. 0, 0);
  855. XFlush(x11_dpy);
  856. }
  857. #endif
  858. /* Wait until _caca_delay + time of last call */
  859. ticks += _caca_getticks();
  860. for(; ticks + IDLE_USEC < (int)_caca_delay; ticks += _caca_getticks())
  861. usleep(IDLE_USEC);
  862. /* Update the sliding mean of the render time */
  863. _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
  864. lastticks = ticks - _caca_delay;
  865. /* If we drifted too much, it's bad, bad, bad. */
  866. if(lastticks > (int)_caca_delay)
  867. lastticks = 0;
  868. }
  869. #if defined(USE_SLANG)
  870. static void slang_init_palette(void)
  871. {
  872. /* See SLang ref., 5.4.4. */
  873. static char *slang_colors[16] =
  874. {
  875. /* Standard colours */
  876. "black",
  877. "blue",
  878. "green",
  879. "cyan",
  880. "red",
  881. "magenta",
  882. "brown",
  883. "lightgray",
  884. /* Bright colours */
  885. "gray",
  886. "brightblue",
  887. "brightgreen",
  888. "brightcyan",
  889. "brightred",
  890. "brightmagenta",
  891. "yellow",
  892. "white",
  893. };
  894. #if defined(OPTIMISE_SLANG_PALETTE)
  895. int i;
  896. for(i = 0; i < 16 * 16; i++)
  897. SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
  898. slang_colors[slang_palette[i * 2 + 1]]);
  899. #else
  900. int fg, bg;
  901. for(bg = 0; bg < 16; bg++)
  902. for(fg = 0; fg < 16; fg++)
  903. {
  904. int i = fg + 16 * bg;
  905. SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
  906. }
  907. #endif
  908. }
  909. #endif /* USE_SLANG */
  910. #if defined(USE_X11)
  911. static int x11_error_handler(Display *dpy, XErrorEvent *event)
  912. {
  913. return 0;
  914. }
  915. #endif