From b0a514366eed9b5ef4d7f99768af0d7e89755728 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Mon, 12 Mar 2018 20:10:49 +0000 Subject: [PATCH] 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 --- FL/android.H | 2 +- README.Android.txt | 21 +- .../app/src/main/cpp/HelloAndroid.cxx | 24 +- src/drivers/Android/Fl_Android_Application.H | 107 +++--- .../Android/Fl_Android_Application.cxx | 96 +++--- .../Android/Fl_Android_Screen_Driver.H | 42 +-- .../Android/Fl_Android_Screen_Driver.cxx | 304 ++++++++++-------- 7 files changed, 312 insertions(+), 284 deletions(-) diff --git a/FL/android.H b/FL/android.H index c6da1e3c9..262ab82b7 100644 --- a/FL/android.H +++ b/FL/android.H @@ -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 { diff --git a/README.Android.txt b/README.Android.txt index e31811766..58c817e48 100644 --- a/README.Android.txt +++ b/README.Android.txt @@ -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 diff --git a/ide/AndroidStudio3/app/src/main/cpp/HelloAndroid.cxx b/ide/AndroidStudio3/app/src/main/cpp/HelloAndroid.cxx index 02b563517..f22728c33 100644 --- a/ide/AndroidStudio3/app/src/main/cpp/HelloAndroid.cxx +++ b/ide/AndroidStudio3/app/src/main/cpp/HelloAndroid.cxx @@ -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(); diff --git a/src/drivers/Android/Fl_Android_Application.H b/src/drivers/Android/Fl_Android_Application.H index dbc0a4dd9..8489f495c 100644 --- a/src/drivers/Android/Fl_Android_Application.H +++ b/src/drivers/Android/Fl_Android_Application.H @@ -40,41 +40,28 @@ extern void (*fl_lock_function)(); #include /** - * 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 . 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; + }; diff --git a/src/drivers/Android/Fl_Android_Application.cxx b/src/drivers/Android/Fl_Android_Application.cxx index 0d3181b78..ed8be53eb 100644 --- a/src/drivers/Android/Fl_Android_Application.cxx +++ b/src/drivers/Android/Fl_Android_Application.cxx @@ -25,6 +25,8 @@ #include "Fl_Android_Application.H" #include "Fl_Android_Window_Driver.H" +#include + #include #include @@ -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 - bool Fl_Android_Application::copy_screen() { bool ret = false; diff --git a/src/drivers/Android/Fl_Android_Screen_Driver.H b/src/drivers/Android/Fl_Android_Screen_Driver.H index 32ae04899..a63941783 100644 --- a/src/drivers/Android/Fl_Android_Screen_Driver.H +++ b/src/drivers/Android/Fl_Android_Screen_Driver.H @@ -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; diff --git a/src/drivers/Android/Fl_Android_Screen_Driver.cxx b/src/drivers/Android/Fl_Android_Screen_Driver.cxx index 810ccedbf..545d8b979 100644 --- a/src/drivers/Android/Fl_Android_Screen_Driver.cxx +++ b/src/drivers/Android/Fl_Android_Screen_Driver.cxx @@ -27,12 +27,17 @@ #include #include #include +#include +#include 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$". //