Logo Search packages:      
Sourcecode: libcaca version File versions  Download package

graphics.c

Go to the documentation of this file.
/*
 *  libcaca       ASCII-Art library
 *  Copyright (c) 2002, 2003 Sam Hocevar <sam@zoy.org>
 *                All Rights Reserved
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA
 */

/** \file graphics.c
 *  \version \$Id: graphics.c 258 2004-01-26 10:52:30Z sam $
 *  \author Sam Hocevar <sam@zoy.org>
 *  \brief Character drawing
 *
 *  This file contains character and string drawing functions.
 */

#include "config.h"

#if defined(USE_SLANG)
#   if defined(HAVE_SLANG_SLANG_H)
#       include <slang/slang.h>
#   else
#       include <slang.h>
#   endif
#endif
#if defined(USE_NCURSES)
#   if defined(HAVE_NCURSES_H)
#       include <ncurses.h>
#   else
#       include <curses.h>
#   endif
#endif
#if defined(USE_CONIO)
#   include <conio.h>
#   if defined(SCREENUPDATE_IN_PC_H)
#       include <pc.h>
#   endif
#endif
#if defined(USE_X11)
#   include <X11/Xlib.h>
#   if defined(HAVE_X11_XKBLIB_H)
#       include <X11/XKBlib.h>
#   endif
#endif
#if defined(USE_WIN32)
#   include <windows.h>
#endif

#if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME)
#   include <inttypes.h>
#else
typedef unsigned char uint8_t;
#endif

#include <stdio.h> /* BUFSIZ */
#include <string.h>
#include <stdlib.h>
#if defined(HAVE_UNISTD_H)
#   include <unistd.h>
#endif
#include <stdarg.h>

#if defined(HAVE_SIGNAL_H)
#   include <signal.h>
#endif
#if defined(HAVE_SYS_IOCTL_H)
#   include <sys/ioctl.h>
#endif

#include "caca.h"
#include "caca_internals.h"

/*
 * Global variables
 */
#if !defined(_DOXYGEN_SKIP_ME)
unsigned int _caca_width = 0;
unsigned int _caca_height = 0;
int _caca_resize = 0;
int _caca_resize_event = 0;
#endif

/*
 * Local variables
 */
#if defined(USE_NCURSES)
static int ncurses_attr[16*16];
#endif

#if defined(USE_SLANG)
/* Tables generated by test/optipal.c */
static int const slang_palette[2*16*16] =
{
     1,  0,   2,  0,   3,  0,   4,  0,   5,  0,   6,  0,   7,  0,   8,  0,
     9,  0,  10,  0,  11,  0,  12,  0,  13,  0,  14,  0,  15,  0,   0,  8,
     8,  7,   7,  8,  15,  7,   7, 15,  15,  9,   9, 15,   1,  9,   9,  1,
     7,  9,   9,  7,   8,  1,   1,  8,   0,  1,  15, 10,  10, 15,   2, 10,
    10,  2,   7, 10,  10,  7,   8,  2,   2,  8,   0,  2,  15, 11,  11, 15,
     3, 11,  11,  3,   7, 11,  11,  7,   8,  3,   3,  8,   0,  3,  15, 12,
    12, 15,   4, 12,  12,  4,   7, 12,  12,  7,   8,  4,   4,  8,   0,  4,
    15, 13,  13, 15,   5, 13,  13,  5,   7, 13,  13,  7,   8,  5,   5,  8,
     0,  5,  15, 14,  14, 15,   6, 14,  14,  6,   7, 14,  14,  7,   8,  6,
     6,  8,   0,  6,   4,  6,   6,  4,  12, 14,  14, 12,   6,  2,   2,  6,
    14, 10,  10, 14,   2,  3,   3,  2,  10, 11,  11, 10,   3,  1,   1,  3,
    11,  9,   9, 11,   1,  5,   5,  1,   9, 13,  13,  9,   5,  4,   4,  5,
    13, 12,  12, 13,   4, 14,   6, 12,  12,  6,  14,  4,   6, 10,   2, 14,
    14,  2,  10,  6,   2, 11,   3, 10,  10,  3,  11,  2,   3,  9,   1, 11,
    11,  1,   9,  3,   1, 13,   5,  9,   9,  5,  13,  1,   5, 12,   4, 13,
    13,  4,  12,  5,   0,  7,   0, 15,  15,  8,   8, 15,  15,  1,   7,  1,
     1,  6,   2,  5,   3,  4,   4,  3,   5,  2,   6,  1,   0,  0,   1,  1,
     9,  6,  10,  5,  11,  4,  12,  3,  13,  2,  14,  1,   2,  2,   3,  3,
     4,  4,   5,  5,   6,  6,   7,  7,  14,  9,   1, 15,   8,  9,   8,  8,
     9,  9,   1,  7,   0,  9,   9,  8,   6,  9,  13, 10,   2, 15,   8, 10,
     7,  2,  15,  2,   2,  7,   0, 10,  10,  8,   5, 10,  12, 11,   3, 15,
     8, 11,   7,  3,  15,  3,   3,  7,   0, 11,  11,  8,   4, 11,  11, 12,
     4, 15,   8, 12,   7,  4,  15,  4,   4,  7,   0, 12,  12,  8,   3, 12,
    10, 13,   5, 15,   8, 13,   7,  5,  15,  5,   5,  7,   0, 13,  13,  8,
     2, 13,   9, 14,   6, 15,   8, 14,   7,  6,  15,  6,   6,  7,   0, 14,
    14,  8,   1, 14,   5,  6,   2,  4,  13, 14,  10, 12,   4,  2,   3,  6,
    12, 10,  11, 14,   6,  3,   1,  2,  14, 11,   9, 10,   2,  1,   5,  3,
    10,  9,  13, 11,   3,  5,   4,  1,  11, 13,  12,  9,   1,  4,   6,  5,
     9, 12,  14, 13,   5, 14,   2, 12,  13,  6,  10,  4,   4, 10,   3, 14,
    12,  2,  11,  6,   6, 11,   1, 10,  14,  3,   9,  2,   2,  9,   5, 11,
    10,  1,  13,  3,   3, 13,   4,  9,  11,  5,  12,  1,   1, 12,   6, 13,
     9,  4,  14,  5,  10, 10,  11, 11,  12, 12,  13, 13,  14, 14,  15, 15,
};

static int const slang_assoc[16*16] =
{
    134, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
    28, 135, 214, 86, 219, 91, 133, 127, 26, 23, 240, 112, 245, 117, 141, 126,
    37, 211, 142, 83, 206, 132, 78, 160, 35, 237, 32, 109, 232, 140, 104, 161,
    46, 87, 82, 143, 131, 215, 210, 169, 44, 113, 108, 41, 139, 241, 236, 170,
    55, 222, 203, 130, 144, 94, 75, 178, 53, 248, 229, 138, 50, 120, 101, 179,
    64, 90, 129, 218, 95, 145, 223, 187, 62, 116, 137, 244, 121, 59, 249, 188,
    73, 128, 79, 207, 74, 202, 146, 196, 71, 136, 105, 233, 100, 228, 68, 197,
    122, 153, 162, 171, 180, 189, 198, 147, 16, 25, 34, 43, 52, 61, 70, 18,
    15, 27, 36, 45, 54, 63, 72, 17, 151, 155, 164, 173, 182, 191, 200, 124,
    154, 22, 238, 110, 243, 115, 156, 24, 150, 152, 216, 88, 221, 93, 148, 20,
    163, 235, 31, 107, 230, 165, 102, 33, 159, 213, 250, 85, 208, 157, 80, 29,
    172, 111, 106, 40, 174, 239, 234, 42, 168, 89, 84, 251, 166, 217, 212, 38,
    181, 246, 227, 183, 49, 118, 99, 51, 177, 224, 205, 175, 252, 96, 77, 47,
    190, 114, 192, 242, 119, 58, 247, 60, 186, 92, 184, 220, 97, 253, 225, 56,
    199, 201, 103, 231, 98, 226, 67, 69, 195, 193, 81, 209, 76, 204, 254, 65,
    123, 149, 158, 167, 176, 185, 194, 19, 125, 21, 30, 39, 48, 57, 66, 255,
};
#endif

#if defined(USE_CONIO)
static struct text_info conio_ti;
static char *conio_screen;
#endif

#if defined(USE_X11) && !defined(_DOXYGEN_SKIP_ME)
Display *x11_dpy;
Window x11_window;
Pixmap x11_pixmap;
GC x11_gc;
long int x11_event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
            | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
            | ExposureMask;
int x11_font_width, x11_font_height;
unsigned int x11_new_width, x11_new_height;
static uint8_t *x11_char, *x11_attr;
static int x11_colors[16];
static Font x11_font;
static XFontStruct *x11_font_struct;
static int x11_font_offset;
#if defined(HAVE_X11_XKBLIB_H)
static Bool x11_detect_autorepeat;
#endif
#endif

#if defined(USE_WIN32)
static uint8_t *win32_char, *win32_attr;
HANDLE win32_hin, win32_hout;
static HANDLE win32_front, win32_back;
static CHAR_INFO *win32_buffer;

static int const win32_fg_palette[] =
{
    0,
    FOREGROUND_BLUE,
    FOREGROUND_GREEN,
    FOREGROUND_GREEN | FOREGROUND_BLUE,
    FOREGROUND_RED,
    FOREGROUND_RED | FOREGROUND_BLUE,
    FOREGROUND_RED | FOREGROUND_GREEN,
    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
    FOREGROUND_INTENSITY,
    FOREGROUND_INTENSITY | FOREGROUND_BLUE,
    FOREGROUND_INTENSITY | FOREGROUND_GREEN,
    FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
    FOREGROUND_INTENSITY | FOREGROUND_RED,
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
};

static int const win32_bg_palette[] =
{
    0,
    BACKGROUND_BLUE,
    BACKGROUND_GREEN,
    BACKGROUND_GREEN | BACKGROUND_BLUE,
    BACKGROUND_RED,
    BACKGROUND_RED | BACKGROUND_BLUE,
    BACKGROUND_RED | BACKGROUND_GREEN,
    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
    BACKGROUND_INTENSITY,
    BACKGROUND_INTENSITY | BACKGROUND_BLUE,
    BACKGROUND_INTENSITY | BACKGROUND_GREEN,
    BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
    BACKGROUND_INTENSITY | BACKGROUND_RED,
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
    BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
};
#endif

static char *_caca_empty_line;
static char *_caca_scratch_line;

static unsigned int _caca_delay;
static unsigned int _caca_rendertime;

#if defined(OPTIMISE_SLANG_PALETTE)
static int _caca_fgisbg = 0;
#endif
static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;

/*
 * Local functions
 */
static void caca_handle_resize(void);

#if defined(USE_SLANG)
static void slang_init_palette(void);
#endif

#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
static RETSIGTYPE sigwinch_handler(int);
#endif

#if defined(USE_X11)
static int x11_error_handler(Display *, XErrorEvent *);
#endif

/** \brief Set the default colour pair.
 *
 *  This function sets the default colour pair. String functions such as
 *  caca_printf() and graphical primitive functions such as caca_draw_line()
 *  will use these colour pairs.
 *
 *  \param fgcolor The requested foreground colour.
 *  \param bgcolor The requested background colour.
 */
00272 void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
{
    if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
        return;

    _caca_fgcolor = fgcolor;
    _caca_bgcolor = bgcolor;

    switch(_caca_driver)
    {
#if defined(USE_SLANG)
    case CACA_DRIVER_SLANG:

#if defined(OPTIMISE_SLANG_PALETTE)
        /* If foreground == background, discard this colour pair. Functions
         * such as caca_putchar will print spaces instead of characters */
        if(fgcolor != bgcolor)
            _caca_fgisbg = 0;
        else
        {
            _caca_fgisbg = 1;
            if(fgcolor == CACA_COLOR_BLACK)
                fgcolor = CACA_COLOR_WHITE;
            else if(fgcolor == CACA_COLOR_WHITE
                     || fgcolor <= CACA_COLOR_LIGHTGRAY)
                fgcolor = CACA_COLOR_BLACK;
            else
                fgcolor = CACA_COLOR_WHITE;
        }
#endif

#if defined(OPTIMISE_SLANG_PALETTE)
        SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
#else
        SLsmg_set_color(fgcolor + 16 * bgcolor);
#endif
        break;
#endif
#if defined(USE_NCURSES)
    case CACA_DRIVER_NCURSES:
        attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
        break;
#endif
#if defined(USE_CONIO)
    case CACA_DRIVER_CONIO:
        textbackground(bgcolor);
        textcolor(fgcolor);
        break;
#endif
#if defined(USE_X11)
    case CACA_DRIVER_X11:
        /* Nothing to do */
        break;
#endif
#if defined(USE_WIN32)
    case CACA_DRIVER_WIN32:
        /* Nothing to do */
        break;
#endif
    default:
        break;
    }
}

/** \brief Get the current foreground colour.
 *
 *  This function returns the current foreground colour that was set with
 *  caca_set_color().
 *
 *  \return The current foreground colour.
 */
00343 enum caca_color caca_get_fg_color(void)
{
    return _caca_fgcolor;
}

/** \brief Get the current background colour.
 *
 *  This function returns the current background colour that was set with
 *  caca_set_color().
 *
 *  \return The current background colour.
 */
00355 enum caca_color caca_get_bg_color(void)
{
    return _caca_bgcolor;
}

/** \brief Print a character.
 *
 *  This function prints a character at the given coordinates, using the
 *  default foreground and background values. If the coordinates are outside
 *  the screen boundaries, nothing is printed.
 *
 *  \param x X coordinate.
 *  \param y Y coordinate.
 *  \param c The character to print.
 */
00370 void caca_putchar(int x, int y, char c)
{
#if defined(USE_CONIO)
    char *data;
#endif
    if(x < 0 || x >= (int)_caca_width ||
       y < 0 || y >= (int)_caca_height)
        return;

    switch(_caca_driver)
    {
#if defined(USE_SLANG)
    case CACA_DRIVER_SLANG:
        SLsmg_gotorc(y, x);
#if defined(OPTIMISE_SLANG_PALETTE)
        if(_caca_fgisbg)
            SLsmg_write_char(' ');
        else
#endif
            SLsmg_write_char(c);
        break;
#endif
#if defined(USE_NCURSES)
    case CACA_DRIVER_NCURSES:
        move(y, x);
        addch(c);
        break;
#endif
#if defined(USE_CONIO)
    case CACA_DRIVER_CONIO:
        data = conio_screen + 2 * (x + y * _caca_width);
        data[0] = c;
        data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
        break;
#endif
#if defined(USE_X11)
    case CACA_DRIVER_X11:
        x11_char[x + y * _caca_width] = c;
        x11_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
        break;
#endif
#if defined(USE_WIN32)
    case CACA_DRIVER_WIN32:
        win32_char[x + y * _caca_width] = c;
        win32_attr[x + y * _caca_width] = (_caca_bgcolor << 4) | _caca_fgcolor;
        break;
#endif
    default:
        break;
    }
}

/** \brief Print a string.
 *
 *  This function prints a string at the given coordinates, using the
 *  default foreground and background values. The coordinates may be outside
 *  the screen boundaries (eg. a negative Y coordinate) and the string will
 *  be cropped accordingly if it is too long.
 *
 *  \param x X coordinate.
 *  \param y Y coordinate.
 *  \param s The string to print.
 */
00433 void caca_putstr(int x, int y, char const *s)
{
#if defined(USE_CONIO) | defined(USE_X11) | defined(USE_WIN32)
    char *charbuf;
#endif
#if defined(USE_X11) | defined(USE_WIN32)
    char *attrbuf;
#endif
    unsigned int len;

    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
        return;

    len = strlen(s);

    if(x < 0)
    {
        if(len < (unsigned int)-x)
            return;
        len -= -x;
        s += -x;
        x = 0;
    }

    if(x + len >= _caca_width)
    {
        len = _caca_width - x;
        memcpy(_caca_scratch_line, s, len);
        _caca_scratch_line[len] = '\0';
        s = _caca_scratch_line;
    }

    switch(_caca_driver)
    {
#if defined(USE_SLANG)
    case CACA_DRIVER_SLANG:
        SLsmg_gotorc(y, x);
#if defined(OPTIMISE_SLANG_PALETTE)
        if(_caca_fgisbg)
            SLsmg_write_string(_caca_empty_line + _caca_width - len);
        else
#endif
            SLsmg_write_string((char *)(intptr_t)s);
        break;
#endif
#if defined(USE_NCURSES)
    case CACA_DRIVER_NCURSES:
        move(y, x);
        addstr(s);
        break;
#endif
#if defined(USE_CONIO)
    case CACA_DRIVER_CONIO:
        charbuf = conio_screen + 2 * (x + y * _caca_width);
        while(*s)
        {
            *charbuf++ = *s++;
            *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
        }
        break;
#endif
#if defined(USE_X11)
    case CACA_DRIVER_X11:
        charbuf = x11_char + x + y * _caca_width;
        attrbuf = x11_attr + x + y * _caca_width;
        while(*s)
        {
            *charbuf++ = *s++;
            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
        }
        break;
#endif
#if defined(USE_WIN32)
    case CACA_DRIVER_WIN32:
        charbuf = win32_char + x + y * _caca_width;
        attrbuf = win32_attr + x + y * _caca_width;
        while(*s)
        {
            *charbuf++ = *s++;
            *attrbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
        }
        break;
#endif
    default:
        break;
    }
}

/** \brief Format a string.
 *
 *  This function formats a string at the given coordinates, using the
 *  default foreground and background values. The coordinates may be outside
 *  the screen boundaries (eg. a negative Y coordinate) and the string will
 *  be cropped accordingly if it is too long. The syntax of the format
 *  string is the same as for the C printf() function.
 *
 *  \param x X coordinate.
 *  \param y Y coordinate.
 *  \param format The format string to print.
 *  \param ... Arguments to the format string.
 */
00534 void caca_printf(int x, int y, char const *format, ...)
{
    char tmp[BUFSIZ];
    char *buf = tmp;
    va_list args;

    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
        return;

    if(_caca_width - x + 1 > BUFSIZ)
        buf = malloc(_caca_width - x + 1);

    va_start(args, format);
#if defined(HAVE_VSNPRINTF)
    vsnprintf(buf, _caca_width - x + 1, format, args);
#else
    vsprintf(buf, format, args);
#endif
    buf[_caca_width - x] = '\0';
    va_end(args);

    caca_putstr(x, y, buf);

    if(buf != tmp)
        free(buf);
}

/** \brief Clear the screen.
 *
 *  This function clears the screen using a black background.
 */
00565 void caca_clear(void)
{
    enum caca_color oldfg = caca_get_fg_color();
    enum caca_color oldbg = caca_get_bg_color();
    int y = _caca_height;

    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);

    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
    while(y--)
        caca_putstr(0, y, _caca_empty_line);

    caca_set_color(oldfg, oldbg);
}

#if !defined(_DOXYGEN_SKIP_ME)
int _caca_init_graphics(void)
{
#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
    signal(SIGWINCH, sigwinch_handler);
#endif

#if defined(USE_SLANG)
    if(_caca_driver == CACA_DRIVER_SLANG)
    {
        slang_init_palette();

        /* Disable alt charset support so that we get a chance to have all
         * 256 colour pairs */
        SLtt_Has_Alt_Charset = 0;

        _caca_width = SLtt_Screen_Cols;
        _caca_height = SLtt_Screen_Rows;
    }
    else
#endif
#if defined(USE_NCURSES)
    if(_caca_driver == CACA_DRIVER_NCURSES)
    {
        static int curses_colors[] =
        {
            /* Standard curses colours */
            COLOR_BLACK,
            COLOR_BLUE,
            COLOR_GREEN,
            COLOR_CYAN,
            COLOR_RED,
            COLOR_MAGENTA,
            COLOR_YELLOW,
            COLOR_WHITE,
            /* Extra values for xterm-16color */
            COLOR_BLACK + 8,
            COLOR_BLUE + 8,
            COLOR_GREEN + 8,
            COLOR_CYAN + 8,
            COLOR_RED + 8,
            COLOR_MAGENTA + 8,
            COLOR_YELLOW + 8,
            COLOR_WHITE + 8
        };

        int fg, bg, max;

        /* Activate colour */
        start_color();

        /* If COLORS == 16, it means the terminal supports full bright colours
         * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
         * we can build 16*16 colour pairs.
         * If COLORS == 8, it means the terminal does not know about bright
         * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
         * and \e[5m). We can only build 8*8 colour pairs. */
        max = COLORS >= 16 ? 16 : 8;

        for(bg = 0; bg < max; bg++)
            for(fg = 0; fg < max; fg++)
            {
                /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
                 * is light gray on black, since some terminals don't like
                 * this colour pair to be redefined. */
                int col = ((max + 7 - fg) % max) + max * bg;
                init_pair(col, curses_colors[fg], curses_colors[bg]);
                ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);

                if(max == 8)
                {
                    /* Bright fg on simple bg */
                    ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
                    /* Simple fg on bright bg */
                    ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
                                                        | COLOR_PAIR(col);
                    /* Bright fg on bright bg */
                    ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
                                                            | COLOR_PAIR(col);
                }
            }

        _caca_width = COLS;
        _caca_height = LINES;
    }
    else
#endif
#if defined(USE_CONIO)
    if(_caca_driver == CACA_DRIVER_CONIO)
    {
        gettextinfo(&conio_ti);
        conio_screen = malloc(2 * conio_ti.screenwidth
                                 * conio_ti.screenheight * sizeof(char));
        if(conio_screen == NULL)
            return -1;
#   if defined(SCREENUPDATE_IN_PC_H)
        ScreenRetrieve(conio_screen);
#   else
        /* FIXME */
#   endif
        _caca_width = conio_ti.screenwidth;
        _caca_height = conio_ti.screenheight;
    }
    else
#endif
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        static int x11_palette[] =
        {
            /* Standard curses colours */
            0x0,    0x0,    0x0,
            0x0,    0x0,    0x8000,
            0x0,    0x8000, 0x0,
            0x0,    0x8000, 0x8000,
            0x8000, 0x0,    0x0,
            0x8000, 0x0,    0x8000,
            0x8000, 0x8000, 0x0,
            0x8000, 0x8000, 0x8000,
            /* Extra values for xterm-16color */
            0x4000, 0x4000, 0x4000,
            0x4000, 0x4000, 0xffff,
            0x4000, 0xffff, 0x4000,
            0x4000, 0xffff, 0xffff,
            0xffff, 0x4000, 0x4000,
            0xffff, 0x4000, 0xffff,
            0xffff, 0xffff, 0x4000,
            0xffff, 0xffff, 0xffff,
        };

        Colormap colormap;
        XSetWindowAttributes x11_winattr;
        int (*old_error_handler)(Display *, XErrorEvent *);
        char const *font_name = "8x13bold";
        int i;

        if(getenv("CACA_GEOMETRY") && *(getenv("CACA_GEOMETRY")))
            sscanf(getenv("CACA_GEOMETRY"),
                   "%ux%u", &_caca_width, &_caca_height);

        if(!_caca_width)
            _caca_width = 80;
        if(!_caca_height)
            _caca_height = 32;

        x11_char = malloc(_caca_width * _caca_height * sizeof(int));
        if(x11_char == NULL)
            return -1;

        x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
        if(x11_attr == NULL)
        {
            free(x11_char);
            return -1;
        }

        memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
        memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));

        x11_dpy = XOpenDisplay(NULL);
        if(x11_dpy == NULL)
        {
            free(x11_char);
            free(x11_attr);
            return -1;
        }

        if(getenv("CACA_FONT") && *(getenv("CACA_FONT")))
            font_name = getenv("CACA_FONT");

        /* Ignore font errors */
        old_error_handler = XSetErrorHandler(x11_error_handler);

        x11_font = XLoadFont(x11_dpy, font_name);
        if(!x11_font)
        {
            XCloseDisplay(x11_dpy);
            free(x11_char);
            free(x11_attr);
            return -1;
        }

        x11_font_struct = XQueryFont(x11_dpy, x11_font);
        if(!x11_font_struct)
        {
            XUnloadFont(x11_dpy, x11_font);
            XCloseDisplay(x11_dpy);
            free(x11_char);
            free(x11_attr);
            return -1;
        }

        /* Reset the default X11 error handler */
        XSetErrorHandler(old_error_handler);

        x11_font_width = x11_font_struct->max_bounds.width;
        x11_font_height = x11_font_struct->max_bounds.ascent
                             + x11_font_struct->max_bounds.descent;
        x11_font_offset = x11_font_struct->max_bounds.descent;

        colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
        for(i = 0; i < 16; i++)
        {
            XColor color;
            color.red = x11_palette[i * 3];
            color.green = x11_palette[i * 3 + 1];
            color.blue = x11_palette[i * 3 + 2];
            XAllocColor(x11_dpy, colormap, &color);
            x11_colors[i] = color.pixel;
        }

        x11_winattr.backing_store = Always;
        x11_winattr.background_pixel = x11_colors[0];
        x11_winattr.event_mask = ExposureMask | StructureNotifyMask;

        x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
                                   _caca_width * x11_font_width,
                                   _caca_height * x11_font_height,
                                   0, 0, InputOutput, 0,
                                   CWBackingStore | CWBackPixel | CWEventMask,
                                   &x11_winattr);

        XStoreName(x11_dpy, x11_window, "caca for X");

        XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
        XMapWindow(x11_dpy, x11_window);

        x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
        XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
        XSetFont(x11_dpy, x11_gc, x11_font);

        for(;;)
        {
            XEvent event;
            XNextEvent(x11_dpy, &event);
            if (event.type == MapNotify)
                break;
        }

        /* Disable autorepeat */
#if defined(HAVE_X11_XKBLIB_H)
        XkbSetDetectableAutoRepeat(x11_dpy, True, &x11_detect_autorepeat);
        if(!x11_detect_autorepeat)
            XAutoRepeatOff(x11_dpy);
#endif

        XSelectInput(x11_dpy, x11_window, x11_event_mask);

        XSync(x11_dpy, False);

        x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
                                   _caca_width * x11_font_width,
                                   _caca_height * x11_font_height,
                                   DefaultDepth(x11_dpy,
                                                DefaultScreen(x11_dpy)));

        x11_new_width = x11_new_height = 0;
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        CONSOLE_CURSOR_INFO cci;
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        COORD size;

        win32_front = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
                                                0, NULL,
                                                CONSOLE_TEXTMODE_BUFFER, NULL);
        if(!win32_front || win32_front == INVALID_HANDLE_VALUE)
            return -1;

        win32_back = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
                                               0, NULL,
                                               CONSOLE_TEXTMODE_BUFFER, NULL);
        if(!win32_back || win32_back == INVALID_HANDLE_VALUE)
            return -1;

        if(!GetConsoleScreenBufferInfo(win32_hout, &csbi))
            return -1;

        /* Sample code to get the biggest possible window */
        //size = GetLargestConsoleWindowSize(win32_hout);

        _caca_width = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        _caca_height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;

        size.X = _caca_width;
        size.Y = _caca_height;
        SetConsoleScreenBufferSize(win32_front, size);
        SetConsoleScreenBufferSize(win32_back, size);

        SetConsoleMode(win32_front, 0);
        SetConsoleMode(win32_back, 0);

        GetConsoleCursorInfo(win32_front, &cci);
        cci.dwSize = 0;
        cci.bVisible = FALSE;
        SetConsoleCursorInfo(win32_front, &cci);
        SetConsoleCursorInfo(win32_back, &cci);

        SetConsoleActiveScreenBuffer(win32_front);

        win32_char = malloc(_caca_width * _caca_height * sizeof(int));
        if(win32_char == NULL)
            return -1;

        win32_attr = malloc(_caca_width * _caca_height * sizeof(int));
        if(win32_attr == NULL)
        {
            free(win32_char);
            return -1;
        }

        win32_buffer = malloc(_caca_width * _caca_height * sizeof(CHAR_INFO));
        if(win32_buffer == NULL)
        {
            free(win32_attr);
            free(win32_char);
            return -1;
        }

        memset(win32_char, 0, _caca_width * _caca_height * sizeof(int));
        memset(win32_attr, 0, _caca_width * _caca_height * sizeof(int));
    }
    else
#endif
    {
        /* Dummy */
    }

    _caca_empty_line = malloc(_caca_width + 1);
    memset(_caca_empty_line, ' ', _caca_width);
    _caca_empty_line[_caca_width] = '\0';

    _caca_scratch_line = malloc(_caca_width + 1);

    _caca_delay = 0;
    _caca_rendertime = 0;

    return 0;
}

int _caca_end_graphics(void)
{
#if defined(USE_SLANG)
    /* Nothing to do */
#endif
#if defined(USE_NCURSES)
    /* Nothing to do */
#endif
#if defined(USE_CONIO)
    if(_caca_driver == CACA_DRIVER_CONIO)
    {
        free(conio_screen);
    }
    else
#endif
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        XSync(x11_dpy, False);
#if defined(HAVE_X11_XKBLIB_H)
        if(!x11_detect_autorepeat)
            XAutoRepeatOn(x11_dpy);
#endif
        XFreePixmap(x11_dpy, x11_pixmap);
        XFreeFont(x11_dpy, x11_font_struct);
        XFreeGC(x11_dpy, x11_gc);
        XUnmapWindow(x11_dpy, x11_window);
        XDestroyWindow(x11_dpy, x11_window);
        XCloseDisplay(x11_dpy);
        free(x11_char);
        free(x11_attr);
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        SetConsoleActiveScreenBuffer(win32_hout);
        CloseHandle(win32_back);
        CloseHandle(win32_front);
        free(win32_char);
        free(win32_attr);
    }
    else
#endif
    {
        /* Dummy */
    }

    free(_caca_empty_line);

    return 0;
}
#endif /* _DOXYGEN_SKIP_ME */

/** \brief Set the window title.
 *
 *  If libcaca runs in a window, try to change its title. This works with
 *  the X11 and Win32 drivers.
 *
 *  \param title The desired window title.
 *  \return 0 upon success, a non-zero value if an error occurs.
 */
00987 int caca_set_window_title(char const *title)
{
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        XStoreName(x11_dpy, x11_window, title);
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        SetConsoleTitle(title);
    }
    else
#endif
    {
        /* Not supported */
        return -1;
    }

    return 0;
}

/** \brief Get the window width.
 *
 *  If libcaca runs in a window, get the usable window width. This value can
 *  be used for aspect ratio calculation. If libcaca does not run in a window
 *  or if there is no way to know the font size, assume a 6x10 font is being
 *  used. Note that the units are not necessarily pixels.
 *
 *  \return The window width.
 */
01020 unsigned int caca_get_window_width(void)
{
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        return _caca_width * x11_font_width;
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        /* FIXME */
    }
    else
#endif
    {
        /* Dummy */
    }

    /* Fallback to a 6x10 font */
    return _caca_width * 6;
}

/** \brief Get the window height.
 *
 *  If libcaca runs in a window, get the usable window height. This value can
 *  be used for aspect ratio calculation. If libcaca does not run in a window
 *  or if there is no way to know the font size, assume a 6x10 font is being
 *  used. Note that the units are not necessarily pixels.
 *
 *  \return The window height.
 */
01053 unsigned int caca_get_window_height(void)
{
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        return _caca_height * x11_font_height;
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        /* FIXME */
    }
    else
#endif
    {
        /* Dummy */
    }

    /* Fallback to a 6x10 font */
    return _caca_height * 10;
}

/** \brief Set the refresh delay.
 *
 *  This function sets the refresh delay in microseconds. The refresh delay
 *  is used by caca_refresh() to achieve constant framerate. See the
 *  caca_refresh() documentation for more details.
 *
 *  If the argument is zero, constant framerate is disabled. This is the
 *  default behaviour.
 *
 *  \param usec The refresh delay in microseconds.
 */
01088 void caca_set_delay(unsigned int usec)
{
    _caca_delay = usec;
}

/** \brief Get the average rendering time.
 *
 *  This function returns the average rendering time, which is the average
 *  measured time between two caca_refresh() calls, in microseconds. If
 *  constant framerate was activated by calling caca_set_delay(), the average
 *  rendering time will not be considerably shorter than the requested delay
 *  even if the real rendering time was shorter.
 *
 *  \return The render time in microseconds.
 */
01103 unsigned int caca_get_rendertime(void)
{
    return _caca_rendertime;
}

/** \brief Flush pending changes and redraw the screen.
 *
 *  This function flushes all graphical operations and prints them to the
 *  screen. Nothing will show on the screen until caca_refresh() is
 *  called.
 *
 *  If caca_set_delay() was called with a non-zero value, caca_refresh()
 *  will use that value to achieve constant framerate: if two consecutive
 *  calls to caca_refresh() are within a time range shorter than the value
 *  set with caca_set_delay(), the second call will wait a bit before
 *  performing the screen refresh.
 */
01120 void caca_refresh(void)
{
#if !defined(_DOXYGEN_SKIP_ME)
#define IDLE_USEC 10000
#endif
    static struct caca_timer timer = CACA_TIMER_INITIALIZER;
    static int lastticks = 0;
    int ticks = lastticks + _caca_getticks(&timer);

#if defined(USE_SLANG)
    if(_caca_driver == CACA_DRIVER_SLANG)
    {
        SLsmg_refresh();
    }
    else
#endif
#if defined(USE_NCURSES)
    if(_caca_driver == CACA_DRIVER_NCURSES)
    {
        refresh();
    }
    else
#endif
#if defined(USE_CONIO)
    if(_caca_driver == CACA_DRIVER_CONIO)
    {
#   if defined(SCREENUPDATE_IN_PC_H)
        ScreenUpdate(conio_screen);
#   else
        /* FIXME */
#   endif
    }
    else
#endif
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        unsigned int x, y, len;

        /* First draw the background colours. Splitting the process in two
         * loops like this is actually slightly faster. */
        for(y = 0; y < _caca_height; y++)
        {
            for(x = 0; x < _caca_width; x += len)
            {
                unsigned char *attr = x11_attr + x + y * _caca_width;

                len = 1;
                while(x + len < _caca_width
                       && (attr[len] >> 4) == (attr[0] >> 4))
                    len++;

                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] >> 4]);
                XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
                               x * x11_font_width, y * x11_font_height,
                               len * x11_font_width, x11_font_height);
            }
        }

        /* Then print the foreground characters */
        for(y = 0; y < _caca_height; y++)
        {
            for(x = 0; x < _caca_width; x += len)
            {
                unsigned char *attr = x11_attr + x + y * _caca_width;

                len = 1;

                /* Skip spaces */
                if(x11_char[x + y * _caca_width] == ' ')
                    continue;

                while(x + len < _caca_width
                       && (attr[len] & 0xf) == (attr[0] & 0xf))
                    len++;

                XSetForeground(x11_dpy, x11_gc, x11_colors[attr[0] & 0xf]);
                XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
                            (y + 1) * x11_font_height - x11_font_offset,
                            x11_char + x + y * _caca_width, len);
            }
        }

        XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
                  _caca_width * x11_font_width, _caca_height * x11_font_height,
                  0, 0);
        XFlush(x11_dpy);
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
        COORD size, pos;
        SMALL_RECT rect;
        DWORD dummy;
        unsigned int x, y, len;

        /* Render everything to our back buffer */
        for(y = 0; y < _caca_height; y++)
        {
            pos.X = 0;
            pos.Y = y;
            SetConsoleCursorPosition(win32_back, pos);

            for(x = 0; x < _caca_width; x += len)
            {
                unsigned char *attr = win32_attr + x + y * _caca_width;

                len = 1;
                while(x + len < _caca_width && attr[len] == attr[0])
                    len++;

                SetConsoleTextAttribute(win32_back,
                                        win32_fg_palette[attr[0] & 0xf]
                                         | win32_bg_palette[attr[0] >> 4]);

                WriteConsole(win32_back, win32_char + x + y * _caca_width,
                             len, &dummy, NULL);
            }
        }

        /* Blit the back buffer to the front buffer */
        size.X = _caca_width;
        size.Y = _caca_height;
        pos.X = pos.Y = 0;
        rect.Left = rect.Top = 0;
        rect.Right = _caca_width - 1;
        rect.Bottom = _caca_height - 1;
        ReadConsoleOutput(win32_back, win32_buffer, size, pos, &rect);
        WriteConsoleOutput(win32_front, win32_buffer, size, pos, &rect);
    }
    else
#endif
    {
        /* Dummy */
    }

    if(_caca_resize)
    {
        _caca_resize = 0;
        caca_handle_resize();
    }

    /* Wait until _caca_delay + time of last call */
    ticks += _caca_getticks(&timer);
    for(ticks += _caca_getticks(&timer);
        ticks + IDLE_USEC < (int)_caca_delay;
        ticks += _caca_getticks(&timer))
    {
        _caca_sleep(IDLE_USEC);
    }

    /* Update the sliding mean of the render time */
    _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;

    lastticks = ticks - _caca_delay;

    /* If we drifted too much, it's bad, bad, bad. */
    if(lastticks > (int)_caca_delay)
        lastticks = 0;
}

/*
 * XXX: following functions are loca
 */
static void caca_handle_resize(void)
{
    unsigned int old_width = _caca_width;
    unsigned int old_height = _caca_height;

#if defined(USE_SLANG)
    if(_caca_driver == CACA_DRIVER_SLANG)
    {
        SLtt_get_screen_size();
        _caca_width = SLtt_Screen_Cols;
        _caca_height = SLtt_Screen_Rows;

        if(_caca_width != old_width || _caca_height != old_height)
            SLsmg_reinit_smg();
    }
    else
#endif
#if defined(USE_NCURSES)
    if(_caca_driver == CACA_DRIVER_NCURSES)
    {
        struct winsize size;

        if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
        {
            _caca_width = size.ws_col;
            _caca_height = size.ws_row;
            resize_term(_caca_height, _caca_width);
            wrefresh(curscr);
        }
    }
    else
#endif
#if defined(USE_CONIO)
    if(_caca_driver == CACA_DRIVER_CONIO)
    {
    }
    else
#endif
#if defined(USE_X11)
    if(_caca_driver == CACA_DRIVER_X11)
    {
        Pixmap new_pixmap;

        _caca_width = x11_new_width;
        _caca_height = x11_new_height;

        free(x11_char);
        free(x11_attr);

        new_pixmap = XCreatePixmap(x11_dpy, x11_window,
                                   _caca_width * x11_font_width,
                                   _caca_height * x11_font_height,
                                   DefaultDepth(x11_dpy,
                                                DefaultScreen(x11_dpy)));
        XCopyArea(x11_dpy, x11_pixmap, new_pixmap, x11_gc, 0, 0,
                  old_width * x11_font_width, old_height * x11_font_height,
                  0, 0);
        XFreePixmap(x11_dpy, x11_pixmap);
        x11_pixmap = new_pixmap;

        x11_char = malloc(_caca_width * _caca_height * sizeof(int));
        memset(x11_char, 0, _caca_width * _caca_height * sizeof(int));
        x11_attr = malloc(_caca_width * _caca_height * sizeof(int));
        memset(x11_attr, 0, _caca_width * _caca_height * sizeof(int));
    }
    else
#endif
#if defined(USE_WIN32)
    if(_caca_driver == CACA_DRIVER_WIN32)
    {
    }
    else
#endif
    {
        /* Dummy */
    }

    if(_caca_width != old_width)
    {
        free(_caca_empty_line);
        _caca_empty_line = malloc(_caca_width + 1);
        memset(_caca_empty_line, ' ', _caca_width);
        _caca_empty_line[_caca_width] = '\0';

        free(_caca_scratch_line);
        _caca_scratch_line = malloc(_caca_width + 1);
    }
}

#if defined(USE_SLANG)
static void slang_init_palette(void)
{
    /* See SLang ref., 5.4.4. */
    static char *slang_colors[16] =
    {
        /* Standard colours */
        "black",
        "blue",
        "green",
        "cyan",
        "red",
        "magenta",
        "brown",
        "lightgray",
        /* Bright colours */
        "gray",
        "brightblue",
        "brightgreen",
        "brightcyan",
        "brightred",
        "brightmagenta",
        "yellow",
        "white",
    };

#if defined(OPTIMISE_SLANG_PALETTE)
    int i;

    for(i = 0; i < 16 * 16; i++)
        SLtt_set_color(i, NULL, slang_colors[slang_palette[i * 2]],
                                slang_colors[slang_palette[i * 2 + 1]]);
#else
    int fg, bg;

    for(bg = 0; bg < 16; bg++)
        for(fg = 0; fg < 16; fg++)
        {
            int i = fg + 16 * bg;
            SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
        }
#endif
}
#endif /* USE_SLANG */

#if defined(USE_X11)
static int x11_error_handler(Display *dpy, XErrorEvent *event)
{
    /* Ignore the error */
    return 0;
}
#endif

#if defined(HAVE_SIGNAL) && (defined(USE_NCURSES) || defined(USE_SLANG))
static RETSIGTYPE sigwinch_handler(int sig)
{
    _caca_resize_event = 1;

    signal(SIGWINCH, sigwinch_handler);;
}
#endif


Generated by  Doxygen 1.6.0   Back to index