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

2024 wiersze
55 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. # if defined(HAVE_NCURSES_H)
  38. # include <ncurses.h>
  39. # else
  40. # include <curses.h>
  41. # endif
  42. #endif
  43. #if defined(USE_CONIO)
  44. # include <conio.h>
  45. # if defined(SCREENUPDATE_IN_PC_H)
  46. # include <pc.h>
  47. # endif
  48. #endif
  49. #if defined(USE_X11)
  50. # include <X11/Xlib.h>
  51. # if defined(HAVE_X11_XKBLIB_H)
  52. # include <X11/XKBlib.h>
  53. # endif
  54. #endif
  55. #if defined(USE_WIN32)
  56. # include <windows.h>
  57. #endif
  58. #if defined(USE_GL)
  59. # include <GL/gl.h>
  60. # include <GL/glut.h>
  61. #endif
  62. #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
  63. # include <inttypes.h>
  64. #else
  65. typedef unsigned char uint8_t;
  66. #endif
  67. #include <stdio.h> /* BUFSIZ */
  68. #include <string.h>
  69. #include <stdlib.h>
  70. #if defined(HAVE_UNISTD_H)
  71. # include <unistd.h>
  72. #endif
  73. #include <stdarg.h>
  74. #if defined(HAVE_SIGNAL_H)
  75. # include <signal.h>
  76. #endif
  77. #if defined(HAVE_SYS_IOCTL_H)
  78. # include <sys/ioctl.h>
  79. #endif
  80. #include "caca.h"
  81. #include "caca_internals.h"
  82. /*
  83. * Global variables
  84. */
  85. #if !defined(_DOXYGEN_SKIP_ME)
  86. unsigned int _caca_width = 0;
  87. unsigned int _caca_height = 0;
  88. int _caca_resize = 0;
  89. int _caca_resize_event = 0;
  90. #endif
  91. /*
  92. * Local variables
  93. */
  94. #if !defined(_DOXYGEN_SKIP_ME)
  95. static uint8_t *cache_char, *cache_attr;
  96. #endif
  97. #if defined(USE_NCURSES)
  98. static int ncurses_attr[16*16];
  99. #endif
  100. #if defined(USE_SLANG)
  101. /* Tables generated by test/optipal.c */
  102. static int const slang_palette[2*16*16] =
  103. {
  104. 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0,
  105. 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 8,
  106. 8, 7, 7, 8, 15, 7, 7, 15, 15, 9, 9, 15, 1, 9, 9, 1,
  107. 7, 9, 9, 7, 8, 1, 1, 8, 0, 1, 15, 10, 10, 15, 2, 10,
  108. 10, 2, 7, 10, 10, 7, 8, 2, 2, 8, 0, 2, 15, 11, 11, 15,
  109. 3, 11, 11, 3, 7, 11, 11, 7, 8, 3, 3, 8, 0, 3, 15, 12,
  110. 12, 15, 4, 12, 12, 4, 7, 12, 12, 7, 8, 4, 4, 8, 0, 4,
  111. 15, 13, 13, 15, 5, 13, 13, 5, 7, 13, 13, 7, 8, 5, 5, 8,
  112. 0, 5, 15, 14, 14, 15, 6, 14, 14, 6, 7, 14, 14, 7, 8, 6,
  113. 6, 8, 0, 6, 4, 6, 6, 4, 12, 14, 14, 12, 6, 2, 2, 6,
  114. 14, 10, 10, 14, 2, 3, 3, 2, 10, 11, 11, 10, 3, 1, 1, 3,
  115. 11, 9, 9, 11, 1, 5, 5, 1, 9, 13, 13, 9, 5, 4, 4, 5,
  116. 13, 12, 12, 13, 4, 14, 6, 12, 12, 6, 14, 4, 6, 10, 2, 14,
  117. 14, 2, 10, 6, 2, 11, 3, 10, 10, 3, 11, 2, 3, 9, 1, 11,
  118. 11, 1, 9, 3, 1, 13, 5, 9, 9, 5, 13, 1, 5, 12, 4, 13,
  119. 13, 4, 12, 5, 0, 7, 0, 15, 15, 8, 8, 15, 15, 1, 7, 1,
  120. 1, 6, 2, 5, 3, 4, 4, 3, 5, 2, 6, 1, 0, 0, 1, 1,
  121. 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 2, 2, 3, 3,
  122. 4, 4, 5, 5, 6, 6, 7, 7, 14, 9, 1, 15, 8, 9, 8, 8,
  123. 9, 9, 1, 7, 0, 9, 9, 8, 6, 9, 13, 10, 2, 15, 8, 10,
  124. 7, 2, 15, 2, 2, 7, 0, 10, 10, 8, 5, 10, 12, 11, 3, 15,
  125. 8, 11, 7, 3, 15, 3, 3, 7, 0, 11, 11, 8, 4, 11, 11, 12,
  126. 4, 15, 8, 12, 7, 4, 15, 4, 4, 7, 0, 12, 12, 8, 3, 12,
  127. 10, 13, 5, 15, 8, 13, 7, 5, 15, 5, 5, 7, 0, 13, 13, 8,
  128. 2, 13, 9, 14, 6, 15, 8, 14, 7, 6, 15, 6, 6, 7, 0, 14,
  129. 14, 8, 1, 14, 5, 6, 2, 4, 13, 14, 10, 12, 4, 2, 3, 6,
  130. 12, 10, 11, 14, 6, 3, 1, 2, 14, 11, 9, 10, 2, 1, 5, 3,
  131. 10, 9, 13, 11, 3, 5, 4, 1, 11, 13, 12, 9, 1, 4, 6, 5,
  132. 9, 12, 14, 13, 5, 14, 2, 12, 13, 6, 10, 4, 4, 10, 3, 14,
  133. 12, 2, 11, 6, 6, 11, 1, 10, 14, 3, 9, 2, 2, 9, 5, 11,
  134. 10, 1, 13, 3, 3, 13, 4, 9, 11, 5, 12, 1, 1, 12, 6, 13,
  135. 9, 4, 14, 5, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
  136. };
  137. static int const slang_assoc[16*16] =
  138. {
  139. 134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  140. 28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
  141. 37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
  142. 46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
  143. 55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
  144. 64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
  145. 73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
  146. 122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
  147. 15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
  148. 154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
  149. 163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
  150. 172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
  151. 181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
  152. 190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
  153. 199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
  154. 123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
  155. };
  156. #endif
  157. #if defined(USE_CONIO)
  158. static struct text_info conio_ti;
  159. static char *conio_screen;
  160. #endif
  161. #if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
  162. Display *x11_dpy;
  163. Window x11_window;
  164. Pixmap x11_pixmap;
  165. GC x11_gc;
  166. long int x11_event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
  167. | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
  168. | ExposureMask;
  169. int x11_font_width, x11_font_height;
  170. unsigned int x11_new_width, x11_new_height;
  171. static int x11_colors[16];
  172. static Font x11_font;
  173. static XFontStruct *x11_font_struct;
  174. static int x11_font_offset;
  175. #if defined(HAVE_X11_XKBLIB_H)
  176. static Bool x11_detect_autorepeat;
  177. #endif
  178. #endif
  179. #if defined(USE_WIN32)
  180. HANDLE win32_hin, win32_hout;
  181. static HANDLE win32_front, win32_back;
  182. static CHAR_INFO *win32_buffer;
  183. static int const win32_fg_palette[] =
  184. {
  185. 0,
  186. FOREGROUND_BLUE,
  187. FOREGROUND_GREEN,
  188. FOREGROUND_GREEN | FOREGROUND_BLUE,
  189. FOREGROUND_RED,
  190. FOREGROUND_RED | FOREGROUND_BLUE,
  191. FOREGROUND_RED | FOREGROUND_GREEN,
  192. FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
  193. FOREGROUND_INTENSITY,
  194. FOREGROUND_INTENSITY | FOREGROUND_BLUE,
  195. FOREGROUND_INTENSITY | FOREGROUND_GREEN,
  196. FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
  197. FOREGROUND_INTENSITY | FOREGROUND_RED,
  198. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
  199. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
  200. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  201. };
  202. static int const win32_bg_palette[] =
  203. {
  204. 0,
  205. BACKGROUND_BLUE,
  206. BACKGROUND_GREEN,
  207. BACKGROUND_GREEN | BACKGROUND_BLUE,
  208. BACKGROUND_RED,
  209. BACKGROUND_RED | BACKGROUND_BLUE,
  210. BACKGROUND_RED | BACKGROUND_GREEN,
  211. BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
  212. BACKGROUND_INTENSITY,
  213. BACKGROUND_INTENSITY | BACKGROUND_BLUE,
  214. BACKGROUND_INTENSITY | BACKGROUND_GREEN,
  215. BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
  216. BACKGROUND_INTENSITY | BACKGROUND_RED,
  217. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
  218. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
  219. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
  220. };
  221. #endif
  222. #if defined(USE_GL)
  223. static unsigned int const gl_bg_palette[] =
  224. {
  225. 0,
  226. 0x0000007F,
  227. 0x00007F00,
  228. 0x00007F7F,
  229. 0x007F0000,
  230. 0x007F007F,
  231. 0x007F7F00,
  232. 0x007F7F7F,
  233. // + intensity
  234. 0x00000000,
  235. 0x000000FF,
  236. 0x0000FF00,
  237. 0x0000FFFF,
  238. 0x00FF0000,
  239. 0x00FF00FF,
  240. 0x00FFFF00,
  241. 0x00FFFFFF,
  242. };
  243. int gl_window;
  244. unsigned int gl_width, gl_height;
  245. float gl_font_width, gl_font_height;
  246. float gl_incx, gl_incy;
  247. int id[94];
  248. unsigned char gl_resized=0, gl_bit=0;
  249. unsigned char gl_mouse_changed=0, gl_mouse_clicked=0;
  250. unsigned int gl_mouse_x, gl_mouse_y;
  251. unsigned int gl_mouse_button=0, gl_mouse_state=0;
  252. #endif
  253. static char *_caca_empty_line;
  254. static char *_caca_scratch_line;
  255. static unsigned int _caca_delay;
  256. static unsigned int _caca_rendertime;
  257. #if defined(OPTIMISE_SLANG_PALETTE)
  258. static int _caca_fgisbg = 0;
  259. #endif
  260. static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
  261. static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
  262. /*
  263. * Local functions
  264. */
  265. static void caca_handle_resize(void);
  266. #if defined(USE_SLANG)
  267. static void slang_init_palette(void);
  268. #endif
  269. #if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
  270. static RETSIGTYPE sigwinch_handler(int);
  271. #endif
  272. #if defined(USE_X11)
  273. static int x11_error_handler(Display *, XErrorEvent *);
  274. #endif
  275. #if defined(USE_GL)
  276. unsigned char gl_key = 0;
  277. int gl_special_key=0;
  278. int gl_new_width;
  279. int gl_new_height;
  280. static void gl_handle_keyboard(unsigned char key, int x, int y)
  281. {
  282. gl_key = key;
  283. }
  284. static void gl_handle_special_key(int key, int x, int y)
  285. {
  286. gl_special_key = key;
  287. }
  288. static void gl_handle_reshape (int w, int h)
  289. {
  290. if(gl_bit) /* Do not handle reshaping at the first time*/
  291. {
  292. gl_new_width = w;
  293. gl_new_height = h;
  294. gl_resized = 1;
  295. }
  296. else
  297. gl_bit=1;
  298. }
  299. static void gl_handle_mouse(int button, int state, int x, int y)
  300. {
  301. gl_mouse_clicked = 1;
  302. gl_mouse_button = button;
  303. gl_mouse_state = state;
  304. gl_mouse_x = x/gl_font_width;
  305. gl_mouse_y = y/gl_font_height;
  306. gl_mouse_changed = 1;
  307. }
  308. static void gl_handle_mouse_motion(int x, int y)
  309. {
  310. gl_mouse_x = x/gl_font_width;
  311. gl_mouse_y = y/gl_font_height;
  312. gl_mouse_changed = 1;
  313. }
  314. #endif
  315. /** \brief Set the default colour pair.
  316. *
  317. * This function sets the default colour pair. String functions such as
  318. * caca_printf() and graphical primitive functions such as caca_draw_line()
  319. * will use these colour pairs.
  320. *
  321. * \param fgcolor The requested foreground colour.
  322. * \param bgcolor The requested background colour.
  323. */
  324. void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
  325. {
  326. if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
  327. return;
  328. _caca_fgcolor = fgcolor;
  329. _caca_bgcolor = bgcolor;
  330. switch(_caca_driver)
  331. {
  332. #if defined(USE_SLANG)
  333. case CACA_DRIVER_SLANG:
  334. #if defined(OPTIMISE_SLANG_PALETTE)
  335. /* If foreground == background, discard this colour pair. Functions
  336. * such as caca_putchar will print spaces instead of characters */
  337. if(fgcolor != bgcolor)
  338. _caca_fgisbg = 0;
  339. else
  340. {
  341. _caca_fgisbg = 1;
  342. if(fgcolor == CACA_COLOR_BLACK)
  343. fgcolor = CACA_COLOR_WHITE;
  344. else if(fgcolor == CACA_COLOR_WHITE
  345. || fgcolor <= CACA_COLOR_LIGHTGRAY)
  346. fgcolor = CACA_COLOR_BLACK;
  347. else
  348. fgcolor = CACA_COLOR_WHITE;
  349. }
  350. #endif
  351. #if defined(OPTIMISE_SLANG_PALETTE)
  352. SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
  353. #else
  354. SLsmg_set_color(fgcolor + 16 * bgcolor);
  355. #endif
  356. break;
  357. #endif
  358. #if defined(USE_NCURSES)
  359. case CACA_DRIVER_NCURSES:
  360. attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
  361. break;
  362. #endif
  363. #if defined(USE_CONIO)
  364. case CACA_DRIVER_CONIO:
  365. textbackground(bgcolor);
  366. textcolor(fgcolor);
  367. break;
  368. #endif
  369. #if defined(USE_X11)
  370. case CACA_DRIVER_X11:
  371. /* Nothing to do */
  372. break;
  373. #endif
  374. #if defined(USE_WIN32)
  375. case CACA_DRIVER_WIN32:
  376. /* Nothing to do */
  377. break;
  378. #endif
  379. #if defined(USE_GL)
  380. case CACA_DRIVER_GL:
  381. /* Nothing to do */
  382. break;
  383. #endif
  384. #if defined(USE_NULL)
  385. case CACA_DRIVER_NULL:
  386. /* Nothing to do */
  387. break;
  388. #endif
  389. default:
  390. break;
  391. }
  392. }
  393. /** \brief Get the current foreground colour.
  394. *
  395. * This function returns the current foreground colour that was set with
  396. * caca_set_color().
  397. *
  398. * \return The current foreground colour.
  399. */
  400. enum caca_color caca_get_fg_color(void)
  401. {
  402. return _caca_fgcolor;
  403. }
  404. /** \brief Get the current background colour.
  405. *
  406. * This function returns the current background colour that was set with
  407. * caca_set_color().
  408. *
  409. * \return The current background colour.
  410. */
  411. enum caca_color caca_get_bg_color(void)
  412. {
  413. return _caca_bgcolor;
  414. }
  415. /** \brief Print a character.
  416. *
  417. * This function prints a character at the given coordinates, using the
  418. * default foreground and background values. If the coordinates are outside
  419. * the screen boundaries, nothing is printed.
  420. *
  421. * \param x X coordinate.
  422. * \param y Y coordinate.
  423. * \param c The character to print.
  424. */
  425. void caca_putchar(int x, int y, char c)
  426. {
  427. #if defined(USE_CONIO)
  428. char *data;
  429. #endif
  430. if(x < 0 || x >= (int)_caca_width ||
  431. y < 0 || y >= (int)_caca_height)
  432. return;
  433. cache_char[x + y * _caca_width] = c;
  434. cache_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
  435. switch(_caca_driver)
  436. {
  437. #if defined(USE_SLANG)
  438. case CACA_DRIVER_SLANG:
  439. SLsmg_gotorc(y, x);
  440. #if defined(OPTIMISE_SLANG_PALETTE)
  441. if(_caca_fgisbg)
  442. SLsmg_write_char(' ');
  443. else
  444. #endif
  445. SLsmg_write_char(c);
  446. break;
  447. #endif
  448. #if defined(USE_NCURSES)
  449. case CACA_DRIVER_NCURSES:
  450. move(y, x);
  451. addch(c);
  452. break;
  453. #endif
  454. #if defined(USE_CONIO)
  455. case CACA_DRIVER_CONIO:
  456. data = conio_screen + 2 * (x + y * _caca_width);
  457. data[0] = c;
  458. data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
  459. break;
  460. #endif
  461. #if defined(USE_X11)
  462. case CACA_DRIVER_X11:
  463. break;
  464. #endif
  465. #if defined(USE_WIN32)
  466. case CACA_DRIVER_WIN32:
  467. break;
  468. #endif
  469. #if defined(USE_GL)
  470. case CACA_DRIVER_GL:
  471. break;
  472. #endif
  473. #if defined(USE_NULL)
  474. case CACA_DRIVER_NULL:
  475. break;
  476. #endif
  477. default:
  478. break;
  479. }
  480. }
  481. /** \brief Print a string.
  482. *
  483. * This function prints a string at the given coordinates, using the
  484. * default foreground and background values. The coordinates may be outside
  485. * the screen boundaries (eg. a negative Y coordinate) and the string will
  486. * be cropped accordingly if it is too long.
  487. *
  488. * \param x X coordinate.
  489. * \param y Y coordinate.
  490. * \param s The string to print.
  491. */
  492. void caca_putstr(int x, int y, char const *s)
  493. {
  494. char *charbuf;
  495. char *attrbuf;
  496. char const *t;
  497. unsigned int len;
  498. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  499. return;
  500. len = strlen(s);
  501. if(x < 0)
  502. {
  503. if(len < (unsigned int)-x)
  504. return;
  505. len -= -x;
  506. s += -x;
  507. x = 0;
  508. }
  509. if(x + len >= _caca_width)
  510. {
  511. len = _caca_width - x;
  512. memcpy(_caca_scratch_line, s, len);
  513. _caca_scratch_line[len] = '\0';
  514. s = _caca_scratch_line;
  515. }
  516. charbuf = cache_char + x + y * _caca_width;
  517. attrbuf = cache_attr + x + y * _caca_width;
  518. t = s;
  519. while(*t)
  520. {
  521. *charbuf++ = *t++;
  522. *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  523. }
  524. switch(_caca_driver)
  525. {
  526. #if defined(USE_SLANG)
  527. case CACA_DRIVER_SLANG:
  528. SLsmg_gotorc(y, x);
  529. #if defined(OPTIMISE_SLANG_PALETTE)
  530. if(_caca_fgisbg)
  531. SLsmg_write_string(_caca_empty_line + _caca_width - len);
  532. else
  533. #endif
  534. {
  535. union { char *ch; const char *constch; } u;
  536. u.constch = s;
  537. SLsmg_write_string(u.ch);
  538. }
  539. break;
  540. #endif
  541. #if defined(USE_NCURSES)
  542. case CACA_DRIVER_NCURSES:
  543. move(y, x);
  544. addstr(s);
  545. break;
  546. #endif
  547. #if defined(USE_CONIO)
  548. case CACA_DRIVER_CONIO:
  549. charbuf = conio_screen + 2 * (x + y * _caca_width);
  550. while(*s)
  551. {
  552. *charbuf++ = *s++;
  553. *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
  554. }
  555. break;
  556. #endif
  557. #if defined(USE_X11)
  558. case CACA_DRIVER_X11:
  559. break;
  560. #endif
  561. #if defined(USE_WIN32)
  562. case CACA_DRIVER_WIN32:
  563. break;
  564. #endif
  565. #if defined(USE_GL)
  566. case CACA_DRIVER_GL:
  567. break;
  568. #endif
  569. #if defined(USE_NULL)
  570. case CACA_DRIVER_NULL:
  571. break;
  572. #endif
  573. default:
  574. break;
  575. }
  576. }
  577. /** \brief Format a string.
  578. *
  579. * This function formats a string at the given coordinates, using the
  580. * default foreground and background values. The coordinates may be outside
  581. * the screen boundaries (eg. a negative Y coordinate) and the string will
  582. * be cropped accordingly if it is too long. The syntax of the format
  583. * string is the same as for the C printf() function.
  584. *
  585. * \param x X coordinate.
  586. * \param y Y coordinate.
  587. * \param format The format string to print.
  588. * \param ... Arguments to the format string.
  589. */
  590. void caca_printf(int x, int y, char const *format, ...)
  591. {
  592. char tmp[BUFSIZ];
  593. char *buf = tmp;
  594. va_list args;
  595. if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
  596. return;
  597. if(_caca_width - x + 1 > BUFSIZ)
  598. buf = malloc(_caca_width - x + 1);
  599. va_start(args, format);
  600. #if defined(HAVE_VSNPRINTF)
  601. vsnprintf(buf, _caca_width - x + 1, format, args);
  602. #else
  603. vsprintf(buf, format, args);
  604. #endif
  605. buf[_caca_width - x] = '\0';
  606. va_end(args);
  607. caca_putstr(x, y, buf);
  608. if(buf != tmp)
  609. free(buf);
  610. }
  611. /** \brief Get the screen.
  612. *
  613. * This function fills a byte array with the character values.
  614. */
  615. void caca_get_screen(char *buffer)
  616. {
  617. unsigned int x, y;
  618. for(y = 0; y < _caca_height; y++)
  619. {
  620. for(x = 0; x < _caca_width; x++)
  621. {
  622. *buffer++ = cache_attr[x + y * _caca_width];
  623. *buffer++ = cache_char[x + y * _caca_width];
  624. }
  625. }
  626. }
  627. /** \brief Clear the screen.
  628. *
  629. * This function clears the screen using a black background.
  630. */
  631. void caca_clear(void)
  632. {
  633. enum caca_color oldfg = caca_get_fg_color();
  634. enum caca_color oldbg = caca_get_bg_color();
  635. int y = _caca_height;
  636. caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
  637. /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
  638. while(y--)
  639. caca_putstr(0, y, _caca_empty_line);
  640. caca_set_color(oldfg, oldbg);
  641. }
  642. #if !defined(_DOXYGEN_SKIP_ME)
  643. int _caca_init_graphics(void)
  644. {
  645. #if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
  646. signal(SIGWINCH, sigwinch_handler);
  647. #endif
  648. #if defined(USE_SLANG)
  649. if(_caca_driver == CACA_DRIVER_SLANG)
  650. {
  651. slang_init_palette();
  652. /* Disable alt charset support so that we get a chance to have all
  653. * 256 colour pairs */
  654. SLtt_Has_Alt_Charset = 0;
  655. _caca_width = SLtt_Screen_Cols;
  656. _caca_height = SLtt_Screen_Rows;
  657. }
  658. else
  659. #endif
  660. #if defined(USE_NCURSES)
  661. if(_caca_driver == CACA_DRIVER_NCURSES)
  662. {
  663. static int curses_colors[] =
  664. {
  665. /* Standard curses colours */
  666. COLOR_BLACK,
  667. COLOR_BLUE,
  668. COLOR_GREEN,
  669. COLOR_CYAN,
  670. COLOR_RED,
  671. COLOR_MAGENTA,
  672. COLOR_YELLOW,
  673. COLOR_WHITE,
  674. /* Extra values for xterm-16color */
  675. COLOR_BLACK + 8,
  676. COLOR_BLUE + 8,
  677. COLOR_GREEN + 8,
  678. COLOR_CYAN + 8,
  679. COLOR_RED + 8,
  680. COLOR_MAGENTA + 8,
  681. COLOR_YELLOW + 8,
  682. COLOR_WHITE + 8
  683. };
  684. int fg, bg, max;
  685. /* Activate colour */
  686. start_color();
  687. /* If COLORS == 16, it means the terminal supports full bright colours
  688. * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
  689. * we can build 16*16 colour pairs.
  690. * If COLORS == 8, it means the terminal does not know about bright
  691. * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
  692. * and \e[5m). We can only build 8*8 colour pairs. */
  693. max = COLORS >= 16 ? 16 : 8;
  694. for(bg = 0; bg < max; bg++)
  695. for(fg = 0; fg < max; fg++)
  696. {
  697. /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
  698. * is light gray on black, since some terminals don't like
  699. * this colour pair to be redefined. */
  700. int col = ((max + 7 - fg) % max) + max * bg;
  701. init_pair(col, curses_colors[fg], curses_colors[bg]);
  702. ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
  703. if(max == 8)
  704. {
  705. /* Bright fg on simple bg */
  706. ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
  707. /* Simple fg on bright bg */
  708. ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
  709. | COLOR_PAIR(col);
  710. /* Bright fg on bright bg */
  711. ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
  712. | COLOR_PAIR(col);
  713. }
  714. }
  715. _caca_width = COLS;
  716. _caca_height = LINES;
  717. }
  718. else
  719. #endif
  720. #if defined(USE_CONIO)
  721. if(_caca_driver == CACA_DRIVER_CONIO)
  722. {
  723. gettextinfo(&conio_ti);
  724. conio_screen = malloc(2 * conio_ti.screenwidth
  725. * conio_ti.screenheight * sizeof(char));
  726. if(conio_screen == NULL)
  727. return -1;
  728. # if defined(SCREENUPDATE_IN_PC_H)
  729. ScreenRetrieve(conio_screen);
  730. # else
  731. /* FIXME */
  732. # endif
  733. _caca_width = conio_ti.screenwidth;
  734. _caca_height = conio_ti.screenheight;
  735. }
  736. else
  737. #endif
  738. #if defined(USE_X11)
  739. if(_caca_driver == CACA_DRIVER_X11)
  740. {
  741. static int x11_palette[] =
  742. {
  743. /* Standard curses colours */
  744. 0x0, 0x0, 0x0,
  745. 0x0, 0x0, 0x8000,
  746. 0x0, 0x8000, 0x0,
  747. 0x0, 0x8000, 0x8000,
  748. 0x8000, 0x0, 0x0,
  749. 0x8000, 0x0, 0x8000,
  750. 0x8000, 0x8000, 0x0,
  751. 0x8000, 0x8000, 0x8000,
  752. /* Extra values for xterm-16color */
  753. 0x4000, 0x4000, 0x4000,
  754. 0x4000, 0x4000, 0xffff,
  755. 0x4000, 0xffff, 0x4000,
  756. 0x4000, 0xffff, 0xffff,
  757. 0xffff, 0x4000, 0x4000,
  758. 0xffff, 0x4000, 0xffff,
  759. 0xffff, 0xffff, 0x4000,
  760. 0xffff, 0xffff, 0xffff,
  761. };
  762. Colormap colormap;
  763. XSetWindowAttributes x11_winattr;
  764. int (*old_error_handler)(Display *, XErrorEvent *);
  765. char const *font_name = "8x13bold";
  766. int i;
  767. if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
  768. sscanf(getenv("CACA_GEOMETRY"),
  769. "%ux%u", &_caca_width, &_caca_height);
  770. if(!_caca_width)
  771. _caca_width = 80;
  772. if(!_caca_height)
  773. _caca_height = 32;
  774. x11_dpy = XOpenDisplay(NULL);
  775. if(x11_dpy == NULL)
  776. return -1;
  777. if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
  778. font_name = getenv("CACA_FONT");
  779. /* Ignore font errors */
  780. old_error_handler = XSetErrorHandler(x11_error_handler);
  781. x11_font = XLoadFont(x11_dpy, font_name);
  782. if(!x11_font)
  783. {
  784. XCloseDisplay(x11_dpy);
  785. return -1;
  786. }
  787. x11_font_struct = XQueryFont(x11_dpy, x11_font);
  788. if(!x11_font_struct)
  789. {
  790. XUnloadFont(x11_dpy, x11_font);
  791. XCloseDisplay(x11_dpy);
  792. return -1;
  793. }
  794. /* Reset the default X11 error handler */
  795. XSetErrorHandler(old_error_handler);
  796. x11_font_width = x11_font_struct->max_bounds.width;
  797. x11_font_height = x11_font_struct->max_bounds.ascent
  798. + x11_font_struct->max_bounds.descent;
  799. x11_font_offset = x11_font_struct->max_bounds.descent;
  800. colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
  801. for(i = 0; i < 16; i++)
  802. {
  803. XColor color;
  804. color.red = x11_palette[i * 3];
  805. color.green = x11_palette[i * 3 + 1];
  806. color.blue = x11_palette[i * 3 + 2];
  807. XAllocColor(x11_dpy, colormap, &color);
  808. x11_colors[i] = color.pixel;
  809. }
  810. x11_winattr.backing_store = Always;
  811. x11_winattr.background_pixel = x11_colors[0];
  812. x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
  813. x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
  814. _caca_width * x11_font_width,
  815. _caca_height * x11_font_height,
  816. 0, 0, InputOutput, 0,
  817. CWBackingStore | CWBackPixel | CWEventMask,
  818. &x11_winattr);
  819. XStoreName(x11_dpy, x11_window, "caca for X");
  820. XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
  821. XMapWindow(x11_dpy, x11_window);
  822. x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
  823. XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
  824. XSetFont(x11_dpy, x11_gc, x11_font);
  825. for(;;)
  826. {
  827. XEvent event;
  828. XNextEvent(x11_dpy, &event);
  829. if (event.type == MapNotify)
  830. break;
  831. }
  832. /* Disable autorepeat */
  833. #if defined(HAVE_X11_XKBLIB_H)
  834. XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
  835. if(!x11_detect_autorepeat)
  836. XAutoRepeatOff(x11_dpy);
  837. #endif
  838. XSelectInput(x11_dpy, x11_window, x11_event_mask);
  839. XSync(x11_dpy, False);
  840. x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
  841. _caca_width * x11_font_width,
  842. _caca_height * x11_font_height,
  843. DefaultDepth(x11_dpy,
  844. DefaultScreen(x11_dpy)));
  845. x11_new_width = x11_new_height = 0;
  846. }
  847. else
  848. #endif
  849. #if defined(USE_WIN32)
  850. if(_caca_driver == CACA_DRIVER_WIN32)
  851. {
  852. CONSOLE_CURSOR_INFO cci;
  853. CONSOLE_SCREEN_BUFFER_INFO csbi;
  854. COORD size;
  855. win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  856. 0, NULL,
  857. CONSOLE_TEXTMODE_BUFFER, NULL);
  858. if(!win32_front || win32_front == INVALID_HANDLE_VALUE)
  859. return -1;
  860. win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  861. 0, NULL,
  862. CONSOLE_TEXTMODE_BUFFER, NULL);
  863. if(!win32_back || win32_back == INVALID_HANDLE_VALUE)
  864. return -1;
  865. if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
  866. return -1;
  867. /* Sample code to get the biggest possible window */
  868. //size = GetLargestConsoleWindowSize(win32_hout);
  869. _caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  870. _caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  871. size.X = _caca_width;
  872. size.Y = _caca_height;
  873. SetConsoleScreenBufferSize(win32_front, size);
  874. SetConsoleScreenBufferSize(win32_back, size);
  875. SetConsoleMode(win32_front, 0);
  876. SetConsoleMode(win32_back, 0);
  877. GetConsoleCursorInfo(win32_front, &cci);
  878. cci.dwSize = 0;
  879. cci.bVisible = FALSE;
  880. SetConsoleCursorInfo(win32_front, &cci);
  881. SetConsoleCursorInfo(win32_back, &cci);
  882. SetConsoleActiveScreenBuffer(win32_front);
  883. win32_buffer = malloc(_caca_width * _caca_height * sizeof(CHAR_INFO));
  884. if(win32_buffer == NULL)
  885. return -1;
  886. }
  887. else
  888. #endif
  889. #if defined(USE_GL)
  890. if(_caca_driver == CACA_DRIVER_GL)
  891. {
  892. int i;
  893. char *empty;
  894. if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
  895. sscanf(getenv("CACA_GEOMETRY"),
  896. "%ux%u", &_caca_width, &_caca_height);
  897. if(!_caca_width)
  898. _caca_width = 80;
  899. if(!_caca_height)
  900. _caca_height = 32;
  901. gl_font_width = 9;
  902. gl_font_height = 15;
  903. gl_width = _caca_width*gl_font_width;
  904. gl_height = _caca_height*gl_font_height;
  905. glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  906. glutInitWindowSize(gl_width, gl_height);
  907. gl_window = glutCreateWindow("caca for GL");
  908. gluOrtho2D(0,gl_width, gl_height, 0);
  909. glDisable(GL_CULL_FACE);
  910. glDisable(GL_DEPTH_TEST);
  911. glutKeyboardFunc(gl_handle_keyboard);
  912. glutSpecialFunc(gl_handle_special_key);
  913. glutReshapeFunc(gl_handle_reshape);
  914. glutMouseFunc(gl_handle_mouse);
  915. glutMotionFunc(gl_handle_mouse_motion);
  916. glutPassiveMotionFunc(gl_handle_mouse_motion);
  917. glLoadIdentity();
  918. glMatrixMode(GL_PROJECTION);
  919. glPushMatrix();
  920. glLoadIdentity();
  921. gluOrtho2D(0, gl_width, gl_height, 0);
  922. glMatrixMode(GL_MODELVIEW);
  923. glClear(GL_COLOR_BUFFER_BIT);
  924. empty = malloc(16*16*4);
  925. if(empty == NULL)
  926. return -1;
  927. memset(empty, 255, 16*16*4);
  928. glEnable(GL_TEXTURE_2D);
  929. for(i=0;i<94;i++)
  930. {
  931. glGenTextures(1,&id[i]);
  932. glBindTexture(GL_TEXTURE_2D, id[i]);
  933. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  934. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  935. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,
  936. 16,16,
  937. 0, GL_RGB, GL_UNSIGNED_BYTE, empty);
  938. }
  939. for(i=0;i<94;i++)
  940. {
  941. glDisable(GL_TEXTURE_2D);
  942. glClear(GL_COLOR_BUFFER_BIT);
  943. glColor3f(1,1,1);
  944. glRasterPos2f(0,15);
  945. glutBitmapCharacter(GLUT_BITMAP_9_BY_15,i+32);
  946. glEnable(GL_TEXTURE_2D);
  947. glBindTexture(GL_TEXTURE_2D,id[i]);
  948. glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, gl_height-16, 16,16, 0);
  949. glutMainLoopEvent();
  950. glutPostRedisplay();
  951. }
  952. }
  953. else
  954. #endif
  955. #if defined(USE_NULL)
  956. if(_caca_driver == CACA_DRIVER_NULL)
  957. {
  958. if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
  959. sscanf(getenv("CACA_GEOMETRY"),
  960. "%ux%u", &_caca_width, &_caca_height);
  961. if(!_caca_width)
  962. _caca_width = 80;
  963. if(!_caca_height)
  964. _caca_height = 32;
  965. }
  966. else
  967. #endif
  968. {
  969. /* Dummy */
  970. }
  971. cache_char = malloc(_caca_width * _caca_height * sizeof(uint8_t));
  972. if(cache_char == NULL)
  973. return -1;
  974. cache_attr = malloc(_caca_width * _caca_height * sizeof(uint8_t));
  975. if(cache_attr == NULL)
  976. {
  977. free(cache_char);
  978. return -1;
  979. }
  980. memset(cache_char, 0, _caca_width * _caca_height * sizeof(uint8_t));
  981. memset(cache_attr, 0, _caca_width * _caca_height * sizeof(uint8_t));
  982. _caca_empty_line = malloc(_caca_width + 1);
  983. memset(_caca_empty_line, ' ', _caca_width);
  984. _caca_empty_line[_caca_width] = '\0';
  985. _caca_scratch_line = malloc(_caca_width + 1);
  986. _caca_delay = 0;
  987. _caca_rendertime = 0;
  988. return 0;
  989. }
  990. int _caca_end_graphics(void)
  991. {
  992. free(cache_char);
  993. free(cache_attr);
  994. #if defined(USE_SLANG)
  995. /* Nothing to do */
  996. #endif
  997. #if defined(USE_NCURSES)
  998. /* Nothing to do */
  999. #endif
  1000. #if defined(USE_CONIO)
  1001. if(_caca_driver == CACA_DRIVER_CONIO)
  1002. {
  1003. free(conio_screen);
  1004. }
  1005. else
  1006. #endif
  1007. #if defined(USE_X11)
  1008. if(_caca_driver == CACA_DRIVER_X11)
  1009. {
  1010. XSync(x11_dpy, False);
  1011. #if defined(HAVE_X11_XKBLIB_H)
  1012. if(!x11_detect_autorepeat)
  1013. XAutoRepeatOn(x11_dpy);
  1014. #endif
  1015. XFreePixmap(x11_dpy, x11_pixmap);
  1016. XFreeFont(x11_dpy, x11_font_struct);
  1017. XFreeGC(x11_dpy, x11_gc);
  1018. XUnmapWindow(x11_dpy, x11_window);
  1019. XDestroyWindow(x11_dpy, x11_window);
  1020. XCloseDisplay(x11_dpy);
  1021. }
  1022. else
  1023. #endif
  1024. #if defined(USE_WIN32)
  1025. if(_caca_driver == CACA_DRIVER_WIN32)
  1026. {
  1027. SetConsoleActiveScreenBuffer(win32_hout);
  1028. CloseHandle(win32_back);
  1029. CloseHandle(win32_front);
  1030. }
  1031. else
  1032. #endif
  1033. #if defined(USE_GL)
  1034. if(_caca_driver == CACA_DRIVER_GL)
  1035. {
  1036. glutDestroyWindow(gl_window);
  1037. }
  1038. else
  1039. #endif
  1040. #if defined(USE_NULL)
  1041. if(_caca_driver == CACA_DRIVER_NULL)
  1042. {
  1043. /* I'm bored to write 'nothing to do'.
  1044. * So I don't.
  1045. */
  1046. }
  1047. else
  1048. #endif
  1049. {
  1050. /* Dummy */
  1051. }
  1052. free(_caca_empty_line);
  1053. return 0;
  1054. }
  1055. #endif /* _DOXYGEN_SKIP_ME */
  1056. /** \brief Set the window title.
  1057. *
  1058. * If libcaca runs in a window, try to change its title. This works with
  1059. * the X11 and Win32 drivers.
  1060. *
  1061. * \param title The desired window title.
  1062. * \return 0 upon success, a non-zero value if an error occurs.
  1063. */
  1064. int caca_set_window_title(char const *title)
  1065. {
  1066. #if defined(USE_X11)
  1067. if(_caca_driver == CACA_DRIVER_X11)
  1068. {
  1069. XStoreName(x11_dpy, x11_window, title);
  1070. }
  1071. else
  1072. #endif
  1073. #if defined(USE_WIN32)
  1074. if(_caca_driver == CACA_DRIVER_WIN32)
  1075. {
  1076. SetConsoleTitle(title);
  1077. }
  1078. else
  1079. #endif
  1080. #if defined(USE_GL)
  1081. if(_caca_driver == CACA_DRIVER_GL)
  1082. {
  1083. glutSetWindowTitle(title);
  1084. }
  1085. else
  1086. #endif
  1087. {
  1088. /* Not supported */
  1089. return -1;
  1090. }
  1091. return 0;
  1092. }
  1093. /** \brief Get the window width.
  1094. *
  1095. * If libcaca runs in a window, get the usable window width. This value can
  1096. * be used for aspect ratio calculation. If libcaca does not run in a window
  1097. * or if there is no way to know the font size, assume a 6x10 font is being
  1098. * used. Note that the units are not necessarily pixels.
  1099. *
  1100. * \return The window width.
  1101. */
  1102. unsigned int caca_get_window_width(void)
  1103. {
  1104. #if defined(USE_X11)
  1105. if(_caca_driver == CACA_DRIVER_X11)
  1106. {
  1107. return _caca_width * x11_font_width;
  1108. }
  1109. else
  1110. #endif
  1111. #if defined(USE_WIN32)
  1112. if(_caca_driver == CACA_DRIVER_WIN32)
  1113. {
  1114. /* FIXME */
  1115. }
  1116. else
  1117. #endif
  1118. #if defined(USE_GL)
  1119. if(_caca_driver == CACA_DRIVER_GL)
  1120. {
  1121. return gl_width;
  1122. }
  1123. else
  1124. #endif
  1125. {
  1126. /* Dummy */
  1127. }
  1128. /* Fallback to a 6x10 font */
  1129. return _caca_width * 6;
  1130. }
  1131. /** \brief Get the window height.
  1132. *
  1133. * If libcaca runs in a window, get the usable window height. This value can
  1134. * be used for aspect ratio calculation. If libcaca does not run in a window
  1135. * or if there is no way to know the font size, assume a 6x10 font is being
  1136. * used. Note that the units are not necessarily pixels.
  1137. *
  1138. * \return The window height.
  1139. */
  1140. unsigned int caca_get_window_height(void)
  1141. {
  1142. #if defined(USE_X11)
  1143. if(_caca_driver == CACA_DRIVER_X11)
  1144. {
  1145. return _caca_height * x11_font_height;
  1146. }
  1147. else
  1148. #endif
  1149. #if defined(USE_WIN32)
  1150. if(_caca_driver == CACA_DRIVER_WIN32)
  1151. {
  1152. /* FIXME */
  1153. }
  1154. else
  1155. #endif
  1156. #if defined(USE_GL)
  1157. if(_caca_driver == CACA_DRIVER_GL)
  1158. {
  1159. return gl_height;
  1160. }
  1161. else
  1162. #endif
  1163. {
  1164. /* Dummy */
  1165. }
  1166. /* Fallback to a 6x10 font */
  1167. return _caca_height * 10;
  1168. }
  1169. /** \brief Set the refresh delay.
  1170. *
  1171. * This function sets the refresh delay in microseconds. The refresh delay
  1172. * is used by caca_refresh() to achieve constant framerate. See the
  1173. * caca_refresh() documentation for more details.
  1174. *
  1175. * If the argument is zero, constant framerate is disabled. This is the
  1176. * default behaviour.
  1177. *
  1178. * \param usec The refresh delay in microseconds.
  1179. */
  1180. void caca_set_delay(unsigned int usec)
  1181. {
  1182. _caca_delay = usec;
  1183. }
  1184. /** \brief Get the average rendering time.
  1185. *
  1186. * This function returns the average rendering time, which is the average
  1187. * measured time between two caca_refresh() calls, in microseconds. If
  1188. * constant framerate was activated by calling caca_set_delay(), the average
  1189. * rendering time will not be considerably shorter than the requested delay
  1190. * even if the real rendering time was shorter.
  1191. *
  1192. * \return The render time in microseconds.
  1193. */
  1194. unsigned int caca_get_rendertime(void)
  1195. {
  1196. return _caca_rendertime;
  1197. }
  1198. /** \brief Flush pending changes and redraw the screen.
  1199. *
  1200. * This function flushes all graphical operations and prints them to the
  1201. * screen. Nothing will show on the screen until caca_refresh() is
  1202. * called.
  1203. *
  1204. * If caca_set_delay() was called with a non-zero value, caca_refresh()
  1205. * will use that value to achieve constant framerate: if two consecutive
  1206. * calls to caca_refresh() are within a time range shorter than the value
  1207. * set with caca_set_delay(), the second call will wait a bit before
  1208. * performing the screen refresh.
  1209. */
  1210. void caca_refresh(void)
  1211. {
  1212. #if !defined(_DOXYGEN_SKIP_ME)
  1213. #define IDLE_USEC 10000
  1214. #endif
  1215. static struct caca_timer timer = CACA_TIMER_INITIALIZER;
  1216. static int lastticks = 0;
  1217. int ticks = lastticks + _caca_getticks(&timer);
  1218. #if defined(USE_SLANG)
  1219. if(_caca_driver == CACA_DRIVER_SLANG)
  1220. {
  1221. SLsmg_refresh();
  1222. }
  1223. else
  1224. #endif
  1225. #if defined(USE_NCURSES)
  1226. if(_caca_driver == CACA_DRIVER_NCURSES)
  1227. {
  1228. refresh();
  1229. }
  1230. else
  1231. #endif
  1232. #if defined(USE_CONIO)
  1233. if(_caca_driver == CACA_DRIVER_CONIO)
  1234. {
  1235. # if defined(SCREENUPDATE_IN_PC_H)
  1236. ScreenUpdate(conio_screen);
  1237. # else
  1238. /* FIXME */
  1239. # endif
  1240. }
  1241. else
  1242. #endif
  1243. #if defined(USE_X11)
  1244. if(_caca_driver == CACA_DRIVER_X11)
  1245. {
  1246. unsigned int x, y, len;
  1247. /* First draw the background colours. Splitting the process in two
  1248. * loops like this is actually slightly faster. */
  1249. for(y = 0; y < _caca_height; y++)
  1250. {
  1251. for(x = 0; x < _caca_width; x += len)
  1252. {
  1253. uint8_t *attr = cache_attr + x + y * _caca_width;
  1254. len = 1;
  1255. while(x + len < _caca_width
  1256. && (attr[len] >> 4) == (attr[0] >> 4))
  1257. len++;
  1258. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
  1259. XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
  1260. x * x11_font_width, y * x11_font_height,
  1261. len * x11_font_width, x11_font_height);
  1262. }
  1263. }
  1264. /* Then print the foreground characters */
  1265. for(y = 0; y < _caca_height; y++)
  1266. {
  1267. for(x = 0; x < _caca_width; x += len)
  1268. {
  1269. uint8_t *attr = cache_attr + x + y * _caca_width;
  1270. len = 1;
  1271. /* Skip spaces */
  1272. if(cache_char[x + y * _caca_width] == ' ')
  1273. continue;
  1274. while(x + len < _caca_width
  1275. && (attr[len] & 0xf) == (attr[0] & 0xf))
  1276. len++;
  1277. XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
  1278. XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
  1279. (y + 1) * x11_font_height - x11_font_offset,
  1280. cache_char + x + y * _caca_width, len);
  1281. }
  1282. }
  1283. XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
  1284. _caca_width * x11_font_width, _caca_height * x11_font_height,
  1285. 0, 0);
  1286. XFlush(x11_dpy);
  1287. }
  1288. else
  1289. #endif
  1290. #if defined(USE_WIN32)
  1291. if(_caca_driver == CACA_DRIVER_WIN32)
  1292. {
  1293. COORD size, pos;
  1294. SMALL_RECT rect;
  1295. unsigned int i;
  1296. /* Render everything to our back buffer */
  1297. for(i = 0; i < _caca_width * _caca_height; i++)
  1298. {
  1299. win32_buffer[i].Char.AsciiChar = cache_char[i];
  1300. win32_buffer[i].Attributes = win32_fg_palette[cache_attr[i] & 0xf]
  1301. | win32_bg_palette[cache_attr[i] >> 4];
  1302. }
  1303. /* Blit the back buffer to the front buffer */
  1304. size.X = _caca_width;
  1305. size.Y = _caca_height;
  1306. pos.X = pos.Y = 0;
  1307. rect.Left = rect.Top = 0;
  1308. rect.Right = _caca_width - 1;
  1309. rect.Bottom = _caca_height - 1;
  1310. WriteConsoleOutput(win32_front, win32_buffer, size, pos, &rect);
  1311. }
  1312. else
  1313. #endif
  1314. #if defined(USE_GL)
  1315. if(_caca_driver == CACA_DRIVER_GL)
  1316. {
  1317. unsigned int x, y, offsetx, offsety;
  1318. glClear(GL_COLOR_BUFFER_BIT);
  1319. offsety=0;
  1320. for(y=0;y<gl_height;y+=gl_font_height)
  1321. {
  1322. offsetx = 0;
  1323. for(x=0;x<gl_width;x+=gl_font_width)
  1324. {
  1325. uint8_t *attr = cache_attr + offsetx + offsety * _caca_width;
  1326. int offset;
  1327. float br, bg, bb;
  1328. offset = attr[0]>>4;
  1329. br = ((gl_bg_palette[offset]&0x00FF0000)>>16)/255.0f;
  1330. bg = ((gl_bg_palette[offset]&0x0000FF00)>>8)/255.0f;
  1331. bb = ((gl_bg_palette[offset]&0x000000FF))/255.0f;
  1332. glDisable(GL_TEXTURE_2D);
  1333. glColor3f(br, bg, bb);
  1334. glBegin(GL_QUADS);
  1335. glVertex2f(x,y);
  1336. glVertex2f(x+gl_font_width,y);
  1337. glVertex2f(x+gl_font_width,y+gl_font_height);
  1338. glVertex2f(x,y+gl_font_height);
  1339. glEnd();
  1340. offsetx++;
  1341. }
  1342. offsety++;
  1343. }
  1344. /* 2nd pass, avoids changing render state too much */
  1345. glEnable(GL_BLEND);
  1346. glEnable(GL_TEXTURE_2D);
  1347. glBlendFunc(GL_ONE, GL_ONE);
  1348. offsety=0;
  1349. for(y=0;y<gl_height;y+=gl_font_height)
  1350. {
  1351. offsetx = 0;
  1352. for(x=0;x<gl_width;x+=gl_font_width)
  1353. {
  1354. uint8_t *attr = cache_attr + offsetx + offsety * _caca_width;
  1355. unsigned char *chr = cache_char + offsetx + offsety * _caca_width;
  1356. float fr, fg, fb;
  1357. fr = ((gl_bg_palette[attr[0] & 0xf]&0x00FF0000)>>16)/255.0f;
  1358. fg = ((gl_bg_palette[attr[0] & 0xf]&0x0000FF00)>>8)/255.0f;
  1359. fb = ((gl_bg_palette[attr[0] & 0xf]&0x000000FF))/255.0f;
  1360. if(chr[0] != ' ')
  1361. {
  1362. glBindTexture(GL_TEXTURE_2D, id[chr[0]-32]);
  1363. glColor3f(fr, fg, fb);
  1364. glBegin(GL_QUADS);
  1365. glTexCoord2f(0,1);
  1366. glVertex2f(x,y);
  1367. glTexCoord2f(0.5,1);
  1368. glVertex2f(x+gl_font_width,y);
  1369. glTexCoord2f(0.5,0);
  1370. glVertex2f(x+gl_font_width,y+gl_font_height);
  1371. glTexCoord2f(0,0);
  1372. glVertex2f(x,y+gl_font_height);
  1373. glEnd();
  1374. }
  1375. offsetx++;
  1376. }
  1377. offsety++;
  1378. }
  1379. glDisable(GL_BLEND);
  1380. glDisable(GL_TEXTURE_2D);
  1381. glutMainLoopEvent();
  1382. glutSwapBuffers();
  1383. glutPostRedisplay();
  1384. }
  1385. else
  1386. #endif
  1387. #if defined(USE_NULL)
  1388. if(_caca_driver == CACA_DRIVER_NULL)
  1389. {
  1390. /* Do I told you about my cat ? */
  1391. }
  1392. #endif
  1393. {
  1394. /* Dummy */
  1395. }
  1396. if(_caca_resize)
  1397. {
  1398. _caca_resize = 0;
  1399. caca_handle_resize();
  1400. }
  1401. /* Wait until _caca_delay + time of last call */
  1402. ticks += _caca_getticks(&timer);
  1403. for(ticks += _caca_getticks(&timer);
  1404. ticks + IDLE_USEC < (int)_caca_delay;
  1405. ticks += _caca_getticks(&timer))
  1406. {
  1407. _caca_sleep(IDLE_USEC);
  1408. }
  1409. /* Update the sliding mean of the render time */
  1410. _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
  1411. lastticks = ticks - _caca_delay;
  1412. /* If we drifted too much, it's bad, bad, bad. */
  1413. if(lastticks > (int)_caca_delay)
  1414. lastticks = 0;
  1415. }
  1416. /*
  1417. * XXX: following functions are loca
  1418. */
  1419. static void caca_handle_resize(void)
  1420. {
  1421. unsigned int old_width = _caca_width;
  1422. unsigned int old_height = _caca_height;
  1423. #if defined(USE_SLANG)
  1424. if(_caca_driver == CACA_DRIVER_SLANG)
  1425. {
  1426. SLtt_get_screen_size();
  1427. _caca_width = SLtt_Screen_Cols;
  1428. _caca_height = SLtt_Screen_Rows;
  1429. if(_caca_width != old_width || _caca_height != old_height)
  1430. SLsmg_reinit_smg();
  1431. }
  1432. else
  1433. #endif
  1434. #if defined(USE_NCURSES)
  1435. if(_caca_driver == CACA_DRIVER_NCURSES)
  1436. {
  1437. struct winsize size;
  1438. if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
  1439. {
  1440. _caca_width = size.ws_col;
  1441. _caca_height = size.ws_row;
  1442. #if defined(HAVE_RESIZE_TERM)
  1443. resize_term(_caca_height, _caca_width);
  1444. #else
  1445. resizeterm(_caca_height, _caca_width);
  1446. #endif
  1447. wrefresh(curscr);
  1448. }
  1449. }
  1450. else
  1451. #endif
  1452. #if defined(USE_CONIO)
  1453. if(_caca_driver == CACA_DRIVER_CONIO)
  1454. {
  1455. }
  1456. else
  1457. #endif
  1458. #if defined(USE_X11)
  1459. if(_caca_driver == CACA_DRIVER_X11)
  1460. {
  1461. Pixmap new_pixmap;
  1462. _caca_width = x11_new_width;
  1463. _caca_height = x11_new_height;
  1464. new_pixmap = XCreatePixmap(x11_dpy, x11_window,
  1465. _caca_width * x11_font_width,
  1466. _caca_height * x11_font_height,
  1467. DefaultDepth(x11_dpy,
  1468. DefaultScreen(x11_dpy)));
  1469. XCopyArea(x11_dpy, x11_pixmap, new_pixmap, x11_gc, 0, 0,
  1470. old_width * x11_font_width, old_height * x11_font_height,
  1471. 0, 0);
  1472. XFreePixmap(x11_dpy, x11_pixmap);
  1473. x11_pixmap = new_pixmap;
  1474. }
  1475. else
  1476. #endif
  1477. #if defined(USE_WIN32)
  1478. if(_caca_driver == CACA_DRIVER_WIN32)
  1479. {
  1480. }
  1481. else
  1482. #endif
  1483. #if defined(USE_GL)
  1484. if(_caca_driver == CACA_DRIVER_GL)
  1485. {
  1486. gl_width = gl_new_width;
  1487. gl_height = gl_new_height;
  1488. _caca_width = gl_width/gl_font_width;
  1489. _caca_height = (gl_height/gl_font_height)+1;
  1490. glMatrixMode(GL_PROJECTION);
  1491. glPushMatrix();
  1492. glLoadIdentity();
  1493. glViewport(0,0,gl_width, gl_height);
  1494. gluOrtho2D(0, gl_width, gl_height, 0);
  1495. glMatrixMode(GL_MODELVIEW);
  1496. }
  1497. else
  1498. #endif
  1499. #if defined(USE_NULL)
  1500. if(_caca_driver == CACA_DRIVER_NULL)
  1501. {
  1502. /* \_o< pOaK */
  1503. /* By the way, we should never reach this,
  1504. * as events are not handled
  1505. */
  1506. }
  1507. #endif
  1508. {
  1509. /* Dummy */
  1510. }
  1511. if(_caca_width != old_width || _caca_height != old_height)
  1512. {
  1513. free(cache_char);
  1514. free(cache_attr);
  1515. cache_char = malloc(_caca_width * _caca_height * sizeof(uint8_t));
  1516. memset(cache_char, 0, _caca_width * _caca_height * sizeof(uint8_t));
  1517. cache_attr = malloc(_caca_width * _caca_height * sizeof(uint8_t));
  1518. memset(cache_attr, 0, _caca_width * _caca_height * sizeof(uint8_t));
  1519. }
  1520. if(_caca_width != old_width)
  1521. {
  1522. free(_caca_empty_line);
  1523. _caca_empty_line = malloc(_caca_width + 1);
  1524. memset(_caca_empty_line, ' ', _caca_width);
  1525. _caca_empty_line[_caca_width] = '\0';
  1526. free(_caca_scratch_line);
  1527. _caca_scratch_line = malloc(_caca_width + 1);
  1528. }
  1529. }
  1530. #if defined(USE_SLANG)
  1531. static void slang_init_palette(void)
  1532. {
  1533. /* See SLang ref., 5.4.4. */
  1534. static char *slang_colors[16] =
  1535. {
  1536. /* Standard colours */
  1537. "black",
  1538. "blue",
  1539. "green",
  1540. "cyan",
  1541. "red",
  1542. "magenta",
  1543. "brown",
  1544. "lightgray",
  1545. /* Bright colours */
  1546. "gray",
  1547. "brightblue",
  1548. "brightgreen",
  1549. "brightcyan",
  1550. "brightred",
  1551. "brightmagenta",
  1552. "yellow",
  1553. "white",
  1554. };
  1555. #if defined(OPTIMISE_SLANG_PALETTE)
  1556. int i;
  1557. for(i = 0; i < 16 * 16; i++)
  1558. SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
  1559. slang_colors[slang_palette[i * 2 + 1]]);
  1560. #else
  1561. int fg, bg;
  1562. for(bg = 0; bg < 16; bg++)
  1563. for(fg = 0; fg < 16; fg++)
  1564. {
  1565. int i = fg + 16 * bg;
  1566. SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
  1567. }
  1568. #endif
  1569. }
  1570. #endif /* USE_SLANG */
  1571. #if defined(USE_X11)
  1572. static int x11_error_handler(Display *dpy, XErrorEvent *event)
  1573. {
  1574. /* Ignore the error */
  1575. return 0;
  1576. }
  1577. #endif
  1578. #if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
  1579. static RETSIGTYPE sigwinch_handler(int sig)
  1580. {
  1581. _caca_resize_event = 1;
  1582. signal(SIGWINCH, sigwinch_handler);;
  1583. }
  1584. #endif
  1585. /* Exporters */
  1586. /* HTML */
  1587. /** \brief Generate HTML representation of current image.
  1588. *
  1589. * This function generates and returns the HTML representation of
  1590. * the current image.
  1591. */
  1592. char* caca_get_html(void)
  1593. {
  1594. static int const palette[] =
  1595. {
  1596. 0x000, 0x008, 0x080, 0x088, 0x800, 0x808, 0x880, 0x888,
  1597. 0x444, 0x44f, 0x4f4, 0x4ff, 0xf44, 0xf4f, 0xff4, 0xfff,
  1598. };
  1599. char *buffer, *cur;
  1600. unsigned int x, y, len;
  1601. /* 13000 -> css palette
  1602. * 40 -> max size used for a pixel (plus 10, never know)*/
  1603. /* FIXME: Check this value */
  1604. buffer = malloc((13000 + ((_caca_width*_caca_height) * 40)) * sizeof(char));
  1605. cur = buffer;
  1606. /* HTML header */
  1607. cur += sprintf(cur, "<html>\n<head>\n<title>Generated by libcaca %s</title>\n", VERSION);
  1608. /* CSS */
  1609. cur += sprintf(cur, "<style>\n");
  1610. cur += sprintf(cur, ".caca { font-family: monospace, fixed; font-weight: bold; }");
  1611. for(x = 0; x < 0x100; x++)
  1612. {
  1613. cur += sprintf(cur, ".b%02x { color:#%03x; background-color:#%03x; }\n",
  1614. x, palette[x & 0xf ], palette[x >> 4]);
  1615. }
  1616. cur += sprintf(cur, "</style>\n</head>\n<body>\n");
  1617. cur += sprintf(cur, "<div cellpadding='0' cellspacing='0' style='%s'>\n",
  1618. "font-family: monospace, fixed; font-weight: bold;");
  1619. for(y = 0; y < _caca_height; y++)
  1620. {
  1621. uint8_t *lineattr = cache_attr + y * _caca_width;
  1622. uint8_t *linechar = cache_char + y * _caca_width;
  1623. for(x = 0; x < _caca_width; x += len)
  1624. {
  1625. cur += sprintf(cur, "<span class='b%02x'>", lineattr[x]);
  1626. for(len = 0;
  1627. x + len < _caca_width && lineattr[x + len] == lineattr[x];
  1628. len++)
  1629. {
  1630. if(linechar[x + len] == ' ')
  1631. cur += sprintf(cur, "&nbsp;");
  1632. else
  1633. cur += sprintf(cur, "%c", linechar[x + len]);
  1634. }
  1635. cur += sprintf(cur, "</span>");
  1636. }
  1637. /* New line */
  1638. cur += sprintf(cur, "<br />\n");
  1639. }
  1640. cur += sprintf(cur, "</div></body></html>\n");
  1641. /* Crop to really used size */
  1642. buffer = realloc(buffer, (strlen(buffer) + 1) * sizeof(char));
  1643. return buffer;
  1644. }
  1645. /** \brief Generate HTML3 representation of current image.
  1646. *
  1647. * This function generates and returns the HTML3 representation of
  1648. * the current image. It is way bigger than caca_get_html(), but
  1649. * permits viewing in old browsers (or limited ones such as links)
  1650. * Won't work under gecko (mozilla rendering engine) unless you set
  1651. * a correct header.
  1652. */
  1653. char* caca_get_html3(void)
  1654. {
  1655. static int const palette[] =
  1656. {
  1657. 0x000000, 0x000088, 0x008800, 0x008888,
  1658. 0x880000, 0x880088, 0x888800, 0x888888,
  1659. 0x444444, 0x4444ff, 0x44ff44, 0x44ffff,
  1660. 0xff4444, 0xff44ff, 0xffff44, 0xffffff,
  1661. };
  1662. char *buffer, *cur;
  1663. unsigned int x, y, len;
  1664. /* 13000 -> css palette
  1665. * 40 -> max size used for a pixel (plus 10, never know) */
  1666. buffer = malloc((13000 + ((_caca_width*_caca_height)*40))*sizeof(char));
  1667. cur = buffer;
  1668. /* Table */
  1669. cur += sprintf(cur, "<table cols='%d' cellpadding='0' cellspacing='0'>\n",
  1670. _caca_height);
  1671. for(y = 0; y < _caca_height; y++)
  1672. {
  1673. uint8_t *lineattr = cache_attr + y * _caca_width;
  1674. uint8_t *linechar = cache_char + y * _caca_width;
  1675. cur += sprintf(cur, "<tr>");
  1676. for(x = 0; x < _caca_width; x += len)
  1677. {
  1678. int i;
  1679. /* Use colspan option to factorize cells with same attributes
  1680. * (see below) */
  1681. len = 1;
  1682. while(x + len < _caca_width && lineattr[x + len] == lineattr[x])
  1683. len++;
  1684. cur += sprintf(cur, "<td bgcolor=#%06x", palette[lineattr[x] >> 4]);
  1685. if(len > 1)
  1686. cur += sprintf(cur, " colspan=%d", len);
  1687. cur += sprintf(cur, "><font color=#%06x>",
  1688. palette[lineattr[x] & 0x0f]);
  1689. for(i = 0; i < len; i++)
  1690. {
  1691. if(linechar[x + i] == ' ')
  1692. cur += sprintf(cur, "&nbsp;");
  1693. else
  1694. cur += sprintf(cur, "%c", linechar[x + i]);
  1695. }
  1696. cur += sprintf(cur, "</font></td>");
  1697. }
  1698. cur += sprintf(cur, "</tr>\n");
  1699. }
  1700. /* Footer */
  1701. cur += sprintf(cur, "</table>\n");
  1702. /* Crop to really used size */
  1703. buffer = realloc(buffer, (strlen(buffer) + 1) * sizeof(char));
  1704. return buffer;
  1705. }
  1706. /** \brief Generate IRC representation of current image.
  1707. *
  1708. * This function generates and returns an IRC representation of
  1709. * the current image.
  1710. */
  1711. char* caca_get_irc(void)
  1712. {
  1713. static int const palette[] =
  1714. {
  1715. 1, 2, 3, 10, 5, 6, 7, 15, /* Dark */
  1716. 14, 12, 9, 11, 4, 13, 8, 0, /* Light */
  1717. };
  1718. char *buffer, *cur;
  1719. unsigned int x, y;
  1720. /* 11 bytes assumed for max length per pixel. Worst case scenario:
  1721. * ^Cxx,yy 6 bytes
  1722. * ^B^B 2 bytes
  1723. * c 1 byte
  1724. * \r\n 2 bytes
  1725. * In real life, the average bytes per pixel value will be around 5.
  1726. */
  1727. buffer = malloc((2 + (_caca_width * _caca_height * 11)) * sizeof(char));
  1728. cur = buffer;
  1729. *cur++ = '\x0f';
  1730. for(y = 0; y < _caca_height; y++)
  1731. {
  1732. uint8_t *lineattr = cache_attr + y * _caca_width;
  1733. uint8_t *linechar = cache_char + y * _caca_width;
  1734. uint8_t prevfg = -1;
  1735. uint8_t prevbg = -1;
  1736. for(x = 0; x < _caca_width; x++)
  1737. {
  1738. uint8_t fg = palette[lineattr[x] & 0x0f];
  1739. uint8_t bg = palette[lineattr[x] >> 4];
  1740. uint8_t c = linechar[x];
  1741. if(bg == prevbg)
  1742. {
  1743. if(fg == prevfg)
  1744. ; /* Same fg/bg, do nothing */
  1745. else if(c == ' ')
  1746. fg = prevfg; /* Hackety hack */
  1747. else
  1748. {
  1749. cur += sprintf(cur, "\x03%d", fg);
  1750. if(c >= '0' && c <= '9')
  1751. cur += sprintf(cur, "\x02\x02");
  1752. }
  1753. }
  1754. else
  1755. {
  1756. if(fg == prevfg)
  1757. cur += sprintf(cur, "\x03,%d", bg);
  1758. else
  1759. cur += sprintf(cur, "\x03%d,%d", fg, bg);
  1760. if(c >= '0' && c <= '9')
  1761. cur += sprintf(cur, "\x02\x02");
  1762. }
  1763. *cur++ = c;
  1764. prevfg = fg;
  1765. prevbg = bg;
  1766. }
  1767. *cur++ = '\r';
  1768. *cur++ = '\n';
  1769. }
  1770. *cur++ = '\x0f';
  1771. /* Crop to really used size */
  1772. buffer = realloc(buffer, (strlen(buffer) + 1) * sizeof(char));
  1773. return buffer;
  1774. }