source: libcaca/trunk/src/graphics.c @ 519

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