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

Last change on this file since 336 was 336, checked in by Sam Hocevar, 16 years ago
  • src/caca.c src/graphics.c src/io.c src/time.c: + Native win32 port.
  • Property svn:keywords set to Id
File size: 34.6 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22/** \file graphics.c
23 *  \version \$Id: graphics.c 336 2004-01-11 05:09:02Z sam $
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#   include <curses.h>
41#endif
42#if defined(USE_CONIO)
43#   include <conio.h>
44#   if defined(SCREENUPDATE_IN_PC_H)
45#       include <pc.h>
46#   endif
47#endif
48#if defined(USE_X11)
49#   include <X11/Xlib.h>
50#   if defined(HAVE_X11_XKBLIB_H)
51#       include <X11/XKBlib.h>
52#   endif
53#endif
54#if defined(USE_WIN32)
55#   include <windows.h>
56#endif
57
58#if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
59#   include <inttypes.h>
60#else
61typedef unsigned char uint8_t;
62#endif
63
64#include <stdio.h> /* BUFSIZ */
65#include <string.h>
66#include <stdlib.h>
67#include <unistd.h>
68#include <stdarg.h>
69
70#include "caca.h"
71#include "caca_internals.h"
72
73/*
74 * Global variables
75 */
76#if !defined(_DOXYGEN_SKIP_ME)
77unsigned int _caca_width = 0;
78unsigned int _caca_height = 0;
79#endif
80
81/*
82 * Local variables
83 */
84#if defined(USE_NCURSES)
85static int ncurses_attr[16*16];
86#endif
87
88#if defined(USE_SLANG)
89/* Tables generated by test/optipal.c */
90static int const slang_palette[2*16*16] =
91{
92     1,  0,   2,  0,   3,  0,   4,  0,   5,  0,   6,  0,   7,  0,   8,  0,
93     9,  0,  10,  0,  11,  0,  12,  0,  13,  0,  14,  0,  15,  0,   0,  8,
94     8,  7,   7,  8,  15,  7,   7, 15,  15,  9,   9, 15,   1,  9,   9,  1,
95     7,  9,   9,  7,   8,  1,   1,  8,   0,  1,  15, 10,  10, 15,   2, 10,
96    10,  2,   7, 10,  10,  7,   8,  2,   2,  8,   0,  2,  15, 11,  11, 15,
97     3, 11,  11,  3,   7, 11,  11,  7,   8,  3,   3,  8,   0,  3,  15, 12,
98    12, 15,   4, 12,  12,  4,   7, 12,  12,  7,   8,  4,   4,  8,   0,  4,
99    15, 13,  13, 15,   5, 13,  13,  5,   7, 13,  13,  7,   8,  5,   5,  8,
100     0,  5,  15, 14,  14, 15,   6, 14,  14,  6,   7, 14,  14,  7,   8,  6,
101     6,  8,   0,  6,   4,  6,   6,  4,  12, 14,  14, 12,   6,  2,   2,  6,
102    14, 10,  10, 14,   2,  3,   3,  2,  10, 11,  11, 10,   3,  1,   1,  3,
103    11,  9,   9, 11,   1,  5,   5,  1,   9, 13,  13,  9,   5,  4,   4,  5,
104    13, 12,  12, 13,   4, 14,   6, 12,  12,  6,  14,  4,   6, 10,   2, 14,
105    14,  2,  10,  6,   2, 11,   3, 10,  10,  3,  11,  2,   3,  9,   1, 11,
106    11,  1,   9,  3,   1, 13,   5,  9,   9,  5,  13,  1,   5, 12,   4, 13,
107    13,  4,  12,  5,   0,  7,   0, 15,  15,  8,   8, 15,  15,  1,   7,  1,
108     1,  6,   2,  5,   3,  4,   4,  3,   5,  2,   6,  1,   0,  0,   1,  1,
109     9,  6,  10,  5,  11,  4,  12,  3,  13,  2,  14,  1,   2,  2,   3,  3,
110     4,  4,   5,  5,   6,  6,   7,  7,  14,  9,   1, 15,   8,  9,   8,  8,
111     9,  9,   1,  7,   0,  9,   9,  8,   6,  9,  13, 10,   2, 15,   8, 10,
112     7,  2,  15,  2,   2,  7,   0, 10,  10,  8,   5, 10,  12, 11,   3, 15,
113     8, 11,   7,  3,  15,  3,   3,  7,   0, 11,  11,  8,   4, 11,  11, 12,
114     4, 15,   8, 12,   7,  4,  15,  4,   4,  7,   0, 12,  12,  8,   3, 12,
115    10, 13,   5, 15,   8, 13,   7,  5,  15,  5,   5,  7,   0, 13,  13,  8,
116     2, 13,   9, 14,   6, 15,   8, 14,   7,  6,  15,  6,   6,  7,   0, 14,
117    14,  8,   1, 14,   5,  6,   2,  4,  13, 14,  10, 12,   4,  2,   3,  6,
118    12, 10,  11, 14,   6,  3,   1,  2,  14, 11,   9, 10,   2,  1,   5,  3,
119    10,  9,  13, 11,   3,  5,   4,  1,  11, 13,  12,  9,   1,  4,   6,  5,
120     9, 12,  14, 13,   5, 14,   2, 12,  13,  6,  10,  4,   4, 10,   3, 14,
121    12,  2,  11,  6,   6, 11,   1, 10,  14,  3,   9,  2,   2,  9,   5, 11,
122    10,  1,  13,  3,   3, 13,   4,  9,  11,  5,  12,  1,   1, 12,   6, 13,
123     9,  4,  14,  5,  10, 10,  11, 11,  12, 12,  13, 13,  14, 14,  15, 15,
124};
125
126static int const slang_assoc[16*16] =
127{
128    134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
129    28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
130    37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
131    46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
132    55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
133    64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
134    73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
135    122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
136    15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
137    154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
138    163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
139    172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
140    181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
141    190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
142    199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
143    123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
144};
145#endif
146
147#if defined(USE_CONIO)
148static struct text_info conio_ti;
149static char *conio_screen;
150#endif
151
152#if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
153Display *x11_dpy;
154Window x11_window;
155int x11_font_width, x11_font_height;
156static GC x11_gc;
157static Pixmap x11_pixmap;
158static uint8_t *x11_char, *x11_attr;
159static int x11_colors[16];
160static Font x11_font;
161static XFontStruct *x11_font_struct;
162static int x11_font_offset;
163#if defined(HAVE_X11_XKBLIB_H)
164static Bool x11_detect_autorepeat;
165#endif
166#endif
167
168#if defined(USE_WIN32)
169static uint8_t *win32_char, *win32_attr;
170HANDLE win32_hin, win32_hout;
171static HANDLE win32_front, win32_back;
172static CHAR_INFO *win32_buffer;
173
174static int const win32_fg_palette[] =
175{
176    0,
177    FOREGROUND_BLUE,
178    FOREGROUND_GREEN,
179    FOREGROUND_GREEN | FOREGROUND_BLUE,
180    FOREGROUND_RED,
181    FOREGROUND_RED | FOREGROUND_BLUE,
182    FOREGROUND_RED | FOREGROUND_GREEN,
183    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
184    FOREGROUND_INTENSITY,
185    FOREGROUND_INTENSITY | FOREGROUND_BLUE,
186    FOREGROUND_INTENSITY | FOREGROUND_GREEN,
187    FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
188    FOREGROUND_INTENSITY | FOREGROUND_RED,
189    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
190    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
191    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
192};
193
194static int const win32_bg_palette[] =
195{
196    0,
197    BACKGROUND_BLUE,
198    BACKGROUND_GREEN,
199    BACKGROUND_GREEN | BACKGROUND_BLUE,
200    BACKGROUND_RED,
201    BACKGROUND_RED | BACKGROUND_BLUE,
202    BACKGROUND_RED | BACKGROUND_GREEN,
203    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
204    BACKGROUND_INTENSITY,
205    BACKGROUND_INTENSITY | BACKGROUND_BLUE,
206    BACKGROUND_INTENSITY | BACKGROUND_GREEN,
207    BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
208    BACKGROUND_INTENSITY | BACKGROUND_RED,
209    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
210    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
211    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
212};
213#endif
214
215static char *_caca_empty_line;
216static char *_caca_scratch_line;
217
218static unsigned int _caca_delay;
219static unsigned int _caca_rendertime;
220
221#if defined(OPTIMISE_SLANG_PALETTE)
222static int _caca_fgisbg = 0;
223#endif
224static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
225static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
226
227/*
228 * Local functions
229 */
230#if defined(USE_SLANG)
231static void slang_init_palette(void);
232#endif
233
234#if defined(USE_X11)
235static int x11_error_handler(Display *, XErrorEvent *);
236#endif
237
238/** \brief Set the default colour pair.
239 *
240 *  This function sets the default colour pair. String functions such as
241 *  caca_printf() and graphical primitive functions such as caca_draw_line()
242 *  will use these colour pairs.
243 *
244 *  \param fgcolor The requested foreground colour.
245 *  \param bgcolor The requested background colour.
246 */
247void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
248{
249    if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
250        return;
251
252    _caca_fgcolor = fgcolor;
253    _caca_bgcolor = bgcolor;
254
255    switch(_caca_driver)
256    {
257#if defined(USE_SLANG)
258    case CACA_DRIVER_SLANG:
259
260#if defined(OPTIMISE_SLANG_PALETTE)
261        /* If foreground == background, discard this colour pair. Functions
262         * such as caca_putchar will print spaces instead of characters */
263        if(fgcolor != bgcolor)
264            _caca_fgisbg = 0;
265        else
266        {
267            _caca_fgisbg = 1;
268            if(fgcolor == CACA_COLOR_BLACK)
269                fgcolor = CACA_COLOR_WHITE;
270            else if(fgcolor == CACA_COLOR_WHITE
271                     || fgcolor <= CACA_COLOR_LIGHTGRAY)
272                fgcolor = CACA_COLOR_BLACK;
273            else
274                fgcolor = CACA_COLOR_WHITE;
275        }
276#endif
277
278#if defined(OPTIMISE_SLANG_PALETTE)
279        SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
280#else
281        SLsmg_set_color(fgcolor + 16 * bgcolor);
282#endif
283        break;
284#endif
285#if defined(USE_NCURSES)
286    case CACA_DRIVER_NCURSES:
287        attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
288        break;
289#endif
290#if defined(USE_CONIO)
291    case CACA_DRIVER_CONIO:
292        textbackground(bgcolor);
293        textcolor(fgcolor);
294        break;
295#endif
296#if defined(USE_X11)
297    case CACA_DRIVER_X11:
298        /* Nothing to do */
299        break;
300#endif
301#if defined(USE_WIN32)
302    case CACA_DRIVER_WIN32:
303        /* Nothing to do */
304        break;
305#endif
306    default:
307        break;
308    }
309}
310
311/** \brief Get the current foreground colour.
312 *
313 *  This function returns the current foreground colour that was set with
314 *  caca_set_color().
315 *
316 *  \return The current foreground colour.
317 */
318enum caca_color caca_get_fg_color(void)
319{
320    return _caca_fgcolor;
321}
322
323/** \brief Get the current background colour.
324 *
325 *  This function returns the current background colour that was set with
326 *  caca_set_color().
327 *
328 *  \return The current background colour.
329 */
330enum caca_color caca_get_bg_color(void)
331{
332    return _caca_bgcolor;
333}
334
335/** \brief Print a character.
336 *
337 *  This function prints a character at the given coordinates, using the
338 *  default foreground and background values. If the coordinates are outside
339 *  the screen boundaries, nothing is printed.
340 *
341 *  \param x X coordinate.
342 *  \param y Y coordinate.
343 *  \param c The character to print.
344 */
345void caca_putchar(int x, int y, char c)
346{
347#if defined(USE_CONIO)
348    char *data;
349#endif
350    if(x < 0 || x >= (int)_caca_width ||
351       y < 0 || y >= (int)_caca_height)
352        return;
353
354    switch(_caca_driver)
355    {
356#if defined(USE_SLANG)
357    case CACA_DRIVER_SLANG:
358        SLsmg_gotorc(y, x);
359#if defined(OPTIMISE_SLANG_PALETTE)
360        if(_caca_fgisbg)
361            SLsmg_write_char(' ');
362        else
363#endif
364            SLsmg_write_char(c);
365        break;
366#endif
367#if defined(USE_NCURSES)
368    case CACA_DRIVER_NCURSES:
369        move(y, x);
370        addch(c);
371        break;
372#endif
373#if defined(USE_CONIO)
374    case CACA_DRIVER_CONIO:
375        data = conio_screen + 2 * (x + y * _caca_width);
376        data[0] = c;
377        data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
378        break;
379#endif
380#if defined(USE_X11)
381    case CACA_DRIVER_X11:
382        x11_char[x + y * _caca_width] = c;
383        x11_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
384        break;
385#endif
386#if defined(USE_WIN32)
387    case CACA_DRIVER_WIN32:
388        win32_char[x + y * _caca_width] = c;
389        win32_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
390        break;
391#endif
392    default:
393        break;
394    }
395}
396
397/** \brief Print a string.
398 *
399 *  This function prints a string at the given coordinates, using the
400 *  default foreground and background values. The coordinates may be outside
401 *  the screen boundaries (eg. a negative Y coordinate) and the string will
402 *  be cropped accordingly if it is too long.
403 *
404 *  \param x X coordinate.
405 *  \param y Y coordinate.
406 *  \param s The string to print.
407 */
408void caca_putstr(int x, int y, char const *s)
409{
410#if defined(USE_CONIO) | defined(USE_X11) | defined(USE_WIN32)
411    char *charbuf;
412#endif
413#if defined(USE_X11) | defined(USE_WIN32)
414    char *attrbuf;
415#endif
416    unsigned int len;
417
418    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
419        return;
420
421    len = strlen(s);
422
423    if(x < 0)
424    {
425        len -= -x;
426        if(len < 0)
427            return;
428        s += -x;
429        x = 0;
430    }
431
432    if(x + len >= _caca_width)
433    {
434        len = _caca_width - x;
435        memcpy(_caca_scratch_line, s, len);
436        _caca_scratch_line[len] = '\0';
437        s = _caca_scratch_line;
438    }
439
440    switch(_caca_driver)
441    {
442#if defined(USE_SLANG)
443    case CACA_DRIVER_SLANG:
444        SLsmg_gotorc(y, x);
445#if defined(OPTIMISE_SLANG_PALETTE)
446        if(_caca_fgisbg)
447            SLsmg_write_string(_caca_empty_line + _caca_width - len);
448        else
449#endif
450            SLsmg_write_string((char *)(intptr_t)s);
451        break;
452#endif
453#if defined(USE_NCURSES)
454    case CACA_DRIVER_NCURSES:
455        move(y, x);
456        addstr(s);
457        break;
458#endif
459#if defined(USE_CONIO)
460    case CACA_DRIVER_CONIO:
461        charbuf = conio_screen + 2 * (x + y * _caca_width);
462        while(*s)
463        {
464            *charbuf++ = *s++;
465            *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
466        }
467        break;
468#endif
469#if defined(USE_X11)
470    case CACA_DRIVER_X11:
471        charbuf = x11_char + x + y * _caca_width;
472        attrbuf = x11_attr + x + y * _caca_width;
473        while(*s)
474        {
475            *charbuf++ = *s++;
476            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
477        }
478        break;
479#endif
480#if defined(USE_WIN32)
481    case CACA_DRIVER_WIN32:
482        charbuf = win32_char + x + y * _caca_width;
483        attrbuf = win32_attr + x + y * _caca_width;
484        while(*s)
485        {
486            *charbuf++ = *s++;
487            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
488        }
489        break;
490#endif
491    default:
492        break;
493    }
494}
495
496/** \brief Format a string.
497 *
498 *  This function formats a string at the given coordinates, using the
499 *  default foreground and background values. The coordinates may be outside
500 *  the screen boundaries (eg. a negative Y coordinate) and the string will
501 *  be cropped accordingly if it is too long. The syntax of the format
502 *  string is the same as for the C printf() function.
503 *
504 *  \param x X coordinate.
505 *  \param y Y coordinate.
506 *  \param format The format string to print.
507 *  \param ... Arguments to the format string.
508 */
509void caca_printf(int x, int y, char const *format, ...)
510{
511    char tmp[BUFSIZ];
512    char *buf = tmp;
513    va_list args;
514
515    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
516        return;
517
518    if(_caca_width - x + 1 > BUFSIZ)
519        buf = malloc(_caca_width - x + 1);
520
521    va_start(args, format);
522#if defined(HAVE_VSNPRINTF)
523    vsnprintf(buf, _caca_width - x + 1, format, args);
524#else
525    vsprintf(buf, format, args);
526#endif
527    buf[_caca_width - x] = '\0';
528    va_end(args);
529
530    caca_putstr(x, y, buf);
531
532    if(buf != tmp)
533        free(buf);
534}
535
536/** \brief Clear the screen.
537 *
538 *  This function clears the screen using a black background.
539 */
540void caca_clear(void)
541{
542    enum caca_color oldfg = caca_get_fg_color();
543    enum caca_color oldbg = caca_get_bg_color();
544    int y = _caca_height;
545
546    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
547
548    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
549    while(y--)
550        caca_putstr(0, y, _caca_empty_line);
551
552    caca_set_color(oldfg, oldbg);
553}
554
555#if !defined(_DOXYGEN_SKIP_ME)
556int _caca_init_graphics(void)
557{
558#if defined(USE_SLANG)
559    if(_caca_driver == CACA_DRIVER_SLANG)
560    {
561        slang_init_palette();
562
563        /* Disable alt charset support so that we get a chance to have all
564         * 256 colour pairs */
565        SLtt_Has_Alt_Charset = 0;
566
567        _caca_width = SLtt_Screen_Cols;
568        _caca_height = SLtt_Screen_Rows;
569    }
570    else
571#endif
572#if defined(USE_NCURSES)
573    if(_caca_driver == CACA_DRIVER_NCURSES)
574    {
575        static int curses_colors[] =
576        {
577            /* Standard curses colours */
578            COLOR_BLACK,
579            COLOR_BLUE,
580            COLOR_GREEN,
581            COLOR_CYAN,
582            COLOR_RED,
583            COLOR_MAGENTA,
584            COLOR_YELLOW,
585            COLOR_WHITE,
586            /* Extra values for xterm-16color */
587            COLOR_BLACK + 8,
588            COLOR_BLUE + 8,
589            COLOR_GREEN + 8,
590            COLOR_CYAN + 8,
591            COLOR_RED + 8,
592            COLOR_MAGENTA + 8,
593            COLOR_YELLOW + 8,
594            COLOR_WHITE + 8
595        };
596
597        int fg, bg, max;
598
599        /* Activate colour */
600        start_color();
601
602        /* If COLORS == 16, it means the terminal supports full bright colours
603         * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
604         * we can build 16*16 colour pairs.
605         * If COLORS == 8, it means the terminal does not know about bright
606         * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
607         * and \e[5m). We can only build 8*8 colour pairs. */
608        max = COLORS >= 16 ? 16 : 8;
609
610        for(bg = 0; bg < max; bg++)
611            for(fg = 0; fg < max; fg++)
612            {
613                /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
614                 * is light gray on black, since some terminals don't like
615                 * this colour pair to be redefined. */
616                int col = ((max + 7 - fg) % max) + max * bg;
617                init_pair(col, curses_colors[fg], curses_colors[bg]);
618                ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
619
620                if(max == 8)
621                {
622                    /* Bright fg on simple bg */
623                    ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
624                    /* Simple fg on bright bg */
625                    ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
626                                                        | COLOR_PAIR(col);
627                    /* Bright fg on bright bg */
628                    ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
629                                                            | COLOR_PAIR(col);
630                }
631            }
632
633        _caca_width = COLS;
634        _caca_height = LINES;
635    }
636    else
637#endif
638#if defined(USE_CONIO)
639    if(_caca_driver == CACA_DRIVER_CONIO)
640    {
641        gettextinfo(&conio_ti);
642        conio_screen = malloc(2 * conio_ti.screenwidth
643                                 * conio_ti.screenheight * sizeof(char));
644        if(conio_screen == NULL)
645            return -1;
646#   if defined(SCREENUPDATE_IN_PC_H)
647        ScreenRetrieve(conio_screen);
648#   else
649        /* FIXME */
650#   endif
651        _caca_width = conio_ti.screenwidth;
652        _caca_height = conio_ti.screenheight;
653    }
654    else
655#endif
656#if defined(USE_X11)
657    if(_caca_driver == CACA_DRIVER_X11)
658    {
659        static int x11_palette[] =
660        {
661            /* Standard curses colours */
662            0x0,    0x0,    0x0,
663            0x0,    0x0,    0x8000,
664            0x0,    0x8000, 0x0,
665            0x0,    0x8000, 0x8000,
666            0x8000, 0x0,    0x0,
667            0x8000, 0x0,    0x8000,
668            0x8000, 0x8000, 0x0,
669            0x8000, 0x8000, 0x8000,
670            /* Extra values for xterm-16color */
671            0x4000, 0x4000, 0x4000,
672            0x4000, 0x4000, 0xffff,
673            0x4000, 0xffff, 0x4000,
674            0x4000, 0xffff, 0xffff,
675            0xffff, 0x4000, 0x4000,
676            0xffff, 0x4000, 0xffff,
677            0xffff, 0xffff, 0x4000,
678            0xffff, 0xffff, 0xffff,
679        };
680
681        Colormap colormap;
682        XSetWindowAttributes x11_winattr;
683        int (*old_error_handler)(Display *, XErrorEvent *);
684        char const *font_name = "8x13bold";
685        int i;
686
687        if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
688            sscanf(getenv("CACA_GEOMETRY"),
689                   "%ux%u", &_caca_width, &_caca_height);
690
691        if(!_caca_width)
692            _caca_width = 80;
693        if(!_caca_height)
694            _caca_height = 32;
695
696        x11_char = malloc(_caca_width * _caca_height * sizeof(int));
697        if(x11_char == NULL)
698            return -1;
699
700        x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
701        if(x11_attr == NULL)
702        {
703            free(x11_char);
704            return -1;
705        }
706
707        memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
708        memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
709
710        x11_dpy = XOpenDisplay(NULL);
711        if(x11_dpy == NULL)
712        {
713            free(x11_char);
714            free(x11_attr);
715            return -1;
716        }
717
718        if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
719            font_name = getenv("CACA_FONT");
720
721        /* Ignore font errors */
722        old_error_handler = XSetErrorHandler(x11_error_handler);
723
724        x11_font = XLoadFont(x11_dpy, font_name);
725        if(!x11_font)
726        {
727            XCloseDisplay(x11_dpy);
728            free(x11_char);
729            free(x11_attr);
730            return -1;
731        }
732
733        x11_font_struct = XQueryFont(x11_dpy, x11_font);
734        if(!x11_font_struct)
735        {
736            XUnloadFont(x11_dpy, x11_font);
737            XCloseDisplay(x11_dpy);
738            free(x11_char);
739            free(x11_attr);
740            return -1;
741        }
742
743        /* Reset the default X11 error handler */
744        XSetErrorHandler(old_error_handler);
745
746        x11_font_width = x11_font_struct->max_bounds.width;
747        x11_font_height = x11_font_struct->max_bounds.ascent
748                             + x11_font_struct->max_bounds.descent;
749        x11_font_offset = x11_font_struct->max_bounds.descent;
750
751        colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
752        for(i = 0; i < 16; i++)
753        {
754            XColor color;
755            color.red = x11_palette[i * 3];
756            color.green = x11_palette[i * 3 + 1];
757            color.blue = x11_palette[i * 3 + 2];
758            XAllocColor(x11_dpy, colormap, &color);
759            x11_colors[i] = color.pixel;
760        }
761
762        x11_winattr.backing_store = Always;
763        x11_winattr.background_pixel = x11_colors[0];
764        x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
765
766        x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
767                                   _caca_width * x11_font_width,
768                                   _caca_height * x11_font_height,
769                                   0, 0, InputOutput, 0,
770                                   CWBackingStore | CWBackPixel | CWEventMask,
771                                   &x11_winattr);
772
773        XStoreName(x11_dpy, x11_window, "caca for X");
774
775        XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
776        XMapWindow(x11_dpy, x11_window);
777
778        x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
779        XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
780        XSetFont(x11_dpy, x11_gc, x11_font);
781
782        for(;;)
783        {
784            XEvent event;
785            XNextEvent(x11_dpy, &event);
786            if (event.type == MapNotify)
787                break;
788        }
789
790        /* Disable autorepeat */
791#if defined(HAVE_X11_XKBLIB_H)
792        XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
793        if(!x11_detect_autorepeat)
794            XAutoRepeatOff(x11_dpy);
795#endif
796
797        XSelectInput(x11_dpy, x11_window,
798                     KeyPressMask | KeyReleaseMask | ButtonPressMask
799                      | ButtonReleaseMask | PointerMotionMask);
800
801        XSync(x11_dpy, False);
802
803        x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
804                                   _caca_width * x11_font_width,
805                                   _caca_height * x11_font_height,
806                                   DefaultDepth(x11_dpy,
807                                                DefaultScreen(x11_dpy)));
808    }
809    else
810#endif
811#if defined(USE_WIN32)
812    if(_caca_driver == CACA_DRIVER_WIN32)
813    {
814        CONSOLE_CURSOR_INFO cci;
815        CONSOLE_SCREEN_BUFFER_INFO csbi;
816        COORD size;
817
818        win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
819                                                0, NULL,
820                                                CONSOLE_TEXTMODE_BUFFER, NULL);
821        if(!win32_front)
822            return -1;
823
824        win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
825                                               0, NULL,
826                                               CONSOLE_TEXTMODE_BUFFER, NULL);
827        if(!win32_back)
828            return -1;
829
830        if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
831            return -1;
832
833        _caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
834        _caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
835
836        size.X = _caca_width;
837        size.Y = _caca_height;
838        SetConsoleScreenBufferSize(win32_front, size);
839        SetConsoleScreenBufferSize(win32_back, size);
840
841        SetConsoleMode(win32_front, 0);
842        SetConsoleMode(win32_back, 0);
843
844        GetConsoleCursorInfo(win32_front, &cci);
845        cci.bVisible = FALSE;
846        SetConsoleCursorInfo(win32_front, &cci);
847        SetConsoleCursorInfo(win32_back, &cci);
848
849        SetConsoleActiveScreenBuffer(win32_front);
850
851        win32_char = malloc(_caca_width * _caca_height * sizeof(int));
852        if(win32_char == NULL)
853            return -1;
854
855        win32_attr = malloc(_caca_width * _caca_height * sizeof(int));
856        if(win32_attr == NULL)
857        {
858            free(win32_char);
859            return -1;
860        }
861
862win32_buffer = malloc(sizeof(CHAR_INFO) * _caca_width * _caca_height);
863
864        memset(win32_char, 0, _caca_width * _caca_height * sizeof(int));
865        memset(win32_attr, 0, _caca_width * _caca_height * sizeof(int));
866    }
867    else
868#endif
869    {
870        /* Dummy */
871    }
872
873    _caca_empty_line = malloc(_caca_width + 1);
874    memset(_caca_empty_line, ' ', _caca_width);
875    _caca_empty_line[_caca_width] = '\0';
876
877    _caca_scratch_line = malloc(_caca_width + 1);
878
879    _caca_delay = 0;
880    _caca_rendertime = 0;
881
882    return 0;
883}
884
885int _caca_end_graphics(void)
886{
887#if defined(USE_SLANG)
888    /* Nothing to do */
889#endif
890#if defined(USE_NCURSES)
891    /* Nothing to do */
892#endif
893#if defined(USE_CONIO)
894    if(_caca_driver == CACA_DRIVER_CONIO)
895    {
896        free(conio_screen);
897    }
898    else
899#endif
900#if defined(USE_X11)
901    if(_caca_driver == CACA_DRIVER_X11)
902    {
903        XSync(x11_dpy, False);
904#if defined(HAVE_X11_XKBLIB_H)
905        if(!x11_detect_autorepeat)
906            XAutoRepeatOn(x11_dpy);
907#endif
908        XFreePixmap(x11_dpy, x11_pixmap);
909        XFreeFont(x11_dpy, x11_font_struct);
910        XFreeGC(x11_dpy, x11_gc);
911        XUnmapWindow(x11_dpy, x11_window);
912        XDestroyWindow(x11_dpy, x11_window);
913        XCloseDisplay(x11_dpy);
914        free(x11_char);
915        free(x11_attr);
916    }
917    else
918#endif
919#if defined(USE_WIN32)
920    if(_caca_driver == CACA_DRIVER_WIN32)
921    {
922        SetConsoleActiveScreenBuffer(win32_hout);
923        CloseHandle(win32_back);
924        CloseHandle(win32_front);
925        free(win32_char);
926        free(win32_attr);
927    }
928    else
929#endif
930    {
931        /* Dummy */
932    }
933
934    free(_caca_empty_line);
935
936    return 0;
937}
938#endif /* _DOXYGEN_SKIP_ME */
939
940/** \brief Set the refresh delay.
941 *
942 *  This function sets the refresh delay in microseconds. The refresh delay
943 *  is used by caca_refresh() to achieve constant framerate. See the
944 *  caca_refresh() documentation for more details.
945 *
946 *  If the argument is zero, constant framerate is disabled. This is the
947 *  default behaviour.
948 *
949 *  \param usec The refresh delay in microseconds.
950 */
951void caca_set_delay(unsigned int usec)
952{
953    _caca_delay = usec;
954}
955
956/** \brief Get the average rendering time.
957 *
958 *  This function returns the average rendering time, which is the average
959 *  measured time between two caca_refresh() calls, in microseconds. If
960 *  constant framerate was activated by calling caca_set_delay(), the average
961 *  rendering time will not be considerably shorter than the requested delay
962 *  even if the real rendering time was shorter.
963 *
964 *  \return The render time in microseconds.
965 */
966unsigned int caca_get_rendertime(void)
967{
968    return _caca_rendertime;
969}
970
971/** \brief Flush pending changes and redraw the screen.
972 *
973 *  This function flushes all graphical operations and prints them to the
974 *  screen. Nothing will show on the screen until caca_refresh() is
975 *  called.
976 *
977 *  If caca_set_delay() was called with a non-zero value, caca_refresh()
978 *  will use that value to achieve constant framerate: if two consecutive
979 *  calls to caca_refresh() are within a time range shorter than the value
980 *  set with caca_set_delay(), the second call will wait a bit before
981 *  performing the screen refresh.
982 */
983void caca_refresh(void)
984{
985#if !defined(_DOXYGEN_SKIP_ME)
986#define IDLE_USEC 10000
987#endif
988    static struct caca_timer timer = CACA_TIMER_INITIALIZER;
989    static int lastticks = 0;
990    int ticks = lastticks + _caca_getticks(&timer);
991
992#if defined(USE_SLANG)
993    if(_caca_driver == CACA_DRIVER_SLANG)
994    {
995        SLsmg_refresh();
996    }
997    else
998#endif
999#if defined(USE_NCURSES)
1000    if(_caca_driver == CACA_DRIVER_NCURSES)
1001    {
1002        refresh();
1003    }
1004    else
1005#endif
1006#if defined(USE_CONIO)
1007    if(_caca_driver == CACA_DRIVER_CONIO)
1008    {
1009#   if defined(SCREENUPDATE_IN_PC_H)
1010        ScreenUpdate(conio_screen);
1011#   else
1012        /* FIXME */
1013#   endif
1014    }
1015    else
1016#endif
1017#if defined(USE_X11)
1018    if(_caca_driver == CACA_DRIVER_X11)
1019    {
1020        unsigned int x, y, len;
1021
1022        /* First draw the background colours. Splitting the process in two
1023         * loops like this is actually slightly faster. */
1024        for(y = 0; y < _caca_height; y++)
1025        {
1026            for(x = 0; x < _caca_width; x += len)
1027            {
1028                unsigned char *attr = x11_attr + x + y * _caca_width;
1029
1030                len = 1;
1031                while(x + len < _caca_width
1032                       && (attr[len] >> 4) == (attr[0] >> 4))
1033                    len++;
1034
1035                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
1036                XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
1037                               x * x11_font_width, y * x11_font_height,
1038                               len * x11_font_width, x11_font_height);
1039            }
1040        }
1041
1042        /* Then print the foreground characters */
1043        for(y = 0; y < _caca_height; y++)
1044        {
1045            for(x = 0; x < _caca_width; x += len)
1046            {
1047                unsigned char *attr = x11_attr + x + y * _caca_width;
1048
1049                len = 1;
1050
1051                /* Skip spaces */
1052                if(x11_char[x + y * _caca_width] == ' ')
1053                    continue;
1054
1055                while(x + len < _caca_width
1056                       && (attr[len] & 0xf) == (attr[0] & 0xf))
1057                    len++;
1058
1059                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
1060                XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
1061                            (y + 1) * x11_font_height - x11_font_offset,
1062                            x11_char + x + y * _caca_width, len);
1063            }
1064        }
1065
1066        XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
1067                  _caca_width * x11_font_width, _caca_height * x11_font_height,
1068                  0, 0);
1069        XFlush(x11_dpy);
1070    }
1071    else
1072#endif
1073#if defined(USE_WIN32)
1074    if(_caca_driver == CACA_DRIVER_WIN32)
1075    {
1076        COORD size, pos;
1077        SMALL_RECT rect;
1078        DWORD dummy;
1079        unsigned int x, y, len;
1080
1081        /* Render everything to our back buffer */
1082        for(y = 0; y < _caca_height; y++)
1083        {
1084            pos.X = 0;
1085            pos.Y = y;
1086            SetConsoleCursorPosition(win32_back, pos);
1087
1088            for(x = 0; x < _caca_width; x += len)
1089            {
1090                unsigned char *attr = win32_attr + x + y * _caca_width;
1091
1092                len = 1;
1093                while(x + len < _caca_width && attr[len] == attr[0])
1094                    len++;
1095
1096                SetConsoleTextAttribute(win32_back,
1097                                        win32_fg_palette[attr[0] & 0xf]
1098                                         | win32_bg_palette[attr[0] >> 4]);
1099
1100                WriteConsole(win32_back, win32_char + x + y * _caca_width,
1101                             len, &dummy, NULL);
1102            }
1103        }
1104
1105        /* Blit the back buffer to the front buffer */
1106        size.X = _caca_width;
1107        size.Y = _caca_height;
1108        pos.X = pos.Y = 0;
1109        rect.Left = rect.Top = 0;
1110        rect.Right = _caca_width - 1;
1111        rect.Bottom = _caca_height - 1;
1112        ReadConsoleOutput(win32_back, win32_buffer, size, pos, &rect);
1113        WriteConsoleOutput(win32_front, win32_buffer, size, pos, &rect);
1114    }
1115    else
1116#endif
1117    {
1118        /* Dummy */
1119    }
1120
1121    /* Wait until _caca_delay + time of last call */
1122    ticks += _caca_getticks(&timer);
1123    for(ticks += _caca_getticks(&timer);
1124        ticks + IDLE_USEC < (int)_caca_delay;
1125        ticks += _caca_getticks(&timer))
1126    {
1127        _caca_sleep(IDLE_USEC);
1128    }
1129
1130    /* Update the sliding mean of the render time */
1131    _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
1132
1133    lastticks = ticks - _caca_delay;
1134
1135    /* If we drifted too much, it's bad, bad, bad. */
1136    if(lastticks > (int)_caca_delay)
1137        lastticks = 0;
1138}
1139
1140#if defined(USE_SLANG)
1141static void slang_init_palette(void)
1142{
1143    /* See SLang ref., 5.4.4. */
1144    static char *slang_colors[16] =
1145    {
1146        /* Standard colours */
1147        "black",
1148        "blue",
1149        "green",
1150        "cyan",
1151        "red",
1152        "magenta",
1153        "brown",
1154        "lightgray",
1155        /* Bright colours */
1156        "gray",
1157        "brightblue",
1158        "brightgreen",
1159        "brightcyan",
1160        "brightred",
1161        "brightmagenta",
1162        "yellow",
1163        "white",
1164    };
1165
1166#if defined(OPTIMISE_SLANG_PALETTE)
1167    int i;
1168
1169    for(i = 0; i < 16 * 16; i++)
1170        SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
1171                                slang_colors[slang_palette[i * 2 + 1]]);
1172#else
1173    int fg, bg;
1174
1175    for(bg = 0; bg < 16; bg++)
1176        for(fg = 0; fg < 16; fg++)
1177        {
1178            int i = fg + 16 * bg;
1179            SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
1180        }
1181#endif
1182}
1183#endif /* USE_SLANG */
1184
1185#if defined(USE_X11)
1186static int x11_error_handler(Display *dpy, XErrorEvent *event)
1187{
1188    return 0;
1189}
1190#endif
1191
Note: See TracBrowser for help on using the repository browser.