~ubuntu-branches/ubuntu/gutsy/libcaca/gutsy

« back to all changes in this revision

Viewing changes to src/graphics.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hocevar (Debian packages)
  • Date: 2006-12-03 02:05:11 UTC
  • mfrom: (3.1.6 feisty)
  • Revision ID: james.westby@ubuntu.com-20061203020511-h5nzqgf8nov7ns3z
Tags: 0.99.beta11.debian-2
Remove toilet from caca-utils now that it has entered testing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 258 2004-01-26 10:52:30Z 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
 
#   if defined(HAVE_NCURSES_H)
41
 
#       include <ncurses.h>
42
 
#   else
43
 
#       include <curses.h>
44
 
#   endif
45
 
#endif
46
 
#if defined(USE_CONIO)
47
 
#   include <conio.h>
48
 
#   if defined(SCREENUPDATE_IN_PC_H)
49
 
#       include <pc.h>
50
 
#   endif
51
 
#endif
52
 
#if defined(USE_X11)
53
 
#   include <X11/Xlib.h>
54
 
#   if defined(HAVE_X11_XKBLIB_H)
55
 
#       include <X11/XKBlib.h>
56
 
#   endif
57
 
#endif
58
 
#if defined(USE_WIN32)
59
 
#   include <windows.h>
60
 
#endif
61
 
 
62
 
#if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
63
 
#   include <inttypes.h>
64
 
#else
65
 
typedef unsigned char uint8_t;
66
 
#endif
67
 
 
68
 
#include <stdio.h> /* BUFSIZ */
69
 
#include <string.h>
70
 
#include <stdlib.h>
71
 
#if defined(HAVE_UNISTD_H)
72
 
#   include <unistd.h>
73
 
#endif
74
 
#include <stdarg.h>
75
 
 
76
 
#if defined(HAVE_SIGNAL_H)
77
 
#   include <signal.h>
78
 
#endif
79
 
#if defined(HAVE_SYS_IOCTL_H)
80
 
#   include <sys/ioctl.h>
81
 
#endif
82
 
 
83
 
#include "caca.h"
84
 
#include "caca_internals.h"
85
 
 
86
 
/*
87
 
 * Global variables
88
 
 */
89
 
#if !defined(_DOXYGEN_SKIP_ME)
90
 
unsigned int _caca_width = 0;
91
 
unsigned int _caca_height = 0;
92
 
int _caca_resize = 0;
93
 
int _caca_resize_event = 0;
94
 
#endif
95
 
 
96
 
/*
97
 
 * Local variables
98
 
 */
99
 
#if defined(USE_NCURSES)
100
 
static int ncurses_attr[16*16];
101
 
#endif
102
 
 
103
 
#if defined(USE_SLANG)
104
 
/* Tables generated by test/optipal.c */
105
 
static int const slang_palette[2*16*16] =
106
 
{
107
 
     1,  0,   2,  0,   3,  0,   4,  0,   5,  0,   6,  0,   7,  0,   8,  0,
108
 
     9,  0,  10,  0,  11,  0,  12,  0,  13,  0,  14,  0,  15,  0,   0,  8,
109
 
     8,  7,   7,  8,  15,  7,   7, 15,  15,  9,   9, 15,   1,  9,   9,  1,
110
 
     7,  9,   9,  7,   8,  1,   1,  8,   0,  1,  15, 10,  10, 15,   2, 10,
111
 
    10,  2,   7, 10,  10,  7,   8,  2,   2,  8,   0,  2,  15, 11,  11, 15,
112
 
     3, 11,  11,  3,   7, 11,  11,  7,   8,  3,   3,  8,   0,  3,  15, 12,
113
 
    12, 15,   4, 12,  12,  4,   7, 12,  12,  7,   8,  4,   4,  8,   0,  4,
114
 
    15, 13,  13, 15,   5, 13,  13,  5,   7, 13,  13,  7,   8,  5,   5,  8,
115
 
     0,  5,  15, 14,  14, 15,   6, 14,  14,  6,   7, 14,  14,  7,   8,  6,
116
 
     6,  8,   0,  6,   4,  6,   6,  4,  12, 14,  14, 12,   6,  2,   2,  6,
117
 
    14, 10,  10, 14,   2,  3,   3,  2,  10, 11,  11, 10,   3,  1,   1,  3,
118
 
    11,  9,   9, 11,   1,  5,   5,  1,   9, 13,  13,  9,   5,  4,   4,  5,
119
 
    13, 12,  12, 13,   4, 14,   6, 12,  12,  6,  14,  4,   6, 10,   2, 14,
120
 
    14,  2,  10,  6,   2, 11,   3, 10,  10,  3,  11,  2,   3,  9,   1, 11,
121
 
    11,  1,   9,  3,   1, 13,   5,  9,   9,  5,  13,  1,   5, 12,   4, 13,
122
 
    13,  4,  12,  5,   0,  7,   0, 15,  15,  8,   8, 15,  15,  1,   7,  1,
123
 
     1,  6,   2,  5,   3,  4,   4,  3,   5,  2,   6,  1,   0,  0,   1,  1,
124
 
     9,  6,  10,  5,  11,  4,  12,  3,  13,  2,  14,  1,   2,  2,   3,  3,
125
 
     4,  4,   5,  5,   6,  6,   7,  7,  14,  9,   1, 15,   8,  9,   8,  8,
126
 
     9,  9,   1,  7,   0,  9,   9,  8,   6,  9,  13, 10,   2, 15,   8, 10,
127
 
     7,  2,  15,  2,   2,  7,   0, 10,  10,  8,   5, 10,  12, 11,   3, 15,
128
 
     8, 11,   7,  3,  15,  3,   3,  7,   0, 11,  11,  8,   4, 11,  11, 12,
129
 
     4, 15,   8, 12,   7,  4,  15,  4,   4,  7,   0, 12,  12,  8,   3, 12,
130
 
    10, 13,   5, 15,   8, 13,   7,  5,  15,  5,   5,  7,   0, 13,  13,  8,
131
 
     2, 13,   9, 14,   6, 15,   8, 14,   7,  6,  15,  6,   6,  7,   0, 14,
132
 
    14,  8,   1, 14,   5,  6,   2,  4,  13, 14,  10, 12,   4,  2,   3,  6,
133
 
    12, 10,  11, 14,   6,  3,   1,  2,  14, 11,   9, 10,   2,  1,   5,  3,
134
 
    10,  9,  13, 11,   3,  5,   4,  1,  11, 13,  12,  9,   1,  4,   6,  5,
135
 
     9, 12,  14, 13,   5, 14,   2, 12,  13,  6,  10,  4,   4, 10,   3, 14,
136
 
    12,  2,  11,  6,   6, 11,   1, 10,  14,  3,   9,  2,   2,  9,   5, 11,
137
 
    10,  1,  13,  3,   3, 13,   4,  9,  11,  5,  12,  1,   1, 12,   6, 13,
138
 
     9,  4,  14,  5,  10, 10,  11, 11,  12, 12,  13, 13,  14, 14,  15, 15,
139
 
};
140
 
 
141
 
static int const slang_assoc[16*16] =
142
 
{
143
 
    134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
144
 
    28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
145
 
    37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
146
 
    46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
147
 
    55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
148
 
    64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
149
 
    73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
150
 
    122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
151
 
    15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
152
 
    154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
153
 
    163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
154
 
    172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
155
 
    181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
156
 
    190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
157
 
    199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
158
 
    123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
159
 
};
160
 
#endif
161
 
 
162
 
#if defined(USE_CONIO)
163
 
static struct text_info conio_ti;
164
 
static char *conio_screen;
165
 
#endif
166
 
 
167
 
#if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
168
 
Display *x11_dpy;
169
 
Window x11_window;
170
 
Pixmap x11_pixmap;
171
 
GC x11_gc;
172
 
long int x11_event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
173
 
            | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
174
 
            | ExposureMask;
175
 
int x11_font_width, x11_font_height;
176
 
unsigned int x11_new_width, x11_new_height;
177
 
static uint8_t *x11_char, *x11_attr;
178
 
static int x11_colors[16];
179
 
static Font x11_font;
180
 
static XFontStruct *x11_font_struct;
181
 
static int x11_font_offset;
182
 
#if defined(HAVE_X11_XKBLIB_H)
183
 
static Bool x11_detect_autorepeat;
184
 
#endif
185
 
#endif
186
 
 
187
 
#if defined(USE_WIN32)
188
 
static uint8_t *win32_char, *win32_attr;
189
 
HANDLE win32_hin, win32_hout;
190
 
static HANDLE win32_front, win32_back;
191
 
static CHAR_INFO *win32_buffer;
192
 
 
193
 
static int const win32_fg_palette[] =
194
 
{
195
 
    0,
196
 
    FOREGROUND_BLUE,
197
 
    FOREGROUND_GREEN,
198
 
    FOREGROUND_GREEN | FOREGROUND_BLUE,
199
 
    FOREGROUND_RED,
200
 
    FOREGROUND_RED | FOREGROUND_BLUE,
201
 
    FOREGROUND_RED | FOREGROUND_GREEN,
202
 
    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
203
 
    FOREGROUND_INTENSITY,
204
 
    FOREGROUND_INTENSITY | FOREGROUND_BLUE,
205
 
    FOREGROUND_INTENSITY | FOREGROUND_GREEN,
206
 
    FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
207
 
    FOREGROUND_INTENSITY | FOREGROUND_RED,
208
 
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
209
 
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
210
 
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
211
 
};
212
 
 
213
 
static int const win32_bg_palette[] =
214
 
{
215
 
    0,
216
 
    BACKGROUND_BLUE,
217
 
    BACKGROUND_GREEN,
218
 
    BACKGROUND_GREEN | BACKGROUND_BLUE,
219
 
    BACKGROUND_RED,
220
 
    BACKGROUND_RED | BACKGROUND_BLUE,
221
 
    BACKGROUND_RED | BACKGROUND_GREEN,
222
 
    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
223
 
    BACKGROUND_INTENSITY,
224
 
    BACKGROUND_INTENSITY | BACKGROUND_BLUE,
225
 
    BACKGROUND_INTENSITY | BACKGROUND_GREEN,
226
 
    BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
227
 
    BACKGROUND_INTENSITY | BACKGROUND_RED,
228
 
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
229
 
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
230
 
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
231
 
};
232
 
#endif
233
 
 
234
 
static char *_caca_empty_line;
235
 
static char *_caca_scratch_line;
236
 
 
237
 
static unsigned int _caca_delay;
238
 
static unsigned int _caca_rendertime;
239
 
 
240
 
#if defined(OPTIMISE_SLANG_PALETTE)
241
 
static int _caca_fgisbg = 0;
242
 
#endif
243
 
static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
244
 
static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
245
 
 
246
 
/*
247
 
 * Local functions
248
 
 */
249
 
static void caca_handle_resize(void);
250
 
 
251
 
#if defined(USE_SLANG)
252
 
static void slang_init_palette(void);
253
 
#endif
254
 
 
255
 
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
256
 
static RETSIGTYPE sigwinch_handler(int);
257
 
#endif
258
 
 
259
 
#if defined(USE_X11)
260
 
static int x11_error_handler(Display *, XErrorEvent *);
261
 
#endif
262
 
 
263
 
/** \brief Set the default colour pair.
264
 
 *
265
 
 *  This function sets the default colour pair. String functions such as
266
 
 *  caca_printf() and graphical primitive functions such as caca_draw_line()
267
 
 *  will use these colour pairs.
268
 
 *
269
 
 *  \param fgcolor The requested foreground colour.
270
 
 *  \param bgcolor The requested background colour.
271
 
 */
272
 
void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
273
 
{
274
 
    if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
275
 
        return;
276
 
 
277
 
    _caca_fgcolor = fgcolor;
278
 
    _caca_bgcolor = bgcolor;
279
 
 
280
 
    switch(_caca_driver)
281
 
    {
282
 
#if defined(USE_SLANG)
283
 
    case CACA_DRIVER_SLANG:
284
 
 
285
 
#if defined(OPTIMISE_SLANG_PALETTE)
286
 
        /* If foreground == background, discard this colour pair. Functions
287
 
         * such as caca_putchar will print spaces instead of characters */
288
 
        if(fgcolor != bgcolor)
289
 
            _caca_fgisbg = 0;
290
 
        else
291
 
        {
292
 
            _caca_fgisbg = 1;
293
 
            if(fgcolor == CACA_COLOR_BLACK)
294
 
                fgcolor = CACA_COLOR_WHITE;
295
 
            else if(fgcolor == CACA_COLOR_WHITE
296
 
                     || fgcolor <= CACA_COLOR_LIGHTGRAY)
297
 
                fgcolor = CACA_COLOR_BLACK;
298
 
            else
299
 
                fgcolor = CACA_COLOR_WHITE;
300
 
        }
301
 
#endif
302
 
 
303
 
#if defined(OPTIMISE_SLANG_PALETTE)
304
 
        SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
305
 
#else
306
 
        SLsmg_set_color(fgcolor + 16 * bgcolor);
307
 
#endif
308
 
        break;
309
 
#endif
310
 
#if defined(USE_NCURSES)
311
 
    case CACA_DRIVER_NCURSES:
312
 
        attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
313
 
        break;
314
 
#endif
315
 
#if defined(USE_CONIO)
316
 
    case CACA_DRIVER_CONIO:
317
 
        textbackground(bgcolor);
318
 
        textcolor(fgcolor);
319
 
        break;
320
 
#endif
321
 
#if defined(USE_X11)
322
 
    case CACA_DRIVER_X11:
323
 
        /* Nothing to do */
324
 
        break;
325
 
#endif
326
 
#if defined(USE_WIN32)
327
 
    case CACA_DRIVER_WIN32:
328
 
        /* Nothing to do */
329
 
        break;
330
 
#endif
331
 
    default:
332
 
        break;
333
 
    }
334
 
}
335
 
 
336
 
/** \brief Get the current foreground colour.
337
 
 *
338
 
 *  This function returns the current foreground colour that was set with
339
 
 *  caca_set_color().
340
 
 *
341
 
 *  \return The current foreground colour.
342
 
 */
343
 
enum caca_color caca_get_fg_color(void)
344
 
{
345
 
    return _caca_fgcolor;
346
 
}
347
 
 
348
 
/** \brief Get the current background colour.
349
 
 *
350
 
 *  This function returns the current background colour that was set with
351
 
 *  caca_set_color().
352
 
 *
353
 
 *  \return The current background colour.
354
 
 */
355
 
enum caca_color caca_get_bg_color(void)
356
 
{
357
 
    return _caca_bgcolor;
358
 
}
359
 
 
360
 
/** \brief Print a character.
361
 
 *
362
 
 *  This function prints a character at the given coordinates, using the
363
 
 *  default foreground and background values. If the coordinates are outside
364
 
 *  the screen boundaries, nothing is printed.
365
 
 *
366
 
 *  \param x X coordinate.
367
 
 *  \param y Y coordinate.
368
 
 *  \param c The character to print.
369
 
 */
370
 
void caca_putchar(int x, int y, char c)
371
 
{
372
 
#if defined(USE_CONIO)
373
 
    char *data;
374
 
#endif
375
 
    if(x < 0 || x >= (int)_caca_width ||
376
 
       y < 0 || y >= (int)_caca_height)
377
 
        return;
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_char(' ');
387
 
        else
388
 
#endif
389
 
            SLsmg_write_char(c);
390
 
        break;
391
 
#endif
392
 
#if defined(USE_NCURSES)
393
 
    case CACA_DRIVER_NCURSES:
394
 
        move(y, x);
395
 
        addch(c);
396
 
        break;
397
 
#endif
398
 
#if defined(USE_CONIO)
399
 
    case CACA_DRIVER_CONIO:
400
 
        data = conio_screen + 2 * (x + y * _caca_width);
401
 
        data[0] = c;
402
 
        data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
403
 
        break;
404
 
#endif
405
 
#if defined(USE_X11)
406
 
    case CACA_DRIVER_X11:
407
 
        x11_char[x + y * _caca_width] = c;
408
 
        x11_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
409
 
        break;
410
 
#endif
411
 
#if defined(USE_WIN32)
412
 
    case CACA_DRIVER_WIN32:
413
 
        win32_char[x + y * _caca_width] = c;
414
 
        win32_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
415
 
        break;
416
 
#endif
417
 
    default:
418
 
        break;
419
 
    }
420
 
}
421
 
 
422
 
/** \brief Print a string.
423
 
 *
424
 
 *  This function prints a string at the given coordinates, using the
425
 
 *  default foreground and background values. The coordinates may be outside
426
 
 *  the screen boundaries (eg. a negative Y coordinate) and the string will
427
 
 *  be cropped accordingly if it is too long.
428
 
 *
429
 
 *  \param x X coordinate.
430
 
 *  \param y Y coordinate.
431
 
 *  \param s The string to print.
432
 
 */
433
 
void caca_putstr(int x, int y, char const *s)
434
 
{
435
 
#if defined(USE_CONIO) | defined(USE_X11) | defined(USE_WIN32)
436
 
    char *charbuf;
437
 
#endif
438
 
#if defined(USE_X11) | defined(USE_WIN32)
439
 
    char *attrbuf;
440
 
#endif
441
 
    unsigned int len;
442
 
 
443
 
    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
444
 
        return;
445
 
 
446
 
    len = strlen(s);
447
 
 
448
 
    if(x < 0)
449
 
    {
450
 
        if(len < (unsigned int)-x)
451
 
            return;
452
 
        len -= -x;
453
 
        s += -x;
454
 
        x = 0;
455
 
    }
456
 
 
457
 
    if(x + len >= _caca_width)
458
 
    {
459
 
        len = _caca_width - x;
460
 
        memcpy(_caca_scratch_line, s, len);
461
 
        _caca_scratch_line[len] = '\0';
462
 
        s = _caca_scratch_line;
463
 
    }
464
 
 
465
 
    switch(_caca_driver)
466
 
    {
467
 
#if defined(USE_SLANG)
468
 
    case CACA_DRIVER_SLANG:
469
 
        SLsmg_gotorc(y, x);
470
 
#if defined(OPTIMISE_SLANG_PALETTE)
471
 
        if(_caca_fgisbg)
472
 
            SLsmg_write_string(_caca_empty_line + _caca_width - len);
473
 
        else
474
 
#endif
475
 
            SLsmg_write_string((char *)(intptr_t)s);
476
 
        break;
477
 
#endif
478
 
#if defined(USE_NCURSES)
479
 
    case CACA_DRIVER_NCURSES:
480
 
        move(y, x);
481
 
        addstr(s);
482
 
        break;
483
 
#endif
484
 
#if defined(USE_CONIO)
485
 
    case CACA_DRIVER_CONIO:
486
 
        charbuf = conio_screen + 2 * (x + y * _caca_width);
487
 
        while(*s)
488
 
        {
489
 
            *charbuf++ = *s++;
490
 
            *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
491
 
        }
492
 
        break;
493
 
#endif
494
 
#if defined(USE_X11)
495
 
    case CACA_DRIVER_X11:
496
 
        charbuf = x11_char + x + y * _caca_width;
497
 
        attrbuf = x11_attr + x + y * _caca_width;
498
 
        while(*s)
499
 
        {
500
 
            *charbuf++ = *s++;
501
 
            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
502
 
        }
503
 
        break;
504
 
#endif
505
 
#if defined(USE_WIN32)
506
 
    case CACA_DRIVER_WIN32:
507
 
        charbuf = win32_char + x + y * _caca_width;
508
 
        attrbuf = win32_attr + x + y * _caca_width;
509
 
        while(*s)
510
 
        {
511
 
            *charbuf++ = *s++;
512
 
            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
513
 
        }
514
 
        break;
515
 
#endif
516
 
    default:
517
 
        break;
518
 
    }
519
 
}
520
 
 
521
 
/** \brief Format a string.
522
 
 *
523
 
 *  This function formats a string at the given coordinates, using the
524
 
 *  default foreground and background values. The coordinates may be outside
525
 
 *  the screen boundaries (eg. a negative Y coordinate) and the string will
526
 
 *  be cropped accordingly if it is too long. The syntax of the format
527
 
 *  string is the same as for the C printf() function.
528
 
 *
529
 
 *  \param x X coordinate.
530
 
 *  \param y Y coordinate.
531
 
 *  \param format The format string to print.
532
 
 *  \param ... Arguments to the format string.
533
 
 */
534
 
void caca_printf(int x, int y, char const *format, ...)
535
 
{
536
 
    char tmp[BUFSIZ];
537
 
    char *buf = tmp;
538
 
    va_list args;
539
 
 
540
 
    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
541
 
        return;
542
 
 
543
 
    if(_caca_width - x + 1 > BUFSIZ)
544
 
        buf = malloc(_caca_width - x + 1);
545
 
 
546
 
    va_start(args, format);
547
 
#if defined(HAVE_VSNPRINTF)
548
 
    vsnprintf(buf, _caca_width - x + 1, format, args);
549
 
#else
550
 
    vsprintf(buf, format, args);
551
 
#endif
552
 
    buf[_caca_width - x] = '\0';
553
 
    va_end(args);
554
 
 
555
 
    caca_putstr(x, y, buf);
556
 
 
557
 
    if(buf != tmp)
558
 
        free(buf);
559
 
}
560
 
 
561
 
/** \brief Clear the screen.
562
 
 *
563
 
 *  This function clears the screen using a black background.
564
 
 */
565
 
void caca_clear(void)
566
 
{
567
 
    enum caca_color oldfg = caca_get_fg_color();
568
 
    enum caca_color oldbg = caca_get_bg_color();
569
 
    int y = _caca_height;
570
 
 
571
 
    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
572
 
 
573
 
    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
574
 
    while(y--)
575
 
        caca_putstr(0, y, _caca_empty_line);
576
 
 
577
 
    caca_set_color(oldfg, oldbg);
578
 
}
579
 
 
580
 
#if !defined(_DOXYGEN_SKIP_ME)
581
 
int _caca_init_graphics(void)
582
 
{
583
 
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
584
 
    signal(SIGWINCH, sigwinch_handler);
585
 
#endif
586
 
 
587
 
#if defined(USE_SLANG)
588
 
    if(_caca_driver == CACA_DRIVER_SLANG)
589
 
    {
590
 
        slang_init_palette();
591
 
 
592
 
        /* Disable alt charset support so that we get a chance to have all
593
 
         * 256 colour pairs */
594
 
        SLtt_Has_Alt_Charset = 0;
595
 
 
596
 
        _caca_width = SLtt_Screen_Cols;
597
 
        _caca_height = SLtt_Screen_Rows;
598
 
    }
599
 
    else
600
 
#endif
601
 
#if defined(USE_NCURSES)
602
 
    if(_caca_driver == CACA_DRIVER_NCURSES)
603
 
    {
604
 
        static int curses_colors[] =
605
 
        {
606
 
            /* Standard curses colours */
607
 
            COLOR_BLACK,
608
 
            COLOR_BLUE,
609
 
            COLOR_GREEN,
610
 
            COLOR_CYAN,
611
 
            COLOR_RED,
612
 
            COLOR_MAGENTA,
613
 
            COLOR_YELLOW,
614
 
            COLOR_WHITE,
615
 
            /* Extra values for xterm-16color */
616
 
            COLOR_BLACK + 8,
617
 
            COLOR_BLUE + 8,
618
 
            COLOR_GREEN + 8,
619
 
            COLOR_CYAN + 8,
620
 
            COLOR_RED + 8,
621
 
            COLOR_MAGENTA + 8,
622
 
            COLOR_YELLOW + 8,
623
 
            COLOR_WHITE + 8
624
 
        };
625
 
 
626
 
        int fg, bg, max;
627
 
 
628
 
        /* Activate colour */
629
 
        start_color();
630
 
 
631
 
        /* If COLORS == 16, it means the terminal supports full bright colours
632
 
         * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
633
 
         * we can build 16*16 colour pairs.
634
 
         * If COLORS == 8, it means the terminal does not know about bright
635
 
         * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
636
 
         * and \e[5m). We can only build 8*8 colour pairs. */
637
 
        max = COLORS >= 16 ? 16 : 8;
638
 
 
639
 
        for(bg = 0; bg < max; bg++)
640
 
            for(fg = 0; fg < max; fg++)
641
 
            {
642
 
                /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
643
 
                 * is light gray on black, since some terminals don't like
644
 
                 * this colour pair to be redefined. */
645
 
                int col = ((max + 7 - fg) % max) + max * bg;
646
 
                init_pair(col, curses_colors[fg], curses_colors[bg]);
647
 
                ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
648
 
 
649
 
                if(max == 8)
650
 
                {
651
 
                    /* Bright fg on simple bg */
652
 
                    ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
653
 
                    /* Simple fg on bright bg */
654
 
                    ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
655
 
                                                        | COLOR_PAIR(col);
656
 
                    /* Bright fg on bright bg */
657
 
                    ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
658
 
                                                            | COLOR_PAIR(col);
659
 
                }
660
 
            }
661
 
 
662
 
        _caca_width = COLS;
663
 
        _caca_height = LINES;
664
 
    }
665
 
    else
666
 
#endif
667
 
#if defined(USE_CONIO)
668
 
    if(_caca_driver == CACA_DRIVER_CONIO)
669
 
    {
670
 
        gettextinfo(&conio_ti);
671
 
        conio_screen = malloc(2 * conio_ti.screenwidth
672
 
                                 * conio_ti.screenheight * sizeof(char));
673
 
        if(conio_screen == NULL)
674
 
            return -1;
675
 
#   if defined(SCREENUPDATE_IN_PC_H)
676
 
        ScreenRetrieve(conio_screen);
677
 
#   else
678
 
        /* FIXME */
679
 
#   endif
680
 
        _caca_width = conio_ti.screenwidth;
681
 
        _caca_height = conio_ti.screenheight;
682
 
    }
683
 
    else
684
 
#endif
685
 
#if defined(USE_X11)
686
 
    if(_caca_driver == CACA_DRIVER_X11)
687
 
    {
688
 
        static int x11_palette[] =
689
 
        {
690
 
            /* Standard curses colours */
691
 
            0x0,    0x0,    0x0,
692
 
            0x0,    0x0,    0x8000,
693
 
            0x0,    0x8000, 0x0,
694
 
            0x0,    0x8000, 0x8000,
695
 
            0x8000, 0x0,    0x0,
696
 
            0x8000, 0x0,    0x8000,
697
 
            0x8000, 0x8000, 0x0,
698
 
            0x8000, 0x8000, 0x8000,
699
 
            /* Extra values for xterm-16color */
700
 
            0x4000, 0x4000, 0x4000,
701
 
            0x4000, 0x4000, 0xffff,
702
 
            0x4000, 0xffff, 0x4000,
703
 
            0x4000, 0xffff, 0xffff,
704
 
            0xffff, 0x4000, 0x4000,
705
 
            0xffff, 0x4000, 0xffff,
706
 
            0xffff, 0xffff, 0x4000,
707
 
            0xffff, 0xffff, 0xffff,
708
 
        };
709
 
 
710
 
        Colormap colormap;
711
 
        XSetWindowAttributes x11_winattr;
712
 
        int (*old_error_handler)(Display *, XErrorEvent *);
713
 
        char const *font_name = "8x13bold";
714
 
        int i;
715
 
 
716
 
        if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
717
 
            sscanf(getenv("CACA_GEOMETRY"),
718
 
                   "%ux%u", &_caca_width, &_caca_height);
719
 
 
720
 
        if(!_caca_width)
721
 
            _caca_width = 80;
722
 
        if(!_caca_height)
723
 
            _caca_height = 32;
724
 
 
725
 
        x11_char = malloc(_caca_width * _caca_height * sizeof(int));
726
 
        if(x11_char == NULL)
727
 
            return -1;
728
 
 
729
 
        x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
730
 
        if(x11_attr == NULL)
731
 
        {
732
 
            free(x11_char);
733
 
            return -1;
734
 
        }
735
 
 
736
 
        memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
737
 
        memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
738
 
 
739
 
        x11_dpy = XOpenDisplay(NULL);
740
 
        if(x11_dpy == NULL)
741
 
        {
742
 
            free(x11_char);
743
 
            free(x11_attr);
744
 
            return -1;
745
 
        }
746
 
 
747
 
        if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
748
 
            font_name = getenv("CACA_FONT");
749
 
 
750
 
        /* Ignore font errors */
751
 
        old_error_handler = XSetErrorHandler(x11_error_handler);
752
 
 
753
 
        x11_font = XLoadFont(x11_dpy, font_name);
754
 
        if(!x11_font)
755
 
        {
756
 
            XCloseDisplay(x11_dpy);
757
 
            free(x11_char);
758
 
            free(x11_attr);
759
 
            return -1;
760
 
        }
761
 
 
762
 
        x11_font_struct = XQueryFont(x11_dpy, x11_font);
763
 
        if(!x11_font_struct)
764
 
        {
765
 
            XUnloadFont(x11_dpy, x11_font);
766
 
            XCloseDisplay(x11_dpy);
767
 
            free(x11_char);
768
 
            free(x11_attr);
769
 
            return -1;
770
 
        }
771
 
 
772
 
        /* Reset the default X11 error handler */
773
 
        XSetErrorHandler(old_error_handler);
774
 
 
775
 
        x11_font_width = x11_font_struct->max_bounds.width;
776
 
        x11_font_height = x11_font_struct->max_bounds.ascent
777
 
                             + x11_font_struct->max_bounds.descent;
778
 
        x11_font_offset = x11_font_struct->max_bounds.descent;
779
 
 
780
 
        colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
781
 
        for(i = 0; i < 16; i++)
782
 
        {
783
 
            XColor color;
784
 
            color.red = x11_palette[i * 3];
785
 
            color.green = x11_palette[i * 3 + 1];
786
 
            color.blue = x11_palette[i * 3 + 2];
787
 
            XAllocColor(x11_dpy, colormap, &color);
788
 
            x11_colors[i] = color.pixel;
789
 
        }
790
 
 
791
 
        x11_winattr.backing_store = Always;
792
 
        x11_winattr.background_pixel = x11_colors[0];
793
 
        x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
794
 
 
795
 
        x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
796
 
                                   _caca_width * x11_font_width,
797
 
                                   _caca_height * x11_font_height,
798
 
                                   0, 0, InputOutput, 0,
799
 
                                   CWBackingStore | CWBackPixel | CWEventMask,
800
 
                                   &x11_winattr);
801
 
 
802
 
        XStoreName(x11_dpy, x11_window, "caca for X");
803
 
 
804
 
        XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
805
 
        XMapWindow(x11_dpy, x11_window);
806
 
 
807
 
        x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
808
 
        XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
809
 
        XSetFont(x11_dpy, x11_gc, x11_font);
810
 
 
811
 
        for(;;)
812
 
        {
813
 
            XEvent event;
814
 
            XNextEvent(x11_dpy, &event);
815
 
            if (event.type == MapNotify)
816
 
                break;
817
 
        }
818
 
 
819
 
        /* Disable autorepeat */
820
 
#if defined(HAVE_X11_XKBLIB_H)
821
 
        XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
822
 
        if(!x11_detect_autorepeat)
823
 
            XAutoRepeatOff(x11_dpy);
824
 
#endif
825
 
 
826
 
        XSelectInput(x11_dpy, x11_window, x11_event_mask);
827
 
 
828
 
        XSync(x11_dpy, False);
829
 
 
830
 
        x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
831
 
                                   _caca_width * x11_font_width,
832
 
                                   _caca_height * x11_font_height,
833
 
                                   DefaultDepth(x11_dpy,
834
 
                                                DefaultScreen(x11_dpy)));
835
 
 
836
 
        x11_new_width = x11_new_height = 0;
837
 
    }
838
 
    else
839
 
#endif
840
 
#if defined(USE_WIN32)
841
 
    if(_caca_driver == CACA_DRIVER_WIN32)
842
 
    {
843
 
        CONSOLE_CURSOR_INFO cci;
844
 
        CONSOLE_SCREEN_BUFFER_INFO csbi;
845
 
        COORD size;
846
 
 
847
 
        win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
848
 
                                                0, NULL,
849
 
                                                CONSOLE_TEXTMODE_BUFFER, NULL);
850
 
        if(!win32_front || win32_front == INVALID_HANDLE_VALUE)
851
 
            return -1;
852
 
 
853
 
        win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
854
 
                                               0, NULL,
855
 
                                               CONSOLE_TEXTMODE_BUFFER, NULL);
856
 
        if(!win32_back || win32_back == INVALID_HANDLE_VALUE)
857
 
            return -1;
858
 
 
859
 
        if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
860
 
            return -1;
861
 
 
862
 
        /* Sample code to get the biggest possible window */
863
 
        //size = GetLargestConsoleWindowSize(win32_hout);
864
 
 
865
 
        _caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
866
 
        _caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
867
 
 
868
 
        size.X = _caca_width;
869
 
        size.Y = _caca_height;
870
 
        SetConsoleScreenBufferSize(win32_front, size);
871
 
        SetConsoleScreenBufferSize(win32_back, size);
872
 
 
873
 
        SetConsoleMode(win32_front, 0);
874
 
        SetConsoleMode(win32_back, 0);
875
 
 
876
 
        GetConsoleCursorInfo(win32_front, &cci);
877
 
        cci.dwSize = 0;
878
 
        cci.bVisible = FALSE;
879
 
        SetConsoleCursorInfo(win32_front, &cci);
880
 
        SetConsoleCursorInfo(win32_back, &cci);
881
 
 
882
 
        SetConsoleActiveScreenBuffer(win32_front);
883
 
 
884
 
        win32_char = malloc(_caca_width * _caca_height * sizeof(int));
885
 
        if(win32_char == NULL)
886
 
            return -1;
887
 
 
888
 
        win32_attr = malloc(_caca_width * _caca_height * sizeof(int));
889
 
        if(win32_attr == NULL)
890
 
        {
891
 
            free(win32_char);
892
 
            return -1;
893
 
        }
894
 
 
895
 
        win32_buffer = malloc(_caca_width * _caca_height * sizeof(CHAR_INFO));
896
 
        if(win32_buffer == NULL)
897
 
        {
898
 
            free(win32_attr);
899
 
            free(win32_char);
900
 
            return -1;
901
 
        }
902
 
 
903
 
        memset(win32_char, 0, _caca_width * _caca_height * sizeof(int));
904
 
        memset(win32_attr, 0, _caca_width * _caca_height * sizeof(int));
905
 
    }
906
 
    else
907
 
#endif
908
 
    {
909
 
        /* Dummy */
910
 
    }
911
 
 
912
 
    _caca_empty_line = malloc(_caca_width + 1);
913
 
    memset(_caca_empty_line, ' ', _caca_width);
914
 
    _caca_empty_line[_caca_width] = '\0';
915
 
 
916
 
    _caca_scratch_line = malloc(_caca_width + 1);
917
 
 
918
 
    _caca_delay = 0;
919
 
    _caca_rendertime = 0;
920
 
 
921
 
    return 0;
922
 
}
923
 
 
924
 
int _caca_end_graphics(void)
925
 
{
926
 
#if defined(USE_SLANG)
927
 
    /* Nothing to do */
928
 
#endif
929
 
#if defined(USE_NCURSES)
930
 
    /* Nothing to do */
931
 
#endif
932
 
#if defined(USE_CONIO)
933
 
    if(_caca_driver == CACA_DRIVER_CONIO)
934
 
    {
935
 
        free(conio_screen);
936
 
    }
937
 
    else
938
 
#endif
939
 
#if defined(USE_X11)
940
 
    if(_caca_driver == CACA_DRIVER_X11)
941
 
    {
942
 
        XSync(x11_dpy, False);
943
 
#if defined(HAVE_X11_XKBLIB_H)
944
 
        if(!x11_detect_autorepeat)
945
 
            XAutoRepeatOn(x11_dpy);
946
 
#endif
947
 
        XFreePixmap(x11_dpy, x11_pixmap);
948
 
        XFreeFont(x11_dpy, x11_font_struct);
949
 
        XFreeGC(x11_dpy, x11_gc);
950
 
        XUnmapWindow(x11_dpy, x11_window);
951
 
        XDestroyWindow(x11_dpy, x11_window);
952
 
        XCloseDisplay(x11_dpy);
953
 
        free(x11_char);
954
 
        free(x11_attr);
955
 
    }
956
 
    else
957
 
#endif
958
 
#if defined(USE_WIN32)
959
 
    if(_caca_driver == CACA_DRIVER_WIN32)
960
 
    {
961
 
        SetConsoleActiveScreenBuffer(win32_hout);
962
 
        CloseHandle(win32_back);
963
 
        CloseHandle(win32_front);
964
 
        free(win32_char);
965
 
        free(win32_attr);
966
 
    }
967
 
    else
968
 
#endif
969
 
    {
970
 
        /* Dummy */
971
 
    }
972
 
 
973
 
    free(_caca_empty_line);
974
 
 
975
 
    return 0;
976
 
}
977
 
#endif /* _DOXYGEN_SKIP_ME */
978
 
 
979
 
/** \brief Set the window title.
980
 
 *
981
 
 *  If libcaca runs in a window, try to change its title. This works with
982
 
 *  the X11 and Win32 drivers.
983
 
 *
984
 
 *  \param title The desired window title.
985
 
 *  \return 0 upon success, a non-zero value if an error occurs.
986
 
 */
987
 
int caca_set_window_title(char const *title)
988
 
{
989
 
#if defined(USE_X11)
990
 
    if(_caca_driver == CACA_DRIVER_X11)
991
 
    {
992
 
        XStoreName(x11_dpy, x11_window, title);
993
 
    }
994
 
    else
995
 
#endif
996
 
#if defined(USE_WIN32)
997
 
    if(_caca_driver == CACA_DRIVER_WIN32)
998
 
    {
999
 
        SetConsoleTitle(title);
1000
 
    }
1001
 
    else
1002
 
#endif
1003
 
    {
1004
 
        /* Not supported */
1005
 
        return -1;
1006
 
    }
1007
 
 
1008
 
    return 0;
1009
 
}
1010
 
 
1011
 
/** \brief Get the window width.
1012
 
 *
1013
 
 *  If libcaca runs in a window, get the usable window width. This value can
1014
 
 *  be used for aspect ratio calculation. If libcaca does not run in a window
1015
 
 *  or if there is no way to know the font size, assume a 6x10 font is being
1016
 
 *  used. Note that the units are not necessarily pixels.
1017
 
 *
1018
 
 *  \return The window width.
1019
 
 */
1020
 
unsigned int caca_get_window_width(void)
1021
 
{
1022
 
#if defined(USE_X11)
1023
 
    if(_caca_driver == CACA_DRIVER_X11)
1024
 
    {
1025
 
        return _caca_width * x11_font_width;
1026
 
    }
1027
 
    else
1028
 
#endif
1029
 
#if defined(USE_WIN32)
1030
 
    if(_caca_driver == CACA_DRIVER_WIN32)
1031
 
    {
1032
 
        /* FIXME */
1033
 
    }
1034
 
    else
1035
 
#endif
1036
 
    {
1037
 
        /* Dummy */
1038
 
    }
1039
 
 
1040
 
    /* Fallback to a 6x10 font */
1041
 
    return _caca_width * 6;
1042
 
}
1043
 
 
1044
 
/** \brief Get the window height.
1045
 
 *
1046
 
 *  If libcaca runs in a window, get the usable window height. This value can
1047
 
 *  be used for aspect ratio calculation. If libcaca does not run in a window
1048
 
 *  or if there is no way to know the font size, assume a 6x10 font is being
1049
 
 *  used. Note that the units are not necessarily pixels.
1050
 
 *
1051
 
 *  \return The window height.
1052
 
 */
1053
 
unsigned int caca_get_window_height(void)
1054
 
{
1055
 
#if defined(USE_X11)
1056
 
    if(_caca_driver == CACA_DRIVER_X11)
1057
 
    {
1058
 
        return _caca_height * x11_font_height;
1059
 
    }
1060
 
    else
1061
 
#endif
1062
 
#if defined(USE_WIN32)
1063
 
    if(_caca_driver == CACA_DRIVER_WIN32)
1064
 
    {
1065
 
        /* FIXME */
1066
 
    }
1067
 
    else
1068
 
#endif
1069
 
    {
1070
 
        /* Dummy */
1071
 
    }
1072
 
 
1073
 
    /* Fallback to a 6x10 font */
1074
 
    return _caca_height * 10;
1075
 
}
1076
 
 
1077
 
/** \brief Set the refresh delay.
1078
 
 *
1079
 
 *  This function sets the refresh delay in microseconds. The refresh delay
1080
 
 *  is used by caca_refresh() to achieve constant framerate. See the
1081
 
 *  caca_refresh() documentation for more details.
1082
 
 *
1083
 
 *  If the argument is zero, constant framerate is disabled. This is the
1084
 
 *  default behaviour.
1085
 
 *
1086
 
 *  \param usec The refresh delay in microseconds.
1087
 
 */
1088
 
void caca_set_delay(unsigned int usec)
1089
 
{
1090
 
    _caca_delay = usec;
1091
 
}
1092
 
 
1093
 
/** \brief Get the average rendering time.
1094
 
 *
1095
 
 *  This function returns the average rendering time, which is the average
1096
 
 *  measured time between two caca_refresh() calls, in microseconds. If
1097
 
 *  constant framerate was activated by calling caca_set_delay(), the average
1098
 
 *  rendering time will not be considerably shorter than the requested delay
1099
 
 *  even if the real rendering time was shorter.
1100
 
 *
1101
 
 *  \return The render time in microseconds.
1102
 
 */
1103
 
unsigned int caca_get_rendertime(void)
1104
 
{
1105
 
    return _caca_rendertime;
1106
 
}
1107
 
 
1108
 
/** \brief Flush pending changes and redraw the screen.
1109
 
 *
1110
 
 *  This function flushes all graphical operations and prints them to the
1111
 
 *  screen. Nothing will show on the screen until caca_refresh() is
1112
 
 *  called.
1113
 
 *
1114
 
 *  If caca_set_delay() was called with a non-zero value, caca_refresh()
1115
 
 *  will use that value to achieve constant framerate: if two consecutive
1116
 
 *  calls to caca_refresh() are within a time range shorter than the value
1117
 
 *  set with caca_set_delay(), the second call will wait a bit before
1118
 
 *  performing the screen refresh.
1119
 
 */
1120
 
void caca_refresh(void)
1121
 
{
1122
 
#if !defined(_DOXYGEN_SKIP_ME)
1123
 
#define IDLE_USEC 10000
1124
 
#endif
1125
 
    static struct caca_timer timer = CACA_TIMER_INITIALIZER;
1126
 
    static int lastticks = 0;
1127
 
    int ticks = lastticks + _caca_getticks(&timer);
1128
 
 
1129
 
#if defined(USE_SLANG)
1130
 
    if(_caca_driver == CACA_DRIVER_SLANG)
1131
 
    {
1132
 
        SLsmg_refresh();
1133
 
    }
1134
 
    else
1135
 
#endif
1136
 
#if defined(USE_NCURSES)
1137
 
    if(_caca_driver == CACA_DRIVER_NCURSES)
1138
 
    {
1139
 
        refresh();
1140
 
    }
1141
 
    else
1142
 
#endif
1143
 
#if defined(USE_CONIO)
1144
 
    if(_caca_driver == CACA_DRIVER_CONIO)
1145
 
    {
1146
 
#   if defined(SCREENUPDATE_IN_PC_H)
1147
 
        ScreenUpdate(conio_screen);
1148
 
#   else
1149
 
        /* FIXME */
1150
 
#   endif
1151
 
    }
1152
 
    else
1153
 
#endif
1154
 
#if defined(USE_X11)
1155
 
    if(_caca_driver == CACA_DRIVER_X11)
1156
 
    {
1157
 
        unsigned int x, y, len;
1158
 
 
1159
 
        /* First draw the background colours. Splitting the process in two
1160
 
         * loops like this is actually slightly faster. */
1161
 
        for(y = 0; y < _caca_height; y++)
1162
 
        {
1163
 
            for(x = 0; x < _caca_width; x += len)
1164
 
            {
1165
 
                unsigned char *attr = x11_attr + x + y * _caca_width;
1166
 
 
1167
 
                len = 1;
1168
 
                while(x + len < _caca_width
1169
 
                       && (attr[len] >> 4) == (attr[0] >> 4))
1170
 
                    len++;
1171
 
 
1172
 
                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
1173
 
                XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
1174
 
                               x * x11_font_width, y * x11_font_height,
1175
 
                               len * x11_font_width, x11_font_height);
1176
 
            }
1177
 
        }
1178
 
 
1179
 
        /* Then print the foreground characters */
1180
 
        for(y = 0; y < _caca_height; y++)
1181
 
        {
1182
 
            for(x = 0; x < _caca_width; x += len)
1183
 
            {
1184
 
                unsigned char *attr = x11_attr + x + y * _caca_width;
1185
 
 
1186
 
                len = 1;
1187
 
 
1188
 
                /* Skip spaces */
1189
 
                if(x11_char[x + y * _caca_width] == ' ')
1190
 
                    continue;
1191
 
 
1192
 
                while(x + len < _caca_width
1193
 
                       && (attr[len] & 0xf) == (attr[0] & 0xf))
1194
 
                    len++;
1195
 
 
1196
 
                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
1197
 
                XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
1198
 
                            (y + 1) * x11_font_height - x11_font_offset,
1199
 
                            x11_char + x + y * _caca_width, len);
1200
 
            }
1201
 
        }
1202
 
 
1203
 
        XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
1204
 
                  _caca_width * x11_font_width, _caca_height * x11_font_height,
1205
 
                  0, 0);
1206
 
        XFlush(x11_dpy);
1207
 
    }
1208
 
    else
1209
 
#endif
1210
 
#if defined(USE_WIN32)
1211
 
    if(_caca_driver == CACA_DRIVER_WIN32)
1212
 
    {
1213
 
        COORD size, pos;
1214
 
        SMALL_RECT rect;
1215
 
        DWORD dummy;
1216
 
        unsigned int x, y, len;
1217
 
 
1218
 
        /* Render everything to our back buffer */
1219
 
        for(y = 0; y < _caca_height; y++)
1220
 
        {
1221
 
            pos.X = 0;
1222
 
            pos.Y = y;
1223
 
            SetConsoleCursorPosition(win32_back, pos);
1224
 
 
1225
 
            for(x = 0; x < _caca_width; x += len)
1226
 
            {
1227
 
                unsigned char *attr = win32_attr + x + y * _caca_width;
1228
 
 
1229
 
                len = 1;
1230
 
                while(x + len < _caca_width && attr[len] == attr[0])
1231
 
                    len++;
1232
 
 
1233
 
                SetConsoleTextAttribute(win32_back,
1234
 
                                        win32_fg_palette[attr[0] & 0xf]
1235
 
                                         | win32_bg_palette[attr[0] >> 4]);
1236
 
 
1237
 
                WriteConsole(win32_back, win32_char + x + y * _caca_width,
1238
 
                             len, &dummy, NULL);
1239
 
            }
1240
 
        }
1241
 
 
1242
 
        /* Blit the back buffer to the front buffer */
1243
 
        size.X = _caca_width;
1244
 
        size.Y = _caca_height;
1245
 
        pos.X = pos.Y = 0;
1246
 
        rect.Left = rect.Top = 0;
1247
 
        rect.Right = _caca_width - 1;
1248
 
        rect.Bottom = _caca_height - 1;
1249
 
        ReadConsoleOutput(win32_back, win32_buffer, size, pos, &rect);
1250
 
        WriteConsoleOutput(win32_front, win32_buffer, size, pos, &rect);
1251
 
    }
1252
 
    else
1253
 
#endif
1254
 
    {
1255
 
        /* Dummy */
1256
 
    }
1257
 
 
1258
 
    if(_caca_resize)
1259
 
    {
1260
 
        _caca_resize = 0;
1261
 
        caca_handle_resize();
1262
 
    }
1263
 
 
1264
 
    /* Wait until _caca_delay + time of last call */
1265
 
    ticks += _caca_getticks(&timer);
1266
 
    for(ticks += _caca_getticks(&timer);
1267
 
        ticks + IDLE_USEC < (int)_caca_delay;
1268
 
        ticks += _caca_getticks(&timer))
1269
 
    {
1270
 
        _caca_sleep(IDLE_USEC);
1271
 
    }
1272
 
 
1273
 
    /* Update the sliding mean of the render time */
1274
 
    _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
1275
 
 
1276
 
    lastticks = ticks - _caca_delay;
1277
 
 
1278
 
    /* If we drifted too much, it's bad, bad, bad. */
1279
 
    if(lastticks > (int)_caca_delay)
1280
 
        lastticks = 0;
1281
 
}
1282
 
 
1283
 
/*
1284
 
 * XXX: following functions are loca
1285
 
 */
1286
 
static void caca_handle_resize(void)
1287
 
{
1288
 
    unsigned int old_width = _caca_width;
1289
 
    unsigned int old_height = _caca_height;
1290
 
 
1291
 
#if defined(USE_SLANG)
1292
 
    if(_caca_driver == CACA_DRIVER_SLANG)
1293
 
    {
1294
 
        SLtt_get_screen_size();
1295
 
        _caca_width = SLtt_Screen_Cols;
1296
 
        _caca_height = SLtt_Screen_Rows;
1297
 
 
1298
 
        if(_caca_width != old_width || _caca_height != old_height)
1299
 
            SLsmg_reinit_smg();
1300
 
    }
1301
 
    else
1302
 
#endif
1303
 
#if defined(USE_NCURSES)
1304
 
    if(_caca_driver == CACA_DRIVER_NCURSES)
1305
 
    {
1306
 
        struct winsize size;
1307
 
 
1308
 
        if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
1309
 
        {
1310
 
            _caca_width = size.ws_col;
1311
 
            _caca_height = size.ws_row;
1312
 
            resize_term(_caca_height, _caca_width);
1313
 
            wrefresh(curscr);
1314
 
        }
1315
 
    }
1316
 
    else
1317
 
#endif
1318
 
#if defined(USE_CONIO)
1319
 
    if(_caca_driver == CACA_DRIVER_CONIO)
1320
 
    {
1321
 
    }
1322
 
    else
1323
 
#endif
1324
 
#if defined(USE_X11)
1325
 
    if(_caca_driver == CACA_DRIVER_X11)
1326
 
    {
1327
 
        Pixmap new_pixmap;
1328
 
 
1329
 
        _caca_width = x11_new_width;
1330
 
        _caca_height = x11_new_height;
1331
 
 
1332
 
        free(x11_char);
1333
 
        free(x11_attr);
1334
 
 
1335
 
        new_pixmap = XCreatePixmap(x11_dpy, x11_window,
1336
 
                                   _caca_width * x11_font_width,
1337
 
                                   _caca_height * x11_font_height,
1338
 
                                   DefaultDepth(x11_dpy,
1339
 
                                                DefaultScreen(x11_dpy)));
1340
 
        XCopyArea(x11_dpy, x11_pixmap, new_pixmap, x11_gc, 0, 0,
1341
 
                  old_width * x11_font_width, old_height * x11_font_height,
1342
 
                  0, 0);
1343
 
        XFreePixmap(x11_dpy, x11_pixmap);
1344
 
        x11_pixmap = new_pixmap;
1345
 
 
1346
 
        x11_char = malloc(_caca_width * _caca_height * sizeof(int));
1347
 
        memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
1348
 
        x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
1349
 
        memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
1350
 
    }
1351
 
    else
1352
 
#endif
1353
 
#if defined(USE_WIN32)
1354
 
    if(_caca_driver == CACA_DRIVER_WIN32)
1355
 
    {
1356
 
    }
1357
 
    else
1358
 
#endif
1359
 
    {
1360
 
        /* Dummy */
1361
 
    }
1362
 
 
1363
 
    if(_caca_width != old_width)
1364
 
    {
1365
 
        free(_caca_empty_line);
1366
 
        _caca_empty_line = malloc(_caca_width + 1);
1367
 
        memset(_caca_empty_line, ' ', _caca_width);
1368
 
        _caca_empty_line[_caca_width] = '\0';
1369
 
 
1370
 
        free(_caca_scratch_line);
1371
 
        _caca_scratch_line = malloc(_caca_width + 1);
1372
 
    }
1373
 
}
1374
 
 
1375
 
#if defined(USE_SLANG)
1376
 
static void slang_init_palette(void)
1377
 
{
1378
 
    /* See SLang ref., 5.4.4. */
1379
 
    static char *slang_colors[16] =
1380
 
    {
1381
 
        /* Standard colours */
1382
 
        "black",
1383
 
        "blue",
1384
 
        "green",
1385
 
        "cyan",
1386
 
        "red",
1387
 
        "magenta",
1388
 
        "brown",
1389
 
        "lightgray",
1390
 
        /* Bright colours */
1391
 
        "gray",
1392
 
        "brightblue",
1393
 
        "brightgreen",
1394
 
        "brightcyan",
1395
 
        "brightred",
1396
 
        "brightmagenta",
1397
 
        "yellow",
1398
 
        "white",
1399
 
    };
1400
 
 
1401
 
#if defined(OPTIMISE_SLANG_PALETTE)
1402
 
    int i;
1403
 
 
1404
 
    for(i = 0; i < 16 * 16; i++)
1405
 
        SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
1406
 
                                slang_colors[slang_palette[i * 2 + 1]]);
1407
 
#else
1408
 
    int fg, bg;
1409
 
 
1410
 
    for(bg = 0; bg < 16; bg++)
1411
 
        for(fg = 0; fg < 16; fg++)
1412
 
        {
1413
 
            int i = fg + 16 * bg;
1414
 
            SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
1415
 
        }
1416
 
#endif
1417
 
}
1418
 
#endif /* USE_SLANG */
1419
 
 
1420
 
#if defined(USE_X11)
1421
 
static int x11_error_handler(Display *dpy, XErrorEvent *event)
1422
 
{
1423
 
    /* Ignore the error */
1424
 
    return 0;
1425
 
}
1426
 
#endif
1427
 
 
1428
 
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
1429
 
static RETSIGTYPE sigwinch_handler(int sig)
1430
 
{
1431
 
    _caca_resize_event = 1;
1432
 
 
1433
 
    signal(SIGWINCH, sigwinch_handler);;
1434
 
}
1435
 
#endif
1436