fix(color): widget Lua 'update' function may give unexpected error (#7119)

This commit is contained in:
philmoz
2026-02-22 09:49:58 +10:00
committed by GitHub
parent 044c2a08d8
commit 653a2013ea
10 changed files with 102 additions and 57 deletions
+2 -3
View File
@@ -203,8 +203,6 @@ void Widget::onCancel()
if (!fullscreen) ButtonBase::onCancel();
}
void Widget::update() {}
void Widget::setFullscreen(bool enable)
{
if (!!parent->isTopBar() || (enable == fullscreen)) return;
@@ -252,7 +250,8 @@ void Widget::setFullscreen(bool enable)
onFullscreen(enable);
update();
if (fullscreen)
updateWithoutRefresh();
}
bool Widget::onLongPress()
+2 -1
View File
@@ -106,7 +106,8 @@ class Widget : public ButtonBase
virtual bool enableFullScreenRE() const { return true; }
// Called when the widget options have changed
virtual void update();
virtual void updateWithoutRefresh() {}
virtual void update() {}
// Called at regular time interval if the widget is hidden or off screen
virtual void background() {}
@@ -220,6 +220,6 @@ WidgetSettings::WidgetSettings(Widget* w) :
void WidgetSettings::onCancel()
{
widget->update();
widget->updateWithoutRefresh();
deleteLater();
}
+2 -2
View File
@@ -196,8 +196,8 @@ void StandaloneLuaWindow::deleteLater()
{
if (_deleted) return;
if (initFunction != LUA_REFNIL) luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction);
if (runFunction != LUA_REFNIL) luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction);
luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction);
luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction);
luaLcdBuffer = nullptr;
luaClose(&lsStandalone);
+53 -31
View File
@@ -40,13 +40,28 @@
//-----------------------------------------------------------------------------
#if defined(DEBUG)
int32_t refCnt = 0;
#endif
static void clearRef(lua_State *L, int& ref)
{
#if defined(DEBUG)
if (ref != LUA_REFNIL)
luaL_unref(L, LUA_REGISTRYINDEX, ref);
refCnt -= 1;
#endif
luaL_unref(L, LUA_REGISTRYINDEX, ref);
ref = LUA_REFNIL;
}
static int getRef(lua_State *L, int t)
{
#if defined(DEBUG)
refCnt += 1;
#endif
return luaL_ref(L, t);
}
static bool getLuaBool(lua_State *L)
{
if (lua_isboolean(L, -1))
@@ -59,7 +74,7 @@ static bool getLuaBool(lua_State *L)
void LvglParamFuncOrValue::parse(lua_State *L)
{
if (lua_isfunction(L, -1)) {
function = luaL_ref(L, LUA_REGISTRYINDEX);
function = ::getRef(L, LUA_REGISTRYINDEX);
} else if (lua_isboolean(L, -1)) {
value = lua_toboolean(L, -1);
} else {
@@ -95,7 +110,7 @@ void LvglParamFuncOrValue::clearRef(lua_State *L)
void LvglParamFuncOrString::parse(lua_State *L)
{
if (lua_isfunction(L, -1)) {
function = luaL_ref(L, LUA_REGISTRYINDEX);
function = ::getRef(L, LUA_REGISTRYINDEX);
} else {
txt = luaL_checkstring(L, -1);
}
@@ -122,11 +137,11 @@ void LvglParamFuncOrString::clearRef(lua_State *L)
bool LvglGetSetParams::parseGetSetParam(lua_State *L, const char *key)
{
if (!strcmp(key, "get")) {
getFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getFunction = ::getRef(L, LUA_REGISTRYINDEX);
return true;
}
if (!strcmp(key, "set")) {
setFunction = luaL_ref(L, LUA_REGISTRYINDEX);
setFunction = ::getRef(L, LUA_REGISTRYINDEX);
return true;
}
return false;
@@ -248,16 +263,22 @@ bool LvglScrollableParams::parseScrollableParam(lua_State *L, const char *key)
return true;
}
if (!strcmp(key, "scrollTo")) {
scrollToFunction = luaL_ref(L, LUA_REGISTRYINDEX);
scrollToFunction = ::getRef(L, LUA_REGISTRYINDEX);
return true;
}
if (!strcmp(key, "scrolled")) {
scrolledFunction = luaL_ref(L, LUA_REGISTRYINDEX);
scrolledFunction = ::getRef(L, LUA_REGISTRYINDEX);
return true;
}
return false;
}
void LvglScrollableParams::clearScrollableRefs(lua_State *L)
{
clearRef(L, scrollToFunction);
clearRef(L, scrolledFunction);
}
//-----------------------------------------------------------------------------
static int pcallcont (lua_State *L, int status, lua_KContext extra) {
@@ -340,7 +361,7 @@ int LvglWidgetObjectBase::getRef(lua_State *L)
lua_setmetatable(L, -2);
// Save reference
luaRef = luaL_ref(L, LUA_REGISTRYINDEX);
luaRef = ::getRef(L, LUA_REGISTRYINDEX);
lvglManager->saveLvglObjectRef(luaRef);
return luaRef;
@@ -627,11 +648,11 @@ void LvglWidgetObjectBase::parseParam(lua_State *L, const char *key)
} else if (!strcmp(key, "opacity")) {
opacity.parse(L);
} else if (!strcmp(key, "visible")) {
getVisibleFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getVisibleFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "size")) {
getSizeFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getSizeFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "pos")) {
getPosFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getPosFunction = ::getRef(L, LUA_REGISTRYINDEX);
}
}
@@ -991,7 +1012,7 @@ void LvglWidgetLine::parseParam(lua_State *L, const char *key)
if (parseThicknessParam(L, key)) return;
if (!strcmp(key, "pts")) {
if (lua_isfunction(L, -1)) {
getPointsFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getPointsFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
ptsHash = getPts(L);
}
@@ -1134,7 +1155,7 @@ void LvglWidgetTriangle::parseParam(lua_State *L, const char *key)
{
if (!strcmp(key, "pts")) {
if (lua_isfunction(L, -1)) {
getPointsFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getPointsFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
luaL_checktype(L, -1, LUA_TTABLE);
getPt(L, 0);
@@ -1426,7 +1447,7 @@ void LvglWidgetObject::parseParam(lua_State *L, const char *key)
}
}
} else if (!strcmp(key, "active")) {
getActiveFunction = luaL_ref(L, LUA_REGISTRYINDEX);
getActiveFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetObjectBase::parseParam(L, key);
}
@@ -1520,7 +1541,7 @@ bool LvglWidgetBox::callRefs(lua_State *L)
void LvglWidgetBox::clearRefs(lua_State *L)
{
clearAlignRefs(L);
clearRef(L, scrollToFunction);
clearScrollableRefs(L);
LvglWidgetObject::clearRefs(L);
}
@@ -1948,7 +1969,7 @@ void LvglWidgetTextButtonBase::parseParam(lua_State *L, const char *key)
} else if (!strcmp(key, "textColor")) {
textColor.parse(L);
} else if (!strcmp(key, "press")) {
pressFunction = luaL_ref(L, LUA_REGISTRYINDEX);
pressFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!parseTextParam(L, key)) {
LvglWidgetObject::parseParam(L, key);
}
@@ -2013,7 +2034,7 @@ void LvglWidgetTextButton::parseParam(lua_State *L, const char *key)
if (!strcmp(key, "checked")) {
checked = getLuaBool(L);
} else if (!strcmp(key, "longpress")) {
longPressFunction = luaL_ref(L, LUA_REGISTRYINDEX);
longPressFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetTextButtonBase::parseParam(L, key);
}
@@ -2069,7 +2090,7 @@ void LvglWidgetTextButton::build(lua_State *L)
void LvglWidgetMomentaryButton::parseParam(lua_State *L, const char *key)
{
if (!strcmp(key, "release")) {
releaseFunction = luaL_ref(L, LUA_REGISTRYINDEX);
releaseFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetTextButtonBase::parseParam(L, key);
}
@@ -2145,7 +2166,7 @@ void LvglWidgetTextEdit::parseParam(lua_State *L, const char *key)
if (maxLen > 128) maxLen = 128;
if (maxLen <= 0) maxLen = 32;
} else if (!strcmp(key, "set")) {
setFunction = luaL_ref(L, LUA_REGISTRYINDEX);
setFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetObject::parseParam(L, key);
}
@@ -2190,9 +2211,9 @@ void LvglWidgetNumberEdit::parseParam(lua_State *L, const char *key)
if (parseGetSetParam(L, key)) return;
if (!strcmp(key, "display")) {
dispFunction = luaL_ref(L, LUA_REGISTRYINDEX);
dispFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "edited")) {
editedFunction = luaL_ref(L, LUA_REGISTRYINDEX);
editedFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetObject::parseParam(L, key);
}
@@ -2401,18 +2422,18 @@ void LvglWidgetPage::parseParam(lua_State *L, const char *key)
if (parseTitleParam(L, key)) return;
if (parseScrollableParam(L, key)) return;
if (!strcmp(key, "back")) {
backActionFunction = luaL_ref(L, LUA_REGISTRYINDEX);
backActionFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "menu")) {
menuActionFunction = luaL_ref(L, LUA_REGISTRYINDEX);
menuActionFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "prevButton")) {
luaL_checktype(L, -1, LUA_TTABLE);
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
const char *key = lua_tostring(L, -2);
if (!strcmp(key, "press")) {
prevActionFunction = luaL_ref(L, LUA_REGISTRYINDEX);
prevActionFunction = ::getRef(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
} else if (!strcmp(key, "active")) {
prevActiveFunction = luaL_ref(L, LUA_REGISTRYINDEX);
prevActiveFunction = ::getRef(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
}
}
@@ -2421,10 +2442,10 @@ void LvglWidgetPage::parseParam(lua_State *L, const char *key)
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
const char *key = lua_tostring(L, -2);
if (!strcmp(key, "press")) {
nextActionFunction = luaL_ref(L, LUA_REGISTRYINDEX);
nextActionFunction = ::getRef(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
} else if (!strcmp(key, "active")) {
nextActiveFunction = luaL_ref(L, LUA_REGISTRYINDEX);
nextActiveFunction = ::getRef(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
}
}
@@ -2483,6 +2504,7 @@ void LvglWidgetPage::clearRefs(lua_State *L)
{
clearAlignRefs(L);
clearTitleRefs(L);
clearScrollableRefs(L);
subtitle.clearRef(L);
clearRef(L, backActionFunction);
clearRef(L, menuActionFunction);
@@ -2548,7 +2570,7 @@ void LvglWidgetDialog::parseParam(lua_State *L, const char *key)
{
if (parseTitleParam(L, key)) return;
if (!strcmp(key, "close")) {
closeFunction = luaL_ref(L, LUA_REGISTRYINDEX);
closeFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetObject::parseParam(L, key);
}
@@ -2586,9 +2608,9 @@ void LvglWidgetConfirmDialog::parseParam(lua_State *L, const char *key)
if (parseTitleParam(L, key)) return;
if (parseMessageParam(L, key)) return;
if (!strcmp(key, "confirm")) {
confirmFunction = luaL_ref(L, LUA_REGISTRYINDEX);
confirmFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "cancel")) {
cancelFunction = luaL_ref(L, LUA_REGISTRYINDEX);
cancelFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else {
LvglWidgetObject::parseParam(L, key);
}
@@ -2655,7 +2677,7 @@ void LvglWidgetChoice::parseParam(lua_State *L, const char *key)
if (parseTitleParam(L, key)) return;
if (parseValuesParam(L, key)) return;
if (!strcmp(key, "filter")) {
filterFunction = luaL_ref(L, LUA_REGISTRYINDEX);
filterFunction = ::getRef(L, LUA_REGISTRYINDEX);
} else if (!strcmp(key, "popupWidth")) {
popupWidth = luaL_checkinteger(L, -1);
} else {
+1
View File
@@ -231,6 +231,7 @@ class LvglScrollableParams
int scrolledFunction = LUA_REFNIL;
bool parseScrollableParam(lua_State *L, const char *key);
void clearScrollableRefs(lua_State *L);
};
//-----------------------------------------------------------------------------
+28 -14
View File
@@ -251,7 +251,7 @@ LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent,
if (lua_pcall(lsWidgets, 3, 1, 0) == LUA_OK) {
luaScriptContextRef = luaL_ref(lsWidgets, LUA_REGISTRYINDEX);
} else {
luaScriptContextRef = LUA_NOREF;
luaScriptContextRef = LUA_REFNIL;
setErrorMessage("create()");
}
@@ -267,9 +267,9 @@ LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent,
LuaWidget::~LuaWidget()
{
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, luaScriptContextRef);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, zoneRectDataRef);
free(errorMessage);
if (errorMessage)
free(errorMessage);
}
void LuaWidget::onClicked()
@@ -308,12 +308,12 @@ void LuaWidget::foreground()
refresh(nullptr);
if (!errorMessage) {
if (!callRefs(lsWidgets)) {
setErrorMessage("function");
setErrorMessage("foreground calRefs error");
}
}
refreshInstructionsPercent = instructionsPercent;
} else {
setErrorMessage("function");
setErrorMessage("foreground protect Lua error");
}
luaScriptManager = save;
UNPROTECT_LUA();
@@ -328,11 +328,11 @@ void LuaWidget::foreground()
#endif
}
void LuaWidget::update()
void LuaWidget::updateWithoutRefresh()
{
Widget::update();
Widget::updateWithoutRefresh();
if (lsWidgets == 0 || errorMessage) return;
if (lsWidgets == 0 || errorMessage || luaFactory()->updateFunction == LUA_REFNIL) return;
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->updateFunction);
@@ -366,6 +366,18 @@ void LuaWidget::update()
if (lua_pcall(lsWidgets, 2, 0, 0) != 0)
setErrorMessage("update()");
luaScriptManager = save;
}
void LuaWidget::update()
{
updateWithoutRefresh();
Widget::update();
auto save = luaScriptManager;
luaScriptManager = this;
if (useLvglLayout()) {
if (!lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN)) {
lv_area_t a;
@@ -374,10 +386,10 @@ void LuaWidget::update()
if (a.x2 >= 0 && a.x1 < LCD_W) {
PROTECT_LUA() {
if (!callRefs(lsWidgets)) {
setErrorMessage("function");
setErrorMessage("update callRefs error");
}
} else {
setErrorMessage("function");
setErrorMessage("update protect Lua error");
}
UNPROTECT_LUA();
}
@@ -429,7 +441,7 @@ void LuaWidget::updateZoneRect(rect_t rect, bool updateUI)
lua_pop(lsWidgets, 1);
if (changed && updateUI)
update();
updateWithoutRefresh();
}
}
@@ -452,7 +464,8 @@ void LuaWidget::setErrorMessage(const char* funcName)
lua_err);
TRACE("Widget disabled");
errorMessage = (char*)malloc(err_len + 1);
if (!errorMessage)
errorMessage = (char*)malloc(err_len + 1);
if (errorMessage) {
snprintf(errorMessage, err_len, "ERROR in %s: %s", funcName, lua_err);
@@ -469,7 +482,7 @@ const char* LuaWidget::getErrorMessage() const { return errorMessage; }
void LuaWidget::refresh(BitmapBuffer* dc)
{
if (lsWidgets == 0) return;
if (lsWidgets == 0 || luaFactory()->refreshFunction == LUA_REFNIL) return;
if (errorMessage) {
if (dc) {
@@ -531,7 +544,7 @@ void LuaWidget::background()
{
if (lsWidgets == 0 || errorMessage) return;
if (luaFactory()->backgroundFunction) {
if (luaFactory()->backgroundFunction != LUA_REFNIL) {
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->backgroundFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaScriptContextRef);
@@ -605,6 +618,7 @@ void LuaScriptManager::createTelemetryQueue()
LuaScriptManager::~LuaScriptManager()
{
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, luaScriptContextRef);
if (luaInputTelemetryFifo != nullptr) {
deregisterTelemetryQueue(luaInputTelemetryFifo);
delete luaInputTelemetryFifo;
+2 -1
View File
@@ -94,7 +94,7 @@ class LuaScriptManager : public LuaEventHandler
TelemetryQueue* telemetryQueue() { return luaInputTelemetryFifo; }
protected:
int luaScriptContextRef = 0;
int luaScriptContextRef = LUA_REFNIL;
std::vector<int> lvglObjectRefs;
LvglWidgetObjectBase* tempParent = nullptr;
#if defined(LUA)
@@ -118,6 +118,7 @@ class LuaWidget : public Widget, public LuaScriptManager
// Widget interface
const char* getErrorMessage() const override;
void updateWithoutRefresh() override;
void update() override;
void background() override;
void foreground() override;
+8 -1
View File
@@ -49,6 +49,13 @@ LuaWidgetFactory::LuaWidgetFactory(const char* name, WidgetOption* widgetOptions
LuaWidgetFactory::~LuaWidgetFactory() {
unregisterWidget(this);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, optionDefinitionsReference);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, createFunction);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, updateFunction);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, refreshFunction);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, backgroundFunction);
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, translateFunction);
if (name) delete name;
if (displayName) delete displayName;
@@ -108,7 +115,7 @@ void LuaWidgetFactory::translateOptions(WidgetOption * options)
if (lsWidgets == 0) return;
// No translations provided
if (!translateFunction) return;
if (translateFunction == LUA_REFNIL) return;
#if defined(ALL_LANGS)
char lang[3];
+3 -3
View File
@@ -103,8 +103,8 @@ void luaLoadWidgetCallback(const char* filename)
TRACE("luaLoadWidgetCallback()");
const char * name=NULL;
int optionDefinitionsReference = LUA_REFNIL, createFunction = 0, updateFunction = 0,
refreshFunction = 0, backgroundFunction = 0, translateFunction = 0;
int optionDefinitionsReference = LUA_REFNIL, createFunction = LUA_REFNIL, updateFunction = LUA_REFNIL,
refreshFunction = LUA_REFNIL, backgroundFunction = LUA_REFNIL, translateFunction = LUA_REFNIL;
bool lvglLayout = false;
luaL_checktype(lsWidgets, -1, LUA_TTABLE);
@@ -143,7 +143,7 @@ void luaLoadWidgetCallback(const char* filename)
}
}
if (name && createFunction) {
if (name && createFunction != LUA_REFNIL) {
WidgetOption * options = LuaWidgetFactory::parseOptionDefinitions(optionDefinitionsReference);
if (options) {
new LuaWidgetFactory(strdup(name), options, optionDefinitionsReference,