|
commit 29d9e31c51e6c11d6e33abf9bc4551afd9de3836
Author: Albrecht Schlosser <albrechts.fltk@online.de>
AuthorDate: Mon Jan 31 22:27:17 2022 +0100
Commit: GitHub <noreply@github.com>
CommitDate: Mon Jan 31 22:27:17 2022 +0100
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 +-
src/drivers/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(-)
diff --git FL/Fl.H FL/Fl.H
index 37ec2ca..a197885 100644
--- FL/Fl.H
+++ 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 src/CMakeLists.txt src/CMakeLists.txt
index bca6d98..aa0eed1 100644
--- src/CMakeLists.txt
+++ 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 src/Fl.cxx src/Fl.cxx
index db69fbd..a37e1a1 100644
--- src/Fl.cxx
+++ 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 <FL/Fl_Window.H>
#include <FL/Fl_Tooltip.H>
#include <FL/fl_draw.H>
@@ -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 src/Fl_Screen_Driver.H src/Fl_Screen_Driver.H
index 027b300..1f6c80f 100644
--- src/Fl_Screen_Driver.H
+++ 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 src/Fl_Timeout.cxx src/Fl_Timeout.cxx
new file mode 100644
index 0000000..fed9fba
--- /dev/null
+++ 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 <stdio.h>
+
+/**
+ \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 <kbd>Fl::has_timeout(callback, (void *)0);</kbd>.
+
+ 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 src/Fl_Timeout.h src/Fl_Timeout.h
new file mode 100644
index 0000000..de41106
--- /dev/null
+++ 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 <FL/Fl.H>
+
+#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 src/Fl_add_idle.cxx src/Fl_add_idle.cxx
index f5d588c..1f1673f 100644
--- src/Fl_add_idle.cxx
+++ 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 src/Fl_cocoa.mm src/Fl_cocoa.mm
index 52680a8..d9f6e0f 100644
--- src/Fl_cocoa.mm
+++ 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 <FL/platform.H>
#include "Fl_Window_Driver.H"
#include "Fl_Screen_Driver.H"
+#include "Fl_Timeout.h"
#include <FL/Fl_Window.H>
#include <FL/Fl_Tooltip.H>
#include <FL/Fl_Printer.H>
@@ -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 src/Fl_win32.cxx src/Fl_win32.cxx
index 5ee2fe4..b57b81a 100644
--- src/Fl_win32.cxx
+++ 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 <FL/platform.H>
#include "Fl_Window_Driver.H"
#include "Fl_Screen_Driver.H"
+#include "Fl_Timeout.h"
#include <FL/Fl_Graphics_Driver.H> // 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 src/Makefile src/Makefile
index 6df9160..f98ca02 100644
--- src/Makefile
+++ 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 src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H
index dcd5c4f..7dd6fc1 100644
--- src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.H
+++ 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 src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx
index 1a36149..e213e5f 100644
--- src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx
+++ 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 src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H
index 9bd8485..c621f7e 100644
--- src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.H
+++ 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 src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx
index 5747f16..489e7fe 100644
--- src/drivers/WinAPI/Fl_WinAPI_Screen_Driver.cxx
+++ 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 src/drivers/X11/Fl_X11_Screen_Driver.H src/drivers/X11/Fl_X11_Screen_Driver.H
index 7d665db..c8676f3 100644
--- src/drivers/X11/Fl_X11_Screen_Driver.H
+++ 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 src/drivers/X11/Fl_X11_Screen_Driver.cxx src/drivers/X11/Fl_X11_Screen_Driver.cxx
index beb30df..3e7edf8 100644
--- src/drivers/X11/Fl_X11_Screen_Driver.cxx
+++ src/drivers/X11/Fl_X11_Screen_Driver.cxx
@@ -31,6 +31,8 @@
#include <FL/filename.H>
#include <sys/time.h>
+#include "../../Fl_Timeout.h"
+
#if HAVE_XINERAMA
# include <X11/extensions/Xinerama.h>
#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;
[ Direct Link to Message ] | |