diff --git a/radio/src/debug.h b/radio/src/debug.h index 04c54c335..f3dcf5b3a 100644 --- a/radio/src/debug.h +++ b/radio/src/debug.h @@ -387,9 +387,8 @@ enum DebugTimers { debugTimerPerMain, debugTimerPerMain1, debugTimerGuiMain, - debugTimerLuaBg, + debugTimerLua, debugTimerLcdRefreshWait, - debugTimerLuaFg, debugTimerLcdRefresh, debugTimerMenus, debugTimerMenuHandlers, diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 16dd56461..783b68cf9 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -60,6 +60,7 @@ set(GUI_SRC menu_screen.cpp screen_setup.cpp switch_warn_dialog.cpp + standalone_lua.cpp ) macro(add_gui_src src) diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index fab0fa2dd..646b726a4 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -24,6 +24,7 @@ #include "io/frsky_firmware_update.h" #include "io/multi_firmware_update.h" #include "io/bootloader_flash.h" +#include "standalone_lua.h" RadioSdManagerPage::RadioSdManagerPage() : PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) @@ -247,6 +248,8 @@ void RadioSdManagerPage::build(FormWindow * window) else if (isExtensionMatching(ext, SCRIPTS_EXT)) { menu->addLine(STR_EXECUTE_FILE, [=]() { luaExec(getFullPath(name)); + //TODO: check 'luaState' + new StandaloneLuaWindow(window); }); } #endif diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp new file mode 100644 index 000000000..07fc3b8f3 --- /dev/null +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "standalone_lua.h" +#include "view_main.h" +#include "lua/lua_api.h" + +constexpr uint32_t STANDALONE_LUA_REFRESH = 1000 / 1; // 1Hz + +uint16_t* lcdGetBackupBuffer(); + +// #if !defined(SIMU) + +// // LUA script draw into the backup buffer +// extern uint8_t * LCD_BACKUP_FRAME_BUFFER; +// #define LUA_LCD_BUFFER ((uint16_t *)LCD_BACKUP_FRAME_BUFFER) + +// #else + +// extern pixel_t* simuLcdBuf; +// #define LUA_LCD_BUFFER ((uint16_t *)simuLcdBuf) + +// #endif + +StandaloneLuaWindow::StandaloneLuaWindow(Window* parent) : + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, OPAQUE), + lcdBuffer(BMP_RGB565, LCD_W, LCD_H, lcdGetBackupBuffer()) +{ + TRACE("backup frame buffer = %p", lcdGetBackupBuffer()); + TRACE("lcdBuffer.getData() = %p", lcdBuffer.getData()); + assert(lcdBuffer.getData()); + Layer::push(this); + setFocus(); +} + +void StandaloneLuaWindow::deleteLater(bool detach, bool trash) +{ + Layer::pop(this); + Window::deleteLater(detach, trash); +} + +void StandaloneLuaWindow::paint(BitmapBuffer* dc) +{ + dc->drawSolidFilledRect(0, 0, width(), height(), DEFAULT_BGCOLOR); + + // TODO: + // -> grab coords from 'dc' + // -> copy from backup buffer into 'dc' + dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), &lcdBuffer); +} + +void StandaloneLuaWindow::checkEvents() +{ + // Execute first in case onEvent() is called. + // (would trigger refresh) + Window::checkEvents(); + + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= STANDALONE_LUA_REFRESH) { + lastRefresh = now; + runLua(0); + } +} + +#if defined(HARDWARE_KEYS) +void StandaloneLuaWindow::onEvent(event_t evt) +{ + lastRefresh = RTOS_GET_MS(); // mark as 'refreshed' + runLua(evt); +} +#endif + +extern BitmapBuffer* luaLcdBuffer; + +void StandaloneLuaWindow::runLua(event_t evt) +{ + // Set global LUA LCD buffer + luaLcdBuffer = &lcdBuffer; + + TRACE("evt = 0x%x", evt); + + bool hasRun = luaTask(evt, RUN_STNDAL_SCRIPT, true); + if (hasRun) { +#if defined(DEBUG_WINDOWS) + TRACE("# StandaloneLuaWindow::invalidate()"); +#endif + invalidate(); + } else { + // Script does not run anymore... + TRACE("LUA standalone script exited: deleting window!"); + deleteLater(); + } + + // Kill global LUA LCD buffer + luaLcdBuffer = nullptr; +} diff --git a/radio/src/gui/colorlcd/standalone_lua.h b/radio/src/gui/colorlcd/standalone_lua.h new file mode 100644 index 000000000..5a0eaacbd --- /dev/null +++ b/radio/src/gui/colorlcd/standalone_lua.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * 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 "libopenui.h" + +class StandaloneLuaWindow : public Window +{ + public: + explicit StandaloneLuaWindow(Window* parent); + + void deleteLater(bool detach=true, bool trash=true) override; + void paint(BitmapBuffer* dc) override; + void checkEvents() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() const override { return "StandaloneLuaWindow"; } +#endif + +#if defined(HARDWARE_KEYS) + void onEvent(event_t evt) override; +#endif + + protected: + BitmapBuffer lcdBuffer; + uint32_t lastRefresh = 0; + + void runLua(event_t evt); +}; diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index be9ae136d..bbef3cbf3 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -22,21 +22,25 @@ #include #include "opentx.h" #include "lua_api.h" +#include "libopenui.h" + +// TODO: obsolete definition +#define INVERS 0 + +BitmapBuffer* luaLcdBuffer = nullptr; /*luadoc @function lcd.refresh() Refresh the LCD screen -@status current Introduced in 2.2.0 - -@notice This function only works in stand-alone and telemetry scripts. +@status current Obsoleted in 2.4.0 */ static int luaLcdRefresh(lua_State *L) { -#if 0 - if (luaLcdAllowed) lcdRefresh(); -#endif + // This one does not seem to be used by any script + // (at least none of the opentx LUA scripts) + // we cannot possibly force a re-draw here return 0; } @@ -53,16 +57,10 @@ Clear the LCD screen */ static int luaLcdClear(lua_State * L) { -#if 0 - if (luaLcdAllowed) { -#if defined(COLORLCD) - LcdFlags color = luaL_optunsigned(L, 1, TEXT_BGCOLOR); - lcd->clear(color); -#else - lcdClear(); -#endif + if (luaLcdAllowed && luaLcdBuffer) { + LcdFlags color = luaL_optunsigned(L, 1, DEFAULT_BGCOLOR); + luaLcdBuffer->clear(color); } -#endif return 0; } @@ -100,14 +98,14 @@ bottom line is 63. Drawing on an existing black pixel produces white pixel (TODO */ static int luaLcdDrawPoint(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); LcdFlags att = luaL_optunsigned(L, 3, 0); - lcdDrawPoint(x, y, att); -#endif + luaLcdBuffer->drawPixel(x, y, lcdColorTable[COLOR_IDX(att)]); + return 0; } @@ -131,9 +129,9 @@ whole line will not be drawn (starting from OpenTX 2.1.5) */ static int luaLcdDrawLine(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + coord_t x1 = luaL_checkunsigned(L, 1); coord_t y1 = luaL_checkunsigned(L, 2); coord_t x2 = luaL_checkunsigned(L, 3); @@ -146,17 +144,17 @@ static int luaLcdDrawLine(lua_State *L) if (pat == SOLID) { if (x1 == x2) { - lcdDrawSolidVerticalLine(x1, y1drawSolidVerticalLine(x1, y1drawSolidHorizontalLine(x1drawLine(x1, y1, x2, y2, pat, flags); + return 0; } @@ -185,18 +183,19 @@ See the [Appendix](../appendix/fonts.md) for available characters in each font s */ static int luaLcdDrawText(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); const char * s = luaL_checkstring(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); - #if defined(COLORLCD) - if ((att&SHADOWED) && !(att&INVERS)) lcdDrawText(x+1, y+1, s, att&0xFFFF); - #endif - lcdDrawText(x, y, s, att); -#endif + + if ((att&SHADOWED) && !(att&INVERS)) { + luaLcdBuffer->drawText(x+1, y+1, s, att&0xFFFF); + } + luaLcdBuffer->drawText(x, y, s, att); + return 0; } @@ -219,20 +218,21 @@ Display a value formatted as time at (x,y) */ static int luaLcdDrawTimer(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + +#if 0 // TODO int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int seconds = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); -#if defined(COLORLCD) - if (att&SHADOWED) drawTimer(x+1, y+1, seconds, (att&0xFFFF)|LEFT); - drawTimer(x, y, seconds, att|LEFT); -#else - drawTimer(x, y, seconds, att|LEFT, att); -#endif + + if (att & SHADOWED) { + luaLcdBuffer->drawTimer(x+1, y+1, seconds, (att & 0xFFFF) | LEFT); + } + luaLcdBuffer->drawTimer(x, y, seconds, att | LEFT); #endif + return 0; } @@ -256,18 +256,19 @@ Display a number at (x,y) */ static int luaLcdDrawNumber(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int val = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); - #if defined(COLORLCD) - if ((att&SHADOWED) && !(att&INVERS)) lcdDrawNumber(x, y, val, att&0xFFFF); - #endif - lcdDrawNumber(x, y, val, att); -#endif + + if ((att&SHADOWED) && !(att&INVERS)) { + luaLcdBuffer->drawNumber(x, y, val, att&0xFFFF); + } + luaLcdBuffer->drawNumber(x, y, val, att); + return 0; } @@ -390,7 +391,6 @@ Bitmap loading can fail if: */ static int luaOpenBitmap(lua_State * L) { -#if 0 const char * filename = luaL_checkstring(L, 1); BitmapBuffer ** b = (BitmapBuffer **)lua_newuserdata(L, sizeof(BitmapBuffer *)); @@ -401,10 +401,10 @@ static int luaOpenBitmap(lua_State * L) *b = 0; } else { - *b = BitmapBuffer::load(filename); + *b = BitmapBuffer::loadBitmap(filename); if (*b == NULL && G(L)->gcrunning) { luaC_fullgc(L, 1); /* try to free some memory... */ - *b = BitmapBuffer::load(filename); /* try again */ + *b = BitmapBuffer::loadBitmap(filename); /* try again */ } } @@ -416,7 +416,7 @@ static int luaOpenBitmap(lua_State * L) luaL_getmetatable(L, LUA_BITMAPHANDLE); lua_setmetatable(L, -2); -#endif + return 1; } @@ -443,17 +443,15 @@ Return width, height of a bitmap object */ static int luaGetBitmapSize(lua_State * L) { -#if 0 const BitmapBuffer * b = checkBitmap(L, 1); if (b) { - lua_pushinteger(L, b->getWidth()); - lua_pushinteger(L, b->getHeight()); + lua_pushinteger(L, b->width()); + lua_pushinteger(L, b->height()); } else { lua_pushinteger(L, 0); lua_pushinteger(L, 0); } -#endif return 2; } @@ -508,9 +506,9 @@ Omitting scale draws image in 1:1 scale and is faster than specifying 100 for sc */ static int luaLcdDrawBitmap(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + const BitmapBuffer * b = checkBitmap(L, 1); if (b) { @@ -518,13 +516,13 @@ static int luaLcdDrawBitmap(lua_State *L) unsigned int y = luaL_checkunsigned(L, 3); unsigned int scale = luaL_optunsigned(L, 4, 0); if (scale) { - lcd->drawBitmap(x, y, b, 0, 0, 0, 0, scale/100.0f); + luaLcdBuffer->drawBitmap(x, y, b, 0, 0, 0, 0, scale/100.0f); } else { - lcd->drawBitmap(x, y, b); + luaLcdBuffer->drawBitmap(x, y, b); } } -#endif + return 0; } @@ -547,21 +545,18 @@ Draw a rectangle from top left corner (x,y) of specified width and height */ static int luaLcdDrawRectangle(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int w = luaL_checkinteger(L, 3); int h = luaL_checkinteger(L, 4); + unsigned int flags = luaL_optunsigned(L, 5, 0); -#if defined(PCBHORUS) unsigned int t = luaL_optunsigned(L, 6, 1); - lcdDrawRect(x, y, w, h, t, 0xff, flags); -#else - lcdDrawRect(x, y, w, h, 0xff, flags); -#endif -#endif + luaLcdBuffer->drawRect(x, y, w, h, t, 0xff, flags); + return 0; } @@ -582,16 +577,17 @@ Draw a solid rectangle from top left corner (x,y) of specified width and height */ static int luaLcdDrawFilledRectangle(lua_State *L) { -#if 0 - if (!luaLcdAllowed) + if (!luaLcdAllowed || !luaLcdBuffer) return 0; + int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); int w = luaL_checkinteger(L, 3); int h = luaL_checkinteger(L, 4); + unsigned int flags = luaL_optunsigned(L, 5, 0); - lcdDrawFilledRect(x, y, w, h, SOLID, flags); -#endif + luaLcdBuffer->drawFilledRect(x, y, w, h, SOLID, flags); + return 0; } @@ -744,10 +740,12 @@ static int luaRGB(lua_State *L) { if (!luaLcdAllowed) return 0; + int r = luaL_checkinteger(L, 1); int g = luaL_checkinteger(L, 2); int b = luaL_checkinteger(L, 3); lua_pushinteger(L, RGB(r, g, b)); + return 1; } diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 1b06b0168..c20e4a10a 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -882,8 +882,7 @@ void luaDoOneRunStandalone(event_t evt) return; } else if (luaDisplayStatistics) { -#if defined(COLORLCD) -#else +#if !defined(COLORLCD) lcdDrawSolidHorizontalLine(0, 7*FH-1, lcdLastRightPos+6, ERASE); lcdDrawText(0, 7*FH, "GV Use: "); lcdDrawNumber(lcdLastRightPos, 7*FH, luaGetMemUsed(lsScripts), LEFT); @@ -1025,12 +1024,22 @@ bool luaDoOneRunPermanentScript(event_t evt, int i, uint32_t scriptType) return true; } + +// This loop-pass should run in checkEvents(). +// However, libopenui has 2 main separated routines: +// - checkEvents(): runs the event loop and can effect an async redraw (if visible) +// - paint(): (if visible) the redraw itself +// bool luaTask(event_t evt, uint8_t scriptType, bool allowLcdUsage) { if (luaState == INTERPRETER_PANIC) return false; luaLcdAllowed = allowLcdUsage; bool scriptWasRun = false; + // TRACE("luaTask(0x%x,0x%x,%s)", + // evt, scriptType, + // allowLcdUsage ? "true" : "false"); + // we run either standalone script or permanent scripts if (luaState & INTERPRETER_RUNNING_STANDALONE_SCRIPT) { // run standalone script diff --git a/radio/src/main.cpp b/radio/src/main.cpp index c7b0fd8da..b7c72514c 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -284,9 +284,8 @@ void periodicTick() #if defined(GUI) && defined(COLORLCD) void guiMain(event_t evt) { - bool refreshNeeded = false; -#if defined(LUA) && !defined(LIBOPENUI) +#if defined(LUA) uint32_t t0 = get_tmr10ms(); static uint32_t lastLuaTime = 0; uint16_t interval = (lastLuaTime == 0 ? 0 : (t0 - lastLuaTime)); @@ -295,123 +294,35 @@ void guiMain(event_t evt) maxLuaInterval = interval; } - // run Lua scripts that don't use LCD (to use CPU time while LCD DMA is running) - DEBUG_TIMER_START(debugTimerLuaBg); - luaTask(0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT | RUN_TELEM_BG_SCRIPT, false); - DEBUG_TIMER_STOP(debugTimerLuaBg); - // wait for LCD DMA to finish before continuing, because code from this point - // is allowed to change the contents of LCD buffer - // - // WARNING: make sure no code above this line does any change to the LCD display buffer! - // - DEBUG_TIMER_START(debugTimerLcdRefreshWait); - lcdRefreshWait(); - DEBUG_TIMER_STOP(debugTimerLcdRefreshWait); + DEBUG_TIMER_START(debugTimerLua); - // draw LCD from menus or from Lua script - // run Lua scripts that use LCD + // Run Lua scripts first that don't use LCD + luaTask( 0, RUN_MIX_SCRIPT | RUN_FUNC_SCRIPT, false); - DEBUG_TIMER_START(debugTimerLuaFg); - refreshNeeded = luaTask(evt, RUN_STNDAL_SCRIPT, true); - if (!refreshNeeded) { - refreshNeeded = luaTask(evt, RUN_TELEM_FG_SCRIPT, true); - } - DEBUG_TIMER_STOP(debugTimerLuaFg); + // This is run from StandaloneLuaWindow::checkEvents() + // luaTask(evt, RUN_STNDAL_SCRIPT, true); + + // TODO: Telemetry scripts are run from Window::checkEvents() + // luaTask( 0, RUN_TELEM_BG_SCRIPT, false/* NO LCD */); + // luaTask(evt, RUN_TELEM_FG_SCRIPT, true/* LCD YES */); + DEBUG_TIMER_STOP(debugTimerLua); t0 = get_tmr10ms() - t0; if (t0 > maxLuaDuration) { maxLuaDuration = t0; } -#elif !defined(LIBOPENUI) - lcdRefreshWait(); // WARNING: make sure no code above this line does any change to the LCD display buffer! #endif - bool screenshotRequested = (mainRequestFlags & (1u << REQUEST_SCREENSHOT)); - -#if defined(LIBOPENUI) MainWindow::instance()->run(); -#else - if (!refreshNeeded) { - DEBUG_TIMER_START(debugTimerMenus); - while (true) { - // normal GUI from menus - const char * warn = warningText; - uint8_t menu = popupMenuItemsCount; - static bool popupDisplayed = false; - if (warn || menu) { - if (popupDisplayed == false) { - menuHandlers[menuLevel](EVT_REFRESH); - lcdDrawBlackOverlay(); - TIME_MEASURE_START(storebackup); - lcdStoreBackupBuffer(); - TIME_MEASURE_STOP(storebackup); - } - if (popupDisplayed == false || evt || screenshotRequested) { - popupDisplayed = lcdRestoreBackupBuffer(); - if (warn) { - DISPLAY_WARNING(evt); - } - if (menu) { - const char * result = runPopupMenu(evt); - if (result) { - TRACE("popupMenuHandler(%s)", result); - auto handler = popupMenuHandler; - if (result != STR_UPDATE_LIST) - CLEAR_POPUP(); - handler(result); - if (menuEvent == 0) { - evt = EVT_REFRESH; - continue; - } - } - } - refreshNeeded = true; - } - } - else { - if (popupDisplayed) { - if (evt == 0) { - evt = EVT_REFRESH; - } - popupDisplayed = false; - } - DEBUG_TIMER_START(debugTimerMenuHandlers); - refreshNeeded = menuHandlers[menuLevel](evt); - DEBUG_TIMER_STOP(debugTimerMenuHandlers); - } - - if (menuEvent == EVT_ENTRY) { - menuVerticalPosition = 0; - menuHorizontalPosition = 0; - evt = menuEvent; - menuEvent = 0; - } - else if (menuEvent == EVT_ENTRY_UP) { - menuVerticalPosition = menuVerticalPositions[menuLevel]; - menuHorizontalPosition = 0; - evt = menuEvent; - menuEvent = 0; - } - else { - break; - } - } - DEBUG_TIMER_STOP(debugTimerMenus); - } + bool screenshotRequested = (mainRequestFlags & (1u << REQUEST_SCREENSHOT)); if (screenshotRequested) { writeScreenshot(); mainRequestFlags &= ~(1u << REQUEST_SCREENSHOT); } - - if (refreshNeeded) { - DEBUG_TIMER_START(debugTimerLcdRefresh); - lcdRefresh(); - DEBUG_TIMER_STOP(debugTimerLcdRefresh); - } -#endif } #elif defined(GUI) + void handleGui(event_t event) { // if Lua standalone, run it and don't clear the screen (Lua will do it) // else if Lua telemetry view, run it and don't clear the screen diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index 61f5699cb..681f56e5c 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -529,6 +529,11 @@ int lcdRestoreBackupBuffer() return 1; } +uint16_t* lcdGetBackupBuffer() +{ + return simuLcdBackupBuf; +} + uint32_t pwrCheck() { // TODO: ability to simulate shutdown warning for a "soft" simulator restart