2
* libcaca ASCII-Art library
3
* Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
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.
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.
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
23
* \version \$Id: graphics.c 258 2004-01-26 10:52:30Z sam $
24
* \author Sam Hocevar <sam@zoy.org>
25
* \brief Character drawing
27
* This file contains character and string drawing functions.
32
#if defined(USE_SLANG)
33
# if defined(HAVE_SLANG_SLANG_H)
34
# include <slang/slang.h>
39
#if defined(USE_NCURSES)
40
# if defined(HAVE_NCURSES_H)
46
#if defined(USE_CONIO)
48
# if defined(SCREENUPDATE_IN_PC_H)
53
# include <X11/Xlib.h>
54
# if defined(HAVE_X11_XKBLIB_H)
55
# include <X11/XKBlib.h>
58
#if defined(USE_WIN32)
62
#if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
63
# include <inttypes.h>
65
typedef unsigned char uint8_t;
68
#include <stdio.h> /* BUFSIZ */
71
#if defined(HAVE_UNISTD_H)
76
#if defined(HAVE_SIGNAL_H)
79
#if defined(HAVE_SYS_IOCTL_H)
80
# include <sys/ioctl.h>
84
#include "caca_internals.h"
89
#if !defined(_DOXYGEN_SKIP_ME)
90
unsigned int _caca_width = 0;
91
unsigned int _caca_height = 0;
93
int _caca_resize_event = 0;
99
#if defined(USE_NCURSES)
100
static int ncurses_attr[16*16];
103
#if defined(USE_SLANG)
104
/* Tables generated by test/optipal.c */
105
static int const slang_palette[2*16*16] =
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,
141
static int const slang_assoc[16*16] =
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,
162
#if defined(USE_CONIO)
163
static struct text_info conio_ti;
164
static char *conio_screen;
167
#if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
172
long int x11_event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
173
| ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
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;
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;
193
static int const win32_fg_palette[] =
198
FOREGROUND_GREEN | FOREGROUND_BLUE,
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
213
static int const win32_bg_palette[] =
218
BACKGROUND_GREEN | BACKGROUND_BLUE,
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
234
static char *_caca_empty_line;
235
static char *_caca_scratch_line;
237
static unsigned int _caca_delay;
238
static unsigned int _caca_rendertime;
240
#if defined(OPTIMISE_SLANG_PALETTE)
241
static int _caca_fgisbg = 0;
243
static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
244
static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
249
static void caca_handle_resize(void);
251
#if defined(USE_SLANG)
252
static void slang_init_palette(void);
255
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
256
static RETSIGTYPE sigwinch_handler(int);
260
static int x11_error_handler(Display *, XErrorEvent *);
263
/** \brief Set the default colour pair.
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.
269
* \param fgcolor The requested foreground colour.
270
* \param bgcolor The requested background colour.
272
void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
274
if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
277
_caca_fgcolor = fgcolor;
278
_caca_bgcolor = bgcolor;
282
#if defined(USE_SLANG)
283
case CACA_DRIVER_SLANG:
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)
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;
299
fgcolor = CACA_COLOR_WHITE;
303
#if defined(OPTIMISE_SLANG_PALETTE)
304
SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
306
SLsmg_set_color(fgcolor + 16 * bgcolor);
310
#if defined(USE_NCURSES)
311
case CACA_DRIVER_NCURSES:
312
attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
315
#if defined(USE_CONIO)
316
case CACA_DRIVER_CONIO:
317
textbackground(bgcolor);
322
case CACA_DRIVER_X11:
326
#if defined(USE_WIN32)
327
case CACA_DRIVER_WIN32:
336
/** \brief Get the current foreground colour.
338
* This function returns the current foreground colour that was set with
341
* \return The current foreground colour.
343
enum caca_color caca_get_fg_color(void)
345
return _caca_fgcolor;
348
/** \brief Get the current background colour.
350
* This function returns the current background colour that was set with
353
* \return The current background colour.
355
enum caca_color caca_get_bg_color(void)
357
return _caca_bgcolor;
360
/** \brief Print a character.
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.
366
* \param x X coordinate.
367
* \param y Y coordinate.
368
* \param c The character to print.
370
void caca_putchar(int x, int y, char c)
372
#if defined(USE_CONIO)
375
if(x < 0 || x >= (int)_caca_width ||
376
y < 0 || y >= (int)_caca_height)
381
#if defined(USE_SLANG)
382
case CACA_DRIVER_SLANG:
384
#if defined(OPTIMISE_SLANG_PALETTE)
386
SLsmg_write_char(' ');
392
#if defined(USE_NCURSES)
393
case CACA_DRIVER_NCURSES:
398
#if defined(USE_CONIO)
399
case CACA_DRIVER_CONIO:
400
data = conio_screen + 2 * (x + y * _caca_width);
402
data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
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;
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;
422
/** \brief Print a string.
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.
429
* \param x X coordinate.
430
* \param y Y coordinate.
431
* \param s The string to print.
433
void caca_putstr(int x, int y, char const *s)
435
#if defined(USE_CONIO) | defined(USE_X11) | defined(USE_WIN32)
438
#if defined(USE_X11) | defined(USE_WIN32)
443
if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
450
if(len < (unsigned int)-x)
457
if(x + len >= _caca_width)
459
len = _caca_width - x;
460
memcpy(_caca_scratch_line, s, len);
461
_caca_scratch_line[len] = '\0';
462
s = _caca_scratch_line;
467
#if defined(USE_SLANG)
468
case CACA_DRIVER_SLANG:
470
#if defined(OPTIMISE_SLANG_PALETTE)
472
SLsmg_write_string(_caca_empty_line + _caca_width - len);
475
SLsmg_write_string((char *)(intptr_t)s);
478
#if defined(USE_NCURSES)
479
case CACA_DRIVER_NCURSES:
484
#if defined(USE_CONIO)
485
case CACA_DRIVER_CONIO:
486
charbuf = conio_screen + 2 * (x + y * _caca_width);
490
*charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
495
case CACA_DRIVER_X11:
496
charbuf = x11_char + x + y * _caca_width;
497
attrbuf = x11_attr + x + y * _caca_width;
501
*attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
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;
512
*attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
521
/** \brief Format a string.
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.
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.
534
void caca_printf(int x, int y, char const *format, ...)
540
if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
543
if(_caca_width - x + 1 > BUFSIZ)
544
buf = malloc(_caca_width - x + 1);
546
va_start(args, format);
547
#if defined(HAVE_VSNPRINTF)
548
vsnprintf(buf, _caca_width - x + 1, format, args);
550
vsprintf(buf, format, args);
552
buf[_caca_width - x] = '\0';
555
caca_putstr(x, y, buf);
561
/** \brief Clear the screen.
563
* This function clears the screen using a black background.
565
void caca_clear(void)
567
enum caca_color oldfg = caca_get_fg_color();
568
enum caca_color oldbg = caca_get_bg_color();
569
int y = _caca_height;
571
caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
573
/* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
575
caca_putstr(0, y, _caca_empty_line);
577
caca_set_color(oldfg, oldbg);
580
#if !defined(_DOXYGEN_SKIP_ME)
581
int _caca_init_graphics(void)
583
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
584
signal(SIGWINCH, sigwinch_handler);
587
#if defined(USE_SLANG)
588
if(_caca_driver == CACA_DRIVER_SLANG)
590
slang_init_palette();
592
/* Disable alt charset support so that we get a chance to have all
593
* 256 colour pairs */
594
SLtt_Has_Alt_Charset = 0;
596
_caca_width = SLtt_Screen_Cols;
597
_caca_height = SLtt_Screen_Rows;
601
#if defined(USE_NCURSES)
602
if(_caca_driver == CACA_DRIVER_NCURSES)
604
static int curses_colors[] =
606
/* Standard curses colours */
615
/* Extra values for xterm-16color */
628
/* Activate colour */
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;
639
for(bg = 0; bg < max; bg++)
640
for(fg = 0; fg < max; fg++)
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);
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
656
/* Bright fg on bright bg */
657
ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
663
_caca_height = LINES;
667
#if defined(USE_CONIO)
668
if(_caca_driver == CACA_DRIVER_CONIO)
670
gettextinfo(&conio_ti);
671
conio_screen = malloc(2 * conio_ti.screenwidth
672
* conio_ti.screenheight * sizeof(char));
673
if(conio_screen == NULL)
675
# if defined(SCREENUPDATE_IN_PC_H)
676
ScreenRetrieve(conio_screen);
680
_caca_width = conio_ti.screenwidth;
681
_caca_height = conio_ti.screenheight;
686
if(_caca_driver == CACA_DRIVER_X11)
688
static int x11_palette[] =
690
/* Standard curses colours */
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,
711
XSetWindowAttributes x11_winattr;
712
int (*old_error_handler)(Display *, XErrorEvent *);
713
char const *font_name = "8x13bold";
716
if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
717
sscanf(getenv("CACA_GEOMETRY"),
718
"%ux%u", &_caca_width, &_caca_height);
725
x11_char = malloc(_caca_width * _caca_height * sizeof(int));
729
x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
736
memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
737
memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
739
x11_dpy = XOpenDisplay(NULL);
747
if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
748
font_name = getenv("CACA_FONT");
750
/* Ignore font errors */
751
old_error_handler = XSetErrorHandler(x11_error_handler);
753
x11_font = XLoadFont(x11_dpy, font_name);
756
XCloseDisplay(x11_dpy);
762
x11_font_struct = XQueryFont(x11_dpy, x11_font);
765
XUnloadFont(x11_dpy, x11_font);
766
XCloseDisplay(x11_dpy);
772
/* Reset the default X11 error handler */
773
XSetErrorHandler(old_error_handler);
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;
780
colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
781
for(i = 0; i < 16; i++)
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;
791
x11_winattr.backing_store = Always;
792
x11_winattr.background_pixel = x11_colors[0];
793
x11_winattr.event_mask = ExposureMask | StructureNotifyMask;
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,
802
XStoreName(x11_dpy, x11_window, "caca for X");
804
XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
805
XMapWindow(x11_dpy, x11_window);
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);
814
XNextEvent(x11_dpy, &event);
815
if (event.type == MapNotify)
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);
826
XSelectInput(x11_dpy, x11_window, x11_event_mask);
828
XSync(x11_dpy, False);
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)));
836
x11_new_width = x11_new_height = 0;
840
#if defined(USE_WIN32)
841
if(_caca_driver == CACA_DRIVER_WIN32)
843
CONSOLE_CURSOR_INFO cci;
844
CONSOLE_SCREEN_BUFFER_INFO csbi;
847
win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
849
CONSOLE_TEXTMODE_BUFFER, NULL);
850
if(!win32_front || win32_front == INVALID_HANDLE_VALUE)
853
win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
855
CONSOLE_TEXTMODE_BUFFER, NULL);
856
if(!win32_back || win32_back == INVALID_HANDLE_VALUE)
859
if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
862
/* Sample code to get the biggest possible window */
863
//size = GetLargestConsoleWindowSize(win32_hout);
865
_caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
866
_caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
868
size.X = _caca_width;
869
size.Y = _caca_height;
870
SetConsoleScreenBufferSize(win32_front, size);
871
SetConsoleScreenBufferSize(win32_back, size);
873
SetConsoleMode(win32_front, 0);
874
SetConsoleMode(win32_back, 0);
876
GetConsoleCursorInfo(win32_front, &cci);
878
cci.bVisible = FALSE;
879
SetConsoleCursorInfo(win32_front, &cci);
880
SetConsoleCursorInfo(win32_back, &cci);
882
SetConsoleActiveScreenBuffer(win32_front);
884
win32_char = malloc(_caca_width * _caca_height * sizeof(int));
885
if(win32_char == NULL)
888
win32_attr = malloc(_caca_width * _caca_height * sizeof(int));
889
if(win32_attr == NULL)
895
win32_buffer = malloc(_caca_width * _caca_height * sizeof(CHAR_INFO));
896
if(win32_buffer == NULL)
903
memset(win32_char, 0, _caca_width * _caca_height * sizeof(int));
904
memset(win32_attr, 0, _caca_width * _caca_height * sizeof(int));
912
_caca_empty_line = malloc(_caca_width + 1);
913
memset(_caca_empty_line, ' ', _caca_width);
914
_caca_empty_line[_caca_width] = '\0';
916
_caca_scratch_line = malloc(_caca_width + 1);
919
_caca_rendertime = 0;
924
int _caca_end_graphics(void)
926
#if defined(USE_SLANG)
929
#if defined(USE_NCURSES)
932
#if defined(USE_CONIO)
933
if(_caca_driver == CACA_DRIVER_CONIO)
940
if(_caca_driver == CACA_DRIVER_X11)
942
XSync(x11_dpy, False);
943
#if defined(HAVE_X11_XKBLIB_H)
944
if(!x11_detect_autorepeat)
945
XAutoRepeatOn(x11_dpy);
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);
958
#if defined(USE_WIN32)
959
if(_caca_driver == CACA_DRIVER_WIN32)
961
SetConsoleActiveScreenBuffer(win32_hout);
962
CloseHandle(win32_back);
963
CloseHandle(win32_front);
973
free(_caca_empty_line);
977
#endif /* _DOXYGEN_SKIP_ME */
979
/** \brief Set the window title.
981
* If libcaca runs in a window, try to change its title. This works with
982
* the X11 and Win32 drivers.
984
* \param title The desired window title.
985
* \return 0 upon success, a non-zero value if an error occurs.
987
int caca_set_window_title(char const *title)
990
if(_caca_driver == CACA_DRIVER_X11)
992
XStoreName(x11_dpy, x11_window, title);
996
#if defined(USE_WIN32)
997
if(_caca_driver == CACA_DRIVER_WIN32)
999
SetConsoleTitle(title);
1011
/** \brief Get the window width.
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.
1018
* \return The window width.
1020
unsigned int caca_get_window_width(void)
1022
#if defined(USE_X11)
1023
if(_caca_driver == CACA_DRIVER_X11)
1025
return _caca_width * x11_font_width;
1029
#if defined(USE_WIN32)
1030
if(_caca_driver == CACA_DRIVER_WIN32)
1040
/* Fallback to a 6x10 font */
1041
return _caca_width * 6;
1044
/** \brief Get the window height.
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.
1051
* \return The window height.
1053
unsigned int caca_get_window_height(void)
1055
#if defined(USE_X11)
1056
if(_caca_driver == CACA_DRIVER_X11)
1058
return _caca_height * x11_font_height;
1062
#if defined(USE_WIN32)
1063
if(_caca_driver == CACA_DRIVER_WIN32)
1073
/* Fallback to a 6x10 font */
1074
return _caca_height * 10;
1077
/** \brief Set the refresh delay.
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.
1083
* If the argument is zero, constant framerate is disabled. This is the
1084
* default behaviour.
1086
* \param usec The refresh delay in microseconds.
1088
void caca_set_delay(unsigned int usec)
1093
/** \brief Get the average rendering time.
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.
1101
* \return The render time in microseconds.
1103
unsigned int caca_get_rendertime(void)
1105
return _caca_rendertime;
1108
/** \brief Flush pending changes and redraw the screen.
1110
* This function flushes all graphical operations and prints them to the
1111
* screen. Nothing will show on the screen until caca_refresh() is
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.
1120
void caca_refresh(void)
1122
#if !defined(_DOXYGEN_SKIP_ME)
1123
#define IDLE_USEC 10000
1125
static struct caca_timer timer = CACA_TIMER_INITIALIZER;
1126
static int lastticks = 0;
1127
int ticks = lastticks + _caca_getticks(&timer);
1129
#if defined(USE_SLANG)
1130
if(_caca_driver == CACA_DRIVER_SLANG)
1136
#if defined(USE_NCURSES)
1137
if(_caca_driver == CACA_DRIVER_NCURSES)
1143
#if defined(USE_CONIO)
1144
if(_caca_driver == CACA_DRIVER_CONIO)
1146
# if defined(SCREENUPDATE_IN_PC_H)
1147
ScreenUpdate(conio_screen);
1154
#if defined(USE_X11)
1155
if(_caca_driver == CACA_DRIVER_X11)
1157
unsigned int x, y, len;
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++)
1163
for(x = 0; x < _caca_width; x += len)
1165
unsigned char *attr = x11_attr + x + y * _caca_width;
1168
while(x + len < _caca_width
1169
&& (attr[len] >> 4) == (attr[0] >> 4))
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);
1179
/* Then print the foreground characters */
1180
for(y = 0; y < _caca_height; y++)
1182
for(x = 0; x < _caca_width; x += len)
1184
unsigned char *attr = x11_attr + x + y * _caca_width;
1189
if(x11_char[x + y * _caca_width] == ' ')
1192
while(x + len < _caca_width
1193
&& (attr[len] & 0xf) == (attr[0] & 0xf))
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);
1203
XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
1204
_caca_width * x11_font_width, _caca_height * x11_font_height,
1210
#if defined(USE_WIN32)
1211
if(_caca_driver == CACA_DRIVER_WIN32)
1216
unsigned int x, y, len;
1218
/* Render everything to our back buffer */
1219
for(y = 0; y < _caca_height; y++)
1223
SetConsoleCursorPosition(win32_back, pos);
1225
for(x = 0; x < _caca_width; x += len)
1227
unsigned char *attr = win32_attr + x + y * _caca_width;
1230
while(x + len < _caca_width && attr[len] == attr[0])
1233
SetConsoleTextAttribute(win32_back,
1234
win32_fg_palette[attr[0] & 0xf]
1235
| win32_bg_palette[attr[0] >> 4]);
1237
WriteConsole(win32_back, win32_char + x + y * _caca_width,
1242
/* Blit the back buffer to the front buffer */
1243
size.X = _caca_width;
1244
size.Y = _caca_height;
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);
1261
caca_handle_resize();
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))
1270
_caca_sleep(IDLE_USEC);
1273
/* Update the sliding mean of the render time */
1274
_caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
1276
lastticks = ticks - _caca_delay;
1278
/* If we drifted too much, it's bad, bad, bad. */
1279
if(lastticks > (int)_caca_delay)
1284
* XXX: following functions are loca
1286
static void caca_handle_resize(void)
1288
unsigned int old_width = _caca_width;
1289
unsigned int old_height = _caca_height;
1291
#if defined(USE_SLANG)
1292
if(_caca_driver == CACA_DRIVER_SLANG)
1294
SLtt_get_screen_size();
1295
_caca_width = SLtt_Screen_Cols;
1296
_caca_height = SLtt_Screen_Rows;
1298
if(_caca_width != old_width || _caca_height != old_height)
1303
#if defined(USE_NCURSES)
1304
if(_caca_driver == CACA_DRIVER_NCURSES)
1306
struct winsize size;
1308
if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
1310
_caca_width = size.ws_col;
1311
_caca_height = size.ws_row;
1312
resize_term(_caca_height, _caca_width);
1318
#if defined(USE_CONIO)
1319
if(_caca_driver == CACA_DRIVER_CONIO)
1324
#if defined(USE_X11)
1325
if(_caca_driver == CACA_DRIVER_X11)
1329
_caca_width = x11_new_width;
1330
_caca_height = x11_new_height;
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,
1343
XFreePixmap(x11_dpy, x11_pixmap);
1344
x11_pixmap = new_pixmap;
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));
1353
#if defined(USE_WIN32)
1354
if(_caca_driver == CACA_DRIVER_WIN32)
1363
if(_caca_width != old_width)
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';
1370
free(_caca_scratch_line);
1371
_caca_scratch_line = malloc(_caca_width + 1);
1375
#if defined(USE_SLANG)
1376
static void slang_init_palette(void)
1378
/* See SLang ref., 5.4.4. */
1379
static char *slang_colors[16] =
1381
/* Standard colours */
1390
/* Bright colours */
1401
#if defined(OPTIMISE_SLANG_PALETTE)
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]]);
1410
for(bg = 0; bg < 16; bg++)
1411
for(fg = 0; fg < 16; fg++)
1413
int i = fg + 16 * bg;
1414
SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
1418
#endif /* USE_SLANG */
1420
#if defined(USE_X11)
1421
static int x11_error_handler(Display *dpy, XErrorEvent *event)
1423
/* Ignore the error */
1428
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
1429
static RETSIGTYPE sigwinch_handler(int sig)
1431
_caca_resize_event = 1;
1433
signal(SIGWINCH, sigwinch_handler);;