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

Last change on this file since 332 was 332, checked in by Sam Hocevar, 17 years ago
  • src/graphics: + Initialise the char buffers in the X11 driver.
  • src/io.c: + caca_get_event() and caca_wait_event() immediately return zero if

event_mask is zero.

+ Added support for the Home, End, PgUp? and PgDown? keys.
+ In the S-Lang driver, added a call to SLang_getkey() before the call to

SLkp_getkey(), otherwise a single Escape call will not be interpreted.

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