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

Last change on this file since 300 was 300, checked in by Sam Hocevar, 18 years ago
  • src/graphics.c: + Minor optimisation in caca_set_color(). + Added an X11 error handler to avoid BadFont? aborts in the X11 driver. + Optimised the X11 caca_refresh() routine by handling strings of the

same colour as one text chunk.

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