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

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