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

Last change on this file since 322 was 322, checked in by Sam Hocevar, 19 years ago
  • libcaca.spec debian/control: + Updated the package descriptions to include cacaball.
  • doc/cacademo.1 doc/Makefile.am: + Use the cacademo manpage as the cacaball manpage.
  • src/: + Added #ifdef _DOXYGEN_SKIP_ME here and there to prvent Doxygen from

documenting bizarre stuff.

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