diff --git a/BUGS b/BUGS index 88a0c64..bbb2ece 100644 --- a/BUGS +++ b/BUGS @@ -4,9 +4,8 @@ Video rendering o ncurses' hashmap scrolling optimisation code causes the screen to occasionally flicker because it tries to optimise the vertical - scrolling. - - o the X11 driver is very slow. + scrolling. With S-Lang, SLtt_Term_Cannot_Scroll prevents this + unwanted behaviour. o some terminal emulators (Konsole, or the Linux VGA console) honour the blink attribute instead of using it for bold or bright like any diff --git a/NOTES b/NOTES index 0257c2c..9b0abda 100644 --- a/NOTES +++ b/NOTES @@ -193,3 +193,5 @@ $Id$ o MS-DOS: all bright colours, bright backgrounds, and bright combinations work using . No need to kludge anything. + o Win32: we use GetConsoleScreenBufferInfo etc. + diff --git a/README b/README index d5d3572..8b2f414 100644 --- a/README +++ b/README @@ -11,10 +11,12 @@ Building libcaca --disable-imlib2: remove Imlib2 support in cacaview - o Cross-compilation example: + o Cross-compilation examples: ./configure --disable-imlib2 --host=i386-pc-msdosdjgpp + ./configure --disable-imlib2 --host=i586-mingw32msvc + Using libcaca diff --git a/configure.ac b/configure.ac index 38ec94c..8101b2f 100644 --- a/configure.ac +++ b/configure.ac @@ -15,19 +15,21 @@ AC_PROG_RANLIB dnl AC_PROG_EGREP only exists in autoconf 2.54+, so we use AC_EGREP_CPP right dnl now otherwise it might be set in an obscure if statement. -AC_EGREP_CPP(foo,foo) +AC_EGREP_CPP(foo, foo) AC_ARG_ENABLE(slang, [ --enable-slang slang graphics support (autodetected)]) AC_ARG_ENABLE(ncurses, [ --enable-ncurses ncurses graphics support (autodetected)]) +AC_ARG_ENABLE(win32, + [ --enable-win32 Windows console support (autodetected)]) AC_ARG_ENABLE(conio, - [ --enable-conio DOS conio.h graphics support (default disabled)]) + [ --enable-conio DOS conio.h graphics support (autodetected)]) AC_ARG_ENABLE(x11, [ --enable-x11 X11 support (autodetected)]) AC_CHECK_HEADERS(inttypes.h endian.h) -AC_CHECK_FUNCS(vsnprintf getenv putenv strcasecmp) +AC_CHECK_FUNCS(vsnprintf getenv putenv strcasecmp usleep Sleep) CACA_DRIVERS="" @@ -35,7 +37,7 @@ if test "${enable_conio}" != "no"; then ac_cv_my_have_conio="no" AC_CHECK_HEADERS(conio.h, [AC_MSG_CHECKING(for ScreenUpdate in pc.h) - AC_EGREP_HEADER(ScreenUpdate,pc.h, + AC_EGREP_HEADER(ScreenUpdate, pc.h, [ac_cv_my_have_conio="yes" AC_MSG_RESULT(yes) AC_DEFINE(SCREENUPDATE_IN_PC_H, 1, @@ -48,6 +50,23 @@ if test "${enable_conio}" != "no"; then fi fi +if test "${enable_win32}" != "no"; then + ac_cv_my_have_win32="no" + AC_CHECK_HEADERS(windows.h, + [AC_MSG_CHECKING(for AllocConsole in windows.h) + AC_EGREP_HEADER(AllocConsole, windows.h, + [ac_cv_my_have_win32="yes" + AC_MSG_RESULT(yes) + AC_DEFINE(ALLOCCONSOLE_IN_WINDOWS_H, 1, + Define if defines AllocConsole.) + AC_DEFINE(USE_WIN32, 1, Define to activate the win32 backend driver) + CACA_DRIVERS="${CACA_DRIVERS} win32"], + [AC_MSG_RESULT(no)])]) + if test "${ac_cv_my_have_win32}" = "no" -a "${enable_win32}" = "yes"; then + AC_MSG_ERROR([cannot find win32 console development files]) + fi +fi + if test "${enable_slang}" != "no"; then ac_cv_my_have_slang="no" AC_CHECK_HEADERS(slang.h slang/slang.h, diff --git a/src/Makefile.am b/src/Makefile.am index 132063f..16248d1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,7 @@ libcaca_a_SOURCES = \ triangle.c \ sprite.c \ bitmap.c \ + time.c \ $(NULL) if NEED_PIC diff --git a/src/caca.c b/src/caca.c index 2ad23d1..56fa424 100644 --- a/src/caca.c +++ b/src/caca.c @@ -140,6 +140,9 @@ int caca_init(void) 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 diff --git a/src/caca_internals.h b/src/caca_internals.h index 640e36b..4d49777 100644 --- a/src/caca_internals.h +++ b/src/caca_internals.h @@ -48,6 +48,13 @@ enum caca_driver CACA_DRIVER_NONE = 0 }; +/* Timer structure */ +#define CACA_TIMER_INITIALIZER { 0, 0 } +struct caca_timer +{ + int last_sec, last_usec; +}; + extern enum caca_driver _caca_driver; /* Initialisation functions */ @@ -56,6 +63,9 @@ extern int _caca_end_graphics(void); extern int _caca_init_bitmap(void); extern int _caca_end_bitmap(void); +/* Timer functions */ +extern unsigned int _caca_getticks(struct caca_timer *); + /* Cached screen size */ extern unsigned int _caca_width; extern unsigned int _caca_height; diff --git a/src/graphics.c b/src/graphics.c index 17cdae8..95adcb3 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -63,8 +63,6 @@ typedef unsigned char uint8_t; #include #include #include -#include -#include #include "caca.h" #include "caca_internals.h" @@ -187,8 +185,6 @@ static void slang_init_palette(void); static int x11_error_handler(Display *, XErrorEvent *); #endif -static unsigned int _caca_getticks(void); - /** \brief Set the default colour pair. * * This function sets the default colour pair. String functions such as @@ -828,39 +824,11 @@ unsigned int caca_get_rendertime(void) return _caca_rendertime; } -static unsigned int _caca_getticks(void) -{ - static int last_sec = 0, last_usec = 0; - - struct timeval tv; - unsigned int ticks = 0; - - gettimeofday(&tv, NULL); - - if(last_sec != 0) - { - /* If the delay was greater than 60 seconds, return 10 seconds - * otherwise we may overflow our ticks counter. */ - if(tv.tv_sec >= last_sec + 60) - ticks = 60 * 1000000; - else - { - ticks = (tv.tv_sec - last_sec) * 1000000; - ticks += tv.tv_usec; - ticks -= last_usec; - } - } - - last_sec = tv.tv_sec; - last_usec = tv.tv_usec; - - return ticks; -} - /** \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 caca_refresh() is not called. + * 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 @@ -873,8 +841,9 @@ 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(); + int ticks = lastticks + _caca_getticks(&timer); #if defined(USE_SLANG) if(_caca_driver == CACA_DRIVER_SLANG) @@ -958,9 +927,19 @@ void caca_refresh(void) #endif /* Wait until _caca_delay + time of last call */ - ticks += _caca_getticks(); - for(; ticks + IDLE_USEC < (int)_caca_delay; ticks += _caca_getticks()) + ticks += _caca_getticks(&timer); + for(ticks += _caca_getticks(&timer); + ticks + IDLE_USEC < (int)_caca_delay; + ticks += _caca_getticks(&timer)) + { +#if defined(HAVE_USLEEP) usleep(IDLE_USEC); +#elif defined(HAVE_SLEEP) + Sleep(IDLE_USEC / 1000); +#else + SLEEP +#endif + } /* Update the sliding mean of the render time */ _caca_rendertime = (7 * _caca_rendertime + ticks) / 8; diff --git a/src/io.c b/src/io.c index e359716..4bf9a3d 100644 --- a/src/io.c +++ b/src/io.c @@ -54,6 +54,7 @@ #include "caca_internals.h" static unsigned int _get_next_event(void); +static unsigned int _lowlevel_event(void); static void _push_event(unsigned int); static unsigned int _pop_event(void); @@ -63,6 +64,16 @@ static unsigned int _pop_event(void); static unsigned int eventbuf[EVENTBUF_LEN]; static int events = 0; +#if !defined(_DOXYGEN_SKIP_ME) +/* If no new key was pressed after AUTOREPEAT_THRESHOLD usec, assume the + * key was released */ +#define AUTOREPEAT_THRESHOLD 200000 +/* Start repeating key after AUTOREPEAT_TRIGGER usec and send keypress + * events every AUTOREPEAT_RATE usec. */ +#define AUTOREPEAT_TRIGGER 300000 +#define AUTOREPEAT_RATE 100000 +#endif + /** \brief Get the next mouse or keyboard input event. * * This function polls the event queue for mouse or keyboard events matching @@ -104,7 +115,13 @@ unsigned int caca_wait_event(unsigned int event_mask) if(event & event_mask) return event; - usleep(1000); +#if defined(HAVE_USLEEP) + usleep(10000); +#elif defined(HAVE_SLEEP) + Sleep(10); +#else + SLEEP +#endif } } @@ -113,6 +130,72 @@ unsigned int caca_wait_event(unsigned int event_mask) */ static unsigned int _get_next_event(void) +{ +#if defined(USE_SLANG) || defined(USE_NCURSES) + static struct caca_timer key_timer = CACA_TIMER_INITIALIZER; + static unsigned int last_key_ticks = 0; + static unsigned int autorepeat_ticks = 0; + static unsigned int last_key = 0; + unsigned int ticks; +#endif + unsigned int event = _lowlevel_event(); + +#if defined(USE_SLANG) + if(_caca_driver != CACA_DRIVER_SLANG) +#endif +#if defined(USE_NCURSES) + if(_caca_driver != CACA_DRIVER_NCURSES) +#endif + return event; + +#if defined(USE_SLANG) || defined(USE_NCURSES) + /* Simulate long keypresses using autorepeat features */ + ticks = _caca_getticks(&key_timer); + last_key_ticks += ticks; + autorepeat_ticks += ticks; + + /* Handle autorepeat */ + if(last_key && autorepeat_ticks > AUTOREPEAT_TRIGGER + && autorepeat_ticks > AUTOREPEAT_THRESHOLD + && autorepeat_ticks > AUTOREPEAT_RATE) + { + _push_event(event); + autorepeat_ticks -= AUTOREPEAT_RATE; + return CACA_EVENT_KEY_PRESS | last_key; + } + + /* We are in autorepeat mode and the same key was just pressed, ignore + * this event and return the next one by calling ourselves. */ + if(event == (CACA_EVENT_KEY_PRESS | last_key)) + { + last_key_ticks = 0; + return _get_next_event(); + } + + /* We are in autorepeat mode, but key has expired or a new key was + * pressed - store our event and return a key release event first */ + if(last_key && (last_key_ticks > AUTOREPEAT_THRESHOLD + || (event & CACA_EVENT_KEY_PRESS))) + { + _push_event(event); + event = CACA_EVENT_KEY_RELEASE | last_key; + last_key = 0; + return event; + } + + /* A new key was pressed, enter autorepeat mode */ + if(event & CACA_EVENT_KEY_PRESS) + { + last_key_ticks = 0; + autorepeat_ticks = 0; + last_key = event & 0x00ffffff; + } + + return event; +#endif +} + +static unsigned int _lowlevel_event(void) { unsigned int event = _pop_event(); @@ -213,7 +296,6 @@ static unsigned int _get_next_event(void) if(intkey < 0x100) { - _push_event(CACA_EVENT_KEY_RELEASE | intkey); return CACA_EVENT_KEY_PRESS | intkey; } @@ -360,7 +442,6 @@ static unsigned int _get_next_event(void) case KEY_F(12): event = CACA_KEY_F12; break; } - _push_event(CACA_EVENT_KEY_RELEASE | event); return CACA_EVENT_KEY_PRESS | event; } else @@ -377,7 +458,6 @@ static unsigned int _get_next_event(void) if(intkey < 0x100) { - _push_event(CACA_EVENT_KEY_RELEASE | intkey); return CACA_EVENT_KEY_PRESS | intkey; } @@ -412,7 +492,6 @@ static unsigned int _get_next_event(void) case SL_KEY_F(12): event = CACA_KEY_F12; break; } - _push_event(CACA_EVENT_KEY_RELEASE | event); return CACA_EVENT_KEY_PRESS | event; } else diff --git a/src/time.c b/src/time.c new file mode 100644 index 0000000..555db74 --- /dev/null +++ b/src/time.c @@ -0,0 +1,65 @@ +/* + * libcaca ASCII-Art library + * Copyright (c) 2002, 2003, 2004 Sam Hocevar + * 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 time.c + * \version \$Id$ + * \author Sam Hocevar + * \brief Timer routines + * + * This file contains simple timer routines. + */ + +#include "config.h" + +#include +#include +#include + +#include "caca.h" +#include "caca_internals.h" + +unsigned int _caca_getticks(struct caca_timer *timer) +{ + struct timeval tv; + unsigned int ticks = 0; + + gettimeofday(&tv, NULL); + + if(timer->last_sec != 0) + { + /* If the delay was greater than 60 seconds, return 10 seconds + * otherwise we may overflow our ticks counter. */ + if(tv.tv_sec >= timer->last_sec + 60) + ticks = 60 * 1000000; + else + { + ticks = (tv.tv_sec - timer->last_sec) * 1000000; + ticks += tv.tv_usec; + ticks -= timer->last_usec; + } + } + + timer->last_sec = tv.tv_sec; + timer->last_usec = tv.tv_usec; + + return ticks; +} + diff --git a/test/event.c b/test/event.c index cb781c8..7238354 100644 --- a/test/event.c +++ b/test/event.c @@ -33,7 +33,7 @@ static void print_event(int, int, unsigned int); int main(int argc, char **argv) { int *events; - int i, h; + int i, h, quit; if(caca_init()) return 1; @@ -48,32 +48,38 @@ int main(int argc, char **argv) events = malloc(h * sizeof(int)); memset(events, 0, h * sizeof(int)); - for( ; ; ) + for(quit = 0; !quit; ) { unsigned int event = caca_wait_event(CACA_EVENT_ANY); if(!event) continue; - memmove(events + 1, events, (h - 1) * sizeof(int)); - events[0] = event; + do + { + /* q quits */ + if(event == (CACA_EVENT_KEY_PRESS | 'q')) + quit = 1; + + memmove(events + 1, events, (h - 1) * sizeof(int)); + events[0] = event; + + event = caca_get_event(CACA_EVENT_ANY); + } + while(event); caca_clear(); /* Print current event */ caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE); caca_draw_line(0, 0, caca_get_width() - 1, 0, ' '); - print_event(0, 0, event); + print_event(0, 0, events[0]); /* Print previous events */ caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLACK); for(i = 1; i < h && events[i]; i++) print_event(0, i, events[i]); - /* q quits */ - if(event == (CACA_EVENT_KEY_PRESS | 'q')) - break; - caca_refresh(); } @@ -85,18 +91,22 @@ int main(int argc, char **argv) static void print_event(int x, int y, unsigned int event) { + int character; + switch(event & 0xff000000) { case CACA_EVENT_NONE: caca_printf(x, y, "CACA_EVENT_NONE"); break; case CACA_EVENT_KEY_PRESS: - caca_printf(x, y, "CACA_EVENT_KEY_PRESS 0x%02x", - event & 0x00ffffff); + character = event & 0x00ffffff; + caca_printf(x, y, "CACA_EVENT_KEY_PRESS 0x%02x (%c)", character, + (character > 0x20 && character < 0x80) ? character : '?'); break; case CACA_EVENT_KEY_RELEASE: - caca_printf(x, y, "CACA_EVENT_KEY_RELEASE 0x2%x", - event & 0x00ffffff); + character = event & 0x00ffffff; + caca_printf(x, y, "CACA_EVENT_KEY_RELEASE 0x%02x (%c)", character, + (character > 0x20 && character < 0x80) ? character : '?'); break; case CACA_EVENT_MOUSE_MOTION: caca_printf(x, y, "CACA_EVENT_MOUSE_MOTION %u %u",