Android: added support for Fl::add_timeout() and friends.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12742 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Matthias Melcher 2018-03-12 20:10:49 +00:00
parent 1b52ead802
commit b0a514366e
7 changed files with 312 additions and 284 deletions

View File

@ -36,7 +36,7 @@ extern void *fl_gc;
/**
* These events are specific to the Android OS driver system. They can
* be read by adding a callback via Fl::add_system_handler() and reading Fl::event()
* Dont's change the order of these enums! See Fl_Android_Application.H .
* Don't change the order of these enums! See Fl_Android_Application.H .
*/
enum Fl_Android_Platform_Event
{

View File

@ -9,7 +9,8 @@ WARNING: FLTK FOR ANDROID IS WORK IN PROGRESS IN A PRETTY EARLY STAGE.
==========
1 Building FLTK with Android Studio 3
2 DOCUMENT HISTORY
2 Limitation of FLTK on Android
3 DOCUMENT HISTORY
BUILDING FLTK SAMPLE WITH ANDROID STUDIO 3
@ -31,9 +32,21 @@ device, you are ready to install FLTK.
- click "run"; the project should compile and run out of the box
Limitation of FLTK on Android
===============================
Android support for FLTK is in a very early stage. As of March 2018, very basic
rendering work and mouse clicks are detected.
- the screen size is currently always 600x800 and is scaled up
- there is no support for multiple windows (yet)
DOCUMENT HISTORY
==================
Mar 6 2018 - matt: moved project to ide/AndroidStudio3/
Mar 2 2018 - matt: rewriting Android port and documentation from scratch
Feb 9 2016 - matt: recreated document with more warnings and active support
Mar 12 2018 - matt: started list of limitation that serevs as information to the
user as much as a todo list for core developers
Mar 6 2018 - matt: moved project to ide/AndroidStudio3/
Mar 2 2018 - matt: rewriting Android port and documentation from scratch
Feb 9 2016 - matt: recreated document with more warnings and active support

View File

@ -23,7 +23,7 @@
Fl_Window *win;
Fl_Button *btn;
Fl_Button *btn, *btn2;
class MyButton : public Fl_Button
@ -37,12 +37,34 @@ public:
}
};
void bye_cb(void*)
{
btn2->color(FL_BLUE);
btn2->redraw();
Fl::remove_timeout(bye_cb, NULL);
}
void hello_cb(void*)
{
btn2->color(FL_GREEN);
btn2->redraw();
Fl::add_timeout(1.0, bye_cb, NULL);
Fl::remove_timeout(hello_cb, NULL);
}
void start_timer(Fl_Widget*, void*)
{
Fl::add_timeout(1.0, hello_cb, NULL);
}
int main(int argc, char **argv)
{
win = new Fl_Window(50, 150, 500, 400, "Hallo");
btn2 = new Fl_Button(10, 10, 50, 50, "-@circle;-");
btn2->color(FL_BLUE);
btn = new MyButton((win->w()-280)/2, 200, 280, 35, "Hello, Android!");
btn->color(FL_LIGHT2);
btn->callback(start_timer);
win->show(argc, argv);
Fl::run();

View File

@ -40,41 +40,28 @@ extern void (*fl_lock_function)();
#include <android/native_activity.h>
/**
* Static class that keeps often used data for global access.
* Static class that manages all interaction between the Android Native Activity
* and the FLTK library. It also keeps often used data for global access.
*
* This class holds global variables that are needed by the Android
* drivers.
*
* It also contains the interface to the Android Native Activity.
* On launch, it creates a main thread and communication pipe to
* the Activity. All FLTK code will run in that thread. Activity
* events can be polled from the Screen driver using the provided
* Android Looper, but must also be forwarded to this class.
*
* All other events are handled in Fl_Android_Screen_Driver
* events will be polled by the Screen driver using the provided
* Android Looper, and will also be routed back to this class as needed.
*
* This code is based on the native activity interface
* provided by <android/native_activity.h>. It is based on a set
* of application-provided callbacks that will be called
* by the Activity's main thread when certain events occur.
*
* This means that each one of this callbacks _should_ _not_ block, or they
* risk having the system force-close the application. This programming
* model is direct, lightweight, but constraining.
*
* The 'Fl_Android_Application' interface is used to provide a different
* execution model where the application can implement its own main event
* loop in a different thread instead. Here's how it works:
*
* 1/ The application must provide a function named "int main(argc, argv)" that
* will be called when the activity is created, in a new thread that is
* distinct from the activity's main thread.
*
* 2/ android_main() receives a pointer to a valid "android_app" structure
* 2/ The application has access to a static "Fl_Android_Application" class
* that contains references to other important objects, e.g. the
* ANativeActivity obejct instance the application is running in.
* ANativeActivity object instance the application is running in.
*
* 3/ the "android_app" object holds an ALooper instance that already
* 3/ the "Fl_Android_Application" class holds an ALooper instance that already
* listens to two important things:
*
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
@ -86,23 +73,8 @@ extern void (*fl_lock_function)();
* ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
* respectively.
*
* Your application can use the same ALooper to listen to additional
* file-descriptors. They can either be callback based, or with return
* identifiers starting with LOOPER_ID_USER.
*
* 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
* the returned data will point to an android_poll_source structure. You
* can call the process() function on it, and fill in android_app->onAppCmd
* and android_app->onInputEvent to be called for your own processing
* of the event.
*
* Alternatively, you can call the low-level functions to read and process
* the data directly... look at the process_cmd() and process_input()
* implementations in the glue to see how to do this.
*
* See the sample named "native-activity" that comes with the NDK with a
* full usage example. Also look at the JavaDoc of NativeActivity.
* FLTK will add more items to the looper for timers and file and socket
* communication. (fl_add_timeout, Fl::add_fd(), ...
*/
class Fl_Android_Application
{
@ -125,12 +97,18 @@ public:
* android_poll_source structure. These can be read via the inputQueue
* object of android_app.
*/
LOOPER_ID_INPUT = 2,
LOOPER_ID_INPUT,
/**
* Timer data ID of all timer events coming from the Unix timer_create()
* and friends, used in fl_add_timeout() and colleagues.
*/
LOOPER_ID_TIMER,
/**
* Start of user-defined ALooper identifiers.
*/
LOOPER_ID_USER = 3,
LOOPER_ID_USER,
};
/**
@ -155,67 +133,59 @@ public:
APP_CMD_DESTROY,
};
/**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*/
struct android_poll_source {
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
int32_t id;
// Function to call to perform the standard processing of data from
// this source.
void (*process)(struct android_poll_source* source);
};
public:
// --- logging
static void log_e(const char *text, ...);
static void log_w(const char *text, ...);
static void log_i(const char *text, ...);
static void log_v(const char *text, ...);
// --- application state stuff
static int8_t read_cmd();
static void pre_exec_cmd(int8_t cmd);
static void post_exec_cmd(int8_t cmd);
static int destroy_requested() { return pDestroyRequested; }
// --- event handling
static AInputQueue *input_event_queue() { return pInputQueue; }
// --- screen stuff
static bool copy_screen();
static inline ANativeWindow *native_window() { return pNativeWindow; }
static inline ANativeWindow_Buffer &graphics_buffer() { return pApplicationWindowBuffer; }
static int destroy_requested() { return pDestroyRequested; }
//static void set_on_app_cmd(void (*cmd)(int32_t cmd)) { pOnAppCmd = cmd; }
//static void set_on_input_event(int32_t (*cmd)(AInputEvent* event)) { pOnInputEvent = cmd; }
static bool copy_screen();
// --- timer stuff
static void send_timer_index(uint8_t ix);
static uint8_t receive_timer_index();
protected:
static void free_saved_state();
static void print_cur_config();
static void destroy();
static void process_input(struct android_poll_source* source);
static void process_cmd(struct android_poll_source* source);
static void* thread_entry(void* param);
// --- screen handling stuff
static void allocate_screen();
static bool lock_screen();
static void unlock_and_post_screen();
static bool screen_is_locked();
// --- timer stuff
static void create_timer_handler();
static void destroy_timer_handler();
static ANativeActivity *pActivity;
static AConfiguration *pConfig;
static void *pSavedState;
static size_t pSavedStateSize;
static ALooper *pMsgPipeLooper;
//static ALooper *pRedrawLooper;
static ALooper *pAppLooper;
static AInputQueue *pInputQueue;
static ANativeWindow *pNativeWindow;
static ANativeWindow_Buffer pNativeWindowBuffer;
static ANativeWindow_Buffer pApplicationWindowBuffer;
static int pActivityState;
static int pDestroyRequested;
static void (*pOnAppCmd)(int32_t cmd);
static int32_t (*pOnInputEvent)(AInputEvent* event);
// ---- no need to make these visible to the outside ----
static pthread_mutex_t pMutex;
@ -223,15 +193,16 @@ protected:
static int pMsgReadPipe;
static int pMsgWritePipe;
static pthread_t pThread;
static struct android_poll_source pCmdPollSource;
static struct android_poll_source pInputPollSource;
static int pRunning;
static int pStateSaved;
static int pDestroyed;
//static int pRedrawNeeded;
static AInputQueue* pPendingInputQueue;
static ANativeWindow* pPendingWindow;
//static ARect pPendingContentRect;
// --- timer variables
static int pTimerReadPipe;
static int pTimerWritePipe;
};

View File

@ -25,6 +25,8 @@
#include "Fl_Android_Application.H"
#include "Fl_Android_Window_Driver.H"
#include <FL/fl_draw.H>
#include <jni.h>
#include <errno.h>
@ -58,7 +60,7 @@ void* Fl_Android_Application::pSavedState = 0;
size_t Fl_Android_Application::pSavedStateSize = 0;
// The ALooper associated with the app's thread.
ALooper* Fl_Android_Application::pMsgPipeLooper = 0;
ALooper* Fl_Android_Application::pAppLooper = 0;
// When non-NULL, this is the input queue from which the app will
// receive user input events.
@ -79,22 +81,11 @@ int Fl_Android_Application::pActivityState = 0;
// destroyed and waiting for the app thread to complete.
int Fl_Android_Application::pDestroyRequested = 0;
// Fill this in with the function to process main app commands (APP_CMD_*)
void (*Fl_Android_Application::pOnAppCmd)(int32_t cmd) = 0;
// Fill this in with the function to process input events. At this point
// the event has already been pre-dispatched, and it will be finished upon
// return. Return 1 if you have handled the event, 0 for any default
// dispatching.
int32_t (*Fl_Android_Application::pOnInputEvent)(AInputEvent* event) = 0;
pthread_mutex_t Fl_Android_Application::pMutex = { 0 };
pthread_cond_t Fl_Android_Application::pCond = { 0 };
int Fl_Android_Application::pMsgReadPipe = 0;
int Fl_Android_Application::pMsgWritePipe = 0;
pthread_t Fl_Android_Application::pThread = 0;
struct Fl_Android_Application::android_poll_source Fl_Android_Application::pCmdPollSource = { 0 };
struct Fl_Android_Application::android_poll_source Fl_Android_Application::pInputPollSource = { 0 };
int Fl_Android_Application::pRunning = 0;
int Fl_Android_Application::pStateSaved = 0;
int Fl_Android_Application::pDestroyed = 0;
@ -103,6 +94,9 @@ AInputQueue *Fl_Android_Application::pPendingInputQueue = 0;
ANativeWindow *Fl_Android_Application::pPendingWindow = 0;
//ARect Fl_Android_Application::pPendingContentRect = { 0 };
int Fl_Android_Application::pTimerReadPipe = -1;
int Fl_Android_Application::pTimerWritePipe = -1;
void Fl_Android_Application::log_e(const char *text, ...)
@ -222,9 +216,7 @@ void Fl_Android_Application::pre_exec_cmd(int8_t cmd)
pInputQueue = pPendingInputQueue;
if (pInputQueue != NULL) {
log_v("Attaching input queue to looper");
AInputQueue_attachLooper(pInputQueue,
pMsgPipeLooper, LOOPER_ID_INPUT, NULL,
&pInputPollSource);
AInputQueue_attachLooper(pInputQueue, pAppLooper, LOOPER_ID_INPUT, NULL, NULL);
}
pthread_cond_broadcast(&pCond);
pthread_mutex_unlock(&pMutex);
@ -312,6 +304,43 @@ void Fl_Android_Application::post_exec_cmd(int8_t cmd)
}
void Fl_Android_Application::create_timer_handler()
{
int msgpipe[2];
if (::pipe(msgpipe)) {
log_e("could not create timer pipe: %s", strerror(errno));
return;
}
pTimerReadPipe = msgpipe[0];
pTimerWritePipe = msgpipe[1];
ALooper_addFd(pAppLooper, pTimerReadPipe, LOOPER_ID_TIMER, ALOOPER_EVENT_INPUT, NULL, NULL);
}
void Fl_Android_Application::destroy_timer_handler()
{
ALooper_removeFd(pAppLooper, pTimerReadPipe);
::close(pTimerWritePipe);
::close(pTimerReadPipe);
}
void Fl_Android_Application::send_timer_index(uint8_t ix)
{
if (pTimerWritePipe!=-1)
::write(pTimerWritePipe, &ix, 1);
}
uint8_t Fl_Android_Application::receive_timer_index()
{
uint8_t ix = 0;
if (pTimerReadPipe!=-1)
::read(pTimerReadPipe, &ix, 1);
return ix;
}
void Fl_Android_Application::destroy()
{
log_v("android_app_destroy!");
@ -320,6 +349,7 @@ void Fl_Android_Application::destroy()
if (pInputQueue != NULL) {
AInputQueue_detachLooper(pInputQueue);
}
destroy_timer_handler();
AConfiguration_delete(pConfig);
pDestroyed = 1;
pthread_cond_broadcast(&pCond);
@ -328,29 +358,6 @@ void Fl_Android_Application::destroy()
}
void Fl_Android_Application::process_input(struct android_poll_source* source)
{
AInputEvent* event = NULL;
while (AInputQueue_getEvent(pInputQueue, &event) >= 0) {
if (AInputQueue_preDispatchEvent(pInputQueue, event)) {
continue;
}
int32_t handled = 0;
if (pOnInputEvent != NULL) handled = pOnInputEvent(event);
AInputQueue_finishEvent(pInputQueue, event, handled);
}
}
void Fl_Android_Application::process_cmd(struct android_poll_source* source)
{
int8_t cmd = read_cmd();
pre_exec_cmd(cmd);
if (pOnAppCmd != NULL) pOnAppCmd(cmd);
post_exec_cmd(cmd);
}
void *Fl_Android_Application::thread_entry(void* param)
{
pConfig = AConfiguration_new();
@ -358,15 +365,10 @@ void *Fl_Android_Application::thread_entry(void* param)
print_cur_config();
pCmdPollSource.id = LOOPER_ID_MAIN;
pCmdPollSource.process = process_cmd;
pInputPollSource.id = LOOPER_ID_INPUT;
pInputPollSource.process = process_input;
pAppLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(pAppLooper, pMsgReadPipe, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, NULL);
ALooper *looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, pMsgReadPipe, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&pCmdPollSource);
pMsgPipeLooper = looper;
create_timer_handler();
pthread_mutex_lock(&pMutex);
pRunning = 1;
@ -395,8 +397,6 @@ void Fl_Android_Application::allocate_screen() {
}
#include <FL/fl_draw.H>
bool Fl_Android_Application::copy_screen()
{
bool ret = false;

View File

@ -45,26 +45,14 @@ private:
int handle_keyboard_event(AInputEvent*);
int handle_mouse_event(AInputEvent*);
#if 0
protected:
RECT screens[MAX_SCREENS];
RECT work_area[MAX_SCREENS];
float dpi[MAX_SCREENS][2];
float scale_of_screen[MAX_SCREENS];
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM);
BOOL screen_cb(HMONITOR mon, HDC, LPRECT r);
int get_mouse_unscaled(int &mx, int &my);
#ifdef FLTK_HIDPI_SUPPORT
void init_screen_scale_factors();
#endif
#endif
public:
Fl_Android_Screen_Driver() : Fl_Screen_Driver(), pScreenContentChanged(false) { }
void add_timeout(double time, Fl_Timeout_Handler cb, void *argp);
void repeat_timeout(double time, Fl_Timeout_Handler cb, void *argp);
int has_timeout(Fl_Timeout_Handler cb, void *argp);
void remove_timeout(Fl_Timeout_Handler cb, void *argp);
#if 0
Fl_WinAPI_Screen_Driver() : Fl_Screen_Driver() {
for (int i = 0; i < MAX_SCREENS; i++) scale_of_screen[i] = 1;
@ -94,10 +82,6 @@ public:
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 Fl_RGB_Image *read_win_rectangle(int X, int Y, int w, int h);
@ -122,6 +106,22 @@ public:
#endif
virtual float desktop_scale_factor();
#endif
#if 0
protected:
RECT screens[MAX_SCREENS];
RECT work_area[MAX_SCREENS];
float dpi[MAX_SCREENS][2];
float scale_of_screen[MAX_SCREENS];
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM);
BOOL screen_cb(HMONITOR mon, HDC, LPRECT r);
int get_mouse_unscaled(int &mx, int &my);
#ifdef FLTK_HIDPI_SUPPORT
void init_screen_scale_factors();
#endif
#endif
bool pScreenContentChanged;

View File

@ -27,12 +27,17 @@
#include <FL/Fl_RGB_Image.H>
#include <FL/fl_ask.H>
#include <stdio.h>
#include <errno.h>
#include <math.h>
static void nothing() {}
void (*fl_unlock_function)() = nothing;
void (*fl_lock_function)() = nothing;
static void timer_do_callback(int timerIndex);
#if 0
// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx
@ -95,7 +100,7 @@ int Fl_Android_Screen_Driver::handle_input_event()
consumed = handle_mouse_event(event);
break;
default:
// don;t do anything. There may be additional event types in the future
// don't do anything. There may be additional event types in the future
break;
}
// TODO: handle all events here
@ -178,6 +183,9 @@ int Fl_Android_Screen_Driver::handle_queued_events(double time_to_wait)
case Fl_Android_Application::LOOPER_ID_INPUT:
ret = handle_input_event();
break;
case Fl_Android_Application::LOOPER_ID_TIMER:
timer_do_callback(Fl_Android_Application::receive_timer_index());
break;
case -3: return ret;
default: return ret;
}
@ -186,6 +194,14 @@ int Fl_Android_Screen_Driver::handle_queued_events(double time_to_wait)
}
// TODO: we need a timout: nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);
// static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) return secs*1000000000;
// int timer_create(clockid_t __clock, struct sigevent* __event, timer_t* __timer_ptr);
// int timer_delete(timer_t __timer);
// int timer_settime(timer_t __timer, int __flags, const struct itimerspec* __new_value, struct itimerspec* __old_value);
// int timer_gettime(timer_t __timer, struct itimerspec* __ts);
// int timer_getoverrun(timer_t __timer);
double Fl_Android_Screen_Driver::wait(double time_to_wait)
{
Fl::run_checks();
@ -514,148 +530,8 @@ const char *Fl_WinAPI_Screen_Driver::get_system_scheme()
}
// ---- 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];
@ -832,6 +708,152 @@ int Fl_WinAPI_Screen_Driver::screen_num_unscaled(int x, int y)
#endif
// ---- timers
struct TimerData
{
timer_t handle;
struct sigevent sigevent;
Fl_Timeout_Handler callback;
void *data;
bool used;
bool triggered;
};
static TimerData* timerData = 0L;
static int NTimerData = 0;
static int nTimerData = 0;
static int allocate_more_timers()
{
if (NTimerData == 0) {
NTimerData = 8;
}
if (NTimerData>256) { // out of timers
return -1;
}
NTimerData *= 2;
timerData = (TimerData*)realloc(timerData, sizeof(TimerData) * NTimerData);
return nTimerData;
}
static void timer_signal_handler(union sigval data)
{
int timerIndex = data.sival_int;
Fl_Android_Application::send_timer_index(timerIndex);
}
static void timer_do_callback(int timerIndex)
{
TimerData& t = timerData[timerIndex];
t.triggered = false;
if (t.callback) {
t.callback(t.data);
// TODO: should we release the timer at this point?
}
}
void Fl_Android_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void *data)
{
repeat_timeout(time, cb, data);
}
void Fl_Android_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void *data)
{
int ret = -1;
int timerIndex = -1;
// first, find the timer associated with this handler
for (int i = 0; i < nTimerData; ++i) {
TimerData& t = timerData[i];
if ( (t.used) && (t.callback==cb) && (t.data==data) ) {
timerIndex = i;
break;
}
}
// if we did not have a timer yet, find a free slot
if (timerIndex==-1) {
for (int i = 0; i < nTimerData; ++i) {
if (!timerData[i].used)
timerIndex = i;
break;
}
}
// if that didn't work, allocate more timers
if (timerIndex==-1) {
if (nTimerData==NTimerData)
allocate_more_timers();
timerIndex = nTimerData++;
}
// if that didn;t work either, we ran out of timers
if (timerIndex==-1) {
Fl::error("FLTK ran out of timer slots.");
return;
}
TimerData& t = timerData[timerIndex];
if (!t.used) {
t.data = data;
t.callback = cb;
memset(&t.sigevent, 0, sizeof(struct sigevent));
t.sigevent.sigev_notify = SIGEV_THREAD;
t.sigevent.sigev_notify_function = timer_signal_handler;
t.sigevent.sigev_value.sival_int = timerIndex;
ret = timer_create(CLOCK_MONOTONIC, &t.sigevent, &t.handle);
if (ret==-1) {
Fl_Android_Application::log_e("Can't create timer: %s", strerror(errno));
return;
}
t.used = true;
}
double ff;
struct itimerspec timeout = {
{ 0, 0 },
{ (time_t)floor(time), (long)(modf(time, &ff)*1000000000) }
};
ret = timer_settime(t.handle, 0, &timeout, 0L);
if (ret==-1) {
Fl_Android_Application::log_e("Can't launch timer: %s", strerror(errno));
return;
}
t.triggered = true;
}
int Fl_Android_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void *data)
{
for (int i = 0; i < nTimerData; ++i) {
TimerData& t = timerData[i];
if ( (t.used) && (t.callback==cb) && (t.data==data) ) {
return 1;
}
}
return 0;
}
void Fl_Android_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void *data)
{
for (int i = 0; i < nTimerData; ++i) {
TimerData& t = timerData[i];
if ( t.used && (t.callback==cb) && ( (t.data==data) || (data==NULL) ) ) {
if (t.used)
timer_delete(t.handle);
t.triggered = t.used = false;
}
}
}
//
// End of "$Id$".
//