mirror of
https://github.com/opentx/opentx.git
synced 2025-07-23 16:25:16 +03:00
Lua: Use incremental GC and also call it for Widgets (#4369)
Fixes #3885: Error in Lua Widget options handled better (does not disable entire Lua state) Disable Lua Widget if any of its functions has error.
This commit is contained in:
parent
d594843de2
commit
b493973d7d
11 changed files with 213 additions and 89 deletions
|
@ -418,9 +418,15 @@ int cliMemoryInfo(const char ** argv)
|
|||
|
||||
#if defined(LUA)
|
||||
serialPrint("\nLua:");
|
||||
serialPrint("\tScripts %d", luaGetMemUsed(lsScripts));
|
||||
#if defined(PCBHORUS)
|
||||
serialPrint("\tWidgets %d", luaGetMemUsed(lsWidgets));
|
||||
uint32_t s = luaGetMemUsed(lsScripts);
|
||||
serialPrint("\tScripts %u", s);
|
||||
#if defined(COLORLCD)
|
||||
uint32_t w = luaGetMemUsed(lsWidgets);
|
||||
uint32_t e = luaExtraMemoryUsage;
|
||||
serialPrint("\tWidgets %u", w);
|
||||
serialPrint("\tExtra %u", e);
|
||||
serialPrint("------------");
|
||||
serialPrint("\tTotal %u", s + w + e);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
|
|
|
@ -79,6 +79,11 @@ class BitmapBufferBase
|
|||
return data;
|
||||
}
|
||||
|
||||
uint32_t getDataSize() const
|
||||
{
|
||||
return width * height * sizeof(T);
|
||||
}
|
||||
|
||||
inline const display_t * getPixelPtr(coord_t x, coord_t y) const
|
||||
{
|
||||
#if defined(PCBX10)
|
||||
|
|
|
@ -176,6 +176,23 @@ int getOptionsCount(const ZoneOption * options)
|
|||
template <class T>
|
||||
bool menuSettings(const char * title, const T * object, uint32_t i_flags, event_t event)
|
||||
{
|
||||
|
||||
if (object->getErrorMessage()) {
|
||||
// display error instead of widget settings
|
||||
// TODO nicer display (proper word-wrap)
|
||||
SIMPLE_SUBMENU("Widget Error", ICON_MODEL_LUA_SCRIPTS, 1);
|
||||
int len = strlen(object->getErrorMessage());
|
||||
int y = 3*FH;
|
||||
const char * p = object->getErrorMessage();
|
||||
while (len > 0) {
|
||||
lcdDrawSizedText(MENUS_MARGIN_LEFT, y, p, 30);
|
||||
p += 30;
|
||||
y += FH;
|
||||
len -= 30;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const ZoneOption * options = object->getOptions();
|
||||
linesCount = getOptionsCount(options);
|
||||
uint8_t mstate_tab[MAX_WIDGET_OPTIONS];
|
||||
|
|
|
@ -139,6 +139,16 @@ bool menuStatsDebug(event_t event)
|
|||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+line*FH, "Lua interval");
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+line*FH, 10*maxLuaInterval, LEFT, 0, NULL, "ms");
|
||||
++line;
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+line*FH, "Lua memory");
|
||||
lcdDrawText(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+line*FH+1, "[S]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+line*FH, luaGetMemUsed(lsScripts), LEFT);
|
||||
lcdDrawText(lcdNextPos+20, MENU_CONTENT_TOP+line*FH+1, "[W]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+line*FH, luaGetMemUsed(lsWidgets), LEFT);
|
||||
lcdDrawText(lcdNextPos+20, MENU_CONTENT_TOP+line*FH+1, "[B]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+line*FH, luaExtraMemoryUsage, LEFT);
|
||||
++line;
|
||||
|
||||
#endif
|
||||
|
||||
lcdDrawText(LCD_W/2, MENU_FOOTER_TOP+2, STR_MENUTORESET, CENTERED);
|
||||
|
|
|
@ -56,6 +56,11 @@ class Widget
|
|||
|
||||
inline const ZoneOption * getOptions() const;
|
||||
|
||||
virtual const char * getErrorMessage() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline ZoneOptionValue * getOptionValue(unsigned int index) const
|
||||
{
|
||||
return &persistentData->options[index];
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "stamp.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "lua_api.h"
|
||||
#include "telemetry/frsky.h"
|
||||
|
||||
#if defined(PCBX12S)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "lua_api.h"
|
||||
|
||||
/*luadoc
|
||||
@function lcd.refresh()
|
||||
|
@ -339,12 +339,16 @@ static int luaOpenBitmap(lua_State * L)
|
|||
|
||||
BitmapBuffer ** ptr = (BitmapBuffer **)lua_newuserdata(L, sizeof(BitmapBuffer *));
|
||||
*ptr = BitmapBuffer::load(filename);
|
||||
TRACE("luaOpenBitmap: %p", *ptr);
|
||||
|
||||
if (*ptr == NULL && G(L)->gcrunning) {
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
*ptr = BitmapBuffer::load(filename); /* try again */
|
||||
TRACE("luaOpenBitmap: %p (second try)", *ptr);
|
||||
}
|
||||
|
||||
if (*ptr) {
|
||||
uint32_t size = (*ptr)->getDataSize();
|
||||
luaExtraMemoryUsage += size;
|
||||
TRACE("luaOpenBitmap: %p (%u)", *ptr, size);
|
||||
}
|
||||
|
||||
luaL_getmetatable(L, LUA_BITMAPHANDLE);
|
||||
|
@ -389,7 +393,9 @@ static int luaGetBitmapSize(lua_State * L)
|
|||
static int luaDestroyBitmap(lua_State * L)
|
||||
{
|
||||
BitmapBuffer * ptr = checkBitmap(L, 1);
|
||||
TRACE("luaDestroyBitmap: %p", ptr);
|
||||
uint32_t size = ptr->getDataSize();
|
||||
TRACE("luaDestroyBitmap: %p (%u)", ptr, size);
|
||||
if (luaExtraMemoryUsage > size) luaExtraMemoryUsage -= size;
|
||||
delete ptr;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "lua_api.h"
|
||||
#include "timers.h"
|
||||
|
||||
/*luadoc
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "bin_allocator.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "lua_api.h"
|
||||
#include "sdcard.h"
|
||||
|
||||
extern "C" {
|
||||
|
@ -47,11 +47,14 @@ bool luaLcdAllowed;
|
|||
int instructionsPercent = 0;
|
||||
char lua_warning_info[LUA_WARNING_INFO_LEN+1];
|
||||
struct our_longjmp * global_lj = 0;
|
||||
#if defined(COLORLCD)
|
||||
uint32_t luaExtraMemoryUsage = 0;
|
||||
#endif
|
||||
|
||||
/* custom panic handler */
|
||||
int custom_lua_atpanic(lua_State * L)
|
||||
{
|
||||
TRACE("PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1));
|
||||
TRACE("PANIC: unprotected error in call to Lua API (%s)", lua_tostring(L, -1));
|
||||
if (global_lj) {
|
||||
longjmp(global_lj->b, 1);
|
||||
/* will never return */
|
||||
|
@ -66,7 +69,7 @@ void luaHook(lua_State * L, lua_Debug *ar)
|
|||
// From now on, as soon as a line is executed, error
|
||||
// keep erroring until you're script reaches the top
|
||||
lua_sethook(L, luaHook, LUA_MASKLINE, 0);
|
||||
luaL_error(L, "");
|
||||
luaL_error(L, "CPU limit");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,23 +180,45 @@ void luaRegisterLibraries(lua_State * L)
|
|||
#endif
|
||||
}
|
||||
|
||||
void luaDoGc(lua_State * L)
|
||||
#define GC_REPORT_TRESHOLD (2*1024)
|
||||
|
||||
void luaDoGc(lua_State * L, bool full)
|
||||
{
|
||||
if (L) {
|
||||
PROTECT_LUA() {
|
||||
if (full) {
|
||||
lua_gc(L, LUA_GCCOLLECT, 0);
|
||||
#if defined(SIMU) || defined(DEBUG)
|
||||
static int lastgc = 0;
|
||||
int gc = luaGetMemUsed(L);
|
||||
if (gc != lastgc) {
|
||||
lastgc = gc;
|
||||
TRACE("GC Use: %dbytes", gc);
|
||||
}
|
||||
else {
|
||||
lua_gc(L, LUA_GCSTEP, 10);
|
||||
}
|
||||
#if defined(SIMU) || defined(DEBUG)
|
||||
if (L == lsScripts) {
|
||||
static uint32_t lastgcSctipts = 0;
|
||||
uint32_t gc = luaGetMemUsed(L);
|
||||
if (gc > (lastgcSctipts + GC_REPORT_TRESHOLD) || (gc + GC_REPORT_TRESHOLD) < lastgcSctipts) {
|
||||
lastgcSctipts = gc;
|
||||
TRACE("GC Use Scripts: %u bytes", gc);
|
||||
}
|
||||
}
|
||||
#if defined(COLORLCD)
|
||||
if (L == lsWidgets) {
|
||||
static uint32_t lastgcWidgets = 0;
|
||||
uint32_t gc = luaGetMemUsed(L);
|
||||
if (gc > (lastgcWidgets + GC_REPORT_TRESHOLD) || (gc + GC_REPORT_TRESHOLD) < lastgcWidgets) {
|
||||
lastgcWidgets = gc;
|
||||
TRACE("GC Use Widgets: %u bytes + Extra %u", gc, luaExtraMemoryUsage);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// we disable Lua for the rest of the session
|
||||
luaDisable();
|
||||
if (L == lsScripts) luaDisable();
|
||||
#if defined(COLORLCD)
|
||||
if (L == lsWidgets) lsWidgets = 0;
|
||||
#endif
|
||||
}
|
||||
UNPROTECT_LUA();
|
||||
}
|
||||
|
@ -216,7 +241,7 @@ void luaFree(lua_State * L, ScriptInternalData & sid)
|
|||
}
|
||||
UNPROTECT_LUA();
|
||||
|
||||
luaDoGc(L);
|
||||
luaDoGc(L, true);
|
||||
}
|
||||
|
||||
#if defined(LUA_COMPILER)
|
||||
|
@ -494,7 +519,7 @@ static int luaLoad(lua_State * L, const char * filename, ScriptInternalData & si
|
|||
luaFree(L, sid);
|
||||
}
|
||||
|
||||
luaDoGc(L);
|
||||
luaDoGc(L, true);
|
||||
|
||||
return sid.state;
|
||||
}
|
||||
|
@ -922,11 +947,14 @@ bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage)
|
|||
//todo gc step between scripts
|
||||
}
|
||||
}
|
||||
luaDoGc(lsScripts);
|
||||
luaDoGc(lsScripts, false);
|
||||
#if defined(COLORLCD)
|
||||
luaDoGc(lsWidgets, false);
|
||||
#endif
|
||||
return scriptWasRun;
|
||||
}
|
||||
|
||||
int luaGetMemUsed(lua_State * L)
|
||||
uint32_t luaGetMemUsed(lua_State * L)
|
||||
{
|
||||
return L ? (lua_gc(L, LUA_GCCOUNT, 0) << 10) + lua_gc(L, LUA_GCCOUNTB, 0) : 0;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ extern "C" {
|
|||
extern lua_State * lsScripts;
|
||||
extern lua_State * lsWidgets;
|
||||
extern bool luaLcdAllowed;
|
||||
#if defined(COLORLCD)
|
||||
extern uint32_t luaExtraMemoryUsage;
|
||||
#endif
|
||||
|
||||
void luaInit();
|
||||
void luaInitThemesAndWidgets();
|
||||
|
@ -116,8 +119,9 @@ extern ScriptInputsOutputs scriptInputsOutputs[MAX_SCRIPTS];
|
|||
void luaClose(lua_State ** L);
|
||||
bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage);
|
||||
void luaExec(const char * filename);
|
||||
void luaDoGc(lua_State * L, bool full);
|
||||
void luaError(lua_State * L, uint8_t error, bool acknowledge=true);
|
||||
int luaGetMemUsed(lua_State * L);
|
||||
uint32_t luaGetMemUsed(lua_State * L);
|
||||
void luaGetValueAndPush(lua_State * L, int src);
|
||||
#define luaGetCpuUsed(idx) scriptInternalData[idx].instructions
|
||||
uint8_t isTelemetryScriptAvailable(uint8_t index);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
#include "bin_allocator.h"
|
||||
#include "lua/lua_api.h"
|
||||
#include "lua_api.h"
|
||||
|
||||
#define WIDGET_SCRIPTS_MAX_INSTRUCTIONS (10000/100)
|
||||
#define MANUAL_SCRIPTS_MAX_INSTRUCTIONS (20000/100)
|
||||
|
@ -42,6 +42,7 @@ void exec(int function, int nresults=0)
|
|||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, function);
|
||||
if (lua_pcall(lsWidgets, 0, nresults, 0) != 0) {
|
||||
TRACE("Error in theme %s", lua_tostring(lsWidgets, -1));
|
||||
// TODO disable theme - revert back to default theme???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ ZoneOption * createOptionsArray(int reference)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PROTECT_LUA() {
|
||||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, reference);
|
||||
ZoneOption * option = options;
|
||||
for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2); lua_pop(lsWidgets, 1)) {
|
||||
|
@ -100,9 +102,14 @@ ZoneOption * createOptionsArray(int reference)
|
|||
}
|
||||
option++;
|
||||
}
|
||||
|
||||
option->name = NULL; // sentinel
|
||||
|
||||
}
|
||||
else {
|
||||
TRACE("error in theme/widget options");
|
||||
free(options);
|
||||
return NULL;
|
||||
}
|
||||
UNPROTECT_LUA();
|
||||
return options;
|
||||
}
|
||||
|
||||
|
@ -111,8 +118,8 @@ class LuaTheme: public Theme
|
|||
friend void luaLoadThemeCallback();
|
||||
|
||||
public:
|
||||
LuaTheme(const char * name, int options):
|
||||
Theme(name, createOptionsArray(options)),
|
||||
LuaTheme(const char * name, ZoneOption * options):
|
||||
Theme(name, options),
|
||||
loadFunction(0),
|
||||
drawBackgroundFunction(0),
|
||||
drawTopbarBackgroundFunction(0),
|
||||
|
@ -182,36 +189,46 @@ void luaLoadThemeCallback()
|
|||
}
|
||||
|
||||
if (name) {
|
||||
LuaTheme * theme = new LuaTheme(name, themeOptions);
|
||||
ZoneOption * options = createOptionsArray(themeOptions);
|
||||
if (options) {
|
||||
LuaTheme * theme = new LuaTheme(name, options);
|
||||
theme->loadFunction = loadFunction;
|
||||
theme->drawBackgroundFunction = drawBackgroundFunction;
|
||||
theme->drawTopbarBackgroundFunction = drawTopbarBackgroundFunction;
|
||||
TRACE("Loaded Lua theme %s", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LuaWidget: public Widget
|
||||
{
|
||||
public:
|
||||
LuaWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData, int widgetData):
|
||||
Widget(factory, zone, persistentData),
|
||||
widgetData(widgetData)
|
||||
widgetData(widgetData),
|
||||
errorMessage(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~LuaWidget()
|
||||
{
|
||||
luaL_unref(lsWidgets, LUA_REGISTRYINDEX, widgetData);
|
||||
if (errorMessage) free(errorMessage);
|
||||
}
|
||||
|
||||
virtual void update() const;
|
||||
virtual void update();
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
virtual void background();
|
||||
|
||||
virtual const char * getErrorMessage() const;
|
||||
|
||||
protected:
|
||||
int widgetData;
|
||||
char * errorMessage;
|
||||
|
||||
void setErrorMessage(const char * funcName);
|
||||
};
|
||||
|
||||
void l_pushtableint(const char * key, int value)
|
||||
|
@ -227,8 +244,8 @@ class LuaWidgetFactory: public WidgetFactory
|
|||
friend class LuaWidget;
|
||||
|
||||
public:
|
||||
LuaWidgetFactory(const char * name, int widgetOptions, int createFunction):
|
||||
WidgetFactory(name, createOptionsArray(widgetOptions)),
|
||||
LuaWidgetFactory(const char * name, ZoneOption * widgetOptions, int createFunction):
|
||||
WidgetFactory(name, widgetOptions),
|
||||
createFunction(createFunction),
|
||||
updateFunction(0),
|
||||
refreshFunction(0),
|
||||
|
@ -273,9 +290,9 @@ class LuaWidgetFactory: public WidgetFactory
|
|||
int backgroundFunction;
|
||||
};
|
||||
|
||||
void LuaWidget::update() const
|
||||
void LuaWidget::update()
|
||||
{
|
||||
if (lsWidgets == 0) return;
|
||||
if (lsWidgets == 0 || errorMessage) return;
|
||||
|
||||
luaSetInstructionsLimit(lsWidgets, WIDGET_SCRIPTS_MAX_INSTRUCTIONS);
|
||||
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
|
||||
|
@ -289,26 +306,48 @@ void LuaWidget::update() const
|
|||
}
|
||||
|
||||
if (lua_pcall(lsWidgets, 2, 0, 0) != 0) {
|
||||
TRACE("Error in widget %s update() function: %s", factory->getName(), lua_tostring(lsWidgets, -1));
|
||||
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, "%s: %s", funcName, lua_tostring(lsWidgets, -1)) + 1;
|
||||
errorMessage = (char *)malloc(needed);
|
||||
if (errorMessage) {
|
||||
snprintf(errorMessage, needed, "%s: %s", funcName, lua_tostring(lsWidgets, -1));
|
||||
}
|
||||
}
|
||||
|
||||
const char * LuaWidget::getErrorMessage() const
|
||||
{
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
void LuaWidget::refresh()
|
||||
{
|
||||
if (lsWidgets == 0) return;
|
||||
|
||||
if (errorMessage) {
|
||||
lcdSetColor(RED);
|
||||
lcdDrawText(zone.x, zone.y, "Disabled", SMLSIZE|CUSTOM_COLOR);
|
||||
return;
|
||||
}
|
||||
|
||||
luaSetInstructionsLimit(lsWidgets, WIDGET_SCRIPTS_MAX_INSTRUCTIONS);
|
||||
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
|
||||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->refreshFunction);
|
||||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, widgetData);
|
||||
if (lua_pcall(lsWidgets, 1, 0, 0) != 0) {
|
||||
TRACE("Error in widget %s refresh() function: %s", factory->getName(), lua_tostring(lsWidgets, -1));
|
||||
setErrorMessage("refresh()");
|
||||
}
|
||||
}
|
||||
|
||||
void LuaWidget::background()
|
||||
{
|
||||
if (lsWidgets == 0) return;
|
||||
if (lsWidgets == 0 || errorMessage) return;
|
||||
|
||||
luaSetInstructionsLimit(lsWidgets, WIDGET_SCRIPTS_MAX_INSTRUCTIONS);
|
||||
LuaWidgetFactory * factory = (LuaWidgetFactory *)this->factory;
|
||||
|
@ -316,7 +355,7 @@ void LuaWidget::background()
|
|||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->backgroundFunction);
|
||||
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, widgetData);
|
||||
if (lua_pcall(lsWidgets, 1, 0, 0) != 0) {
|
||||
TRACE("Error in widget %s background() function: %s", factory->getName(), lua_tostring(lsWidgets, -1));
|
||||
setErrorMessage("background()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,13 +396,16 @@ void luaLoadWidgetCallback()
|
|||
}
|
||||
|
||||
if (name && createFunction) {
|
||||
LuaWidgetFactory * factory = new LuaWidgetFactory(name, widgetOptions, createFunction);
|
||||
ZoneOption * options = createOptionsArray(widgetOptions);
|
||||
if (options) {
|
||||
LuaWidgetFactory * factory = new LuaWidgetFactory(name, options, createFunction);
|
||||
factory->updateFunction = updateFunction;
|
||||
factory->refreshFunction = refreshFunction;
|
||||
factory->backgroundFunction = backgroundFunction;
|
||||
TRACE("Loaded Lua widget %s", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void luaLoadFile(const char * filename, void (*callback)())
|
||||
{
|
||||
|
@ -385,8 +427,8 @@ void luaLoadFile(const char * filename, void (*callback)())
|
|||
}
|
||||
}
|
||||
else {
|
||||
// luaDisable();
|
||||
lsWidgets = 0;
|
||||
// error while loading Lua widget/theme,
|
||||
// do not disable whole Lua state, just ingnore bad widget/theme
|
||||
return;
|
||||
}
|
||||
UNPROTECT_LUA();
|
||||
|
@ -453,5 +495,6 @@ void luaInitThemesAndWidgets()
|
|||
TRACE("lsWidgets %p", lsWidgets);
|
||||
luaLoadFiles(THEMES_PATH, luaLoadThemeCallback);
|
||||
luaLoadFiles(WIDGETS_PATH, luaLoadWidgetCallback);
|
||||
luaDoGc(lsWidgets, true);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue