broken, and event handling is not yet in the driver-specific files, but
I will of course fix that later.
tags/v0.99.beta14
| @@ -11,6 +11,12 @@ libcaca_la_SOURCES = \ | |||
| graphics.c \ | |||
| event.c \ | |||
| time.c \ | |||
| driver_conio.c \ | |||
| driver_gl.c \ | |||
| driver_ncurses.c \ | |||
| driver_slang.c \ | |||
| driver_win32.c \ | |||
| driver_x11.c \ | |||
| $(NULL) | |||
| libcaca_la_CPPFLAGS = -I$(top_srcdir)/cucul | |||
| libcaca_la_LDFLAGS = -no-undefined | |||
| @@ -28,33 +28,6 @@ typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #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 <dos.h> | |||
| # include <conio.h> | |||
| #endif | |||
| #if defined(USE_X11) | |||
| # include <X11/Xlib.h> | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| # include <windows.h> | |||
| #endif | |||
| #if defined(USE_GL) | |||
| # include <GL/gl.h> | |||
| #endif | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| @@ -63,138 +36,33 @@ typedef unsigned char uint8_t; | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| static void caca_init_driver(caca_t *kk); | |||
| static int caca_init_driver(caca_t *kk); | |||
| static void caca_init_terminal(caca_t *kk); | |||
| #if defined(USE_NCURSES) | |||
| static mmask_t oldmask; | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| static CONSOLE_CURSOR_INFO cci; | |||
| #endif | |||
| caca_t * caca_attach(cucul_t * qq) | |||
| { | |||
| int ret; | |||
| caca_t *kk = malloc(sizeof(caca_t)); | |||
| #if defined(USE_NCURSES) | |||
| mmask_t newmask; | |||
| #endif | |||
| caca_init_driver(kk); | |||
| if(kk->driver == CACA_DRIVER_NONE) | |||
| return NULL; | |||
| caca_init_terminal(kk); | |||
| #if defined(USE_SLANG) | |||
| if(kk->driver == CACA_DRIVER_SLANG) | |||
| { | |||
| /* Initialise slang library */ | |||
| SLsig_block_signals(); | |||
| SLtt_get_terminfo(); | |||
| if(SLkp_init() == -1) | |||
| { | |||
| SLsig_unblock_signals(); | |||
| return NULL; | |||
| } | |||
| SLang_init_tty(-1, 0, 1); | |||
| if(SLsmg_init_smg() == -1) | |||
| { | |||
| SLsig_unblock_signals(); | |||
| return NULL; | |||
| } | |||
| SLsig_unblock_signals(); | |||
| ret = caca_init_driver(kk); | |||
| SLsmg_cls(); | |||
| SLtt_set_cursor_visibility(0); | |||
| SLkp_define_keysym("\e[M", 1001); | |||
| SLtt_set_mouse_mode(1, 0); | |||
| SLsmg_refresh(); | |||
| /* Disable scrolling so that hashmap scrolling optimization code | |||
| * does not cause ugly refreshes due to slow terminals */ | |||
| SLtt_Term_Cannot_Scroll = 1; | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver == CACA_DRIVER_NCURSES) | |||
| if(ret) | |||
| { | |||
| initscr(); | |||
| keypad(stdscr, TRUE); | |||
| nonl(); | |||
| raw(); | |||
| noecho(); | |||
| nodelay(stdscr, TRUE); | |||
| curs_set(0); | |||
| /* Activate mouse */ | |||
| newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS; | |||
| mousemask(newmask, &oldmask); | |||
| mouseinterval(-1); /* No click emulation */ | |||
| /* Set the escape delay to a ridiculously low value */ | |||
| ESCDELAY = 10; | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| if(kk->driver == CACA_DRIVER_CONIO) | |||
| { | |||
| _wscroll = 0; | |||
| _setcursortype(_NOCURSOR); | |||
| clrscr(); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_X11) | |||
| if(kk->driver == CACA_DRIVER_X11) | |||
| { | |||
| /* Nothing to do */ | |||
| kk->x11.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | |||
| | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | |||
| | ExposureMask; | |||
| free(kk); | |||
| return NULL; | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| if(kk->driver == CACA_DRIVER_WIN32) | |||
| { | |||
| /* This call is allowed to fail in case we already have a console */ | |||
| AllocConsole(); | |||
| kk->win32.hin = GetStdHandle(STD_INPUT_HANDLE); | |||
| kk->win32.hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, | |||
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |||
| OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |||
| if(kk->win32.hout == INVALID_HANDLE_VALUE) | |||
| return NULL; | |||
| qq->refcount++; | |||
| kk->qq = qq; | |||
| GetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| cci.bVisible = FALSE; | |||
| SetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| /* Only for slang and ncurses */ | |||
| caca_init_terminal(kk); | |||
| SetConsoleMode(kk->win32.hout, ENABLE_MOUSE_INPUT); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_GL) | |||
| if(kk->driver == CACA_DRIVER_GL) | |||
| { | |||
| /* Nothing to do */ | |||
| } | |||
| else | |||
| #endif | |||
| if(_caca_init_graphics(kk)) | |||
| { | |||
| /* Dummy */ | |||
| qq->refcount--; | |||
| free(kk); | |||
| return NULL; | |||
| } | |||
| /* Initialise events stuff */ | |||
| @@ -204,9 +72,6 @@ caca_t * caca_attach(cucul_t * qq) | |||
| kk->events.autorepeat_ticks = 0; | |||
| kk->events.last_key = 0; | |||
| qq->refcount++; | |||
| kk->qq = qq; | |||
| kk->timer.last_sec = 0; | |||
| kk->timer.last_usec = 0; | |||
| kk->lastticks = 0; | |||
| @@ -214,81 +79,13 @@ caca_t * caca_attach(cucul_t * qq) | |||
| kk->resize = 0; | |||
| kk->resize_event = 0; | |||
| if(_caca_init_graphics(kk)) | |||
| return NULL; | |||
| return kk; | |||
| } | |||
| void caca_detach(caca_t *kk) | |||
| { | |||
| _caca_end_graphics(kk); | |||
| #if defined(USE_SLANG) | |||
| if(kk->driver == CACA_DRIVER_SLANG) | |||
| { | |||
| SLtt_set_mouse_mode(0, 0); | |||
| SLtt_set_cursor_visibility(1); | |||
| SLang_reset_tty(); | |||
| SLsmg_reset_smg(); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver == CACA_DRIVER_NCURSES) | |||
| { | |||
| mousemask(oldmask, NULL); | |||
| curs_set(1); | |||
| noraw(); | |||
| endwin(); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| if(kk->driver == CACA_DRIVER_CONIO) | |||
| { | |||
| _wscroll = 1; | |||
| textcolor((enum COLORS)WHITE); | |||
| textbackground((enum COLORS)BLACK); | |||
| gotoxy(_caca_width, _caca_height); | |||
| cputs("\r\n"); | |||
| _setcursortype(_NORMALCURSOR); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_X11) | |||
| if(kk->driver == CACA_DRIVER_X11) | |||
| { | |||
| /* Nothing to do */ | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| if(kk->driver == CACA_DRIVER_WIN32) | |||
| { | |||
| SetConsoleTextAttribute(kk->win32.hout, FOREGROUND_INTENSITY | |||
| | FOREGROUND_RED | |||
| | FOREGROUND_GREEN | |||
| | FOREGROUND_BLUE); | |||
| cci.bVisible = TRUE; | |||
| SetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| CloseHandle(kk->win32.hout); | |||
| } | |||
| else | |||
| #endif | |||
| #if defined(USE_GL) | |||
| if(kk->driver == CACA_DRIVER_GL) | |||
| { | |||
| /* Nothing to do */ | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| /* Dummy */ | |||
| } | |||
| kk->driver.end_graphics(kk); | |||
| kk->qq->refcount--; | |||
| free(kk); | |||
| } | |||
| @@ -296,7 +93,7 @@ void caca_detach(caca_t *kk) | |||
| * XXX: The following functions are local. | |||
| */ | |||
| static void caca_init_driver(caca_t *kk) | |||
| static int caca_init_driver(caca_t *kk) | |||
| { | |||
| #if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP) | |||
| char *var = getenv("CACA_DRIVER"); | |||
| @@ -306,56 +103,55 @@ static void caca_init_driver(caca_t *kk) | |||
| { | |||
| #if defined(USE_WIN32) | |||
| if(!strcasecmp(var, "win32")) | |||
| kk->driver = CACA_DRIVER_WIN32; | |||
| win32_init_driver(kk); | |||
| else | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| if(!strcasecmp(var, "conio")) | |||
| kk->driver = CACA_DRIVER_CONIO; | |||
| conio_init_driver(kk); | |||
| else | |||
| #endif | |||
| #if defined(USE_X11) | |||
| if(!strcasecmp(var, "x11")) | |||
| kk->driver = CACA_DRIVER_X11; | |||
| x11_init_driver(kk); | |||
| else | |||
| #endif | |||
| #if defined(USE_GL) | |||
| if(!strcasecmp(var, "gl")) | |||
| kk->driver = CACA_DRIVER_GL; | |||
| gl_init_driver(kk); | |||
| else | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| if(!strcasecmp(var, "slang")) | |||
| kk->driver = CACA_DRIVER_SLANG; | |||
| slang_init_driver(kk); | |||
| else | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(!strcasecmp(var, "ncurses")) | |||
| kk->driver = CACA_DRIVER_NCURSES; | |||
| ncurses_init_driver(kk); | |||
| else | |||
| #endif | |||
| return -1; | |||
| kk->driver = CACA_DRIVER_NONE; | |||
| return; | |||
| return 0; | |||
| } | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| kk->driver = CACA_DRIVER_WIN32; | |||
| return; | |||
| win32_init_driver(kk); | |||
| return 0; | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| kk->driver = CACA_DRIVER_CONIO; | |||
| return; | |||
| conio_init_driver(kk); | |||
| return 0; | |||
| #endif | |||
| #if defined(USE_X11) | |||
| #if defined(HAVE_GETENV) | |||
| if(getenv("DISPLAY") && *(getenv("DISPLAY"))) | |||
| #endif | |||
| { | |||
| kk->driver = CACA_DRIVER_X11; | |||
| return; | |||
| x11_init_driver(kk); | |||
| return 0; | |||
| } | |||
| #endif | |||
| #if defined(USE_GL) | |||
| @@ -363,21 +159,20 @@ static void caca_init_driver(caca_t *kk) | |||
| if(getenv("DISPLAY") && *(getenv("DISPLAY"))) | |||
| #endif | |||
| { | |||
| kk->driver = CACA_DRIVER_GL; | |||
| return; | |||
| gl_init_driver(kk); | |||
| return 0; | |||
| } | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| kk->driver = CACA_DRIVER_SLANG; | |||
| return; | |||
| slang_init_driver(kk); | |||
| return 0; | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| kk->driver = CACA_DRIVER_NCURSES; | |||
| return; | |||
| slang_init_driver(kk); | |||
| return 0; | |||
| #endif | |||
| kk->driver = CACA_DRIVER_NONE; | |||
| return; | |||
| return -1; | |||
| } | |||
| static void caca_init_terminal(caca_t *kk) | |||
| @@ -388,10 +183,10 @@ static void caca_init_terminal(caca_t *kk) | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| if(kk->driver != CACA_DRIVER_SLANG) | |||
| if(kk->driver.driver != CACA_DRIVER_SLANG) | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver != CACA_DRIVER_NCURSES) | |||
| if(kk->driver.driver != CACA_DRIVER_NCURSES) | |||
| #endif | |||
| return; | |||
| @@ -406,7 +201,7 @@ static void caca_init_terminal(caca_t *kk) | |||
| if(colorterm && !strcmp(colorterm, "gnome-terminal")) | |||
| { | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver == CACA_DRIVER_NCURSES) | |||
| if(kk->driver.driver == CACA_DRIVER_NCURSES) | |||
| { | |||
| SCREEN *screen; | |||
| screen = newterm("xterm-16color", stdout, stdin); | |||
| @@ -424,7 +219,7 @@ static void caca_init_terminal(caca_t *kk) | |||
| if(other) | |||
| { | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver == CACA_DRIVER_NCURSES) | |||
| if(kk->driver.driver == CACA_DRIVER_NCURSES) | |||
| { | |||
| SCREEN *screen; | |||
| screen = newterm("xterm-16color", stdout, stdin); | |||
| @@ -20,8 +20,21 @@ | |||
| #ifndef __CACA_INTERNALS_H__ | |||
| #define __CACA_INTERNALS_H__ | |||
| #if defined(USE_GL) | |||
| # include <GL/glut.h> | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| # if defined(HAVE_NCURSES_H) | |||
| # include <ncurses.h> | |||
| # else | |||
| # include <curses.h> | |||
| # endif | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| # include <windows.h> | |||
| #endif | |||
| #if defined(USE_X11) | |||
| #include <X11/Xlib.h> | |||
| # include <X11/Xlib.h> | |||
| #endif | |||
| /* Graphics driver */ | |||
| @@ -48,6 +61,26 @@ enum caca_driver | |||
| CACA_DRIVER_NONE = 0 | |||
| }; | |||
| /* Available drivers */ | |||
| #if defined(USE_CONIO) | |||
| void conio_init_driver(caca_t *); | |||
| #endif | |||
| #if defined(USE_GL) | |||
| void gl_init_driver(caca_t *); | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| void ncurses_init_driver(caca_t *); | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| void slang_init_driver(caca_t *); | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| void win32_init_driver(caca_t *); | |||
| #endif | |||
| #if defined(USE_X11) | |||
| void x11_init_driver(caca_t *); | |||
| #endif | |||
| /* Timer structure */ | |||
| struct caca_timer | |||
| { | |||
| @@ -59,7 +92,19 @@ struct caca_context | |||
| { | |||
| cucul_t *qq; | |||
| enum caca_driver driver; | |||
| struct driver | |||
| { | |||
| enum caca_driver driver; | |||
| int (* init_graphics) (caca_t *); | |||
| int (* end_graphics) (caca_t *); | |||
| int (* set_window_title) (caca_t *, char const *); | |||
| unsigned int (* get_window_width) (caca_t *); | |||
| unsigned int (* get_window_height) (caca_t *); | |||
| void (* display) (caca_t *); | |||
| void (* handle_resize) (caca_t *); | |||
| } driver; | |||
| unsigned int width, height; | |||
| int resize; | |||
| @@ -102,6 +147,7 @@ struct caca_context | |||
| struct ncurses | |||
| { | |||
| int attr[16*16]; | |||
| mmask_t oldmask; | |||
| } ncurses; | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| @@ -156,15 +202,4 @@ extern unsigned int _caca_height; | |||
| extern int _caca_resize; | |||
| extern int _caca_resize_event; | |||
| #if defined(USE_WIN32) | |||
| #include <windows.h> | |||
| extern HANDLE win32_hin, win32_hout; | |||
| #endif | |||
| #if defined(USE_GL) | |||
| #include <GL/glut.h> | |||
| extern unsigned int gl_width, gl_height; | |||
| #endif | |||
| #endif /* __CACA_INTERNALS_H__ */ | |||
| @@ -0,0 +1,148 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| #include <dos.h> | |||
| #include <conio.h> | |||
| #if defined(SCREENUPDATE_IN_PC_H) | |||
| # include <pc.h> | |||
| #endif | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #if defined(HAVE_UNISTD_H) | |||
| # include <unistd.h> | |||
| #endif | |||
| #include <stdarg.h> | |||
| #if defined(HAVE_SYS_IOCTL_H) | |||
| # include <sys/ioctl.h> | |||
| #endif | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| #if !defined(_DOXYGEN_SKIP_ME) | |||
| int conio_init_graphics(caca_t *kk) | |||
| { | |||
| _wscroll = 0; | |||
| _setcursortype(_NOCURSOR); | |||
| clrscr(); | |||
| gettextinfo(&kk->conio.ti); | |||
| kk->conio.screen = malloc(2 * kk->conio.ti.screenwidth | |||
| * kk->conio.ti.screenheight * sizeof(char)); | |||
| if(kk->conio.screen == NULL) | |||
| return -1; | |||
| # if defined(SCREENUPDATE_IN_PC_H) | |||
| ScreenRetrieve(kk->conio.screen); | |||
| # else | |||
| /* FIXME */ | |||
| # endif | |||
| cucul_set_size(kk->qq, kk->conio.ti.screenwidth, | |||
| kk->conio.ti.screenheight); | |||
| return 0; | |||
| } | |||
| int conio_end_graphics(caca_t *kk) | |||
| { | |||
| _wscroll = 1; | |||
| textcolor((enum COLORS)WHITE); | |||
| textbackground((enum COLORS)BLACK); | |||
| gotoxy(_caca_width, _caca_height); | |||
| cputs("\r\n"); | |||
| _setcursortype(_NORMALCURSOR); | |||
| free(kk->conio.screen); | |||
| return 0; | |||
| } | |||
| #endif /* _DOXYGEN_SKIP_ME */ | |||
| int conio_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| return 0; | |||
| } | |||
| unsigned int conio_get_window_width(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->width * 6; | |||
| } | |||
| unsigned int conio_get_window_height(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->height * 10; | |||
| } | |||
| void conio_display(caca_t *kk) | |||
| { | |||
| int n; | |||
| char *screen = kk->conio.screen; | |||
| uint8_t *attr = kk->qq->attr; | |||
| uint32_t *chars = kk->qq->chars; | |||
| for(n = kk->qq->height * kk->qq->width; n--; ) | |||
| { | |||
| *screen++ = *chars++ & 0x7f; | |||
| *screen++ = *attr++; | |||
| } | |||
| # if defined(SCREENUPDATE_IN_PC_H) | |||
| ScreenUpdate(kk->conio.screen); | |||
| # else | |||
| /* FIXME */ | |||
| # endif | |||
| } | |||
| void conio_handle_resize(caca_t *kk) | |||
| { | |||
| return; | |||
| } | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void conio_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_CONIO; | |||
| kk->driver.init_graphics = conio_init_graphics; | |||
| kk->driver.end_graphics = conio_end_graphics; | |||
| kk->driver.set_window_title = conio_set_window_title; | |||
| kk->driver.get_window_width = conio_get_window_width; | |||
| kk->driver.get_window_height = conio_get_window_height; | |||
| kk->driver.display = conio_display; | |||
| kk->driver.handle_resize = conio_handle_resize; | |||
| } | |||
| #endif /* USE_CONIO */ | |||
| @@ -0,0 +1,378 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_GL) | |||
| #include <GL/gl.h> | |||
| #include <GL/glut.h> | |||
| #include <GL/freeglut_ext.h> | |||
| #include <stdio.h> /* BUFSIZ */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #if defined(HAVE_UNISTD_H) | |||
| # include <unistd.h> | |||
| #endif | |||
| #include <stdarg.h> | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| /* | |||
| * Global variables | |||
| */ | |||
| /* Ok, I just suck. */ | |||
| static GLbyte const gl_bgpal[][4] = | |||
| { | |||
| { 0x00, 0x00, 0x00, 0x7f }, | |||
| { 0x00, 0x00, 0x3f, 0x7f }, | |||
| { 0x00, 0x3f, 0x00, 0x7f }, | |||
| { 0x00, 0x3f, 0x3f, 0x7f }, | |||
| { 0x3f, 0x00, 0x00, 0x7f }, | |||
| { 0x3f, 0x00, 0x3f, 0x7f }, | |||
| { 0x3f, 0x3f, 0x00, 0x7f }, | |||
| { 0x3f, 0x3f, 0x3f, 0x7f }, | |||
| // + intensity | |||
| { 0x00, 0x00, 0x00, 0x7f }, | |||
| { 0x00, 0x00, 0x7f, 0x7f }, | |||
| { 0x00, 0x7f, 0x00, 0x7f }, | |||
| { 0x00, 0x7f, 0x7f, 0x7f }, | |||
| { 0x7f, 0x00, 0x00, 0x7f }, | |||
| { 0x7f, 0x00, 0x7f, 0x7f }, | |||
| { 0x7f, 0x7f, 0x00, 0x7f }, | |||
| { 0x7f, 0x7f, 0x7f, 0x7f } | |||
| }; | |||
| static caca_t *gl_kk; /* FIXME: we ought to get rid of this */ | |||
| /* | |||
| * Local functions | |||
| */ | |||
| static void gl_handle_keyboard(unsigned char, int, int); | |||
| static void gl_handle_special_key(int, int, int); | |||
| static void gl_handle_reshape(int, int); | |||
| static void gl_handle_mouse(int, int, int, int); | |||
| static void gl_handle_mouse_motion(int, int); | |||
| int gl_init_graphics(caca_t *kk) | |||
| { | |||
| char *empty_texture; | |||
| char const *geometry; | |||
| char *argv[2] = { "", NULL }; | |||
| unsigned int width = 0, height = 0; | |||
| int argc = 1; | |||
| int i; | |||
| gl_kk = kk; | |||
| geometry = getenv("CACA_GEOMETRY"); | |||
| if(geometry && *(geometry)) | |||
| sscanf(geometry, "%ux%u", &width, &height); | |||
| if(width && height) | |||
| cucul_set_size(kk->qq, width, height); | |||
| kk->gl.font_width = 9; | |||
| kk->gl.font_height = 15; | |||
| kk->gl.width = kk->qq->width * kk->gl.font_width; | |||
| kk->gl.height = kk->qq->height * kk->gl.font_height; | |||
| kk->gl.resized = 0; | |||
| kk->gl.bit = 0; | |||
| kk->gl.mouse_changed = kk->gl.mouse_clicked = 0; | |||
| kk->gl.mouse_button = kk->gl.mouse_state = 0; | |||
| kk->gl.key = 0; | |||
| kk->gl.special_key = 0; | |||
| kk->gl.sw = 9.0f / 16.0f; | |||
| kk->gl.sh = 15.0f / 16.0f; | |||
| glutInit(&argc, argv); | |||
| glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); | |||
| glutInitWindowSize(kk->gl.width, kk->gl.height); | |||
| kk->gl.window = glutCreateWindow("caca for GL"); | |||
| gluOrtho2D(0, kk->gl.width, kk->gl.height, 0); | |||
| glDisable(GL_CULL_FACE); | |||
| glDisable(GL_DEPTH_TEST); | |||
| glutKeyboardFunc(gl_handle_keyboard); | |||
| glutSpecialFunc(gl_handle_special_key); | |||
| glutReshapeFunc(gl_handle_reshape); | |||
| glutMouseFunc(gl_handle_mouse); | |||
| glutMotionFunc(gl_handle_mouse_motion); | |||
| glutPassiveMotionFunc(gl_handle_mouse_motion); | |||
| glLoadIdentity(); | |||
| glMatrixMode(GL_PROJECTION); | |||
| glPushMatrix(); | |||
| glLoadIdentity(); | |||
| gluOrtho2D(0, kk->gl.width, kk->gl.height, 0); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| glClear(GL_COLOR_BUFFER_BIT); | |||
| empty_texture = malloc(16 * 16 * 4); | |||
| if(empty_texture == NULL) | |||
| return -1; | |||
| memset(empty_texture, 0xff, 16 * 16 * 4); | |||
| glEnable(GL_TEXTURE_2D); | |||
| for(i = 0; i < 94; i++) | |||
| { | |||
| glGenTextures(1, (GLuint*)&kk->gl.id[i]); | |||
| glBindTexture(GL_TEXTURE_2D, kk->gl.id[i]); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, | |||
| 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, empty_texture); | |||
| } | |||
| for(i = 0; i < 94; i++) | |||
| { | |||
| glDisable(GL_TEXTURE_2D); | |||
| glClear(GL_COLOR_BUFFER_BIT); | |||
| glColor3f(1, 1, 1); | |||
| glRasterPos2f(0, 15); | |||
| glutBitmapCharacter(GLUT_BITMAP_9_BY_15, i + 32); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBindTexture(GL_TEXTURE_2D, kk->gl.id[i]); | |||
| glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, | |||
| 0, kk->gl.height - 16, 16, 16, 0); | |||
| glutMainLoopEvent(); | |||
| glutPostRedisplay(); | |||
| } | |||
| return 0; | |||
| } | |||
| int gl_end_graphics(caca_t *kk) | |||
| { | |||
| glutDestroyWindow(kk->gl.window); | |||
| return 0; | |||
| } | |||
| int gl_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| glutSetWindowTitle(title); | |||
| return 0; | |||
| } | |||
| unsigned int gl_get_window_width(caca_t *kk) | |||
| { | |||
| return kk->gl.width; | |||
| } | |||
| unsigned int gl_get_window_height(caca_t *kk) | |||
| { | |||
| return kk->gl.height; | |||
| } | |||
| void gl_display(caca_t *kk) | |||
| { | |||
| unsigned int x, y, line; | |||
| glClear(GL_COLOR_BUFFER_BIT); | |||
| line = 0; | |||
| for(y = 0; y < kk->gl.height; y += kk->gl.font_height) | |||
| { | |||
| uint8_t *attr = kk->qq->attr + line * kk->qq->width; | |||
| for(x = 0; x < kk->gl.width; x += kk->gl.font_width) | |||
| { | |||
| glDisable(GL_TEXTURE_2D); | |||
| glColor4bv(gl_bgpal[attr[0] >> 4]); | |||
| glBegin(GL_QUADS); | |||
| glVertex2f(x, y); | |||
| glVertex2f(x + kk->gl.font_width, y); | |||
| glVertex2f(x + kk->gl.font_width, y + kk->gl.font_height); | |||
| glVertex2f(x, y + kk->gl.font_height); | |||
| glEnd(); | |||
| attr++; | |||
| } | |||
| line++; | |||
| } | |||
| /* 2nd pass, avoids changing render state too much */ | |||
| glEnable(GL_BLEND); | |||
| glEnable(GL_TEXTURE_2D); | |||
| glBlendFunc(GL_ONE, GL_ONE); | |||
| line = 0; | |||
| for(y = 0; y < kk->gl.height; y += kk->gl.font_height) | |||
| { | |||
| uint8_t *attr = kk->qq->attr + line * kk->qq->width; | |||
| uint32_t *chars = kk->qq->chars + line * kk->qq->width; | |||
| for(x = 0; x < kk->gl.width; x += kk->gl.font_width) | |||
| { | |||
| if(*chars != (uint32_t)' ') | |||
| { | |||
| char ch = *chars & 0x7f; | |||
| /* FIXME: check ch bounds */ | |||
| glBindTexture(GL_TEXTURE_2D, kk->gl.id[ch - 32]); | |||
| glColor4bv(gl_bgpal[attr[0] & 0xf]); | |||
| glBegin(GL_QUADS); | |||
| glTexCoord2f(0, kk->gl.sh); | |||
| glVertex2f(x, y); | |||
| glTexCoord2f(kk->gl.sw, kk->gl.sh); | |||
| glVertex2f(x + kk->gl.font_width, y); | |||
| glTexCoord2f(kk->gl.sw, 0); | |||
| glVertex2f(x + kk->gl.font_width, y + kk->gl.font_height); | |||
| glTexCoord2f(0, 0); | |||
| glVertex2f(x, y + kk->gl.font_height); | |||
| glEnd(); | |||
| } | |||
| attr++; | |||
| chars++; | |||
| } | |||
| line++; | |||
| } | |||
| glDisable(GL_BLEND); | |||
| glDisable(GL_TEXTURE_2D); | |||
| glutMainLoopEvent(); | |||
| glutSwapBuffers(); | |||
| glutPostRedisplay(); | |||
| } | |||
| void gl_handle_resize(caca_t *kk) | |||
| { | |||
| unsigned int new_width, new_height; | |||
| new_width = kk->qq->width; | |||
| new_height = kk->qq->height; | |||
| kk->gl.width = kk->gl.new_width; | |||
| kk->gl.height = kk->gl.new_height; | |||
| new_width = kk->gl.width / kk->gl.font_width; | |||
| new_height = (kk->gl.height / kk->gl.font_height) + 1; | |||
| glMatrixMode(GL_PROJECTION); | |||
| glPushMatrix(); | |||
| glLoadIdentity(); | |||
| glViewport(0, 0, kk->gl.width, kk->gl.height); | |||
| gluOrtho2D(0, kk->gl.width, kk->gl.height, 0); | |||
| glMatrixMode(GL_MODELVIEW); | |||
| } | |||
| /* | |||
| * XXX: following functions are local | |||
| */ | |||
| static void gl_handle_keyboard(unsigned char key, int x, int y) | |||
| { | |||
| caca_t *kk = gl_kk; | |||
| kk->gl.key = key; | |||
| } | |||
| static void gl_handle_special_key(int key, int x, int y) | |||
| { | |||
| caca_t *kk = gl_kk; | |||
| kk->gl.special_key = key; | |||
| } | |||
| static void gl_handle_reshape(int w, int h) | |||
| { | |||
| caca_t *kk = gl_kk; | |||
| if(kk->gl.bit) /* Do not handle reshaping at the first time */ | |||
| { | |||
| kk->gl.new_width = w; | |||
| kk->gl.new_height = h; | |||
| kk->gl.resized = 1; | |||
| } | |||
| else | |||
| kk->gl.bit = 1; | |||
| } | |||
| static void gl_handle_mouse(int button, int state, int x, int y) | |||
| { | |||
| caca_t *kk = gl_kk; | |||
| kk->gl.mouse_clicked = 1; | |||
| kk->gl.mouse_button = button; | |||
| kk->gl.mouse_state = state; | |||
| kk->gl.mouse_x = x / kk->gl.font_width; | |||
| kk->gl.mouse_y = y / kk->gl.font_height; | |||
| kk->gl.mouse_changed = 1; | |||
| } | |||
| static void gl_handle_mouse_motion(int x, int y) | |||
| { | |||
| caca_t *kk = gl_kk; | |||
| kk->gl.mouse_x = x / kk->gl.font_width; | |||
| kk->gl.mouse_y = y / kk->gl.font_height; | |||
| kk->gl.mouse_changed = 1; | |||
| } | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void gl_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_GL; | |||
| kk->driver.init_graphics = gl_init_graphics; | |||
| kk->driver.end_graphics = gl_end_graphics; | |||
| kk->driver.set_window_title = gl_set_window_title; | |||
| kk->driver.get_window_width = gl_get_window_width; | |||
| kk->driver.get_window_height = gl_get_window_height; | |||
| kk->driver.display = gl_display; | |||
| kk->driver.handle_resize = gl_handle_resize; | |||
| } | |||
| #endif /* USE_GL */ | |||
| @@ -0,0 +1,256 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| #if defined(HAVE_NCURSES_H) | |||
| # include <ncurses.h> | |||
| #else | |||
| # include <curses.h> | |||
| #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" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| int ncurses_init_graphics(caca_t *); | |||
| int ncurses_end_graphics(caca_t *); | |||
| int ncurses_set_window_title(caca_t *, char const *); | |||
| unsigned int ncurses_get_window_width(caca_t *); | |||
| unsigned int ncurses_get_window_height(caca_t *); | |||
| void ncurses_display(caca_t *); | |||
| void ncurses_handle_resize(caca_t *); | |||
| /* | |||
| * Local functions | |||
| */ | |||
| #if defined(HAVE_SIGNAL) | |||
| static RETSIGTYPE sigwinch_handler(int); | |||
| static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */ | |||
| #endif | |||
| int ncurses_init_graphics(caca_t *kk) | |||
| { | |||
| 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 | |||
| }; | |||
| mmask_t newmask; | |||
| int fg, bg, max; | |||
| #if defined(HAVE_SIGNAL) | |||
| sigwinch_kk = kk; | |||
| signal(SIGWINCH, sigwinch_handler); | |||
| #endif | |||
| initscr(); | |||
| keypad(stdscr, TRUE); | |||
| nonl(); | |||
| raw(); | |||
| noecho(); | |||
| nodelay(stdscr, TRUE); | |||
| curs_set(0); | |||
| /* Activate mouse */ | |||
| newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS; | |||
| mousemask(newmask, &kk->ncurses.oldmask); | |||
| mouseinterval(-1); /* No click emulation */ | |||
| /* Set the escape delay to a ridiculously low value */ | |||
| ESCDELAY = 10; | |||
| /* 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. 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]); | |||
| kk->ncurses.attr[fg + 16 * bg] = COLOR_PAIR(col); | |||
| if(max == 8) | |||
| { | |||
| /* Bright fg on simple bg */ | |||
| kk->ncurses.attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col); | |||
| /* Simple fg on bright bg */ | |||
| kk->ncurses.attr[fg + 16 * (bg + 8)] = A_BLINK | |||
| | COLOR_PAIR(col); | |||
| /* Bright fg on bright bg */ | |||
| kk->ncurses.attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD | |||
| | COLOR_PAIR(col); | |||
| } | |||
| } | |||
| cucul_set_size(kk->qq, COLS, LINES); | |||
| return 0; | |||
| } | |||
| int ncurses_end_graphics(caca_t *kk) | |||
| { | |||
| mousemask(kk->ncurses.oldmask, NULL); | |||
| curs_set(1); | |||
| noraw(); | |||
| endwin(); | |||
| return 0; | |||
| } | |||
| int ncurses_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| return 0; | |||
| } | |||
| unsigned int ncurses_get_window_width(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->width * 6; | |||
| } | |||
| unsigned int ncurses_get_window_height(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->height * 10; | |||
| } | |||
| void ncurses_display(caca_t *kk) | |||
| { | |||
| int x, y; | |||
| uint8_t *attr = kk->qq->attr; | |||
| uint32_t *chars = kk->qq->chars; | |||
| for(y = 0; y < (int)kk->qq->height; y++) | |||
| { | |||
| move(y, 0); | |||
| for(x = kk->qq->width; x--; ) | |||
| { | |||
| attrset(kk->ncurses.attr[*attr++]); | |||
| addch(*chars++ & 0x7f); | |||
| } | |||
| } | |||
| refresh(); | |||
| } | |||
| void ncurses_handle_resize(caca_t *kk) | |||
| { | |||
| unsigned int new_width, new_height; | |||
| struct winsize size; | |||
| new_width = kk->qq->width; | |||
| new_height = kk->qq->height; | |||
| if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) | |||
| { | |||
| new_width = size.ws_col; | |||
| new_height = size.ws_row; | |||
| #if defined(HAVE_RESIZE_TERM) | |||
| resize_term(new_height, new_width); | |||
| #else | |||
| resizeterm(new_height, new_width); | |||
| #endif | |||
| wrefresh(curscr); | |||
| } | |||
| } | |||
| /* | |||
| * XXX: following functions are local | |||
| */ | |||
| #if defined(HAVE_SIGNAL) | |||
| static RETSIGTYPE sigwinch_handler(int sig) | |||
| { | |||
| sigwinch_kk->resize_event = 1; | |||
| signal(SIGWINCH, sigwinch_handler);; | |||
| } | |||
| #endif | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void ncurses_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_NCURSES; | |||
| kk->driver.init_graphics = ncurses_init_graphics; | |||
| kk->driver.end_graphics = ncurses_end_graphics; | |||
| kk->driver.set_window_title = ncurses_set_window_title; | |||
| kk->driver.get_window_width = ncurses_get_window_width; | |||
| kk->driver.get_window_height = ncurses_get_window_height; | |||
| kk->driver.display = ncurses_display; | |||
| kk->driver.handle_resize = ncurses_handle_resize; | |||
| } | |||
| #endif /* USE_NCURSES */ | |||
| @@ -0,0 +1,342 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| #if defined(HAVE_SLANG_SLANG_H) | |||
| # include <slang/slang.h> | |||
| #else | |||
| # include <slang.h> | |||
| #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 | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| /* | |||
| * Global variables | |||
| */ | |||
| /* 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, | |||
| }; | |||
| int slang_init_graphics(caca_t *); | |||
| int slang_end_graphics(caca_t *); | |||
| int slang_set_window_title(caca_t *, char const *); | |||
| unsigned int slang_get_window_width(caca_t *); | |||
| unsigned int slang_get_window_height(caca_t *); | |||
| void slang_display(caca_t *); | |||
| void slang_handle_resize(caca_t *); | |||
| /* | |||
| * Local functions | |||
| */ | |||
| static void slang_init_palette(void); | |||
| #if defined(HAVE_SIGNAL) | |||
| static RETSIGTYPE sigwinch_handler(int); | |||
| static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */ | |||
| #endif | |||
| #if !defined(_DOXYGEN_SKIP_ME) | |||
| int slang_init_graphics(caca_t *kk) | |||
| { | |||
| #if defined(HAVE_SIGNAL) | |||
| sigwinch_kk = kk; | |||
| signal(SIGWINCH, sigwinch_handler); | |||
| #endif | |||
| /* Initialise slang library */ | |||
| SLsig_block_signals(); | |||
| SLtt_get_terminfo(); | |||
| if(SLkp_init() == -1) | |||
| { | |||
| SLsig_unblock_signals(); | |||
| return NULL; | |||
| } | |||
| SLang_init_tty(-1, 0, 1); | |||
| if(SLsmg_init_smg() == -1) | |||
| { | |||
| SLsig_unblock_signals(); | |||
| return NULL; | |||
| } | |||
| SLsig_unblock_signals(); | |||
| SLsmg_cls(); | |||
| SLtt_set_cursor_visibility(0); | |||
| SLkp_define_keysym("\e[M", 1001); | |||
| SLtt_set_mouse_mode(1, 0); | |||
| SLsmg_refresh(); | |||
| /* Disable scrolling so that hashmap scrolling optimization code | |||
| * does not cause ugly refreshes due to slow terminals */ | |||
| SLtt_Term_Cannot_Scroll = 1; | |||
| slang_init_palette(); | |||
| /* Disable alt charset support so that we get a chance to have all | |||
| * 256 colour pairs */ | |||
| SLtt_Has_Alt_Charset = 0; | |||
| cucul_set_size(kk->qq, SLtt_Screen_Cols, SLtt_Screen_Rows); | |||
| return 0; | |||
| } | |||
| int slang_end_graphics(caca_t *kk) | |||
| { | |||
| SLtt_set_mouse_mode(0, 0); | |||
| SLtt_set_cursor_visibility(1); | |||
| SLang_reset_tty(); | |||
| SLsmg_reset_smg(); | |||
| return 0; | |||
| } | |||
| #endif /* _DOXYGEN_SKIP_ME */ | |||
| int slang_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| return 0; | |||
| } | |||
| unsigned int slang_get_window_width(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->width * 6; | |||
| } | |||
| unsigned int slang_get_window_height(caca_t *kk) | |||
| { | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->height * 10; | |||
| } | |||
| void slang_display(caca_t *kk) | |||
| { | |||
| int x, y; | |||
| uint8_t *attr = kk->qq->attr; | |||
| uint32_t *chars = kk->qq->chars; | |||
| for(y = 0; y < (int)kk->qq->height; y++) | |||
| { | |||
| SLsmg_gotorc(y, 0); | |||
| for(x = kk->qq->width; x--; ) | |||
| { | |||
| #if defined(OPTIMISE_SLANG_PALETTE) | |||
| /* If foreground == background, just don't use this colour | |||
| * pair, and print a space instead of the real character. */ | |||
| uint8_t fgcolor = *attr & 0xf; | |||
| uint8_t bgcolor = *attr >> 4; | |||
| if(fgcolor != bgcolor) | |||
| { | |||
| SLsmg_set_color(slang_assoc[*attr++]); | |||
| SLsmg_write_char(*chars++ & 0x7f); | |||
| } | |||
| else | |||
| { | |||
| if(fgcolor == CUCUL_COLOR_BLACK) | |||
| fgcolor = CUCUL_COLOR_WHITE; | |||
| else if(fgcolor == CUCUL_COLOR_WHITE | |||
| || fgcolor <= CUCUL_COLOR_LIGHTGRAY) | |||
| fgcolor = CUCUL_COLOR_BLACK; | |||
| else | |||
| fgcolor = CUCUL_COLOR_WHITE; | |||
| SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]); | |||
| SLsmg_write_char(' '); | |||
| chars++; | |||
| attr++; | |||
| } | |||
| #else | |||
| SLsmg_set_color(*attr++); | |||
| SLsmg_write_char(*chars++ & 0x7f); | |||
| #endif | |||
| } | |||
| } | |||
| SLsmg_refresh(); | |||
| } | |||
| /* | |||
| * XXX: following functions are local | |||
| */ | |||
| void slang_handle_resize(caca_t *kk) | |||
| { | |||
| unsigned int new_width, new_height; | |||
| new_width = kk->qq->width; | |||
| new_height = kk->qq->height; | |||
| SLtt_get_screen_size(); | |||
| new_width = SLtt_Screen_Cols; | |||
| new_height = SLtt_Screen_Rows; | |||
| if(new_width != kk->qq->width || new_height != kk->qq->height) | |||
| SLsmg_reinit_smg(); | |||
| } | |||
| 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 | |||
| } | |||
| #if defined(HAVE_SIGNAL) | |||
| static RETSIGTYPE sigwinch_handler(int sig) | |||
| { | |||
| sigwinch_kk->resize_event = 1; | |||
| signal(SIGWINCH, sigwinch_handler);; | |||
| } | |||
| #endif | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void slang_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_SLANG; | |||
| kk->driver.init_graphics = slang_init_graphics; | |||
| kk->driver.end_graphics = slang_end_graphics; | |||
| kk->driver.set_window_title = slang_set_window_title; | |||
| kk->driver.get_window_width = slang_get_window_width; | |||
| kk->driver.get_window_height = slang_get_window_height; | |||
| kk->driver.display = slang_display; | |||
| kk->driver.handle_resize = slang_handle_resize; | |||
| } | |||
| #endif /* USE_SLANG */ | |||
| @@ -0,0 +1,255 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| #include <windows.h> | |||
| #include <stdio.h> /* BUFSIZ */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #if defined(HAVE_UNISTD_H) | |||
| # include <unistd.h> | |||
| #endif | |||
| #include <stdarg.h> | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| /* | |||
| * Global variables | |||
| */ | |||
| 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 | |||
| }; | |||
| int win32_init_graphics(caca_t *); | |||
| int win32_end_graphics(caca_t *); | |||
| int win32_set_window_title(caca_t *, char const *); | |||
| unsigned int win32_get_window_width(caca_t *); | |||
| unsigned int win32_get_window_height(caca_t *); | |||
| void win32_display(caca_t *); | |||
| void win32_handle_resize(caca_t *); | |||
| int win32_init_graphics(caca_t *kk) | |||
| { | |||
| CONSOLE_CURSOR_INFO cci; | |||
| CONSOLE_SCREEN_BUFFER_INFO csbi; | |||
| COORD size; | |||
| /* This call is allowed to fail in case we already have a console */ | |||
| AllocConsole(); | |||
| kk->win32.hin = GetStdHandle(STD_INPUT_HANDLE); | |||
| kk->win32.hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, | |||
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |||
| OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |||
| if(kk->win32.hout == INVALID_HANDLE_VALUE) | |||
| return -1; | |||
| GetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| cci.bVisible = FALSE; | |||
| SetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| SetConsoleMode(kk->win32.hout, ENABLE_MOUSE_INPUT); | |||
| kk->win32.front = | |||
| CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, | |||
| 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); | |||
| if(!kk->win32.front || kk->win32.front == INVALID_HANDLE_VALUE) | |||
| return -1; | |||
| kk->win32.back = | |||
| CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, | |||
| 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); | |||
| if(!kk->win32.back || kk->win32.back == INVALID_HANDLE_VALUE) | |||
| return -1; | |||
| if(!GetConsoleScreenBufferInfo(kk->win32.hout, &csbi)) | |||
| return -1; | |||
| /* Sample code to get the biggest possible window */ | |||
| //size = GetLargestConsoleWindowSize(kk->win32.hout); | |||
| cucul_set_size(kk->qq, csbi.srWindow.Right - csbi.srWindow.Left + 1, | |||
| csbi.srWindow.Bottom - csbi.srWindow.Top + 1); | |||
| size.X = kk->qq->width; | |||
| size.Y = kk->qq->height; | |||
| SetConsoleScreenBufferSize(kk->win32.front, size); | |||
| SetConsoleScreenBufferSize(kk->win32.back, size); | |||
| SetConsoleMode(kk->win32.front, 0); | |||
| SetConsoleMode(kk->win32.back, 0); | |||
| GetConsoleCursorInfo(kk->win32.front, &cci); | |||
| cci.dwSize = 0; | |||
| cci.bVisible = FALSE; | |||
| SetConsoleCursorInfo(kk->win32.front, &cci); | |||
| SetConsoleCursorInfo(kk->win32.back, &cci); | |||
| SetConsoleActiveScreenBuffer(kk->win32.front); | |||
| kk->win32.buffer = malloc(kk->qq->width * kk->qq->height | |||
| * sizeof(CHAR_INFO)); | |||
| if(kk->win32.buffer == NULL) | |||
| return -1; | |||
| return 0; | |||
| } | |||
| int win32_end_graphics(caca_t *kk) | |||
| { | |||
| SetConsoleActiveScreenBuffer(kk->win32.hout); | |||
| CloseHandle(kk->win32.back); | |||
| CloseHandle(kk->win32.front); | |||
| SetConsoleTextAttribute(kk->win32.hout, FOREGROUND_INTENSITY | |||
| | FOREGROUND_RED | |||
| | FOREGROUND_GREEN | |||
| | FOREGROUND_BLUE); | |||
| cci.bVisible = TRUE; | |||
| SetConsoleCursorInfo(kk->win32.hout, &cci); | |||
| CloseHandle(kk->win32.hout); | |||
| return 0; | |||
| } | |||
| int win32_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| SetConsoleTitle(title); | |||
| return 0; | |||
| } | |||
| unsigned int win32_get_window_width(caca_t *kk) | |||
| { | |||
| /* FIXME */ | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->width * 6; | |||
| } | |||
| unsigned int win32_get_window_height(caca_t *kk) | |||
| { | |||
| /* FIXME */ | |||
| /* Fallback to a 6x10 font */ | |||
| return kk->qq->height * 10; | |||
| } | |||
| void win32_display(caca_t *kk) | |||
| { | |||
| COORD size, pos; | |||
| SMALL_RECT rect; | |||
| unsigned int i; | |||
| /* Render everything to our back buffer */ | |||
| for(i = 0; i < kk->qq->width * kk->qq->height; i++) | |||
| { | |||
| kk->win32.buffer[i].Char.AsciiChar = kk->qq->chars[i] & 0x7f; | |||
| kk->win32.buffer[i].Attributes = | |||
| win32_fg_palette[kk->qq->attr[i] & 0xf] | |||
| | win32_bg_palette[kk->qq->attr[i] >> 4]; | |||
| } | |||
| /* Blit the back buffer to the front buffer */ | |||
| size.X = kk->qq->width; | |||
| size.Y = kk->qq->height; | |||
| pos.X = pos.Y = 0; | |||
| rect.Left = rect.Top = 0; | |||
| rect.Right = kk->qq->width - 1; | |||
| rect.Bottom = kk->qq->height - 1; | |||
| WriteConsoleOutput(kk->win32.front, kk->win32.buffer, size, pos, &rect); | |||
| } | |||
| void win32_handle_resize(caca_t *kk) | |||
| { | |||
| unsigned int new_width, new_height; | |||
| new_width = kk->qq->width; | |||
| new_height = kk->qq->height; | |||
| /* Nothing to do here. */ | |||
| } | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void win32_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_WIN32; | |||
| kk->driver.init_graphics = win32_init_graphics; | |||
| kk->driver.end_graphics = win32_end_graphics; | |||
| kk->driver.set_window_title = win32_set_window_title; | |||
| kk->driver.get_window_width = win32_get_window_width; | |||
| kk->driver.get_window_height = win32_get_window_height; | |||
| kk->driver.display = win32_display; | |||
| kk->driver.handle_resize = win32_handle_resize; | |||
| } | |||
| #endif /* USE_WIN32 */ | |||
| @@ -0,0 +1,363 @@ | |||
| /* | |||
| * libcaca ASCII-Art library | |||
| * Copyright (c) 2002-2006 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 Do What The Fuck You Want To | |||
| * Public License, Version 2, as published by Sam Hocevar. See | |||
| * http://sam.zoy.org/wtfpl/COPYING for more details. | |||
| */ | |||
| /** \file graphics.c | |||
| * \version \$Id$ | |||
| * \author Sam Hocevar <sam@zoy.org> | |||
| * \brief Character drawing | |||
| * | |||
| * This file contains character and string drawing functions. | |||
| */ | |||
| #include "config.h" | |||
| #if defined(HAVE_INTTYPES_H) || defined(_DOXYGEN_SKIP_ME) | |||
| # include <inttypes.h> | |||
| #else | |||
| typedef unsigned int uint32_t; | |||
| typedef unsigned char uint8_t; | |||
| #endif | |||
| #if defined(USE_X11) | |||
| #include <X11/Xlib.h> | |||
| #if defined(HAVE_X11_XKBLIB_H) | |||
| # include <X11/XKBlib.h> | |||
| #endif | |||
| #include <stdio.h> /* BUFSIZ */ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #if defined(HAVE_UNISTD_H) | |||
| # include <unistd.h> | |||
| #endif | |||
| #include <stdarg.h> | |||
| #include "caca.h" | |||
| #include "caca_internals.h" | |||
| #include "cucul.h" | |||
| #include "cucul_internals.h" | |||
| int x11_init_graphics(caca_t *); | |||
| int x11_end_graphics(caca_t *); | |||
| int x11_set_window_title(caca_t *, char const *); | |||
| unsigned int x11_get_window_width(caca_t *); | |||
| unsigned int x11_get_window_height(caca_t *); | |||
| void x11_display(caca_t *); | |||
| void x11_handle_resize(caca_t *); | |||
| /* | |||
| * Local functions | |||
| */ | |||
| static int x11_error_handler(Display *, XErrorEvent *); | |||
| #if !defined(_DOXYGEN_SKIP_ME) | |||
| int x11_init_graphics(caca_t *kk) | |||
| { | |||
| static int const 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 *fonts[] = { NULL, "8x13bold", "fixed" }, **parser; | |||
| char const *geometry; | |||
| unsigned int width = 0, height = 0; | |||
| int i; | |||
| geometry = getenv("CACA_GEOMETRY"); | |||
| if(geometry && *(geometry)) | |||
| sscanf(geometry, "%ux%u", &width, &height); | |||
| if(width && height) | |||
| cucul_set_size(kk->qq, width, height); | |||
| kk->x11.dpy = XOpenDisplay(NULL); | |||
| if(kk->x11.dpy == NULL) | |||
| return -1; | |||
| fonts[0] = getenv("CACA_FONT"); | |||
| if(fonts[0] && *fonts[0]) | |||
| parser = fonts; | |||
| else | |||
| parser = fonts + 1; | |||
| /* Ignore font errors */ | |||
| old_error_handler = XSetErrorHandler(x11_error_handler); | |||
| /* Parse our font list */ | |||
| for( ; ; parser++) | |||
| { | |||
| if(!*parser) | |||
| { | |||
| XSetErrorHandler(old_error_handler); | |||
| XCloseDisplay(kk->x11.dpy); | |||
| return -1; | |||
| } | |||
| kk->x11.font = XLoadFont(kk->x11.dpy, *parser); | |||
| if(!kk->x11.font) | |||
| continue; | |||
| kk->x11.font_struct = XQueryFont(kk->x11.dpy, kk->x11.font); | |||
| if(!kk->x11.font_struct) | |||
| { | |||
| XUnloadFont(kk->x11.dpy, kk->x11.font); | |||
| continue; | |||
| } | |||
| break; | |||
| } | |||
| /* Reset the default X11 error handler */ | |||
| XSetErrorHandler(old_error_handler); | |||
| kk->x11.font_width = kk->x11.font_struct->max_bounds.width; | |||
| kk->x11.font_height = kk->x11.font_struct->max_bounds.ascent | |||
| + kk->x11.font_struct->max_bounds.descent; | |||
| kk->x11.font_offset = kk->x11.font_struct->max_bounds.descent; | |||
| colormap = DefaultColormap(kk->x11.dpy, DefaultScreen(kk->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(kk->x11.dpy, colormap, &color); | |||
| kk->x11.colors[i] = color.pixel; | |||
| } | |||
| x11_winattr.backing_store = Always; | |||
| x11_winattr.background_pixel = kk->x11.colors[0]; | |||
| x11_winattr.event_mask = ExposureMask | StructureNotifyMask; | |||
| kk->x11.window = | |||
| XCreateWindow(kk->x11.dpy, DefaultRootWindow(kk->x11.dpy), 0, 0, | |||
| kk->qq->width * kk->x11.font_width, | |||
| kk->qq->height * kk->x11.font_height, | |||
| 0, 0, InputOutput, 0, | |||
| CWBackingStore | CWBackPixel | CWEventMask, | |||
| &x11_winattr); | |||
| XStoreName(kk->x11.dpy, kk->x11.window, "caca for X"); | |||
| XSelectInput(kk->x11.dpy, kk->x11.window, StructureNotifyMask); | |||
| XMapWindow(kk->x11.dpy, kk->x11.window); | |||
| kk->x11.gc = XCreateGC(kk->x11.dpy, kk->x11.window, 0, NULL); | |||
| XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[15]); | |||
| XSetFont(kk->x11.dpy, kk->x11.gc, kk->x11.font); | |||
| for(;;) | |||
| { | |||
| XEvent event; | |||
| XNextEvent(kk->x11.dpy, &event); | |||
| if (event.type == MapNotify) | |||
| break; | |||
| } | |||
| #if defined(HAVE_X11_XKBLIB_H) | |||
| /* Disable autorepeat */ | |||
| XkbSetDetectableAutoRepeat(kk->x11.dpy, True, &kk->x11.autorepeat); | |||
| if(!kk->x11.autorepeat) | |||
| XAutoRepeatOff(kk->x11.dpy); | |||
| #endif | |||
| kk->x11.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | |||
| | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | |||
| | ExposureMask; | |||
| XSelectInput(kk->x11.dpy, kk->x11.window, kk->x11.event_mask); | |||
| XSync(kk->x11.dpy, False); | |||
| kk->x11.pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window, | |||
| kk->qq->width * kk->x11.font_width, | |||
| kk->qq->height * kk->x11.font_height, | |||
| DefaultDepth(kk->x11.dpy, | |||
| DefaultScreen(kk->x11.dpy))); | |||
| kk->x11.new_width = kk->x11.new_height = 0; | |||
| return 0; | |||
| } | |||
| int x11_end_graphics(caca_t *kk) | |||
| { | |||
| XSync(kk->x11.dpy, False); | |||
| #if defined(HAVE_X11_XKBLIB_H) | |||
| if(!kk->x11.autorepeat) | |||
| XAutoRepeatOn(kk->x11.dpy); | |||
| #endif | |||
| XFreePixmap(kk->x11.dpy, kk->x11.pixmap); | |||
| XFreeFont(kk->x11.dpy, kk->x11.font_struct); | |||
| XFreeGC(kk->x11.dpy, kk->x11.gc); | |||
| XUnmapWindow(kk->x11.dpy, kk->x11.window); | |||
| XDestroyWindow(kk->x11.dpy, kk->x11.window); | |||
| XCloseDisplay(kk->x11.dpy); | |||
| return 0; | |||
| } | |||
| #endif /* _DOXYGEN_SKIP_ME */ | |||
| int x11_set_window_title(caca_t *kk, char const *title) | |||
| { | |||
| XStoreName(kk->x11.dpy, kk->x11.window, title); | |||
| return 0; | |||
| } | |||
| unsigned int x11_get_window_width(caca_t *kk) | |||
| { | |||
| return kk->qq->width * kk->x11.font_width; | |||
| } | |||
| unsigned int x11_get_window_height(caca_t *kk) | |||
| { | |||
| return kk->qq->height * kk->x11.font_height; | |||
| } | |||
| void x11_display(caca_t *kk) | |||
| { | |||
| 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 < kk->qq->height; y++) | |||
| { | |||
| for(x = 0; x < kk->qq->width; x += len) | |||
| { | |||
| uint8_t *attr = kk->qq->attr + x + y * kk->qq->width; | |||
| len = 1; | |||
| while(x + len < kk->qq->width | |||
| && (attr[len] >> 4) == (attr[0] >> 4)) | |||
| len++; | |||
| XSetForeground(kk->x11.dpy, kk->x11.gc, | |||
| kk->x11.colors[attr[0] >> 4]); | |||
| XFillRectangle(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc, | |||
| x * kk->x11.font_width, y * kk->x11.font_height, | |||
| len * kk->x11.font_width, kk->x11.font_height); | |||
| } | |||
| } | |||
| /* Then print the foreground characters */ | |||
| for(y = 0; y < kk->qq->height; y++) | |||
| { | |||
| for(x = 0; x < kk->qq->width; x += len) | |||
| { | |||
| char buffer[BUFSIZ]; /* FIXME: use a smaller buffer */ | |||
| uint32_t *chars = kk->qq->chars + x + y * kk->qq->width; | |||
| uint8_t *attr = kk->qq->attr + x + y * kk->qq->width; | |||
| len = 1; | |||
| /* Skip spaces */ | |||
| if(chars[0] == ' ') | |||
| continue; | |||
| buffer[0] = chars[0] & 0x7f; | |||
| while(x + len < kk->qq->width | |||
| && (attr[len] & 0xf) == (attr[0] & 0xf)) | |||
| { | |||
| buffer[len] = chars[len] & 0x7f; | |||
| len++; | |||
| } | |||
| XSetForeground(kk->x11.dpy, kk->x11.gc, kk->x11.colors[attr[0] & 0xf]); | |||
| XDrawString(kk->x11.dpy, kk->x11.pixmap, kk->x11.gc, | |||
| x * kk->x11.font_width, | |||
| (y + 1) * kk->x11.font_height - kk->x11.font_offset, | |||
| buffer, len); | |||
| } | |||
| } | |||
| XCopyArea(kk->x11.dpy, kk->x11.pixmap, kk->x11.window, kk->x11.gc, 0, 0, | |||
| kk->qq->width * kk->x11.font_width, | |||
| kk->qq->height * kk->x11.font_height, | |||
| 0, 0); | |||
| XFlush(kk->x11.dpy); | |||
| } | |||
| void x11_handle_resize(caca_t *kk) | |||
| { | |||
| unsigned int new_width, new_height; | |||
| Pixmap new_pixmap; | |||
| new_width = kk->qq->width; | |||
| new_height = kk->qq->height; | |||
| new_width = kk->x11.new_width; | |||
| new_height = kk->x11.new_height; | |||
| new_pixmap = XCreatePixmap(kk->x11.dpy, kk->x11.window, | |||
| kk->qq->width * kk->x11.font_width, | |||
| kk->qq->height * kk->x11.font_height, | |||
| DefaultDepth(kk->x11.dpy, | |||
| DefaultScreen(kk->x11.dpy))); | |||
| XCopyArea(kk->x11.dpy, kk->x11.pixmap, new_pixmap, kk->x11.gc, 0, 0, | |||
| kk->qq->width * kk->x11.font_width, | |||
| kk->qq->height * kk->x11.font_height, 0, 0); | |||
| XFreePixmap(kk->x11.dpy, kk->x11.pixmap); | |||
| kk->x11.pixmap = new_pixmap; | |||
| } | |||
| /* | |||
| * XXX: following functions are local | |||
| */ | |||
| static int x11_error_handler(Display *dpy, XErrorEvent *event) | |||
| { | |||
| /* Ignore the error */ | |||
| return 0; | |||
| } | |||
| /* | |||
| * Driver initialisation | |||
| */ | |||
| void x11_init_driver(caca_t *kk) | |||
| { | |||
| kk->driver.driver = CACA_DRIVER_X11; | |||
| kk->driver.init_graphics = x11_init_graphics; | |||
| kk->driver.end_graphics = x11_end_graphics; | |||
| kk->driver.set_window_title = x11_set_window_title; | |||
| kk->driver.get_window_width = x11_get_window_width; | |||
| kk->driver.get_window_height = x11_get_window_height; | |||
| kk->driver.display = x11_display; | |||
| kk->driver.handle_resize = x11_handle_resize; | |||
| } | |||
| #endif /* USE_X11 */ | |||
| @@ -187,10 +187,10 @@ static unsigned int _get_next_event(caca_t *kk) | |||
| event = _lowlevel_event(kk); | |||
| #if defined(USE_SLANG) | |||
| if(kk->driver != CACA_DRIVER_SLANG) | |||
| if(kk->driver.driver != CACA_DRIVER_SLANG) | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver != CACA_DRIVER_NCURSES) | |||
| if(kk->driver.driver != CACA_DRIVER_NCURSES) | |||
| #endif | |||
| return event; | |||
| @@ -256,7 +256,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| #if defined(USE_X11) | |||
| /* The X11 event check routine */ | |||
| if(kk->driver == CACA_DRIVER_X11) | |||
| if(kk->driver.driver == CACA_DRIVER_X11) | |||
| { | |||
| XEvent xevent; | |||
| char key; | |||
| @@ -372,7 +372,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| else | |||
| #endif | |||
| #if defined(USE_NCURSES) | |||
| if(kk->driver == CACA_DRIVER_NCURSES) | |||
| if(kk->driver.driver == CACA_DRIVER_NCURSES) | |||
| { | |||
| int intkey; | |||
| @@ -556,7 +556,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| else | |||
| #endif | |||
| #if defined(USE_SLANG) | |||
| if(kk->driver == CACA_DRIVER_SLANG) | |||
| if(kk->driver.driver == CACA_DRIVER_SLANG) | |||
| { | |||
| int intkey; | |||
| @@ -639,7 +639,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| else | |||
| #endif | |||
| #if defined(USE_CONIO) | |||
| if(kk->driver == CACA_DRIVER_CONIO) | |||
| if(kk->driver.driver == CACA_DRIVER_CONIO) | |||
| { | |||
| if(!_conio_kbhit()) | |||
| return CACA_EVENT_NONE; | |||
| @@ -651,7 +651,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| else | |||
| #endif | |||
| #if defined(USE_WIN32) | |||
| if(kk->driver == CACA_DRIVER_WIN32) | |||
| if(kk->driver.driver == CACA_DRIVER_WIN32) | |||
| { | |||
| INPUT_RECORD rec; | |||
| DWORD num; | |||
| @@ -725,7 +725,7 @@ static unsigned int _lowlevel_event(caca_t *kk) | |||
| else | |||
| #endif | |||
| #if defined(USE_GL) | |||
| if(kk->driver == CACA_DRIVER_GL) | |||
| if(kk->driver.driver == CACA_DRIVER_GL) | |||
| { | |||
| glutMainLoopEvent(); | |||