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

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