2018-03-02 16:34:43 +00:00
|
|
|
//
|
2018-03-03 14:37:53 +00:00
|
|
|
// Android screen interface for the Fast Light Tool Kit (FLTK).
|
2018-03-02 16:34:43 +00:00
|
|
|
//
|
|
|
|
|
// Copyright 1998-2018 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:
|
|
|
|
|
//
|
2020-07-01 16:03:10 +00:00
|
|
|
// https://www.fltk.org/COPYING.php
|
2018-03-02 16:34:43 +00:00
|
|
|
//
|
2020-07-01 16:03:10 +00:00
|
|
|
// Please see the following page on how to report bugs and issues:
|
2018-03-02 16:34:43 +00:00
|
|
|
//
|
2020-07-01 16:03:10 +00:00
|
|
|
// https://www.fltk.org/bugs.php
|
2018-03-02 16:34:43 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
2018-06-23 10:46:28 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
@cond AndroidDev
|
|
|
|
|
\defgroup AndroidDeveloper Android Developer Documentation
|
|
|
|
|
\{
|
2018-06-23 10:46:28 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2018-03-02 16:34:43 +00:00
|
|
|
#include "../../config_lib.h"
|
|
|
|
|
#include "Fl_Android_Screen_Driver.H"
|
2018-03-05 21:26:51 +00:00
|
|
|
#include "Fl_Android_Application.H"
|
2018-03-16 20:27:12 +00:00
|
|
|
#include "Fl_Android_Graphics_Font.H"
|
2018-03-02 16:34:43 +00:00
|
|
|
#include <FL/Fl.H>
|
|
|
|
|
#include <FL/platform.H>
|
|
|
|
|
#include <FL/Fl_Graphics_Driver.H>
|
|
|
|
|
#include <FL/Fl_RGB_Image.H>
|
|
|
|
|
#include <FL/fl_ask.H>
|
|
|
|
|
#include <stdio.h>
|
2018-03-12 20:10:49 +00:00
|
|
|
#include <errno.h>
|
|
|
|
|
#include <math.h>
|
2018-03-02 16:34:43 +00:00
|
|
|
|
2018-03-03 14:05:18 +00:00
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
\class Fl_Android_Screen_Driver
|
|
|
|
|
|
|
|
|
|
Handle Android screen devices.
|
|
|
|
|
|
|
|
|
|
\todo This class is in an early development stage
|
2018-06-23 10:06:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2018-03-03 14:05:18 +00:00
|
|
|
static void nothing() {}
|
|
|
|
|
void (*fl_unlock_function)() = nothing;
|
|
|
|
|
void (*fl_lock_function)() = nothing;
|
|
|
|
|
|
2018-03-12 20:10:49 +00:00
|
|
|
static void timer_do_callback(int timerIndex);
|
|
|
|
|
|
|
|
|
|
|
2018-06-23 10:18:58 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Creates a driver that manages all Android screen and display related calls.
|
2018-03-02 16:34:43 +00:00
|
|
|
*/
|
|
|
|
|
Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver()
|
|
|
|
|
{
|
|
|
|
|
return new Fl_Android_Screen_Driver();
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-07 23:01:50 +00:00
|
|
|
|
|
|
|
|
extern int fl_send_system_handlers(void *e);
|
|
|
|
|
|
2018-03-25 16:48:44 +00:00
|
|
|
|
2018-06-19 16:29:20 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Create the screen driver.
|
2018-06-19 16:29:20 +00:00
|
|
|
*/
|
|
|
|
|
Fl_Android_Screen_Driver::Fl_Android_Screen_Driver() :
|
|
|
|
|
super(),
|
|
|
|
|
pContentChanged(false),
|
|
|
|
|
pClearDesktop(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-06-23 10:18:58 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Call the FLTK System handler with Android specific events.
|
|
|
|
|
|
|
|
|
|
\return always 1, assuming the event was handled
|
|
|
|
|
|
|
|
|
|
\see Fl_Android_Platform_Event
|
2018-06-23 10:18:58 +00:00
|
|
|
*/
|
2018-03-07 22:12:34 +00:00
|
|
|
int Fl_Android_Screen_Driver::handle_app_command()
|
2018-03-03 14:05:18 +00:00
|
|
|
{
|
2018-03-07 23:01:50 +00:00
|
|
|
// get the command
|
2018-03-07 22:12:34 +00:00
|
|
|
int8_t cmd = Fl_Android_Application::read_cmd();
|
2018-03-07 23:01:50 +00:00
|
|
|
|
|
|
|
|
// setup the Android glue and prepare all settings for calling into FLTK
|
2018-03-07 22:12:34 +00:00
|
|
|
Fl_Android_Application::pre_exec_cmd(cmd);
|
2018-03-07 23:01:50 +00:00
|
|
|
|
|
|
|
|
// call all registered FLTK system handlers
|
2018-03-07 23:06:55 +00:00
|
|
|
Fl::e_number = ((uint32_t)(cmd-Fl_Android_Application::APP_CMD_INPUT_CHANGED)) + FL_ANDROID_EVENT_INPUT_CHANGED;
|
2018-03-18 14:58:25 +00:00
|
|
|
fl_send_system_handlers(nullptr);
|
2018-03-07 23:01:50 +00:00
|
|
|
|
|
|
|
|
// fixup and finalize application wide command handling
|
2018-03-07 22:12:34 +00:00
|
|
|
Fl_Android_Application::post_exec_cmd(cmd);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2018-03-03 14:05:18 +00:00
|
|
|
|
2018-03-25 16:48:44 +00:00
|
|
|
|
2018-03-07 22:12:34 +00:00
|
|
|
int Fl_Android_Screen_Driver::handle_input_event()
|
|
|
|
|
{
|
|
|
|
|
AInputQueue *queue = Fl_Android_Application::input_event_queue();
|
2018-03-18 14:58:25 +00:00
|
|
|
AInputEvent *event = nullptr;
|
2018-03-07 22:12:34 +00:00
|
|
|
|
|
|
|
|
if (AInputQueue_getEvent(queue, &event) >= 0) {
|
|
|
|
|
if (AInputQueue_preDispatchEvent(queue, event)==0) {
|
|
|
|
|
int consumed = 0;
|
|
|
|
|
switch (AInputEvent_getType(event)) {
|
|
|
|
|
case AINPUT_EVENT_TYPE_KEY:
|
2018-03-24 17:08:25 +00:00
|
|
|
consumed = handle_keyboard_event(queue, event);
|
2018-03-07 22:12:34 +00:00
|
|
|
break;
|
|
|
|
|
case AINPUT_EVENT_TYPE_MOTION:
|
2018-03-24 13:06:59 +00:00
|
|
|
consumed = handle_mouse_event(queue, event);
|
2018-03-07 22:12:34 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2018-03-12 20:10:49 +00:00
|
|
|
// don't do anything. There may be additional event types in the future
|
2018-03-24 13:06:59 +00:00
|
|
|
AInputQueue_finishEvent(queue, event, consumed);
|
2018-03-07 22:12:34 +00:00
|
|
|
break;
|
2018-03-03 14:05:18 +00:00
|
|
|
}
|
2018-03-07 22:12:34 +00:00
|
|
|
// TODO: handle all events here
|
2018-03-24 13:06:59 +00:00
|
|
|
// AInputQueue_finishEvent(queue, event, consumed);
|
2018-03-03 14:05:18 +00:00
|
|
|
}
|
2018-03-07 22:12:34 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-03-03 14:05:18 +00:00
|
|
|
|
2018-03-24 17:08:25 +00:00
|
|
|
|
2018-03-24 13:06:59 +00:00
|
|
|
int Fl_Android_Screen_Driver::handle_mouse_event(AInputQueue *queue, AInputEvent *event)
|
2018-03-07 22:12:34 +00:00
|
|
|
{
|
2018-03-11 22:00:59 +00:00
|
|
|
int ex = Fl::e_x_root = (int)(AMotionEvent_getX(event, 0) * 600 /
|
|
|
|
|
ANativeWindow_getWidth(Fl_Android_Application::native_window()));
|
|
|
|
|
int ey = Fl::e_y_root = (int)(AMotionEvent_getY(event, 0) * 800 /
|
|
|
|
|
ANativeWindow_getHeight(Fl_Android_Application::native_window()));
|
|
|
|
|
|
|
|
|
|
// FIXME: find the window in which the event happened
|
2018-03-18 13:44:37 +00:00
|
|
|
Fl_Window *win = Fl::grab();
|
|
|
|
|
if (!win) {
|
|
|
|
|
win = Fl::first_window();
|
2018-03-24 13:06:59 +00:00
|
|
|
if (win && !win->modal()) {
|
|
|
|
|
while (win) {
|
|
|
|
|
if (ex >= win->x() && ex < win->x() + win->w() && ey >= win->y() &&
|
|
|
|
|
ey < win->y() + win->h())
|
|
|
|
|
break;
|
|
|
|
|
win = Fl::next_window(win);
|
|
|
|
|
}
|
2018-03-18 13:44:37 +00:00
|
|
|
}
|
2018-03-11 22:00:59 +00:00
|
|
|
}
|
2018-03-24 13:06:59 +00:00
|
|
|
if (!win) {
|
|
|
|
|
AInputQueue_finishEvent(queue, event, 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-03-11 22:00:59 +00:00
|
|
|
|
|
|
|
|
if (win) {
|
|
|
|
|
Fl::e_x = ex-win->x();
|
|
|
|
|
Fl::e_y = ey-win->y();
|
|
|
|
|
} else {
|
|
|
|
|
Fl::e_x = ex;
|
|
|
|
|
Fl::e_y = ey;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-07 22:12:34 +00:00
|
|
|
Fl::e_state = FL_BUTTON1;
|
|
|
|
|
Fl::e_keysym = FL_Button + 1;
|
|
|
|
|
if (AMotionEvent_getAction(event) == AMOTION_EVENT_ACTION_DOWN) {
|
2018-03-24 13:06:59 +00:00
|
|
|
AInputQueue_finishEvent(queue, event, 1);
|
2018-03-07 22:12:34 +00:00
|
|
|
Fl::e_is_click = 1;
|
2018-06-23 10:06:04 +00:00
|
|
|
// Fl_Android_Application::log_i("Mouse push %x %d at %d, %d", win, Fl::event_button(), Fl::event_x(), Fl::event_y());
|
2018-03-18 13:44:37 +00:00
|
|
|
if (win) Fl::handle(FL_PUSH, win); // do NOT send a push event into the "Desktop"
|
2018-03-07 22:12:34 +00:00
|
|
|
} else if (AMotionEvent_getAction(event) == AMOTION_EVENT_ACTION_MOVE) {
|
2018-03-24 13:06:59 +00:00
|
|
|
AInputQueue_finishEvent(queue, event, 1);
|
2018-06-23 10:06:04 +00:00
|
|
|
// Fl_Android_Application::log_i("Mouse drag %x %d at %d, %d", win, Fl::event_button(), Fl::event_x(), Fl::event_y());
|
2018-06-21 18:14:13 +00:00
|
|
|
if (win) Fl::handle(FL_DRAG, win);
|
2018-03-07 22:12:34 +00:00
|
|
|
} else if (AMotionEvent_getAction(event) == AMOTION_EVENT_ACTION_UP) {
|
2018-03-24 13:06:59 +00:00
|
|
|
AInputQueue_finishEvent(queue, event, 1);
|
2018-03-07 22:12:34 +00:00
|
|
|
Fl::e_state = 0;
|
2018-06-23 10:06:04 +00:00
|
|
|
// Fl_Android_Application::log_i("Mouse release %x %d at %d, %d", win, Fl::event_button(), Fl::event_x(), Fl::event_y());
|
2018-06-21 18:14:13 +00:00
|
|
|
if (win) Fl::handle(FL_RELEASE, win);
|
2018-03-24 13:06:59 +00:00
|
|
|
} else {
|
|
|
|
|
AInputQueue_finishEvent(queue, event, 0);
|
2018-03-07 22:12:34 +00:00
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:18:58 +00:00
|
|
|
|
2018-03-07 22:12:34 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Handle all events in the even queue.
|
|
|
|
|
|
|
|
|
|
\todo what should this function return?
|
|
|
|
|
|
|
|
|
|
\param time_to_wait
|
|
|
|
|
\return we do not know
|
2018-03-07 22:12:34 +00:00
|
|
|
*/
|
|
|
|
|
int Fl_Android_Screen_Driver::handle_queued_events(double time_to_wait)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
// Read all pending events.
|
|
|
|
|
int ident;
|
|
|
|
|
int events;
|
2018-03-24 17:08:25 +00:00
|
|
|
int delay_millis = time_to_wait*1000;
|
|
|
|
|
bool done = false;
|
|
|
|
|
|
2018-03-25 16:34:40 +00:00
|
|
|
int delay = Fl::damage() ? 0 : delay_millis;
|
|
|
|
|
while (!done) {
|
2018-03-24 17:08:25 +00:00
|
|
|
ident = ALooper_pollOnce(delay, nullptr, &events, nullptr);
|
|
|
|
|
switch (ident) {
|
|
|
|
|
case Fl_Android_Application::LOOPER_ID_MAIN:
|
|
|
|
|
ret = handle_app_command();
|
|
|
|
|
break;
|
|
|
|
|
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 ALOOPER_POLL_WAKE:
|
2018-03-25 16:34:40 +00:00
|
|
|
Fl_Android_Application::log_e("Someone woke up ALooper_pollOnce.");
|
2018-03-24 17:08:25 +00:00
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
case ALOOPER_POLL_CALLBACK:
|
|
|
|
|
Fl_Android_Application::log_e(
|
2018-03-25 16:34:40 +00:00
|
|
|
"Someone added a callback to ALooper_pollOnce.");
|
2018-03-24 17:08:25 +00:00
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
case ALOOPER_POLL_TIMEOUT:
|
2018-03-25 16:34:40 +00:00
|
|
|
done = true; // timer expired, return to FLTK
|
2018-03-24 17:08:25 +00:00
|
|
|
break;
|
|
|
|
|
case ALOOPER_POLL_ERROR:
|
|
|
|
|
Fl_Android_Application::log_e(
|
2018-03-25 16:34:40 +00:00
|
|
|
"Something caused an ERROR in ALooper_pollOnce.");
|
|
|
|
|
done = true; // return to the app to find the error
|
2018-03-24 17:08:25 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Fl_Android_Application::log_e(
|
2018-03-25 16:34:40 +00:00
|
|
|
"Unknown return value from ALooper_pollOnce.");
|
|
|
|
|
done = true; // return to the app, just in case
|
2018-03-24 17:08:25 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2018-03-25 16:34:40 +00:00
|
|
|
// we need to repeat this as long as there are messages in the queue, or any
|
|
|
|
|
// change in the graphical interface will trigger a redraw immediately. To
|
|
|
|
|
// save time and energy, we want to collect graphics changes and execute
|
|
|
|
|
// them as soon as no more events are pending.
|
|
|
|
|
// Setting delay to zero on the second round makes sure that all events
|
|
|
|
|
// are handled first, and the call returns only when no more
|
|
|
|
|
// events are pending.
|
|
|
|
|
delay = 0;
|
|
|
|
|
}
|
2018-03-07 22:12:34 +00:00
|
|
|
return ret;
|
2018-03-03 14:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:18:58 +00:00
|
|
|
|
2018-03-24 13:06:59 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Wait for a maximum of `time_to_wait` until something happens.
|
|
|
|
|
|
|
|
|
|
\param time_to_wait in seconds
|
|
|
|
|
\return We really do not know; check other platforms to see what is
|
|
|
|
|
consistent here.
|
|
|
|
|
|
|
|
|
|
\todo return the remaining time to reach 'time_to_wait'
|
2018-03-24 13:06:59 +00:00
|
|
|
*/
|
2018-03-03 14:05:18 +00:00
|
|
|
double Fl_Android_Screen_Driver::wait(double time_to_wait)
|
|
|
|
|
{
|
|
|
|
|
Fl::run_checks();
|
|
|
|
|
static int in_idle = 0;
|
|
|
|
|
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 (time_to_wait==0.0) {
|
|
|
|
|
// if there is no wait time, handle the event and show the results right away
|
|
|
|
|
fl_unlock_function();
|
|
|
|
|
handle_queued_events(time_to_wait);
|
|
|
|
|
fl_lock_function();
|
2018-06-19 16:29:20 +00:00
|
|
|
// FIXME: kludge to erase a window after it was hidden
|
|
|
|
|
if (pClearDesktop && fl_graphics_driver) {
|
|
|
|
|
((Fl_Android_Graphics_Driver*)fl_graphics_driver)->make_current(nullptr);
|
|
|
|
|
fl_rectf(0, 0, 600, 800, FL_BLACK);
|
|
|
|
|
pClearDesktop = false;
|
|
|
|
|
pContentChanged = true;
|
|
|
|
|
}
|
2018-03-03 14:05:18 +00:00
|
|
|
Fl::flush();
|
|
|
|
|
} else {
|
|
|
|
|
// if there is wait time, show the pending changes and then handle the events
|
2018-06-19 16:29:20 +00:00
|
|
|
// FIXME: kludge to erase a window after it was hidden
|
|
|
|
|
if (pClearDesktop && fl_graphics_driver) {
|
|
|
|
|
((Fl_Android_Graphics_Driver*)fl_graphics_driver)->make_current(nullptr);
|
|
|
|
|
fl_rectf(0, 0, 600, 800, FL_BLACK);
|
|
|
|
|
pClearDesktop = false;
|
|
|
|
|
pContentChanged = true;
|
|
|
|
|
}
|
2018-03-03 14:05:18 +00:00
|
|
|
Fl::flush();
|
|
|
|
|
if (Fl::idle && !in_idle) // 'idle' may have been set within flush()
|
|
|
|
|
time_to_wait = 0.0;
|
|
|
|
|
fl_unlock_function();
|
|
|
|
|
handle_queued_events(time_to_wait);
|
|
|
|
|
fl_lock_function();
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-24 13:06:59 +00:00
|
|
|
return 0.0;
|
2018-03-03 14:05:18 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
|
2018-03-05 21:26:51 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
On Android, we currently write into a memory buffer and copy
|
|
|
|
|
the content to the screen.
|
|
|
|
|
|
|
|
|
|
\see fl_flush()
|
2018-03-05 21:26:51 +00:00
|
|
|
*/
|
|
|
|
|
void Fl_Android_Screen_Driver::flush()
|
2018-03-02 16:34:43 +00:00
|
|
|
{
|
2018-03-05 22:57:33 +00:00
|
|
|
Fl_Screen_Driver::flush();
|
2018-03-07 16:29:31 +00:00
|
|
|
// FIXME: do this only if anything actually changed on screen (need to optimize)!
|
2018-06-19 16:29:20 +00:00
|
|
|
if (pContentChanged) {
|
2018-03-07 16:29:31 +00:00
|
|
|
if (Fl_Android_Application::copy_screen())
|
2018-06-19 16:29:20 +00:00
|
|
|
pContentChanged = false;
|
2018-03-05 21:26:51 +00:00
|
|
|
}
|
2018-03-02 16:34:43 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
|
2018-03-24 21:36:55 +00:00
|
|
|
// ---- timers -----------------------------------------------------------------
|
2018-03-12 20:10:49 +00:00
|
|
|
|
2018-03-25 16:48:44 +00:00
|
|
|
|
2018-03-12 20:10:49 +00:00
|
|
|
struct TimerData
|
|
|
|
|
{
|
|
|
|
|
timer_t handle;
|
|
|
|
|
struct sigevent sigevent;
|
|
|
|
|
Fl_Timeout_Handler callback;
|
|
|
|
|
void *data;
|
|
|
|
|
bool used;
|
|
|
|
|
bool triggered;
|
2018-03-18 13:44:37 +00:00
|
|
|
struct itimerspec timeout;
|
2018-03-12 20:10:49 +00:00
|
|
|
};
|
2018-03-18 14:58:25 +00:00
|
|
|
static TimerData* timerData = nullptr;
|
2018-03-12 20:10:49 +00:00
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
// if that didn't work either, we ran out of timers
|
2018-03-12 20:10:49 +00:00
|
|
|
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;
|
2018-03-18 13:44:37 +00:00
|
|
|
t.timeout = {
|
2018-03-12 20:10:49 +00:00
|
|
|
{ 0, 0 },
|
|
|
|
|
{ (time_t)floor(time), (long)(modf(time, &ff)*1000000000) }
|
|
|
|
|
};
|
2018-03-18 14:58:25 +00:00
|
|
|
ret = timer_settime(t.handle, 0, &t.timeout, nullptr);
|
2018-03-12 20:10:49 +00:00
|
|
|
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];
|
2018-03-18 14:58:25 +00:00
|
|
|
if ( t.used && (t.callback==cb) && ( (t.data==data) || (data==nullptr) ) ) {
|
2018-03-12 20:10:49 +00:00
|
|
|
if (t.used)
|
|
|
|
|
timer_delete(t.handle);
|
|
|
|
|
t.triggered = t.used = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Play some system sound.
|
|
|
|
|
|
|
|
|
|
This function plays some rather arbitrary system sounds.
|
|
|
|
|
|
|
|
|
|
\param type
|
|
|
|
|
|
|
|
|
|
\see Fl_Screen_Driver::beep(int)
|
|
|
|
|
\see fl_beep(int)
|
2018-06-23 10:06:04 +00:00
|
|
|
*/
|
2018-06-21 12:36:02 +00:00
|
|
|
void Fl_Android_Screen_Driver::beep(int type)
|
|
|
|
|
{
|
2018-06-23 10:06:04 +00:00
|
|
|
int androidSoundID = 93; // default to TONE_CDMA_ALERT_CALL_GUARD
|
2018-06-21 12:36:02 +00:00
|
|
|
switch (type) {
|
2018-06-23 10:06:04 +00:00
|
|
|
case FL_BEEP_DEFAULT: androidSoundID = 92; break;
|
|
|
|
|
case FL_BEEP_MESSAGE: androidSoundID = 86; break;
|
|
|
|
|
case FL_BEEP_ERROR: androidSoundID = 87; break;
|
|
|
|
|
case FL_BEEP_QUESTION: androidSoundID = 91; break;
|
|
|
|
|
case FL_BEEP_PASSWORD: androidSoundID = 95; break;
|
|
|
|
|
case FL_BEEP_NOTIFICATION: androidSoundID = 93; break;
|
2018-06-21 12:36:02 +00:00
|
|
|
}
|
|
|
|
|
Fl_Android_Java java;
|
|
|
|
|
if (java.is_attached()) {
|
|
|
|
|
|
|
|
|
|
jclass class_tone_generator = java.env()->FindClass("android/media/ToneGenerator");
|
|
|
|
|
|
|
|
|
|
jmethodID toneGeneratorConstructor = java.env()->GetMethodID(
|
|
|
|
|
class_tone_generator, "<init>",
|
|
|
|
|
"(II)V");
|
|
|
|
|
|
|
|
|
|
jobject toneGeneratorObj = java.env()->NewObject(
|
|
|
|
|
class_tone_generator, toneGeneratorConstructor,
|
|
|
|
|
4, // STREAM_ALARM
|
|
|
|
|
100); // volume
|
|
|
|
|
|
|
|
|
|
jmethodID method_start_tone = java.env()->GetMethodID(
|
|
|
|
|
class_tone_generator,
|
|
|
|
|
"startTone",
|
2018-06-23 10:06:04 +00:00
|
|
|
"(II)Z");
|
2018-06-21 12:36:02 +00:00
|
|
|
|
|
|
|
|
java.env()->CallBooleanMethod(
|
|
|
|
|
toneGeneratorObj, method_start_tone,
|
2018-06-23 10:06:04 +00:00
|
|
|
androidSoundID,
|
|
|
|
|
1000);
|
2018-06-21 12:36:02 +00:00
|
|
|
|
|
|
|
|
java.env()->DeleteLocalRef(class_tone_generator);
|
|
|
|
|
java.env()->DeleteLocalRef(toneGeneratorObj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
|
2018-06-21 17:02:47 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
Get the current mouse coordinates.
|
|
|
|
|
|
|
|
|
|
This is used, among other things, to position the FLTK standard dialogs in
|
|
|
|
|
a way that makes it easy to click the most common button. For an Android
|
|
|
|
|
touch screen, this makes no sense at all, which is why we return the center
|
|
|
|
|
of the screen for now.
|
|
|
|
|
|
|
|
|
|
\todo rethink the dialog positioning scheme for touch devices.
|
|
|
|
|
|
|
|
|
|
\todo this method assumes a fixed screen resolution
|
|
|
|
|
|
|
|
|
|
\param [out] x
|
|
|
|
|
\param [out] y
|
|
|
|
|
\return
|
2018-06-21 17:02:47 +00:00
|
|
|
*/
|
|
|
|
|
int Fl_Android_Screen_Driver::get_mouse(int &x, int &y)
|
|
|
|
|
{
|
|
|
|
|
x = 600/2;
|
|
|
|
|
y = 800/2;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 18:14:13 +00:00
|
|
|
|
|
|
|
|
void Fl_Android_Screen_Driver::grab(Fl_Window* win)
|
|
|
|
|
{
|
|
|
|
|
if (win) {
|
|
|
|
|
if (!Fl::grab_) {
|
|
|
|
|
// TODO: will we need to fix any focus and/or direct the input stream to a window
|
|
|
|
|
}
|
|
|
|
|
Fl::grab_ = win;
|
|
|
|
|
} else {
|
|
|
|
|
if (Fl::grab_) {
|
|
|
|
|
Fl::grab_ = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-06-23 10:06:04 +00:00
|
|
|
/**
|
2018-06-23 20:50:22 +00:00
|
|
|
\}
|
|
|
|
|
\endcond
|
2018-06-23 10:06:04 +00:00
|
|
|
*/
|