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

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