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

Last change on this file since 486 was 486, checked in by Jean-Yves Lamoureux, 17 years ago

GL driver : Updated documentation and added support for CACA_GEOMETRY

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