source: libcaca/branches/0.7/src/graphics.c @ 4104

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