Support for touch/key events in LUA widgets

This commit is contained in:
Raphael Coeffic
2022-06-11 15:38:08 +02:00
committed by Raphael Coeffic
parent 93797a3e14
commit a3424d5bc0
21 changed files with 965 additions and 753 deletions
+1
View File
@@ -218,6 +218,7 @@ if(LUA)
lua/api_general.cpp
lua/api_model.cpp
lua/api_filesystem.cpp
lua/lua_event.cpp
)
if(GUI_DIR STREQUAL colorlcd)
+1 -1
View File
@@ -31,7 +31,7 @@
#include "serial.h"
extern volatile uint32_t g_tmr10ms;
EXTERN_C(extern volatile uint32_t g_tmr10ms);
#if defined(SIMU)
typedef void (*traceCallbackFunc)(const char * text);
+5 -1
View File
@@ -104,7 +104,11 @@ endmacro(add_gui_src)
if(LUA)
add_gui_src(standalone_lua.cpp)
set(SRC ${SRC} lua/widgets.cpp)
set(SRC ${SRC}
lua/lua_widget.cpp
lua/lua_widget_factory.cpp
lua/widgets.cpp
)
endif()
if(LUA_MIXER)
+6 -1
View File
@@ -69,7 +69,12 @@ static bool evt_to_indev_data(event_t evt, lv_indev_data_t *data)
case KEY_EXIT:
if (evt != EVT_KEY_LONG(KEY_EXIT)) {
data->key = LV_KEY_ESC;
} else { return false; }
} else {
// prevent subsequent RELEASE event
lv_indev_wait_release(lv_indev_get_act());
data->state = LV_INDEV_STATE_PRESSED;
return false;
}
break;
default:
-1
View File
@@ -185,7 +185,6 @@ struct ToolButton : public TextButton {
void RadioToolsPage::rebuild(FormWindow * window)
{
window->clear();
// Window::clearFocus();
std::list<ToolEntry> tools;
+16 -76
View File
@@ -21,7 +21,7 @@
#include "standalone_lua.h"
#include "view_main.h"
#include "touch.h"
// #include "touch.h"
using std::string;
extern BitmapBuffer* luaLcdBuffer;
@@ -51,15 +51,13 @@ void LuaPopup::paint(BitmapBuffer* dc, uint8_t type, const char* text, const cha
// singleton instance
StandaloneLuaWindow* StandaloneLuaWindow::_instance;
#if defined(HARDWARE_TOUCH)
bool StandaloneLuaWindow::fingerDown = false;
#endif
StandaloneLuaWindow::StandaloneLuaWindow() :
Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE),
lcdBuffer(BMP_RGB565, LCD_W, LCD_H),
popup({50, 73, LCD_W - 100, LCD_H - 146})
{
// setup LUA event handler
setupHandler(this);
}
StandaloneLuaWindow* StandaloneLuaWindow::instance()
@@ -82,8 +80,10 @@ void StandaloneLuaWindow::attach()
lv_scr_load(lvobj);
Layer::push(this);
lv_group_add_obj(lv_group_get_default(), lvobj);
lv_group_set_editing(lv_group_get_default(), true);
}
// setFocus();
}
void StandaloneLuaWindow::deleteLater(bool detach, bool trash)
@@ -119,30 +119,11 @@ void StandaloneLuaWindow::checkEvents()
luaLcdBuffer = &lcdBuffer;
if (luaState != INTERPRETER_RELOAD_PERMANENT_SCRIPTS) {
#if defined(HARDWARE_TOUCH)
// Stick touch in the same slot of touches as luaTask sticks evt in events
if (IS_TOUCH_EVENT(event)) {
for (int i = 0; i < EVENT_BUFFER_SIZE; i++) {
if (events[i] == 0) {
touches[i] = touch;
break;
}
}
}
#endif
if (luaTask(event, true)) {
#if defined(DEBUG_WINDOWS)
TRACE("# StandaloneLuaWindow::invalidate()");
#endif
invalidate();
}
// if LUA finished a full cycle,
// invalidate to display the screen buffer
if (luaTask(0, true)) { invalidate(); }
}
event = 0;
#if defined(HARDWARE_TOUCH)
memclear(&touch, sizeof(touch));
#endif
if (luaState == INTERPRETER_RELOAD_PERMANENT_SCRIPTS) {
// Script does not run anymore...
TRACE("LUA standalone script exited: deleting window!");
@@ -153,61 +134,20 @@ void StandaloneLuaWindow::checkEvents()
luaLcdBuffer = nullptr;
}
void StandaloneLuaWindow::onClicked()
{
LuaEventHandler::onClicked();
}
void StandaloneLuaWindow::onCancel()
{
// do nothing: wait for long press to kill
// the script
LuaEventHandler::onCancel();
}
#if defined(HARDWARE_KEYS)
void StandaloneLuaWindow::onEvent(event_t evt)
{
event = evt;
LuaEventHandler::onEvent(evt);
}
#endif
#if defined(HARDWARE_TOUCH)
bool StandaloneLuaWindow::onTouchStart(coord_t x, coord_t y)
{
TRACE_WINDOWS("StandaloneLuaWindow received touch start (%d) x=%d;y=%d", hasFocus(), x, y);
// Only one EVT_TOUCH_FIRST at a time
if (!fingerDown) {
event = EVT_TOUCH_FIRST;
touch.touchX = x;
touch.touchY = y;
fingerDown = true;
}
return true;
}
bool StandaloneLuaWindow::onTouchEnd(coord_t x, coord_t y)
{
TRACE_WINDOWS("StandaloneLuaWindow received touch end (%d) x=%d;y=%d", hasFocus(), x, y);
if (touchState.tapCount > 0) {
event = EVT_TOUCH_TAP;
touch.tapCount = touchState.tapCount;
} else
event = EVT_TOUCH_BREAK;
touch.touchX = x;
touch.touchY = y;
fingerDown = false;
return true;
}
bool StandaloneLuaWindow::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY)
{
TRACE_WINDOWS("StandaloneLuaWindow touch slide");
event = EVT_TOUCH_SLIDE;
touch.touchX = x;
touch.touchY = y;
touch.startX = startX;
touch.startY = startY;
touch.slideX = slideX;
touch.slideY = slideY;
fingerDown = false;
return true;
}
#endif
bool StandaloneLuaWindow::displayPopup(event_t event, uint8_t type, const char* text,
const char* info, bool& result)
+8 -19
View File
@@ -24,6 +24,7 @@
#if defined(LUA)
#include "libopenui.h"
#include "lua/lua_api.h"
#include "lua/lua_widget.h"
struct LuaPopup
{
@@ -32,7 +33,7 @@ struct LuaPopup
void paint(BitmapBuffer* dc, uint8_t type, const char* text, const char* info);
};
class StandaloneLuaWindow : public Window
class StandaloneLuaWindow : public Window, public LuaEventHandler
{
static StandaloneLuaWindow* _instance;
@@ -43,24 +44,11 @@ public:
void attach();
void deleteLater(bool detach = true, bool trash = true) override;
void paint(BitmapBuffer* dc) override;
void checkEvents() override;
void onCancel() override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "StandaloneLuaWindow"; }
#endif
#if defined(HARDWARE_KEYS)
void onEvent(event_t evt) override;
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchStart(coord_t x, coord_t y) override;
bool onTouchEnd(coord_t x, coord_t y) override;
bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override;
#endif
bool displayPopup(event_t event, uint8_t type, const char* text,
const char* info, bool& result);
@@ -75,10 +63,11 @@ protected:
// run LUA code
void runLua(event_t evt);
event_t event = 0;
#if defined(HARDWARE_TOUCH)
static bool fingerDown;
LuaTouchData touch;
#endif
void paint(BitmapBuffer* dc) override;
void onEvent(event_t evt) override;
void checkEvents() override;
void onClicked() override;
void onCancel() override;
};
#endif
+16 -34
View File
@@ -30,8 +30,6 @@
#include "touch.h"
#endif
// constexpr uint32_t WIDGET_FOCUS_TIMEOUT = 10*1000; // 10 seconds
static void openWidgetMenu(Widget * parent)
{
Menu *menu = new Menu(parent);
@@ -64,17 +62,6 @@ Widget::Widget(const WidgetFactory* factory, Window* parent,
});
}
void Widget::checkEvents()
{
Button::checkEvents();
// Give the focus back to ViewMain after WIDGET_FOCUS_TIMEOUT milliseconds
// if (!fullscreen && hasFocus() && (RTOS_GET_MS() - focusGainedTS >= WIDGET_FOCUS_TIMEOUT)) {
// ViewMain::instance()->setFocus();
// }
}
void Widget::paint(BitmapBuffer * dc)
{
if (fullscreen) {
@@ -86,12 +73,6 @@ void Widget::paint(BitmapBuffer * dc)
refresh(dc);
if (hasFocus() && !fullscreen) {
// // Blink from haft-time before expiring (5s)
// if ((RTOS_GET_MS() - focusGainedTS >= WIDGET_FOCUS_TIMEOUT / 2)
// && !FAST_BLINK_ON_PHASE) {
// return;
// }
dc->drawRect(0, 0, width(), height(), 2, STASHED, COLOR_THEME_FOCUS);
}
}
@@ -99,21 +80,7 @@ void Widget::paint(BitmapBuffer * dc)
#if defined(HARDWARE_KEYS)
void Widget::onEvent(event_t event)
{
TRACE("### event = 0x%x ###", event);
if (!fullscreen) {
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
// [EXIT] -> exit focus mode (if not fullscreen)
// killEvents(event);
// ViewMain::instance()->setFocus();
return;
}
// Forward the rest to the parent class
Button::onEvent(event);
}
// In fullscreen mode, we react only to that one key:
// [RTN / EXIT LONG] -> exit fullscreen mode
else if (EVT_KEY_LONG(KEY_EXIT) == event) {
// killEvents(event);
if (fullscreen && (EVT_KEY_LONG(KEY_EXIT) == event)) {
setFullscreen(false);
}
}
@@ -148,6 +115,11 @@ void Widget::setFullscreen(bool enable)
// re-enable scroll chaining (sliding main view)
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
// exit editing mode
lv_group_set_editing(lv_group_get_default(), false);
onFullscreen(enable);
}
// Enter Fullscreen Mode
else {
@@ -167,9 +139,19 @@ void Widget::setFullscreen(bool enable)
// disable scroll chaining (sliding main view)
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_CHAIN_HOR);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
// set group in editing mode (keys LEFT / RIGHT)
lv_group_set_editing(lv_group_get_default(), true);
onFullscreen(enable);
}
}
void Widget::onLongPress()
{
if (!fullscreen) Button::onLongPress();
}
std::list<const WidgetFactory *> & getRegisteredWidgets()
{
static std::list<const WidgetFactory *> widgets;
+3 -1
View File
@@ -83,7 +83,6 @@ class Widget : public Button
#endif
// Window interface
void checkEvents() override;
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
@@ -108,6 +107,9 @@ class Widget : public Button
PersistentData * persistentData;
uint32_t focusGainedTS = 0;
bool fullscreen = false;
void onLongPress() override;
virtual void onFullscreen(bool enable) {}
};
void registerWidget(const WidgetFactory * factory);
+22 -101
View File
@@ -24,9 +24,13 @@
#include <ctype.h>
#include <stdio.h>
#include <algorithm>
#include "opentx.h"
#include "bin_allocator.h"
#include "lua_api.h"
#include "lua_event.h"
#include "sdcard.h"
#include "api_filesystem.h"
@@ -49,15 +53,9 @@ extern "C" {
#define PERMANENT_SCRIPTS_MAX_INSTRUCTIONS 100
#define LUA_TASK_PERIOD_TICKS 5 // 50 ms
#if defined(HARDWARE_TOUCH)
#include "touch.h"
#define EVT_TOUCH_SWIPE_LOCK 4
#define EVT_TOUCH_SWIPE_SPEED 60
#define EVT_TOUCH_SWIPE_TIMEOUT 50
tmr10ms_t swipeTimeOut = 0;
LuaTouchData touches[EVENT_BUFFER_SIZE] = { 0 };
#endif
// #if defined(HARDWARE_TOUCH)
// #include "touch.h"
// #endif
// Since we may not run FG every time, keep the events in a buffer
event_t events[EVENT_BUFFER_SIZE] = { 0 };
@@ -148,30 +146,6 @@ static void luaHook(lua_State * L, lua_Debug *ar)
#endif // #if defined(LUA_ALLOCATOR_TRACER)
}
#if defined(COLORLCD)
static void l_pushtableint(const char * key, int value)
{
lua_pushstring(lsScripts, key);
lua_pushinteger(lsScripts, value);
lua_settable(lsScripts, -3);
}
static void l_pushtablebool(const char * key, bool value)
{
lua_pushstring(lsScripts, key);
lua_pushboolean(lsScripts, value);
lua_settable(lsScripts, -3);
}
#endif
void luaEmptyEventBuffer()
{
memclear(&events, sizeof(events));
#if defined(HARDWARE_TOUCH)
memclear(&touches, sizeof(touches));
#endif
}
#if defined(LUA_MODEL_SCRIPTS)
void luaGetInputs(ScriptInputsOutputs & sid)
{
@@ -988,7 +962,7 @@ void luaExec(const char * filename)
static bool resumeLua(bool init, bool allowLcdUsage)
{
static uint8_t idx;
static event_t evt = 0;
static LuaEventData evt;
if (init) idx = 0;
bool scriptWasRun = false;
@@ -1021,13 +995,8 @@ static bool resumeLua(bool init, bool allowLcdUsage)
if (ref == SCRIPT_STANDALONE) {
// Pull a new event from the buffer
evt = events[0];
for (int i = 1; i < EVENT_BUFFER_SIZE; i++)
events[i - 1] = events[i];
events[EVENT_BUFFER_SIZE - 1] = 0;
if (evt == EVT_KEY_LONG(KEY_EXIT)) {
// killEvents(evt);
luaNextEvent(&evt);
if (evt.event == EVT_KEY_LONG(KEY_EXIT)) {
luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS;
}
}
@@ -1044,65 +1013,24 @@ static bool resumeLua(bool init, bool allowLcdUsage)
if (allowLcdUsage) {
#if defined(PCBTARANIS)
if ((menuHandlers[menuLevel] == menuViewTelemetry && ref == SCRIPT_TELEMETRY_FIRST + s_frsky_view) || ref == SCRIPT_STANDALONE) {
if ((menuHandlers[menuLevel] == menuViewTelemetry &&
ref == SCRIPT_TELEMETRY_FIRST + s_frsky_view) ||
ref == SCRIPT_STANDALONE) {
#else
if (ref == SCRIPT_STANDALONE) {
#endif
// Pull a new event from the buffer
evt = events[0];
for (int i = 1; i < EVENT_BUFFER_SIZE; i++)
events[i - 1] = events[i];
events[EVENT_BUFFER_SIZE - 1] = 0;
#if defined(HARDWARE_TOUCH)
LuaTouchData* touch = &touches[0];
#endif
luaNextEvent(&evt);
lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, sid.run);
lua_pushunsigned(lsScripts, evt);
lua_pushunsigned(lsScripts, evt.event);
inputsCount = 1;
#if defined(HARDWARE_TOUCH)
if (IS_TOUCH_EVENT(evt)) {
lua_newtable(lsScripts);
l_pushtableint("x", touch->touchX);
l_pushtableint("y", touch->touchY);
l_pushtableint("tapCount", touch->tapCount);
if (evt == EVT_TOUCH_SLIDE) {
l_pushtableint("startX", touch->startX);
l_pushtableint("startY", touch->startY);
l_pushtableint("slideX", touch->slideX);
l_pushtableint("slideY", touch->slideY);
// Do we have a swipe? Only one at a time!
if (get_tmr10ms() > swipeTimeOut) {
coord_t absX = (touch->slideX < 0) ? -(touch->slideX) : touch->slideX;
coord_t absY = (touch->slideY < 0) ? -(touch->slideY) : touch->slideY;
bool swiped = false;
if (absX > EVT_TOUCH_SWIPE_LOCK * absY) {
if ((swiped = (touch->slideX > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeRight", true);
else if ((swiped = (touch->slideX < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeLeft", true);
}
else if (absY > EVT_TOUCH_SWIPE_LOCK * absX) {
if ((swiped = (touch->slideY > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeDown", true);
else if ((swiped = (touch->slideY < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeUp", true);
}
if (swiped)
swipeTimeOut = get_tmr10ms() + EVT_TOUCH_SWIPE_TIMEOUT;
}
}
if (IS_TOUCH_EVENT(evt.event)) {
luaPushTouchEventTable(lsScripts, &evt);
inputsCount = 2;
}
// Move the touch buffer forward
for (int i = 1; i < EVENT_BUFFER_SIZE; i++)
touches[i - 1] = touches[i];
memclear(&touches[EVENT_BUFFER_SIZE - 1], sizeof(LuaTouchData));
#endif
}
else continue;
@@ -1218,7 +1146,7 @@ static bool resumeLua(bool init, bool allowLcdUsage)
luaError(lsScripts, sid.state);
}
if (evt == EVT_KEY_LONG(KEY_EXIT)) {
if (evt.event == EVT_KEY_LONG(KEY_EXIT)) {
TRACE("Script force exit");
// killEvents(evt);
luaEmptyEventBuffer();
@@ -1226,8 +1154,8 @@ static bool resumeLua(bool init, bool allowLcdUsage)
}
#if defined(KEYS_GPIO_REG_MENU)
// TODO find another key and add a #define
else if (evt == EVT_KEY_LONG(KEY_MENU)) {
killEvents(evt);
else if (evt.event == EVT_KEY_LONG(KEY_MENU)) {
killEvents(evt.event);
luaEmptyEventBuffer();
luaDisplayStatistics = !luaDisplayStatistics;
}
@@ -1262,14 +1190,7 @@ bool luaTask(event_t evt, bool allowLcdUsage)
bool scriptWasRun = false;
// Add event to buffer
if (evt != 0) {
for (int i = 0; i < EVENT_BUFFER_SIZE; i++) {
if (events[i] == 0) {
events[i] = evt;
break;
}
}
}
if (evt != 0) { luaPushEvent(evt); }
// For preemption
luaCycleStart = get_tmr10ms();
+12 -15
View File
@@ -61,9 +61,6 @@ extern lua_State * lsScripts;
extern bool luaLcdAllowed;
#define EVENT_BUFFER_SIZE 4
extern event_t events[EVENT_BUFFER_SIZE];
#if defined(COLORLCD)
extern lua_State * lsWidgets;
extern uint32_t luaExtraMemoryUsage;
@@ -251,19 +248,19 @@ inline bool isLuaStandaloneRunning() {
return scriptInternalData[0].reference == SCRIPT_STANDALONE;
}
#if defined(HARDWARE_TOUCH)
struct LuaTouchData {
coord_t touchX;
coord_t touchY;
coord_t startX;
coord_t startY;
coord_t slideX;
coord_t slideY;
short tapCount;
};
// #if defined(HARDWARE_TOUCH)
// struct LuaTouchData {
// coord_t touchX;
// coord_t touchY;
// coord_t startX;
// coord_t startY;
// coord_t slideX;
// coord_t slideY;
// short tapCount;
// };
extern LuaTouchData touches[EVENT_BUFFER_SIZE];
#endif
// extern LuaTouchData touches[EVENT_BUFFER_SIZE];
// #endif
#else // defined(LUA)
+131
View File
@@ -0,0 +1,131 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "lua_event.h"
#include "opentx_helpers.h"
#include "timers_driver.h"
#include "keys.h"
extern "C" {
#include "lua.h"
};
#define EVT_TOUCH_SWIPE_LOCK 4
#define EVT_TOUCH_SWIPE_SPEED 60
#define EVT_TOUCH_SWIPE_TIMEOUT 50
static tmr10ms_t _swipeTimeOut = 0;
static LuaEventData _lua_event_buffer[EVENT_BUFFER_SIZE];
LuaEventData::LuaEventData()
{
memclear(this, sizeof(LuaEventData));
}
void luaPushEvent(event_t evt)
{
for (int i = 0; i < EVENT_BUFFER_SIZE; i++) {
if (_lua_event_buffer[i].event == 0) {
_lua_event_buffer[i].event = evt;
return;
}
}
}
void luaNextEvent(LuaEventData* evt)
{
if (!evt) return;
*evt = _lua_event_buffer[0];
if (evt->event != 0) {
size_t len = sizeof(_lua_event_buffer) - sizeof(LuaEventData);
memmove(_lua_event_buffer, _lua_event_buffer + 1, len);
memclear(_lua_event_buffer + (EVENT_BUFFER_SIZE - 1),
sizeof(EVENT_BUFFER_SIZE));
}
}
void luaEmptyEventBuffer()
{
memclear(_lua_event_buffer, sizeof(_lua_event_buffer));
}
// Look for a slot in the event buffer that is either unused (zero) or matches event
LuaEventData* luaGetEventSlot(event_t event)
{
for (int i = 0; i < EVENT_BUFFER_SIZE; i++) {
if (_lua_event_buffer[i].event == event || _lua_event_buffer[i].event == 0)
return &_lua_event_buffer[i];
}
return nullptr;
}
static void l_pushtableint(lua_State* ls, const char * key, int value)
{
lua_pushstring(ls, key);
lua_pushinteger(ls, value);
lua_settable(ls, -3);
}
static void l_pushtablebool(lua_State* ls, const char * key, bool value)
{
lua_pushstring(ls, key);
lua_pushboolean(ls, value);
lua_settable(ls, -3);
}
void luaPushTouchEventTable(lua_State* ls, LuaEventData* evt)
{
lua_newtable(ls);
l_pushtableint(ls, "x", evt->touchX);
l_pushtableint(ls, "y", evt->touchY);
l_pushtableint(ls, "tapCount", evt->tapCount);
if (evt->event == EVT_TOUCH_SLIDE) {
l_pushtableint(ls, "startX", evt->startX);
l_pushtableint(ls, "startY", evt->startY);
l_pushtableint(ls, "slideX", evt->slideX);
l_pushtableint(ls, "slideY", evt->slideY);
// Do we have a swipe? Only one at a time!
if (get_tmr10ms() > _swipeTimeOut) {
coord_t absX = (evt->slideX < 0) ? -(evt->slideX) : evt->slideX;
coord_t absY = (evt->slideY < 0) ? -(evt->slideY) : evt->slideY;
bool swiped = false;
if (absX > EVT_TOUCH_SWIPE_LOCK * absY) {
if ((swiped = (evt->slideX > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool(ls, "swipeRight", true);
else if ((swiped = (evt->slideX < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool(ls, "swipeLeft", true);
} else if (absY > EVT_TOUCH_SWIPE_LOCK * absX) {
if ((swiped = (evt->slideY > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool(ls, "swipeDown", true);
else if ((swiped = (evt->slideY < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool(ls, "swipeUp", true);
}
if (swiped) _swipeTimeOut = get_tmr10ms() + EVT_TOUCH_SWIPE_TIMEOUT;
}
}
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "opentx_types.h"
#define EVENT_BUFFER_SIZE 4
struct LuaEventData {
event_t event;
#if defined(HARDWARE_TOUCH)
coord_t touchX;
coord_t touchY;
coord_t startX;
coord_t startY;
coord_t slideX;
coord_t slideY;
short tapCount;
#endif
LuaEventData();
};
void luaPushEvent(event_t evt);
void luaNextEvent(LuaEventData* evt);
void luaEmptyEventBuffer();
// Look for a slot in the event buffer that is either unused (zero) or matches event
LuaEventData* luaGetEventSlot(event_t event = 0);
struct lua_State;
typedef struct lua_State lua_State;
void luaPushTouchEventTable(lua_State* ls, LuaEventData* evt);
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
struct LuaEventData {
event_t event;
#if defined(HARDWARE_TOUCH)
coord_t touchX;
coord_t touchY;
coord_t startX;
coord_t startY;
coord_t slideX;
coord_t slideY;
short tapCount;
#endif
EventData();
};
+358
View File
@@ -0,0 +1,358 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "lua_widget.h"
#include "lua_widget_factory.h"
#include "lua_api.h"
#include "lua_event.h"
#include "api_colorlcd.h"
#include "draw_functions.h"
#define MAX_INSTRUCTIONS (20000/100)
#if defined(HARDWARE_TOUCH)
tmr10ms_t LuaEventHandler::swipeTimeOut = 0;
coord_t LuaEventHandler::_startX;
coord_t LuaEventHandler::_startY;
bool LuaEventHandler::_sliding = false;
#endif
void LuaEventHandler::event_cb(lv_event_t* e)
{
auto obj = lv_event_get_target(e);
auto win = (Window*)lv_obj_get_user_data(obj);
if (!win) return;
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING) {
lv_area_t obj_coords;
lv_obj_get_coords(obj, &obj_coords);
lv_point_t point_act;
lv_indev_t* click_source = (lv_indev_t*)lv_event_get_param(e);
lv_indev_get_point(click_source, &point_act);
// Ignore event from keypad
if (point_act.x < 0 || point_act.y < 0) return;
lv_point_t rel_pos;
rel_pos.x = point_act.x - obj_coords.x1;
rel_pos.y = point_act.y - obj_coords.y1;
if (code == LV_EVENT_PRESSING) {
lv_point_t vect_act;
lv_indev_get_vect(click_source, &vect_act);
if ((abs(vect_act.x) >= SLIDE_RANGE) || (abs(vect_act.y) >= SLIDE_RANGE))
_sliding = true;
if (!_sliding) return;
TRACE_WINDOWS("LuaWidget touch slide");
// If we already have a SLIDE going, then accumulate slide values instead
// of allocating an empty slot
LuaEventData* es = luaGetEventSlot(EVT_TOUCH_SLIDE);
if (es) {
es->event = EVT_TOUCH_SLIDE;
es->touchX = rel_pos.x;
es->touchY = rel_pos.y;
es->startX = _startX;
es->startY = _startY;
es->slideX += vect_act.x;
es->slideY += vect_act.y;
TRACE("EVT_TOUCH_SLIDE [%d,%d]", rel_pos.x, rel_pos.y);
}
} else {
LuaEventData* es = luaGetEventSlot();
if (es) {
es->event = EVT_TOUCH_FIRST;
es->touchX = rel_pos.x;
es->touchY = rel_pos.y;
TRACE("EVT_TOUCH_FIRST [%d,%d]", rel_pos.x, rel_pos.y);
}
_startX = rel_pos.x;
_startY = rel_pos.y;
}
} else if (code == LV_EVENT_KEY) {
uint32_t key = *(uint32_t*)lv_event_get_param(e);
if (key == LV_KEY_LEFT) {
win->onEvent(EVT_ROTARY_LEFT);
} else if (key == LV_KEY_RIGHT) {
win->onEvent(EVT_ROTARY_RIGHT);
}
}
}
void LuaEventHandler::onClicked()
{
auto click_source = lv_indev_get_act();
bool is_pointer = lv_indev_get_type(click_source) == LV_INDEV_TYPE_POINTER;
if (is_pointer) {
lv_point_t point_act;
lv_indev_get_point(click_source, &point_act);
LuaEventData* es = luaGetEventSlot();
if (!es) return;
es->event = EVT_TOUCH_TAP;
es->touchX = point_act.x;
es->touchY = point_act.y;
es->tapCount = 1;
_sliding = false;
} else {
luaPushEvent(EVT_KEY_BREAK(KEY_ENTER));
}
}
void LuaEventHandler::onCancel()
{
luaPushEvent(EVT_KEY_BREAK(KEY_EXIT));
}
void LuaEventHandler::onEvent(event_t event)
{
// if (event == EVT_KEY_LONG(KEY_EXIT)) {
// // Exits widget fullscreen / standalone LUA
// lv_indev_wait_release(lv_indev_get_act());
// }
luaPushEvent(event);
}
void LuaEventHandler::setupHandler(Window* w)
{
lv_obj_t* obj = w->getLvObj();
lv_obj_add_event_cb(obj, LuaEventHandler::event_cb, LV_EVENT_ALL, nullptr);
}
void LuaEventHandler::removeHandler(Window* w)
{
lv_obj_t* obj = w->getLvObj();
lv_obj_remove_event_cb(obj, LuaEventHandler::event_cb);
}
LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent,
const rect_t& rect, WidgetPersistentData* persistentData,
int luaWidgetDataRef) :
Widget(factory, parent, rect, persistentData),
luaWidgetDataRef(luaWidgetDataRef),
errorMessage(nullptr)
{
}
LuaWidget::~LuaWidget()
{
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
free(errorMessage);
}
void LuaWidget::onClicked()
{
if (!fullscreen) {
Button::onClicked();
return;
}
LuaEventHandler::onClicked();
}
void LuaWidget::onCancel()
{
if (!fullscreen) {
Button::onCancel();
return;
}
LuaEventHandler::onCancel();
}
void LuaWidget::checkEvents()
{
Widget::checkEvents();
// paint has not been called
if (!refreshed) {
background();
refreshed = true;
}
refreshed = false;
invalidate();
#if defined(DEBUG_WINDOWS)
TRACE_WINDOWS("# refresh: %s", getWindowDebugString().c_str());
#endif
}
static void l_pushtableint(const char * key, int value)
{
lua_pushstring(lsWidgets, key);
lua_pushinteger(lsWidgets, value);
lua_settable(lsWidgets, -3);
}
void LuaWidget::update()
{
Widget::update();
if (lsWidgets == 0 || errorMessage) return;
LuaWidgetFactory * lua_factory = (LuaWidgetFactory *)factory;
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, lua_factory->updateFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
lua_newtable(lsWidgets);
int i = 0;
for (const ZoneOption * option = getOptions(); option->name; option++, i++) {
if (option->type == ZoneOption::String) {
lua_pushstring(lsWidgets, option->name);
char str[LEN_ZONE_OPTION_STRING + 1] = {0}; // Zero-terminated string for Lua
strncpy(str, persistentData->options[i].value.stringValue, LEN_ZONE_OPTION_STRING);
lua_pushstring(lsWidgets, &str[0]);
lua_settable(lsWidgets, -3);
} else if (option->type == ZoneOption::Color) {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, COLOR2FLAGS(value) | RGB_FLAG);
} else {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, value);
}
}
if (lua_pcall(lsWidgets, 2, 0, 0) != 0) {
setErrorMessage("update()");
}
}
void LuaWidget::onFullscreen(bool enable)
{
if (enable) {
setupHandler(this);
} else {
removeHandler(this);
luaEmptyEventBuffer();
}
}
void LuaWidget::setErrorMessage(const char * funcName)
{
const char* lua_err = lua_tostring(lsWidgets, -1);
TRACE("Error in widget %s %s function: %s", factory->getName(), funcName, lua_err);
TRACE("Widget disabled");
size_t err_len = snprintf(NULL, 0, "ERROR in %s: %s", funcName, lua_err);
errorMessage = (char*)malloc(err_len + 1);
if (errorMessage) {
snprintf(errorMessage, err_len, "ERROR in %s: %s", funcName, lua_err);
errorMessage[err_len] = '\0';
}
}
const char * LuaWidget::getErrorMessage() const
{
return errorMessage;
}
void LuaWidget::refresh(BitmapBuffer* dc)
{
if (lsWidgets == 0) return;
if (errorMessage) {
drawTextLines(dc, 0, 0, fullscreen ? LCD_W : rect.w,
fullscreen ? LCD_H : rect.h, errorMessage,
FONT(XS) | COLOR_THEME_WARNING);
return;
}
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->refreshFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
// Pass key event to fullscreen Lua widget
LuaEventData evt;
luaNextEvent(&evt);
if (fullscreen) {
lua_pushinteger(lsWidgets, evt.event);
}
else
lua_pushnil(lsWidgets);
#if defined(HARDWARE_TOUCH)
if (fullscreen && IS_TOUCH_EVENT(evt.event)) {
luaPushTouchEventTable(lsWidgets, &evt);
} else
#endif
lua_pushnil(lsWidgets);
// Enable drawing into the current LCD buffer
luaLcdBuffer = dc;
// This little hack is needed to not interfere with the LCD usage of preempted scripts
bool lla = luaLcdAllowed;
luaLcdAllowed = true;
runningFS = this;
if (lua_pcall(lsWidgets, 3, 0, 0) != 0) {
setErrorMessage("refresh()");
}
runningFS = nullptr;
// Remove LCD
luaLcdAllowed = lla;
luaLcdBuffer = nullptr;
// mark as refreshed
refreshed = true;
}
void LuaWidget::background()
{
if (lsWidgets == 0 || errorMessage) return;
// TRACE("LuaWidget::background()");
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
if (factory->backgroundFunction) {
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->backgroundFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
runningFS = this;
if (lua_pcall(lsWidgets, 1, 0, 0) != 0) {
setErrorMessage("background()");
}
runningFS = nullptr;
}
}
void LuaWidget::onEvent(event_t event)
{
if (fullscreen) {
LuaEventHandler::onEvent(event);
}
Widget::onEvent(event);
}
+88
View File
@@ -0,0 +1,88 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "window.h"
#include "widget.h"
#include "lua_api.h"
#include "opentx_types.h"
class LuaEventHandler
{
#if defined(HARDWARE_TOUCH)
static tmr10ms_t swipeTimeOut;
static bool _sliding;
static coord_t _startX;
static coord_t _startY;
#endif
static void event_cb(lv_event_t* e);
protected:
void onClicked();
void onCancel();
void onEvent(event_t event);
public:
LuaEventHandler() = default;
void setupHandler(Window* w);
void removeHandler(Window* w);
};
class LuaWidget : public Widget, public LuaEventHandler
{
friend class LuaWidgetFactory;
int luaWidgetDataRef;
char* errorMessage;
bool refreshed = false;
// Window interface
void onClicked() override;
void onCancel() override;
void checkEvents() override;
// Widget interface
void onFullscreen(bool enable) override;
void setErrorMessage(const char* funcName);
public:
LuaWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect,
WidgetPersistentData* persistentData, int luaWidgetDataRef);
~LuaWidget() override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "LuaWidget"; }
#endif
// Window interface
void onEvent(event_t event) override;
// Widget interface
const char* getErrorMessage() const override;
void update() override;
void background() override;
// Calls LUA widget 'refresh' method
void refresh(BitmapBuffer* dc) override;
};
+92
View File
@@ -0,0 +1,92 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "lua_widget_factory.h"
#include "lua_widget.h"
#include "lua_api.h"
#include "api_colorlcd.h"
#define MAX_INSTRUCTIONS (20000/100)
static void l_pushtableint(const char * key, int value)
{
lua_pushstring(lsWidgets, key);
lua_pushinteger(lsWidgets, value);
lua_settable(lsWidgets, -3);
}
LuaWidgetFactory::LuaWidgetFactory(const char* name, ZoneOption* widgetOptions,
int createFunction) :
WidgetFactory(name, widgetOptions),
createFunction(createFunction),
updateFunction(0),
refreshFunction(0),
backgroundFunction(0)
{
}
LuaWidgetFactory::~LuaWidgetFactory() { unregisterWidget(this); }
Widget* LuaWidgetFactory::create(Window* parent, const rect_t& rect,
Widget::PersistentData* persistentData,
bool init) const
{
if (lsWidgets == 0) return 0;
initPersistentData(persistentData, init);
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, createFunction);
lua_newtable(lsWidgets);
l_pushtableint("x", 0);
l_pushtableint("y", 0);
l_pushtableint("w", rect.w);
l_pushtableint("h", rect.h);
l_pushtableint("xabs", rect.x);
l_pushtableint("yabs", rect.y);
lua_newtable(lsWidgets);
int i = 0;
for (const ZoneOption* option = options; option->name; option++, i++) {
if (option->type == ZoneOption::String) {
lua_pushstring(lsWidgets, option->name);
// Zero-terminated string for Lua
char str[LEN_ZONE_OPTION_STRING + 1] = {0};
strncpy(str, persistentData->options[i].value.stringValue,
LEN_ZONE_OPTION_STRING);
lua_pushstring(lsWidgets, &str[0]);
lua_settable(lsWidgets, -3);
} else if (option->type == ZoneOption::Color) {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, COLOR2FLAGS(value) | RGB_FLAG);
} else {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, value);
}
}
bool err = lua_pcall(lsWidgets, 2, 1, 0);
int widgetData = err ? LUA_NOREF : luaL_ref(lsWidgets, LUA_REGISTRYINDEX);
LuaWidget* lw = new LuaWidget(this, parent, rect, persistentData, widgetData);
if (err) lw->setErrorMessage("create()");
return lw;
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "gui/colorlcd/widget.h"
class LuaWidgetFactory : public WidgetFactory
{
friend void luaLoadWidgetCallback();
friend class LuaWidget;
public:
LuaWidgetFactory(const char* name, ZoneOption* widgetOptions,
int createFunction);
~LuaWidgetFactory();
Widget* create(Window* parent, const rect_t& rect,
Widget::PersistentData* persistentData,
bool init = true) const override;
protected:
int createFunction;
int updateFunction;
int refreshFunction;
int backgroundFunction;
};
+68 -500
View File
@@ -29,40 +29,45 @@
#include "api_colorlcd.h"
#include "view_main.h"
#include "lua_widget.h"
#include "lua_widget_factory.h"
#define MAX_INSTRUCTIONS (20000/100)
#define LUA_WARNING_INFO_LEN 64
#if defined(HARDWARE_TOUCH)
#include "touch.h"
#define EVT_TOUCH_SWIPE_LOCK 4
#define EVT_TOUCH_SWIPE_SPEED 60
#define EVT_TOUCH_SWIPE_TIMEOUT 50
#endif
// #if defined(HARDWARE_TOUCH)
// #include "touch.h"
// #define EVT_TOUCH_SWIPE_LOCK 4
// #define EVT_TOUCH_SWIPE_SPEED 60
// #define EVT_TOUCH_SWIPE_TIMEOUT 50
// #endif
lua_State * lsWidgets = NULL;
extern int custom_lua_atpanic(lua_State *L);
#define LUA_WIDGET_FILENAME "/main.lua"
#define LUA_FULLPATH_MAXLEN (LEN_FILE_PATH_MAX + LEN_SCRIPT_FILENAME + LEN_FILE_EXTENSION_MAX) // max length (example: /SCRIPTS/THEMES/mytheme.lua)
#define LUA_WIDGET_FILENAME "/main.lua"
#define LUA_FULLPATH_MAXLEN \
(LEN_FILE_PATH_MAX + LEN_SCRIPT_FILENAME + \
LEN_FILE_EXTENSION_MAX) // max length (example:
// /SCRIPTS/THEMES/mytheme.lua)
static void luaHook(lua_State * L, lua_Debug *ar)
static void luaHook(lua_State *L, lua_Debug *ar)
{
if (ar->event == LUA_HOOKCOUNT) {
instructionsPercent++;
#if defined(DEBUG)
// Disable Lua script instructions limit in DEBUG mode,
// just report max value reached
static uint16_t max = 0;
if (instructionsPercent > 100) {
if (max + 10 < instructionsPercent) {
max = instructionsPercent;
TRACE("LUA instructionsPercent %u%%", (uint32_t)max);
// Disable Lua script instructions limit in DEBUG mode,
// just report max value reached
static uint16_t max = 0;
if (instructionsPercent > 100) {
if (max + 10 < instructionsPercent) {
max = instructionsPercent;
TRACE("LUA instructionsPercent %u%%", (uint32_t)max);
}
} else if (instructionsPercent < 10) {
max = 0;
}
}
else if (instructionsPercent < 10) {
max = 0;
}
#else
if (instructionsPercent > 100) {
// From now on, as soon as a line is executed, error
@@ -75,16 +80,17 @@ static void luaHook(lua_State * L, lua_Debug *ar)
#if defined(LUA_ALLOCATOR_TRACER)
else if (ar->event == LUA_HOOKLINE) {
lua_getinfo(L, "nSl", ar);
LuaMemTracer * tracer = GET_TRACER(L);
LuaMemTracer *tracer = GET_TRACER(L);
if (tracer->alloc || tracer->free) {
TRACE("LT: [+%u,-%u] %s:%d", tracer->alloc, tracer->free, tracer->script, tracer->lineno);
TRACE("LT: [+%u,-%u] %s:%d", tracer->alloc, tracer->free, tracer->script,
tracer->lineno);
}
tracer->script = ar->source;
tracer->lineno = ar->currentline;
tracer->alloc = 0;
tracer->free = 0;
}
#endif // #if defined(LUA_ALLOCATOR_TRACER)
#endif // #if defined(LUA_ALLOCATOR_TRACER)
}
void luaSetInstructionsLimit(lua_State * L, int count)
@@ -97,7 +103,7 @@ void luaSetInstructionsLimit(lua_State * L, int count)
#endif
}
ZoneOption * createOptionsArray(int reference, uint8_t maxOptions)
ZoneOption *createOptionsArray(int reference, uint8_t maxOptions)
{
if (reference == 0) {
// TRACE("createOptionsArray() no options");
@@ -116,30 +122,33 @@ ZoneOption * createOptionsArray(int reference, uint8_t maxOptions)
// TRACE("limited to %d options", count);
}
ZoneOption * options = (ZoneOption *)malloc(sizeof(ZoneOption) * (count+1));
ZoneOption *options = (ZoneOption *)malloc(sizeof(ZoneOption) * (count + 1));
if (!options) {
return NULL;
}
PROTECT_LUA() {
PROTECT_LUA()
{
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, reference);
ZoneOption * option = options;
for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2), count-- > 0; lua_pop(lsWidgets, 1)) {
ZoneOption *option = options;
for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2), count-- > 0;
lua_pop(lsWidgets, 1)) {
// TRACE("parsing option %d", count);
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TTABLE); // value is table
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TTABLE); // value is table
uint8_t field = 0;
for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2) && field<5; lua_pop(lsWidgets, 1), field++) {
for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2) && field < 5;
lua_pop(lsWidgets, 1), field++) {
switch (field) {
case 0:
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TSTRING); // value is string
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TSTRING); // value is string
option->name = lua_tostring(lsWidgets, -1);
// TRACE("name = %s", option->name);
break;
case 1:
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->type = (ZoneOption::Type)lua_tointeger(lsWidgets, -1);
// TRACE("type = %d", option->type);
if (option->type > ZoneOption::Color) {
@@ -154,44 +163,42 @@ ZoneOption * createOptionsArray(int reference, uint8_t maxOptions)
}
break;
case 2:
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
if (option->type == ZoneOption::Integer) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->deflt.signedValue = lua_tointeger(lsWidgets, -1);
// TRACE("default signed = %d", option->deflt.signedValue);
}
else if (option->type == ZoneOption::Source ||
option->type == ZoneOption::TextSize) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
} else if (option->type == ZoneOption::Source ||
option->type == ZoneOption::TextSize) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->deflt.unsignedValue = lua_tounsigned(lsWidgets, -1);
// TRACE("default unsigned = %u", option->deflt.unsignedValue);
}
else if (option->type == ZoneOption::Color) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->deflt.unsignedValue = COLOR_VAL(flagsRGB(lua_tounsigned(lsWidgets, -1)));
} else if (option->type == ZoneOption::Color) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->deflt.unsignedValue =
COLOR_VAL(flagsRGB(lua_tounsigned(lsWidgets, -1)));
// TRACE("default unsigned = %u", option->deflt.unsignedValue);
}
else if (option->type == ZoneOption::Bool) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
} else if (option->type == ZoneOption::Bool) {
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->deflt.boolValue = (lua_tounsigned(lsWidgets, -1) != 0);
// TRACE("default bool = %d", (int)(option->deflt.boolValue));
}
else if (option->type == ZoneOption::String) {
strncpy(option->deflt.stringValue, lua_tostring(lsWidgets, -1), LEN_ZONE_OPTION_STRING);
} else if (option->type == ZoneOption::String) {
strncpy(option->deflt.stringValue, lua_tostring(lsWidgets, -1),
LEN_ZONE_OPTION_STRING);
// TRACE("default string = %s", lua_tostring(lsWidgets, -1));
}
break;
case 3:
if (option->type == ZoneOption::Integer) {
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->min.signedValue = lua_tointeger(lsWidgets, -1);
}
break;
case 4:
if (option->type == ZoneOption::Integer) {
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
luaL_checktype(lsWidgets, -2, LUA_TNUMBER); // key is number
luaL_checktype(lsWidgets, -1, LUA_TNUMBER); // value is number
option->max.signedValue = lua_tointeger(lsWidgets, -1);
}
break;
@@ -199,9 +206,10 @@ ZoneOption * createOptionsArray(int reference, uint8_t maxOptions)
}
option++;
}
option->name = NULL; // sentinel
option->name = NULL; // sentinel
}
else {
else
{
TRACE("error in theme/widget options");
free(options);
return NULL;
@@ -210,452 +218,13 @@ ZoneOption * createOptionsArray(int reference, uint8_t maxOptions)
return options;
}
struct eventData {
event_t event;
#if defined(HARDWARE_TOUCH)
coord_t touchX;
coord_t touchY;
coord_t startX;
coord_t startY;
coord_t slideX;
coord_t slideY;
short tapCount;
#endif
};
class LuaWidget: public Widget
{
friend class LuaWidgetFactory;
public:
LuaWidget(const WidgetFactory * factory, Window* parent, const rect_t & rect, WidgetPersistentData * persistentData, int luaWidgetDataRef):
Widget(factory, parent, rect, persistentData),
luaWidgetDataRef(luaWidgetDataRef),
errorMessage(nullptr)
{
}
~LuaWidget() override
{
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
free(errorMessage);
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "LuaWidget";
}
#endif
// Widget interface
const char * getErrorMessage() const override;
void update() override;
void background() override;
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
#if defined(HARDWARE_TOUCH)
bool onTouchStart(coord_t x, coord_t y) override;
bool onTouchEnd(coord_t x, coord_t y) override;
bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override;
#endif
// Calls LUA widget 'refresh' method
void refresh(BitmapBuffer* dc) override;
protected:
int luaWidgetDataRef;
char * errorMessage;
bool refreshed = false;
static eventData events[EVENT_BUFFER_SIZE];
#if defined(HARDWARE_TOUCH)
static bool fingerDown;
static tmr10ms_t swipeTimeOut;
#endif
void checkEvents() override;
void setErrorMessage(const char * funcName);
private:
eventData* findOpenEventSlot(event_t event = 0);
};
eventData LuaWidget::events[EVENT_BUFFER_SIZE] = { 0 };
#if defined(HARDWARE_TOUCH)
bool LuaWidget::fingerDown = false;
tmr10ms_t LuaWidget::swipeTimeOut = 0;
#endif
static void l_pushtableint(const char * key, int value)
{
lua_pushstring(lsWidgets, key);
lua_pushinteger(lsWidgets, value);
lua_settable(lsWidgets, -3);
}
static void l_pushtablebool(const char * key, bool value)
{
lua_pushstring(lsWidgets, key);
lua_pushboolean(lsWidgets, value);
lua_settable(lsWidgets, -3);
}
class LuaWidgetFactory: public WidgetFactory
{
friend void luaLoadWidgetCallback();
friend class LuaWidget;
public:
LuaWidgetFactory(const char * name, ZoneOption * widgetOptions, int createFunction):
WidgetFactory(name, widgetOptions),
createFunction(createFunction),
updateFunction(0),
refreshFunction(0),
backgroundFunction(0)
{
}
~LuaWidgetFactory()
{
unregisterWidget(this);
}
Widget * create(Window* parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init=true) const override
{
if (lsWidgets == 0) return 0;
initPersistentData(persistentData, init);
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, createFunction);
lua_newtable(lsWidgets);
l_pushtableint("x", 0);
l_pushtableint("y", 0);
l_pushtableint("w", rect.w);
l_pushtableint("h", rect.h);
l_pushtableint("xabs", rect.x);
l_pushtableint("yabs", rect.y);
lua_newtable(lsWidgets);
int i = 0;
for (const ZoneOption * option = options; option->name; option++, i++) {
if (option->type == ZoneOption::String) {
lua_pushstring(lsWidgets, option->name);
char str[LEN_ZONE_OPTION_STRING + 1] = {0}; // Zero-terminated string for Lua
strncpy(str, persistentData->options[i].value.stringValue, LEN_ZONE_OPTION_STRING);
lua_pushstring(lsWidgets, &str[0]);
lua_settable(lsWidgets, -3);
} else if (option->type == ZoneOption::Color) {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, COLOR2FLAGS(value) | RGB_FLAG);
} else {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, value);
}
}
bool err = lua_pcall(lsWidgets, 2, 1, 0);
int widgetData = err ? LUA_NOREF : luaL_ref(lsWidgets, LUA_REGISTRYINDEX);
LuaWidget* lw = new LuaWidget(this, parent, rect, persistentData, widgetData);
if (err)
lw->setErrorMessage("create()");
return lw;
}
protected:
int createFunction;
int updateFunction;
int refreshFunction;
int backgroundFunction;
};
// Look for a slot in the event buffer that is either unused (zero) or matches event
eventData* LuaWidget::findOpenEventSlot(event_t event)
{
for (int i = 0; i < EVENT_BUFFER_SIZE; i++) {
if (events[i].event == event || events[i].event == 0)
return &events[i];
}
return NULL;
}
void LuaWidget::checkEvents()
{
Widget::checkEvents();
// paint has not been called
if (!refreshed) {
background();
refreshed = true;
}
refreshed = false;
invalidate();
#if defined(DEBUG_WINDOWS)
TRACE_WINDOWS("# refresh: %s", getWindowDebugString().c_str());
#endif
}
void LuaWidget::update()
{
Widget::update();
if (lsWidgets == 0 || errorMessage) return;
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->updateFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
lua_newtable(lsWidgets);
int i = 0;
for (const ZoneOption * option = getOptions(); option->name; option++, i++) {
if (option->type == ZoneOption::String) {
lua_pushstring(lsWidgets, option->name);
char str[LEN_ZONE_OPTION_STRING + 1] = {0}; // Zero-terminated string for Lua
strncpy(str, persistentData->options[i].value.stringValue, LEN_ZONE_OPTION_STRING);
lua_pushstring(lsWidgets, &str[0]);
lua_settable(lsWidgets, -3);
} else if (option->type == ZoneOption::Color) {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, COLOR2FLAGS(value) | RGB_FLAG);
} else {
int32_t value = persistentData->options[i].value.signedValue;
l_pushtableint(option->name, value);
}
}
if (lua_pcall(lsWidgets, 2, 0, 0) != 0) {
setErrorMessage("update()");
}
}
void LuaWidget::setErrorMessage(const char * funcName)
{
TRACE("Error in widget %s %s function: %s", factory->getName(), funcName, lua_tostring(lsWidgets, -1));
TRACE("Widget disabled");
size_t needed = snprintf(NULL, 0, "ERROR in %s: %s", funcName, lua_tostring(lsWidgets, -1)) + 1;
errorMessage = (char *)malloc(needed + 1);
if (errorMessage) {
snprintf(errorMessage, needed, "ERROR in %s: %s", funcName, lua_tostring(lsWidgets, -1));
errorMessage[needed] = '\0';
}
}
const char * LuaWidget::getErrorMessage() const
{
return errorMessage;
}
void LuaWidget::refresh(BitmapBuffer* dc)
{
if (lsWidgets == 0) return;
if (errorMessage) {
drawTextLines(dc, 0, 0, fullscreen ? LCD_W : rect.w, fullscreen ? LCD_H : rect.h, errorMessage, FONT(XS) | COLOR_THEME_WARNING);
return;
}
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->refreshFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
// Pass key event to fullscreen Lua widget
eventData* es = &events[0];
if (fullscreen)
lua_pushinteger(lsWidgets, es->event);
else
lua_pushnil(lsWidgets);
#if defined(HARDWARE_TOUCH)
if (fullscreen && IS_TOUCH_EVENT(es->event)) {
lua_newtable(lsWidgets);
l_pushtableint("x", es->touchX);
l_pushtableint("y", es->touchY);
l_pushtableint("tapCount", es->tapCount);
if (es->event == EVT_TOUCH_SLIDE) {
l_pushtableint("startX", es->startX);
l_pushtableint("startY", es->startY);
l_pushtableint("slideX", es->slideX);
l_pushtableint("slideY", es->slideY);
// Do we have a swipe? Only one at a time!
if (get_tmr10ms() > swipeTimeOut) {
coord_t absX = (es->slideX < 0) ? -(es->slideX) : es->slideX;
coord_t absY = (es->slideY < 0) ? -(es->slideY) : es->slideY;
bool swiped = false;
if (absX > EVT_TOUCH_SWIPE_LOCK * absY) {
if ((swiped = (es->slideX > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeRight", true);
else if ((swiped = (es->slideX < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeLeft", true);
}
else if (absY > EVT_TOUCH_SWIPE_LOCK * absX) {
if ((swiped = (es->slideY > EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeDown", true);
else if ((swiped = (es->slideY < -EVT_TOUCH_SWIPE_SPEED)))
l_pushtablebool("swipeUp", true);
}
if (swiped)
swipeTimeOut = get_tmr10ms() + EVT_TOUCH_SWIPE_TIMEOUT;
}
}
} else
#endif
lua_pushnil(lsWidgets);
// Move the event buffer forward
for (int i = 1; i < EVENT_BUFFER_SIZE; i++)
events[i - 1] = events[i];
memclear(&events[EVENT_BUFFER_SIZE - 1], sizeof(eventData));
// Enable drawing into the current LCD buffer
luaLcdBuffer = dc;
// This little hack is needed to not interfere with the LCD usage of preempted scripts
bool lla = luaLcdAllowed;
luaLcdAllowed = true;
runningFS = this;
if (lua_pcall(lsWidgets, 3, 0, 0) != 0) {
setErrorMessage("refresh()");
}
runningFS = nullptr;
// Remove LCD
luaLcdAllowed = lla;
luaLcdBuffer = nullptr;
// mark as refreshed
refreshed = true;
}
void LuaWidget::background()
{
if (lsWidgets == 0 || errorMessage) return;
// TRACE("LuaWidget::background()");
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
if (factory->backgroundFunction) {
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->backgroundFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
runningFS = this;
if (lua_pcall(lsWidgets, 1, 0, 0) != 0) {
setErrorMessage("background()");
}
runningFS = nullptr;
}
}
#if defined(HARDWARE_KEYS)
void LuaWidget::onEvent(event_t event)
{
if (fullscreen) {
if (EVT_KEY_LONG(KEY_EXIT) == event) {
// Clear event buffer on full screen exit
memclear(&events, EVENT_BUFFER_SIZE * sizeof(eventData));
} else {
eventData* es = findOpenEventSlot();
if (es)
es->event = event;
}
}
Widget::onEvent(event);
}
#endif
#if defined(HARDWARE_TOUCH)
bool LuaWidget::onTouchStart(coord_t x, coord_t y)
{
TRACE_WINDOWS("LuaWidget received touch start (%d) x=%d;y=%d", hasFocus(), x, y);
// Only one EVT_TOUCH_FIRST at a time
if (fullscreen) {
if (!fingerDown) {
eventData* es = findOpenEventSlot();
if (es) {
es->event = EVT_TOUCH_FIRST;
es->touchX = x;
es->touchY = y;
}
fingerDown = true;
}
return true;
}
return Widget::onTouchStart(x, y);
}
bool LuaWidget::onTouchEnd(coord_t x, coord_t y)
{
TRACE_WINDOWS("LuaWidget received touch end (%d) x=%d;y=%d", hasFocus(), x, y);
if (fullscreen) {
eventData* es = findOpenEventSlot();
if (es) {
if (touchState.tapCount > 0) {
es->event = EVT_TOUCH_TAP;
es->tapCount = touchState.tapCount;
} else
es->event = EVT_TOUCH_BREAK;
es->touchX = x;
es->touchY = y;
}
fingerDown = false;
return true;
}
return Widget::onTouchEnd(x, y);
}
bool LuaWidget::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY)
{
TRACE_WINDOWS("LuaWidget touch slide");
if (fullscreen) {
// If we already have a SLIDE going, then accumulate slide values instead of allocating an empty slot
eventData* es = findOpenEventSlot(EVT_TOUCH_SLIDE);
if (es) {
ViewMain* vm = ViewMain::instance();
es->event = EVT_TOUCH_SLIDE;
// es->touchX = x + vm->getScrollPositionX();
// es->touchY = y + vm->getScrollPositionY();
es->startX = startX;
es->startY = startY;
es->slideX += slideX;
es->slideY += slideY;
}
fingerDown = false;
return true;
}
return Widget::onTouchSlide(x, y, startX, startY, slideX, slideY);
}
#endif
void luaLoadWidgetCallback()
{
TRACE("luaLoadWidgetCallback()");
const char * name=NULL;
int widgetOptions=0, createFunction=0, updateFunction=0, refreshFunction=0, backgroundFunction=0;
int widgetOptions = 0, createFunction = 0, updateFunction = 0,
refreshFunction = 0, backgroundFunction = 0;
luaL_checktype(lsWidgets, -1, LUA_TTABLE);
@@ -798,7 +367,6 @@ void luaInitThemesAndWidgets()
}
UNPROTECT_LUA();
TRACE("lsWidgets %p", lsWidgets);
//luaLoadFiles(THEMES_PATH, luaLoadThemeCallback);
luaLoadFiles(WIDGETS_PATH, luaLoadWidgetCallback);
luaDoGc(lsWidgets, true);
}
@@ -42,7 +42,7 @@ void watchdogSuspend(uint32_t timeout);
#include "opentx_types.h"
extern volatile tmr10ms_t g_tmr10ms;
extern "C" volatile tmr10ms_t g_tmr10ms;
static inline tmr10ms_t get_tmr10ms()
{
+2 -2
View File
@@ -178,8 +178,8 @@ static void simuRefreshLcd(lv_disp_drv_t * disp_drv, uint16_t *buffer, const rec
lv_area_t refr_area;
lv_area_copy(&refr_area, &disp->inv_areas[i]);
TRACE("{%d,%d,%d,%d}", refr_area.x1,
refr_area.y1, refr_area.x2, refr_area.y2);
// TRACE("{%d,%d,%d,%d}", refr_area.x1,
// refr_area.y1, refr_area.x2, refr_area.y2);
// _rotate_area_180(refr_area);
_copy_screen_area(dst, src, refr_area);