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:
parent
cf4a832e6a
commit
29d9e31c51
3
FL/Fl.H
3
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
|
||||
|
@ -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
|
||||
|
74
src/Fl.cxx
74
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);
|
||||
}
|
||||
|
||||
|
@ -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
456
src/Fl_Timeout.cxx
Normal 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
156
src/Fl_Timeout.h
Normal 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_
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) ;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user