Cleanup of the timeout and Fl::wait() code. This one calls the clock

function less than half as much, which resulted in a noticable
improvement in a flip book program I was working on.  The code looks
nicer, too, and I am reasonably certain it works the same.

Fl::wait(time) with a time greater than the system can handle (24.855
days on NT, the same on some Unix systems) will now act as though the
time is infinity.  Before it would do unpredictable things.

"USE_POLL" now compiles and works, although it is disabled by default.
poll() is an alternative to the Unix select() call which is available on
Linux and Irix, but I don't know if it is faster, you can try it by
editing config.h.

I tried making the NT USE_ASYNC_SELECT code do translate/dispatch of
the select events on the assumption (based on experience) that not
doing this to every event gives NT fits.  This appears to work but I'm
not sure if it fixes anything.

X version does not crash if Fl::wait() is called when the display is
closed (it will not return unless you have a timeout or fd callback
set up, though).

Fixed up the documentation for all of this, including getting rid of
some completely misleading documentation.

Now I need to get this stuff into 2.0...


git-svn-id: file:///fltk/svn/fltk/branches/branch-1.0@1215 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Bill Spitzak 2000-06-18 00:38:41 +00:00
parent 4dd096e7b5
commit be106a0977
5 changed files with 1205 additions and 1007 deletions

View File

@ -1,5 +1,5 @@
/*
* "$Id: configh.in,v 1.11.2.4 2000/06/07 12:11:52 mike Exp $"
* "$Id: configh.in,v 1.11.2.5 2000/06/18 00:38:37 bill Exp $"
*
* Configuration file for the Fast Light Tool Kit (FLTK).
* @configure_input@
@ -145,13 +145,13 @@
#define HAVE_SYS_SELECT_H 0
/*
* HAVE_POLL:
* USE_POLL:
*
* Use poll() if we don't have select().
* Use the poll() call provided on Linux and Irix instead of select()
*/
#define HAVE_POLL 0
#define USE_POLL 0
/*
* End of "$Id: configh.in,v 1.11.2.4 2000/06/07 12:11:52 mike Exp $".
* End of "$Id: configh.in,v 1.11.2.5 2000/06/18 00:38:37 bill Exp $".
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
//
// "$Id: Fl.cxx,v 1.24.2.26 2000/06/16 07:28:02 bill Exp $"
// "$Id: Fl.cxx,v 1.24.2.27 2000/06/18 00:38:39 bill Exp $"
//
// Main event handling code for the Fast Light Tool Kit (FLTK).
//
@ -50,8 +50,6 @@ int Fl::damage_,
char *Fl::e_text = "";
int Fl::e_length;
static double fl_elapsed();
//
// 'Fl:event_inside()' - Return whether or not the mouse event is inside
// the given rectangle.
@ -67,6 +65,11 @@ int Fl::event_inside(const Fl_Widget *o) /*const*/ {
return event_inside(o->x(),o->y(),o->w(),o->h());
}
////////////////////////////////////////////////////////////////
// Timeouts and Fl::wait()
void (*Fl::idle)();
// Timeouts are insert-sorted into order. This works good if there
// are only a small number:
@ -78,9 +81,131 @@ static struct Timeout {
static int numtimeouts;
static int timeout_array_size;
extern int fl_wait(double time); // warning: assummes time >= 0.0
extern int fl_ready();
#ifndef WIN32
#include <sys/time.h>
#endif
// I avoid the overhead of getting the current time when we have no
// timeouts by setting this flag instead of getting the time.
// In this case calling elapse_timeouts() does nothing, but records
// the current time, and the next call will actualy elapse time.
static char reset_clock = 1;
static void elapse_timeouts() {
#ifdef WIN32
unsigned long newclock = GetTickCount();
static unsigned long prevclock;
if (reset_clock) {
prevclock = newclock;
reset_clock = 0;
return;
}
if (newclock <= prevclock) return;
double elapsed = (newclock-prevclock)/1000.0;
prevclock = newclock;
#else
static struct timeval prevclock;
struct timeval newclock;
gettimeofday(&newclock, NULL);
if (reset_clock) {
prevclock.tv_sec = newclock.tv_sec;
prevclock.tv_usec = newclock.tv_usec;
reset_clock = 0;
return;
}
double elapsed = newclock.tv_sec - prevclock.tv_sec +
(newclock.tv_usec - prevclock.tv_usec)/1000000.0;
prevclock.tv_sec = newclock.tv_sec;
prevclock.tv_usec = newclock.tv_usec;
if (elapsed <= 0) return;
#endif
for (int i=0; i<numtimeouts; i++) timeout[i].time -= elapsed;
}
static char in_idle;
static char in_timeout;
double Fl::wait(double time_to_wait) {
if (numtimeouts) {
elapse_timeouts();
if (timeout[0].time <= time_to_wait) time_to_wait = timeout[0].time;
while (numtimeouts) {
if (timeout[0].time > 0) break;
// The first timeout in the array has expired.
// We must remove timeout from array before doing the callback:
void (*cb)(void*) = timeout[0].cb;
void *arg = timeout[0].arg;
numtimeouts--;
if (numtimeouts)
memmove(timeout, timeout+1, numtimeouts*sizeof(Timeout));
// Now it is safe for the callback to do add_timeout:
in_timeout = 1;
cb(arg);
in_timeout = 0;
}
} else {
reset_clock = 1; // we are not going to check the clock
}
if (idle) {
if (!in_idle) {in_idle = 1; idle(); in_idle = 0;}
// the idle function may turn off idle, we can then wait:
if (idle) time_to_wait = 0.0;
}
if (time_to_wait <= 0.0) {
// do flush second so that the results of events are visible:
int ret = fl_wait(0.0);
flush();
return ret;
} else {
// do flush first so that user sees the display:
flush();
return fl_wait(time_to_wait);
}
}
#define FOREVER 1e20
int Fl::run() {
while (Fl_X::first) wait(FOREVER);
return 0;
}
int Fl::wait() {
wait(FOREVER);
return Fl_X::first != 0; // return true if there is a window
}
int Fl::check() {
wait(0.0);
return Fl_X::first != 0; // return true if there is a window
}
int Fl::ready() {
if (numtimeouts) {
elapse_timeouts();
if (timeout[0].time <= 0) return 1;
} else {
reset_clock = 1;
}
return fl_ready();
}
void Fl::add_timeout(double t, Fl_Timeout_Handler cb, void *v) {
fl_elapsed();
// This little test gets rid of about half the calls to get the time
// and has the added advantage of making timeouts that think they
// are happening at regular intervals actually happen at regular
// intervals:
if (!in_timeout) elapse_timeouts();
if (numtimeouts >= timeout_array_size) {
timeout_array_size = 2*timeout_array_size+1;
@ -117,145 +242,6 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void *v) {
numtimeouts = j;
}
static int call_timeouts() {
int expired = 0;
while (numtimeouts) {
if (timeout[0].time > 0) break;
// we must remove timeout from array before doing the callback:
void (*cb)(void*) = timeout[0].cb;
void *arg = timeout[0].arg;
numtimeouts--; expired++;
if (numtimeouts) memmove(timeout, timeout+1, numtimeouts*sizeof(Timeout));
// now it is safe for the callback to do add_timeout:
cb(arg);
}
return expired;
}
void Fl::flush() {
if (damage()) {
damage_ = 0;
for (Fl_X* x = Fl_X::first; x; x = x->next) {
if (x->w->damage() && x->w->visible_r()) {
if (x->wait_for_expose) {
// leave Fl::damage() set so programs can tell damage still exists
damage_ = 1;
} else {
x->flush();
x->w->clear_damage();
}
}
}
}
#ifndef WIN32
if (fl_display) XFlush(fl_display);
#endif
}
extern double fl_wait(int timeout_flag, double timeout);
extern int fl_ready();
static int initclock; // if false we didn't call fl_elapsed() last time
#ifndef WIN32
#include <sys/time.h>
#endif
// fl_elapsed must return the amount of time since the last time it was
// called. To reduce the number of system calls to get the
// current time, the "initclock" symbol is turned on by an indefinite
// wait. This should then reset the measured-from time and return zero
static double fl_elapsed() {
#ifdef WIN32
unsigned long newclock = GetTickCount();
const int TICKS_PER_SECOND = 1000; // divisor of the value to get seconds
static unsigned long prevclock;
if (!initclock) {prevclock = newclock; initclock = 1; return 0.0;}
else if (newclock < prevclock) return 0.0;
double t = double(newclock-prevclock)/TICKS_PER_SECOND;
prevclock = newclock;
#else
static struct timeval prevclock;
struct timeval newclock;
gettimeofday(&newclock, NULL);
if (!initclock) {
prevclock.tv_sec = newclock.tv_sec;
prevclock.tv_usec = newclock.tv_usec;
initclock = 1;
return 0.0;
}
double t = newclock.tv_sec - prevclock.tv_sec +
(newclock.tv_usec - prevclock.tv_usec)/1000000.0;
prevclock.tv_sec = newclock.tv_sec;
prevclock.tv_usec = newclock.tv_usec;
#endif
// expire any timeouts:
if (t > 0.0) for (int i=0; i<numtimeouts; i++) timeout[i].time -= t;
return t;
}
void (*Fl::idle)();
static char in_idle;
static void callidle() {
if (!Fl::idle || in_idle) return;
in_idle = 1;
Fl::idle();
in_idle = 0;
}
int Fl::wait() {
callidle();
int expired = 0;
if (numtimeouts) {fl_elapsed(); expired = call_timeouts();}
flush();
if ((idle && !in_idle) || expired) {
fl_wait(1,0.0);
} else if (numtimeouts) {
fl_wait(1, timeout[0].time);
} else {
initclock = 0;
fl_wait(0,0);
}
return Fl_X::first != 0; // return true if there is a window
}
double Fl::wait(double time) {
callidle();
int expired = 0;
if (numtimeouts) {time -= fl_elapsed(); expired = call_timeouts();}
flush();
double wait_time = (idle && !in_idle) || expired ? 0.0 : time;
if (numtimeouts && timeout[0].time < wait_time) wait_time = timeout[0].time;
fl_wait(1, wait_time);
return time - fl_elapsed();
}
int Fl::check() {
callidle();
if (numtimeouts) {fl_elapsed(); call_timeouts();}
fl_wait(1, 0.0);
flush();
return Fl_X::first != 0; // return true if there is a window
}
int Fl::ready() {
// if (idle && !in_idle) return 1; // should it do this?
if (numtimeouts) {fl_elapsed(); if (timeout[0].time <= 0) return 1;}
return fl_ready();
}
int Fl::run() {
while (wait());
return 0;
}
////////////////////////////////////////////////////////////////
// Window list management:
@ -296,6 +282,26 @@ void Fl::redraw() {
for (Fl_X* x = Fl_X::first; x; x = x->next) x->w->redraw();
}
void Fl::flush() {
if (damage()) {
damage_ = 0;
for (Fl_X* x = Fl_X::first; x; x = x->next) {
if (x->w->damage() && x->w->visible_r()) {
if (x->wait_for_expose) {
// leave Fl::damage() set so programs can tell damage still exists
damage_ = 1;
} else {
x->flush();
x->w->clear_damage();
}
}
}
}
#ifndef WIN32
if (fl_display) XFlush(fl_display);
#endif
}
////////////////////////////////////////////////////////////////
// Event handlers:
@ -727,5 +733,5 @@ void Fl_Window::flush() {
}
//
// End of "$Id: Fl.cxx,v 1.24.2.26 2000/06/16 07:28:02 bill Exp $".
// End of "$Id: Fl.cxx,v 1.24.2.27 2000/06/18 00:38:39 bill Exp $".
//

View File

@ -1,5 +1,5 @@
//
// "$Id: Fl_win32.cxx,v 1.33.2.26 2000/06/05 21:21:02 mike Exp $"
// "$Id: Fl_win32.cxx,v 1.33.2.27 2000/06/18 00:38:40 bill Exp $"
//
// WIN32-specific code for the Fast Light Tool Kit (FLTK).
//
@ -151,24 +151,14 @@ void Fl::remove_fd(int n) {
MSG fl_msg;
int fl_ready() {
if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
#define FOREVER 1e20
#ifdef USE_ASYNC_SELECT
return (0);
#else
timeval t;
t.tv_sec = 0;
t.tv_usec = 0;
fd_set fdt[3];
fdt[0] = fdsets[0];
fdt[1] = fdsets[1];
fdt[2] = fdsets[2];
return ::select(0,&fdt[0],&fdt[1],&fdt[2],&t);
#endif // USE_ASYNC_SELECT
}
double fl_wait(int timeout_flag, double time) {
// This is never called with time_to_wait < 0.0.
// It *should* return negative on error, 0 if nothing happens before
// timeout, and >0 if any callbacks were done. This version only
// returns zero if nothing happens during a 0.0 timeout, otherwise
// it returns 1.
int fl_wait(double time_to_wait) {
int have_message = 0;
int timerid;
@ -195,46 +185,29 @@ double fl_wait(int timeout_flag, double time) {
if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
}
time_to_wait = 0.0; // just peek for any messages
} else {
// we need to check them periodically, so set a short timeout:
if (time_to_wait > .001) time_to_wait = .001;
}
}
#endif // USE_ASYNC_SELECT
// get the first message by waiting the correct amount of time:
if (!timeout_flag) {
#ifdef USE_ASYNC_SELECT
// Wait for a message...
have_message = GetMessage(&fl_msg, NULL, 0, 0);
#else
// If we are monitoring sockets we need to check them periodically,
// so set a timer in this case...
if (nfds) {
// First see if there is a message waiting...
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
if (!have_message) {
// If not then set a 1ms timer...
timerid = SetTimer(NULL, 0, 1, NULL);
GetMessage(&fl_msg, NULL, 0, 0);
KillTimer(NULL, timerid);
}
} else {
// Wait for a message...
GetMessage(&fl_msg, NULL, 0, 0);
}
have_message = 1;
#endif // USE_ASYNC_SELECT
} else {
if (time_to_wait < 2147483.648) {
// Perform the requested timeout...
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
if (!have_message && time > 0.0) {
int t = (int)(time * 1000.0);
if (t <= 0) t = 1;
if (!have_message) {
int t = (int)(time_to_wait * 1000.0 + .5);
if (t <= 0) return 0; // too short to measure
timerid = SetTimer(NULL, 0, t, NULL);
have_message = GetMessage(&fl_msg, NULL, 0, 0);
KillTimer(NULL, timerid);
}
} else {
have_message = GetMessage(&fl_msg, NULL, 0, 0);
}
// execute it, them execute any other messages that become ready during it:
// Execute the message we got, and all other pending messages:
while (have_message) {
#ifdef USE_ASYNC_SELECT
if (fl_msg.message == WM_FLSELECT) {
@ -244,20 +217,33 @@ double fl_wait(int timeout_flag, double time) {
(fd[i].cb)(fd[i].fd, fd[i].arg);
break;
}
} else {
// Some other message...
TranslateMessage(&fl_msg);
DispatchMessage(&fl_msg);
// looks like it is best to do the dispatch-message anyway:
}
#else
#endif
TranslateMessage(&fl_msg);
DispatchMessage(&fl_msg);
#endif // USE_ASYNC_SELECT
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
}
return time;
// This should return 0 if only timer events were handled:
return 1;
}
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
int fl_ready() {
if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
#ifdef USE_ASYNC_SELECT
return (0);
#else
timeval t;
t.tv_sec = 0;
t.tv_usec = 0;
fd_set fdt[3];
fdt[0] = fdsets[0];
fdt[1] = fdsets[1];
fdt[2] = fdsets[2];
return ::select(0,&fdt[0],&fdt[1],&fdt[2],&t);
#endif // USE_ASYNC_SELECT
}
////////////////////////////////////////////////////////////////
@ -967,5 +953,5 @@ void Fl_Window::make_current() {
}
//
// End of "$Id: Fl_win32.cxx,v 1.33.2.26 2000/06/05 21:21:02 mike Exp $".
// End of "$Id: Fl_win32.cxx,v 1.33.2.27 2000/06/18 00:38:40 bill Exp $".
//

View File

@ -1,5 +1,5 @@
//
// "$Id: Fl_x.cxx,v 1.24.2.17 2000/06/05 21:21:03 mike Exp $"
// "$Id: Fl_x.cxx,v 1.24.2.18 2000/06/18 00:38:41 bill Exp $"
//
// X specific code for the Fast Light Tool Kit (FLTK).
//
@ -45,7 +45,7 @@
////////////////////////////////////////////////////////////////
// interface to poll/select call:
#if HAVE_POLL
#if USE_POLL
#include <poll.h>
static pollfd *pollfds = 0;
@ -65,13 +65,15 @@ static int maxfd;
#define POLLOUT 4
#define POLLERR 8
#endif /* HAVE_POLL */
#endif /* USE_POLL */
static int nfds = 0;
static int fd_array_size = 0;
static struct FD {
#if !USE_POLL
int fd;
short events;
#endif
void (*cb)(int, void*);
void* arg;
} *fd = 0;
@ -82,18 +84,18 @@ void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
if (i >= fd_array_size) {
fd_array_size = 2*fd_array_size+1;
fd = (FD*)realloc(fd, fd_array_size*sizeof(FD));
#if HAVE_POLL
#if USE_POLL
pollfds = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
#endif
}
fd[i].fd = n;
fd[i].events = events;
fd[i].cb = cb;
fd[i].arg = v;
#if HAVE_POLL
fds[i].fd = n;
fds[i].events = events;
#if USE_POLL
pollfds[i].fd = n;
pollfds[i].events = events;
#else
fd[i].fd = n;
fd[i].events = events;
if (events & POLLIN) FD_SET(n, &fdsets[0]);
if (events & POLLOUT) FD_SET(n, &fdsets[1]);
if (events & POLLERR) FD_SET(n, &fdsets[2]);
@ -108,25 +110,30 @@ void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
void Fl::remove_fd(int n, int events) {
int i,j;
for (i=j=0; i<nfds; i++) {
#if USE_POLL
if (pollfds[i].fd == n) {
int e = pollfds[i].events & ~events;
if (!e) continue; // if no events left, delete this fd
pollfds[j].events = e;
}
#else
if (fd[i].fd == n) {
int e = fd[i].events & ~events;
if (!e) continue; // if no events left, delete this fd
fd[i].events = e;
#if HAVE_POLL
fds[j].events = e;
#endif
}
#endif
// move it down in the array if necessary:
if (j<i) {
fd[j]=fd[i];
#if HAVE_POLL
fds[j]=fds[i];
fd[j] = fd[i];
#if USE_POLL
pollfds[j] = pollfds[i];
#endif
}
j++;
}
nfds = j;
#if !HAVE_POLL
#if !USE_POLL
if (events & POLLIN) FD_CLR(n, &fdsets[0]);
if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
if (events & POLLERR) FD_CLR(n, &fdsets[2]);
@ -138,22 +145,6 @@ void Fl::remove_fd(int n) {
remove_fd(n, -1);
}
int fl_ready() {
if (XQLength(fl_display)) return 1;
#if HAVE_POLL
return ::poll(fds, nfds, 0);
#else
timeval t;
t.tv_sec = 0;
t.tv_usec = 0;
fd_set fdt[3];
fdt[0] = fdsets[0];
fdt[1] = fdsets[1];
fdt[2] = fdsets[2];
return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
#endif
}
#if CONSOLIDATE_MOTION
static Fl_Window* send_motion;
extern Fl_Window* fl_xmousewin;
@ -172,14 +163,17 @@ static void do_queued_events() {
#endif
}
double fl_wait(int timeout_flag, double time) {
// This is never called with time_to_wait < 0.0:
// It should return negative on error, 0 if nothing happens before
// timeout, and >0 if any callbacks were done.
int fl_wait(double time_to_wait) {
// OpenGL and other broken libraries call XEventsQueued
// unnecessarily and thus cause the file descriptor to not be ready,
// so we must check for already-read events:
if (XQLength(fl_display)) {do_queued_events(); return time;}
if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
#if !HAVE_POLL
#if !USE_POLL
fd_set fdt[3];
fdt[0] = fdsets[0];
fdt[1] = fdsets[1];
@ -187,31 +181,26 @@ double fl_wait(int timeout_flag, double time) {
#endif
int n;
if (!timeout_flag) {
#if HAVE_POLL
n = ::poll(fds, nfds, -1);
#else
n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
#endif
} else {
#if HAVE_POLL
int n = ::poll(fds, nfds, time > 0.0 ? int(time*1000) : 0);
if (time_to_wait < 2147483.648) {
#if USE_POLL
n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
#else
timeval t;
if (time <= 0.0) {
t.tv_sec = 0;
t.tv_usec = 0;
} else {
t.tv_sec = int(time);
t.tv_usec = int(1000000 * (time-t.tv_sec));
}
t.tv_sec = int(time_to_wait);
t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
#endif
} else {
#if USE_POLL
n = ::poll(pollfds, nfds, -1);
#else
n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
#endif
}
if (n > 0) {
for (int i=0; i<nfds; i++) {
#if HAVE_POLL
if (fds[i].revents) fd[i].cb(fd[i].fd, fd[i].arg);
#if USE_POLL
if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
#else
int f = fd[i].fd;
short revents = 0;
@ -222,7 +211,24 @@ double fl_wait(int timeout_flag, double time) {
#endif
}
}
return time;
return n;
}
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
int fl_ready() {
if (XQLength(fl_display)) return 1;
#if USE_POLL
return ::poll(pollfds, nfds, 0);
#else
timeval t;
t.tv_sec = 0;
t.tv_usec = 0;
fd_set fdt[3];
fdt[0] = fdsets[0];
fdt[1] = fdsets[1];
fdt[2] = fdsets[2];
return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
#endif
}
////////////////////////////////////////////////////////////////
@ -890,5 +896,5 @@ void Fl_Window::make_current() {
#endif
//
// End of "$Id: Fl_x.cxx,v 1.24.2.17 2000/06/05 21:21:03 mike Exp $".
// End of "$Id: Fl_x.cxx,v 1.24.2.18 2000/06/18 00:38:41 bill Exp $".
//