Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

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