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
This commit is contained in:
Albrecht Schlosser 2022-01-31 22:27:17 +01:00 committed by GitHub
parent cf4a832e6a
commit 29d9e31c51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 719 additions and 500 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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.

456
src/Fl_Timeout.cxx Normal file
View File

@ -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

156
src/Fl_Timeout.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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 \

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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) ;

View File

@ -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();

View File

@ -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;