mirror of https://github.com/fltk/fltk
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:
parent
1b52ead802
commit
b0a514366e
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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$".
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue