Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions radio/src/gui/colorlcd/libui/bitmapbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class BitmapBuffer
inline uint16_t height() const { return _height; }

inline pixel_t* getData() const { return data; }
inline uint8_t getFormat() const { return format; }

uint32_t getDataSize() const { return _width * _height * sizeof(pixel_t); }

Expand Down
11 changes: 11 additions & 0 deletions radio/src/gui/colorlcd/libui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,23 @@ MainWindow::MainWindow() : Window(nullptr, {0, 0, LCD_W, LCD_H})
etx_solid_bg(lvobj);
}

std::vector<std::function<void()>> MainWindow::deferredDeletes;

void MainWindow::deleteLater(std::function<void()> deleter)
{
deferredDeletes.push_back(std::move(deleter));
}

void MainWindow::emptyTrash()
{
for (auto window : trash) {
delete window;
}
trash.clear();

auto deleters = std::move(deferredDeletes);
deferredDeletes.clear();
for (auto& deleter : deleters) deleter();
}

void MainWindow::run(bool trash)
Expand Down
5 changes: 5 additions & 0 deletions radio/src/gui/colorlcd/libui/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ class MainWindow: public Window

void blockUntilClose(bool checkPwr, std::function<bool(void)> closeCondition, bool isError = false);

// Runs deleter with the next top-level emptyTrash(), like closed windows,
// once outer stack frames can no longer reference the object.
static void deleteLater(std::function<void()> deleter);

protected:
BitmapBuffer *backgroundBitmap = nullptr;
bool widgetRefreshEnable = true;

static MainWindow * _instance;
static std::vector<std::function<void()>> deferredDeletes;

void emptyTrash();
};
22 changes: 22 additions & 0 deletions radio/src/gui/colorlcd/libui/static.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,34 @@ void StaticImage::setSource(std::string filename)
}
}

void StaticImage::setSource(const lv_img_dsc_t* bitmap)
{
if (!image) image = lv_img_create(lvobj);
lv_obj_set_size(image, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_img_set_src(image, bitmap);
lv_obj_center(image);
setZoom();
}

void StaticImage::clearSource()
{
if (image) lv_obj_del(image);
image = nullptr;
}

void StaticImage::clear()
{
// Only remove content added on top; the displayed image is not a child.
deleteChildren();
if (lvobj) {
for (int i = (int)lv_obj_get_child_cnt(lvobj) - 1; i >= 0; i -= 1) {
auto child = lv_obj_get_child(lvobj, i);
if (child != image) lv_obj_del(child);
}
}
invalidate();
}

bool StaticImage::hasImage() const
{
lv_img_t* img = (lv_img_t*)image;
Expand Down
3 changes: 3 additions & 0 deletions radio/src/gui/colorlcd/libui/static.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,12 @@ class StaticImage : public Window

void setZoom();
void setSource(std::string filename);
void setSource(const lv_img_dsc_t* bitmap);
void clearSource();
bool hasImage() const;

void clear() override;

protected:
bool fillFrame;
bool dontEnlarge = false;
Expand Down
2 changes: 1 addition & 1 deletion radio/src/lua/api_colorlcd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ static int luaOpenBitmap(lua_State *L)
return 1;
}

static BitmapBuffer * checkBitmap(lua_State * L, int index)
BitmapBuffer * checkBitmap(lua_State * L, int index)
{
BitmapBuffer ** b = (BitmapBuffer **)luaL_checkudata(L, index, BITMAP_METATABLE);
return *b;
Expand Down
5 changes: 5 additions & 0 deletions radio/src/lua/api_colorlcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@
#include "definitions.h"
#include "lua_states.h"

#ifdef __cplusplus
class BitmapBuffer;
BitmapBuffer* checkBitmap(lua_State* L, int index);
#endif

EXTERN_C(LUALIB_API int luaopen_bitmap(lua_State * L));
EXTERN_C(LUALIB_API int luaopen_lvgl(lua_State * L));
82 changes: 62 additions & 20 deletions radio/src/lua/api_colorlcd_lvgl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,29 @@ static void buildLvgl(lua_State *L, int srcIndex, int refIndex)
}
}

// Calls fn with the top nargs values, restoring the temporary parent if it
// raises so later objects are not attached to a failed build's parent.
static void pcallRestoringParent(lua_State *L, int nargs, lua_CFunction fn,
LvglWidgetObjectBase* prevParent)
{
lua_pushcfunction(L, fn);
lua_insert(L, -(nargs + 1));
if (lua_pcall(L, nargs, 0, 0) != LUA_OK) {
luaScriptManager->setTempParent(prevParent);
lua_error(L);
}
}

static void addChildren(lua_State *L, LvglWidgetObjectBase* obj)
{
if (obj->getWindow()) {
lua_getfield(L, -1, "children");
auto prevParent = luaScriptManager->getTempParent();
luaScriptManager->setTempParent(obj);
buildLvgl(L, -1, 0);
lua_pop(L, 1);
pcallRestoringParent(L, 1, [](lua_State* L) {
buildLvgl(L, -1, 0);
return 0;
}, prevParent);
luaScriptManager->setTempParent(prevParent);
}
}
Expand All @@ -424,21 +439,39 @@ static int luaLvglObjEx(lua_State *L, std::function<LvglWidgetObjectBase*()> cre
{
if (luaScriptManager && (!fullscreenOnly || luaScriptManager->isFullscreen())) {
LvglWidgetObjectBase* p = nullptr;
LvglWidgetObjectBase* prevParent = nullptr;
auto prevParent = luaScriptManager->getTempParent();
if (lua_gettop(L) == 2) {
p = LvglWidgetObjectBase::checkLvgl(L, 1, true);
if (p) {
prevParent = luaScriptManager->getTempParent();
luaScriptManager->setTempParent(p);
}
if (p && p->hasDeletedWindow())
return luaL_error(L, "Parent window has been deleted");
}

LvglWidgetParams params(L, -1);

if (p) luaScriptManager->setTempParent(p);
auto obj = create();
obj->create(L, -1);

if (params.hasChildren) addChildren(L, obj);
bool created = false;
lua_pushlightuserdata(L, obj);
lua_pushlightuserdata(L, &created);
lua_pushboolean(L, params.hasChildren);
lua_pushcclosure(L, [](lua_State* L) {
auto obj =
(LvglWidgetObjectBase*)lua_touserdata(L, lua_upvalueindex(1));
obj->create(L, 1);
*(bool*)lua_touserdata(L, lua_upvalueindex(2)) = true;
if (lua_toboolean(L, lua_upvalueindex(3))) addChildren(L, obj);
return 0;
}, 3);
lua_pushvalue(L, -2);
if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
luaScriptManager->setTempParent(prevParent);
if (created) {
auto window = obj->getWindow();
obj->release(L);
if (window) window->closeWindow();
}
return lua_error(L);
}

obj->push(L);

Expand Down Expand Up @@ -475,21 +508,25 @@ static int luaLvglBuild(lua_State *L)
{
if (luaScriptManager) {
LvglWidgetObjectBase* p = nullptr;
LvglWidgetObjectBase* prevParent = nullptr;
auto prevParent = luaScriptManager->getTempParent();
if (lua_gettop(L) == 2) {
p = LvglWidgetObjectBase::checkLvgl(L, 1, true);
if (p) {
prevParent = luaScriptManager->getTempParent();
luaScriptManager->setTempParent(p);
}
if (p && p->hasDeletedWindow())
return luaL_error(L, "Parent window has been deleted");
}

if (p) luaScriptManager->setTempParent(p);

// Return array of lvgl object references
lua_newtable(L);
buildLvgl(L, -2, -1);
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
pcallRestoringParent(L, 2, [](lua_State* L) {
buildLvgl(L, -2, -1);
return 0;
}, prevParent);

if (p)
luaScriptManager->setTempParent((prevParent));
luaScriptManager->setTempParent(prevParent);
} else {
lua_pushnil(L);
}
Expand Down Expand Up @@ -823,14 +860,19 @@ LROT_BEGIN(lvgllib, NULL, 0)

@param params (table):
@commonparams lvgl_object
* `file` (string) full path to the image file on the SD card, defaults to an empty string
* `file` (string or function) full path to the image file on the SD card, or a callback returning that path; mutually exclusive with a Bitmap source, but may be combined with `bitmap=false`, defaults to an empty string
* `bitmap` (Bitmap or false) existing Bitmap object assigned directly, not a callback; false clears the source and releases the image's Bitmap reference, defaults to nil
* `fill` (boolean) if true the image is scaled to completely fill the frame (may be cropped); if false it is scaled to fit entirely within the frame (may have empty borders), defaults to false

@retval table LVGL object

@notice `w` and `h` are required.
@notice `image:set({file=...})` changes the displayed source and replaces any previous filename callback. If combined with `bitmap=false`, the filename source is used.
@notice The image keeps its Bitmap alive until replaced or deleted; `lvgl.clear(image)` removes only the image's children. Use `image:set({bitmap=false})` to release this reference immediately, including when hidden. A nil table field is absent and does not clear it. Drop script references and run `collectgarbage()` to reclaim unused Bitmap memory.
@notice RGB565 Bitmaps (including JPEGs) are borrowed without copying pixels. ARGB4444 Bitmaps require an RGB565-plus-alpha conversion, charged to the existing Lua bitmap memory budget. Replacing one ARGB4444 Bitmap with another briefly needs budget for both conversions; near the limit, set `bitmap=false` first.
@notice Invalid source values or parameters and conversion allocation or budget failures raise a Lua error without replacing the previous source. Check `Bitmap.getSize()` after `Bitmap.open()`; a failed load returns zero width and height.

@status current Introduced in 2.11.0, floating added in 2.11.6
@status current Introduced in 2.11.0, floating added in 2.11.6, bitmap added in 3.0.0
*/
LROT_FUNCENTRY(image, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetImage(); }); })
/*luadoc
Expand Down
Loading
Loading