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

Last change on this file since 261 was 261, checked in by Sam Hocevar, 17 years ago
  • src/graphics.c: + Progress in the X11 driver. + Fixed a compilation bug in the conio driver.
  • Property svn:keywords set to Id
File size: 14.6 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 261 2003-12-19 17:26:23Z sam $
24 *   \author Sam Hocevar <sam@zoy.org>
25 *   \brief Character drawing functions
26 *
27 *   This file contains character and string drawing functions.
28 */
29
30#include "config.h"
31
32#if defined(USE_SLANG)
33#   include <slang.h>
34#elif defined(USE_NCURSES)
35#   include <curses.h>
36#elif defined(USE_CONIO)
37#   include <conio.h>
38#   if defined(SCREENUPDATE_IN_PC_H)
39#       include <pc.h>
40#   endif
41#elif defined(USE_X11)
42#   include <X11/Xlib.h>
43#else
44#   error "no graphics library detected"
45#endif
46
47#ifdef HAVE_INTTYPES_H
48#   include <inttypes.h>
49#endif
50
51#include <stdio.h> /* BUFSIZ */
52#include <string.h>
53#include <stdlib.h>
54#include <unistd.h>
55#include <stdarg.h>
56#include <sys/time.h>
57#include <time.h>
58
59#include "caca.h"
60#include "caca_internals.h"
61
62/*
63 * Global variables
64 */
65unsigned int _caca_width;
66unsigned int _caca_height;
67
68/*
69 * Local variables
70 */
71#if defined(USE_NCURSES)
72static int _caca_attr[16*16];
73#endif
74
75#if defined(USE_CONIO)
76static struct text_info ti;
77static char *_caca_screen;
78#endif
79
80#if defined(USE_X11)
81Display *_caca_dpy;
82Window _caca_window;
83GC _caca_gc;
84XImage _caca_image;
85static int *_caca_screen;
86int _caca_colors[16];
87#endif
88
89static char *_caca_empty_line;
90static char *_caca_scratch_line;
91
92static unsigned int _caca_delay;
93static unsigned int _caca_rendertime;
94
95static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
96static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
97
98/**
99 * \brief Set the default colour pair.
100 *
101 * \param fgcolor The desired foreground colour.
102 * \param bgcolor The desired background colour.
103 * \return void
104 */
105void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
106{
107    if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
108        return;
109
110    _caca_fgcolor = fgcolor;
111    _caca_bgcolor = bgcolor;
112#if defined(USE_SLANG)
113    SLsmg_set_color((bgcolor + 16 * fgcolor) /*% 128*/);
114#elif defined(USE_NCURSES)
115    attrset(_caca_attr[fgcolor + 16 * bgcolor]);
116#elif defined(USE_CONIO)
117    textbackground(bgcolor);
118    textcolor(fgcolor);
119#elif defined(USE_X11)
120    /* FIXME */
121#endif
122}
123
124/**
125 * \brief Get the current foreground colour.
126 *
127 * \return The current foreground colour.
128 */
129enum caca_color caca_get_fg_color(void)
130{
131    return _caca_fgcolor;
132}
133
134/**
135 * \brief Get the current background colour.
136 *
137 * \return The current background colour.
138 */
139enum caca_color caca_get_bg_color(void)
140{
141    return _caca_bgcolor;
142}
143
144/**
145 * \brief Print a character at given coordinates.
146 *
147 * \param x The X coordinate of the character.
148 * \param y The Y coordinate of the character.
149 * \param c The character to print.
150 * \return void
151 */
152void caca_putchar(int x, int y, char c)
153{
154#if defined(USE_CONIO)
155    char *data;
156#endif
157    if(x < 0 || x >= (int)_caca_width ||
158       y < 0 || y >= (int)_caca_height)
159        return;
160
161#if defined(USE_SLANG)
162    SLsmg_gotorc(y, x);
163    SLsmg_write_char(c);
164#elif defined(USE_NCURSES)
165    move(y, x);
166    addch(c);
167#elif defined(USE_CONIO)
168    data = _caca_screen + 2 * (x + y * _caca_width);
169    data[0] = c;
170    data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
171//    gotoxy(x + 1, y + 1);
172//    putch(c);
173#elif defined(USE_X11)
174    _caca_screen[x + y * _caca_width] =
175        ((int)c << 8) | ((int)_caca_bgcolor << 4) | (int)_caca_fgcolor;
176#endif
177}
178
179/**
180 * \brief Print a string at given coordinates.
181 *
182 * \param x The X coordinate of the string.
183 * \param y The Y coordinate of the string.
184 * \param s The string to print.
185 * \return void
186 */
187void caca_putstr(int x, int y, const char *s)
188{
189#if defined(USE_CONIO)
190    char *buf;
191#elif defined(USE_X11)
192    int *buf;
193#endif
194    unsigned int len;
195
196    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
197        return;
198
199    len = strlen(s);
200
201    if(x < 0)
202    {
203        len -= -x;
204        if(len < 0)
205            return;
206        s += -x;
207        x = 0;
208    }
209
210    if(x + len >= _caca_width)
211    {
212        memcpy(_caca_scratch_line, s, _caca_width - x);
213        _caca_scratch_line[_caca_width - x] = '\0';
214        s = _caca_scratch_line;
215    }
216
217#if defined(USE_SLANG)
218    SLsmg_gotorc(y, x);
219    SLsmg_write_string((char *)(intptr_t)s);
220#elif defined(USE_NCURSES)
221    move(y, x);
222    addstr(s);
223#elif defined(USE_CONIO)
224    buf = _caca_screen + 2 * (x + y * _caca_width);
225    while(*s)
226    {
227        *buf++ = *s++;
228        *buf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
229    }
230//    gotoxy(x + 1, y + 1);
231//    cputs(s);
232#elif defined(USE_X11)
233    buf = _caca_screen + x + y * _caca_width;
234    while(*s)
235        *buf++ = ((int)*s++ << 8) | ((int)_caca_bgcolor << 4) | (int)_caca_fgcolor;
236#endif
237}
238
239/**
240 * \brief Format a string at given coordinates.
241 *
242 * \param x The X coordinate of the string.
243 * \param y The Y coordinate of the string.
244 * \param format The format string to print.
245 * \param ... Arguments to the format string.
246 * \return void
247 */
248void caca_printf(int x, int y, const char *format, ...)
249{
250    char tmp[BUFSIZ];
251    char *buf = tmp;
252    va_list args;
253
254    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
255        return;
256
257    if(_caca_width - x + 1 > BUFSIZ)
258        buf = malloc(_caca_width - x + 1);
259
260    va_start(args, format);
261#if defined(HAVE_VSNPRINTF)
262    vsnprintf(buf, _caca_width - x + 1, format, args);
263#else
264    vsprintf(buf, format, args);
265#endif
266    buf[_caca_width - x] = '\0';
267    va_end(args);
268
269    caca_putstr(x, y, buf);
270
271    if(buf != tmp)
272        free(buf);
273}
274
275/**
276 * \brief Clear the screen.
277 *
278 * \return void
279 */
280void caca_clear(void)
281{
282    enum caca_color oldfg = caca_get_fg_color();
283    enum caca_color oldbg = caca_get_bg_color();
284    int y = _caca_height;
285
286    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
287
288    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
289    while(y--)
290        caca_putstr(0, y, _caca_empty_line);
291
292    caca_set_color(oldfg, oldbg);
293}
294
295int _caca_init_graphics(void)
296{
297#if defined(USE_SLANG)
298    /* See SLang ref., 5.4.4. */
299    static char *slang_colors[16] =
300    {
301        /* Standard colours */
302        "black",
303        "blue",
304        "green",
305        "cyan",
306        "red",
307        "magenta",
308        "brown",
309        "lightgray",
310        /* Bright colours */
311        "gray",
312        "brightblue",
313        "brightgreen",
314        "brightcyan",
315        "brightred",
316        "brightmagenta",
317        "yellow",
318        "white",
319    };
320
321    int fg, bg;
322
323    for(fg = 0; fg < 16; fg++)
324        for(bg = 0; bg < 16; bg++)
325        {
326            int i = bg + 16 * fg;
327            SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
328        }
329
330    /* Disable alt charset support so that we get all 256 colour pairs */
331    SLtt_Has_Alt_Charset = 0;
332
333    _caca_width = SLtt_Screen_Cols;
334    _caca_height = SLtt_Screen_Rows;
335
336#elif defined(USE_NCURSES)
337    static int curses_colors[] =
338    {
339        /* Standard curses colours */
340        COLOR_BLACK,
341        COLOR_BLUE,
342        COLOR_GREEN,
343        COLOR_CYAN,
344        COLOR_RED,
345        COLOR_MAGENTA,
346        COLOR_YELLOW,
347        COLOR_WHITE,
348        /* Extra values for xterm-16color */
349        COLOR_BLACK + 8,
350        COLOR_BLUE + 8,
351        COLOR_GREEN + 8,
352        COLOR_CYAN + 8,
353        COLOR_RED + 8,
354        COLOR_MAGENTA + 8,
355        COLOR_YELLOW + 8,
356        COLOR_WHITE + 8
357    };
358
359    int fg, bg, max;
360
361    /* Activate colour */
362    start_color();
363
364    /* If COLORS == 16, it means the terminal supports full bright colours
365     * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
366     * we can build 16*16 colour pairs.
367     * If COLORS == 8, it means the terminal does not know about bright
368     * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
369     * and \e[5m). We can only build 8*8 colour pairs. */
370    max = COLORS >= 16 ? 16 : 8;
371
372    for(bg = 0; bg < max; bg++)
373        for(fg = 0; fg < max; fg++)
374        {
375            /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
376             * is light gray on black, since some terminals don't like
377             * this colour pair to be redefined. */
378            int col = ((max + 7 - fg) % max) + max * bg;
379            init_pair(col, curses_colors[fg], curses_colors[bg]);
380            _caca_attr[fg + 16 * bg] = COLOR_PAIR(col);
381
382            if(max == 8)
383            {
384                /* Bright fg on simple bg */
385                _caca_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
386                /* Simple fg on bright bg */
387                _caca_attr[fg + 16 * (bg + 8)] = A_BLINK | COLOR_PAIR(col);
388                /* Bright fg on bright bg */
389                _caca_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
390                                                             | COLOR_PAIR(col);
391            }
392        }
393
394    _caca_width = COLS;
395    _caca_height = LINES;
396
397#elif defined(USE_CONIO)
398    gettextinfo(&ti);
399    _caca_screen = malloc(2 * ti.screenwidth * ti.screenheight * sizeof(char));
400    if(_caca_screen == NULL)
401        return -1;
402#   if defined(SCREENUPDATE_IN_PC_H)
403    ScreenRetrieve(_caca_screen);
404#   else
405    /* FIXME */
406#   endif
407    _caca_width = ti.screenwidth;
408    _caca_height = ti.screenheight;
409
410#elif defined(USE_X11)
411    static int x11_colors[] =
412    {
413        /* Standard curses colours */
414        0, 0, 0,
415        0, 0, 32768,
416        0, 32768, 0,
417        0, 32768, 32768,
418        32768, 0, 0,
419        32768, 0, 32768,
420        32768, 32768, 0,
421        32768, 32768, 32768,
422        /* Extra values for xterm-16color */
423        16384, 16384, 16384,
424        16384, 16384, 65535,
425        16384, 65535, 16384,
426        16384, 65535, 65535,
427        65535, 16384, 16384,
428        65535, 16384, 65535,
429        65535, 65535, 16384,
430        65535, 65535, 65535,
431    };
432
433    int i;
434    Colormap colormap;
435
436    /* FIXME */
437    _caca_width = 80;
438    _caca_height = 30;
439
440    _caca_screen = malloc(_caca_width * _caca_height * sizeof(int));
441    if(_caca_screen == NULL)
442        return -1;
443
444    _caca_dpy = XOpenDisplay(NULL);
445    if(_caca_dpy == NULL)
446    {
447        free(_caca_screen);
448        return -1;
449    }
450
451    colormap = DefaultColormap(_caca_dpy, DefaultScreen(_caca_dpy));
452    for(i = 0; i < 16; i++)
453    {
454        XColor color;
455        color.red = x11_colors[i * 3];
456        color.green = x11_colors[i * 3 + 1];
457        color.blue = x11_colors[i * 3 + 2];
458        XAllocColor(_caca_dpy, colormap, &color);
459        _caca_colors[i] = color.pixel;
460    }
461
462    _caca_window = XCreateSimpleWindow(_caca_dpy, DefaultRootWindow(_caca_dpy),
463                                       0, 0, 400, 300, 0,
464                                       _caca_colors[0], _caca_colors[0]);
465    XSelectInput(_caca_dpy, _caca_window, StructureNotifyMask);
466    XMapWindow(_caca_dpy, _caca_window);
467
468    _caca_gc = XCreateGC(_caca_dpy, _caca_window, 0, NULL);
469    XSetForeground(_caca_dpy, _caca_gc, _caca_colors[15]);
470
471    for(;;)
472    {
473        XEvent event;
474        XNextEvent(_caca_dpy, &event);
475        if (event.type == MapNotify)
476            break;
477    }
478
479    XSelectInput(_caca_dpy, _caca_window, KeyPressMask);
480
481    XSync(_caca_dpy, False);
482
483    //_caca_image =
484#endif
485    _caca_empty_line = malloc(_caca_width + 1);
486    memset(_caca_empty_line, ' ', _caca_width);
487    _caca_empty_line[_caca_width] = '\0';
488
489    _caca_scratch_line = malloc(_caca_width + 1);
490
491    _caca_delay = 0;
492    _caca_rendertime = 0;
493
494    return 0;
495}
496
497int _caca_end_graphics(void)
498{
499#if defined(USE_SLANG)
500    /* Nothing to do */
501#elif defined(USE_NCURSES)
502    /* Nothing to do */
503#elif defined(USE_CONIO)
504    free(_caca_screen);
505#elif defined(USE_X11)
506    XSync(_caca_dpy, False);
507    XFreeGC(_caca_dpy, _caca_gc);
508    XUnmapWindow(_caca_dpy, _caca_window);
509    XDestroyWindow(_caca_dpy, _caca_window);
510    XCloseDisplay(_caca_dpy);
511    free(_caca_screen);
512#endif
513    free(_caca_empty_line);
514
515    return 0;
516}
517
518/**
519 * \brief Set the refresh delay.
520 *
521 * \param usec The refresh delay in microseconds.
522 * \return void
523 */
524void caca_set_delay(unsigned int usec)
525{
526    _caca_delay = usec;
527}
528
529/**
530 * \brief Get the average rendering time.
531 *
532 * \return The render time in microseconds.
533 */
534unsigned int caca_get_rendertime(void)
535{
536    return _caca_rendertime;
537}
538
539static unsigned int _caca_getticks(void)
540{
541    static unsigned int last_sec = 0, last_usec = 0;
542
543    struct timeval tv;
544    unsigned int ticks = 0;
545
546    gettimeofday(&tv, NULL);
547
548    if(last_sec != 0)
549    {
550        ticks = (tv.tv_sec - last_sec) * 1000000 + (tv.tv_usec - last_usec);
551    }
552
553    last_sec = tv.tv_sec;
554    last_usec = tv.tv_usec;
555
556    return ticks;
557}
558
559/**
560 * \brief Flush pending changes and redraw the screen.
561 *
562 * \return void
563 */
564void caca_refresh(void)
565{
566#define IDLE_USEC 10000
567    static int lastticks = 0;
568    int ticks = lastticks + _caca_getticks();
569
570#if defined(USE_SLANG)
571    SLsmg_refresh();
572#elif defined(USE_NCURSES)
573    refresh();
574#elif defined(USE_CONIO)
575#   if defined(SCREENUPDATE_IN_PC_H)
576    ScreenUpdate(_caca_screen);
577#   else
578    /* FIXME */
579#   endif
580#elif defined(USE_X11)
581    int x, y;
582
583    for(y = 0; y < _caca_height; y++)
584        for(x = 0; x < _caca_width; x++)
585        {
586            int item = _caca_screen[x + y * _caca_width];
587            char data = item >> 8;
588            XSetForeground(_caca_dpy, _caca_gc, _caca_colors[(item >> 4) & 0xf]);
589            XFillRectangle(_caca_dpy, _caca_window, _caca_gc,
590                           x * 6, y * 12, 6, 12);
591            XSetForeground(_caca_dpy, _caca_gc, _caca_colors[item & 0xf]);
592            XDrawString(_caca_dpy, _caca_window, _caca_gc,
593                        x * 6, y * 12 + 10, &data, 1);
594        }
595    XFlush(_caca_dpy);
596#endif
597
598    /* Wait until _caca_delay + time of last call */
599    ticks += _caca_getticks();
600    for(; ticks + IDLE_USEC < (int)_caca_delay; ticks += _caca_getticks())
601        usleep(IDLE_USEC);
602
603    /* Update the sliding mean of the render time */
604    _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
605
606    lastticks = ticks - _caca_delay;
607
608    /* If we drifted too much, it's bad, bad, bad. */
609    if(lastticks > (int)_caca_delay)
610        lastticks = 0;
611}
612
Note: See TracBrowser for help on using the repository browser.