+ Do not refresh after each event, but only when there is no event pending. + If the pressed key is a printable character, display it. * src/time.c: + Moved _caca_getticks() to this file. * src/caca.c: + Set the escape delay to a very low value in the ncurses driver, because I don't want escape sequences to be entered manually. * src/io.c: + Autorepeat emulation in the ncurses and slang drivers: do not immediately send the key release event. * configure.ac: + Check for usleep. + Improvements in the win32 platform detection.tags/v0.99.beta14
@@ -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 | |||
@@ -193,3 +193,5 @@ $Id$ | |||
o MS-DOS: all bright colours, bright backgrounds, and bright combinations | |||
work using <conio.h>. No need to kludge anything. | |||
o Win32: we use GetConsoleScreenBufferInfo etc. | |||
@@ -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 | |||
@@ -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 <windows.h> 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, | |||
@@ -18,6 +18,7 @@ libcaca_a_SOURCES = \ | |||
triangle.c \ | |||
sprite.c \ | |||
bitmap.c \ | |||
time.c \ | |||
$(NULL) | |||
if NEED_PIC | |||
@@ -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 | |||
@@ -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; | |||
@@ -63,8 +63,6 @@ typedef unsigned char uint8_t; | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <stdarg.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#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; | |||
@@ -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 | |||
@@ -0,0 +1,65 @@ | |||
/* | |||
* libcaca ASCII-Art library | |||
* Copyright (c) 2002, 2003, 2004 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 time.c | |||
* \version \$Id$ | |||
* \author Sam Hocevar <sam@zoy.org> | |||
* \brief Timer routines | |||
* | |||
* This file contains simple timer routines. | |||
*/ | |||
#include "config.h" | |||
#include <stdlib.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#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; | |||
} | |||
@@ -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", | |||