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

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