From 29d9e31c51e6c11d6e33abf9bc4551afd9de3836 Mon Sep 17 00:00:00 2001 From: Albrecht Schlosser Date: Mon, 31 Jan 2022 22:27:17 +0100 Subject: [PATCH] Consolidate timeout handling across platforms (#379) Add Fl_Timeout class Move platform independent code of Fl::wait() to main part - basic timeout handling - Fl::run_checks() - Fl::run_idle() - Fl::idle() - waiting time calculation (next timeout) - remove platform dependent "screen driver" stuff --- FL/Fl.H | 3 +- src/CMakeLists.txt | 3 +- src/Fl.cxx | 74 ++- src/Fl_Screen_Driver.H | 5 - src/Fl_Timeout.cxx | 456 ++++++++++++++++++ src/Fl_Timeout.h | 156 ++++++ src/Fl_add_idle.cxx | 11 +- src/Fl_cocoa.mm | 22 +- src/Fl_win32.cxx | 16 +- src/Makefile | 3 +- src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H | 7 +- src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx | 142 +----- src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H | 10 +- .../WinAPI/Fl_WinAPI_Screen_Driver.cxx | 145 +----- src/drivers/X11/Fl_X11_Screen_Driver.H | 5 - src/drivers/X11/Fl_X11_Screen_Driver.cxx | 161 +------ 16 files changed, 719 insertions(+), 500 deletions(-) create mode 100644 src/Fl_Timeout.cxx create mode 100644 src/Fl_Timeout.h diff --git a/FL/Fl.H b/FL/Fl.H index 37ec2ca13..a19788545 100644 --- a/FL/Fl.H +++ b/FL/Fl.H @@ -1,7 +1,7 @@ // // Main header file for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -510,6 +510,7 @@ int main() { static int has_check(Fl_Timeout_Handler, void* = 0); static void remove_check(Fl_Timeout_Handler, void* = 0); // private + static void run_idle(); static void run_checks(); static void add_fd(int fd, int when, Fl_FD_Handler cb, void* = 0); // platform dependent static void add_fd(int fd, Fl_FD_Handler cb, void* = 0); // platform dependent diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bca6d98e2..aa0eed11f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # # CMakeLists.txt to build the FLTK library using CMake (www.cmake.org) # -# Copyright 1998-2021 by Bill Spitzak and others. +# Copyright 1998-2022 by Bill Spitzak and others. # # This library is free software. Distribution and use rights are outlined in # the file "COPYING" which should have been included with this file. If this @@ -90,6 +90,7 @@ set (CPPFILES Fl_Text_Editor.cxx Fl_Tile.cxx Fl_Tiled_Image.cxx + Fl_Timeout.cxx Fl_Tooltip.cxx Fl_Tree.cxx Fl_Tree_Item_Array.cxx diff --git a/src/Fl.cxx b/src/Fl.cxx index db69fbd97..a37e1a137 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -1,7 +1,7 @@ // // Main event handling code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -23,6 +23,7 @@ #include "Fl_Screen_Driver.H" #include "Fl_Window_Driver.H" #include "Fl_System_Driver.H" +#include "Fl_Timeout.h" #include #include #include @@ -228,24 +229,22 @@ int Fl::event_inside(const Fl_Widget *o) /*const*/ { } // -// -// timer support -// +// cross-platform timer support // void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void *argp) { - Fl::screen_driver()->add_timeout(time, cb, argp); + Fl_Timeout::add_timeout(time, cb, argp); } void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp) { - Fl::screen_driver()->repeat_timeout(time, cb, argp); + Fl_Timeout::repeat_timeout(time, cb, argp); } /** Returns true if the timeout exists and has not been called yet. */ int Fl::has_timeout(Fl_Timeout_Handler cb, void *argp) { - return Fl::screen_driver()->has_timeout(cb, argp); + return Fl_Timeout::has_timeout(cb, argp); } /** @@ -256,7 +255,7 @@ int Fl::has_timeout(Fl_Timeout_Handler cb, void *argp) { This may change in the future. */ void Fl::remove_timeout(Fl_Timeout_Handler cb, void *argp) { - Fl::screen_driver()->remove_timeout(cb, argp); + Fl_Timeout::remove_timeout(cb, argp); } @@ -429,10 +428,46 @@ void fl_trigger_clipboard_notify(int source) { } //////////////////////////////////////////////////////////////// -// wait/run/check/ready: +// idle/wait/run/check/ready: void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions +/* + Private, undocumented method to run idle callbacks. + + FLTK guarantees that idle callbacks will never be called recursively, + i.e. while an idle callback is being executed. + + This method should (must) be the only way to run idle callbacks to + ensure that the `in_idle' flag is respected. + + Idle callbacks are executed whenever Fl::wait() is called and no events + are waiting to be serviced. + + If Fl::idle is set (non-NULL) this points at a function that executes + the first idle callback and appends it to the end of the list of idle + callbacks. For details see static function call_idle() in Fl_add_idle.cxx. + + If it is NULL then no idle callbacks are active and Fl::run_idle() returns + immediately. + + Note: idle callbacks can be queued in nested FLTK event loops like + ``` + while (win->shown()) + Fl::wait(); + ``` + if an event (timeout or button click etc.) handler calls Fl::add_idle() + or even in Fl::flush() if a draw() method calls Fl::add_idle(). +*/ +void Fl::run_idle() { + static char in_idle; + if (Fl::idle && !in_idle) { + in_idle = 1; + Fl::idle(); + in_idle = 0; + } +} + /** Waits a maximum of \p time_to_wait seconds or until "something happens". @@ -444,8 +479,29 @@ void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions occurs (this will happen on X11 if a signal happens). */ double Fl::wait(double time_to_wait) { + + // platform independent part: + // delete all widgets that were listed during callbacks do_widget_deletion(); + + Fl_Timeout::do_timeouts(); // execute timer callbacks + + Fl::run_checks(); + + Fl::run_idle(); + + // the idle function may turn off idle, we can then wait, + // or it leaves Fl::idle active and we set time_to_wait to 0 + if (Fl::idle) { + time_to_wait = 0.0; + } else { + // limit time by next timer interval + time_to_wait = Fl_Timeout::time_to_wait(time_to_wait); + } + + // platform dependent part: + return screen_driver()->wait(time_to_wait); } diff --git a/src/Fl_Screen_Driver.H b/src/Fl_Screen_Driver.H index 027b30077..1f6c80f98 100644 --- a/src/Fl_Screen_Driver.H +++ b/src/Fl_Screen_Driver.H @@ -114,11 +114,6 @@ public: virtual void get_system_colors() {} /* the default implementation of get_system_scheme() may be enough */ virtual const char *get_system_scheme(); - // --- global timers - virtual void add_timeout(double, Fl_Timeout_Handler, void *) {} - virtual void repeat_timeout(double, Fl_Timeout_Handler, void *) {} - virtual int has_timeout(Fl_Timeout_Handler, void *) { return 0; } - virtual void remove_timeout(Fl_Timeout_Handler, void *) {} static int secret_input_character; /* Implement to indicate whether complex text input may involve marked text. diff --git a/src/Fl_Timeout.cxx b/src/Fl_Timeout.cxx new file mode 100644 index 000000000..fed9fbad6 --- /dev/null +++ b/src/Fl_Timeout.cxx @@ -0,0 +1,456 @@ +// +// Timeout support functions for the Fast Light Tool Kit (FLTK). +// +// Author: Albrecht Schlosser +// Copyright 2021-2022 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#include "Fl_Timeout.h" +#include "Fl_System_Driver.H" + +#include + +/** + \file Fl_Timeout.cxx +*/ + +// static class variables + +Fl_Timeout *Fl_Timeout::free_timeout = 0; +Fl_Timeout *Fl_Timeout::first_timeout = 0; +Fl_Timeout *Fl_Timeout::current_timeout = 0; + +#if FL_TIMEOUT_DEBUG +static int num_timers = 0; // DEBUG +#endif + +// Internal timestamp, used for delta time calculation. +// Note: FLTK naming convention is not used here to avoid potential conflicts +// in the future. + +struct FlTimeStamp { + long sec; + long usec; +}; + +typedef struct FlTimeStamp FlTimeStamp_t; + +// Get a timestamp of type FlTimeStamp. + +// Depending on the system the resolution may be milliseconds or microseconds. +// Under certain conditions (particularly on Windows) the value in member `sec' +// may wrap around and does not represent a real time (maybe runtime of the system). +// Function elapsed_time() below uses this to subtract two timestamps which is always +// a correct delta time with milliseconds or microseconds resolution. + +// To do: Fl::system_driver()->gettime() was implemented for the Forms library and +// has a limited resolution (on Windows: milliseconds). On POSIX platforms it uses +// gettimeofday() with microsecond resolution. +// A new function could use a better resolution on Windows with its multimedia +// timers which requires a new dependency: winmm.lib (dll). This could be a future +// improvement, maybe set as a build option or generally (requires Win95 or 98?). + +static void get_timestamp(FlTimeStamp_t *ts) { + time_t sec; + int usec; + Fl::system_driver()->gettime(&sec, &usec); + ts->sec = (long)sec; + ts->usec = usec; +} + +// Returns 0 and initializes the "previous" timestamp when called for the first time. + +/* + Return the elapsed time since the last call in seconds. + + The first call initializes the internal "previous" timestamp and returns 0. + This must only be called from Fl_Timeout::elapse_timeouts(). + + Todo: remove static variable in this function: previous time should be + maintained in the caller. + + Return: double Elapsed time since the last call +*/ +static double elapsed_time() { + static int first = 1; // initialization + static FlTimeStamp_t prev; // previous timestamp + FlTimeStamp_t now; // current timestamp + double elapsed = 0.0; + get_timestamp(&now); + if (first) { + first = 0; + } else { + elapsed = double((now.sec - prev.sec) + (now.usec - prev.usec) / 1000000.); + } + prev = now; + return elapsed; +} + +/** + Insert a timer entry into the active timer queue. + + The base class Fl_Timeout inserts the timer as the first entry in + the queue of active timers. The default implementation is sufficient + for macOS and Windows. + + Derived classes (e.g. Fl_Timeout) can override this method. + Currently the Posix timeout handling (Unix, Linux) does this so + the timer queue entries are ordered by due time. + + \param[in] t Timer to be inserted (Fl_Timeout or derived class) +*/ +void Fl_Timeout::insert() { + Fl_Timeout **p = (Fl_Timeout **)&first_timeout; + while (*p && (*p)->time <= time) { + p = (Fl_Timeout **)&((*p)->next); + } + next = *p; + *p = this; +} + +/** + Returns whether the given timeout is active. + + This returns whether a timeout handler already exists in the queue + of active timers. + + If \p data == NULL only the Fl_Timeout_Handler \p cb must match to return + true, otherwise \p data must also match. + + \note It is a restriction that there is no way to look for a timeout whose + \p data is NULL (zero). Therefore using 0 (zero, NULL) as the timeout + \p data value is discouraged, unless you're sure that you will never + need to use Fl::has_timeout(callback, (void *)0);. + + Implements Fl::has_timeout(Fl_Timeout_Handler cb, void *data) + + \param[in] cb Timer callback (must match) + \param[in] data Wildcard if NULL, must match otherwise + + \returns whether the timer was found in the queue + \retval 0 not found + \retval 1 found +*/ + +int Fl_Timeout::has_timeout(Fl_Timeout_Handler cb, void *data) { + for (Fl_Timeout *t = first_timeout; t; t = t->next) { + if (t->callback == cb && t->data == data) + return 1; + } + return 0; +} + +void Fl_Timeout::add_timeout(double time, Fl_Timeout_Handler cb, void *data) { + elapse_timeouts(); + Fl_Timeout *t = get(time, cb, data); + t->Fl_Timeout::insert(); +} + +void Fl_Timeout::repeat_timeout(double time, Fl_Timeout_Handler cb, void *data) { + elapse_timeouts(); + Fl_Timeout *t = (Fl_Timeout *)get(time, cb, data); + Fl_Timeout *cur = current_timeout; + if (cur) { + t->time += cur->time; // was: missed_timeout_by (always <= 0.0) + } + if (t->time < 0.0) + t->time = 0.001; // at least 1 ms + t->insert(); +} + +/** + Remove a timeout callback. It is harmless to remove a timeout + callback that no longer exists. + + \note This version removes all matching timeouts, not just the first one. + This may change in the future. + + Implements Fl::remove_timeout(Fl_Timeout_Handler cb, void *data) +*/ +void Fl_Timeout::remove_timeout(Fl_Timeout_Handler cb, void *data) { + for (Fl_Timeout** p = &first_timeout; *p;) { + Fl_Timeout* t = *p; + if (t->callback == cb && (t->data == data || !data)) { + *p = t->next; + t->next = free_timeout; + free_timeout = t; + } else { + p = &(t->next); + } + } +} + +/** + Remove the timeout from the active timer queue and push it onto + the stack of currently running callbacks. + + This becomes the current() timeout which can be used in + Fl::repeat_timeout(). + + \see Fl_Timeout::current() +*/ +void Fl_Timeout::make_current() { + // printf("[%4d] Fl_Timeout::make_current(%p)\n", __LINE__, this); + // remove the timer entry from the active timer queue + for (Fl_Timeout** p = &first_timeout; *p;) { + Fl_Timeout* t = *p; + if (t == this) { + *p = t->next; + // push it to the current timer stack + t->next = current_timeout; + current_timeout = t; + break; + } else { + p = &(t->next); + } + } +} + +/** + Remove the top-most timeout from the stack of currently running + timeout callbacks and insert it into the list of free timers. + + This should always return a non-NULL value, otherwise there's a bug + in the library. Typical code in the library would look like: + \code + // The timeout \p Fl_Timeout *t has exired, run its callback + t->make_current(); + (t->callback)(t->data); + t->release(); + \endcode + + \return Fl_Timeout* current timeout or NULL +*/ +void Fl_Timeout::release() { + Fl_Timeout *t = current_timeout; + if (t) { + + // The first timer in the "current" list *should* be 'this' but we + // check it to be sure. Issue an error message which should never appear. + // If it would happen we'd remove the wrong timer from the current timer + // list. This is not good but it doesn't really do harm. + + if (t != this) { + Fl::error("*** Fl_Timeout::release() *** timer t (%p) != this (%p)\n", t, this); + } + + // remove the timer from the list + current_timeout = t->next; + } + // put the timer into the list of free timers + t->next = free_timeout; + free_timeout = t; +} + +/** + Returns the first (top-most) timeout from the current timeout stack. + + This returns a pointer to the timeout but does not remove it from the + list of current timeouts. This should be the timeout that is currently + executing its callback. + + \return Fl_Timeout* The current timeout whose callback is running. + \retval NULL if no callback is currently running. +*/ +Fl_Timeout *Fl_Timeout::current() { + return current_timeout; +} + +/** + Get an Fl_Timeout instance for further handling. + + The timer object will be initialized with the input parameters + as given by Fl::add_timeout() or Fl::repeat_timeout(). + + Fl_Timeout objects are maintained in three queues: + - active timer queue + - list (stack, i.e. LIFO) of currently executing timer callbacks + - free timer entries. + + When the FLTK program is launched all queues are empty. Whenever + a new timer object is required the get() method is called and a timer + object is either found in the queue of free timer entries or a new + timer object is created (operator new). + + Active timer entries are inserted into the "active timer queue" until + they expire and their callback is called. + + Before the callback is called the timer entry is inserted into the list + of current timers, i.e. it becomes the Fl_Timeout::current() timeout. + This can be used in Fl::repeat_timeout() to find out if and how long the + current timeout has been delayed. + + When a timer is no longer used it is popped from the \p current list + and inserted into the "free timer" list so it can be reused later. + + Timer queue entries are never returned to the system, there's no garbage + collection. The total number of timer objects is determined by the + largest number of concurrently active timers. + + \param[in] time requested delta time + \param[in] cb timer callback + \param[in] data userdata for timer callback + + \return Fl_Timeout* Timer entry + + \see Fl::add_timeout(), Fl::repeat_timeout() +*/ + +Fl_Timeout *Fl_Timeout::get(double time, Fl_Timeout_Handler cb, void *data) { + + Fl_Timeout *t = (Fl_Timeout *)free_timeout; + if (t) { + free_timeout = t->next; + t->next = 0; + } else { + t = new Fl_Timeout; +#if FL_TIMEOUT_DEBUG + num_timers++; // DEBUG: count allocated timers +#endif + } + + memset(t, 0, sizeof(*t)); + t->delay(time); + t->callback = cb; + t->data = data; + return t; +} + +/** + Elapse all timers w/o calling their callbacks. + + All timer values are adjusted by the delta time since the last call. + This method does \b NOT call timer callbacks if timers are expired. + + This must be called before new timers are added to the timer queue to make + sure that the next timer decrement does not count down too much time. + + \see Fl_Timeout::do_timeouts() +*/ +void Fl_Timeout::elapse_timeouts() { + double elapsed = elapsed_time(); + // printf("elapse_timeouts: elapsed = %9.6f\n", double(elapsed)/1000000.); + + if (elapsed > 0.0) { + + // active timers + + for (Fl_Timeout* t = first_timeout; t; t = t->next) { + t->time -= elapsed; + } + + // "current" timers, i.e. timers being serviced + + for (Fl_Timeout* t = current_timeout; t; t = t->next) { + t->time -= elapsed; + } + } +} + +/** + Elapse timers and call their callbacks if any timers are expired. +*/ +void Fl_Timeout::do_timeouts() { + if (first_timeout) { + Fl_Timeout::elapse_timeouts(); + Fl_Timeout *t; + while ((t = Fl_Timeout::first_timeout)) { + if (t->time > 0) break; + // make this timeout the "current" timeout + t->make_current(); + // now it is safe for the callback to do add_timeout: + t->callback(t->data); + // release the timer entry + t->release(); + + // Elapse timers (again) because the callback may have used a + // significant amount of time. This is optional though. + + Fl_Timeout::elapse_timeouts(); + } + } +} + +/** + Returns the delay in seconds until the next timer expires, + limited by \p ttw. + + This function calculates the time to wait for the FLTK event queue + processing, depending on the given value \p ttw. + + If at least one timer is active and its timeout value is smaller than + \p ttw then this value is returned. Fl::wait() will wait no longer than + until the next timer expires. + + If no timer is active this returns the input value \p ttw unchanged. + + If at least one timer is expired this returns 0.0 so the event processing + does not wait. + + \param[in] ttw time to wait from Fl::wait() etc. (upper limit) + + \return delay until next timeout or 0.0 (see description) +*/ +double Fl_Timeout::time_to_wait(double ttw) { + Fl_Timeout *t = first_timeout; + if (!t) return ttw; + double tdelay = t->delay(); +if (tdelay < 0.0) + return 0.0; + if (tdelay < ttw) + return tdelay; + return ttw; +} + + +// Write some statistics to stdout for debugging + +#if FL_TIMEOUT_DEBUG + +void Fl_Timeout::debug(int level) { + + printf("\nFl_Timeout::debug: number of allocated timers = %d\n", num_timers); + + int active = 0; + Fl_Timeout *t = first_timeout; + while (t) { + active++; + t = t->next; + } + + int current = 0; + t = current_timeout; + while (t) { + current++; + t = t->next; + } + + int free = 0; + t = free_timeout; + while (t) { + free++; + t = t->next; + } + + printf("Fl_Timeout::debug: active: %d, current: %d, free: %d\n\n", active, current, free); + + t = first_timeout; + int n = 0; + while (t) { + printf("Active timer %3d: time = %10.6f sec\n", n+1, t->delay()); + t = t->next; + n++; + } +} // Fl_Timeout::debug(int) + +#endif // FL_TIMEOUT_DEBUG diff --git a/src/Fl_Timeout.h b/src/Fl_Timeout.h new file mode 100644 index 000000000..de4110609 --- /dev/null +++ b/src/Fl_Timeout.h @@ -0,0 +1,156 @@ +// +// Header for timeout support functions for the Fast Light Tool Kit (FLTK). +// +// Author: Albrecht Schlosser +// Copyright 2021-2022 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// https://www.fltk.org/COPYING.php +// +// Please see the following page on how to report bugs and issues: +// +// https://www.fltk.org/bugs.php +// + +#ifndef _src_Fl_Timeout_h_ +#define _src_Fl_Timeout_h_ + +#include + +#define FL_TIMEOUT_DEBUG 0 // 1 = include debugging features, 0 = no + +/** \file + Fl_Timeout handling. + + This file contains implementations of: + + - Fl::add_timeout() + - Fl::repeat_timeout() + - Fl::remove_timeout() + - Fl::has_timeout() + + and related methods of class Fl_Timeout. +*/ + +/** + Class Fl_Timeout handles all timeout related functions. + + All code is platform independent except retrieving a timestamp + which requires calling a system driver function and potentially + results in different timer resolutions (from milliseconds to + microseconds). +*/ +class Fl_Timeout { + +protected: + + Fl_Timeout *next; // ** Link to next timeout + Fl_Timeout_Handler callback; // the user's callback + void *data; // the user's callback data + double time; // delay until timeout + + // constructor + Fl_Timeout() { + next = 0; + callback = 0; + data = 0; + time = 0; + } + + ~Fl_Timeout() {} + + static Fl_Timeout *get(double time, Fl_Timeout_Handler cb, void *data); + + // insert this timer into the active timer queue, sorted by expiration time + void insert(); + + // remove this timer from the active timer queue and + // add it to the "current" timer stack + void make_current(); + + // remove this timer from the current timer stack and + // add it to the list of free timers + void release(); + + /** Get the timer's delay in seconds. */ + double delay() { + return time; + } + + /** Set the timer's delay in seconds. */ + void delay(double t) { + time = t; + } + +public: + // Returns whether the given timeout is active. + static int has_timeout(Fl_Timeout_Handler cb, void *data); + + // Add or remove timeouts + + static void add_timeout(double time, Fl_Timeout_Handler cb, void *data); + static void repeat_timeout(double time, Fl_Timeout_Handler cb, void *data); + static void remove_timeout(Fl_Timeout_Handler cb, void *data); + + // Elapse timeouts, i.e. calculate new delay time of all timers. + // This does not call the timer callbacks. + static void elapse_timeouts(); + + // Elapse timeouts and call timer callbacks. + static void do_timeouts(); + + // Return the delay in seconds until the next timer expires. + static double time_to_wait(double ttw); + +#if FL_TIMEOUT_DEBUG + // Write some statistics to stdout + static void debug(int level = 1); +#endif + +protected: + + static Fl_Timeout *current(); + + /** + List of active timeouts. + + These timeouts can be triggered when due, which calls their callbacks. + The lifetime of a timeout: + - active, in this queue + - callback running, in queue \p current_timeout + - done, in list of free timeouts, ready to be reused. + */ + static Fl_Timeout *first_timeout; + + /** + List of free timeouts after use. + Timeouts can be reused many times. + */ + static Fl_Timeout *free_timeout; + + /** + The list of current timeouts is used to store the timeout whose callback + is called while the callback is executed. This is used like a stack, the + current timeout is pushed to the front of the list and once the callback + is finished, that timeout is removed and entered into the free list. + + Background: Fl::repeat_timeout() needs to know which timeout triggered it + and the exact schedule time and/or the delay of that timeout, i.e. how + long the scheduled time was missed before the callback was called. + A static, global variable is not sufficient since the user code can call + other functions, e.g. dialogs, that run a nested event loop which can + run another timeout callback. Hence this list of "current" timeouts is + used like a stack (last in, first out). + + \see Fl_Timeout::push() Member function (method) + \see Fl_Timeout *Fl_Timeout::pop() Static function + \see Fl_Timeout *Fl_Timeout::current() TStatic function + */ + static Fl_Timeout *current_timeout; // list of "current" timeouts + +}; // class Fl_Timeout + +#endif // _src_Fl_Timeout_h_ diff --git a/src/Fl_add_idle.cxx b/src/Fl_add_idle.cxx index f5d588cfb..1f1673f36 100644 --- a/src/Fl_add_idle.cxx +++ b/src/Fl_add_idle.cxx @@ -1,7 +1,7 @@ // // Idle routine support for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -32,6 +32,13 @@ static idle_cb* first; static idle_cb* last; static idle_cb* freelist; +// The function call_idle() +// - removes the first idle callback from the front of the list (ring) +// - adds it as the last entry and +// - calls the idle callback. +// The idle callback may remove itself from the list of idle callbacks +// by calling Fl::remove_idle() + static void call_idle() { idle_cb* p = first; last = p; first = p->next; @@ -42,7 +49,7 @@ static void call_idle() { Adds a callback function that is called every time by Fl::wait() and also makes it act as though the timeout is zero (this makes Fl::wait() return immediately, so if it is in a loop it is called repeatedly, and thus the - idle fucntion is called repeatedly). The idle function can be used to get + idle function is called repeatedly). The idle function can be used to get background processing done. You can have multiple idle callbacks. To remove an idle callback use diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 52680a84f..d9f6e0f2a 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -1,7 +1,7 @@ // -// MacOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). +// macOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -27,6 +27,7 @@ extern "C" { #include #include "Fl_Window_Driver.H" #include "Fl_Screen_Driver.H" +#include "Fl_Timeout.h" #include #include #include @@ -749,6 +750,10 @@ static int do_queued_events( double time = 0.0 ) dataready.StartThread(); } + // Elapse timeouts and calculate waiting time + Fl_Timeout::elapse_timeouts(); + time = Fl_Timeout::time_to_wait(time); + fl_unlock_function(); NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:time] @@ -773,22 +778,11 @@ double Fl_Cocoa_Screen_Driver::wait(double time_to_wait) if (dropped_files_list) { // when the list of dropped files is not empty, open one and remove it from list drain_dropped_files_list(); } - Fl::run_checks(); - static int in_idle = 0; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - if (Fl::idle) { - if (!in_idle) { - in_idle = 1; - Fl::idle(); - in_idle = 0; - } - // the idle function may turn off idle, we can then wait: - if (Fl::idle) time_to_wait = 0.0; - } if (fl_mac_os_version < 101100) NSDisableScreenUpdates(); // 10.3 Makes updates to all windows appear as a single event Fl::flush(); if (fl_mac_os_version < 101100) NSEnableScreenUpdates(); // 10.3 - if (Fl::idle && !in_idle) // 'idle' may have been set within flush() + if (Fl::idle) // 'idle' may have been set within flush() time_to_wait = 0.0; int retval = do_queued_events(time_to_wait); diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx index 5ee2fe45a..b57b81a1c 100644 --- a/src/Fl_win32.cxx +++ b/src/Fl_win32.cxx @@ -1,7 +1,7 @@ // // Windows-specific code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2021 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -54,6 +54,7 @@ void fl_cleanup_dc_list(void); #include #include "Fl_Window_Driver.H" #include "Fl_Screen_Driver.H" +#include "Fl_Timeout.h" #include // for fl_graphics_driver #include "drivers/WinAPI/Fl_WinAPI_Window_Driver.H" #include "drivers/WinAPI/Fl_WinAPI_System_Driver.H" @@ -417,16 +418,6 @@ double Fl_WinAPI_Screen_Driver::wait(double time_to_wait) { int have_message = 0; - Fl::run_checks(); - - // idle processing - static char in_idle; - if (Fl::idle && !in_idle) { - in_idle = 1; - Fl::idle(); - in_idle = 0; - } - if (nfds) { // For Windows we need to poll for socket input FIRST, since // the event queue is not something we can select() on... @@ -469,6 +460,9 @@ double Fl_WinAPI_Screen_Driver::wait(double time_to_wait) { fl_unlock_function(); time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait); + + time_to_wait = Fl_Timeout::time_to_wait(time_to_wait); + int t_msec = (int)(time_to_wait * 1000.0 + 0.5); MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT); diff --git a/src/Makefile b/src/Makefile index 6df91602f..f98ca0279 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ # # Library Makefile for the Fast Light Tool Kit (FLTK). # -# Copyright 1998-2021 by Bill Spitzak and others. +# Copyright 1998-2022 by Bill Spitzak and others. # # This library is free software. Distribution and use rights are outlined in # the file "COPYING" which should have been included with this file. If this @@ -92,6 +92,7 @@ CPPFILES = \ Fl_Text_Editor.cxx \ Fl_Tile.cxx \ Fl_Tiled_Image.cxx \ + Fl_Timeout.cxx \ Fl_Tree.cxx \ Fl_Tree_Item.cxx \ Fl_Tree_Item_Array.cxx \ diff --git a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H index dcd5c4f57..7dd6fc14a 100644 --- a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H +++ b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H @@ -2,7 +2,7 @@ // Definition of Apple Cocoa Screen interface // for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2018 by Bill Spitzak and others. +// Copyright 2010-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -80,11 +80,6 @@ public: virtual void grab(Fl_Window* win); // --- global colors virtual void get_system_colors(); - // --- global timers - virtual void add_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual void repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual int has_timeout(Fl_Timeout_Handler cb, void *argp); - virtual void remove_timeout(Fl_Timeout_Handler cb, void *argp); virtual int has_marked_text() const; static void reset_marked_text(); static void insertion_point_location(int x, int y, int height); diff --git a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx index 1a36149e2..e213e5fd0 100644 --- a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx +++ b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx @@ -1,7 +1,7 @@ // // Definition of Apple Cocoa Screen interface. // -// Copyright 1998-2018 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -401,143 +401,3 @@ Fl_RGB_Image *Fl_Cocoa_Screen_Driver::read_win_rectangle(int X, int Y, int w, in rgb->alloc_array = 1; return rgb; } - -// -// MacOS X timers -// - -struct MacTimeout { - Fl_Timeout_Handler callback; - void* data; - CFRunLoopTimerRef timer; - char pending; - CFAbsoluteTime next_timeout; // scheduled time for this timer -}; -static MacTimeout* mac_timers; -static int mac_timer_alloc; -static int mac_timer_used; -static MacTimeout* current_timer; // the timer that triggered its callback function - -static void realloc_timers() -{ - if (mac_timer_alloc == 0) { - mac_timer_alloc = 8; - fl_open_display(); // needed because the timer creates an event - } - mac_timer_alloc *= 2; - MacTimeout* new_timers = new MacTimeout[mac_timer_alloc]; - memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc); - memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used); - if (current_timer) { - MacTimeout* newCurrent = new_timers + (current_timer - mac_timers); - current_timer = newCurrent; - } - MacTimeout* delete_me = mac_timers; - mac_timers = new_timers; - delete [] delete_me; -} - -static void delete_timer(MacTimeout& t) -{ - if (t.timer) { - CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), - t.timer, - kCFRunLoopDefaultMode); - CFRelease(t.timer); - memset(&t, 0, sizeof(MacTimeout)); - } -} - -static void do_timer(CFRunLoopTimerRef timer, void* data) -{ - fl_lock_function(); - fl_intptr_t timerId = (fl_intptr_t)data; - current_timer = &mac_timers[timerId]; - current_timer->pending = 0; - (current_timer->callback)(current_timer->data); - if (current_timer && current_timer->pending == 0) - delete_timer(*current_timer); - current_timer = NULL; - - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); -} - -void Fl_Cocoa_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void* data) -{ - // always create a new timer entry - fl_intptr_t timer_id = -1; - // find an empty slot in the timer array - for (int i = 0; i < mac_timer_used; ++i) { - if ( !mac_timers[i].timer ) { - timer_id = i; - break; - } - } - // if there was no empty slot, append a new timer - if (timer_id == -1) { - // make space if needed - if (mac_timer_used == mac_timer_alloc) { - realloc_timers(); - } - timer_id = mac_timer_used++; - } - // now install a brand new timer - MacTimeout& t = mac_timers[timer_id]; - CFRunLoopTimerContext context = {0, (void*)timer_id, NULL,NULL,NULL}; - CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, - CFAbsoluteTimeGetCurrent() + time, - 1E30, - 0, - 0, - do_timer, - &context - ); - if (timerRef) { - CFRunLoopAddTimer(CFRunLoopGetCurrent(), - timerRef, - kCFRunLoopDefaultMode); - t.callback = cb; - t.data = data; - t.timer = timerRef; - t.pending = 1; - t.next_timeout = CFRunLoopTimerGetNextFireDate(timerRef); - } -} - -void Fl_Cocoa_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) -{ - if (!current_timer) { - add_timeout(time, cb, data); - return; - } - // k = how many times 'time' seconds after the last scheduled timeout until the future - double k = ceil( (CFAbsoluteTimeGetCurrent() - current_timer->next_timeout) / time); - if (k < 1) k = 1; - current_timer->next_timeout += k * time; - CFRunLoopTimerSetNextFireDate(current_timer->timer, current_timer->next_timeout ); - current_timer->callback = cb; - current_timer->data = data; - current_timer->pending = 1; -} - -int Fl_Cocoa_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void* data) -{ - for (int i = 0; i < mac_timer_used; ++i) { - MacTimeout& t = mac_timers[i]; - if (t.callback == cb && t.data == data && t.pending) { - return 1; - } - } - return 0; -} - -void Fl_Cocoa_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void* data) -{ - for (int i = 0; i < mac_timer_used; ++i) { - MacTimeout& t = mac_timers[i]; - if (t.callback == cb && ( t.data == data || data == NULL)) { - delete_timer(t); - } - } -} diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H index 9bd8485a3..c621f7e5e 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H +++ b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H @@ -1,8 +1,7 @@ // -// Definition of Windows screen interface -// for the Fast Light Tool Kit (FLTK). +// Windows screen interface for the Fast Light Tool Kit (FLTK). // -// Copyright 2010-2018 by Bill Spitzak and others. +// Copyright 2010-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -67,11 +66,6 @@ public: virtual void grab(Fl_Window* win); // --- global colors virtual void get_system_colors(); - // --- global timers - virtual void add_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual void repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual int has_timeout(Fl_Timeout_Handler cb, void *argp); - virtual void remove_timeout(Fl_Timeout_Handler cb, void *argp); virtual int dnd(int unused); virtual int compose(int &del); virtual Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h, Fl_Window *win, bool may_capture_subwins, bool *did_capture_subwins); diff --git a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx index 5747f16a4..489e7fe8d 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx +++ b/src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx @@ -1,7 +1,7 @@ // // Windows screen interface for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2018 by Bill Spitzak and others. +// Copyright 1998-2022 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -328,149 +328,6 @@ void Fl_WinAPI_Screen_Driver::get_system_colors() } -// ---- timers - - -struct Win32Timer -{ - UINT_PTR handle; - Fl_Timeout_Handler callback; - void *data; -}; -static Win32Timer* win32_timers; -static int win32_timer_alloc; -static int win32_timer_used; -static HWND s_TimerWnd; - - -static void realloc_timers() -{ - if (win32_timer_alloc == 0) { - win32_timer_alloc = 8; - } - win32_timer_alloc *= 2; - Win32Timer* new_timers = new Win32Timer[win32_timer_alloc]; - memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used); - memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used); - Win32Timer* delete_me = win32_timers; - win32_timers = new_timers; - delete [] delete_me; -} - - -static void delete_timer(Win32Timer& t) -{ - KillTimer(s_TimerWnd, t.handle); - memset(&t, 0, sizeof(Win32Timer)); -} - - -static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_TIMER: - { - unsigned int id = (unsigned) (wParam - 1); - if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) { - Fl_Timeout_Handler cb = win32_timers[id].callback; - void* data = win32_timers[id].data; - delete_timer(win32_timers[id]); - if (cb) { - (*cb)(data); - } - } - } - return 0; - - default: - break; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - - -void Fl_WinAPI_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void* data) -{ - repeat_timeout(time, cb, data); -} - - -void Fl_WinAPI_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) -{ - int timer_id = -1; - for (int i = 0; i < win32_timer_used; ++i) { - if ( !win32_timers[i].handle ) { - timer_id = i; - break; - } - } - if (timer_id == -1) { - if (win32_timer_used == win32_timer_alloc) { - realloc_timers(); - } - timer_id = win32_timer_used++; - } - unsigned int elapsed = (unsigned int)(time * 1000); - - if ( !s_TimerWnd ) { - const char* timer_class = "FLTimer"; - WNDCLASSEX wc; - memset(&wc, 0, sizeof(wc)); - wc.cbSize = sizeof (wc); - wc.style = CS_CLASSDC; - wc.lpfnWndProc = (WNDPROC)s_TimerProc; - wc.hInstance = fl_display; - wc.lpszClassName = timer_class; - /*ATOM atom =*/ RegisterClassEx(&wc); - // create a zero size window to handle timer events - s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW, - timer_class, "", - WS_POPUP, - 0, 0, 0, 0, - NULL, NULL, fl_display, NULL); - // just in case this OS won't let us create a 0x0 size window: - if (!s_TimerWnd) - s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW, - timer_class, "", - WS_POPUP, - 0, 0, 1, 1, - NULL, NULL, fl_display, NULL); - ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE); - } - - win32_timers[timer_id].callback = cb; - win32_timers[timer_id].data = data; - - win32_timers[timer_id].handle = - SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL); -} - - -int Fl_WinAPI_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void* data) -{ - for (int i = 0; i < win32_timer_used; ++i) { - Win32Timer& t = win32_timers[i]; - if (t.handle && t.callback == cb && t.data == data) { - return 1; - } - } - return 0; -} - - -void Fl_WinAPI_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void* data) -{ - int i; - for (i = 0; i < win32_timer_used; ++i) { - Win32Timer& t = win32_timers[i]; - if (t.handle && t.callback == cb && - (t.data == data || data == NULL)) { - delete_timer(t); - } - } -} - int Fl_WinAPI_Screen_Driver::compose(int &del) { unsigned char ascii = (unsigned char)Fl::e_text[0]; int condition = (Fl::e_state & (FL_ALT | FL_META)) && !(ascii & 128) ; diff --git a/src/drivers/X11/Fl_X11_Screen_Driver.H b/src/drivers/X11/Fl_X11_Screen_Driver.H index 7d665dbb4..c8676f347 100644 --- a/src/drivers/X11/Fl_X11_Screen_Driver.H +++ b/src/drivers/X11/Fl_X11_Screen_Driver.H @@ -85,11 +85,6 @@ public: virtual int parse_color(const char* p, uchar& r, uchar& g, uchar& b); virtual void get_system_colors(); virtual const char *get_system_scheme(); - // --- global timers - virtual void add_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual void repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp); - virtual int has_timeout(Fl_Timeout_Handler cb, void *argp); - virtual void remove_timeout(Fl_Timeout_Handler cb, void *argp); virtual int dnd(int unused); virtual int compose(int &del); virtual void compose_reset(); diff --git a/src/drivers/X11/Fl_X11_Screen_Driver.cxx b/src/drivers/X11/Fl_X11_Screen_Driver.cxx index beb30df83..3e7edf872 100644 --- a/src/drivers/X11/Fl_X11_Screen_Driver.cxx +++ b/src/drivers/X11/Fl_X11_Screen_Driver.cxx @@ -31,6 +31,8 @@ #include #include +#include "../../Fl_Timeout.h" + #if HAVE_XINERAMA # include #endif @@ -55,52 +57,6 @@ extern const char *fl_bg; extern const char *fl_bg2; // end of extern additions workaround -// -// X11 timers -// - - -//////////////////////////////////////////////////////////////////////// -// Timeouts are stored in a sorted list (*first_timeout), so only the -// first one needs to be checked to see if any should be called. -// Allocated, but unused (free) Timeout structs are stored in another -// linked list (*free_timeout). - -struct Timeout { - double time; - void (*cb)(void*); - void* arg; - Timeout* next; -}; -static Timeout* first_timeout, *free_timeout; - -// 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 actually elapse time. -static char reset_clock = 1; - -static void elapse_timeouts() { - static struct timeval prevclock; - struct timeval newclock; - gettimeofday(&newclock, NULL); - 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 (reset_clock) { - reset_clock = 0; - } else if (elapsed > 0) { - for (Timeout* t = first_timeout; t; t = t->next) t->time -= elapsed; - } -} - - -// Continuously-adjusted error value, this is a number <= 0 for how late -// we were at calling the last timeout. This appears to make repeat_timeout -// very accurate even when processing takes a significant portion of the -// time interval: -static double missed_timeout_by; /** Creates a driver that manages all screen and display related calls. @@ -411,39 +367,6 @@ void Fl_X11_Screen_Driver::flush() double Fl_X11_Screen_Driver::wait(double time_to_wait) { - static char in_idle; - - if (first_timeout) { - elapse_timeouts(); - Timeout *t; - while ((t = first_timeout)) { - if (t->time > 0) break; - // The first timeout in the array has expired. - missed_timeout_by = t->time; - // We must remove timeout from array before doing the callback: - void (*cb)(void*) = t->cb; - void *argp = t->arg; - first_timeout = t->next; - t->next = free_timeout; - free_timeout = t; - // Now it is safe for the callback to do add_timeout: - cb(argp); - } - } else { - reset_clock = 1; // we are not going to check the clock - } - Fl::run_checks(); - if (Fl::idle) { - if (!in_idle) { - in_idle = 1; - Fl::idle(); - in_idle = 0; - } - // the idle function may turn off idle, we can then wait: - if (Fl::idle) time_to_wait = 0.0; - } - if (first_timeout && first_timeout->time < time_to_wait) - time_to_wait = first_timeout->time; if (time_to_wait <= 0.0) { // do flush second so that the results of events are visible: int ret = this->poll_or_select_with_delay(0.0); @@ -452,25 +375,20 @@ double Fl_X11_Screen_Driver::wait(double time_to_wait) } else { // do flush first so that user sees the display: Fl::flush(); - if (Fl::idle && !in_idle) // 'idle' may have been set within flush() + if (Fl::idle) // 'idle' may have been set within flush() time_to_wait = 0.0; - else if (first_timeout && first_timeout->time < time_to_wait) { - // another timeout may have been queued within flush(), see STR #3188 - time_to_wait = first_timeout->time >= 0.0 ? first_timeout->time : 0.0; + else { + Fl_Timeout::elapse_timeouts(); + time_to_wait = Fl_Timeout::time_to_wait(time_to_wait); } return this->poll_or_select_with_delay(time_to_wait); } } -int Fl_X11_Screen_Driver::ready() -{ - if (first_timeout) { - elapse_timeouts(); - if (first_timeout->time <= 0) return 1; - } else { - reset_clock = 1; - } +int Fl_X11_Screen_Driver::ready() { + Fl_Timeout::elapse_timeouts(); + if (Fl_Timeout::time_to_wait(1.0) <= 0.0) return 1; return this->poll_or_select(); } @@ -598,67 +516,6 @@ const char *Fl_X11_Screen_Driver::get_system_scheme() return s; } -// ###################### *FIXME* ######################## -// ###################### *FIXME* ######################## -// ###################### *FIXME* ######################## - - -// -// X11 timers -// - -void Fl_X11_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void *argp) { - elapse_timeouts(); - missed_timeout_by = 0; - repeat_timeout(time, cb, argp); -} - -void Fl_X11_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp) { - time += missed_timeout_by; if (time < -.05) time = 0; - Timeout* t = free_timeout; - if (t) { - free_timeout = t->next; - } else { - t = new Timeout; - } - t->time = time; - t->cb = cb; - t->arg = argp; - // insert-sort the new timeout: - Timeout** p = &first_timeout; - while (*p && (*p)->time <= time) p = &((*p)->next); - t->next = *p; - *p = t; -} - -/** - Returns true if the timeout exists and has not been called yet. -*/ -int Fl_X11_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void *argp) { - for (Timeout* t = first_timeout; t; t = t->next) - if (t->cb == cb && t->arg == argp) return 1; - return 0; -} - -/** - Removes a timeout callback. It is harmless to remove a timeout - callback that no longer exists. - - \note This version removes all matching timeouts, not just the first one. - This may change in the future. -*/ -void Fl_X11_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void *argp) { - for (Timeout** p = &first_timeout; *p;) { - Timeout* t = *p; - if (t->cb == cb && (t->arg == argp || !argp)) { - *p = t->next; - t->next = free_timeout; - free_timeout = t; - } else { - p = &(t->next); - } - } -} int Fl_X11_Screen_Driver::compose(int& del) { int condition;