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

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