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

Last change on this file since 331 was 331, checked in by Sam Hocevar, 17 years ago
  • test/event.c: + Do not refresh after each event, but only when there is no event

pending.

+ If the pressed key is a printable character, display it.

  • src/time.c: + Moved _caca_getticks() to this file.
  • src/caca.c: + Set the escape delay to a very low value in the ncurses driver,

because I don't want escape sequences to be entered manually.

  • src/io.c: + Autorepeat emulation in the ncurses and slang drivers: do not

immediately send the key release event.

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