Support for touch/key events in LUA widgets
This commit is contained in:
committed by
Raphael Coeffic
parent
93797a3e14
commit
a3424d5bc0
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user