summaryrefslogtreecommitdiff
path: root/lib/luavgl
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
commit8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch)
tree02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl')
-rw-r--r--lib/luavgl/.clang-format6
-rw-r--r--lib/luavgl/.gitignore55
-rw-r--r--lib/luavgl/.gitmodules9
-rw-r--r--lib/luavgl/CMakeLists.txt5
-rw-r--r--lib/luavgl/LICENSE21
-rw-r--r--lib/luavgl/README.md155
-rw-r--r--lib/luavgl/cmake/FindLuaJIT.cmake39
-rw-r--r--lib/luavgl/examples/analogTime.lua13
-rw-r--r--lib/luavgl/examples/animation.lua61
-rw-r--r--lib/luavgl/examples/assets/hand-hour.pngbin0 -> 7529 bytes
-rw-r--r--lib/luavgl/examples/assets/hand-minute.pngbin0 -> 9600 bytes
-rw-r--r--lib/luavgl/examples/assets/hand-second.pngbin0 -> 5338 bytes
-rw-r--r--lib/luavgl/examples/assets/lvgl-logo.pngbin0 -> 2790 bytes
-rw-r--r--lib/luavgl/examples/assets/second.pngbin0 -> 2140 bytes
-rw-r--r--lib/luavgl/examples/dropdown.lua74
-rw-r--r--lib/luavgl/examples/examples.lua44
-rw-r--r--lib/luavgl/examples/extension.lua14
-rw-r--r--lib/luavgl/examples/flappyBird/bg_day.pngbin0 -> 29684 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird1.pngbin0 -> 323 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird2.pngbin0 -> 253 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/bird3.pngbin0 -> 250 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/button_play.pngbin0 -> 1375 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/flappyBird.lua765
-rw-r--r--lib/luavgl/examples/flappyBird/land.pngbin0 -> 2660 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/medals.pngbin0 -> 1571 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/pipe_down.pngbin0 -> 3163 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/pipe_up.pngbin0 -> 3235 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/score.pngbin0 -> 536 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/text_game_over.pngbin0 -> 4224 bytes
-rw-r--r--lib/luavgl/examples/flappyBird/title.pngbin0 -> 3217 bytes
-rw-r--r--lib/luavgl/examples/flexLayout.lua27
-rw-r--r--lib/luavgl/examples/font.lua18
-rw-r--r--lib/luavgl/examples/fs.lua59
-rw-r--r--lib/luavgl/examples/group.lua61
-rw-r--r--lib/luavgl/examples/indev.lua61
-rw-r--r--lib/luavgl/examples/keyboard.lua46
-rw-r--r--lib/luavgl/examples/pointer.lua46
-rw-r--r--lib/luavgl/examples/protectedcall.lua36
-rw-r--r--lib/luavgl/examples/roller.lua14
-rw-r--r--lib/luavgl/examples/tests.lua45
-rw-r--r--lib/luavgl/examples/uservalue.lua34
-rw-r--r--lib/luavgl/simulator/CMakeLists.txt13
-rw-r--r--lib/luavgl/simulator/lv_conf.h703
-rw-r--r--lib/luavgl/simulator/lv_drv_conf.h494
-rw-r--r--lib/luavgl/simulator/main.c340
-rw-r--r--lib/luavgl/simulator/mouse_cursor_icon.c104
-rw-r--r--lib/luavgl/simulator/widgets/analog_time.c113
-rw-r--r--lib/luavgl/simulator/widgets/extension.c69
-rw-r--r--lib/luavgl/simulator/widgets/lv_analog_time.c253
-rw-r--r--lib/luavgl/simulator/widgets/lv_analog_time.h64
-rw-r--r--lib/luavgl/simulator/widgets/lv_pointer.c138
-rw-r--r--lib/luavgl/simulator/widgets/lv_pointer.h67
-rw-r--r--lib/luavgl/simulator/widgets/pointer.c101
-rw-r--r--lib/luavgl/simulator/widgets/widgets.c8
-rw-r--r--lib/luavgl/simulator/widgets/widgets.h12
-rw-r--r--lib/luavgl/simulator/widgets/widgets.lua104
-rw-r--r--lib/luavgl/src/anim.c339
-rw-r--r--lib/luavgl/src/constants.c554
-rw-r--r--lib/luavgl/src/disp.c342
-rw-r--r--lib/luavgl/src/event.c191
-rw-r--r--lib/luavgl/src/font.c313
-rw-r--r--lib/luavgl/src/fs.c383
-rw-r--r--lib/luavgl/src/group.c347
-rw-r--r--lib/luavgl/src/indev.c342
-rw-r--r--lib/luavgl/src/luavgl.c150
-rw-r--r--lib/luavgl/src/luavgl.h263
-rw-r--r--lib/luavgl/src/lvgl.lua1465
-rw-r--r--lib/luavgl/src/obj.c850
-rw-r--r--lib/luavgl/src/private.h39
-rw-r--r--lib/luavgl/src/style.c702
-rw-r--r--lib/luavgl/src/timer.c222
-rw-r--r--lib/luavgl/src/util.c413
-rw-r--r--lib/luavgl/src/widgets/calendar.c169
-rw-r--r--lib/luavgl/src/widgets/checkbox.c74
-rw-r--r--lib/luavgl/src/widgets/dropdown.c188
-rw-r--r--lib/luavgl/src/widgets/img.c196
-rw-r--r--lib/luavgl/src/widgets/keyboard.c76
-rw-r--r--lib/luavgl/src/widgets/label.c155
-rw-r--r--lib/luavgl/src/widgets/led.c91
-rw-r--r--lib/luavgl/src/widgets/list.c89
-rw-r--r--lib/luavgl/src/widgets/roller.c136
-rw-r--r--lib/luavgl/src/widgets/textarea.c117
-rw-r--r--lib/luavgl/src/widgets/widgets.c133
83 files changed, 12630 insertions, 0 deletions
diff --git a/lib/luavgl/.clang-format b/lib/luavgl/.clang-format
new file mode 100644
index 00000000..3e071c10
--- /dev/null
+++ b/lib/luavgl/.clang-format
@@ -0,0 +1,6 @@
+BasedOnStyle: llvm
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterFunction: true
+AlignArrayOfStructures: Left
+SeparateDefinitionBlocks: Always
diff --git a/lib/luavgl/.gitignore b/lib/luavgl/.gitignore
new file mode 100644
index 00000000..3e81438e
--- /dev/null
+++ b/lib/luavgl/.gitignore
@@ -0,0 +1,55 @@
+/build
+/.vscode
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+tmp.txt
diff --git a/lib/luavgl/.gitmodules b/lib/luavgl/.gitmodules
new file mode 100644
index 00000000..531be0bf
--- /dev/null
+++ b/lib/luavgl/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "deps/lvgl"]
+ path = deps/lvgl
+ url = https://github.com/lvgl/lvgl.git
+[submodule "deps/lv_drivers"]
+ path = deps/lv_drivers
+ url = https://github.com/lvgl/lv_drivers.git
+[submodule "deps/lua-compat-5.3"]
+ path = deps/lua-compat-5.3
+ url = https://github.com/lunarmodules/lua-compat-5.3.git
diff --git a/lib/luavgl/CMakeLists.txt b/lib/luavgl/CMakeLists.txt
new file mode 100644
index 00000000..c1f3e8a1
--- /dev/null
+++ b/lib/luavgl/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright 2023 jacqueline <me@jacqueline.id.au>
+#
+# SPDX-License-Identifier: GPL-3.0-only
+idf_component_register(SRCS "src/luavgl.c" INCLUDE_DIRS "src" REQUIRES "esp-idf-lua" "lvgl")
+
diff --git a/lib/luavgl/LICENSE b/lib/luavgl/LICENSE
new file mode 100644
index 00000000..5c9071dd
--- /dev/null
+++ b/lib/luavgl/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Neo Xu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/luavgl/README.md b/lib/luavgl/README.md
new file mode 100644
index 00000000..2bba446c
--- /dev/null
+++ b/lib/luavgl/README.md
@@ -0,0 +1,155 @@
+# lvgl-lua-binding
+lua + lvgl = luavgl
+
+**luavgl is currently under development.**
+
+A flappy bird game is ready for showoff. See simulator instructions below which is tested on ubuntu and mac but not windows.
+
+<p align="center">
+ <img src="https://i.ibb.co/nbgYvZW/flappybird.gif" />
+</p>
+
+## Introduction
+
+`luavgl` is a wrapper around lvgl **core** functions and **widgets** with class inherence in mind, which is lvgl trying to do in `C`. Lua makes widgets inherence happens smoothly.
+
+`luavgl` does not support APIs for low level hardware initialization, lvgl setup etc. Those initialization must be done before executing lua scripts.
+
+`luavgl` mainly targets for embedded device, a simulator has been provided for preview and tested on Ubuntu/macOS.
+
+```lua
+local root = lvgl.Object()
+root:set { w = lvgl.HOR_RES(), h = lvgl.VER_RES() }
+
+-- flex layout and align
+root:set {
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap",
+ justify_content = "center",
+ align_items = "center",
+ align_content = "center",
+ },
+ w = 300,
+ h = 75,
+ align = lvgl.ALIGN.CENTER
+}
+
+-- create obj on root
+local obj = root:Object()
+
+-- create image on root and set position/img src/etc. properties.
+local img = root:Image {
+ src = "res/image.png",
+ x = 0,
+ y = 0,
+ bg_color = "#112233" -- #RRGGBB, 0xRRGGBB or "#RGB"
+ pad_all = 0
+}
+
+-- change image properties.
+
+img:set {
+ src = "/assets/lvgl-logo.png",
+ align = lvgl.ALIGN.CENTER,
+}
+
+-- create animation on object
+img:Anim {
+ run = true,
+ start_value = 0,
+ end_value = 3600,
+ duration = 2000,
+ repeat_count = 2,
+ path = "bounce",
+ exec_cb = function(obj, value)
+ obj:set {
+ angle = value
+ }
+ end
+}
+
+-- create Label on root and set its font
+local label = root:Label {
+ text = string.format("Hello %03d", 123),
+ text_font = lvgl.Font("montserrat", 24, "normal"),
+ -- or builtin font:
+ -- text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ x_ofs = 0,
+ y_ofs = 100,
+ }
+}
+
+```
+
+### Embedded device
+
+For embedded device, lvgl environment must setup before using `luavgl`. Once `lvgl` and `lua interpreter` are up and running, add the `luavgl.c` to sources for compiling. And make sure `luaopen_lvgl` is added to global lib. Below is example from `simulator/main.c` shows this exact method.
+
+```c
+ /* add `lvgl` module to global package table */
+ luaL_requiref(L, "lvgl", luaopen_luavgl, 1);
+ lua_pop(L, 1);
+```
+
+### LuaJIT support
+
+Supporting `LuaJIT` is done by adding `deps/lua-compat-5.3`.
+
+### Developing
+
+I personally using `vscode` plus extension [`Lua`](https://marketplace.visualstudio.com/items?itemName=sumneko.lua).
+
+File `src/lvgl.lua` is used for linting.
+
+<p align="center">
+ <img src="https://i.ibb.co/NpRWXZ1/luavgl-linting.png" />
+</p>
+
+### Run on RTOS nuttx
+
+Check the ready-to-go examples [luavgl-nuttx-example](https://github.com/XuNeo/luavgl-nuttx-example)
+
+### PC simulator
+
+**Currently compile luavgl to `so` or `dll` is NOT available.**
+
+`luavgl` depends on lvgl and various configurations(`lv_conf.h`), thus cannot run without a working lvgl environment.
+The simulator provided in this repo can be used as example if `luavgl` is required on PC.
+
+Make sure clone the submodules, luavgl simulator comes directly from lvgl simulator with lua added.
+
+```bash
+git clone --recursive https://github.com/XuNeo/luavgl.git
+
+# or
+git sudmodule update --init
+```
+
+#### Dependencies
+
+To run simulator on PC, make sure `lua` header is available, you may need to install below packages.
+
+```
+sudo apt install libsdl2-dev lua5.3 liblua5.3-dev
+```
+
+Both lua5.3 and lua5.4 are supported. Versions below 5.2 has not been verified but should work through `deps/lua-compat-5.3`.
+
+#### Build and run
+
+```bash
+cmake -Bbuild -DBUILD_SIMULATOR=ON
+cd build
+make
+make run # run simulator
+```
+## Custom Widget
+
+`luavgl` support custom widget, and use them in `lua` just like lvgl core widgets.
+An example is provided in [`simulator/extension.c`](https://github.com/XuNeo/luavgl/blob/master/simulator/extension.c#L62)
+
+For custom widget, it should be registered to Lua after luavgl lib loaded.
+
diff --git a/lib/luavgl/cmake/FindLuaJIT.cmake b/lib/luavgl/cmake/FindLuaJIT.cmake
new file mode 100644
index 00000000..90c5f3eb
--- /dev/null
+++ b/lib/luavgl/cmake/FindLuaJIT.cmake
@@ -0,0 +1,39 @@
+# MIT License
+#
+# Copyright (c) 2008-2014 CodePoint Ltd, Shift Technology Ltd, and contributors
+# Copyright (c) 2019-2021 The RmlUi Team, and contributors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Try to find the lua library
+# LUAJIT_FOUND - system has lua
+# LUAJIT_INCLUDE_DIR - the lua include directory
+# LUAJIT_LIBRARY - the lua library
+
+FIND_PATH(LUAJIT_INCLUDE_DIR NAMES luajit.h PATH_SUFFIXES luajit luajit-2.0 luajit-2.1)
+SET(_LUAJIT_STATIC_LIBS libluajit-5.1.a libluajit.a liblua51.a)
+SET(_LUAJIT_SHARED_LIBS luajit-5.1 luajit lua51)
+IF(USE_STATIC_LIBS)
+ FIND_LIBRARY(LUAJIT_LIBRARY NAMES ${_LUAJIT_STATIC_LIBS} ${_LUAJIT_SHARED_LIBS})
+ELSE()
+ FIND_LIBRARY(LUAJIT_LIBRARY NAMES ${_LUAJIT_SHARED_LIBS} ${_LUAJIT_STATIC_LIBS})
+ENDIF()
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT DEFAULT_MSG LUAJIT_LIBRARY LUAJIT_INCLUDE_DIR)
+MARK_AS_ADVANCED(LUAJIT_LIBRARY LUAJIT_INCLUDE_DIR)
diff --git a/lib/luavgl/examples/analogTime.lua b/lib/luavgl/examples/analogTime.lua
new file mode 100644
index 00000000..375459a9
--- /dev/null
+++ b/lib/luavgl/examples/analogTime.lua
@@ -0,0 +1,13 @@
+local analogTime = lvgl.AnalogTime(nil, {
+ border_width = 0,
+ x = lvgl.HOR_RES() // 2,
+ y = lvgl.VER_RES() // 2,
+ hands = {
+ hour = SCRIPT_PATH .. "/assets/hand-hour.png",
+ minute = SCRIPT_PATH .. "/assets/hand-minute.png",
+ second = SCRIPT_PATH .. "/assets/hand-second.png",
+ },
+ period = 33,
+})
+
+print("analogTime: ", analogTime)
diff --git a/lib/luavgl/examples/animation.lua b/lib/luavgl/examples/animation.lua
new file mode 100644
index 00000000..95ee5f5a
--- /dev/null
+++ b/lib/luavgl/examples/animation.lua
@@ -0,0 +1,61 @@
+local root = lvgl.Object()
+root:set { w = lvgl.HOR_RES(), h = lvgl.VER_RES() }
+
+-- create image on root and set position/img src/etc. properties.
+root:Object():Image {
+ src = SCRIPT_PATH .. "/assets/lvgl-logo.png",
+ x = 0,
+ y = 0,
+ bg_color = 0x004400,
+ pad_all = 0,
+ align = lvgl.ALIGN.CENTER,
+}:Anim {
+ run = true,
+ start_value = 0,
+ end_value = 3600,
+ duration = 2000,
+ repeat_count = 2,
+ path = "bounce",
+ exec_cb = function(obj, value)
+ obj:set {
+ angle = value
+ }
+ end,
+ done_cb = function (anim, obj)
+ print("anim done.: ", anim, "obj:", obj)
+ anim:delete()
+ end
+}
+
+-- second anim example with playback
+local obj = root:Object {
+ bg_color = "#F00000",
+ radius = lvgl.RADIUS_CIRCLE,
+ align = lvgl.ALIGN.LEFT_MID,
+}
+obj:clear_flag(lvgl.FLAG.SCROLLABLE)
+
+--- @type AnimPara
+local animPara = {
+ run = true,
+ start_value = 10,
+ end_value = 50,
+ duration = 1000,
+ playback_delay = 100,
+ playback_time = 500,
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "ease_in_out",
+}
+
+animPara.exec_cb = function(obj, value)
+ obj:set { size = value }
+end
+
+obj:Anim(animPara)
+
+animPara.end_value = 240
+animPara.exec_cb = function(obj, value)
+ obj:set { x = value }
+end
+
+obj:Anim(animPara)
diff --git a/lib/luavgl/examples/assets/hand-hour.png b/lib/luavgl/examples/assets/hand-hour.png
new file mode 100644
index 00000000..6f5857a9
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-hour.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/hand-minute.png b/lib/luavgl/examples/assets/hand-minute.png
new file mode 100644
index 00000000..52f6f1ba
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-minute.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/hand-second.png b/lib/luavgl/examples/assets/hand-second.png
new file mode 100644
index 00000000..9caf4649
--- /dev/null
+++ b/lib/luavgl/examples/assets/hand-second.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/lvgl-logo.png b/lib/luavgl/examples/assets/lvgl-logo.png
new file mode 100644
index 00000000..b032d1b8
--- /dev/null
+++ b/lib/luavgl/examples/assets/lvgl-logo.png
Binary files differ
diff --git a/lib/luavgl/examples/assets/second.png b/lib/luavgl/examples/assets/second.png
new file mode 100644
index 00000000..d09ea5a3
--- /dev/null
+++ b/lib/luavgl/examples/assets/second.png
Binary files differ
diff --git a/lib/luavgl/examples/dropdown.lua b/lib/luavgl/examples/dropdown.lua
new file mode 100644
index 00000000..037f51cb
--- /dev/null
+++ b/lib/luavgl/examples/dropdown.lua
@@ -0,0 +1,74 @@
+-- a dropdown on top left
+local dd = lvgl.Dropdown(nil, {
+ -- note there are two "Orange"
+ options = "Apple\nBanana\nOrange\nCherry\nGrape\nRaspberry\nMelon\nOrange\nLemon\nNuts",
+ symbol = "\xEF\x81\x94",
+ dir = lvgl.DIR.RIGHT,
+ highlight = false,
+ text = nil,
+ align = {
+ type = lvgl.ALIGN.TOP_LEFT,
+ x_ofs = 20,
+ y_ofs = 20,
+ }
+})
+local list = dd:get("list")
+print("get dropdown list: ", list)
+list:set { text_font = lvgl.BUILTIN_FONT.MONTSERRAT_20 }
+
+print("available options:", dd:get("options"))
+
+local cnt = 0
+dd:onevent(lvgl.EVENT.VALUE_CHANGED,
+ ---comment
+ ---@param obj Dropdown
+ ---@param code ObjEventCode
+ function(obj, code)
+ obj:add_option(string.format("Option:%d", cnt), dd:get("option_cnt"))
+ cnt = cnt + 1
+ print(obj:get("selected_str"), "option_cnt" .. obj:get("option_cnt"))
+ end
+)
+
+-- another dropdown on top left
+local dd = lvgl.Dropdown(nil, {
+ options = "Apple\nBanana\nOrange\nCherry\nGrape\nRaspberry\nMelon\nOrange\nLemon\nNuts",
+ symbol = "\xEF\x81\xB7",
+ dir = lvgl.DIR.BOTTOM,
+ text = "SetText",
+ selected = 5,
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_20,
+ align = {
+ type = lvgl.ALIGN.BOTTOM_MID,
+ x_ofs = 0,
+ y_ofs = -20,
+ }
+})
+
+
+dd:get("list"):set { text_font = lvgl.Font("montserrat", 24) }
+dd:onevent(lvgl.EVENT.VALUE_CHANGED, function(obj, code)
+ print(obj:get("selected_str") .. ":" .. obj:get("selected"),
+ "dir:" .. obj:get("dir"),
+ "option_index" .. dd:get("option_index", obj:get("selected_str")))
+end)
+
+
+
+lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ dd:open()
+ print("now dd should be opened: " .. (dd:is_open() and "open" or "closed"))
+ end
+}
+
+lvgl.Timer {
+ period = 2000,
+ cb = function(t)
+ t:delete()
+ dd:close()
+ print("now dd should be closed: " .. (dd:is_open() and "open" or "closed"))
+ end
+}
diff --git a/lib/luavgl/examples/examples.lua b/lib/luavgl/examples/examples.lua
new file mode 100644
index 00000000..64e0b631
--- /dev/null
+++ b/lib/luavgl/examples/examples.lua
@@ -0,0 +1,44 @@
+local container = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ bg_color = "#888",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 0,
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+})
+
+print("created container", container)
+
+local function createBtn(parent, name)
+ local root = parent:Object {
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_color = "#ccc",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 10,
+ pad_all = 20,
+ }
+
+ root:onClicked(function()
+ container:delete()
+ require(name)
+ end)
+
+ root:Label {
+ text = name,
+ text_color = "#333",
+ align = lvgl.ALIGN.CENTER,
+ }
+end
+
+createBtn(container, "keyboard")
+createBtn(container, "animation")
+createBtn(container, "pointer")
+createBtn(container, "analogTime")
+createBtn(container, "flappyBird/flappyBird")
+createBtn(container, "tests")
diff --git a/lib/luavgl/examples/extension.lua b/lib/luavgl/examples/extension.lua
new file mode 100644
index 00000000..e51f355c
--- /dev/null
+++ b/lib/luavgl/examples/extension.lua
@@ -0,0 +1,14 @@
+-- demo of external widget added to lvgl. See simulator/extension.c
+
+local extension = lvgl.Extension(nil, {
+ border_width = 1,
+ w = lvgl.PCT(20),
+ h = lvgl.PCT(20),
+ align = lvgl.ALIGN.CENTER,
+})
+
+extension:onClicked(function ()
+ print("clicked")
+end)
+
+print("extension: ", extension)
diff --git a/lib/luavgl/examples/flappyBird/bg_day.png b/lib/luavgl/examples/flappyBird/bg_day.png
new file mode 100644
index 00000000..6d24cc26
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bg_day.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird1.png b/lib/luavgl/examples/flappyBird/bird1.png
new file mode 100644
index 00000000..72b202d5
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird1.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird2.png b/lib/luavgl/examples/flappyBird/bird2.png
new file mode 100644
index 00000000..46050452
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird2.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/bird3.png b/lib/luavgl/examples/flappyBird/bird3.png
new file mode 100644
index 00000000..2fc5128d
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/bird3.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/button_play.png b/lib/luavgl/examples/flappyBird/button_play.png
new file mode 100644
index 00000000..ae3d330b
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/button_play.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/flappyBird.lua b/lib/luavgl/examples/flappyBird/flappyBird.lua
new file mode 100644
index 00000000..26018bba
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/flappyBird.lua
@@ -0,0 +1,765 @@
+local lvgl = require("lvgl")
+
+local MOVE_SPEED = 480 / 8000 -- 8s for 480 pixel, pixel per ms
+local PIXEL_PER_METER = 80
+local TOP_Y = 20
+local BOTTOM_Y = 480 - 112
+local PIPE_COUNT = 5
+local PIPE_GAP = 100
+local PIPE_SPACE = 120
+
+-- SCRIPT_PATH is set in simulator/main.c, used to get the abs path of first
+-- lua script lua get called. In this example, SCRIPT_PATH is set to
+-- path of `examples.lua`, flappyBird.lua is called when button is clicked.
+
+local IMAGE_PATH = SCRIPT_PATH
+if not IMAGE_PATH then
+ IMAGE_PATH = "/"
+ print("Note image root path is set to: ", IMAGE_PATH)
+end
+
+IMAGE_PATH = IMAGE_PATH .. "/flappyBird/"
+print("IMAGE_PATH:", IMAGE_PATH)
+
+local function randomY()
+ return math.random(TOP_Y + 30, BOTTOM_Y - 50 - 50)
+end
+
+local function screenCreate(parent)
+ local property = {
+ w = 480,
+ h = 480,
+ bg_opa = 0,
+ border_width = 0,
+ pad_all = 0
+ }
+
+ local scr
+ if parent then
+ scr = parent:Object{
+ w = 480,
+ h = 480,
+ bg_opa = 0,
+ border_width = 0,
+ pad_all = 0
+ }
+ else
+ scr = lvgl.Object(nil, property)
+ end
+ scr:clear_flag(lvgl.FLAG.SCROLLABLE)
+ scr:clear_flag(lvgl.FLAG.CLICKABLE)
+ return scr
+end
+
+local function Image(parent, src)
+ local img = {}
+ img.widget = parent:Image{
+ src = src
+ }
+
+ img.w, img.h = img.widget:get_img_size()
+ if not img.w or not img.h then
+ error("failed to load image: " .. src)
+ end
+ return img
+end
+
+local function ImageScroll(root, src, animSpeed, y)
+ -- image on right
+ local right = Image(root, src).widget
+ right:set{
+ src = src,
+ x = 480,
+ y = y,
+ pad_all = 0
+ }
+
+ local img = Image(root, src).widget
+ img:set{
+ x = 0,
+ y = y,
+ src = src,
+ pad_all = 0
+ }
+
+ img:Anim{
+ run = true,
+ start_value = 0,
+ end_value = -480,
+ time = 480 / animSpeed,
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, value)
+ img:set{
+ x = value
+ }
+
+ right:set{
+ x = value + 480
+ }
+ end
+ }
+
+ img:clear_flag(lvgl.FLAG.CLICKABLE)
+
+ return img
+end
+
+local function Frames(parent, src, fps)
+ local frame = Image(parent, src[1])
+ fps = fps ~= 0 and fps or 25
+
+ frame.src = src
+ frame.len = #src
+ frame.i = 0
+
+ frame.timer = lvgl.Timer {
+ period = 1000 / fps,
+ cb = function(t)
+ frame.widget:set{
+ src = frame.src[frame.i]
+ }
+
+ frame.i = frame.i + 1
+ if frame.i == frame.len then
+ frame.i = 1
+ end
+ end
+ }
+
+ frame.start = function(self)
+ self.timer:resume()
+ end
+
+ frame.pause = function(self)
+ self.timer:pause()
+ end
+
+ return frame
+end
+
+local function Pipe(parent)
+ local up = Image(parent, IMAGE_PATH .. "pipe_up.png")
+ local down = Image(parent, IMAGE_PATH .. "pipe_down.png")
+ local pipe = {
+ up = up.widget,
+ down = down.widget,
+ w = up.w,
+ h = up.h,
+ x = 0,
+ y = 0
+ }
+
+ function pipe:updatePipePos()
+ self.up:set{
+ x = self.x,
+ y = self.y - up.h
+ }
+
+ self.down:set{
+ x = self.x,
+ y = self.y + PIPE_GAP
+ }
+ end
+
+ pipe:updatePipePos()
+ return pipe
+end
+
+local function ObjInfo(x, y, w, h)
+ return {
+ x = x,
+ y = y,
+ w = w,
+ h = h
+ }
+end
+
+local function Pipes(parent)
+ local pipes = {}
+
+ -- add initial pipe
+ for i = 1, PIPE_COUNT do
+ pipes[i] = Pipe(parent)
+ if i == 1 then
+ pipes.w = pipes[i].w -- record pipe size
+ pipes.h = pipes[i].h
+ end
+ end
+
+ local function pipesPosinit()
+ local x = 480;
+ local y = randomY()
+
+ for i = 1, PIPE_COUNT do
+ local pipe = pipes[i]
+ pipe.x = x
+ pipe.y = y
+ pipe:updatePipePos()
+ pipes[i] = pipe
+ x = x + PIPE_SPACE + pipe.w
+ y = randomY()
+ end
+ end
+
+ pipesPosinit()
+
+ pipes.score = 0
+ pipes.last = PIPE_COUNT -- first pipe index in pipes.pipes
+ pipes.totalWidth = (PIPE_COUNT) * (PIPE_SPACE + pipes.w)
+ pipes.birdInfo = ObjInfo(0, 0, 0, 0)
+ pipes.gapInfo = ObjInfo(0, 0, 0, 0)
+
+ function pipes:setObjInfo(x, y, w, h)
+ self.birdInfo.x = x
+ self.birdInfo.y = y
+ if w then
+ self.birdInfo.w = w
+ end
+ if h then
+ self.birdInfo.h = h
+ end
+ end
+
+ local function setGapInfo(x, y, w, h)
+ pipes.gapInfo.x = x
+ pipes.gapInfo.y = y
+ pipes.gapInfo.w = w
+ pipes.gapInfo.h = h
+ end
+
+ pipes.objPassing = -1
+
+ local function isBirdCollision()
+ local bird = pipes.birdInfo
+ local gap = pipes.gapInfo
+
+ -- far left
+ if bird.x + bird.w < gap.x then
+ return false
+ end
+
+ -- far right
+ if bird.x > gap.x + gap.w then
+ return false
+ end
+
+ -- in middle
+
+ if (bird.y > gap.y) and (bird.y + bird.h < gap.y + gap.h) then
+ return false
+ end
+ return true
+ end
+
+ local function moveVirtualX(dx)
+ for i = 1, PIPE_COUNT do
+ local pipe = pipes[i]
+ local newX = pipe.x + dx
+
+ if newX + pipes.w < 0 then
+ newX = newX + pipes.totalWidth
+ pipe.y = randomY()
+ pipes.last = i
+ end
+
+ pipe.x = newX
+ pipe.updatePipePos(pipe)
+ end
+ end
+
+ local function checkScore(i)
+ local pipe = pipes[i]
+
+ local bird = pipes.birdInfo
+ local gap = pipes.gapInfo
+ local passing = pipes.objPassing
+
+ -- far left or right
+ if bird.x + bird.w < gap.x or bird.x > gap.x + gap.w then
+ if passing > 0 and i == passing then
+ pipes.score = pipes.score + 1
+ passing = -1
+ pipes.scoreUpdateCB(pipes.score)
+ end
+ else
+ if passing < 0 then
+ passing = i
+ end
+ end
+
+ pipes.objPassing = passing
+ end
+
+ --- Detect if obj has collision with pipes
+ local function collisionDetect()
+ local first = (pipes.last % PIPE_COUNT) + 1
+ for idx = 0, PIPE_COUNT - 1 do
+ local i = (first + idx - 1) % PIPE_COUNT + 1
+ local pipe = pipes[i]
+ setGapInfo(pipe.x, pipe.y, pipe.w, PIPE_GAP)
+
+ if isBirdCollision() then
+ local bird = pipes.birdInfo
+ if pipes.collisionCB then
+ pipes.collisionCB()
+ end
+ end
+
+ checkScore(i)
+ end
+ end
+
+ pipes.preValue = 0
+ pipes.anim = pipes[1].up:Anim{
+ run = false,
+ start_value = 0,
+ end_value = 480,
+ time = 480 / MOVE_SPEED, -- MOVE_SPEED
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, value)
+ local x = pipes.preValue
+ local d
+ if value < x then
+ d = value + 480 - x
+ else
+ d = value - x
+ end
+ pipes.preValue = value
+ moveVirtualX(-d)
+ collisionDetect()
+ end
+ }
+
+ function pipes:start()
+ self.anim:start()
+ end
+
+ function pipes:stop()
+ self.anim:stop()
+ end
+
+ function pipes:reset()
+ pipesPosinit()
+ pipes.score = 0
+ pipes.preValue = 0
+ pipes.objPassing = -1
+ end
+
+ function pipes:setCollisionCB(collisionCB)
+ self.collisionCB = collisionCB
+ end
+
+ function pipes:setScoreUpdateCB(cb)
+ self.scoreUpdateCB = cb
+ end
+
+ return pipes
+end
+
+local function Bird(parent, birdMovedCB)
+ -- create bird Frame(sprite) in 5FPS
+ local bird = Frames(parent,
+ {IMAGE_PATH .. "bird1.png", IMAGE_PATH .. "bird2.png", IMAGE_PATH .. "bird3.png"}, 5)
+
+ local function birdVarInit()
+ bird.x = 240 - bird.w / 2
+ bird.y = 240 - bird.h / 2
+ bird.widget:set{
+ x = bird.x,
+ y = bird.y
+ }
+
+ bird.head = 0
+ bird.force = 0 -- in unit of m/s^2 rather than N
+ bird.velocity = 0 -- vertical verlocity
+ bird.time = 0 -- time stamp when it updates
+ bird.moving = false
+ end
+
+ birdVarInit()
+
+ bird.setY = function(self)
+ bird.widget:set{
+ y = bird.y
+ }
+ end
+
+ bird.setHead = function(self)
+ bird.widget:set{
+ angle = self.head
+ }
+ end
+
+ bird.applyForce = function(self, force)
+ self.force = force
+ if bird.moving then
+ return
+ end
+
+ bird.moving = true
+ self.y_anim:start()
+ end
+
+ bird.pressed = function(self)
+ bird:applyForce(-13)
+ bird.velocity = 0
+ end
+
+ bird.released = function(self)
+ bird:applyForce(9.8)
+ bird.velocity = 0
+ end
+
+ local function velocity2HeadAngle(v)
+ -- -9.8 ~ 9.8:90 ~ -90
+ return v * 60
+ end
+
+ -- y moving anim, in time.
+ bird.y_anim = bird.widget:Anim{
+ run = false,
+ start_value = 0,
+ end_value = 1000,
+ time = 1000, -- 1000 ms
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ path = "linear",
+ exec_cb = function(obj, tNow)
+ -- we use anim to get current time, can calculate position based on force/velocity
+ if tNow < bird.time then
+ tNow = tNow + 1000
+ end
+ local y = bird.y
+ local preT = bird.time
+ local v = bird.velocity
+ local t = tNow < preT and tNow + 1000 - preT or tNow - preT
+ t = t * 0.001 -- ms to s
+
+ v = bird.force * t + v
+ if v > 10 then
+ v = 10
+ end
+
+ if v < -10 then
+ v = -10
+ end
+
+ y = y + v * t * PIXEL_PER_METER
+ if y > BOTTOM_Y - 30 then
+ y = BOTTOM_Y - 30
+ v = 0
+ end
+ if y < TOP_Y then
+ y = TOP_Y
+ v = 0
+ end
+
+ bird.y = y
+ bird.time = tNow
+ bird.velocity = v
+ bird.head = velocity2HeadAngle(v)
+
+ birdMovedCB(bird.x, bird.y)
+ -- set y
+ bird:setY()
+ bird:setHead()
+ end
+ }
+
+ function bird:stop()
+ bird.y_anim:stop()
+ end
+
+ function bird:gameOver()
+ -- like it's released forever
+ bird.released()
+ end
+
+ function bird:start()
+ bird.y_anim:start()
+ end
+
+ function bird:reset()
+ bird.stop()
+ birdVarInit()
+ end
+
+ return bird;
+end
+
+local function Background(root, bgEventCB)
+ local bgLayer = screenCreate(root) -- background layer
+ bgLayer:add_flag(lvgl.FLAG.CLICKABLE) -- we accept event here
+
+ local bg = ImageScroll(bgLayer, IMAGE_PATH .. "bg_day.png", MOVE_SPEED * 0.4, 0)
+ local pipes = Pipes(bgLayer)
+ local land = ImageScroll(bgLayer, IMAGE_PATH .. "land.png", MOVE_SPEED, BOTTOM_Y)
+
+ bgLayer:onevent(lvgl.EVENT.PRESSED, function(obj, code)
+ bgEventCB(lvgl.EVENT.PRESSED)
+ end)
+
+ bgLayer:onevent(lvgl.EVENT.RELEASED, function(obj, code)
+ bgEventCB(lvgl.EVENT.RELEASED)
+ end)
+
+ return {
+ pipes = pipes
+ }
+end
+
+local function SysLayer(root)
+ local sysLayer = screenCreate(root) -- upper layer
+ return sysLayer
+end
+
+local function createPlayBtn(sysLayer, onEvent)
+ local playBtn = Image(sysLayer, IMAGE_PATH .. "button_play.png").widget
+ playBtn:add_flag(lvgl.FLAG.CLICKABLE)
+ playBtn:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ y_ofs = 80
+ }
+ }
+
+ playBtn:onevent(lvgl.EVENT.PRESSED, onEvent)
+
+ return playBtn
+end
+
+local function entry()
+ local scr = screenCreate()
+ local bgLayer
+ local mainLayer
+ local sysLayer
+ local bird
+ local pipes
+ local bgEventCB -- background layer pressed/released event
+ local birdMovedCB -- callback when bird position updates
+ local collisionCB -- callback when collision happends
+ local flagRunning = false
+ local gameStart -- API to start game
+ local gameOver -- API to stop game
+ local scoreLabel
+ local scoreBest = 0
+ local scoreNow = 0
+ local debouncing = false
+ -- global event process
+
+ local scoreUpdateCB = function(score)
+ scoreLabel:set{
+ text = string.format("%03d", score)
+ }
+ scoreNow = score
+ end
+
+ print("font:", lvgl.BUILTIN_FONT.MONTSERRAT_26)
+ gameStart = function()
+ if flagRunning then
+ return
+ end
+
+ bird:reset()
+ pipes:reset()
+ pipes:start()
+ bird:start()
+ flagRunning = true
+ scoreNow = 0
+ if scoreLabel then
+ scoreLabel:set{
+ text = string.format("%03d", 0)
+ }
+ end
+ end
+
+ gameOver = function()
+ if not flagRunning then
+ return
+ end
+
+ debouncing = true
+ flagRunning = false
+
+ pipes:stop()
+ bird:gameOver()
+ if scoreNow > scoreBest then
+ scoreBest = scoreNow
+ end
+
+ local gameoverImg = Image(sysLayer, IMAGE_PATH .. "text_game_over.png").widget
+ gameoverImg:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 100
+ }
+ }
+
+ gameoverImg:Anim{
+ run = true,
+ start_value = 0,
+ end_value = 3600,
+ time = 2000,
+ repeat_count = 2,
+ path = "bounce",
+ exec_cb = function(obj, value)
+ obj:set{
+ angle = value
+ }
+ end
+ }
+
+ local scoreImg = Image(sysLayer, IMAGE_PATH .. "score.png").widget
+ scoreImg:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ y_ofs = -20,
+ x_ofs = 0
+ }
+ }
+ scoreImg:Anim{
+ run = true,
+ start_value = 480,
+ end_value = 0,
+ time = 1000,
+ repeat_count = 1,
+ path = "ease_in",
+ exec_cb = function(obj, value)
+ obj:set{
+ align = {
+ type = lvgl.ALIGN.CENTER,
+ x_ofs = value,
+ y_ofs = -20
+ }
+ }
+ end
+ }
+
+ local scoreResultLabel = scoreImg:Label{
+ text = string.format("%03d", scoreNow),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ x_ofs = 0,
+ y_ofs = 25
+ }
+ }
+
+ local scoreBestLabel = scoreImg:Label{
+ text = string.format("%03d", scoreBest),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ align = {
+ type = lvgl.ALIGN.BOTTOM_MID,
+ x_ofs = 0,
+ y_ofs = -5
+ }
+ }
+ scoreNow = 0
+
+ local playBtn;
+ playBtn = createPlayBtn(sysLayer, function(obj, code)
+ if debouncing then
+ return
+ end
+
+ gameStart()
+ playBtn:delete()
+ playBtn = nil
+ gameoverImg:delete()
+ gameoverImg = nil
+ scoreImg:delete()
+ scoreImg = nil
+ end)
+
+ lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ debouncing = false
+ end
+ }
+ end
+
+ bgEventCB = function(event)
+ if not flagRunning then
+ return
+ end
+
+ if event == lvgl.EVENT.PRESSED then
+ bird:pressed()
+ else
+ bird:released()
+ end
+ end
+
+ local birdMovedCB = function(x, y)
+ pipes:setObjInfo(bird.x, bird.y) -- set intial bird position.
+ end
+
+ local collisionCB = function()
+ print("bird collision, stop game")
+ -- call later
+ lvgl.Timer {
+ period = 10,
+ cb = function(t)
+ t:delete()
+ gameOver()
+ end
+ }
+
+ end
+
+ -- background layer, including sky, then pipes and land above
+ bgLayer = Background(scr, bgEventCB) -- background layer
+ pipes = bgLayer.pipes -- get pipes from bg layer for set bird info etc.
+ pipes:setCollisionCB(collisionCB)
+ pipes:setScoreUpdateCB(scoreUpdateCB)
+
+ -- main layer, the bird
+ mainLayer = screenCreate(scr) -- main layer
+ bird = Bird(mainLayer, birdMovedCB)
+ pipes:setObjInfo(bird.x, bird.y, bird.w, bird.h)
+ -- system layer, score etc.
+ sysLayer = SysLayer(scr)
+
+ local title = Image(sysLayer, IMAGE_PATH .. "title.png").widget
+ title:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 80
+ }
+ }
+
+ local playBtn;
+ playBtn = createPlayBtn(sysLayer, function()
+ print("pressed")
+ gameStart()
+ playBtn:delete()
+ playBtn = nil
+ title:delete()
+ title = nil
+
+ local medal = Image(sysLayer, IMAGE_PATH .. "medals.png").widget
+ medal:set{
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ y_ofs = 10,
+ x_ofs = -50
+ }
+ }
+ scoreLabel = sysLayer:Label{
+ text = " 000",
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_28,
+ align = {
+ type = lvgl.ALIGN.TOP_MID,
+ x_ofs = 10,
+ y_ofs = 20
+ }
+ }
+ end)
+end
+
+entry()
+
+-- bird = Bird(, nil)
diff --git a/lib/luavgl/examples/flappyBird/land.png b/lib/luavgl/examples/flappyBird/land.png
new file mode 100644
index 00000000..b93a861a
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/land.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/medals.png b/lib/luavgl/examples/flappyBird/medals.png
new file mode 100644
index 00000000..c440df81
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/medals.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/pipe_down.png b/lib/luavgl/examples/flappyBird/pipe_down.png
new file mode 100644
index 00000000..e02d6946
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/pipe_down.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/pipe_up.png b/lib/luavgl/examples/flappyBird/pipe_up.png
new file mode 100644
index 00000000..6e8e8d5d
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/pipe_up.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/score.png b/lib/luavgl/examples/flappyBird/score.png
new file mode 100644
index 00000000..cf567b0c
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/score.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/text_game_over.png b/lib/luavgl/examples/flappyBird/text_game_over.png
new file mode 100644
index 00000000..b8c30fc2
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/text_game_over.png
Binary files differ
diff --git a/lib/luavgl/examples/flappyBird/title.png b/lib/luavgl/examples/flappyBird/title.png
new file mode 100644
index 00000000..557340f2
--- /dev/null
+++ b/lib/luavgl/examples/flappyBird/title.png
Binary files differ
diff --git a/lib/luavgl/examples/flexLayout.lua b/lib/luavgl/examples/flexLayout.lua
new file mode 100644
index 00000000..afafa5c8
--- /dev/null
+++ b/lib/luavgl/examples/flexLayout.lua
@@ -0,0 +1,27 @@
+local lvgl = require("lvgl")
+
+local root = lvgl.Object(nil, {
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap",
+ justify_content = "center",
+ align_items = "center",
+ align_content = "center",
+ },
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ align = lvgl.ALIGN.CENTER
+})
+
+for i = 1, 10 do
+ local item = root:Object {
+ w = 100,
+ h = lvgl.PCT(100),
+ }
+ item:clear_flag(lvgl.FLAG.SCROLLABLE)
+
+ local label = item:Label {
+ text = string.format("label %d", i)
+ }
+ label:center()
+end
diff --git a/lib/luavgl/examples/font.lua b/lib/luavgl/examples/font.lua
new file mode 100644
index 00000000..7d650cc0
--- /dev/null
+++ b/lib/luavgl/examples/font.lua
@@ -0,0 +1,18 @@
+local label1 = lvgl.Label(nil, {
+ x = 0, y = 0,
+ text_font = lvgl.Font("montserrat", 32, "normal"),
+ text = "Hello Font",
+ align = lvgl.ALIGN.CENTER
+})
+
+print("label1: ", label1)
+
+lvgl.Label(nil, {
+ -- x= 0, y = 0,
+ -- text_font = lvgl.Font("MiSansW medium, montserrat", 24),
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_22,
+ text = "Hello Font2",
+}):align_to({
+ type = lvgl.ALIGN.OUT_BOTTOM_LEFT,
+ base = label1,
+})
diff --git a/lib/luavgl/examples/fs.lua b/lib/luavgl/examples/fs.lua
new file mode 100644
index 00000000..6672f4d0
--- /dev/null
+++ b/lib/luavgl/examples/fs.lua
@@ -0,0 +1,59 @@
+local function fs_example()
+ -- local f <close>, msg, code = lvgl.open_file(SCRIPT_PATH .. "/tmp.txt") -- for lua 5.4
+ local f, msg, code = lvgl.fs.open_file(SCRIPT_PATH .. "/tmp.txt", "rw")
+ if not f then
+ print("failed: ", msg, code)
+ return
+ end
+
+ print("f: ", f)
+
+ f:write("0123456789", 123, "\n", "the remaining text")
+ f:seek("set", 0) -- go back
+ local header, remaining = f:read(10, "*a")
+ f:close()
+ if not header then
+ print("read failed or EOF")
+ return
+ end
+
+ print("header len:", #header, ": ", header)
+ print("remaining: ", remaining)
+
+ lvgl.Label(nil, {
+ x = 0,
+ y = 0,
+ text_font = lvgl.Font("montserrat", 20, "normal"),
+ text = header .. remaining,
+ align = lvgl.ALIGN.TOP_LEFT
+ })
+
+ local list = lvgl.List(nil, {
+ align = lvgl.ALIGN.TOP_RIGHT,
+ pad_all = 10,
+ text_font = lvgl.BUILTIN_FONT.MONTSERRAT_12
+ })
+ list:add_text("Directory list:")
+
+ -- local dir <close>, msg, code = lvgl.fs.open_dir(SCRIPT_PATH .. "/")
+ local dir, msg, code = lvgl.fs.open_dir(SCRIPT_PATH .. "/")
+ if not dir then
+ print("open dir failed: ", msg, code)
+ return
+ end
+
+ while true do
+ local d = dir:read()
+ if not d then break end
+ local is_dir = string.byte(d, 1) == string.byte("/", 1)
+ local str = (is_dir and "dir: " or "file: ") .. d
+ print(str)
+
+ list:add_text(str):set{
+ border_width = 1,
+ }
+ end
+ dir:close()
+end
+
+fs_example()
diff --git a/lib/luavgl/examples/group.lua b/lib/luavgl/examples/group.lua
new file mode 100644
index 00000000..f80dd325
--- /dev/null
+++ b/lib/luavgl/examples/group.lua
@@ -0,0 +1,61 @@
+local function group_example()
+ local g = lvgl.group.create()
+ g:set_default()
+
+ -- for demo purpose, set all indev to use this group
+ local indev = nil
+ while true do
+ indev = lvgl.indev.get_next(indev)
+ if not indev then break end
+
+ local t = indev:get_type()
+ if t == 2 or t == 4 then
+ indev:set_group(g)
+ end
+ end
+
+ local style = lvgl.Style({
+ border_width = 5,
+ border_color = "#a00",
+ })
+
+ local root = lvgl.Object(nil, {
+ w = lvgl.PCT(100),
+ h = lvgl.PCT(100),
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#aaa",
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+ })
+
+ root:add_style(style, lvgl.STATE.FOCUSED)
+
+ for _ = 1, 5 do
+ local obj = root:Object({
+ w = lvgl.PCT(50),
+ h = lvgl.PCT(50),
+ bg_color = "#555",
+ })
+
+ obj:add_style(style, lvgl.STATE.FOCUSED)
+
+ obj:onClicked(function(obj, code)
+ print("clicked: ", obj)
+ end)
+
+ obj:onevent(lvgl.EVENT.FOCUSED, function(obj, code)
+ print("focused: ", obj)
+ obj:scroll_to_view(true)
+ end)
+
+ obj:onevent(lvgl.EVENT.DEFOCUSED, function(obj, code)
+ print("defocused: ", obj)
+ end)
+
+ g:add_obj(obj)
+ end
+end
+
+group_example()
diff --git a/lib/luavgl/examples/indev.lua b/lib/luavgl/examples/indev.lua
new file mode 100644
index 00000000..563b0f00
--- /dev/null
+++ b/lib/luavgl/examples/indev.lua
@@ -0,0 +1,61 @@
+local function indev_example()
+ local indev = lvgl.indev.get_act()
+ print("act indev: ", indev)
+
+
+ indev:on_event(lvgl.EVENT.SHORT_CLICKED, function ()
+ print("indev pressed")
+ end)
+ local obj_act = lvgl.indev.get_obj_act()
+ print("obj_act: ", obj_act)
+
+ local root = lvgl.Object(nil, {
+ w = lvgl.PCT(30),
+ h = lvgl.PCT(30),
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#aaa",
+ })
+
+ root:Object({
+ w = 1000,
+ h = 1000,
+ align = lvgl.ALIGN.CENTER,
+ bg_color = "#555",
+ }):onevent(lvgl.EVENT.ALL, function(obj, code)
+ local indev = lvgl.indev.get_act()
+
+ if not indev then return end
+
+ if code == lvgl.EVENT.PRESSED then
+ local x, y = indev:get_point()
+ print("pressed: ", x, y)
+ end
+
+ if code == lvgl.EVENT.PRESSING then
+ local x, y = indev:get_vect()
+ print("vect: ", x, y)
+ end
+
+ if code == lvgl.EVENT.GESTURE then
+ local gesture_dir = indev:get_gesture_dir()
+ print("gesture_dir: ", gesture_dir)
+ end
+
+ if code == lvgl.EVENT.RELEASED then
+ local scroll_dir = indev:get_scroll_dir()
+ print("scroll_dir: ", scroll_dir)
+
+ local scroll_obj = indev:get_scroll_obj()
+ print("scroll_obj: ", scroll_obj)
+
+ local x, y = indev:get_vect()
+ local obj_search = obj:indev_search(x, y)
+ print("indev search obj: ", obj_search)
+
+ local obj_search = obj:indev_search({x, y})
+ print("indev search obj2: ", obj_search)
+ end
+ end)
+end
+
+indev_example()
diff --git a/lib/luavgl/examples/keyboard.lua b/lib/luavgl/examples/keyboard.lua
new file mode 100644
index 00000000..feb85d32
--- /dev/null
+++ b/lib/luavgl/examples/keyboard.lua
@@ -0,0 +1,46 @@
+local root = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ pad_all = 0,
+ bg_color = "#333",
+ bg_opa = lvgl.OPA(50),
+})
+root:add_flag(lvgl.FLAG.CLICKABLE)
+
+local ta = root:Textarea {
+ password_mode = false,
+ one_line = true,
+ text_font = lvgl.Font("montserrat", 22),
+ text_color = "#FFF",
+ text = "Input text here",
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_opa = 0,
+ border_width = 2,
+ pad_all = 2,
+ align = lvgl.ALIGN.TOP_MID,
+}
+
+print("created textarea: ", ta)
+
+local keyboard = root:Keyboard {
+ textarea = ta,
+ align = lvgl.ALIGN.BOTTOM_MID,
+}
+print("created keyboard: ", keyboard)
+
+ta:onevent(lvgl.EVENT.PRESSED, function(obj, code)
+ keyboard:clear_flag(lvgl.FLAG.HIDDEN)
+end)
+
+ta:onevent(lvgl.EVENT.READY, function()
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
+
+ta:onevent(lvgl.EVENT.CANCEL, function(obj, code)
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
+
+root:onClicked(function (obj, code)
+ keyboard:add_flag(lvgl.FLAG.HIDDEN)
+end)
diff --git a/lib/luavgl/examples/pointer.lua b/lib/luavgl/examples/pointer.lua
new file mode 100644
index 00000000..c344f77b
--- /dev/null
+++ b/lib/luavgl/examples/pointer.lua
@@ -0,0 +1,46 @@
+local root = lvgl.Object(nil, {
+ w = 0,
+ h = 0,
+ pad_all = 0,
+ align = lvgl.ALIGN.CENTER,
+ border_width = 1,
+ border_color = "#F00",
+})
+
+local pointer = root:Pointer {
+ src = SCRIPT_PATH .. "/assets/second.png",
+ x = -20,
+ y = -233,
+ border_width = 1,
+ border_color = "#0F0",
+ range = {
+ valueStart = 0,
+ valueRange = 60000,
+ angleStart = 0,
+ angleRange = -3600
+ },
+}
+
+-- lvgl requires src set firstly, since lua cannot guarantee table order,
+-- we set pivot after image src is set.
+pointer:set {
+ pivot = { x = 20, y = 233 },
+}
+
+print("img w,h: ", pointer:get_img_size())
+
+pointer:Anim{
+ run = true,
+ start_value = 0,
+ end_value = 60000,
+ duration = 60000,
+ path = "linear",
+ repeat_count = lvgl.ANIM_REPEAT_INFINITE,
+ exec_cb = function(obj, value)
+ obj:set {
+ value = value
+ }
+ end,
+}
+
+print("created pointer: ", pointer)
diff --git a/lib/luavgl/examples/protectedcall.lua b/lib/luavgl/examples/protectedcall.lua
new file mode 100644
index 00000000..a23fd9db
--- /dev/null
+++ b/lib/luavgl/examples/protectedcall.lua
@@ -0,0 +1,36 @@
+
+local function testCrash()
+ print(nil .. "")
+end
+
+local function testCrash2()
+ testCrash()
+end
+local function testCrash3()
+ testCrash2()
+end
+local function testCrash4()
+ testCrash3()
+end
+
+local function testCrash5()
+ testCrash4()
+end
+
+local function testCrash6()
+ testCrash5()
+end
+
+lvgl.Label(nil, {
+ text = "crash in 1 second.",
+ align = lvgl.ALIGN.CENTER
+})
+
+lvgl.Timer {
+ period = 1000,
+ cb = function(t)
+ t:delete()
+ -- crash in callback function is protected.
+ testCrash6()
+ end
+} \ No newline at end of file
diff --git a/lib/luavgl/examples/roller.lua b/lib/luavgl/examples/roller.lua
new file mode 100644
index 00000000..0d763327
--- /dev/null
+++ b/lib/luavgl/examples/roller.lua
@@ -0,0 +1,14 @@
+local roller = lvgl.Roller(nil, {
+ options = {
+ options = "January\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember",
+ mode = lvgl.ROLLER_MODE.INFINITE,
+ },
+ visible_cnt = 4,
+ align = lvgl.ALIGN.CENTER
+})
+
+print("create roller:", roller)
+roller:onevent(lvgl.EVENT.VALUE_CHANGED, function (obj, code)
+ print(obj:get_selected_str())
+end)
+
diff --git a/lib/luavgl/examples/tests.lua b/lib/luavgl/examples/tests.lua
new file mode 100644
index 00000000..88d224ff
--- /dev/null
+++ b/lib/luavgl/examples/tests.lua
@@ -0,0 +1,45 @@
+local container = lvgl.Object(nil, {
+ w = lvgl.HOR_RES(),
+ h = lvgl.VER_RES(),
+ bg_color = "#888",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 0,
+ flex = {
+ flex_direction = "row",
+ flex_wrap = "wrap"
+ }
+})
+
+local function createBtn(parent, name)
+ local root = parent:Object {
+ w = lvgl.SIZE_CONTENT,
+ h = lvgl.SIZE_CONTENT,
+ bg_color = "#ccc",
+ bg_opa = lvgl.OPA(100),
+ border_width = 0,
+ radius = 10,
+ pad_all = 20,
+ }
+
+ root:onClicked(function()
+ container:delete()
+ require(name)
+ end)
+
+ root:Label {
+ text = name,
+ text_color = "#333",
+ align = lvgl.ALIGN.CENTER,
+ }
+end
+
+createBtn(container, "font")
+createBtn(container, "uservalue")
+createBtn(container, "roller")
+createBtn(container, "dropdown")
+createBtn(container, "extension")
+createBtn(container, "fs")
+createBtn(container, "indev")
+createBtn(container, "group")
+createBtn(container, "protectedcall")
diff --git a/lib/luavgl/examples/uservalue.lua b/lib/luavgl/examples/uservalue.lua
new file mode 100644
index 00000000..414d8f0b
--- /dev/null
+++ b/lib/luavgl/examples/uservalue.lua
@@ -0,0 +1,34 @@
+collectgarbage("collect")
+print("initial: ", collectgarbage("count"))
+
+local str_t = {}
+for i = 1, 1024 * 1024 do
+ str_t[#str_t + 1] = 'a'
+end
+
+local str = table.concat(str_t)
+str_t = nil
+
+collectgarbage("collect")
+collectgarbage("collect")
+print("after string collect: ", collectgarbage("count"))
+
+for i = 1, 10 do
+ local label = lvgl.Label(nil)
+ label:set_text_static(str)
+ label:delete()
+end
+str = nil
+
+print("after set text: ", collectgarbage("count"))
+collectgarbage("collect")
+print("after collect: ", collectgarbage("count"))
+print("again: ", collectgarbage("count"))
+collectgarbage("collect")
+print("again2: ", collectgarbage("count"))
+collectgarbage("collect")
+print("again3: ", collectgarbage("count"))
+
+local label = lvgl.Label(nil)
+label:set({ text = "Test Done" })
+label:center()
diff --git a/lib/luavgl/simulator/CMakeLists.txt b/lib/luavgl/simulator/CMakeLists.txt
new file mode 100644
index 00000000..b0932744
--- /dev/null
+++ b/lib/luavgl/simulator/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(simulator)
+
+add_custom_target(run COMMAND simulator)
+
+file(GLOB_RECURSE WIDGETS ${CMAKE_CURRENT_SOURCE_DIR}/widgets/*.c)
+add_executable(simulator main.c mouse_cursor_icon.c ${WIDGETS})
+target_include_directories(simulator PRIVATE widgets)
+# add_subdirectory(widgets)
+
+target_link_libraries(simulator luavgl lvgl lvgl::demos lvgl::drivers ${SDL2_LIBRARIES} ${LUA_LINK_LIBS})
+target_include_directories(simulator PRIVATE ${LUA_INCLUDE_DIRS})
diff --git a/lib/luavgl/simulator/lv_conf.h b/lib/luavgl/simulator/lv_conf.h
new file mode 100644
index 00000000..bb4a10d8
--- /dev/null
+++ b/lib/luavgl/simulator/lv_conf.h
@@ -0,0 +1,703 @@
+/**
+ * @file lv_conf.h
+ * Configuration file for v8.2.0
+ */
+
+/*
+ * Copy this file as `lv_conf.h`
+ * 1. simply next to the `lvgl` folder
+ * 2. or any other places and
+ * - define `LV_CONF_INCLUDE_SIMPLE`
+ * - add the path as include path
+ */
+
+/* clang-format off */
+#if 1 /*Set it to "1" to enable content*/
+
+#ifndef LV_CONF_H
+#define LV_CONF_H
+
+#include <stdint.h>
+
+/*====================
+ COLOR SETTINGS
+ *====================*/
+
+/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
+#define LV_COLOR_DEPTH 32
+
+/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
+#define LV_COLOR_16_SWAP 0
+
+/*Enable more complex drawing routines to manage screens transparency.
+ *Can be used if the UI is above another layer, e.g. an OSD menu or video player.
+ *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
+#define LV_COLOR_SCREEN_TRANSP 0
+
+/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
+ * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
+#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)
+
+/*Images pixels with this color will not be drawn if they are chroma keyed)*/
+#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
+
+/*=========================
+ MEMORY SETTINGS
+ *=========================*/
+
+/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
+#define LV_MEM_CUSTOM 0
+#if LV_MEM_CUSTOM == 0
+ /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
+ #define LV_MEM_SIZE (10* 1024 * 1024U) /*[bytes]*/
+
+ /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
+ #define LV_MEM_ADR 0 /*0: unused*/
+ /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
+ #if LV_MEM_ADR == 0
+ //#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
+ //#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
+ #endif
+
+#else /*LV_MEM_CUSTOM*/
+ #define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
+ #define LV_MEM_CUSTOM_ALLOC malloc
+ #define LV_MEM_CUSTOM_FREE free
+ #define LV_MEM_CUSTOM_REALLOC realloc
+#endif /*LV_MEM_CUSTOM*/
+
+/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
+ *You will see an error log message if there wasn't enough buffers. */
+#define LV_MEM_BUF_MAX_NUM 16
+
+/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
+#define LV_MEMCPY_MEMSET_STD 0
+
+/*====================
+ HAL SETTINGS
+ *====================*/
+
+/*Default display refresh period. LVG will redraw changed areas with this period time*/
+#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
+
+/*Input device read period in milliseconds*/
+#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
+
+/*Use a custom tick source that tells the elapsed time in milliseconds.
+ *It removes the need to manually update the tick with `lv_tick_inc()`)*/
+#define LV_TICK_CUSTOM 1
+#if LV_TICK_CUSTOM
+ #define LV_TICK_CUSTOM_INCLUDE <SDL.h> /*Header for the system time function*/
+ #define LV_TICK_CUSTOM_SYS_TIME_EXPR SDL_GetTicks() /*Expression evaluating to current system time in ms*/
+#endif /*LV_TICK_CUSTOM*/
+
+/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
+ *(Not so important, you can adjust it to modify default sizes and spaces)*/
+#define LV_DPI_DEF 130 /*[px/inch]*/
+
+/*=======================
+ * FEATURE CONFIGURATION
+ *=======================*/
+
+/*-------------
+ * Drawing
+ *-----------*/
+
+/*Enable complex draw engine.
+ *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
+#define LV_DRAW_COMPLEX 1
+#if LV_DRAW_COMPLEX != 0
+
+ /*Allow buffering some shadow calculation.
+ *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
+ *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
+ #define LV_SHADOW_CACHE_SIZE 0
+
+ /* Set number of maximally cached circle data.
+ * The circumference of 1/4 circle are saved for anti-aliasing
+ * radius * 4 bytes are used per circle (the most often used radiuses are saved)
+ * 0: to disable caching */
+ #define LV_CIRCLE_CACHE_SIZE 4
+#endif /*LV_DRAW_COMPLEX*/
+
+/*Default image cache size. Image caching keeps the images opened.
+ *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
+ *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
+ *However the opened images might consume additional RAM.
+ *0: to disable caching*/
+#define LV_IMG_CACHE_DEF_SIZE 8
+
+/*Number of stops allowed per gradient. Increase this to allow more stops.
+ *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
+#define LV_GRADIENT_MAX_STOPS 2
+
+/*Default gradient buffer size.
+ *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
+ *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.
+ *If the cache is too small the map will be allocated only while it's required for the drawing.
+ *0 mean no caching.*/
+#define LV_GRAD_CACHE_DEF_SIZE 512
+
+/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
+ *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
+ *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
+#define LV_DITHER_GRADIENT 1
+#if LV_DITHER_GRADIENT
+ /*Add support for error diffusion dithering.
+ *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
+ *The increase in memory consumption is (24 bits * object's width)*/
+ #define LV_DITHER_ERROR_DIFFUSION 1
+#endif
+
+/*Maximum buffer size to allocate for rotation.
+ *Only used if software rotation is enabled in the display driver.*/
+#define LV_DISP_ROT_MAX_BUF (32*1024)
+
+/*-------------
+ * GPU
+ *-----------*/
+
+/*Use STM32's DMA2D (aka Chrom Art) GPU*/
+#define LV_USE_GPU_STM32_DMA2D 0
+#if LV_USE_GPU_STM32_DMA2D
+ /*Must be defined to include path of CMSIS header of target processor
+ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
+ #define LV_GPU_DMA2D_CMSIS_INCLUDE
+#endif
+
+/*Use NXP's PXP GPU iMX RTxxx platforms*/
+#define LV_USE_GPU_NXP_PXP 0
+#if LV_USE_GPU_NXP_PXP
+ /*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
+ * and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
+ * has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
+ *0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
+ */
+ #define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
+#endif
+
+/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
+#define LV_USE_GPU_NXP_VG_LITE 0
+
+/*Use SDL renderer API*/
+#define LV_USE_GPU_SDL 0
+#if LV_USE_GPU_SDL
+ #define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
+ /*Texture cache size, 8MB by default*/
+ #define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8 )
+ /*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/
+ #define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
+#endif
+
+/*-------------
+ * Logging
+ *-----------*/
+
+/*Enable the log module*/
+#define LV_USE_LOG 1
+#if LV_USE_LOG
+
+ /*How important log should be added:
+ *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
+ *LV_LOG_LEVEL_INFO Log important events
+ *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
+ *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
+ *LV_LOG_LEVEL_USER Only logs added by the user
+ *LV_LOG_LEVEL_NONE Do not log anything*/
+ #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
+
+ /*1: Print the log with 'printf';
+ *0: User need to register a callback with `lv_log_register_print_cb()`*/
+ #define LV_LOG_PRINTF 1
+
+ /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
+ #define LV_LOG_TRACE_MEM 1
+ #define LV_LOG_TRACE_TIMER 1
+ #define LV_LOG_TRACE_INDEV 1
+ #define LV_LOG_TRACE_DISP_REFR 1
+ #define LV_LOG_TRACE_EVENT 1
+ #define LV_LOG_TRACE_OBJ_CREATE 1
+ #define LV_LOG_TRACE_LAYOUT 1
+ #define LV_LOG_TRACE_ANIM 1
+
+#endif /*LV_USE_LOG*/
+
+/*-------------
+ * Asserts
+ *-----------*/
+
+/*Enable asserts if an operation is failed or an invalid data is found.
+ *If LV_USE_LOG is enabled an error message will be printed on failure*/
+#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
+#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
+#define LV_USE_ASSERT_STYLE 1 /*Check if the styles are properly initialized. (Very fast, recommended)*/
+#define LV_USE_ASSERT_MEM_INTEGRITY 1 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
+#define LV_USE_ASSERT_OBJ 1 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
+
+/*Add a custom handler when assert happens e.g. to restart the MCU*/
+#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
+#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
+
+/*-------------
+ * Others
+ *-----------*/
+
+/*1: Show CPU usage and FPS count*/
+#define LV_USE_PERF_MONITOR 1
+#if LV_USE_PERF_MONITOR
+ #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
+#endif
+
+/*1: Show the used memory and the memory fragmentation
+ * Requires LV_MEM_CUSTOM = 0*/
+#define LV_USE_MEM_MONITOR 1
+#if LV_USE_MEM_MONITOR
+ #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
+#endif
+
+/*1: Draw random colored rectangles over the redrawn areas*/
+#define LV_USE_REFR_DEBUG 0
+
+/*Change the built in (v)snprintf functions*/
+#define LV_SPRINTF_CUSTOM 0
+#if LV_SPRINTF_CUSTOM
+ #define LV_SPRINTF_INCLUDE <stdio.h>
+ #define lv_snprintf snprintf
+ #define lv_vsnprintf vsnprintf
+#else /*LV_SPRINTF_CUSTOM*/
+ #define LV_SPRINTF_USE_FLOAT 0
+#endif /*LV_SPRINTF_CUSTOM*/
+
+#define LV_USE_USER_DATA 1
+
+/*Garbage Collector settings
+ *Used if lvgl is bound to higher level language and the memory is managed by that language*/
+#define LV_ENABLE_GC 0
+#if LV_ENABLE_GC != 0
+ #define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
+#endif /*LV_ENABLE_GC*/
+
+/*=====================
+ * COMPILER SETTINGS
+ *====================*/
+
+/*For big endian systems set to 1*/
+#define LV_BIG_ENDIAN_SYSTEM 0
+
+/*Define a custom attribute to `lv_tick_inc` function*/
+#define LV_ATTRIBUTE_TICK_INC
+
+/*Define a custom attribute to `lv_timer_handler` function*/
+#define LV_ATTRIBUTE_TIMER_HANDLER
+
+/*Define a custom attribute to `lv_disp_flush_ready` function*/
+#define LV_ATTRIBUTE_FLUSH_READY
+
+/*Required alignment size for buffers*/
+#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
+
+/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
+ * E.g. __attribute__((aligned(4)))*/
+#define LV_ATTRIBUTE_MEM_ALIGN
+
+/*Attribute to mark large constant arrays for example font's bitmaps*/
+#define LV_ATTRIBUTE_LARGE_CONST
+
+/*Compiler prefix for a big array declaration in RAM*/
+#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
+
+/*Place performance critical functions into a faster memory (e.g RAM)*/
+#define LV_ATTRIBUTE_FAST_MEM
+
+/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
+#define LV_ATTRIBUTE_DMA
+
+/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
+ *should also appear on LVGL binding API such as Micropython.*/
+#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
+
+/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
+#define LV_USE_LARGE_COORD 0
+
+/*==================
+ * FONT USAGE
+ *===================*/
+
+/*Montserrat fonts with ASCII range and some symbols using bpp = 4
+ *https://fonts.google.com/specimen/Montserrat*/
+#define LV_FONT_MONTSERRAT_8 1
+#define LV_FONT_MONTSERRAT_10 1
+#define LV_FONT_MONTSERRAT_12 1
+#define LV_FONT_MONTSERRAT_14 1
+#define LV_FONT_MONTSERRAT_16 1
+#define LV_FONT_MONTSERRAT_18 1
+#define LV_FONT_MONTSERRAT_20 1
+#define LV_FONT_MONTSERRAT_22 1
+#define LV_FONT_MONTSERRAT_24 1
+#define LV_FONT_MONTSERRAT_26 1
+#define LV_FONT_MONTSERRAT_28 1
+#define LV_FONT_MONTSERRAT_30 1
+#define LV_FONT_MONTSERRAT_32 1
+#define LV_FONT_MONTSERRAT_34 1
+#define LV_FONT_MONTSERRAT_36 1
+#define LV_FONT_MONTSERRAT_38 1
+#define LV_FONT_MONTSERRAT_40 1
+#define LV_FONT_MONTSERRAT_42 1
+#define LV_FONT_MONTSERRAT_44 1
+#define LV_FONT_MONTSERRAT_46 1
+#define LV_FONT_MONTSERRAT_48 1
+
+/*Demonstrate special features*/
+#define LV_FONT_MONTSERRAT_12_SUBPX 1
+#define LV_FONT_MONTSERRAT_28_COMPRESSED 1 /*bpp = 3*/
+#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 1 /*Hebrew, Arabic, Persian letters and all their forms*/
+#define LV_FONT_SIMSUN_16_CJK 1 /*1000 most common CJK radicals*/
+
+/*Pixel perfect monospace fonts*/
+#define LV_FONT_UNSCII_8 1
+#define LV_FONT_UNSCII_16 1
+
+/*Optionally declare custom fonts here.
+ *You can use these fonts as default font too and they will be available globally.
+ *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
+#define LV_FONT_CUSTOM_DECLARE
+
+/*Always set a default font*/
+#define LV_FONT_DEFAULT &lv_font_montserrat_14
+
+/*Enable handling large font and/or fonts with a lot of characters.
+ *The limit depends on the font size, font face and bpp.
+ *Compiler error will be triggered if a font needs it.*/
+#define LV_FONT_FMT_TXT_LARGE 1
+
+/*Enables/disables support for compressed fonts.*/
+#define LV_USE_FONT_COMPRESSED 1
+
+/*Enable subpixel rendering*/
+#define LV_USE_FONT_SUBPX 1
+#if LV_USE_FONT_SUBPX
+ /*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
+ #define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
+#endif
+
+/*=================
+ * TEXT SETTINGS
+ *=================*/
+
+/**
+ * Select a character encoding for strings.
+ * Your IDE or editor should have the same character encoding
+ * - LV_TXT_ENC_UTF8
+ * - LV_TXT_ENC_ASCII
+ */
+#define LV_TXT_ENC LV_TXT_ENC_UTF8
+
+/*Can break (wrap) texts on these chars*/
+#define LV_TXT_BREAK_CHARS " ,.;:-_"
+
+/*If a word is at least this long, will break wherever "prettiest"
+ *To disable, set to a value <= 0*/
+#define LV_TXT_LINE_BREAK_LONG_LEN 0
+
+/*Minimum number of characters in a long word to put on a line before a break.
+ *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
+#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
+
+/*Minimum number of characters in a long word to put on a line after a break.
+ *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
+#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
+
+/*The control character to use for signalling text recoloring.*/
+#define LV_TXT_COLOR_CMD "#"
+
+/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
+ *The direction will be processed according to the Unicode Bidirectional Algorithm:
+ *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
+#define LV_USE_BIDI 1
+#if LV_USE_BIDI
+ /*Set the default direction. Supported values:
+ *`LV_BASE_DIR_LTR` Left-to-Right
+ *`LV_BASE_DIR_RTL` Right-to-Left
+ *`LV_BASE_DIR_AUTO` detect texts base direction*/
+ #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
+#endif
+
+/*Enable Arabic/Persian processing
+ *In these languages characters should be replaced with an other form based on their position in the text*/
+#define LV_USE_ARABIC_PERSIAN_CHARS 0
+
+/*==================
+ * WIDGET USAGE
+ *================*/
+
+/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
+
+#define LV_USE_ARC 1
+
+#define LV_USE_ANIMIMG 1
+
+#define LV_USE_BAR 1
+
+#define LV_USE_BTN 1
+
+#define LV_USE_BTNMATRIX 1
+
+#define LV_USE_CANVAS 1
+
+#define LV_USE_CHECKBOX 1
+
+#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/
+
+#define LV_USE_IMG 1 /*Requires: lv_label*/
+
+#define LV_USE_LABEL 1
+#if LV_USE_LABEL
+ #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
+ #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
+#endif
+
+#define LV_USE_LINE 1
+
+#define LV_USE_ROLLER 1 /*Requires: lv_label*/
+#if LV_USE_ROLLER
+ #define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
+#endif
+
+#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
+
+#define LV_USE_SWITCH 1
+
+#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
+#if LV_USE_TEXTAREA != 0
+ #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
+#endif
+
+#define LV_USE_TABLE 1
+
+#define LV_USE_POINTER 1
+#define LV_USE_ANALOG_TIME 1
+
+/*==================
+ * EXTRA COMPONENTS
+ *==================*/
+
+/*-----------
+ * Widgets
+ *----------*/
+#define LV_USE_CALENDAR 1
+#if LV_USE_CALENDAR
+ #define LV_CALENDAR_WEEK_STARTS_MONDAY 0
+ #if LV_CALENDAR_WEEK_STARTS_MONDAY
+ #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
+ #else
+ #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
+ #endif
+
+ #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
+ #define LV_USE_CALENDAR_HEADER_ARROW 1
+ #define LV_USE_CALENDAR_HEADER_DROPDOWN 1
+#endif /*LV_USE_CALENDAR*/
+
+#define LV_USE_CHART 1
+
+#define LV_USE_COLORWHEEL 1
+
+#define LV_USE_IMGBTN 1
+
+#define LV_USE_KEYBOARD 1
+
+#define LV_USE_LED 1
+
+#define LV_USE_LIST 1
+
+#define LV_USE_MENU 1
+
+#define LV_USE_METER 1
+
+#define LV_USE_MSGBOX 1
+
+#define LV_USE_SPINBOX 1
+
+#define LV_USE_SPINNER 1
+
+#define LV_USE_TABVIEW 1
+
+#define LV_USE_TILEVIEW 1
+
+#define LV_USE_WIN 1
+
+#define LV_USE_SPAN 1
+#if LV_USE_SPAN
+ /*A line text can contain maximum num of span descriptor */
+ #define LV_SPAN_SNIPPET_STACK_SIZE 64
+#endif
+
+/*-----------
+ * Themes
+ *----------*/
+
+/*A simple, impressive and very complete theme*/
+#define LV_USE_THEME_DEFAULT 1
+#if LV_USE_THEME_DEFAULT
+
+ /*0: Light mode; 1: Dark mode*/
+ #define LV_THEME_DEFAULT_DARK 0
+
+ /*1: Enable grow on press*/
+ #define LV_THEME_DEFAULT_GROW 1
+
+ /*Default transition time in [ms]*/
+ #define LV_THEME_DEFAULT_TRANSITION_TIME 80
+#endif /*LV_USE_THEME_DEFAULT*/
+
+/*A very simple theme that is a good starting point for a custom theme*/
+#define LV_USE_THEME_BASIC 1
+
+/*A theme designed for monochrome displays*/
+#define LV_USE_THEME_MONO 1
+
+/*-----------
+ * Layouts
+ *----------*/
+
+/*A layout similar to Flexbox in CSS.*/
+#define LV_USE_FLEX 1
+
+/*A layout similar to Grid in CSS.*/
+#define LV_USE_GRID 1
+
+/*---------------------
+ * 3rd party libraries
+ *--------------------*/
+
+/*File system interfaces for common APIs */
+
+/*API for fopen, fread, etc*/
+#define LV_USE_FS_STDIO 0
+#if LV_USE_FS_STDIO
+ #define LV_FS_STDIO_LETTER 'A' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
+ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
+ #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
+#endif
+
+/*API for open, read, etc*/
+#define LV_USE_FS_POSIX 1
+#if LV_USE_FS_POSIX
+ #define LV_FS_POSIX_LETTER '/' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
+ #define LV_FS_POSIX_PATH "/" /*Set the working directory. File/directory paths will be appended to it.*/
+ #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
+#endif
+
+/*API for CreateFile, ReadFile, etc*/
+#define LV_USE_FS_WIN32 0
+#if LV_USE_FS_WIN32
+ #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
+ #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
+ #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
+#endif
+
+/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
+#define LV_USE_FS_FATFS 0
+#if LV_USE_FS_FATFS
+ #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
+ #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
+#endif
+
+/*PNG decoder library*/
+#define LV_USE_PNG 1
+
+/*BMP decoder library*/
+#define LV_USE_BMP 1
+
+/* JPG + split JPG decoder library.
+ * Split JPG is a custom format optimized for embedded systems. */
+#define LV_USE_SJPG 1
+
+/*GIF decoder library*/
+#define LV_USE_GIF 1
+
+/*QR code library*/
+#define LV_USE_QRCODE 1
+
+/*FreeType library*/
+#define LV_USE_FREETYPE 0
+#if LV_USE_FREETYPE
+ /*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
+ #define LV_FREETYPE_CACHE_SIZE (16 * 1024)
+ #if LV_FREETYPE_CACHE_SIZE >= 0
+ /* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
+ /* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
+ /* if font size >= 256, must be configured as image cache */
+ #define LV_FREETYPE_SBIT_CACHE 0
+ /* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
+ /* (0:use system defaults) */
+ #define LV_FREETYPE_CACHE_FT_FACES 0
+ #define LV_FREETYPE_CACHE_FT_SIZES 0
+ #endif
+#endif
+
+/*Rlottie library*/
+#define LV_USE_RLOTTIE 0
+
+/*FFmpeg library for image decoding and playing videos
+ *Supports all major image formats so do not enable other image decoder with it*/
+#define LV_USE_FFMPEG 0
+#if LV_USE_FFMPEG
+ /*Dump input information to stderr*/
+ #define LV_FFMPEG_AV_DUMP_FORMAT 0
+#endif
+
+/*-----------
+ * Others
+ *----------*/
+
+/*1: Enable API to take snapshot for object*/
+#define LV_USE_SNAPSHOT 1
+
+/*1: Enable Monkey test*/
+#define LV_USE_MONKEY 1
+
+/*1: Enable grid navigation*/
+#define LV_USE_GRIDNAV 1
+
+/*==================
+* EXAMPLES
+*==================*/
+
+/*Enable the examples to be built with the library*/
+#define LV_BUILD_EXAMPLES 1
+
+/*===================
+ * DEMO USAGE
+ ====================*/
+
+/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
+#define LV_USE_DEMO_WIDGETS 1
+#if LV_USE_DEMO_WIDGETS
+#define LV_DEMO_WIDGETS_SLIDESHOW 0
+#endif
+
+/*Demonstrate the usage of encoder and keyboard*/
+#define LV_USE_DEMO_KEYPAD_AND_ENCODER 1
+
+/*Benchmark your system*/
+#define LV_USE_DEMO_BENCHMARK 1
+
+/*Stress test for LVGL*/
+#define LV_USE_DEMO_STRESS 1
+
+/*Music player demo*/
+#define LV_USE_DEMO_MUSIC 1
+#if LV_USE_DEMO_MUSIC
+# define LV_DEMO_MUSIC_SQUARE 0
+# define LV_DEMO_MUSIC_LANDSCAPE 0
+# define LV_DEMO_MUSIC_ROUND 0
+# define LV_DEMO_MUSIC_LARGE 0
+# define LV_DEMO_MUSIC_AUTO_PLAY 0
+#endif
+
+/*--END OF LV_CONF_H--*/
+
+#endif /*LV_CONF_H*/
+
+#endif /*End of "Content enable"*/
diff --git a/lib/luavgl/simulator/lv_drv_conf.h b/lib/luavgl/simulator/lv_drv_conf.h
new file mode 100644
index 00000000..adb149b1
--- /dev/null
+++ b/lib/luavgl/simulator/lv_drv_conf.h
@@ -0,0 +1,494 @@
+/**
+ * @file lv_drv_conf.h
+ * Configuration file for v8.3.0-dev
+ */
+
+/*
+ * COPY THIS FILE AS lv_drv_conf.h
+ */
+
+/* clang-format off */
+#if 1 /*Set it to "1" to enable the content*/
+
+#ifndef LV_DRV_CONF_H
+#define LV_DRV_CONF_H
+
+#include "lv_conf.h"
+
+/*********************
+ * DELAY INTERFACE
+ *********************/
+#define LV_DRV_DELAY_INCLUDE <stdint.h> /*Dummy include by default*/
+#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/
+#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/
+
+/*********************
+ * DISPLAY INTERFACE
+ *********************/
+
+/*------------
+ * Common
+ *------------*/
+#define LV_DRV_DISP_INCLUDE <stdint.h> /*Dummy include by default*/
+#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/
+#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
+
+/*---------
+ * SPI
+ *---------*/
+#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
+#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/
+#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/
+
+/*------------------
+ * Parallel port
+ *-----------------*/
+#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/
+#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/
+#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/
+#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/
+#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/
+
+/***************************
+ * INPUT DEVICE INTERFACE
+ ***************************/
+
+/*----------
+ * Common
+ *----------*/
+#define LV_DRV_INDEV_INCLUDE <stdint.h> /*Dummy include by default*/
+#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/
+#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/
+
+/*---------
+ * SPI
+ *---------*/
+#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/
+#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/
+
+/*---------
+ * I2C
+ *---------*/
+#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/
+#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/
+#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/
+#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/
+#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/
+
+
+/*********************
+ * DISPLAY DRIVERS
+ *********************/
+
+/*-------------------
+ * SDL
+ *-------------------*/
+
+/* SDL based drivers for display, mouse, mousewheel and keyboard*/
+#ifndef USE_SDL
+# define USE_SDL 1
+#endif
+
+/* Hardware accelerated SDL driver */
+#ifndef USE_SDL_GPU
+# define USE_SDL_GPU 0
+#endif
+
+#if USE_SDL || USE_SDL_GPU
+# define SDL_HOR_RES 480
+# define SDL_VER_RES 480
+
+/* Scale window by this factor (useful when simulating small screens) */
+# define SDL_ZOOM 1
+
+/* Used to test true double buffering with only address changing.
+ * Use 2 draw buffers, bith with SDL_HOR_RES x SDL_VER_RES size*/
+# define SDL_DOUBLE_BUFFERED 0
+
+/*Eclipse: <SDL2/SDL.h> Visual Studio: <SDL.h>*/
+# define SDL_INCLUDE_PATH <SDL2/SDL.h>
+
+/*Open two windows to test multi display support*/
+# define SDL_DUAL_DISPLAY 0
+#endif
+
+/*-------------------
+ * Monitor of PC
+ *-------------------*/
+
+/*DEPRECATED: Use the SDL driver instead. */
+#ifndef USE_MONITOR
+# define USE_MONITOR 0
+#endif
+
+#if USE_MONITOR
+# define MONITOR_HOR_RES 480
+# define MONITOR_VER_RES 320
+
+/* Scale window by this factor (useful when simulating small screens) */
+# define MONITOR_ZOOM 1
+
+/* Used to test true double buffering with only address changing.
+ * Use 2 draw buffers, bith with MONITOR_HOR_RES x MONITOR_VER_RES size*/
+# define MONITOR_DOUBLE_BUFFERED 0
+
+/*Eclipse: <SDL2/SDL.h> Visual Studio: <SDL.h>*/
+# define MONITOR_SDL_INCLUDE_PATH <SDL2/SDL.h>
+
+/*Open two windows to test multi display support*/
+# define MONITOR_DUAL 0
+#endif
+
+/*-----------------------------------
+ * Native Windows (including mouse)
+ *----------------------------------*/
+#ifndef USE_WINDOWS
+# define USE_WINDOWS 0
+#endif
+
+#if USE_WINDOWS
+# define WINDOW_HOR_RES 480
+# define WINDOW_VER_RES 320
+#endif
+
+/*----------------------------
+ * Native Windows (win32drv)
+ *---------------------------*/
+#ifndef USE_WIN32DRV
+# define USE_WIN32DRV 0
+#endif
+
+#if USE_WIN32DRV
+/* Scale window by this factor (useful when simulating small screens) */
+# define WIN32DRV_MONITOR_ZOOM 1
+#endif
+
+/*----------------------------------------
+ * GTK drivers (monitor, mouse, keyboard
+ *---------------------------------------*/
+#ifndef USE_GTK
+# define USE_GTK 0
+#endif
+
+/*----------------------------------------
+ * Wayland drivers (monitor, mouse, keyboard, touchscreen)
+ *---------------------------------------*/
+#ifndef USE_WAYLAND
+# define USE_WAYLAND 0
+#endif
+
+#if USE_WAYLAND
+/* Support for client-side decorations */
+# ifndef LV_WAYLAND_CLIENT_SIDE_DECORATIONS
+# define LV_WAYLAND_CLIENT_SIDE_DECORATIONS 1
+# endif
+/* Support for (deprecated) wl-shell protocol */
+# ifndef LV_WAYLAND_WL_SHELL
+# define LV_WAYLAND_WL_SHELL 1
+# endif
+/* Support for xdg-shell protocol */
+# ifndef LV_WAYLAND_XDG_SHELL
+# define LV_WAYLAND_XDG_SHELL 0
+# endif
+#endif
+
+/*----------------
+ * SSD1963
+ *--------------*/
+#ifndef USE_SSD1963
+# define USE_SSD1963 0
+#endif
+
+#if USE_SSD1963
+# define SSD1963_HOR_RES LV_HOR_RES
+# define SSD1963_VER_RES LV_VER_RES
+# define SSD1963_HT 531
+# define SSD1963_HPS 43
+# define SSD1963_LPS 8
+# define SSD1963_HPW 10
+# define SSD1963_VT 288
+# define SSD1963_VPS 12
+# define SSD1963_FPS 4
+# define SSD1963_VPW 10
+# define SSD1963_HS_NEG 0 /*Negative hsync*/
+# define SSD1963_VS_NEG 0 /*Negative vsync*/
+# define SSD1963_ORI 0 /*0, 90, 180, 270*/
+# define SSD1963_COLOR_DEPTH 16
+#endif
+
+/*----------------
+ * R61581
+ *--------------*/
+#ifndef USE_R61581
+# define USE_R61581 0
+#endif
+
+#if USE_R61581
+# define R61581_HOR_RES LV_HOR_RES
+# define R61581_VER_RES LV_VER_RES
+# define R61581_HSPL 0 /*HSYNC signal polarity*/
+# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/
+# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/
+# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */
+# define R61581_VSPL 0 /*VSYNC signal polarity*/
+# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/
+# define R61581_VFP 8 /*Vertical Front poarch*/
+# define R61581_VBP 8 /*Vertical Back poarch */
+# define R61581_DPL 0 /*DCLK signal polarity*/
+# define R61581_EPL 1 /*ENABLE signal polarity*/
+# define R61581_ORI 0 /*0, 180*/
+# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/
+#endif
+
+/*------------------------------
+ * ST7565 (Monochrome, low res.)
+ *-----------------------------*/
+#ifndef USE_ST7565
+# define USE_ST7565 0
+#endif
+
+#if USE_ST7565
+/*No settings*/
+#endif /*USE_ST7565*/
+
+/*------------------------------
+ * GC9A01 (color, low res.)
+ *-----------------------------*/
+#ifndef USE_GC9A01
+# define USE_GC9A01 0
+#endif
+
+#if USE_GC9A01
+/*No settings*/
+#endif /*USE_GC9A01*/
+
+/*------------------------------------------
+ * UC1610 (4 gray 160*[104|128])
+ * (EA DOGXL160 160x104 tested)
+ *-----------------------------------------*/
+#ifndef USE_UC1610
+# define USE_UC1610 0
+#endif
+
+#if USE_UC1610
+# define UC1610_HOR_RES LV_HOR_RES
+# define UC1610_VER_RES LV_VER_RES
+# define UC1610_INIT_CONTRAST 33 /* init contrast, values in [%] */
+# define UC1610_INIT_HARD_RST 0 /* 1 : hardware reset at init, 0 : software reset */
+# define UC1610_TOP_VIEW 0 /* 0 : Bottom View, 1 : Top View */
+#endif /*USE_UC1610*/
+
+/*-------------------------------------------------
+ * SHARP memory in pixel monochrome display series
+ * LS012B7DD01 (184x38 pixels.)
+ * LS013B7DH03 (128x128 pixels.)
+ * LS013B7DH05 (144x168 pixels.)
+ * LS027B7DH01 (400x240 pixels.) (tested)
+ * LS032B7DD02 (336x536 pixels.)
+ * LS044Q7DH01 (320x240 pixels.)
+ *------------------------------------------------*/
+#ifndef USE_SHARP_MIP
+# define USE_SHARP_MIP 0
+#endif
+
+#if USE_SHARP_MIP
+# define SHARP_MIP_HOR_RES LV_HOR_RES
+# define SHARP_MIP_VER_RES LV_VER_RES
+# define SHARP_MIP_SOFT_COM_INVERSION 0
+# define SHARP_MIP_REV_BYTE(b) /*((uint8_t) __REV(__RBIT(b)))*/ /*Architecture / compiler dependent byte bits order reverse*/
+#endif /*USE_SHARP_MIP*/
+
+/*-------------------------------------------------
+ * ILI9341 240X320 TFT LCD
+ *------------------------------------------------*/
+#ifndef USE_ILI9341
+# define USE_ILI9341 0
+#endif
+
+#if USE_ILI9341
+# define ILI9341_HOR_RES LV_HOR_RES
+# define ILI9341_VER_RES LV_VER_RES
+# define ILI9341_GAMMA 1
+# define ILI9341_TEARING 0
+#endif /*USE_ILI9341*/
+
+/*-----------------------------------------
+ * Linux frame buffer device (/dev/fbx)
+ *-----------------------------------------*/
+#ifndef USE_FBDEV
+# define USE_FBDEV 0
+#endif
+
+#if USE_FBDEV
+# define FBDEV_PATH "/dev/fb0"
+#endif
+
+/*-----------------------------------------
+ * FreeBSD frame buffer device (/dev/fbx)
+ *.........................................*/
+#ifndef USE_BSD_FBDEV
+# define USE_BSD_FBDEV 0
+#endif
+
+#if USE_BSD_FBDEV
+# define FBDEV_PATH "/dev/fb0"
+#endif
+
+/*-----------------------------------------
+ * DRM/KMS device (/dev/dri/cardX)
+ *-----------------------------------------*/
+#ifndef USE_DRM
+# define USE_DRM 0
+#endif
+
+#if USE_DRM
+# define DRM_CARD "/dev/dri/card0"
+# define DRM_CONNECTOR_ID -1 /* -1 for the first connected one */
+#endif
+
+/*********************
+ * INPUT DEVICES
+ *********************/
+
+/*--------------
+ * XPT2046
+ *--------------*/
+#ifndef USE_XPT2046
+# define USE_XPT2046 0
+#endif
+
+#if USE_XPT2046
+# define XPT2046_HOR_RES 480
+# define XPT2046_VER_RES 320
+# define XPT2046_X_MIN 200
+# define XPT2046_Y_MIN 200
+# define XPT2046_X_MAX 3800
+# define XPT2046_Y_MAX 3800
+# define XPT2046_AVG 4
+# define XPT2046_X_INV 0
+# define XPT2046_Y_INV 0
+# define XPT2046_XY_SWAP 0
+#endif
+
+/*-----------------
+ * FT5406EE8
+ *-----------------*/
+#ifndef USE_FT5406EE8
+# define USE_FT5406EE8 0
+#endif
+
+#if USE_FT5406EE8
+# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/
+#endif
+
+/*---------------
+ * AD TOUCH
+ *--------------*/
+#ifndef USE_AD_TOUCH
+# define USE_AD_TOUCH 0
+#endif
+
+#if USE_AD_TOUCH
+/*No settings*/
+#endif
+
+
+/*---------------------------------------
+ * Mouse or touchpad on PC (using SDL)
+ *-------------------------------------*/
+/*DEPRECATED: Use the SDL driver instead. */
+#ifndef USE_MOUSE
+# define USE_MOUSE 0
+#endif
+
+#if USE_MOUSE
+/*No settings*/
+#endif
+
+/*-------------------------------------------
+ * Mousewheel as encoder on PC (using SDL)
+ *------------------------------------------*/
+/*DEPRECATED: Use the SDL driver instead. */
+#ifndef USE_MOUSEWHEEL
+# define USE_MOUSEWHEEL 0
+#endif
+
+#if USE_MOUSEWHEEL
+/*No settings*/
+#endif
+
+/*-------------------------------------------------
+ * Touchscreen, mouse/touchpad or keyboard as libinput interface (for Linux based systems)
+ *------------------------------------------------*/
+#ifndef USE_LIBINPUT
+# define USE_LIBINPUT 0
+#endif
+
+#ifndef USE_BSD_LIBINPUT
+# define USE_BSD_LIBINPUT 0
+#endif
+
+#if USE_LIBINPUT || USE_BSD_LIBINPUT
+/*If only a single device of the same type is connected, you can also auto detect it, e.g.:
+ *#define LIBINPUT_NAME libinput_find_dev(LIBINPUT_CAPABILITY_TOUCH, false)*/
+# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
+
+#endif /*USE_LIBINPUT || USE_BSD_LIBINPUT*/
+
+/*-------------------------------------------------
+ * Mouse or touchpad as evdev interface (for Linux based systems)
+ *------------------------------------------------*/
+#ifndef USE_EVDEV
+# define USE_EVDEV 0
+#endif
+
+#ifndef USE_BSD_EVDEV
+# define USE_BSD_EVDEV 0
+#endif
+
+#if USE_EVDEV || USE_BSD_EVDEV
+# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
+# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/
+
+# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/
+
+# if EVDEV_CALIBRATE
+# define EVDEV_HOR_MIN 0 /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/
+# define EVDEV_HOR_MAX 4096 /*"evtest" Linux tool can help to get the correct calibraion values>*/
+# define EVDEV_VER_MIN 0
+# define EVDEV_VER_MAX 4096
+# endif /*EVDEV_CALIBRATE*/
+#endif /*USE_EVDEV*/
+
+/*-------------------------------------------------
+ * Full keyboard support for evdev and libinput interface
+ *------------------------------------------------*/
+# ifndef USE_XKB
+# define USE_XKB 0
+# endif
+
+#if USE_LIBINPUT || USE_BSD_LIBINPUT || USE_EVDEV || USE_BSD_EVDEV
+# if USE_XKB
+# define XKB_KEY_MAP { .rules = NULL, \
+ .model = "pc101", \
+ .layout = "us", \
+ .variant = NULL, \
+ .options = NULL } /*"setxkbmap -query" can help find the right values for your keyboard*/
+# endif /*USE_XKB*/
+#endif /*USE_LIBINPUT || USE_BSD_LIBINPUT || USE_EVDEV || USE_BSD_EVDEV*/
+
+/*-------------------------------
+ * Keyboard of a PC (using SDL)
+ *------------------------------*/
+/*DEPRECATED: Use the SDL driver instead. */
+#ifndef USE_KEYBOARD
+# define USE_KEYBOARD 0
+#endif
+
+#if USE_KEYBOARD
+/*No settings*/
+#endif
+
+#endif /*LV_DRV_CONF_H*/
+
+#endif /*End of "Content enable"*/
diff --git a/lib/luavgl/simulator/main.c b/lib/luavgl/simulator/main.c
new file mode 100644
index 00000000..c3c261eb
--- /dev/null
+++ b/lib/luavgl/simulator/main.c
@@ -0,0 +1,340 @@
+
+#include <stdlib.h>
+#include <unistd.h>
+#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" \
+ issue*/
+#include "demos/lv_demos.h"
+#include "examples/lv_examples.h"
+#include "sdl/sdl.h"
+#include <SDL2/SDL.h>
+#include <lvgl.h>
+
+#include <lua.h>
+
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include <luavgl.h>
+#include "widgets/widgets.h"
+
+typedef struct {
+ lua_State *L;
+ lv_obj_t *root;
+} lua_context_t;
+
+typedef struct {
+ lv_obj_t *root;
+ make_font_cb make_font;
+ delete_font_cb delete_font;
+} luavgl_args_t;
+
+/**
+ * Initialize the Hardware Abstraction Layer (HAL) for the LVGL graphics
+ * library
+ */
+static void hal_init(void)
+{
+ /* Use the 'monitor' driver which creates window on PC's monitor to simulate a
+ * display*/
+ sdl_init();
+
+ /*Create a display buffer*/
+ static lv_disp_draw_buf_t disp_buf1;
+ static lv_color_t buf1_1[SDL_HOR_RES * SDL_VER_RES];
+ lv_disp_draw_buf_init(&disp_buf1, buf1_1, NULL, SDL_HOR_RES * SDL_VER_RES);
+
+ /*Create a display*/
+ static lv_disp_drv_t disp_drv;
+ lv_disp_drv_init(&disp_drv); /*Basic initialization*/
+ disp_drv.draw_buf = &disp_buf1;
+ disp_drv.flush_cb = sdl_display_flush;
+ disp_drv.hor_res = SDL_HOR_RES;
+ disp_drv.ver_res = SDL_VER_RES;
+
+ lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
+
+ lv_theme_t *th = lv_theme_default_init(
+ disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
+ LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
+ lv_disp_set_theme(disp, th);
+
+ lv_group_t *g = lv_group_create();
+ lv_group_set_default(g);
+
+ /* Add the mouse as input device
+ * Use the 'mouse' driver which reads the PC's mouse*/
+ static lv_indev_drv_t indev_drv_1;
+ lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
+ indev_drv_1.type = LV_INDEV_TYPE_POINTER;
+
+ /*This function will be called periodically (by the library) to get the mouse
+ * position and state*/
+ indev_drv_1.read_cb = sdl_mouse_read;
+ lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);
+
+ static lv_indev_drv_t indev_drv_2;
+ lv_indev_drv_init(&indev_drv_2); /*Basic initialization*/
+ indev_drv_2.type = LV_INDEV_TYPE_KEYPAD;
+ indev_drv_2.read_cb = sdl_keyboard_read;
+ lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv_2);
+ lv_indev_set_group(kb_indev, g);
+
+ static lv_indev_drv_t indev_drv_3;
+ lv_indev_drv_init(&indev_drv_3); /*Basic initialization*/
+ indev_drv_3.type = LV_INDEV_TYPE_ENCODER;
+ indev_drv_3.read_cb = sdl_mousewheel_read;
+ lv_indev_t *enc_indev = lv_indev_drv_register(&indev_drv_3);
+ lv_indev_set_group(enc_indev, g);
+
+ /*Set a cursor for the mouse*/
+ LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
+ lv_obj_t *cursor_obj =
+ lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
+ lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
+ lv_indev_set_cursor(mouse_indev,
+ cursor_obj); /*Connect the image object to the driver*/
+}
+
+/*
+** Prints an error message, adding the program name in front of it
+** (if present)
+*/
+static void l_message(const char *pname, const char *msg)
+{
+ printf("%s: %s\n", pname ? pname : " ", msg);
+}
+
+/*
+** Check whether 'status' is not OK and, if so, prints the error
+** message on the top of the stack. It assumes that the error object
+** is a string, as it was either generated by Lua or by 'msghandler'.
+*/
+static int report(lua_State *L, int status)
+{
+ if (status != LUA_OK) {
+ const char *msg = lua_tostring(L, -1);
+ l_message("luactx", msg);
+ lua_pop(L, 1); /* remove message */
+ }
+ return status;
+}
+
+/*
+** Message handler used to run all chunks
+*/
+static int msghandler(lua_State *L)
+{
+ const char *msg = lua_tostring(L, 1);
+ if (msg == NULL) { /* is error object not a string? */
+ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
+ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
+ return 1; /* that is the message */
+ else
+ msg = lua_pushfstring(L, "(error object is a %s value)",
+ luaL_typename(L, 1));
+ }
+
+ /* append a standard traceback */
+ luaL_traceback(L, L, msg, 1);
+
+ msg = lua_tostring(L, -1);
+ lua_pop(L, 1);
+
+ lv_obj_t *root = NULL;
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ root = ctx->root ? ctx->root : lv_scr_act();
+ lv_obj_t *label = lv_label_create(root);
+ lv_label_set_text(label, msg);
+ lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
+ lv_obj_set_style_text_font(label, LV_FONT_DEFAULT, 0);
+ lv_obj_set_width(label, LV_PCT(80));
+ lv_obj_center(label);
+
+ printf("trace back: \n%s\n", msg);
+ return 0; /* return no trace, since we already processed it. */
+}
+
+static int lua_panic(lua_State *L)
+{
+ printf("LUA panic:\n%s\n", lua_tostring(L, -1));
+ return 0; /* return to Lua to abort */
+}
+
+/*
+** protected main entry
+*/
+static int pmain(lua_State *L)
+{
+ int status;
+ const char *script = lua_tostring(L, 1);
+
+ luavgl_args_t *args = lua_touserdata(L, 2);
+ if (args == NULL || args->root == NULL) {
+ printf("Null root object.\n");
+ return 0;
+ }
+
+ luavgl_set_root(L, args->root);
+ luavgl_set_font_extension(L, args->make_font, args->delete_font);
+
+ /**
+ * Set global variable SCRIPT_PATH, to make image src path easier.
+ */
+ char *path = strdup(script);
+ if (path == NULL) {
+ printf("no memory.\n");
+ return 0;
+ }
+
+ int i = strlen(path);
+ for (; i; i--) {
+ if (path[i] == '/') {
+ path[i + 1] = '\0';
+ break;
+ }
+ }
+
+ printf("script path: %s\n", path);
+ lua_pushstring(L, path);
+ lua_setglobal(L, "SCRIPT_PATH");
+ luaL_openlibs(L);
+
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "path");
+
+ const char *pkg_path = lua_tostring(L, -1);
+ char *new_path = malloc(strlen(pkg_path) + strlen(script) + 2);
+ strcpy(new_path, pkg_path);
+ strcat(new_path, ";"), strcat(new_path, path), strcat(new_path, "?.lua");
+ lua_pop(L, 1);
+ lua_pushstring(L, new_path);
+ lua_setfield(L, -2, "path");
+ lua_pop(L, 1);
+ free(path);
+ free(new_path);
+
+ lua_atpanic(L, &lua_panic);
+
+ luaL_requiref(L, "lvgl", luaopen_lvgl, 1);
+ lua_pop(L, 1);
+ luavgl_widgets_init(L);
+
+ lua_pushcfunction(L, msghandler); /* push message handler */
+ int base = lua_gettop(L);
+ status = luaL_loadfile(L, script);
+ if (status != LUA_OK) {
+ lua_pushfstring(L, "failed to load: %s\n", script);
+ /* manually show the error to screen. */
+ lua_insert(L, 1);
+ msghandler(L);
+ return 0;
+ }
+
+ status = lua_pcall(L, 0, 0, base);
+ lua_remove(L, base); /* remove message handler from the stack */
+ report(L, status);
+ lua_pushboolean(L, 1); /* signal no errors */
+ return 1;
+}
+
+lua_context_t *lua_load_script(const char *script, luavgl_args_t *args)
+{
+ int ret, status;
+ /* create the thread to run script. */
+ if (script == NULL) {
+ printf("args error.\n");
+ return NULL;
+ }
+
+ printf("run script: %s\n", script);
+ lua_State *L = luaL_newstate(); /* create state */
+ if (L == NULL) {
+ printf("no mem for lua state.\n");
+ return NULL;
+ }
+
+ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
+ lua_pushstring(L, script);
+ lua_pushlightuserdata(L, args);
+ status = lua_pcall(L, 2, 1, 0); /* do the call */
+ ret = lua_toboolean(L, -1);
+ report(L, status);
+ if (!ret || status != LUA_OK) {
+ /* This should never happen */
+ printf("pcall failed.\n");
+ lua_close(L);
+ return NULL;
+ }
+
+ /* script may fail, but we continue until page destoried. */
+ lua_context_t *luactx = calloc(sizeof(*luactx), 1);
+ if (luactx == NULL) {
+ printf("no memory.\n");
+ goto lua_exit;
+ }
+
+ luactx->L = L;
+ luactx->root = args->root;
+ return luactx;
+
+lua_exit:
+ lua_close(L);
+
+ return NULL;
+}
+
+int lua_terminate(lua_context_t *luactx)
+{
+ lua_State *L = luactx->L;
+
+ lua_close(L);
+ free(luactx);
+ return 0;
+}
+
+static lua_context_t *lua_ctx;
+static luavgl_args_t args;
+
+static void reload_cb(lv_event_t *e)
+{
+ (void)e;
+ if (lua_ctx != NULL) {
+ lua_terminate(lua_ctx);
+ }
+
+ lua_ctx = lua_load_script(LUAVGL_EXAMPLE_DIR "/examples.lua", &args);
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc; /*Unused*/
+ (void)argv; /*Unused*/
+
+ /*Initialize LVGL*/
+ lv_init();
+
+ /*Initialize the HAL (display, input devices, tick) for LVGL*/
+ hal_init();
+
+ args.root = lv_scr_act();
+
+ lua_ctx = lua_load_script(LUAVGL_EXAMPLE_DIR "/examples.lua", &args);
+
+ lv_obj_t *btn = lv_btn_create(lv_layer_sys());
+ lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, 0, -50);
+ lv_obj_set_size(btn, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
+ lv_obj_set_style_pad_all(btn, 5, 0);
+ lv_obj_add_event_cb(btn, reload_cb, LV_EVENT_CLICKED, NULL);
+ lv_obj_t* label = lv_label_create(btn);
+ lv_label_set_text(label, "RELOAD");
+ lv_obj_center(label);
+
+ while (1) {
+ /* Periodically call the lv_task handler.
+ * It could be done in a timer interrupt or an OS task too.*/
+ lv_timer_handler();
+ usleep(5 * 1000);
+ }
+
+ return 0;
+}
diff --git a/lib/luavgl/simulator/mouse_cursor_icon.c b/lib/luavgl/simulator/mouse_cursor_icon.c
new file mode 100644
index 00000000..033ccb3e
--- /dev/null
+++ b/lib/luavgl/simulator/mouse_cursor_icon.c
@@ -0,0 +1,104 @@
+#include "lvgl.h"
+
+const uint8_t mouse_cursor_icon_map[] = {
+#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
+ /*Pixel format: Alpha 8 bit, Red: 3 bit, Green: 3 bit, Blue: 2 bit*/
+ 0x24, 0xb8, 0x24, 0xc8, 0x00, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcc, 0xdb, 0xff, 0x49, 0xcc, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xc8, 0xff, 0xff, 0xff, 0xff, 0x49, 0xe0, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcb, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x6d, 0xf3, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0xff, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0xff, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0xff, 0x24, 0xab, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xff, 0x24, 0xbb, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xff, 0x49, 0xd8, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6d, 0xef, 0x00, 0x4f, 0x00, 0x00,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x92, 0xff, 0x00, 0x6b,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xff, 0x92, 0xf7, 0x92, 0xf8, 0x6e, 0xfb, 0x92, 0xf8, 0x6d, 0xff, 0x00, 0xb3,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xff, 0x24, 0xb7, 0x00, 0x1b, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x25, 0x07,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6e, 0xf0, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0xcc, 0xff, 0xff, 0xff, 0xff, 0x49, 0xd8, 0x00, 0x78, 0x92, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xff, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6d, 0xd3, 0xff, 0xff, 0x6d, 0xef, 0x00, 0x34, 0x00, 0x00, 0x49, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6d, 0xdc, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x49, 0xe0, 0x6d, 0xff, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x68, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x49, 0xd0, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x6d, 0xd8, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xb7, 0xff, 0xff, 0xff, 0x92, 0xff, 0x49, 0xac, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x25, 0xd7, 0x49, 0xc7, 0x00, 0x47, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+#endif
+#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
+ /*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit*/
+ 0xc3, 0x18, 0xb8, 0xe4, 0x20, 0xc8, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0x4a, 0xcc, 0x96, 0xb5, 0xff, 0xc7, 0x39, 0xcc, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe7, 0x39, 0xc8, 0xbf, 0xff, 0xff, 0xfb, 0xde, 0xff, 0x28, 0x42, 0xe0, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe7, 0x39, 0xcb, 0x3d, 0xef, 0xff, 0xff, 0xff, 0xfc, 0x3d, 0xef, 0xff, 0xcb, 0x5a, 0xf3, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe7, 0x39, 0xcb, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x8e, 0x73, 0xff, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe8, 0x41, 0xcb, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x51, 0x8c, 0xff, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x9c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe8, 0x41, 0xcb, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x14, 0xa5, 0xff, 0xa2, 0x10, 0xab, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x42, 0xcb, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd7, 0xbd, 0xff, 0x04, 0x21, 0xbb, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x42, 0xcc, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xff, 0xe8, 0x41, 0xd8, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x42, 0xcc, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe6, 0xff, 0xab, 0x5a, 0xef, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00,
+ 0x08, 0x42, 0xcc, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xf7, 0xff, 0xaf, 0x7b, 0xff, 0x00, 0x00, 0x6b,
+ 0x28, 0x42, 0xcc, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7a, 0xd6, 0xff, 0x10, 0x84, 0xf7, 0xae, 0x73, 0xf8, 0x6e, 0x73, 0xfb, 0x8e, 0x73, 0xf8, 0xcb, 0x5a, 0xff, 0x61, 0x08, 0xb3,
+ 0x28, 0x42, 0xcc, 0x7d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xff, 0xa2, 0x10, 0xb7, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x0c, 0x45, 0x29, 0x07,
+ 0x29, 0x4a, 0xcc, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xde, 0xff, 0xec, 0x62, 0xff, 0x1c, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x63, 0xf0, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x29, 0x4a, 0xcc, 0xdf, 0xff, 0xff, 0x7d, 0xef, 0xff, 0x49, 0x4a, 0xd8, 0x00, 0x00, 0x78, 0x51, 0x8c, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x38, 0xc6, 0xff, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xcb, 0x5a, 0xd3, 0xdb, 0xde, 0xff, 0xec, 0x62, 0xef, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xe7, 0x39, 0xc7, 0x5d, 0xef, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xf7, 0xff, 0xaa, 0x52, 0xdc, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xe8, 0x41, 0xe0, 0xaa, 0x52, 0xff, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x72, 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xff, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x61, 0x08, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x68, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x69, 0x4a, 0xd0, 0x7d, 0xef, 0xff, 0xff, 0xff, 0xfc, 0xbe, 0xf7, 0xff, 0xaa, 0x52, 0xd8, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x75, 0xad, 0xff, 0xbf, 0xff, 0xff, 0x10, 0x84, 0xff, 0x86, 0x31, 0xac, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x08, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x66, 0x31, 0xd7, 0xc7, 0x39, 0xc7, 0x00, 0x00, 0x47, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+#endif
+#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
+ /*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 color bytes are swapped*/
+ 0x18, 0xc3, 0xb8, 0x20, 0xe4, 0xc8, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x4a, 0x49, 0xcc, 0xb5, 0x96, 0xff, 0x39, 0xc7, 0xcc, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x39, 0xe7, 0xc8, 0xff, 0xbf, 0xff, 0xde, 0xfb, 0xff, 0x42, 0x28, 0xe0, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x39, 0xe7, 0xcb, 0xef, 0x3d, 0xff, 0xff, 0xff, 0xfc, 0xef, 0x3d, 0xff, 0x5a, 0xcb, 0xf3, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x39, 0xe7, 0xcb, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x73, 0x8e, 0xff, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0xe8, 0xcb, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, 0x51, 0xff, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xd3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0xe8, 0xcb, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa5, 0x14, 0xff, 0x10, 0xa2, 0xab, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x42, 0x08, 0xcb, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbd, 0xd7, 0xff, 0x21, 0x04, 0xbb, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x42, 0x08, 0xcc, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xff, 0x41, 0xe8, 0xd8, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x42, 0x08, 0xcc, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xfc, 0xff, 0x5a, 0xab, 0xef, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00,
+ 0x42, 0x08, 0xcc, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xff, 0x7b, 0xaf, 0xff, 0x00, 0x00, 0x6b,
+ 0x42, 0x28, 0xcc, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x7a, 0xff, 0x84, 0x10, 0xf7, 0x73, 0xae, 0xf8, 0x73, 0x6e, 0xfb, 0x73, 0x8e, 0xf8, 0x5a, 0xcb, 0xff, 0x08, 0x61, 0xb3,
+ 0x42, 0x28, 0xcc, 0xef, 0x7d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xff, 0x10, 0xa2, 0xb7, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x13, 0x00, 0x00, 0x0c, 0x29, 0x45, 0x07,
+ 0x4a, 0x29, 0xcc, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xde, 0xdb, 0xff, 0x62, 0xec, 0xff, 0xe7, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x0c, 0xf0, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x4a, 0x29, 0xcc, 0xff, 0xdf, 0xff, 0xef, 0x7d, 0xff, 0x4a, 0x49, 0xd8, 0x00, 0x00, 0x78, 0x8c, 0x51, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x38, 0xff, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5a, 0xcb, 0xd3, 0xde, 0xdb, 0xff, 0x62, 0xec, 0xef, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x39, 0xe7, 0xc7, 0xef, 0x5d, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xff, 0x52, 0xaa, 0xdc, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x41, 0xe8, 0xe0, 0x52, 0xaa, 0xff, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x94, 0x72, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xff, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x08, 0x61, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x68, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x4a, 0x69, 0xd0, 0xef, 0x7d, 0xff, 0xff, 0xff, 0xfc, 0xf7, 0xbe, 0xff, 0x52, 0xaa, 0xd8, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xe4, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xad, 0x75, 0xff, 0xff, 0xbf, 0xff, 0x84, 0x10, 0xff, 0x31, 0x86, 0xac, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x41, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x31, 0x66, 0xd7, 0x39, 0xc7, 0xc7, 0x00, 0x00, 0x47, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+#endif
+#if LV_COLOR_DEPTH == 32
+ 0x19, 0x19, 0x19, 0xb8, 0x1e, 0x1e, 0x1e, 0xc8, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x48, 0x48, 0xcc, 0xb2, 0xb2, 0xb2, 0xff, 0x3a, 0x3a, 0x3a, 0xcc, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3b, 0x3b, 0x3b, 0xc8, 0xf6, 0xf6, 0xf6, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0x43, 0x43, 0x43, 0xe0, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3b, 0x3b, 0x3b, 0xcb, 0xe6, 0xe6, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe5, 0xe5, 0xe5, 0xff, 0x59, 0x59, 0x59, 0xf3, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3c, 0x3c, 0x3c, 0xcb, 0xe9, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xf5, 0xf5, 0xff, 0x72, 0x72, 0x72, 0xff, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3d, 0x3d, 0x3d, 0xcb, 0xe9, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8a, 0x8a, 0x8a, 0xff, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3e, 0x3e, 0x3e, 0xcb, 0xe9, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0xa2, 0xa2, 0xff, 0x13, 0x13, 0x13, 0xab, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x3f, 0x3f, 0xcb, 0xe9, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0xb7, 0xb7, 0xff, 0x1f, 0x1f, 0x1f, 0xbb, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x41, 0x41, 0xcc, 0xea, 0xea, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xff, 0x3d, 0x3d, 0x3d, 0xd8, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x41, 0x41, 0xcc, 0xea, 0xea, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, 0xde, 0xde, 0xff, 0x56, 0x56, 0x56, 0xef, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00,
+ 0x42, 0x42, 0x42, 0xcc, 0xea, 0xea, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf3, 0xf3, 0xff, 0x76, 0x76, 0x76, 0xff, 0x00, 0x00, 0x00, 0x6b,
+ 0x43, 0x43, 0x43, 0xcc, 0xea, 0xea, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xce, 0xce, 0xff, 0x80, 0x80, 0x80, 0xf7, 0x74, 0x74, 0x74, 0xf8, 0x6d, 0x6d, 0x6d, 0xfb, 0x72, 0x72, 0x72, 0xf8, 0x57, 0x57, 0x57, 0xff, 0x0c, 0x0c, 0x0c, 0xb3,
+ 0x44, 0x44, 0x44, 0xcc, 0xeb, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfb, 0xfb, 0xfb, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xc9, 0xc9, 0xff, 0x13, 0x13, 0x13, 0xb7, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0c, 0x29, 0x29, 0x29, 0x07,
+ 0x45, 0x45, 0x45, 0xcc, 0xe8, 0xe8, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xd9, 0xd9, 0xff, 0x5e, 0x5e, 0x5e, 0xff, 0xe2, 0xe2, 0xe2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x62, 0x62, 0x62, 0xf0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x45, 0x45, 0x45, 0xcc, 0xf9, 0xf9, 0xf9, 0xff, 0xec, 0xec, 0xec, 0xff, 0x4a, 0x4a, 0x4a, 0xd8, 0x00, 0x00, 0x00, 0x78, 0x8a, 0x8a, 0x8a, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc3, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x58, 0x58, 0x58, 0xd3, 0xd9, 0xd9, 0xd9, 0xff, 0x5e, 0x5e, 0x5e, 0xef, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3b, 0x3b, 0xc7, 0xe9, 0xe9, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf4, 0xf4, 0xff, 0x54, 0x54, 0x54, 0xdc, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x3e, 0x3e, 0x3e, 0xe0, 0x54, 0x54, 0x54, 0xff, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x8e, 0x8e, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x4c, 0x4c, 0x4c, 0xd0, 0xec, 0xec, 0xec, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf4, 0xf4, 0xf4, 0xff, 0x53, 0x53, 0x53, 0xd8, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1e, 0x1e, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xab, 0xab, 0xab, 0xff, 0xf6, 0xf6, 0xf6, 0xff, 0x80, 0x80, 0x80, 0xff, 0x31, 0x31, 0x31, 0xac, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x09, 0x09, 0x09, 0x03, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x2e, 0x2e, 0x2e, 0xd7, 0x38, 0x38, 0x38, 0xc7, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+#endif
+};
+
+lv_img_dsc_t mouse_cursor_icon = {
+ .header.always_zero = 0,
+ .header.w = 14,
+ .header.h = 20,
+ .data_size = 280 * LV_IMG_PX_SIZE_ALPHA_BYTE,
+ .header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA,
+ .data = mouse_cursor_icon_map,
+};
diff --git a/lib/luavgl/simulator/widgets/analog_time.c b/lib/luavgl/simulator/widgets/analog_time.c
new file mode 100644
index 00000000..75dc4290
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/analog_time.c
@@ -0,0 +1,113 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+#include "lv_analog_time.h"
+
+static int luavgl_analog_time_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_analog_time_create);
+}
+
+static void _lv_analog_time_set_hands(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ const void *hour, *minute, *second;
+ lua_getfield(L, -1, "hour");
+
+ hour = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "minute");
+ minute = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "second");
+ second = luavgl_toimgsrc(L, -1);
+ lua_pop(L, 1);
+
+ lv_analog_time_set_hands(obj, hour, minute, second);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t analog_time_property_table[] = {
+ {"hands", SETTER_TYPE_STACK, {.setter_stack = _lv_analog_time_set_hands}},
+ {"period", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_analog_time_set_period}},
+};
+
+/* clang-format on */
+
+static int luavgl_analog_time_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, analog_time_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for analog_time: %s\n", lua_tostring(L, -2));
+ }
+
+ return -1;
+}
+
+static int luavgl_analog_time_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_analog_time_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_analog_time_pause(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_analog_time_pause(obj);
+ return 0;
+}
+
+static int luavgl_analog_time_resume(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_analog_time_resume(obj);
+ return 0;
+}
+
+static const luaL_Reg luavgl_analog_time_methods[] = {
+ {"set", luavgl_analog_time_set },
+ {"pause", luavgl_analog_time_pause },
+ {"resume", luavgl_analog_time_resume},
+
+ {NULL, NULL },
+};
+
+void luavgl_analog_time_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_analog_time_class, "lv_analog_time",
+ luavgl_analog_time_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_analog_time_create);
+ lua_setfield(L, -2, "AnalogTime");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/extension.c b/lib/luavgl/simulator/widgets/extension.c
new file mode 100644
index 00000000..b9cddc5e
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/extension.c
@@ -0,0 +1,69 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+static int luavgl_extension_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_obj_create);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t extension_property_table[] = {
+ {"dummy", SETTER_TYPE_STACK, {.setter_stack = _lv_dummy_set}},
+};
+/* clang-format on */
+
+static int luavgl_extension_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, extension_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for extension.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_extension_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_extension_set_property_kv, obj);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_extension_methods[] = {
+ {"set", luavgl_extension_set},
+
+ {NULL, NULL},
+};
+
+void luavgl_extension_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_btn_class, "lv_extension",
+ luavgl_extension_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_extension_create);
+ lua_setfield(L, -2, "Extension");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/lv_analog_time.c b/lib/luavgl/simulator/widgets/lv_analog_time.c
new file mode 100644
index 00000000..646bb156
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_analog_time.c
@@ -0,0 +1,253 @@
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_analog_time.h"
+#include "lv_pointer.h"
+
+#include <time.h>
+
+#if LV_USE_ANALOG_TIME
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_analog_time_class
+
+#define UNIT_ONE_SECOND (1000)
+#define UNIT_ONE_MINUTE (60 * UNIT_ONE_SECOND)
+#define UNIT_ONE_HOUR (60 * UNIT_ONE_MINUTE)
+#define UNIT_ONE_DAY (24 * UNIT_ONE_HOUR)
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_analog_time_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void lv_analog_time_destructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void update_time(lv_obj_t* obj);
+static void timer_cb(lv_timer_t* timer);
+static lv_obj_t* create_hand(lv_obj_t* obj, const char* img);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+const lv_obj_class_t lv_analog_time_class = {
+ .constructor_cb = lv_analog_time_constructor,
+ .destructor_cb = lv_analog_time_destructor,
+ .instance_size = sizeof(lv_analog_time_t),
+ .base_class = &lv_obj_class,
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t* lv_analog_time_create(lv_obj_t* parent)
+{
+ lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
+ lv_obj_class_init_obj(obj);
+ return obj;
+}
+
+void lv_analog_time_set_hands(lv_obj_t* obj, const void* hour,
+ const void* minute, const void* second)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ uint32_t period = UNIT_ONE_MINUTE; /* default */
+
+ /* remove hand if it's null */
+ if (analog->second) {
+ lv_obj_del(analog->second);
+ analog->second = NULL;
+ }
+
+ if (analog->minute) {
+ lv_obj_del(analog->minute);
+ analog->minute = NULL;
+ }
+
+ if (analog->hour) {
+ lv_obj_del(analog->hour);
+ analog->hour = NULL;
+ }
+
+ /* create hand if not null */
+
+ if (hour) {
+ analog->hour = create_hand(obj, hour);
+
+ /* for hour, we use minute as unit*/
+ lv_pointer_set_range(analog->hour, 0, 24 * 60, 0, 3600 * 2);
+
+ /* update period should be faster than 1minute(default) */
+ }
+
+ if (minute) {
+ analog->minute = create_hand(obj, minute);
+
+ /* for minute, update per second */
+ lv_pointer_set_range(analog->minute, 0, 60 * 60, 0, 3600);
+
+ /* update period should be faster than 1second */
+ if (period > 1 * UNIT_ONE_SECOND) {
+ period = UNIT_ONE_SECOND;
+ }
+ }
+
+ if (second) {
+ analog->second = create_hand(obj, second);
+
+ /* update per second */
+ lv_pointer_set_range(analog->second, 0, 60 * UNIT_ONE_SECOND, 0, 3600);
+
+ /* update period should be faster than 1second */
+ if (period > 1 * UNIT_ONE_SECOND) {
+ period = UNIT_ONE_SECOND;
+ }
+ }
+
+ if (period < analog->period)
+ analog->period = period;
+
+ if (analog->hour || analog->minute || analog->second) {
+ lv_timer_resume(analog->timer);
+ } else {
+ /* no more needed */
+ lv_timer_pause(analog->timer);
+ }
+
+ update_time(obj);
+}
+
+void lv_analog_time_pause(lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_pause(analog->timer);
+}
+
+void lv_analog_time_resume(lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_resume(analog->timer);
+ update_time(obj);
+}
+
+void lv_analog_time_set_period(lv_obj_t* obj, uint32_t period)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ analog->period = period;
+ lv_timer_set_period(analog->timer, period);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_analog_time_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ LV_UNUSED(class_p);
+ LV_TRACE_OBJ_CREATE("begin");
+
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ analog->period = 60 * 60 * 1000; /* default to 1Hour = 60min*60sec*1000ms*/
+ lv_obj_add_flag(lv_obj_get_parent(obj), LV_OBJ_FLAG_OVERFLOW_VISIBLE);
+
+ /* hands pivot is place to 0, 0 */
+ lv_obj_remove_style_all(obj);
+ lv_obj_set_size(obj, 0, 0);
+
+ analog->timer = lv_timer_create(timer_cb, UNIT_ONE_MINUTE, obj);
+ lv_timer_pause(analog->timer);
+
+ LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void lv_analog_time_destructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+ lv_timer_del(analog->timer);
+}
+
+static lv_obj_t* create_hand(lv_obj_t* obj, const char* img)
+{
+ lv_obj_t* hand;
+ hand = lv_pointer_create(obj);
+ lv_img_set_src(hand, img);
+ lv_point_t pivot;
+ lv_img_get_pivot(hand, &pivot);
+
+ /* we position the hand using image's center */
+ lv_obj_set_pos(hand, -pivot.x, -pivot.y);
+ return hand;
+}
+
+static void update_time(lv_obj_t* obj)
+{
+ int value;
+ struct timespec ts;
+ struct tm* time;
+ int ret;
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret != 0) {
+ LV_LOG_ERROR("get real time failed");
+ return;
+ }
+
+ time = localtime(&ts.tv_sec);
+ if (time == NULL) {
+ LV_LOG_ERROR("get local time failed");
+ return;
+ }
+
+ lv_analog_time_t* analog = (lv_analog_time_t*)obj;
+
+ value = time->tm_sec * 1000;
+ if (analog->period < UNIT_ONE_SECOND) {
+ /* sub-second update mode */
+ value += ts.tv_nsec / 1000000; /* add ms part */
+ }
+
+ if (analog->second)
+ lv_pointer_set_value(analog->second, value);
+
+ if (analog->minute)
+ lv_pointer_set_value(analog->minute, time->tm_min * 60 + time->tm_sec);
+
+ if (analog->hour)
+ lv_pointer_set_value(analog->hour, time->tm_hour * 60 + time->tm_min);
+}
+
+static void timer_cb(lv_timer_t* t)
+{
+ /* timeup */
+ update_time(t->user_data);
+}
+
+/* examples */
+#if 0
+
+void lv_analog_time_example(void)
+{
+ lv_obj_t* scr = lv_scr_act();
+ lv_obj_t* analog = lv_analog_time_create(scr);
+ lv_obj_center(analog);
+ lv_analog_time_set_hands(analog, "/data/hand-second.png", "/data/hand-second.png", "/data/hand-second.png");
+ lv_analog_time_set_period(analog, 60);
+}
+
+#endif
+
+#endif
diff --git a/lib/luavgl/simulator/widgets/lv_analog_time.h b/lib/luavgl/simulator/widgets/lv_analog_time.h
new file mode 100644
index 00000000..f4e63fed
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_analog_time.h
@@ -0,0 +1,64 @@
+#ifndef LV_WIDGETS_ANALOG_TIME_H_
+#define LV_WIDGETS_ANALOG_TIME_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include <lvgl.h>
+
+#if LV_USE_ANALOG_TIME
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_obj_t obj;
+ lv_obj_t* hour;
+ lv_obj_t* minute;
+ lv_obj_t* second;
+ uint32_t period;
+ lv_timer_t* timer;
+} lv_analog_time_t;
+
+extern const lv_obj_class_t lv_analog_time_class;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_obj_t* lv_analog_time_create(lv_obj_t* parent);
+
+/**
+ * Set images for hands of hour, minute and second.
+ * If specified hands image is NULL, then it's deleted.
+ *
+ * @note for simplicity, image pivot is set to image center.
+ */
+void lv_analog_time_set_hands(lv_obj_t* obj, const void* hour,
+ const void* minute, const void* second);
+
+void lv_analog_time_pause(lv_obj_t* obj);
+void lv_analog_time_resume(lv_obj_t* obj);
+void lv_analog_time_set_period(lv_obj_t* obj, uint32_t period);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /* */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_WIDGETS_ANALOG_TIME_H_ */
diff --git a/lib/luavgl/simulator/widgets/lv_pointer.c b/lib/luavgl/simulator/widgets/lv_pointer.c
new file mode 100644
index 00000000..5fe8a103
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_pointer.c
@@ -0,0 +1,138 @@
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_pointer.h"
+
+#if LV_USE_POINTER
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_pointer_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_pointer_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj);
+static void angle_update(lv_obj_t* obj);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+const lv_obj_class_t lv_pointer_class = {
+ .constructor_cb = lv_pointer_constructor,
+ .instance_size = sizeof(lv_pointer_t),
+ .base_class = &lv_img_class,
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t* lv_pointer_create(lv_obj_t* parent)
+{
+ lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
+ lv_obj_class_init_obj(obj);
+
+ /* pointer can rotate out of parent's area. */
+ lv_obj_add_flag(parent, LV_OBJ_FLAG_OVERFLOW_VISIBLE);
+ return obj;
+}
+
+void lv_pointer_set_value(lv_obj_t* obj, int value)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ if (pointer->value == value) {
+ return;
+ }
+
+ pointer->value = value;
+ angle_update(obj);
+}
+
+void lv_pointer_set_range(lv_obj_t* obj, int value_start, int value_range,
+ int angle_start, int angle_range)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ pointer->value_start = value_start;
+ pointer->value_range = value_range;
+ pointer->angle_start = angle_start;
+ pointer->angle_range = angle_range;
+
+ /* update angle */
+ angle_update(obj);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_pointer_constructor(const lv_obj_class_t* class_p,
+ lv_obj_t* obj)
+{
+ LV_UNUSED(class_p);
+ LV_TRACE_OBJ_CREATE("begin");
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+
+ /* set default range parameters */
+ pointer->angle_start = 0;
+ pointer->angle_range = 360 * 10;
+ pointer->value_start = 0;
+ pointer->value_range = 100;
+ LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void angle_update(lv_obj_t* obj)
+{
+ lv_pointer_t* pointer = (lv_pointer_t*)obj;
+ int angle = 0;
+ int value = pointer->value;
+ int value_start = pointer->value_start;
+ int value_range = pointer->value_range;
+ int angle_start = pointer->angle_start;
+ int angle_range = pointer->angle_range;
+
+ if (value_range != 0) {
+ /* Check value overflow, overwrite angle if so. */
+ int delta = value - value_start;
+ angle = angle_start;
+ angle += (delta * angle_range) / value_range;
+
+ /* check overflow */
+ if (value_range > 0) {
+ if (delta > value_range)
+ angle = angle_start + angle_range;
+ else if (value < value_start)
+ angle = angle_start;
+ } else { /* case of value_range < 0 */
+ if (delta < value_range)
+ angle = angle_start + angle_range;
+ else if (value > value_start)
+ angle = angle_start;
+ }
+
+ while (angle >= 3600)
+ angle -= 3600;
+ while (angle < 0)
+ angle += 3600;
+ }
+
+ if (angle == lv_img_get_angle(obj)) {
+ /* not changed */
+ return;
+ }
+
+ lv_img_set_angle(obj, angle);
+}
+
+#endif
diff --git a/lib/luavgl/simulator/widgets/lv_pointer.h b/lib/luavgl/simulator/widgets/lv_pointer.h
new file mode 100644
index 00000000..76c273e4
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/lv_pointer.h
@@ -0,0 +1,67 @@
+#ifndef LV_WIDGETS_POINTER_H_
+#define LV_WIDGETS_POINTER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include <lvgl.h>
+
+#if LV_USE_POINTER
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_img_t obj;
+ int value;
+ int value_start;
+ int value_range;
+ int angle_start;
+ int angle_range;
+} lv_pointer_t;
+
+extern const lv_obj_class_t lv_pointer_class;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+lv_obj_t* lv_pointer_create(lv_obj_t* parent);
+
+/**
+ * Set range parameters for pointer
+ * @param value_start the value mapped to angle_start
+ * @param value_range the value range, could be negative
+ * @param angle_start the angle will be set then value equals to value_start
+ * @param angle_range the angle range, could be negative
+ */
+void lv_pointer_set_range(lv_obj_t* obj, int value_start, int value_range,
+ int angle_start, int angle_range);
+
+/**
+ * Set current value pointer points to
+ * @param value If value is out of range, then angle is also clampped to limits
+ */
+void lv_pointer_set_value(lv_obj_t* obj, int value);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /* */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV_WIDGETS_POINTER_H_ */
diff --git a/lib/luavgl/simulator/widgets/pointer.c b/lib/luavgl/simulator/widgets/pointer.c
new file mode 100644
index 00000000..b1161194
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/pointer.c
@@ -0,0 +1,101 @@
+#include <lauxlib.h>
+#include <lua.h>
+
+#include <lvgl.h>
+#include <stdlib.h>
+
+#include <luavgl.h>
+
+#include "lv_pointer.h"
+
+static int luavgl_pointer_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_pointer_create);
+}
+
+static void _lv_pointer_set_range(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ uint32_t value_start, value_range, angle_start, angle_range;
+ lua_getfield(L, -1, "valueStart");
+ value_start = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "valueRange");
+ value_range = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "angleStart");
+ angle_start = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "angleRange");
+ angle_range = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_pointer_set_range(obj, value_start, value_range, angle_start,
+ angle_range);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t pointer_property_table[] = {
+ {"range", SETTER_TYPE_STACK, {.setter_stack = _lv_pointer_set_range}},
+ {"value", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_pointer_set_value}},
+};
+
+/* clang-format on */
+
+static int luavgl_pointer_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, pointer_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_img_set_property_kv(L, obj);
+ if (ret != 0) {
+ printf("unkown property for pointer: %s\n", lua_tostring(L, -2));
+ }
+
+ return -1;
+}
+
+static int luavgl_pointer_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_pointer_set_property_kv, obj);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_pointer_methods[] = {
+ {"set", luavgl_pointer_set},
+
+ {NULL, NULL },
+};
+
+void luavgl_pointer_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_pointer_class, "lv_pointer",
+ luavgl_pointer_methods);
+ lua_pop(L, 1);
+
+ luaL_getmetatable(L, "widgets");
+ lua_getfield(L, -1, "__index");
+ lua_pushcfunction(L, luavgl_pointer_create);
+ lua_setfield(L, -2, "Pointer");
+ lua_pop(L, 2);
+}
diff --git a/lib/luavgl/simulator/widgets/widgets.c b/lib/luavgl/simulator/widgets/widgets.c
new file mode 100644
index 00000000..f8bbe073
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.c
@@ -0,0 +1,8 @@
+#include "widgets.h"
+
+void luavgl_widgets_init(lua_State *L)
+{
+ luavgl_extension_init(L);
+ luavgl_pointer_init(L);
+ luavgl_analog_time_init(L);
+} \ No newline at end of file
diff --git a/lib/luavgl/simulator/widgets/widgets.h b/lib/luavgl/simulator/widgets/widgets.h
new file mode 100644
index 00000000..77ae8f79
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.h
@@ -0,0 +1,12 @@
+#ifndef WIDGETS_WIDGETS_H_
+#define WIDGETS_WIDGETS_H_
+
+#include <lua.h>
+
+void luavgl_extension_init(lua_State *L);
+void luavgl_pointer_init(lua_State *L);
+void luavgl_analog_time_init(lua_State *L);
+
+void luavgl_widgets_init(lua_State *L);
+
+#endif /* WIDGETS_WIDGETS_H_ */
diff --git a/lib/luavgl/simulator/widgets/widgets.lua b/lib/luavgl/simulator/widgets/widgets.lua
new file mode 100644
index 00000000..9a6a39c0
--- /dev/null
+++ b/lib/luavgl/simulator/widgets/widgets.lua
@@ -0,0 +1,104 @@
+---@meta
+
+lvgl = require "lvgl"
+---
+--- Create Extension widget on parent
+--- @param parent? Object | nil
+--- @param property? ObjectStyle
+--- @return Extension
+function lvgl.Extension(parent, property)
+end
+
+--- Create Extension widget on obj
+--- @param property? ObjectStyle
+--- @return Extension
+function obj:Extension(property)
+end
+
+---
+--- Extension widget
+---@class Extension:Object
+---
+local extension = {}
+
+--- set method
+--- @param p ObjectStyle
+--- @return nil
+function extension:set(p)
+end
+
+
+---
+--- Create Pointer widget
+--- @param parent? Object | nil
+--- @param property? ObjectStyle
+--- @return Pointer
+function lvgl.Pointer(parent, property)
+end
+
+--- Create Extension widget on obj
+--- @param property? ObjectStyle
+--- @return Extension
+function obj:Pointer(property)
+end
+
+---
+--- Pointer widget
+---@class Pointer:Object
+---
+local pointer = {}
+
+--- set method
+--- @param p ObjectStyle
+--- @return nil
+function pointer:set(p)
+end
+
+
+---
+--- Create AnalogTime widget on parent
+--- @param parent? Object | nil
+--- @param property? AnalogTimeStyle
+--- @return AnalogTime
+function lvgl.AnalogTime(parent, property)
+end
+
+--- Create AnalogTime widget on obj
+--- @param property? AnalogTimeStyle
+--- @return AnalogTime
+function obj:AnalogTime(property)
+end
+
+---
+--- Extension widget
+---@class AnalogTime:Object
+---
+local analogTime = {}
+
+--- set method
+--- @param p AnalogTimeStyle
+--- @return nil
+function analogTime:set(p)
+end
+
+--- Pause the hand refresh timer
+function analogTime:pause()
+end
+
+--- Resume the hand refresh timer
+function analogTime:resume()
+end
+
+--- Analog time hands parameter
+--- @class AnalogTimeHands
+--- @field hour string Image source path for hour hand
+--- @field minute string minute hand
+--- @field second string second hand
+---
+
+
+--- Analog time widget property
+--- @class AnalogTimeStyle :StyleProp
+--- @field hands AnalogTimeHands Hands images
+--- @field period integer Timer refresh period, default to 1s/1min depending on whether there's second/min hands
+--- \ No newline at end of file
diff --git a/lib/luavgl/src/anim.c b/lib/luavgl/src/anim.c
new file mode 100644
index 00000000..d8f70f27
--- /dev/null
+++ b/lib/luavgl/src/anim.c
@@ -0,0 +1,339 @@
+#include "luavgl.h"
+#include "private.h"
+
+/**
+ * Different to lvgl anim, anim in lua can be restarted after it's done.
+ * anim.stop()
+ * anim.start()
+ * anim.delete() this will mark anim as no longer needed and waiting for gc.
+ */
+
+typedef struct luavgl_anim_s {
+ lua_State *L;
+ lv_anim_t *aa; /* the handler returns be lv_anim_start */
+ lv_anim_t cfg; /* the configuration, must not be the first element. */
+
+ int obj_ref; /* the object used for animation, used as in exec_cb */
+ int exec_cb;
+ int done_cb;
+ int self_ref; /* ref in registry to this anim */
+ bool deleted; /* manually deleted from lua */
+} luavgl_anim_t;
+
+typedef luavgl_anim_t *luavgl_anim_handle_t;
+
+static luavgl_anim_t *luavgl_check_anim(lua_State *L, int index)
+{
+ luavgl_anim_t *a = luaL_checkudata(L, index, "lv_anim");
+
+ if (a->deleted) {
+ luaL_argerror(L, index, "anim already deleted.");
+ return NULL;
+ }
+
+ return a;
+}
+
+static void luavgl_anim_exec_cb(void *var, int32_t value)
+{
+ luavgl_anim_t *a = var;
+ lua_State *L = a->L;
+
+ if (a->exec_cb == LUA_NOREF || a->obj_ref == LUA_NOREF) {
+ debug("anim error, callback or obj not found.\n");
+ luaL_error(L, "anim founds no callback or obj.");
+ return;
+ }
+
+ /* stack: 1. function, 2. obj-userdata, 3. value */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, a->exec_cb);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, a->obj_ref);
+ lua_pushinteger(L, value);
+
+ luavgl_pcall_int(L, 2, 0);
+}
+
+/* callback when anim is deleted by lvgl */
+static void luavgl_anim_delete_cb(lv_anim_t *_a)
+{
+ luavgl_anim_t *a = _a->var;
+ a->aa = NULL; /* handler deleted by lvgl */
+
+ lua_State *L = a->L;
+
+ if (a->done_cb != LUA_NOREF) {
+ /* stack: 1. function, 2. this anim, 3. obj-userdata */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, a->done_cb);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, a->self_ref);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, a->obj_ref);
+ luavgl_pcall_int(L, 2, 0);
+ }
+
+ /* it's paused or deleted, thus can be gc'ed */
+ luaL_unref(L, LUA_REGISTRYINDEX, a->self_ref);
+ a->self_ref = LUA_NOREF;
+}
+
+static void _lv_anim_set_start_value(lv_anim_t *a, int32_t v)
+{
+ a->start_value = v;
+}
+
+static void _lv_anim_set_end_value(lv_anim_t *a, int32_t v)
+{
+ a->end_value = v;
+}
+
+static void _lv_anim_set_path(void *obj, lua_State *L)
+{
+ lv_anim_t *a = obj;
+ const char *path = NULL;
+
+ if (lua_isstring(L, -1)) {
+ path = lua_tostring(L, -1);
+ }
+
+ a->path_cb = lv_anim_path_linear;
+ if (path == NULL || strcmp(path, "linear") == 0) {
+ ; /* use default linear path */
+ } else if (strcmp(path, "ease_in") == 0) {
+ a->path_cb = lv_anim_path_ease_in;
+ } else if (strcmp(path, "ease_out") == 0) {
+ a->path_cb = lv_anim_path_ease_out;
+ } else if (strcmp(path, "ease_in_out") == 0) {
+ a->path_cb = lv_anim_path_ease_in_out;
+ } else if (strcmp(path, "overshoot") == 0) {
+ a->path_cb = lv_anim_path_overshoot;
+ } else if (strcmp(path, "bounce") == 0) {
+ a->path_cb = lv_anim_path_bounce;
+ } else if (strcmp(path, "step") == 0) {
+ a->path_cb = lv_anim_path_step;
+ }
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t anim_property_table[] = {
+ { "run", SETTER_TYPE_STACK, { .setter_stack = _lv_dummy_set } },
+ { "start_value", 0, { .setter = (setter_int_t)_lv_anim_set_start_value } },
+ { "end_value", 0, { .setter = (setter_int_t)_lv_anim_set_end_value } },
+ { "time", 0, { .setter = (setter_int_t)lv_anim_set_time } },
+ { "duration", 0, { .setter = (setter_int_t)lv_anim_set_time } },
+ { "delay", 0, { .setter = (setter_int_t)lv_anim_set_delay } },
+ { "repeat_count", 0, { .setter = (setter_int_t)lv_anim_set_repeat_count } },
+ { "repeat_delay", 0, { .setter = (setter_int_t)lv_anim_set_repeat_delay } },
+ { "early_apply", 0, { .setter = (setter_int_t)lv_anim_set_early_apply } },
+ { "playback_time", 0, { .setter = (setter_int_t)lv_anim_set_playback_time } },
+ { "playback_delay", 0, { .setter = (setter_int_t)lv_anim_set_playback_delay } },
+ { "path", SETTER_TYPE_STACK, { .setter_stack = _lv_anim_set_path } },
+ { "exec_cb", SETTER_TYPE_STACK, { .setter_stack = _lv_dummy_set } },
+};
+
+/* clang-format on */
+
+static int anim_set_para_cb(lua_State *L, void *data)
+{
+ int ret = luavgl_set_property(L, data, anim_property_table);
+
+ if (ret != 0) {
+ debug("failed\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_anim_stop(lua_State *L)
+{
+ luavgl_anim_t *a = luavgl_check_anim(L, 1);
+
+ if (a->aa == NULL || a->self_ref == LUA_NOREF) {
+ debug("already stopped");
+ return 0;
+ }
+
+ lv_anim_del(a, luavgl_anim_exec_cb);
+
+ /* work done in luavgl_anim_delete_cb */
+ return 0;
+}
+
+static int luavgl_anim_delete(lua_State *L)
+{
+ luavgl_anim_stop(L);
+ luavgl_anim_t *a = luavgl_check_anim(L, 1);
+
+ /* object and callbacks can be gc'ed now */
+ luaL_unref(L, LUA_REGISTRYINDEX, a->done_cb);
+ luaL_unref(L, LUA_REGISTRYINDEX, a->exec_cb);
+ luaL_unref(L, LUA_REGISTRYINDEX, a->obj_ref);
+ luaL_unref(L, LUA_REGISTRYINDEX, a->self_ref);
+ a->done_cb = LUA_NOREF;
+ a->exec_cb = LUA_NOREF;
+ a->obj_ref = LUA_NOREF;
+ a->self_ref = LUA_NOREF;
+ a->deleted = true;
+ return 0;
+}
+
+static int luavgl_anim_start(lua_State *L)
+{
+ luavgl_anim_t *a = luavgl_check_anim(L, 1);
+
+ if (a->aa) {
+ debug("we have an anim ongoing, stop it.");
+ luavgl_anim_stop(L);
+ }
+
+ lv_anim_t *new_a = lv_anim_start(&a->cfg);
+ a->aa = new_a;
+ debug("anim %p, aa: %p\n", a, a->aa);
+
+ if (a->self_ref == LUA_NOREF) {
+ /* it's started, thus cannot be gc'ed */
+ lua_pushvalue(L, 1);
+ a->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ return 1;
+}
+
+/**
+ * anim:set { para }
+ * update anim parameter, stop anim if it's ongoing.
+ */
+static int luavgl_anim_set(lua_State *L)
+{
+ luavgl_anim_t *a = luavgl_check_anim(L, 1);
+ if (a->aa) {
+ luavgl_anim_stop(L);
+ }
+
+ /* update anim parameters */
+
+ lv_anim_t *cfg = &a->cfg;
+
+ lua_getfield(L, 2, "exec_cb");
+ if (!lua_isnoneornil(L, -1)) {
+ luaL_unref(L, LUA_REGISTRYINDEX, a->exec_cb);
+ a->exec_cb = luavgl_check_continuation(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "done_cb");
+ if (!lua_isnoneornil(L, -1)) {
+ luaL_unref(L, LUA_REGISTRYINDEX, a->done_cb);
+ a->done_cb = luavgl_check_continuation(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "run");
+ bool run = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ luavgl_iterate(L, 2, anim_set_para_cb, cfg);
+
+ if (run) {
+ luavgl_anim_start(L);
+ }
+
+ return 1;
+}
+
+/**
+ * a = obj:Anim({anim parameters})
+ * a = obj:Anim{anim parameters}
+ * a = lvgl.Anim(var, anim_para)
+ *
+ * a:start()
+ */
+static int luavgl_anim_create(lua_State *L)
+{
+ if (lua_isnoneornil(L, 1)) {
+ return luaL_argerror(L, 1, "anim var must not be nil or none.");
+ }
+
+ if (!lua_istable(L, 2)) {
+ return luaL_argerror(L, 2, "expect anim para table.");
+ }
+
+ luavgl_anim_t *a = lua_newuserdata(L, sizeof(luavgl_anim_t));
+ luaL_getmetatable(L, "lv_anim");
+ lua_setmetatable(L, -2);
+
+ lua_pushvalue(L, 1);
+ a->obj_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ a->exec_cb = LUA_NOREF;
+ a->done_cb = LUA_NOREF;
+ a->self_ref = LUA_NOREF;
+ a->aa = NULL;
+ a->L = L;
+ a->deleted = false;
+
+ lv_anim_t *cfg = &a->cfg;
+ lv_anim_init(cfg);
+ cfg->var = a;
+ cfg->deleted_cb = luavgl_anim_delete_cb;
+ cfg->exec_cb = luavgl_anim_exec_cb;
+
+ /* leave only anim userdata and para table on stack */
+ lua_remove(L, 1); /* para, anim */
+ lua_insert(L, 1); /* anim, para */
+
+ luavgl_anim_set(L);
+ lua_pop(L, 1); /* anim */
+
+ debug("create anim: %p, aa: %p\n", a, a->aa);
+ return 1;
+}
+
+static int luavgl_anim_gc(lua_State *L)
+{
+ debug("\n");
+ luavgl_anim_t *a = luaL_checkudata(L, 1, "lv_anim");
+ if (a->deleted)
+ return 0;
+
+ luavgl_anim_delete(L);
+ return 0;
+}
+
+static int luavgl_anim_tostring(lua_State *L)
+{
+ luavgl_anim_t *a = luavgl_check_anim(L, -1);
+
+ lua_pushfstring(
+ L, "anim %p: %s, value: [%d..%d], current: %d, repeat_cnt: %d", a->aa,
+ a->aa ? "running" : "stopped", a->cfg.start_value, a->cfg.end_value,
+ a->aa ? a->aa->current_value : -1, a->cfg.repeat_cnt);
+ return 1;
+}
+
+static const luaL_Reg luavgl_anim_meta[] = {
+ {"__gc", luavgl_anim_gc },
+ {"__tostring", luavgl_anim_tostring},
+ {"__index", NULL }, /* place holder */
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg luavgl_anim_methods[] = {
+ {"set", luavgl_anim_set },
+ {"start", luavgl_anim_start },
+
+ /* in lua anim, stopped anim can be restarted. */
+ {"stop", luavgl_anim_stop },
+ {"delete", luavgl_anim_delete},
+
+ {NULL, NULL }
+};
+
+static void luavgl_anim_init(lua_State *L)
+{
+ /* create metatable */
+ luaL_newmetatable(L, "lv_anim");
+ luaL_setfuncs(L, luavgl_anim_meta, 0);
+
+ luaL_newlib(L, luavgl_anim_methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+}
diff --git a/lib/luavgl/src/constants.c b/lib/luavgl/src/constants.c
new file mode 100644
index 00000000..1c916405
--- /dev/null
+++ b/lib/luavgl/src/constants.c
@@ -0,0 +1,554 @@
+#include "private.h"
+
+/* clang-format off */
+static void luavgl_event_code_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "ALL"); lua_pushinteger(L, LV_EVENT_ALL); lua_settable(L, -3);
+ lua_pushstring(L, "PRESSED"); lua_pushinteger(L, LV_EVENT_PRESSED); lua_settable(L, -3);
+ lua_pushstring(L, "PRESSING"); lua_pushinteger(L, LV_EVENT_PRESSING); lua_settable(L, -3);
+ lua_pushstring(L, "PRESS_LOST"); lua_pushinteger(L, LV_EVENT_PRESS_LOST); lua_settable(L, -3);
+ lua_pushstring(L, "SHORT_CLICKED"); lua_pushinteger(L, LV_EVENT_SHORT_CLICKED); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_PRESSED"); lua_pushinteger(L, LV_EVENT_LONG_PRESSED); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_PRESSED_REPEAT"); lua_pushinteger(L, LV_EVENT_LONG_PRESSED_REPEAT); lua_settable(L, -3);
+ lua_pushstring(L, "CLICKED"); lua_pushinteger(L, LV_EVENT_CLICKED); lua_settable(L, -3);
+ lua_pushstring(L, "RELEASED"); lua_pushinteger(L, LV_EVENT_RELEASED); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_BEGIN"); lua_pushinteger(L, LV_EVENT_SCROLL_BEGIN); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_END"); lua_pushinteger(L, LV_EVENT_SCROLL_END); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL"); lua_pushinteger(L, LV_EVENT_SCROLL); lua_settable(L, -3);
+ lua_pushstring(L, "GESTURE"); lua_pushinteger(L, LV_EVENT_GESTURE); lua_settable(L, -3);
+ lua_pushstring(L, "KEY"); lua_pushinteger(L, LV_EVENT_KEY); lua_settable(L, -3);
+ lua_pushstring(L, "FOCUSED"); lua_pushinteger(L, LV_EVENT_FOCUSED); lua_settable(L, -3);
+ lua_pushstring(L, "DEFOCUSED"); lua_pushinteger(L, LV_EVENT_DEFOCUSED); lua_settable(L, -3);
+ lua_pushstring(L, "LEAVE"); lua_pushinteger(L, LV_EVENT_LEAVE); lua_settable(L, -3);
+ lua_pushstring(L, "HIT_TEST"); lua_pushinteger(L, LV_EVENT_HIT_TEST); lua_settable(L, -3);
+ lua_pushstring(L, "COVER_CHECK"); lua_pushinteger(L, LV_EVENT_COVER_CHECK); lua_settable(L, -3);
+ lua_pushstring(L, "REFR_EXT_DRAW_SIZE"); lua_pushinteger(L, LV_EVENT_REFR_EXT_DRAW_SIZE); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_MAIN_BEGIN"); lua_pushinteger(L, LV_EVENT_DRAW_MAIN_BEGIN); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_MAIN"); lua_pushinteger(L, LV_EVENT_DRAW_MAIN); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_MAIN_END"); lua_pushinteger(L, LV_EVENT_DRAW_MAIN_END); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_POST_BEGIN"); lua_pushinteger(L, LV_EVENT_DRAW_POST_BEGIN); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_POST"); lua_pushinteger(L, LV_EVENT_DRAW_POST); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_POST_END"); lua_pushinteger(L, LV_EVENT_DRAW_POST_END); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_PART_BEGIN"); lua_pushinteger(L, LV_EVENT_DRAW_PART_BEGIN); lua_settable(L, -3);
+ lua_pushstring(L, "DRAW_PART_END"); lua_pushinteger(L, LV_EVENT_DRAW_PART_END); lua_settable(L, -3);
+ lua_pushstring(L, "VALUE_CHANGED"); lua_pushinteger(L, LV_EVENT_VALUE_CHANGED); lua_settable(L, -3);
+ lua_pushstring(L, "INSERT"); lua_pushinteger(L, LV_EVENT_INSERT); lua_settable(L, -3);
+ lua_pushstring(L, "REFRESH"); lua_pushinteger(L, LV_EVENT_REFRESH); lua_settable(L, -3);
+ lua_pushstring(L, "READY"); lua_pushinteger(L, LV_EVENT_READY); lua_settable(L, -3);
+ lua_pushstring(L, "CANCEL"); lua_pushinteger(L, LV_EVENT_CANCEL); lua_settable(L, -3);
+ lua_pushstring(L, "DELETE"); lua_pushinteger(L, LV_EVENT_DELETE); lua_settable(L, -3);
+ lua_pushstring(L, "CHILD_CHANGED"); lua_pushinteger(L, LV_EVENT_CHILD_CHANGED); lua_settable(L, -3);
+ lua_pushstring(L, "CHILD_CREATED"); lua_pushinteger(L, LV_EVENT_CHILD_CREATED); lua_settable(L, -3);
+ lua_pushstring(L, "CHILD_DELETED"); lua_pushinteger(L, LV_EVENT_CHILD_DELETED); lua_settable(L, -3);
+ lua_pushstring(L, "SCREEN_UNLOAD_START"); lua_pushinteger(L, LV_EVENT_SCREEN_UNLOAD_START); lua_settable(L, -3);
+ lua_pushstring(L, "SCREEN_LOAD_START"); lua_pushinteger(L, LV_EVENT_SCREEN_LOAD_START); lua_settable(L, -3);
+ lua_pushstring(L, "SCREEN_LOADED"); lua_pushinteger(L, LV_EVENT_SCREEN_LOADED); lua_settable(L, -3);
+ lua_pushstring(L, "SCREEN_UNLOADED"); lua_pushinteger(L, LV_EVENT_SCREEN_UNLOADED); lua_settable(L, -3);
+ lua_pushstring(L, "SIZE_CHANGED"); lua_pushinteger(L, LV_EVENT_SIZE_CHANGED); lua_settable(L, -3);
+ lua_pushstring(L, "STYLE_CHANGED"); lua_pushinteger(L, LV_EVENT_STYLE_CHANGED); lua_settable(L, -3);
+ lua_pushstring(L, "LAYOUT_CHANGED"); lua_pushinteger(L, LV_EVENT_LAYOUT_CHANGED); lua_settable(L, -3);
+ lua_pushstring(L, "GET_SELF_SIZE"); lua_pushinteger(L, LV_EVENT_GET_SELF_SIZE); lua_settable(L, -3);
+}
+
+static void luavgl_obj_flag_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "PRESSED"); lua_pushinteger(L, LV_EVENT_PRESSED); lua_settable(L, -3);
+ lua_pushstring(L, "HIDDEN"); lua_pushinteger(L, LV_OBJ_FLAG_HIDDEN); lua_settable(L, -3);
+ lua_pushstring(L, "CLICKABLE"); lua_pushinteger(L, LV_OBJ_FLAG_CLICKABLE); lua_settable(L, -3);
+ lua_pushstring(L, "CLICK_FOCUSABLE"); lua_pushinteger(L, LV_OBJ_FLAG_CLICK_FOCUSABLE); lua_settable(L, -3);
+ lua_pushstring(L, "CHECKABLE"); lua_pushinteger(L, LV_OBJ_FLAG_CHECKABLE); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLLABLE"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLLABLE); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_ELASTIC"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_ELASTIC); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_MOMENTUM"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_MOMENTUM); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_ONE"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_ONE); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_CHAIN_HOR"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_CHAIN_HOR); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_CHAIN_VER"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_CHAIN_VER); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_CHAIN"); lua_pushinteger( L, LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_ON_FOCUS"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_ON_FOCUS); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLL_WITH_ARROW"); lua_pushinteger(L, LV_OBJ_FLAG_SCROLL_WITH_ARROW); lua_settable(L, -3);
+ lua_pushstring(L, "SNAPPABLE"); lua_pushinteger(L, LV_OBJ_FLAG_SNAPPABLE); lua_settable(L, -3);
+ lua_pushstring(L, "PRESS_LOCK"); lua_pushinteger(L, LV_OBJ_FLAG_PRESS_LOCK); lua_settable(L, -3);
+ lua_pushstring(L, "EVENT_BUBBLE"); lua_pushinteger(L, LV_OBJ_FLAG_EVENT_BUBBLE); lua_settable(L, -3);
+ lua_pushstring(L, "GESTURE_BUBBLE"); lua_pushinteger(L, LV_OBJ_FLAG_GESTURE_BUBBLE); lua_settable(L, -3);
+ lua_pushstring(L, "ADV_HITTEST"); lua_pushinteger(L, LV_OBJ_FLAG_ADV_HITTEST); lua_settable(L, -3);
+ lua_pushstring(L, "IGNORE_LAYOUT"); lua_pushinteger(L, LV_OBJ_FLAG_IGNORE_LAYOUT); lua_settable(L, -3);
+ lua_pushstring(L, "FLOATING"); lua_pushinteger(L, LV_OBJ_FLAG_FLOATING); lua_settable(L, -3);
+ lua_pushstring(L, "OVERFLOW_VISIBLE"); lua_pushinteger(L, LV_OBJ_FLAG_OVERFLOW_VISIBLE); lua_settable(L, -3);
+ lua_pushstring(L, "LAYOUT_1"); lua_pushinteger(L, LV_OBJ_FLAG_LAYOUT_1); lua_settable(L, -3);
+ lua_pushstring(L, "LAYOUT_2"); lua_pushinteger(L, LV_OBJ_FLAG_LAYOUT_2); lua_settable(L, -3);
+ lua_pushstring(L, "WIDGET_1"); lua_pushinteger(L, LV_OBJ_FLAG_WIDGET_1); lua_settable(L, -3);
+ lua_pushstring(L, "WIDGET_2"); lua_pushinteger(L, LV_OBJ_FLAG_WIDGET_2); lua_settable(L, -3);
+ lua_pushstring(L, "USER_1"); lua_pushinteger(L, LV_OBJ_FLAG_USER_1); lua_settable(L, -3);
+ lua_pushstring(L, "USER_2"); lua_pushinteger(L, LV_OBJ_FLAG_USER_2); lua_settable(L, -3);
+ lua_pushstring(L, "USER_3"); lua_pushinteger(L, LV_OBJ_FLAG_USER_3); lua_settable(L, -3);
+ lua_pushstring(L, "USER_4"); lua_pushinteger(L, LV_OBJ_FLAG_USER_4); lua_settable(L, -3);
+}
+
+static void luavgl_state_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "DEFAULT"); lua_pushinteger(L, LV_STATE_DEFAULT); lua_settable(L, -3);
+ lua_pushstring(L, "CHECKED"); lua_pushinteger(L, LV_STATE_CHECKED); lua_settable(L, -3);
+ lua_pushstring(L, "FOCUSED"); lua_pushinteger(L, LV_STATE_FOCUSED); lua_settable(L, -3);
+ lua_pushstring(L, "FOCUS_KEY"); lua_pushinteger(L, LV_STATE_FOCUS_KEY); lua_settable(L, -3);
+ lua_pushstring(L, "EDITED"); lua_pushinteger(L, LV_STATE_EDITED); lua_settable(L, -3);
+ lua_pushstring(L, "HOVERED"); lua_pushinteger(L, LV_STATE_HOVERED); lua_settable(L, -3);
+ lua_pushstring(L, "PRESSED"); lua_pushinteger(L, LV_STATE_PRESSED); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLLED"); lua_pushinteger(L, LV_STATE_SCROLLED); lua_settable(L, -3);
+ lua_pushstring(L, "DISABLED"); lua_pushinteger(L, LV_STATE_DISABLED); lua_settable(L, -3);
+ lua_pushstring(L, "USER_1"); lua_pushinteger(L, LV_STATE_USER_1); lua_settable(L, -3);
+ lua_pushstring(L, "USER_2"); lua_pushinteger(L, LV_STATE_USER_2); lua_settable(L, -3);
+ lua_pushstring(L, "USER_3"); lua_pushinteger(L, LV_STATE_USER_3); lua_settable(L, -3);
+ lua_pushstring(L, "USER_4"); lua_pushinteger(L, LV_STATE_USER_4); lua_settable(L, -3);
+ lua_pushstring(L, "ANY"); lua_pushinteger(L, LV_STATE_ANY); lua_settable(L, -3);
+}
+
+static void luavgl_part_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "MAIN"); lua_pushinteger(L, LV_PART_MAIN); lua_settable(L, -3);
+ lua_pushstring(L, "SCROLLBAR"); lua_pushinteger(L, LV_PART_SCROLLBAR); lua_settable(L, -3);
+ lua_pushstring(L, "INDICATOR"); lua_pushinteger(L, LV_PART_INDICATOR); lua_settable(L, -3);
+ lua_pushstring(L, "KNOB"); lua_pushinteger(L, LV_PART_KNOB); lua_settable(L, -3);
+ lua_pushstring(L, "SELECTED"); lua_pushinteger(L, LV_PART_SELECTED); lua_settable(L, -3);
+ lua_pushstring(L, "ITEMS"); lua_pushinteger(L, LV_PART_ITEMS); lua_settable(L, -3);
+ lua_pushstring(L, "TICKS"); lua_pushinteger(L, LV_PART_TICKS); lua_settable(L, -3);
+ lua_pushstring(L, "CURSOR"); lua_pushinteger(L, LV_PART_CURSOR); lua_settable(L, -3);
+ lua_pushstring(L, "CUSTOM_FIRST"); lua_pushinteger(L, LV_PART_CUSTOM_FIRST); lua_settable(L, -3);
+ lua_pushstring(L, "ANY"); lua_pushinteger(L, LV_PART_ANY); lua_settable(L, -3);
+}
+
+static void luavgl_align_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "DEFAULT"); lua_pushinteger(L, LV_ALIGN_DEFAULT); lua_settable(L, -3);
+ lua_pushstring(L, "TOP_LEFT"); lua_pushinteger(L, LV_ALIGN_TOP_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "TOP_MID"); lua_pushinteger(L, LV_ALIGN_TOP_MID); lua_settable(L, -3);
+ lua_pushstring(L, "TOP_RIGHT"); lua_pushinteger(L, LV_ALIGN_TOP_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "BOTTOM_LEFT"); lua_pushinteger(L, LV_ALIGN_BOTTOM_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "BOTTOM_MID"); lua_pushinteger(L, LV_ALIGN_BOTTOM_MID); lua_settable(L, -3);
+ lua_pushstring(L, "BOTTOM_RIGHT"); lua_pushinteger(L, LV_ALIGN_BOTTOM_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "LEFT_MID"); lua_pushinteger(L, LV_ALIGN_LEFT_MID); lua_settable(L, -3);
+ lua_pushstring(L, "RIGHT_MID"); lua_pushinteger(L, LV_ALIGN_RIGHT_MID); lua_settable(L, -3);
+ lua_pushstring(L, "CENTER"); lua_pushinteger(L, LV_ALIGN_CENTER); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_TOP_LEFT"); lua_pushinteger(L, LV_ALIGN_OUT_TOP_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_TOP_MID"); lua_pushinteger(L, LV_ALIGN_OUT_TOP_MID); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_TOP_RIGHT"); lua_pushinteger(L, LV_ALIGN_OUT_TOP_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_BOTTOM_LEFT"); lua_pushinteger(L, LV_ALIGN_OUT_BOTTOM_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_BOTTOM_MID"); lua_pushinteger(L, LV_ALIGN_OUT_BOTTOM_MID); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_BOTTOM_RIGHT"); lua_pushinteger(L, LV_ALIGN_OUT_BOTTOM_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_LEFT_TOP"); lua_pushinteger(L, LV_ALIGN_OUT_LEFT_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_LEFT_MID"); lua_pushinteger(L, LV_ALIGN_OUT_LEFT_MID); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_LEFT_BOTTOM"); lua_pushinteger(L, LV_ALIGN_OUT_LEFT_BOTTOM); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_RIGHT_TOP"); lua_pushinteger(L, LV_ALIGN_OUT_RIGHT_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_RIGHT_MID"); lua_pushinteger(L, LV_ALIGN_OUT_RIGHT_MID); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_RIGHT_BOTTOM"); lua_pushinteger(L, LV_ALIGN_OUT_RIGHT_BOTTOM); lua_settable(L, -3);
+}
+
+static void luavgl_label_const_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "LONG_WRAP"); lua_pushinteger(L, LV_LABEL_LONG_WRAP); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_DOT"); lua_pushinteger(L, LV_LABEL_LONG_DOT); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_SCROLL"); lua_pushinteger(L, LV_LABEL_LONG_SCROLL); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_SCROLL_CIRCULAR"); lua_pushinteger(L, LV_LABEL_LONG_SCROLL_CIRCULAR); lua_settable(L, -3);
+ lua_pushstring(L, "LONG_CLIP"); lua_pushinteger(L, LV_LABEL_LONG_CLIP); lua_settable(L, -3);
+
+}
+static void luavgl_builtin_font_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "DEFAULT"); lua_pushlightuserdata(L, (void*)LV_FONT_DEFAULT); lua_settable(L, -3);
+#if LV_FONT_MONTSERRAT_8
+ LV_FONT_DECLARE(lv_font_montserrat_8)
+ lua_pushstring(L, "MONTSERRAT_8"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_8); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_10
+ LV_FONT_DECLARE(lv_font_montserrat_10)
+ lua_pushstring(L, "MONTSERRAT_10"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_10); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_12
+ LV_FONT_DECLARE(lv_font_montserrat_12)
+ lua_pushstring(L, "MONTSERRAT_12"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_12); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_14
+ LV_FONT_DECLARE(lv_font_montserrat_14)
+ lua_pushstring(L, "MONTSERRAT_14"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_14); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_16
+ LV_FONT_DECLARE(lv_font_montserrat_16)
+ lua_pushstring(L, "MONTSERRAT_16"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_16); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_18
+ LV_FONT_DECLARE(lv_font_montserrat_18)
+ lua_pushstring(L, "MONTSERRAT_18"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_18); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_20
+ LV_FONT_DECLARE(lv_font_montserrat_20)
+ lua_pushstring(L, "MONTSERRAT_20"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_20); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_22
+ LV_FONT_DECLARE(lv_font_montserrat_22)
+ lua_pushstring(L, "MONTSERRAT_22"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_22); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_24
+ LV_FONT_DECLARE(lv_font_montserrat_24)
+ lua_pushstring(L, "MONTSERRAT_24"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_24); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_26
+ LV_FONT_DECLARE(lv_font_montserrat_26)
+ lua_pushstring(L, "MONTSERRAT_26"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_26); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_28
+ LV_FONT_DECLARE(lv_font_montserrat_28)
+ lua_pushstring(L, "MONTSERRAT_28"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_28); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_30
+ LV_FONT_DECLARE(lv_font_montserrat_30)
+ lua_pushstring(L, "MONTSERRAT_30"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_30); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_32
+ LV_FONT_DECLARE(lv_font_montserrat_32)
+ lua_pushstring(L, "MONTSERRAT_32"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_32); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_34
+ LV_FONT_DECLARE(lv_font_montserrat_34)
+ lua_pushstring(L, "MONTSERRAT_34"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_34); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_36
+ LV_FONT_DECLARE(lv_font_montserrat_36)
+ lua_pushstring(L, "MONTSERRAT_36"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_36); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_38
+ LV_FONT_DECLARE(lv_font_montserrat_38)
+ lua_pushstring(L, "MONTSERRAT_38"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_38); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_40
+ LV_FONT_DECLARE(lv_font_montserrat_40)
+ lua_pushstring(L, "MONTSERRAT_40"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_40); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_42
+ LV_FONT_DECLARE(lv_font_montserrat_42)
+ lua_pushstring(L, "MONTSERRAT_42"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_42); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_44
+ LV_FONT_DECLARE(lv_font_montserrat_44)
+ lua_pushstring(L, "MONTSERRAT_44"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_44); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_46
+ LV_FONT_DECLARE(lv_font_montserrat_46)
+ lua_pushstring(L, "MONTSERRAT_46"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_46); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_48
+ LV_FONT_DECLARE(lv_font_montserrat_48)
+ lua_pushstring(L, "MONTSERRAT_48"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_48); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_12_SUBPX
+ LV_FONT_DECLARE(lv_font_montserrat_12_subpx)
+ lua_pushstring(L, "MONTSERRAT_12_SUBPX"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_12_subpx); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_MONTSERRAT_28_COMPRESSED
+ LV_FONT_DECLARE(lv_font_montserrat_28_compressed)
+ lua_pushstring(L, "MONTSERRAT_28_COMPRESSED"); lua_pushlightuserdata(L, (void*)&lv_font_montserrat_28_compressed); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
+ LV_FONT_DECLARE(lv_font_dejavu_16_persian_hebrew)
+ lua_pushstring(L, "DEJAVU_16_PERSIAN_HEBREW"); lua_pushlightuserdata(L, (void*)&lv_font_dejavu_16_persian_hebrew); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_SIMSUN_16_CJK
+ LV_FONT_DECLARE(lv_font_simsun_16_cjk)
+ lua_pushstring(L, "SIMSUN_16_CJK"); lua_pushlightuserdata(L, (void*)&lv_font_simsun_16_cjk); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_UNSCII_8
+ LV_FONT_DECLARE(lv_font_unscii_8)
+ lua_pushstring(L, "UNSCII_8"); lua_pushlightuserdata(L, (void*)&lv_font_unscii_8); lua_settable(L, -3);
+#endif
+
+#if LV_FONT_UNSCII_16
+ LV_FONT_DECLARE(lv_font_unscii_16)
+ lua_pushstring(L, "UNSCII_16"); lua_pushlightuserdata(L, (void*)&lv_font_unscii_16); lua_settable(L, -3);
+#endif
+}
+
+static void luavgl_scr_load_anim_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "NONE"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_NONE); lua_settable(L, -3);
+ lua_pushstring(L, "OVER_LEFT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OVER_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "OVER_RIGHT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OVER_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "OVER_TOP"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OVER_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "OVER_BOTTOM"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OVER_BOTTOM); lua_settable(L, -3);
+ lua_pushstring(L, "MOVE_LEFT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_MOVE_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "MOVE_RIGHT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_MOVE_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "MOVE_TOP"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_MOVE_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "MOVE_BOTTOM"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_MOVE_BOTTOM); lua_settable(L, -3);
+ lua_pushstring(L, "FADE_IN"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_FADE_IN); lua_settable(L, -3);
+ lua_pushstring(L, "FADE_ON"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_FADE_ON); lua_settable(L, -3);
+ lua_pushstring(L, "FADE_OUT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_FADE_OUT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_LEFT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OUT_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_RIGHT"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OUT_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_TOP"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OUT_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "OUT_BOTTOM"); lua_pushinteger(L, LV_SCR_LOAD_ANIM_OUT_BOTTOM); lua_settable(L, -3);
+}
+
+static void luavgl_scrollbar_mode_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "OFF"); lua_pushinteger(L, LV_SCROLLBAR_MODE_OFF); lua_settable(L, -3);
+ lua_pushstring(L, "ON"); lua_pushinteger(L, LV_SCROLLBAR_MODE_ON); lua_settable(L, -3);
+ lua_pushstring(L, "ACTIVE"); lua_pushinteger(L, LV_SCROLLBAR_MODE_ACTIVE); lua_settable(L, -3);
+ lua_pushstring(L, "AUTO"); lua_pushinteger(L, LV_SCROLLBAR_MODE_AUTO); lua_settable(L, -3);
+}
+
+static void luavgl_dir_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "NONE"); lua_pushinteger(L, LV_DIR_NONE); lua_settable(L, -3);
+ lua_pushstring(L, "LEFT"); lua_pushinteger(L, LV_DIR_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "RIGHT"); lua_pushinteger(L, LV_DIR_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "TOP"); lua_pushinteger(L, LV_DIR_TOP); lua_settable(L, -3);
+ lua_pushstring(L, "BOTTOM"); lua_pushinteger(L, LV_DIR_BOTTOM); lua_settable(L, -3);
+ lua_pushstring(L, "HOR"); lua_pushinteger(L, LV_DIR_HOR); lua_settable(L, -3);
+ lua_pushstring(L, "VER"); lua_pushinteger(L, LV_DIR_VER); lua_settable(L, -3);
+ lua_pushstring(L, "ALL"); lua_pushinteger(L, LV_DIR_ALL); lua_settable(L, -3);
+}
+
+static void luavgl_keyboard_mode_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "TEXT_LOWER"); lua_pushinteger(L, LV_KEYBOARD_MODE_TEXT_LOWER); lua_settable(L, -3);
+ lua_pushstring(L, "TEXT_UPPER"); lua_pushinteger(L, LV_KEYBOARD_MODE_TEXT_UPPER); lua_settable(L, -3);
+ lua_pushstring(L, "SPECIAL"); lua_pushinteger(L, LV_KEYBOARD_MODE_SPECIAL); lua_settable(L, -3);
+ lua_pushstring(L, "NUMBER"); lua_pushinteger(L, LV_KEYBOARD_MODE_NUMBER); lua_settable(L, -3);
+ lua_pushstring(L, "USER_1"); lua_pushinteger(L, LV_KEYBOARD_MODE_USER_1); lua_settable(L, -3);
+ lua_pushstring(L, "USER_2"); lua_pushinteger(L, LV_KEYBOARD_MODE_USER_2); lua_settable(L, -3);
+ lua_pushstring(L, "USER_3"); lua_pushinteger(L, LV_KEYBOARD_MODE_USER_3); lua_settable(L, -3);
+ lua_pushstring(L, "USER_4"); lua_pushinteger(L, LV_KEYBOARD_MODE_USER_4); lua_settable(L, -3);
+
+#if LV_USE_ARABIC_PERSIAN_CHARS == 1
+ lua_pushstring(L, "TEXT_ARABIC"); lua_pushinteger(L, LV_KEYBOARD_MODE_TEXT_ARABIC); lua_settable(L, -3);
+#endif
+}
+
+static void luavgl_flex_flow_init(lua_State* L)
+{
+ lua_newtable(L);
+
+ lua_pushstring(L, "ROW"); lua_pushinteger(L, LV_FLEX_FLOW_ROW); lua_settable(L, -3);
+ lua_pushstring(L, "COLUMN"); lua_pushinteger(L, LV_FLEX_FLOW_COLUMN); lua_settable(L, -3);
+ lua_pushstring(L, "ROW_WRAP"); lua_pushinteger(L, LV_FLEX_FLOW_ROW_WRAP); lua_settable(L, -3);
+ lua_pushstring(L, "ROW_REVERSE"); lua_pushinteger(L, LV_FLEX_FLOW_ROW_REVERSE); lua_settable(L, -3);
+ lua_pushstring(L, "ROW_WRAP_REVERSE"); lua_pushinteger(L, LV_FLEX_FLOW_ROW_WRAP_REVERSE); lua_settable(L, -3);
+ lua_pushstring(L, "COLUMN_WRAP"); lua_pushinteger(L, LV_FLEX_FLOW_COLUMN_WRAP); lua_settable(L, -3);
+ lua_pushstring(L, "COLUMN_REVERSE"); lua_pushinteger(L, LV_FLEX_FLOW_COLUMN_REVERSE); lua_settable(L, -3);
+ lua_pushstring(L, "COLUMN_WRAP_REVERSE"); lua_pushinteger(L, LV_FLEX_FLOW_COLUMN_WRAP_REVERSE); lua_settable(L, -3);
+}
+
+static void luavgl_flex_align_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "START"); lua_pushinteger(L, LV_FLEX_ALIGN_START); lua_settable(L, -3);
+ lua_pushstring(L, "END"); lua_pushinteger(L, LV_FLEX_ALIGN_END); lua_settable(L, -3);
+ lua_pushstring(L, "CENTER"); lua_pushinteger(L, LV_FLEX_ALIGN_CENTER); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_EVENLY"); lua_pushinteger(L, LV_FLEX_ALIGN_SPACE_EVENLY); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_AROUND"); lua_pushinteger(L, LV_FLEX_ALIGN_SPACE_AROUND); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_BETWEEN"); lua_pushinteger(L, LV_FLEX_ALIGN_SPACE_BETWEEN); lua_settable(L, -3);
+}
+
+static void luavgl_grid_align_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "START"); lua_pushinteger(L, LV_GRID_ALIGN_START); lua_settable(L, -3);
+ lua_pushstring(L, "CENTER"); lua_pushinteger(L, LV_GRID_ALIGN_CENTER); lua_settable(L, -3);
+ lua_pushstring(L, "END"); lua_pushinteger(L, LV_GRID_ALIGN_END); lua_settable(L, -3);
+ lua_pushstring(L, "STRETCH"); lua_pushinteger(L, LV_GRID_ALIGN_STRETCH); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_EVENLY"); lua_pushinteger(L, LV_GRID_ALIGN_SPACE_EVENLY); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_AROUND"); lua_pushinteger(L, LV_GRID_ALIGN_SPACE_AROUND); lua_settable(L, -3);
+ lua_pushstring(L, "SPACE_BETWEEN"); lua_pushinteger(L, LV_GRID_ALIGN_SPACE_BETWEEN); lua_settable(L, -3);
+}
+
+static void luavgl_roller_mode_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "NORMAL"); lua_pushinteger(L, LV_ROLLER_MODE_NORMAL); lua_settable(L, -3);
+ lua_pushstring(L, "INFINITE"); lua_pushinteger(L, LV_ROLLER_MODE_INFINITE); lua_settable(L, -3);
+}
+
+static void luavgl_key_init(lua_State* L)
+{
+ lua_newtable(L);
+ lua_pushstring(L, "UP"); lua_pushinteger(L, LV_KEY_UP); lua_settable(L, -3);
+ lua_pushstring(L, "DOWN"); lua_pushinteger(L, LV_KEY_DOWN); lua_settable(L, -3);
+ lua_pushstring(L, "RIGHT"); lua_pushinteger(L, LV_KEY_RIGHT); lua_settable(L, -3);
+ lua_pushstring(L, "LEFT"); lua_pushinteger(L, LV_KEY_LEFT); lua_settable(L, -3);
+ lua_pushstring(L, "ESC"); lua_pushinteger(L, LV_KEY_ESC); lua_settable(L, -3);
+ lua_pushstring(L, "DEL"); lua_pushinteger(L, LV_KEY_DEL); lua_settable(L, -3);
+ lua_pushstring(L, "BACKSPACE"); lua_pushinteger(L, LV_KEY_BACKSPACE); lua_settable(L, -3);
+ lua_pushstring(L, "ENTER"); lua_pushinteger(L, LV_KEY_ENTER); lua_settable(L, -3);
+ lua_pushstring(L, "NEXT"); lua_pushinteger(L, LV_KEY_NEXT); lua_settable(L, -3);
+ lua_pushstring(L, "PREV"); lua_pushinteger(L, LV_KEY_PREV); lua_settable(L, -3);
+ lua_pushstring(L, "HOME"); lua_pushinteger(L, LV_KEY_HOME); lua_settable(L, -3);
+ lua_pushstring(L, "END"); lua_pushinteger(L, LV_KEY_END); lua_settable(L, -3);
+}
+
+static int luavgl_LV_PCT(lua_State*L)
+{
+ int pct = lua_tointeger(L, 1);
+ lua_pushinteger(L, LV_PCT(pct));
+ return 1;
+}
+
+static int luavgl_LV_OPA(lua_State*L)
+{
+ int opa = luavgl_tointeger(L, 1) * LV_OPA_100 / 100;
+ if (opa > 255)
+ opa = 255;
+ if (opa < 0)
+ opa = 0;
+
+ lua_pushinteger(L, opa);
+ return 1;
+}
+
+static int luavgl_LV_HOR_RES(lua_State*L)
+{
+ lua_pushinteger(L, LV_HOR_RES);
+ return 1;
+}
+
+static int luavgl_LV_VER_RES(lua_State*L)
+{
+ lua_pushinteger(L, LV_VER_RES);
+ return 1;
+}
+
+/* clang-format on */
+static void luavgl_constants_init(lua_State *L)
+{
+ luavgl_event_code_init(L);
+ lua_setfield(L, -2, "EVENT");
+ luavgl_obj_flag_init(L);
+ lua_setfield(L, -2, "FLAG");
+ luavgl_state_init(L);
+ lua_setfield(L, -2, "STATE");
+ luavgl_part_init(L);
+ lua_setfield(L, -2, "PART");
+ luavgl_align_init(L);
+ lua_setfield(L, -2, "ALIGN");
+ luavgl_builtin_font_init(L);
+ lua_setfield(L, -2, "BUILTIN_FONT");
+ luavgl_label_const_init(L);
+ lua_setfield(L, -2, "LABEL");
+ luavgl_scr_load_anim_init(L);
+ lua_setfield(L, -2, "SCR_LOAD_ANIM");
+ luavgl_scrollbar_mode_init(L);
+ lua_setfield(L, -2, "SCROLLBAR_MODE");
+ luavgl_dir_init(L);
+ lua_setfield(L, -2, "DIR");
+ luavgl_keyboard_mode_init(L);
+ lua_setfield(L, -2, "KEYBOARD_MODE");
+ luavgl_flex_flow_init(L);
+ lua_setfield(L, -2, "FLEX_FLOW");
+ luavgl_flex_align_init(L);
+ lua_setfield(L, -2, "FLEX_ALIGN");
+ luavgl_grid_align_init(L);
+ lua_setfield(L, -2, "GRID_ALIGN");
+ luavgl_roller_mode_init(L);
+ lua_setfield(L, -2, "ROLLER_MODE");
+ luavgl_key_init(L);
+ lua_setfield(L, -2, "KEY");
+ /* miscellaneous. */
+
+ lua_pushinteger(L, LV_ANIM_REPEAT_INFINITE);
+ lua_setfield(L, -2, "ANIM_REPEAT_INFINITE");
+ lua_pushinteger(L, LV_ANIM_PLAYTIME_INFINITE);
+ lua_setfield(L, -2, "ANIM_PLAYTIME_INFINITE");
+
+ lua_pushcfunction(L, luavgl_LV_OPA);
+ lua_setfield(L, -2, "OPA");
+
+ lua_pushcfunction(L, luavgl_LV_PCT);
+ lua_setfield(L, -2, "PCT");
+
+ lua_pushinteger(L, LV_SIZE_CONTENT);
+ lua_setfield(L, -2, "SIZE_CONTENT");
+
+ lua_pushinteger(L, LV_RADIUS_CIRCLE);
+ lua_setfield(L, -2, "RADIUS_CIRCLE");
+
+ lua_pushinteger(L, LV_COORD_MAX);
+ lua_setfield(L, -2, "COORD_MAX");
+ lua_pushinteger(L, LV_COORD_MIN);
+ lua_setfield(L, -2, "COORD_MIN");
+
+ lua_pushinteger(L, LV_IMG_ZOOM_NONE);
+ lua_setfield(L, -2, "IMG_ZOOM_NONE");
+
+ lua_pushinteger(L, LV_BTNMATRIX_BTN_NONE);
+ lua_setfield(L, -2, "BTNMATRIX_BTN_NONE");
+
+ lua_pushinteger(L, LV_CHART_POINT_NONE);
+ lua_setfield(L, -2, "CHART_POINT_NONE");
+
+ lua_pushinteger(L, LV_DROPDOWN_POS_LAST);
+ lua_setfield(L, -2, "DROPDOWN_POS_LAST");
+
+ lua_pushinteger(L, LV_LABEL_DOT_NUM);
+ lua_setfield(L, -2, "LABEL_DOT_NUM");
+ lua_pushinteger(L, LV_LABEL_POS_LAST);
+ lua_setfield(L, -2, "LABEL_POS_LAST");
+ lua_pushinteger(L, LV_LABEL_TEXT_SELECTION_OFF);
+ lua_setfield(L, -2, "LABEL_TEXT_SELECTION_OFF");
+
+ lua_pushinteger(L, LV_TABLE_CELL_NONE);
+ lua_setfield(L, -2, "TABLE_CELL_NONE");
+
+ lua_pushinteger(L, LV_TEXTAREA_CURSOR_LAST);
+ lua_setfield(L, -2, "TEXTAREA_CURSOR_LAST");
+
+ lua_pushinteger(L, LV_LAYOUT_FLEX);
+ lua_setfield(L, -2, "LAYOUT_FLEX");
+
+ lua_pushinteger(L, LV_LAYOUT_GRID);
+ lua_setfield(L, -2, "LAYOUT_GRID");
+
+ lua_pushcfunction(L, luavgl_LV_HOR_RES);
+ lua_setfield(L, -2, "HOR_RES");
+
+ lua_pushcfunction(L, luavgl_LV_VER_RES);
+ lua_setfield(L, -2, "VER_RES");
+}
diff --git a/lib/luavgl/src/disp.c b/lib/luavgl/src/disp.c
new file mode 100644
index 00000000..75233a81
--- /dev/null
+++ b/lib/luavgl/src/disp.c
@@ -0,0 +1,342 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct luavgl_disp_s {
+ lv_disp_t *disp;
+} luavgl_disp_t;
+
+static luavgl_disp_t *luavgl_check_disp(lua_State *L, int idx)
+{
+ luavgl_disp_t *v = luaL_checkudata(L, idx, "lv_disp");
+
+ return v;
+}
+
+static lv_disp_t *luavgl_to_disp(lua_State *L, int idx)
+{
+ return luavgl_check_disp(L, idx)->disp;
+}
+
+static int luavgl_disp_get(lua_State *L, lv_disp_t *disp)
+{
+ /* check if already exists */
+ lua_pushlightuserdata(L, disp);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ /* create new disp userdata and add to registry forever */
+ lua_pop(L, 1);
+ luavgl_disp_t *d = lua_newuserdata(L, sizeof(luavgl_disp_t));
+ d->disp = disp;
+
+ luaL_getmetatable(L, "lv_disp");
+ lua_setmetatable(L, -2);
+
+ lua_pushlightuserdata(L, disp);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+
+ return 1;
+}
+
+static int luavgl_disp_get_default(lua_State *L)
+{
+ lv_disp_t *disp = lv_disp_get_default();
+ return luavgl_disp_get(L, disp);
+}
+
+static int luavgl_disp_get_scr_act(lua_State *L)
+{
+ lv_disp_t *disp = luavgl_to_disp(L, 1);
+ lv_obj_t *obj = lv_disp_get_scr_act(disp);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_disp_get_scr_prev(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_obj_t *obj = lv_disp_get_scr_prev(d->disp);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+/**
+ * luavgl.disp.load_scr(obj, {
+ * anim = "over_left",
+ * time = 1000,
+ * delay = 100,
+ * })
+ */
+static int luavgl_disp_load_scr(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ if (!lua_istable(L, 2)) {
+ lv_disp_load_scr(obj);
+ return 0;
+ }
+
+ /* has parameter table */
+
+ lv_scr_load_anim_t anim = LV_SCR_LOAD_ANIM_NONE;
+ uint32_t time = 0;
+ uint32_t delay = 0;
+ bool auto_del = false;
+
+ const char *str;
+ lua_getfield(L, 2, "anim");
+ str = lua_tostring(L, -1);
+ if (str == NULL || strcmp(str, "none") == 0) {
+ ; /* use default */
+ } else if (strcmp(str, "over_left") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OVER_LEFT;
+ } else if (strcmp(str, "over_right") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OVER_RIGHT;
+ } else if (strcmp(str, "over_top") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OVER_TOP;
+ } else if (strcmp(str, "over_botto") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OVER_BOTTOM;
+ } else if (strcmp(str, "move_left") == 0) {
+ anim = LV_SCR_LOAD_ANIM_MOVE_LEFT;
+ } else if (strcmp(str, "move_right") == 0) {
+ anim = LV_SCR_LOAD_ANIM_MOVE_RIGHT;
+ } else if (strcmp(str, "move_top") == 0) {
+ anim = LV_SCR_LOAD_ANIM_MOVE_TOP;
+ } else if (strcmp(str, "move_botto") == 0) {
+ anim = LV_SCR_LOAD_ANIM_MOVE_BOTTOM;
+ } else if (strcmp(str, "fade_in") == 0) {
+ anim = LV_SCR_LOAD_ANIM_FADE_IN;
+ } else if (strcmp(str, "fade_on") == 0) {
+ anim = LV_SCR_LOAD_ANIM_FADE_ON;
+ } else if (strcmp(str, "fade_out") == 0) {
+ anim = LV_SCR_LOAD_ANIM_FADE_OUT;
+ } else if (strcmp(str, "out_left") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OUT_LEFT;
+ } else if (strcmp(str, "out_right") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OUT_RIGHT;
+ } else if (strcmp(str, "out_top") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OUT_TOP;
+ } else if (strcmp(str, "out_bottom") == 0) {
+ anim = LV_SCR_LOAD_ANIM_OUT_BOTTOM;
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "time");
+ time = luavgl_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "delay");
+ delay = luavgl_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "auto_del");
+ auto_del = luavgl_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_scr_load_anim(obj, anim, time, delay, auto_del);
+ return 0;
+}
+
+static int luavgl_disp_get_layer_top(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_obj_t *obj = lv_disp_get_layer_top(d->disp);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_disp_get_layer_sys(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_obj_t *obj = lv_disp_get_layer_sys(d->disp);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_disp_set_bg_color(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_color_t c = luavgl_tocolor(L, 2);
+
+ lv_disp_set_bg_color(d->disp, c);
+
+ return 0;
+}
+
+static int luavgl_disp_set_bg_image(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ const char *img = luavgl_toimgsrc(L, 2);
+
+ lv_disp_set_bg_image(d->disp, img);
+
+ return 0;
+}
+
+static int luavgl_disp_set_bg_opa(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_opa_t opa = luavgl_tointeger(L, 2);
+ lv_disp_set_bg_opa(d->disp, opa);
+
+ return 0;
+}
+
+static int luavgl_disp_get_chroma_key_color(lua_State *L)
+{
+#if LVGL_VERSION_MAJOR >= 9
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lv_color_t c = lv_disp_get_chroma_key_color(d->disp);
+ lua_pushinteger(L, c.full);
+#else
+ lua_pushinteger(L, 0);
+#endif
+ return 1;
+}
+
+static int luavgl_disp_get_next(lua_State *L)
+{
+ lv_disp_t *disp = NULL;
+ if (!lua_isnoneornil(L, 1)) {
+ disp = luavgl_check_disp(L, 1)->disp;
+ }
+
+ disp = lv_disp_get_next(disp);
+ if (disp == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ return luavgl_disp_get(L, disp);
+}
+
+static int luavgl_disp_get_res(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ lua_pushinteger(L, lv_disp_get_hor_res(d->disp));
+ lua_pushinteger(L, lv_disp_get_ver_res(d->disp));
+ return 2;
+}
+
+static int luavgl_disp_set_rotation(lua_State *L)
+{
+ luavgl_disp_t *d = luavgl_check_disp(L, 1);
+ uint32_t r = lua_tointeger(L, 2);
+
+ lv_disp_rot_t rot;
+ if (r == 0)
+ rot = LV_DISP_ROT_NONE;
+ else if (r == 90)
+ rot = LV_DISP_ROT_90;
+ else if (r == 180)
+ rot = LV_DISP_ROT_180;
+ else if (r == 270)
+ rot = LV_DISP_ROT_270;
+ else {
+ return luaL_argerror(L, 2, "invalid rotation value");
+ }
+
+ lv_disp_set_rotation(d->disp, rot);
+ return 0;
+}
+
+/**
+ * luavgl.disp lib
+ */
+static const luaL_Reg disp_lib[] = {
+ {"get_default", luavgl_disp_get_default },
+ {"get_scr_act", luavgl_disp_get_scr_act },
+ {"get_scr_prev", luavgl_disp_get_scr_prev},
+ {"get_next", luavgl_disp_get_next },
+ {"load_scr", luavgl_disp_load_scr },
+
+ {NULL, NULL },
+};
+
+/*
+** methods for disp handles
+*/
+static const luaL_Reg disp_methods[] = {
+ {"get_layer_top", luavgl_disp_get_layer_top },
+ {"get_layer_sys", luavgl_disp_get_layer_sys },
+ {"set_bg_color", luavgl_disp_set_bg_color },
+ {"set_bg_image", luavgl_disp_set_bg_image },
+ {"set_bg_opa", luavgl_disp_set_bg_opa },
+ {"get_chroma_key_color", luavgl_disp_get_chroma_key_color},
+ {"get_next", luavgl_disp_get_next },
+ {"set_rotation", luavgl_disp_set_rotation },
+ {"get_res", luavgl_disp_get_res },
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg disp_meta[] = {
+ {"__index", NULL}, /* place holder */
+
+ {NULL, NULL}
+};
+
+static void luavgl_disp_init(lua_State *L)
+{
+ /* create lv_fs metatable */
+ luaL_newmetatable(L, "lv_disp");
+ luaL_setfuncs(L, disp_meta, 0);
+
+ luaL_newlib(L, disp_methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+
+ /* luavgl.disp lib */
+ luaL_newlib(L, disp_lib);
+ lua_setfield(L, -2, "disp");
+}
diff --git a/lib/luavgl/src/event.c b/lib/luavgl/src/event.c
new file mode 100644
index 00000000..88748a6f
--- /dev/null
+++ b/lib/luavgl/src/event.c
@@ -0,0 +1,191 @@
+#include "luavgl.h"
+#include "private.h"
+
+static void luavgl_obj_event_cb(lv_event_t *e)
+{
+ lua_State *L = e->user_data;
+ if (L == NULL) {
+ debug("Null user data, should be L.\n");
+ }
+
+ int top = lua_gettop(L);
+ lv_obj_t *obj = e->current_target;
+
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luavgl_obj_t *lobj = luavgl_to_lobj(L, -1);
+ if (lobj == NULL || lobj->obj == NULL)
+ goto event_exit;
+
+ int ref = LUA_NOREF;
+ for (int i = 0; i < lobj->n_events; i++) {
+ if (lobj->events[i].code == LV_EVENT_ALL ||
+ lobj->events[i].code == e->code) {
+ ref = lobj->events[i].ref;
+ break;
+ }
+ }
+
+ if (ref == LUA_NOREF) {
+ /* nobody cares this event, something went wrong but can be ignored. */
+ goto event_exit;
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ lua_pushinteger(L, e->code);
+
+ /* args: obj, code */
+ luavgl_pcall_int(L, 2, 0);
+
+event_exit:
+ lua_settop(L, top);
+}
+
+static void luavgl_obj_remove_event(lua_State *L, lv_obj_t *obj,
+ struct event_callback_s *event)
+{
+ luaL_unref(L, LUA_REGISTRYINDEX, event->ref);
+ event->code = -1; /* mark it as unused. */
+ event->ref = LUA_NOREF;
+ lv_obj_remove_event_dsc(obj, event->dsc);
+ event->dsc = NULL;
+}
+
+/* obj:onevent(luavgl.EVENT.PRESSED, function(code, value) -- end) */
+static int luavgl_obj_on_event(lua_State *L)
+{
+ bool remove_all; /* if third parameter is noneornil, remove all events. */
+
+ luavgl_obj_t *lobj = luavgl_to_lobj(L, 1);
+ lv_obj_t *obj = lobj->obj;
+ if (obj == NULL) {
+ luaL_argerror(L, 1, "expect obj userdata.\n");
+ return 0;
+ }
+
+ lv_event_code_t code = lua_tointeger(L, 2);
+ if (code >= _LV_EVENT_LAST) {
+ luaL_argerror(L, 2, "event code illegal");
+ return 0;
+ }
+
+ remove_all = lua_isnoneornil(L, 3);
+
+ /* check if event code already added, find a slot to store this callback */
+ int slot = 0;
+ if (lobj && lobj->events) {
+ for (; slot < lobj->n_events; slot++) {
+ struct event_callback_s *event = &lobj->events[slot];
+ if (event->code == code) { /* same event can only be added once. */
+ luavgl_obj_remove_event(L, obj, event);
+ if (remove_all)
+ continue; /* continue to remove all events associated. */
+ else
+ break; /* use this slot for our new event callback */
+ }
+
+ if (event->code == -1) {
+ /* this callback has been removed, thus, we can use this slot */
+ break;
+ }
+ }
+ }
+
+ if (remove_all) /* no need to add, just return */
+ return 0;
+
+ struct event_callback_s *events = lobj->events;
+
+ /* create obj->lobj->events, if NULL, realloc if existing and find no slot
+ */
+ if (events == NULL) {
+ events = calloc(sizeof(struct event_callback_s), 1);
+ if (events == NULL) {
+ return luaL_error(L, "No memory.");
+ }
+
+ lobj->events = events;
+ lobj->n_events = 1;
+ } else {
+ /* realloc? */
+ if (slot && slot == lobj->n_events) {
+ struct event_callback_s *_events;
+ _events = realloc(lobj->events, (lobj->n_events + 1) * sizeof(*_events));
+ if (_events == NULL) {
+ return luaL_error(L, "No memory.");
+ }
+ events = _events;
+ lobj->n_events++; /* now we have +1 event */
+ lobj->events = events;
+ }
+ /* else: we have found a slot to reuse, use it. */
+ }
+
+ /* setup event callback */
+
+ void *dsc = lv_obj_add_event_cb(obj, luavgl_obj_event_cb, code, L);
+ struct event_callback_s *event = &events[slot];
+ event->code = code;
+ event->ref = luavgl_check_continuation(L, 3);
+ event->dsc = dsc;
+
+ return 0;
+}
+
+/* obj:onClicked(function(code, value) end) */
+static int luavgl_obj_on_clicked(lua_State *L)
+{
+ /* stack: obj, function cb */
+ lua_pushinteger(L, LV_EVENT_CLICKED);
+ lua_insert(L, 2);
+
+ return luavgl_obj_on_event(L);
+}
+
+/* obj:onShortClicked(function(code, value) end) */
+static int luavgl_obj_on_short_clicked(lua_State *L)
+{
+ /* stack: obj, function cb */
+ lua_pushinteger(L, LV_EVENT_SHORT_CLICKED);
+ lua_insert(L, 2);
+
+ return luavgl_obj_on_event(L);
+}
+
+/* obj:onPressed(function(code, value) end) */
+static int luavgl_obj_on_pressed(lua_State *L)
+{
+ lua_pushinteger(L, LV_EVENT_PRESSED);
+ lua_insert(L, 2);
+
+ return luavgl_obj_on_event(L);
+}
+
+static void luavgl_obj_event_init(luavgl_obj_t *lobj) { lobj->n_events = 0; }
+
+/**
+ * Remove all events added, and free memory of events
+ */
+static void luavgl_obj_remove_event_all(lua_State *L, luavgl_obj_t *lobj)
+{
+ if (lobj == NULL || lobj->events == NULL) {
+ return;
+ }
+
+ struct event_callback_s *events = lobj->events;
+
+ int i = 0;
+ for (; i < lobj->n_events; i++) {
+ struct event_callback_s *event = &lobj->events[i];
+ if (event->code != -1) {
+ luavgl_obj_remove_event(L, lobj->obj, event);
+ }
+ }
+
+ free(events);
+ lobj->n_events = 0;
+ lobj->events = NULL;
+}
diff --git a/lib/luavgl/src/font.c b/lib/luavgl/src/font.c
new file mode 100644
index 00000000..95f2a6f1
--- /dev/null
+++ b/lib/luavgl/src/font.c
@@ -0,0 +1,313 @@
+#include "luavgl.h"
+#include "private.h"
+
+#define FONT_DEFAULT_SIZE 12
+
+#define _ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+/**
+ * Follow css style, specify the name by name family, name size,
+ * name weight. Font weight can be numeric value or 'bold'. Alls strings
+ * are converted to lower-case before matching with local supported name.
+ *
+ * weight named weight
+ * 100 thin
+ * 200 extra light
+ * 300 light
+ * 400 normal (*default)
+ * 500 medium
+ * 600 semi bold
+ * 700 bold
+ * 800 extra bold
+ * 900 ultra bold
+ *
+ * Only normal weight is supported to builtin name.
+ *
+ * Font returned to lua is a userdata, it's used by object style, or style
+ * directly. If name is no longer ref'ed by any object or style, it's
+ * gc'ed.
+ */
+
+/**
+ * lvgl.Font("montserrat, unscii", 8, bold)
+ * lvgl.Font("montserrat", 8)
+ * lvgl.Font("montserrat")
+ *
+ * return nil if failed
+ */
+
+typedef enum {
+ FONT_WEIGHT_THIN = 100,
+ FONT_WEIGHT_EXTRA_LIGHT = 200,
+ FONT_WEIGHT_LIGHT = 300,
+ FONT_WEIGHT_NORMAL = 400,
+ FONT_WEIGHT_MEDIUM = 500,
+ FONT_WEIGHT_SEMI_BOLD = 600,
+ FONT_WEIGHT_BOLD = 700,
+ FONT_WEIGHT_EXTRA_BOLD = 800,
+ FONT_WEIGHT_ULTRA_BOLD = 900,
+} font_weight_t;
+
+static const struct {
+ char *name;
+ int value;
+} g_named_weight[] = {
+ {"thin", 100},
+ {"extra light", 200},
+ {"light", 300},
+ {"normal", 400},
+ {"medium", 500},
+ {"semi bold", 600},
+ {"bold", 700},
+ {"extra bold", 800},
+ {"ultra bold", 900},
+};
+
+static const struct {
+ int size;
+ const lv_font_t *font;
+} g_builtin_montserrat[] = {
+#if LV_FONT_MONTSERRAT_8
+ {8, &lv_font_montserrat_8 },
+#endif
+
+#if LV_FONT_MONTSERRAT_10
+ {10, &lv_font_montserrat_10},
+#endif
+
+#if LV_FONT_MONTSERRAT_12
+ {12, &lv_font_montserrat_12},
+#endif
+
+#if LV_FONT_MONTSERRAT_14
+ {14, &lv_font_montserrat_14},
+#endif
+
+#if LV_FONT_MONTSERRAT_16
+ {16, &lv_font_montserrat_16},
+#endif
+
+#if LV_FONT_MONTSERRAT_18
+ {18, &lv_font_montserrat_18},
+#endif
+
+#if LV_FONT_MONTSERRAT_20
+ {20, &lv_font_montserrat_20},
+#endif
+
+#if LV_FONT_MONTSERRAT_22
+ {22, &lv_font_montserrat_22},
+#endif
+
+#if LV_FONT_MONTSERRAT_24
+ {24, &lv_font_montserrat_24},
+#endif
+
+#if LV_FONT_MONTSERRAT_26
+ {26, &lv_font_montserrat_26},
+#endif
+
+#if LV_FONT_MONTSERRAT_28
+ {28, &lv_font_montserrat_28},
+#endif
+
+#if LV_FONT_MONTSERRAT_30
+ {30, &lv_font_montserrat_30},
+#endif
+
+#if LV_FONT_MONTSERRAT_32
+ {32, &lv_font_montserrat_32},
+#endif
+
+#if LV_FONT_MONTSERRAT_34
+ {34, &lv_font_montserrat_34},
+#endif
+
+#if LV_FONT_MONTSERRAT_36
+ {36, &lv_font_montserrat_36},
+#endif
+
+#if LV_FONT_MONTSERRAT_38
+ {38, &lv_font_montserrat_38},
+#endif
+
+#if LV_FONT_MONTSERRAT_40
+ {40, &lv_font_montserrat_40},
+#endif
+
+#if LV_FONT_MONTSERRAT_42
+ {42, &lv_font_montserrat_42},
+#endif
+
+#if LV_FONT_MONTSERRAT_44
+ {44, &lv_font_montserrat_44},
+#endif
+
+#if LV_FONT_MONTSERRAT_46
+ {46, &lv_font_montserrat_46},
+#endif
+
+#if LV_FONT_MONTSERRAT_48
+ {48, &lv_font_montserrat_48},
+#endif
+};
+
+static int luavgl_get_named_weight(const char *name)
+{
+ if (name == NULL) {
+ return FONT_DEFAULT_SIZE;
+ }
+
+ for (int i = 0; i < _ARRAY_LEN(g_named_weight); i++) {
+ if (strcmp(name, g_named_weight[i].name) == 0) {
+ return g_named_weight[i].value;
+ }
+ }
+
+ return FONT_DEFAULT_SIZE;
+}
+
+static char *to_lower(char *str)
+{
+ for (char *s = str; *s; ++s)
+ *s = *s >= 'A' && *s <= 'Z' ? *s | 0x60 : *s;
+ return str;
+}
+
+static const lv_font_t *_luavgl_font_create(lua_State *L, const char *name,
+ int size, int weight)
+{
+ /* check builtin font firstly. */
+ if (strcmp(name, "montserrat") == 0) {
+ if (FONT_WEIGHT_NORMAL != weight)
+ return NULL;
+
+ for (int i = 0; i < _ARRAY_LEN(g_builtin_montserrat); i++) {
+ if (size == g_builtin_montserrat[i].size) {
+ return g_builtin_montserrat[i].font;
+ }
+ }
+ } else if (strcmp(name, "unscii") == 0) {
+ if (FONT_WEIGHT_NORMAL != weight)
+ return NULL;
+#if LV_FONT_UNSCII_8
+ if (size == 8)
+ return &lv_font_unscii_8;
+#endif
+
+#if LV_FONT_UNSCII_16
+ if (size == 16)
+ return &lv_font_unscii_16;
+#endif
+ }
+#if LV_FONT_MONTSERRAT_12_SUBPX
+ else if (strcmp(name, "montserrat_subpx") == 0) {
+ if (size == 12)
+ return &lv_font_montserrat_12_subpx;
+ }
+#endif
+#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
+ else if (strcmp(name, "dejavu_persian_hebrew") == 0) {
+ if (size == 16)
+ return &lv_font_dejavu_16_persian_hebrew;
+ }
+#endif
+
+#if LV_FONT_SIMSUN_16_CJK
+ else if (strcmp(name, "dejavu_persian_hebrew") == 0) {
+ if (size == 16)
+ return &lv_font_simsun_16_cjk;
+ }
+#endif
+
+ /* not built-in font, check extension */
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ if (ctx->make_font) {
+ return ctx->make_font(name, size, weight);
+ }
+
+ return NULL;
+}
+
+/**
+ * Dynamic font family fallback is not supported.
+ * The fallback only happen when font creation fails and continue to try next
+ * one. Fallback logic in lvgl is supposed to be system wide.
+ *
+ * lvgl.Font("MiSansW medium, montserrat", 24, "normal")
+ */
+static int luavgl_font_create(lua_State *L)
+{
+ int weight;
+ int size;
+ char *str, *name;
+ const lv_font_t *font = NULL;
+
+ if (!lua_isstring(L, 1)) {
+ return luaL_argerror(L, 1, "expect string");
+ }
+
+ /* size is optional, default to FONT_DEFAULT_SIZE */
+ size = lua_tointeger(L, 2);
+ if (size == 0) {
+ size = FONT_DEFAULT_SIZE;
+ }
+
+ if (!lua_isnoneornil(L, 3)) {
+ if (lua_isinteger(L, 3)) {
+ weight = lua_tointeger(L, 3);
+ } else {
+ char *luastr = (char *)lua_tostring(L, 3);
+ int len = strlen(luastr);
+ if (len > 128) {
+ /* not likely to happen */
+ return luaL_argerror(L, 3, "too long");
+ }
+ char s[len + 1];
+ strcpy(s, luastr);
+ weight = luavgl_get_named_weight(to_lower(s));
+ }
+ } else {
+ weight = FONT_WEIGHT_NORMAL;
+ }
+
+ str = strdup(lua_tostring(L, 1));
+ if (str == NULL) {
+ return luaL_error(L, "no memory.");
+ }
+
+ name = to_lower(str);
+ while (*name) {
+ if (*name == ' ') {
+ name++;
+ continue;
+ }
+
+ char *end = strchr(name, ',');
+ if (end != NULL) {
+ *end = '\0';
+ } else {
+ end = name + strlen(name);
+ }
+
+ char *trim = end - 1;
+ while (*trim == ' ') {
+ *trim-- = '\0'; /* trailing space. */
+ }
+
+ font = _luavgl_font_create(L, name, size, weight);
+ if (font) {
+ break;
+ }
+
+ name = end + 1; /* next */
+ }
+
+ free(str);
+ if (font) {
+ lua_pushlightuserdata(L, (void *)font);
+ return 1;
+ }
+
+ return luaL_error(L, "cannot create font.");
+}
diff --git a/lib/luavgl/src/fs.c b/lib/luavgl/src/fs.c
new file mode 100644
index 00000000..e6fbf712
--- /dev/null
+++ b/lib/luavgl/src/fs.c
@@ -0,0 +1,383 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct luavgl_fs_s {
+ lv_fs_file_t file;
+ bool closed; /* userdata exists but lv_fs has been closed */
+} luavgl_fs_t;
+
+typedef struct luavgl_dir_s {
+ lv_fs_dir_t dir;
+ bool closed; /* userdata exists but lv_fs has been closed */
+} luavgl_dir_t;
+
+static luavgl_fs_t *luavgl_to_fs(lua_State *L, int index)
+{
+ luavgl_fs_t *v = luaL_checkudata(L, index, "lv_fs");
+ if (v->closed) {
+ luaL_error(L, "attempt to use a closed file");
+ }
+
+ return v;
+}
+
+static luavgl_dir_t *luavgl_to_dir(lua_State *L, int index)
+{
+ luavgl_dir_t *v = luaL_checkudata(L, index, "lv_dir");
+ if (v->closed) {
+ luaL_error(L, "attempt to use a closed file");
+ }
+
+ return v;
+}
+
+/**
+ * luavgl.open_file(filename, [mode])
+ * mode: "r" "w", others not supported.
+ */
+static int luavgl_fs_open(lua_State *L)
+{
+ const char *path = lua_tostring(L, 1);
+ const char *mode = "r";
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ mode = lua_tostring(L, 2);
+ }
+
+ luavgl_fs_t *f = lua_newuserdata(L, sizeof(luavgl_fs_t));
+ f->closed = false;
+
+ lv_fs_mode_t lmode = 0;
+ if (strchr(mode, 'r'))
+ lmode |= LV_FS_MODE_RD;
+
+ if (strchr(mode, 'w'))
+ lmode |= LV_FS_MODE_WR;
+
+ lv_fs_res_t res = lv_fs_open(&f->file, path, lmode);
+ if (res != LV_FS_RES_OK) {
+ debug("open failed: %s\n", path);
+ lua_pushnil(L);
+ lua_pushfstring(L, "open failed: %s\n", path);
+ lua_pushinteger(L, res); /* return lv_fs error number */
+ return 3;
+ }
+
+ luaL_getmetatable(L, "lv_fs");
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+/* read_chars modified for lv_fs */
+static int read_chars(lua_State *L, lv_fs_file_t *f, size_t n)
+{
+ uint32_t nr; /* number of chars actually read */
+ char *p;
+
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
+ lv_fs_res_t res = lv_fs_read(f, p, n, &nr);
+ if (res != LV_FS_RES_OK)
+ return false;
+
+ luaL_pushresultsize(&b, nr);
+ return (nr > 0); /* true iff read something */
+}
+
+/* read_all modified for lv_fs */
+static void read_all(lua_State *L, lv_fs_file_t *f)
+{
+ uint32_t nr;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
+ char *p = luaL_prepbuffer(&b);
+ lv_fs_read(f, p, LUAL_BUFFERSIZE, &nr);
+ luaL_addsize(&b, nr);
+ } while (nr == LUAL_BUFFERSIZE);
+ luaL_pushresult(&b); /* close buffer */
+}
+
+/**
+ * f:read()
+ */
+static int luavgl_fs_read(lua_State *L)
+{
+ luavgl_fs_t *f = luavgl_to_fs(L, 1);
+ int nargs = lua_gettop(L) - 1;
+ int n, success;
+
+ if (nargs == 0)
+ return luaL_error(L, "read mode required: 'a' or num ");
+
+ /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, nargs + LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = 2; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)luaL_checkinteger(L, n);
+ success = read_chars(L, &f->file, l);
+ } else {
+ const char *p = luaL_checkstring(L, n);
+ if (*p == '*')
+ p++; /* skip optional '*' (for compatibility) */
+ switch (*p) {
+ case 'a': /* file */
+ read_all(L, &f->file); /* read entire file */
+ success = 1; /* always success */
+ break;
+ case 'n': /* number */
+ case 'l': /* line */
+ case 'L': /* line with end-of-line */
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ lua_pushnil(L); /* push nil instead */
+ }
+
+ return n - 2;
+}
+
+static int luavgl_fs_write(lua_State *L)
+{
+ luavgl_fs_t *f = luavgl_to_fs(L, 1);
+ lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
+
+ int arg = 2;
+ int nargs = lua_gettop(L) - arg;
+ int status = 1;
+ lv_fs_res_t res = 0;
+ size_t l;
+ uint32_t nw;
+ const char *s;
+
+ for (; nargs--; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) {
+ s = luaL_tolstring(L, arg, &l);
+ res = lv_fs_write(&f->file, s, l, &nw);
+ lua_pop(L, 1);
+ } else {
+ s = luaL_checklstring(L, arg, &l);
+ res = lv_fs_write(&f->file, s, l, &nw);
+ }
+ status = status && nw == l && res == LV_FS_RES_OK;
+ }
+
+ if (status) {
+ return 1; /* file handle already on stack top */
+ }
+
+ lua_pushnil(L);
+ lua_pushstring(L, "failed"); /* no details */
+ lua_pushinteger(L, res);
+ return 3;
+}
+
+static int luavgl_fs_close(lua_State *L)
+{
+ luavgl_fs_t *f = luavgl_to_fs(L, 1);
+
+ debug("\n");
+ f->closed = true;
+ lv_fs_close(&f->file);
+
+ return 0;
+}
+
+static int luavgl_fs_seek(lua_State *L)
+{
+ static const int mode[] = {LV_FS_SEEK_SET, LV_FS_SEEK_CUR, LV_FS_SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ luavgl_fs_t *f = luavgl_to_fs(L, 1);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ lua_Integer p3 = luaL_optinteger(L, 3, 0);
+ uint32_t offset = (uint32_t)p3;
+ luaL_argcheck(L, (lua_Integer)offset == p3, 3,
+ "not an integer in proper range");
+
+ lv_fs_res_t res = lv_fs_seek(&f->file, offset, mode[op]);
+ if (res != LV_FS_RES_OK) {
+ lua_pushnil(L);
+
+ lua_pushstring(L, "failed");
+ lua_pushinteger(L, res);
+ return 3;
+ }
+
+ uint32_t pos;
+ res = lv_fs_tell(&f->file, &pos);
+ if (res != LV_FS_RES_OK) {
+ lua_pushinteger(L, 0);
+ } else {
+ lua_pushinteger(L, (lua_Integer)pos);
+ }
+ return 1;
+}
+
+static int luavgl_fs_tostring(lua_State *L)
+{
+ lua_pushfstring(L, "lv_fs handler: %p\n", luavgl_to_fs(L, 1));
+ return 1;
+}
+
+static int luavgl_fs_gc(lua_State *L)
+{
+ debug("\n");
+
+ luavgl_fs_t *v = luaL_checkudata(L, 1, "lv_fs");
+ if (!v->closed) {
+ v->closed = true;
+ lv_fs_close(&v->file);
+ }
+
+ return 0;
+}
+
+/**
+ * luavgl.open_dir(path)
+ */
+static int luavgl_dir_open(lua_State *L)
+{
+ const char *path = lua_tostring(L, 1);
+
+ luavgl_dir_t *d = lua_newuserdata(L, sizeof(luavgl_dir_t));
+ d->closed = false;
+
+ lv_fs_res_t res = lv_fs_dir_open(&d->dir, path);
+ if (res != LV_FS_RES_OK) {
+ debug("open failed: %s\n", path);
+ lua_pushnil(L);
+ lua_pushfstring(L, "open failed: %s\n", path);
+ lua_pushinteger(L, res); /* return lv_fs error number */
+ return 3;
+ }
+
+ luaL_getmetatable(L, "lv_dir");
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+static int luavgl_dir_read(lua_State *L)
+{
+ luavgl_dir_t *d = luavgl_to_dir(L, 1);
+ char buffer[PATH_MAX];
+ lv_fs_res_t res = lv_fs_dir_read(&d->dir, buffer);
+ if (res != LV_FS_RES_OK || buffer[0] == '\0') {
+ lua_pushnil(L);
+ } else {
+ lua_pushstring(L, buffer);
+ }
+
+ return 1;
+}
+
+static int luavgl_dir_close(lua_State *L)
+{
+ debug("\n");
+ luavgl_dir_t *d = luavgl_to_dir(L, 1);
+ d->closed = true;
+ lv_fs_dir_close(&d->dir);
+ return 0;
+}
+
+static int luavgl_dir_tostring(lua_State *L)
+{
+ lua_pushfstring(L, "lv_fs dir handler: %p\n", luavgl_to_dir(L, 1));
+ return 1;
+}
+
+static int luavgl_dir_gc(lua_State *L)
+{
+ debug("\n");
+
+ luavgl_dir_t *v = luaL_checkudata(L, 1, "lv_dir");
+ if (!v->closed) {
+ v->closed = true;
+ lv_fs_dir_close(&v->dir);
+ }
+
+ return 0;
+}
+
+/**
+ * luavgl.fs lib
+ *
+ */
+static const luaL_Reg fs_lib[] = {
+ {"open_file", luavgl_fs_open },
+ {"open_dir", luavgl_dir_open},
+
+ {NULL, NULL },
+};
+
+/*
+** methods for file handles
+*/
+static const luaL_Reg fs_methods[] = {
+ {"read", luavgl_fs_read },
+ {"write", luavgl_fs_write},
+ {"close", luavgl_fs_close},
+ {"seek", luavgl_fs_seek },
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg fs_meta[] = {
+ {"__gc", luavgl_fs_gc },
+ {"__close", luavgl_fs_gc },
+ {"__tostring", luavgl_fs_tostring},
+ {"__index", NULL }, /* place holder */
+
+ {NULL, NULL }
+};
+
+/*
+** methods for dir handles
+*/
+static const luaL_Reg dir_methods[] = {
+ {"read", luavgl_dir_read },
+ {"close", luavgl_dir_close},
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg dir_meta[] = {
+ {"__gc", luavgl_dir_gc },
+ {"__close", luavgl_dir_gc },
+ {"__tostring", luavgl_dir_tostring},
+ {"__index", NULL }, /* place holder */
+
+ {NULL, NULL }
+};
+
+static void luavgl_fs_init(lua_State *L)
+{
+ /* create lv_fs metatable */
+ luaL_newmetatable(L, "lv_fs");
+ luaL_setfuncs(L, fs_meta, 0);
+
+ luaL_newlib(L, fs_methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+
+ /* create lv_dir metatable */
+ luaL_newmetatable(L, "lv_dir");
+ luaL_setfuncs(L, dir_meta, 0);
+
+ luaL_newlib(L, dir_methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+
+ /* luavgl.fs lib */
+ luaL_newlib(L, fs_lib);
+ lua_setfield(L, -2, "fs");
+}
diff --git a/lib/luavgl/src/group.c b/lib/luavgl/src/group.c
new file mode 100644
index 00000000..7d88bbe9
--- /dev/null
+++ b/lib/luavgl/src/group.c
@@ -0,0 +1,347 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct luavgl_group_s {
+ lv_group_t *group;
+ bool lua_created; /* this group is created from lua */
+ bool deleted; /* deleted but not gc'ed */
+} luavgl_group_t;
+
+static luavgl_group_t *luavgl_check_group(lua_State *L, int idx)
+{
+ luavgl_group_t *v = luaL_checkudata(L, idx, "lv_group");
+
+ if (v->deleted) {
+ luaL_error(L, "attempt to use a deleted group");
+ return NULL;
+ }
+
+ return v;
+}
+
+static lv_group_t *luavgl_to_group(lua_State *L, int idx)
+{
+ return luavgl_check_group(L, idx)->group;
+}
+
+/**
+ * create new luavgl group userdata.
+ */
+static int luavgl_group_get(lua_State *L, lv_group_t *group)
+{
+ /* check if already exists */
+ lua_pushlightuserdata(L, group);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ /* create new indev userdata and add to registry */
+ lua_pop(L, 1);
+ luavgl_group_t *g = lua_newuserdata(L, sizeof(luavgl_group_t));
+ g->deleted = false;
+ g->lua_created = true;
+ g->group = group;
+
+ luaL_getmetatable(L, "lv_group");
+ lua_setmetatable(L, -2);
+
+ lua_pushlightuserdata(L, group);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+
+ return 1;
+}
+
+/**
+ * group = luavgl.group.create()
+ */
+static int luavgl_group_create(lua_State *L)
+{
+ lv_group_t *group = lv_group_create();
+ return luavgl_group_get(L, group);
+}
+
+/**
+ * Delete a lua created group
+ */
+static int luavgl_group_delete(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ /* remove from registry */
+ lua_pushlightuserdata(L, g->group);
+ lua_pushnil(L);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ g->deleted = true;
+
+#if 0
+ /* delete group if it's lua created. */
+ if (g->lua_created) {
+ lv_group_del(g->group);
+ g->group = NULL;
+ }
+#else
+ /* delete the group anyway */
+ lv_group_del(g->group);
+ g->group = NULL;
+#endif
+
+ debug("delete group:%p\n", g);
+ return 0;
+}
+
+/**
+ * group = luavgl.group.get_default()
+ */
+static int luavgl_group_get_default(lua_State *L)
+{
+ lv_group_t *group = lv_group_get_default();
+
+ /* check if already exists */
+ lua_pushlightuserdata(L, group);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ lua_pop(L, 1);
+ luavgl_group_get(L, group);
+ luavgl_check_group(L, -1)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_group_set_default(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ lv_group_set_default(g->group);
+ return 0;
+}
+
+static int luavgl_group_add_obj(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ lv_obj_t *obj = luavgl_to_obj(L, 2);
+
+ lv_group_add_obj(g->group, obj);
+ return 0;
+}
+
+static int luavgl_group_remove_objs(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ lv_group_remove_all_objs(g->group);
+ return 0;
+}
+
+static int luavgl_group_focus_next(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ lv_group_focus_next(g->group);
+ return 0;
+}
+
+static int luavgl_group_focus_prev(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ lv_group_focus_prev(g->group);
+ return 0;
+}
+
+static int luavgl_group_focus_freeze(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ bool en = luavgl_tointeger(L, 2);
+ lv_group_focus_freeze(g->group, en);
+ return 0;
+}
+
+static int luavgl_group_send_data(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ uint32_t c = luavgl_tointeger(L, 2);
+ lv_group_send_data(g->group, c);
+ return 0;
+}
+
+static int luavgl_group_set_focus_cb(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ (void)g;
+ return 0;
+}
+
+static int luavgl_group_set_edge_cb(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ (void)g;
+ return 0;
+}
+
+static int luavgl_group_get_focus_cb(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ (void)g;
+ return 0;
+}
+
+static int luavgl_group_get_edge_cb(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+
+ (void)g;
+ return 0;
+}
+
+static int luavgl_group_set_editing(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ bool en = luavgl_tointeger(L, 2);
+ lv_group_set_editing(g->group, en);
+ return 0;
+}
+
+static int luavgl_group_set_wrap(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ bool en = luavgl_tointeger(L, 2);
+ lv_group_set_wrap(g->group, en);
+ return 0;
+}
+
+static int luavgl_group_get_wrap(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ bool en = lv_group_get_wrap(g->group);
+ lua_pushboolean(L, en);
+ return 1;
+}
+
+static int luavgl_group_get_obj_count(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ uint32_t count = lv_group_get_obj_count(g->group);
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+static int luavgl_group_get_focused(lua_State *L)
+{
+ luavgl_group_t *g = luavgl_check_group(L, 1);
+ lv_obj_t *obj = lv_group_get_focused(g->group);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ } else {
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+ }
+
+ return 1;
+}
+
+static int luavgl_group_remove_obj(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_group_remove_obj(obj);
+ return 0;
+}
+
+static int luavgl_group_focus_obj(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_group_focus_obj(obj);
+ return 0;
+}
+
+static int luavgl_group_gc(lua_State *L)
+{
+ luavgl_group_t *g = luaL_checkudata(L, 1, "lv_group");
+ if (g->deleted) {
+ return 0;
+ }
+
+#if 0
+ /* delete group if it's lua created. */
+ if (g->lua_created) {
+ lv_group_del(g->group);
+ g->group = NULL;
+ }
+#else
+ /* delete the group anyway */
+ lv_group_del(g->group);
+ g->group = NULL;
+#endif
+
+ debug("gc group: %p\n", g);
+ return 0;
+}
+
+/**
+ * luavgl.group lib
+ */
+static const luaL_Reg group_lib[] = {
+ {"create", luavgl_group_create },
+ {"get_default", luavgl_group_get_default},
+ {"remove_obj", luavgl_group_remove_obj },
+ {"remove_objs", luavgl_group_remove_objs},
+
+ {"focus_obj", luavgl_group_focus_obj },
+ {NULL, NULL },
+};
+
+/*
+** methods for file handles
+*/
+static const luaL_Reg group_methods[] = {
+ {"delete", luavgl_group_delete },
+ {"set_default", luavgl_group_set_default },
+ {"add_obj", luavgl_group_add_obj },
+ {"remove_obj", luavgl_group_remove_obj },
+ {"focus_next", luavgl_group_focus_next },
+ {"focus_prev", luavgl_group_focus_prev },
+ {"focus_freeze", luavgl_group_focus_freeze },
+ {"send_data", luavgl_group_send_data },
+ {"set_focus_cb", luavgl_group_set_focus_cb },
+ {"set_edge_cb", luavgl_group_set_edge_cb },
+ {"get_focus_cb", luavgl_group_get_focus_cb },
+ {"get_edge_cb", luavgl_group_get_edge_cb },
+ {"set_editing", luavgl_group_set_editing },
+ {"set_wrap", luavgl_group_set_wrap },
+ {"get_wrap", luavgl_group_get_wrap },
+ {"get_obj_count", luavgl_group_get_obj_count},
+ {"get_focused", luavgl_group_get_focused },
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg group_meta[] = {
+ {"__gc", luavgl_group_gc},
+ {"__index", NULL }, /* place holder */
+
+ {NULL, NULL }
+};
+
+static void luavgl_group_init(lua_State *L)
+{
+ /* create lv_group metatable */
+ luaL_newmetatable(L, "lv_group");
+ luaL_setfuncs(L, group_meta, 0);
+
+ luaL_newlib(L, group_methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+
+ /* luavgl.indev lib */
+ luaL_newlib(L, group_lib);
+ lua_setfield(L, -2, "group");
+}
diff --git a/lib/luavgl/src/indev.c b/lib/luavgl/src/indev.c
new file mode 100644
index 00000000..a21e3ee8
--- /dev/null
+++ b/lib/luavgl/src/indev.c
@@ -0,0 +1,342 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct luavgl_indev_s {
+ lv_indev_t *indev;
+} luavgl_indev_t;
+
+/* group APIs */
+static lv_group_t *luavgl_to_group(lua_State *L, int idx);
+
+static luavgl_indev_t *luavgl_check_indev(lua_State *L, int idx)
+{
+ luavgl_indev_t *v = luaL_checkudata(L, idx, "lv_indev");
+ if (v->indev == NULL) {
+ luaL_error(L, "null indev");
+ }
+
+ return v;
+}
+
+/**
+ * create new luavgl indev userdata.
+ */
+static int luavgl_indev_get(lua_State *L, lv_indev_t *indev)
+{
+ if (indev == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* check if already exists */
+ lua_pushlightuserdata(L, indev);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ /* create new indev userdata and add to registry forever */
+ lua_pop(L, 1);
+ luavgl_indev_t *i = lua_newuserdata(L, sizeof(luavgl_indev_t));
+ i->indev = indev;
+
+ luaL_getmetatable(L, "lv_indev");
+ lua_setmetatable(L, -2);
+
+ lua_pushlightuserdata(L, indev);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+
+ return 1;
+}
+
+static int luavgl_indev_get_act(lua_State *L)
+{
+ lv_indev_t *indev = lv_indev_get_act();
+ return luavgl_indev_get(L, indev);
+}
+
+static int luavgl_indev_get_obj_act(lua_State *L)
+{
+ lv_obj_t *obj = lv_indev_get_obj_act();
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_indev_get_next(lua_State *L)
+{
+ lv_indev_t *indev = NULL;
+ if (!lua_isnoneornil(L, 1)) {
+ indev = luavgl_check_indev(L, 1)->indev;
+ }
+
+ indev = lv_indev_get_next(indev);
+ return luavgl_indev_get(L, indev);
+}
+
+static int luavgl_indev_get_type(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_indev_type_t t = lv_indev_get_type(i->indev);
+ lua_pushinteger(L, t);
+ return 1;
+}
+
+static int luavgl_indev_reset(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_obj_t *obj = luavgl_to_obj(L, 2);
+ lv_indev_reset(i->indev, obj);
+ return 0;
+}
+
+static int luavgl_indev_reset_long_press(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+
+ lv_indev_reset_long_press(i->indev);
+ return 1;
+}
+
+static int luavgl_indev_set_cursor(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_obj_t *obj = luavgl_to_obj(L, 2);
+ lv_indev_set_cursor(i->indev, obj);
+ return 0;
+}
+
+static int luavgl_indev_set_group(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_group_t *group = luavgl_to_group(L, 2);
+ lv_indev_set_group(i->indev, group);
+ return 0;
+}
+
+static int luavgl_indev_get_point(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_point_t point;
+ lv_indev_get_point(i->indev, &point);
+
+ lua_pushinteger(L, point.x);
+ lua_pushinteger(L, point.y);
+ return 2;
+}
+
+static int luavgl_indev_get_gesture_dir(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_dir_t dir = lv_indev_get_gesture_dir(i->indev);
+
+ lua_pushinteger(L, dir);
+ return 1;
+}
+
+static int luavgl_indev_get_key(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ uint32_t v = lv_indev_get_key(i->indev);
+
+ lua_pushinteger(L, v);
+ return 1;
+}
+
+static int luavgl_indev_get_scroll_dir(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_dir_t dir = lv_indev_get_scroll_dir(i->indev);
+
+ lua_pushinteger(L, dir);
+ return 1;
+}
+
+static int luavgl_indev_get_scroll_obj(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_obj_t *obj = lv_indev_get_scroll_obj(i->indev);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* registry[obj] */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_indev_get_vect(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_point_t point;
+ lv_indev_get_vect(i->indev, &point);
+
+ lua_pushinteger(L, point.x);
+ lua_pushinteger(L, point.y);
+ return 2;
+}
+
+static int luavgl_indev_wait_release(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lv_indev_wait_release(i->indev);
+ return 0;
+}
+
+static void indev_feedback_cb(lv_indev_drv_t *driver, uint8_t code)
+{
+ lua_State *L = driver->user_data;
+ int top = lua_gettop(L);
+ lua_pushlightuserdata(L, driver);
+ lua_rawget(L, LUA_REGISTRYINDEX); /* get indev on stack */
+
+ lua_getuservalue(L, -1); /* stack: indev, uservalue-table */
+ lua_rawgeti(L, -1, code); /* indev, uservalue, cb */
+ if (lua_isnoneornil(L, -1)) {
+ lua_settop(L, top);
+ return;
+ }
+
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, code);
+ luavgl_pcall_int(L, 2, 0);
+ lua_settop(L, top);
+}
+
+/**
+ * indev:on_event(code, cb)
+ */
+static int luavgl_indev_on_event(lua_State *L)
+{
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ int code = lua_tointeger(L, 2);
+ luavgl_check_callable(L, 3);
+
+ lua_pushlightuserdata(L, i->indev->driver);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ lua_pop(L, 1);
+
+ /* set indev to registry using pointer as key */
+ lua_pushlightuserdata(L, i->indev->driver);
+ lua_pushvalue(L, 1);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+
+ int type = lua_getuservalue(L, 1);
+ if (type != LUA_TTABLE) {
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 1);
+ lua_pushvalue(L, -1);
+ lua_setuservalue(L, 1);
+ }
+
+ debug("add feedback_cb code %d, for indev->driver: %p\n", code,
+ i->indev->driver);
+ lua_pushvalue(L, 3);
+ lua_rawseti(L, -2, code);
+
+ i->indev->driver->feedback_cb = indev_feedback_cb;
+ i->indev->driver->user_data = L;
+ lua_settop(L, 1);
+ return 1;
+}
+
+static int luavgl_indev_tostring(lua_State *L)
+{
+ lua_pushfstring(L, "lv_indev: %p\n", luavgl_check_indev(L, 1));
+ return 1;
+}
+
+static int luavgl_indev_gc(lua_State *L)
+{
+ debug("\n");
+
+ /* If set_feedback_cb is used, then the indev only gets gc'ed when lua vm
+ * exits.
+ */
+ luavgl_indev_t *i = luavgl_check_indev(L, 1);
+ lua_getuservalue(L, 1);
+ if (!lua_isnoneornil(L, -1)) {
+ lua_pushlightuserdata(L, i->indev->driver);
+ lua_pushnil(L);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ i->indev->driver->feedback_cb = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * luavgl.indev lib
+ */
+static const luaL_Reg indev_lib[] = {
+ {"get_act", luavgl_indev_get_act },
+ {"get_obj_act", luavgl_indev_get_obj_act},
+ {"get_next", luavgl_indev_get_next },
+
+ {NULL, NULL },
+};
+
+/*
+** methods for file handles
+*/
+static const luaL_Reg methods[] = {
+ {"get_type", luavgl_indev_get_type },
+ {"reset", luavgl_indev_reset },
+ {"reset_long_press", luavgl_indev_reset_long_press},
+ {"set_cursor", luavgl_indev_set_cursor },
+ {"set_group", luavgl_indev_set_group },
+ {"get_point", luavgl_indev_get_point },
+ {"get_gesture_dir", luavgl_indev_get_gesture_dir },
+ {"get_key", luavgl_indev_get_key },
+ {"get_scroll_dir", luavgl_indev_get_scroll_dir },
+ {"get_scroll_obj", luavgl_indev_get_scroll_obj },
+ {"get_vect", luavgl_indev_get_vect },
+ {"wait_release", luavgl_indev_wait_release },
+ {"on_event", luavgl_indev_on_event },
+
+ {NULL, NULL },
+};
+
+static const luaL_Reg meta[] = {
+ {"__gc", luavgl_indev_gc },
+ {"__tostring", luavgl_indev_tostring},
+ {"__index", NULL }, /* place holder */
+
+ {NULL, NULL }
+};
+
+static void luavgl_indev_init(lua_State *L)
+{
+ /* create lv_fs metatable */
+ luaL_newmetatable(L, "lv_indev");
+ luaL_setfuncs(L, meta, 0);
+
+ luaL_newlib(L, methods);
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop metatable */
+
+ /* luavgl.indev lib */
+ luaL_newlib(L, indev_lib);
+ lua_setfield(L, -2, "indev");
+}
diff --git a/lib/luavgl/src/luavgl.c b/lib/luavgl/src/luavgl.c
new file mode 100644
index 00000000..86c1a108
--- /dev/null
+++ b/lib/luavgl/src/luavgl.c
@@ -0,0 +1,150 @@
+#include "luavgl.h"
+#include "private.h"
+
+#include "anim.c"
+#include "constants.c"
+#include "disp.c"
+#include "font.c"
+#include "fs.c"
+#include "group.c"
+#include "indev.c"
+#include "obj.c"
+#include "timer.c"
+#include "util.c"
+
+static const struct luaL_Reg luavgl_methods[] = {
+ {"Timer", luavgl_timer_create}, /* timer.c */
+ {"Font", luavgl_font_create }, /* font.c */
+ {"Style", luavgl_style_create}, /* style.c */
+ {"Anim", luavgl_anim_create }, /* anim.c */
+
+ {NULL, NULL },
+};
+
+LUALIB_API luavgl_ctx_t *luavgl_context(lua_State *L)
+{
+ static const char *luavglgl_key = "luavgl_key";
+
+ luavgl_ctx_t *ctx;
+ lua_pushstring(L, luavglgl_key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /**
+ * ctx = registry[luavgl_key]
+ * if ctx == nil then
+ * ctx = new ctx
+ * else
+ * return ctx
+ * end
+ */
+
+ if (lua_isnil(L, -1)) {
+ /* create it if not exist in registry */
+ lua_pushstring(L, luavglgl_key);
+ ctx = (luavgl_ctx_t *)lua_newuserdata(L, sizeof(*ctx));
+ memset(ctx, 0, sizeof(*ctx));
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ } else {
+ ctx = (luavgl_ctx_t *)lua_touserdata(L, -1);
+ }
+
+ lua_pop(L, 1);
+ return ctx;
+}
+
+LUALIB_API void luavgl_set_pcall(lua_State *L, luavgl_pcall_t pcall)
+{
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ ctx->pcall = pcall;
+}
+
+LUALIB_API void luavgl_set_root(lua_State *L, lv_obj_t *root)
+{
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ ctx->root = root;
+}
+
+LUALIB_API void luavgl_set_font_extension(lua_State *L, make_font_cb make,
+ delete_font_cb d)
+{
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ ctx->make_font = make;
+ ctx->delete_font = d;
+}
+
+static int root_gc(lua_State *L)
+{
+ debug("enter.\n");
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ lv_obj_del(ctx->root);
+ return 0;
+}
+
+static int root_clean(lua_State *L)
+{
+ debug("enter.\n");
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ lv_obj_clean(ctx->root);
+ return 0;
+}
+
+LUALIB_API int luaopen_lvgl(lua_State *L)
+{
+ luavgl_ctx_t *ctx = luavgl_context(L);
+
+ luaL_newlib(L, luavgl_methods);
+
+ luaL_newmetatable(L, "root.meta");
+ lua_pushstring(L, "__gc");
+ lua_pushcfunction(L, ctx->root ? root_clean : root_gc);
+ lua_settable(L, -3);
+ lua_pop(L, 1);
+
+ lv_obj_t *root = ctx->root;
+ if (root == NULL) {
+ debug("create root obj for lua.\n");
+ root = lv_obj_create(lv_scr_act());
+ ctx->root = root;
+ }
+
+ if (ctx->pcall == NULL)
+ ctx->pcall = luavgl_pcall;
+
+ lua_pushstring(L, "_root");
+ *(void **)lua_newuserdata(L, sizeof(void *)) = root;
+ luaL_getmetatable(L, "root.meta");
+ lua_setmetatable(L, -2);
+
+ /*
+ * create a ref to root, avoid __gc early
+ * this puts the root userdata into the _root key
+ * in the returned luv table
+ * */
+ lua_rawset(L, -3);
+
+ lv_obj_remove_style_all(root);
+ lv_obj_set_size(root, LV_HOR_RES, LV_VER_RES);
+ lv_obj_clear_flag(root, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE);
+
+ luavgl_obj_init(L);
+ luavgl_widgets_init(L);
+ luavgl_anim_init(L);
+ luavgl_timer_init(L);
+ luavgl_style_init(L);
+ luavgl_fs_init(L);
+ luavgl_indev_init(L);
+ luavgl_group_init(L);
+ luavgl_disp_init(L);
+
+ luavgl_constants_init(L);
+
+ /* Methods to create widget locate in widgets table, check `luavgl_obj_init`
+ */
+ luaL_getmetatable(L, "widgets");
+ lua_setmetatable(L, -2);
+
+ (void)dumpstack;
+ (void)dumptable;
+ (void)dumpvalue;
+ return 1;
+}
diff --git a/lib/luavgl/src/luavgl.h b/lib/luavgl/src/luavgl.h
new file mode 100644
index 00000000..37e7134b
--- /dev/null
+++ b/lib/luavgl/src/luavgl.h
@@ -0,0 +1,263 @@
+#pragma once
+
+#include <lua.h>
+#if (LUA_VERSION_NUM < 503)
+#include "compat-5.3.h"
+#endif
+
+#include <lauxlib.h>
+#include <lvgl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef const lv_font_t *(*make_font_cb)(const char *name, int size,
+ int weight);
+typedef void (*delete_font_cb)(const lv_font_t *);
+typedef int (*luavgl_pcall_t)(lua_State *L, int nargs, int nresults);
+
+typedef struct {
+ lv_obj_t *root;
+ make_font_cb make_font;
+ delete_font_cb delete_font;
+ luavgl_pcall_t pcall;
+} luavgl_ctx_t;
+
+typedef enum {
+ /* type can be regarded as int */
+ SETTER_TYPE_INT = 0,
+ SETTER_TYPE_COLOR,
+
+ SETTER_TYPE_STACK, /* parameter is on stack -1 */
+ SETTER_TYPE_POINTER, /* parameter from lightuserdata, a pointer */
+ SETTER_TYPE_IMGSRC,
+} setter_type_t;
+
+typedef void (*setter_int_t)(void *, int);
+typedef void (*setter_pointer_t)(void *, void *);
+
+typedef struct {
+ const char *key;
+ setter_type_t type;
+
+ union {
+ void (*setter)(void *, int);
+ void (*setter_stack)(void *, lua_State *L);
+ void (*setter_pointer)(void *, void *);
+ };
+} luavgl_value_setter_t;
+
+typedef struct luavgl_obj_s {
+ lv_obj_t *obj; /* NULL means obj deleted, but not gc'ed in lua */
+ bool lua_created; /* this object is created from lua */
+
+ /* internally used variables */
+ int n_events;
+ struct event_callback_s *events;
+} luavgl_obj_t;
+
+#define luavgl_obj_newmetatable(L, clz, name, l) \
+ luavgl_obj_createmetatable(L, clz, name, l, sizeof(l) / sizeof((l)[0]) - 1);
+
+#define luavgl_set_property(L, obj, table) \
+ luavgl_set_property_array(L, obj, table, sizeof(table) / sizeof(table[0]))
+
+LUALIB_API luavgl_ctx_t *luavgl_context(lua_State *L);
+
+/**
+ * @brief set the protected call used in event callback.
+ * this must be called before luaopen_lvgl
+ */
+LUALIB_API void luavgl_set_pcall(lua_State *L, luavgl_pcall_t pcall);
+
+/**
+ * @brief Set luavgl root object
+ */
+LUALIB_API void luavgl_set_root(lua_State *L, lv_obj_t *root);
+
+/**
+ * @brief Set functions to make and destroy extended fonts.
+ */
+LUALIB_API void luavgl_set_font_extension(lua_State *L, make_font_cb make,
+ delete_font_cb d);
+
+/* on embedded device, may call lib open manually. */
+LUALIB_API int luaopen_lvgl(lua_State *L);
+
+/* object creation */
+
+/**
+ * @brief Helper function to create lua lvgl object.
+ *
+ * Expected stack layout:
+ * [1]: parent object
+ * [2]: table of properties(styles)
+ * Return value in stack:
+ * [1]: userdata of created object, luavgl object.
+ *
+ * @param L
+ * @param create function pointer to create object
+ * @return items in stack for return
+ */
+LUALIB_API int luavgl_obj_create_helper(lua_State *L,
+ lv_obj_t *(*create)(lv_obj_t *parent));
+
+/**
+ * @brief Add existing lvgl object to lua
+ *
+ * Return value in stack:
+ * [1]: luavgl object.
+ *
+ * @return pointer to luavgl object
+ */
+LUALIB_API luavgl_obj_t *luavgl_add_lobj(lua_State *L, lv_obj_t *obj);
+
+/**
+ * @brief Get lua lvgl object on stack
+ *
+ * Return value in stack:
+ * [1]: luavgl object.
+ *
+ * @return pointer to luavgl object
+ */
+LUALIB_API luavgl_obj_t *luavgl_to_lobj(lua_State *L, int idx);
+
+/**
+ * @brief Create metatable for specified object class
+ *
+ * @param L
+ * @param clz object class
+ * @param name optional name for this metatable
+ * @param l methods for this metatable
+ * @param n methods length
+ *
+ * Return value in stack:
+ * [1]: metatable
+ *
+ * @return 1
+ */
+LUALIB_API int luavgl_obj_createmetatable(lua_State *L,
+ const lv_obj_class_t *clz,
+ const char *name, const luaL_Reg *l,
+ int n);
+
+/**
+ * @brief Get user value of userdata of lua lvgl object, create if not exists
+ *
+ * @return type of the uservalue, LUA_TTABLE
+ */
+LUALIB_API int luavgl_obj_getuserdatauv(lua_State *L, int idx);
+
+
+/* helper to get value from stack */
+
+/**
+ * @brief Get lvgl object from stack
+ */
+LUALIB_API lv_obj_t *luavgl_to_obj(lua_State *L, int idx);
+
+/**
+ * @brief Convert value to integer
+ *
+ * Supported values are: integer, float, boolean
+ */
+LUALIB_API int luavgl_tointeger(lua_State *L, int idx);
+
+/**
+ * @brief Convert value to lvgl color
+ *
+ * Supported color formats are:
+ * [1]: string representation: "#RGB" or "#RRGGBB"
+ * [2]: integer value: 0xaabbcc
+ */
+LUALIB_API lv_color_t luavgl_tocolor(lua_State *L, int idx);
+
+/**
+ * @brief Convert value to lvgl image source
+ *
+ * Supported image format:
+ * [1]: image path string, e.g. "/resource/bg.png"
+ * [2]: image source lightuserdata
+ */
+LUALIB_API const char *luavgl_toimgsrc(lua_State *L, int idx);
+
+/**
+ * @brief Helper function to iterate through table
+ *
+ * @param L
+ * @param index index to the table for iteration
+ * @param cb callback function for every key,value pair
+ * Stack layout when cb function made:
+ * [-1]: value
+ * [-2]: key
+ * Care must be taken when calling lua_tostring etc. functions which will
+ * change stack layout.
+ * @param cb_param callback parameter passed to cb function
+ */
+LUALIB_API void luavgl_iterate(lua_State *L, int index,
+ int (*cb)(lua_State *, void *), void *cb_para);
+
+/**
+ * @brief Set object property with provided setter functions
+ *
+ * @param L
+ * @param obj lvgl object
+ * @param table function array
+ * @param len functions in array
+ * @return 1
+ */
+LUALIB_API int luavgl_set_property_array(lua_State *L, void *obj,
+ const luavgl_value_setter_t table[],
+ uint32_t len);
+
+/**
+ * @brief Dummy property set function
+ */
+LUALIB_API void _lv_dummy_set(void *obj, lua_State *L);
+
+/**
+ * @brief Set object property with key,value in stack
+ */
+LUALIB_API int luavgl_obj_set_property_kv(lua_State *L, void *data);
+
+/**
+ * @brief Set image property with key,value in stack
+ */
+LUALIB_API int luavgl_img_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_calendar_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_checkbox_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_dropdown_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_keyboard_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_label_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_led_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_list_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_roller_set_property_kv(lua_State *L, void *data);
+
+LUALIB_API int luavgl_textarea_set_property_kv(lua_State *L, void *data);
+
+
+
+/**
+ * @brief Protected call
+ * @return negative error code or zero.
+ */
+LUALIB_API int luavgl_pcall(lua_State *L, int nargs, int nresult);
+
+/**
+ * @brief Check if lua object is callable.
+ * @return true if callable, false otherwise.
+ */
+LUALIB_API int luavgl_is_callable(lua_State *L, int index);
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
diff --git a/lib/luavgl/src/lvgl.lua b/lib/luavgl/src/lvgl.lua
new file mode 100644
index 00000000..db1ee349
--- /dev/null
+++ b/lib/luavgl/src/lvgl.lua
@@ -0,0 +1,1465 @@
+---@meta
+---
+--- lvgl comments
+---
+
+lvgl = {}
+
+--- constans table. Note that value listed here is only for linter.
+
+--- @enum ObjEventCode
+lvgl.EVENT = {
+ ALL = 0,
+ PRESSED = 0,
+ PRESSING = 0,
+ PRESS_LOST = 0,
+ SHORT_CLICKED = 0,
+ LONG_PRESSED = 0,
+ LONG_PRESSED_REPEAT = 0,
+ CLICKED = 0,
+ RELEASED = 0,
+ SCROLL_BEGIN = 0,
+ SCROLL_END = 0,
+ SCROLL = 0,
+ GESTURE = 0,
+ KEY = 0,
+ FOCUSED = 0,
+ DEFOCUSED = 0,
+ LEAVE = 0,
+ HIT_TEST = 0,
+ COVER_CHECK = 0,
+ REFR_EXT_DRAW_SIZE = 0,
+ DRAW_MAIN_BEGIN = 0,
+ DRAW_MAIN = 0,
+ DRAW_MAIN_END = 0,
+ DRAW_POST_BEGIN = 0,
+ DRAW_POST = 0,
+ DRAW_POST_END = 0,
+ DRAW_PART_BEGIN = 0,
+ DRAW_PART_END = 0,
+ VALUE_CHANGED = 0,
+ INSERT = 0,
+ REFRESH = 0,
+ READY = 0,
+ CANCEL = 0,
+ DELETE = 0,
+ CHILD_CHANGED = 0,
+ CHILD_CREATED = 0,
+ CHILD_DELETED = 0,
+ SCREEN_UNLOAD_START = 0,
+ SCREEN_LOAD_START = 0,
+ SCREEN_LOADED = 0,
+ SCREEN_UNLOADED = 0,
+ SIZE_CHANGED = 0,
+ STYLE_CHANGED = 0,
+ LAYOUT_CHANGED = 0,
+ GET_SELF_SIZE = 0,
+}
+
+--- object flag for obj:add_flag obj:clear_flag
+--- @enum ObjFlag
+lvgl.FLAG = {
+ PRESSED = 0,
+ HIDDEN = 0,
+ CLICKABLE = 0,
+ CLICK_FOCUSABLE = 0,
+ CHECKABLE = 0,
+ SCROLLABLE = 0,
+ SCROLL_ELASTIC = 0,
+ SCROLL_MOMENTUM = 0,
+ SCROLL_ONE = 0,
+ SCROLL_CHAIN_HOR = 0,
+ SCROLL_CHAIN_VER = 0,
+ SCROLL_CHAIN = 0,
+ SCROLL_ON_FOCUS = 0,
+ SCROLL_WITH_ARROW = 0,
+ SNAPPABLE = 0,
+ PRESS_LOCK = 0,
+ EVENT_BUBBLE = 0,
+ GESTURE_BUBBLE = 0,
+ ADV_HITTEST = 0,
+ IGNORE_LAYOUT = 0,
+ FLOATING = 0,
+ OVERFLOW_VISIBLE = 0,
+ LAYOUT_1 = 0,
+ LAYOUT_2 = 0,
+ WIDGET_1 = 0,
+ WIDGET_2 = 0,
+ USER_1 = 0,
+ USER_2 = 0,
+ USER_3 = 0,
+ USER_4 = 0,
+}
+
+--- @enum ObjState
+lvgl.STATE = {
+ DEFAULT = 0,
+ CHECKED = 0,
+ FOCUSED = 0,
+ FOCUS_KEY = 0,
+ EDITED = 0,
+ HOVERED = 0,
+ PRESSED = 0,
+ SCROLLED = 0,
+ DISABLED = 0,
+ USER_1 = 0,
+ USER_2 = 0,
+ USER_3 = 0,
+ USER_4 = 0,
+ ANY = 0,
+}
+
+--- @enum ObjAlignType
+lvgl.ALIGN = {
+ DEFAULT = 0,
+ TOP_LEFT = 0,
+ TOP_MID = 0,
+ TOP_RIGHT = 0,
+ BOTTOM_LEFT = 0,
+ BOTTOM_MID = 0,
+ BOTTOM_RIGHT = 0,
+ LEFT_MID = 0,
+ RIGHT_MID = 0,
+ CENTER = 0,
+ OUT_TOP_LEFT = 0,
+ OUT_TOP_MID = 0,
+ OUT_TOP_RIGHT = 0,
+ OUT_BOTTOM_LEFT = 0,
+ OUT_BOTTOM_MID = 0,
+ OUT_BOTTOM_RIGHT = 0,
+ OUT_LEFT_TOP = 0,
+ OUT_LEFT_MID = 0,
+ OUT_LEFT_BOTTOM = 0,
+ OUT_RIGHT_TOP = 0,
+ OUT_RIGHT_MID = 0,
+ OUT_RIGHT_BOTTOM = 0,
+}
+
+--- @enum BuiltinFont
+lvgl.BUILTIN_FONT = {
+ MONTSERRAT_8 = 0,
+ MONTSERRAT_10 = 0,
+ MONTSERRAT_12 = 0,
+ MONTSERRAT_14 = 0,
+ MONTSERRAT_16 = 0,
+ MONTSERRAT_18 = 0,
+ MONTSERRAT_20 = 0,
+ MONTSERRAT_22 = 0,
+ MONTSERRAT_24 = 0,
+ MONTSERRAT_26 = 0,
+ MONTSERRAT_28 = 0,
+ MONTSERRAT_30 = 0,
+ MONTSERRAT_32 = 0,
+ MONTSERRAT_34 = 0,
+ MONTSERRAT_36 = 0,
+ MONTSERRAT_38 = 0,
+ MONTSERRAT_40 = 0,
+ MONTSERRAT_42 = 0,
+ MONTSERRAT_44 = 0,
+ MONTSERRAT_46 = 0,
+ MONTSERRAT_48 = 0,
+ MONTSERRAT_12_SUBPX = 0,
+ MONTSERRAT_28_COMPRESSED = 0,
+ DEJAVU_16_PERSIAN_HEBREW = 0,
+ SIMSUN_16_CJK = 0,
+ UNSCII_8 = 0,
+ UNSCII_16 = 0,
+}
+
+--- @enum LABEL
+lvgl.LABEL = {
+ LONG_WRAP = 0,
+ LONG_DOT = 0,
+ LONG_SCROLL = 0,
+ LONG_SCROLL_CIRCULAR = 0,
+ LONG_CLIP = 0,
+}
+
+--- @enum SCR_LOAD_ANIM
+lvgl.SCR_LOAD_ANIM = {
+ NONE = 0,
+ OVER_LEFT = 0,
+ OVER_RIGHT = 0,
+ OVER_TOP = 0,
+ OVER_BOTTOM = 0,
+ MOVE_LEFT = 0,
+ MOVE_RIGHT = 0,
+ MOVE_TOP = 0,
+ MOVE_BOTTOM = 0,
+ FADE_IN = 0,
+ FADE_ON = 0,
+ FADE_OUT = 0,
+ OUT_LEFT = 0,
+ OUT_RIGHT = 0,
+ OUT_TOP = 0,
+ OUT_BOTTOM = 0,
+}
+
+--- @enum ScrollbarMode
+lvgl.SCROLLBAR_MODE = {
+ OFF = 0,
+ ON = 0,
+ ACTIVE = 0,
+ AUTO = 0,
+}
+
+--- @enum Dir
+lvgl.DIR = {
+ NONE = 0,
+ LEFT = 0,
+ RIGHT = 0,
+ TOP = 0,
+ BOTTOM = 0,
+ HOR = 0,
+ VER = 0,
+ ALL = 0,
+}
+
+--- @enum KeyboardMode
+lvgl.KEYBOARD_MODE = {
+ TEXT_LOWER = 0,
+ TEXT_UPPER = 0,
+ SPECIAL = 0,
+ NUMBER = 0,
+ USER_1 = 0,
+ USER_2 = 0,
+ USER_3 = 0,
+ USER_4 = 0,
+ TEXT_ARABIC = 0,
+}
+
+--- @enum FlexFlow
+lvgl.FLEX_FLOW = {
+ ROW = 0,
+ COLUMN = 0,
+ ROW_WRAP = 0,
+ ROW_REVERSE = 0,
+ ROW_WRAP_REVERSE = 0,
+ COLUMN_WRAP = 0,
+ COLUMN_REVERSE = 0,
+ COLUMN_WRAP_REVERSE = 0,
+}
+
+--- @enum FlexAlign
+lvgl.FLEX_ALIGN = {
+ START = 0,
+ END = 0,
+ CENTER = 0,
+ SPACE_EVENLY = 0,
+ SPACE_AROUND = 0,
+ SPACE_BETWEEN = 0,
+}
+
+--- @enum GridAlign
+lvgl.GRID_ALIGN = {
+ START = 0,
+ CENTER = 0,
+ END = 0,
+ STRETCH = 0,
+ SPACE_EVENLY = 0,
+ SPACE_AROUND = 0,
+ SPACE_BETWEEN = 0,
+}
+
+--- @enum RollerMode
+lvgl.ROLLER_MODE = {
+ NORMAL = 0,
+ INFINITE = 0,
+}
+
+--- @enum KEY
+lvgl.KEY = {
+ UP = 0,
+ DOWN = 0,
+ RIGHT = 0,
+ LEFT = 0,
+ ESC = 0,
+ DEL = 0,
+ BACKSPACE = 0,
+ ENTER = 0,
+ NEXT = 0,
+ PREV = 0,
+ HOME = 0,
+ END = 0,
+}
+
+lvgl.ANIM_REPEAT_INFINITE = 0
+lvgl.ANIM_PLAYTIME_INFINITE = 0
+lvgl.SIZE_CONTENT = 0
+lvgl.RADIUS_CIRCLE = 0
+lvgl.COORD_MAX = 0
+lvgl.COORD_MIN = 0
+lvgl.IMG_ZOOM_NONE = 0
+lvgl.BTNMATRIX_BTN_NONE = 0
+lvgl.CHART_POINT_NONE = 0
+lvgl.DROPDOWN_POS_LAST = 0
+lvgl.LABEL_DOT_NUM = 0
+lvgl.LABEL_POS_LAST = 0
+lvgl.LABEL_TEXT_SELECTION_OFF = 0
+lvgl.TABLE_CELL_NONE = 0
+lvgl.TEXTAREA_CURSOR_LAST = 0
+lvgl.LAYOUT_FLEX = 0
+lvgl.LAYOUT_GRID = 0
+
+--- return a opacity value in lvgl unit(0..255)
+---@param p integer opacity value in range of 0..100
+---@return integer
+function lvgl.OPA(p)
+end
+
+--- return a layout in percent
+---@param p integer
+---@return integer
+function lvgl.PCT(p)
+end
+
+--- return lvgl horizontal resolution
+---@return integer
+function lvgl.HOR_RES()
+end
+
+--- return lvgl vertical resolution
+---@return integer
+function lvgl.VER_RES()
+end
+
+---
+--[[
+ ### Create basic object
+ - when parent is nil, object is created on lvgl root.
+ - property can be used to set any object style, like using `lv_obj_set_style`
+]]
+--- @param parent? Object | nil
+--- @param property? StyleProp
+--- @return Object
+function lvgl.Object(parent, property)
+end
+
+--- Create Calendar widget on parent
+--- @param parent? Object | nil
+--- @param property? StyleProp
+--- @return Calendar
+function lvgl.Calendar(parent, property)
+end
+
+
+---
+--- Create Label on parent
+--- @param parent? Object | nil
+--- @param property? CheckboxStyle
+--- @return Checkbox
+function lvgl.Checkbox(parent, property)
+end
+
+---
+--- Create Dropdown on parent
+--- @param parent? Object | nil
+--- @param property? DropdownStyle
+--- @return Dropdown
+function lvgl.Dropdown(parent, property)
+end
+
+---
+--- Create Image on parent
+--- @param parent? Object | nil
+--- @param property? ImageStyle
+--- @return Image
+function lvgl.Image(parent, property)
+end
+
+---
+--- Create Label on parent
+--- @param parent? Object | nil
+--- @param property? LabelStyle
+--- @return Label
+function lvgl.Label(parent, property)
+end
+
+---
+--- Create Textarea Widget on parent
+--- @param parent? Object | nil
+--- @param property? TextareaStyle
+--- @return Textarea
+function lvgl.Textarea(parent, property)
+end
+
+---
+--- Create Keyboard Widget on parent
+--- @param parent? Object | nil
+--- @param property? KeyboardStyle
+--- @return Keyboard
+function lvgl.Keyboard(parent, property)
+end
+
+
+---
+--- Create Led Widget on parent
+--- @param parent? Object | nil
+--- @param property? LedStyle
+--- @return Led
+function lvgl.Led(parent, property)
+end
+
+---
+--- Create List Widget on parent
+--- @param parent? Object | nil
+--- @param property? LabelStyle
+--- @return List
+function lvgl.List(parent, property)
+end
+
+---
+--- Create Roller Widget on parent
+--- @param parent? Object | nil
+--- @param property? RollerStyle
+--- @return Roller
+function lvgl.Roller(parent, property)
+end
+
+---
+--- Create Timer
+--- @param p TimerPara
+--- @return Timer
+function lvgl.Timer(p)
+end
+
+--[[
+Return a font which is lightuserdata, to be used for label etc.
+]]
+--- @param family string | "montserrat" | "unscii" | "Your Extension Font Name"
+--- @param size integer the font size in px
+--- @param weight @see built-in font only support "normal" weight
+--- | integer # default to normal, weight 400
+--- | nil # default to normal, weight 400
+--- | "thin" # weight 100
+--- | "extra light" # weight 200
+--- | "light" # weight 300
+--- | "normal" # weight 400
+--- | "medium" # weight 500
+--- | "semi bold" # weight 600
+--- | "bold" # weight 700
+--- | "extra bold" # weight 800
+--- | "ultra bold" # weight 900
+--- @return Font
+---
+--- [View Wiki](https://github.com/sumneko/lua-language-server/wiki/Annotations#class)
+function lvgl.Font(family, size, weight)
+end
+
+---
+--- Create style
+--- @param p? StyleProp
+--- @return Style
+function lvgl.Style(p)
+end
+
+---
+--- Basic lvgl object
+--- @class Object
+obj = {}
+
+---
+--- Create object on object
+--- @param property? StyleProp
+--- @return Object
+function obj:Object(property)
+end
+
+---
+--- Create calendar on object
+--- @param property? CalendarStyle
+--- @return Calendar
+function obj:Calendar(property)
+end
+
+---
+--- Create checkbox on object
+--- @param property? CheckboxStyle
+--- @return Checkbox
+function obj:Checkbox(property)
+end
+
+---
+--- Create Dropdown on parent
+--- @param property? DropdownStyle
+--- @return Dropdown
+function obj:Dropdown(parent, property)
+end
+
+---
+--- Create image on object
+--- @param property? ImageStyle
+--- @return Image
+function obj:Image(property)
+end
+
+---
+--- Create image on object
+--- @param property? LabelStyle
+--- @return Label
+function obj:Label(property)
+end
+
+---
+--- Create Textarea Widget on parent
+--- @param property? TextareaStyle
+--- @return Textarea
+function obj:Textarea(property)
+end
+
+---
+--- Create Keyboard Widget on parent
+--- @param property? KeyboardStyle
+--- @return Keyboard
+function obj:Keyboard(property)
+end
+
+---
+--- Create Led Widget on parent
+--- @param property? LedStyle
+--- @return Led
+function obj:Led(property)
+end
+
+---
+--- Create List on object
+--- @param property? ListStyle
+--- @return List
+function obj:List(property)
+end
+
+
+---
+--- Create Roller Widget on parent
+--- @param property? RollerStyle
+--- @return Roller
+function obj:Roller(parent, property)
+end
+---
+--- Set object property
+--- @param p StyleProp
+---
+function obj:set(p)
+end
+
+---
+--- Set object property
+--- @param p StyleProp
+--- @param state ObjState
+---
+function obj:set_style(p, state)
+end
+
+---
+--- Set object property
+--- @param p AlignToPara
+---
+function obj:align_to(p)
+end
+
+---
+--- Delete obj
+--- @return nil
+function obj:delete()
+end
+
+---
+--- Delete all children of this object
+--- @return nil
+function obj:clean()
+end
+
+---
+--- Set parent, note parent should also created by lua, native object may not work.
+--- @param p Object
+--- @return nil
+function obj:set_parent(p)
+end
+
+---
+--- get screen object where this one created on(non-native object)
+--- @return Object
+function obj:get_screen(p)
+end
+
+---
+--- get parent object
+--- @return Object
+function obj:get_parent()
+end
+
+---
+--- set and/or get object's parent
+--- @param p Object
+--- @return Object
+function obj:parent(p)
+end
+
+---
+--- get child object
+--- @param id integer 0 the first child, -1 the lastly created child
+--- @return Object
+function obj:get_child(id)
+end
+
+---
+--- get object children count
+--- @return integer
+function obj:get_child_cnt()
+end
+
+---
+--- get the state of this object
+--- @return ObjState
+function obj:get_state(p)
+end
+
+---
+--- Scroll to a given coordinate on an object.
+--- @class ScrollToPara
+--- @field x integer position x
+--- @field y integer
+--- @field anim boolean
+---
+--- @param p ScrollToPara
+--- @return ObjState
+function obj:scroll_to(p)
+end
+
+---
+--- Tell whether an object is being scrolled or not at this moment
+--- @return boolean
+function obj:is_scrolling()
+end
+
+---
+--- Tell whether an object is visible (even partially) now or not
+--- @return boolean
+function obj:is_visible()
+end
+
+---
+--- add flag to object
+--- @param p ObjFlag
+--- @return nil
+function obj:add_flag(p)
+end
+
+---
+--- clear object flag
+--- @param p ObjFlag
+--- @return nil
+function obj:clear_flag(p)
+end
+
+---
+--- add state to object
+--- @param p ObjState
+--- @return nil
+function obj:add_state(p)
+end
+
+---
+--- clear object state
+--- @param p ObjState
+--- @return nil
+function obj:clear_state(p)
+end
+
+---
+--- add style to object
+--- @param s Style
+--- @param selector? integer
+--- @return nil
+function obj:add_style(s, selector)
+end
+
+---
+--- remove style from object
+--- @param s Style
+--- @param selector? integer
+--- @return nil
+function obj:remove_style(s, selector)
+end
+
+---
+--- remove all style from object
+--- @return nil
+function obj:remove_style_all()
+end
+
+---scroll obj by x,y
+---@param x integer
+---@param y integer
+---@param anim_en? boolean
+function obj:scroll_by(x, y, anim_en)
+end
+
+---scroll obj by x,y
+---@param x integer
+---@param y integer
+---@param anim_en boolean
+function obj:scroll_by_bounded(x, y, anim_en)
+end
+
+--- Scroll to an object until it becomes visible on its parent
+---@param anim_en? boolean
+function obj:scroll_to_view(anim_en)
+end
+
+--- Scroll to an object until it becomes visible on its parent
+--- Do the same on the parent's parent, and so on.
+--- Therefore the object will be scrolled into view even it has nested scrollable parents
+---@param anim_en? boolean
+function obj:scroll_to_view_recursive(anim_en)
+end
+
+---scroll obj by x,y, low level APIs
+---@param x integer
+---@param y integer
+---@param anim_en boolean
+function obj:scroll_by_raw(x, y, anim_en)
+end
+
+---Invalidate the area of the scrollbars
+function obj:scrollbar_invalidate()
+end
+
+---Checked if the content is scrolled "in" and adjusts it to a normal position.
+---@param anim_en boolean
+function obj:readjust_scroll(anim_en)
+end
+
+---If object is editable
+---@return boolean
+function obj:is_editable()
+end
+
+--- class group def
+---@return boolean
+function obj:is_group_def()
+end
+
+--- Test whether the and object is positioned by a layout or not
+---@return boolean
+function obj:is_layout_positioned()
+end
+
+--- Mark the object for layout update.
+---@return nil
+function obj:mark_layout_as_dirty()
+end
+
+--- Align an object to the center on its parent. same as obj:set{align={type = lvgl.ALIGN.CENTER}}
+---@return nil
+function obj:center()
+end
+
+--- Align an object to the center on its parent. same as obj:set{align={type = lvgl.ALIGN.CENTER}}
+---@return nil
+function obj:invalidate()
+end
+
+---
+--- Object event callback. `para` is not used for now.
+--- @alias EventCallback fun(obj:Object, code: ObjEventCode): nil
+---
+--- set object event callback
+--- @param code ObjEventCode
+--- @param cb EventCallback
+--- @return nil
+function obj:onevent(code, cb)
+end
+
+---
+--- set object pressed event callback, same as obj:onevent(lvgl.EVENT.PRESSED, cb)
+--- @param cb EventCallback
+--- @return nil
+function obj:onPressed(cb)
+end
+
+---
+--- set object clicked event callback, same as obj:onevent(lvgl.EVENT.CLICKED, cb)
+--- @param cb EventCallback
+--- @return nil
+function obj:onClicked(cb)
+end
+
+---
+--- set object short clicked event callback, same as obj:onevent(lvgl.EVENT.SHORT_CLICKED, cb)
+--- @param cb EventCallback
+--- @return nil
+function obj:onShortClicked(cb)
+end
+
+---
+--- Create anim for object
+--- @param p AnimPara
+--- @return Anim
+function obj:Anim(p)
+end
+
+---
+--- Get coords of object
+--- @return Coords coords
+function obj:get_coords()
+end
+
+---
+--- Get real postion of object relative to its parent
+--- @return Coords coords
+function obj:get_pos()
+end
+
+---
+--- Calendar widget
+---@class Calendar:Object
+---
+local calendar = {}
+
+--- set method for calendar widget
+--- @param p CalendarStyle
+--- @return nil
+function calendar:set(p)
+end
+
+--- get today para setting from calendar widget
+--- @return CalendarDatePara
+function calendar:get_today(p)
+end
+
+--- get the currently showed date
+--- @return CalendarDatePara
+function calendar:get_showed(p)
+end
+
+--- get the currently pressed day
+--- @return CalendarDatePara
+function calendar:get_pressed(p)
+end
+
+--- get the button matrix object of the calendar.
+--- @return Object
+function calendar:get_btnm(p)
+end
+
+--- create a calendar header with drop-drowns to select the year and month.
+--- @return Object
+function calendar:Arrow(p)
+end
+
+--- create a calendar header with drop-drowns to select the year and month
+--- @return Object
+function calendar:Dropdown(p)
+end
+
+---
+--- Checkbox widget
+---@class Checkbox:Object
+---
+local checkbox = {}
+
+--- set method
+--- @param p CheckboxStyle
+--- @return nil
+function checkbox:set(p)
+end
+
+---
+--- Get the text of a label
+--- @return string
+function checkbox:get_text()
+end
+
+---
+--- Dropdown widget
+---@class Dropdown:Object
+---
+local dropdown = {}
+
+--- set method
+--- @param p DropdownStyle
+--- @return nil
+function dropdown:set(p)
+end
+
+--- get method
+--- @param which "list" | "options" | "selected" | "option_cnt" | "selected_str" | "option_index" | "symbol" | "dir"
+--- @param arg ? string
+--- @return string | Dir | Object
+function dropdown:get(which, arg)
+end
+
+--- Open the drop down list
+function dropdown:open()
+end
+
+--- Close (Collapse) the drop-down list
+function dropdown:close()
+end
+
+--- Tells whether the list is opened or not
+function dropdown:is_open()
+end
+
+--- Add an options to a drop-down list from a string
+--- @param option string
+--- @param pos integer
+function dropdown:add_option(option, pos)
+end
+
+--- Tells whether the list is opened or not
+function dropdown:clear_option()
+end
+
+---
+--- Image widget
+---@class Image:Object
+---
+local img = {}
+
+--- Image set method
+--- @param p ImageStyle
+--- @return nil
+function img:set(p)
+end
+
+--- set image source
+--- @param src string image source path
+--- @return nil
+function img:set_src(src)
+end
+
+--- set image offset
+--- img:set_offset{x = 0, y = 100}
+--- @param p table
+--- @return nil
+function img:set_offset(p)
+end
+
+--- set image pivot
+--- img:set_pivot{x = 0, y = 100}
+--- @param p table
+--- @return nil
+function img:set_pivot(p)
+end
+
+--- get image size, return w,h
+--- w, h = img:get_img_size()
+--- w, h = img:get_img_size("/path/to/this/image.png")
+--- @param src ? string
+--- @return integer, integer
+function img:get_img_size(src)
+end
+
+---
+--- Label widget
+---@class Label: Object
+---
+local label = {}
+
+--- Image set method
+--- @param p LabelStyle
+--- @return nil
+function label:set(p)
+end
+
+---
+--- Get the text of a label
+--- @return string
+function label:get_text()
+end
+
+---
+--- Get the long mode of a label
+--- @return string
+function label:get_long_mode()
+end
+
+---
+--- Get the recoloring attribute
+--- @return string
+function label:get_recolor()
+end
+
+---
+--- Insert a text to a label.
+--- @param pos integer
+--- @param txt string
+--- @return nil
+function label:ins_text(pos, txt)
+end
+
+---
+--- Delete characters from a label.
+--- @param pos integer
+--- @param cnt integer
+--- @return nil
+function label:cut_text(pos, cnt)
+end
+
+---
+--- Textarea widget
+---@class Textarea: Object
+---
+local textarea = {}
+
+--- Textarea set method
+--- @param p TextareaStyle
+--- @return nil
+function textarea:set(p)
+end
+
+--- get textarea text
+--- @return string
+function textarea:get_text(p)
+end
+
+---
+--- Keyboard widget
+---@class Keyboard: Object based on btnmatrix object
+---
+local keyboard = {}
+
+--- Keyboard set method
+--- @param p KeyboardStyle
+--- @return nil
+function keyboard:set(p)
+end
+
+---
+--- LED widget
+---@class Led: Object
+---
+local led = {}
+
+--- LED set method
+--- @param p LedStyle
+--- @return nil
+function led:set(p)
+end
+
+--- LED set to ON
+--- @return nil
+function led:on()
+end
+
+--- LED set to OFF
+--- @return nil
+function led:off()
+end
+
+--- toggle LED status
+--- @return nil
+function led:toggle()
+end
+
+--- get LED brightness
+--- @return integer
+function led:get_brightness()
+end
+
+---
+--- List widget
+---@class List: Object
+---
+local list = {}
+
+--- List set method
+--- @param p ListStyle
+--- @return nil
+function list:set(p)
+end
+
+--- add text to list
+--- @param text string
+--- @return Label
+function list:add_text(text)
+end
+
+--- add button to list
+--- @param icon ImgSrc | nil
+--- @param text? string
+--- @return Object a button object
+function list:add_btn(icon, text)
+end
+
+--- get list button text
+--- @param btn Object
+--- @return string
+function list:get_btn_text(btn)
+end
+
+---
+--- Roller widget
+---@class Roller: Object
+---
+local roller = {}
+
+--- Roller set method
+--- @param p RollerStyle
+--- @return nil
+function roller:set(p)
+end
+
+--- Get the options of a roller
+--- @return string
+function roller:get_options()
+end
+
+--- Get the index of the selected option
+--- @return integer
+function roller:get_selected()
+end
+
+--- Get the current selected option as a string.
+--- @return string
+function roller:get_selected_str()
+end
+
+--- Get the total number of options
+--- @return integer
+function roller:get_options_cnt()
+end
+
+---
+--- Anim
+---@class Anim
+---
+local Anim = {}
+
+--- start animation
+--- @return nil
+function Anim:start()
+end
+
+--- set animation new parameters
+--- @param para AnimPara new animation parameters
+--- @return nil
+function Anim:set(para)
+end
+
+--- stop animation
+--- @return nil
+function Anim:stop()
+end
+
+--- delete animation
+--- @return nil
+function Anim:delete()
+end
+
+---
+--- Timer
+---@class Timer
+---
+local timer = {}
+
+--- set timer property
+--- @param p TimerPara
+--- @return nil
+function timer:set(p)
+end
+
+--- resume timer
+--- @return nil
+function timer:resume()
+end
+
+--- pause timer
+--- @return nil
+function timer:pause()
+end
+
+--- delete timer
+--- @return nil
+function timer:delete()
+end
+
+--- make timer ready now, cb will be made soon on next loop
+--- @return nil
+function timer:ready()
+end
+
+--[[
+Font is a light userdata that can be uset to set style text_font.
+]]
+--- @class Font
+---
+
+local font = {}
+
+---
+--- @class Style : lightuserdata
+---
+local style = {}
+
+--- update style properties
+--- @param p StyleProp
+--- @return nil
+function style:set(p)
+end
+
+--- delete style, only delted style could be gc'ed
+--- @return nil
+function style:delete()
+end
+
+--- remove specified property from style
+--- @param p string property name from field of StyleProp
+--- @return nil
+function style:remove_prop(p)
+end
+
+---
+--- Align parameter
+--- @class Align
+--- @field type ObjAlignType
+--- @field x_ofs integer
+--- @field y_ofs integer
+
+--- AlignTo parameter
+--- @class AlignToPara
+--- @field type ObjAlignType
+--- @field base Object
+--- @field x_ofs integer
+
+--- Style properties
+--- @class StyleProp
+--- @field w integer
+--- @field width integer
+--- @field min_width integer
+--- @field max_width integer
+--- @field height integer
+--- @field min_height integer
+--- @field max_height integer
+--- @field x integer
+--- @field y integer
+--- @field size integer set size is equilent to set w/h to same value
+--- @field align Align | ObjAlignType
+--- @field transform_width integer
+--- @field transform_height integer
+--- @field translate_x integer
+--- @field translate_y integer
+--- @field transform_zoom integer
+--- @field transform_angle integer
+--- @field transform_pivot_x integer
+--- @field transform_pivot_y integer
+--- @field pad_all integer
+--- @field pad_top integer
+--- @field pad_bottom integer
+--- @field pad_ver integer
+--- @field pad_left integer
+--- @field pad_right integer
+--- @field pad_hor integer
+--- @field pad_row integer
+--- @field pad_column integer
+--- @field pad_gap integer
+--- @field bg_color integer | string text color in hex integer or #RGB or #RRGGBB format
+--- @field bg_opa integer
+--- @field bg_grad_color integer
+--- @field bg_grad_dir integer
+--- @field bg_main_stop integer
+--- @field bg_grad_stop integer
+--- @field bg_dither_mode integer
+--- @field bg_img_src integer
+--- @field bg_img_opa integer
+--- @field bg_img_recolor integer
+--- @field bg_img_recolor_opa integer
+--- @field bg_img_tiled integer
+--- @field border_color integer | string
+--- @field border_opa integer
+--- @field border_width integer
+--- @field border_side integer
+--- @field border_post integer
+--- @field outline_width integer
+--- @field outline_color integer | string
+--- @field outline_opa integer
+--- @field outline_pad integer
+--- @field shadow_width integer
+--- @field shadow_ofs_x integer
+--- @field shadow_ofs_y integer
+--- @field shadow_spread integer
+--- @field shadow_color integer | string
+--- @field shadow_opa integer
+--- @field img_opa integer
+--- @field img_recolor integer
+--- @field img_recolor_opa integer
+--- @field line_width integer
+--- @field line_dash_width integer
+--- @field line_dash_gap integer
+--- @field line_rounded integer
+--- @field line_color integer | string
+--- @field line_opa integer
+--- @field arc_width integer
+--- @field arc_rounded integer
+--- @field arc_color integer | string
+--- @field arc_opa integer
+--- @field arc_img_src integer
+--- @field text_color integer | string
+--- @field text_opa integer
+--- @field text_font Font | BuiltinFont
+--- @field text_letter_space integer
+--- @field text_line_space integer
+--- @field text_decor integer
+--- @field text_align integer
+--- @field radius integer
+--- @field clip_corner integer
+--- @field opa integer
+--- @field color_filter_opa integer
+--- @field anim_time integer
+--- @field anim_speed integer
+--- @field blend_mode integer
+--- @field layout integer
+--- @field base_dir integer
+--- @field flex_flow FlexFlow
+--- @field flex_main_place FlexAlign
+--- @field flex_cross_place FlexAlign
+--- @field flex_track_place FlexAlign
+--- @field flex_grow integer 0..255
+--- @field flex FlexLayoutPara
+
+---
+
+--- Object style
+--- @class ObjectStyle :StyleProp
+--- @field x integer
+--- @field y integer
+--- @field w integer
+--- @field h integer
+--- @field align Align | integer
+--- @field align_to AlignToPara
+--- @field scrollbar_mode ScrollbarMode
+--- @field scroll_dir Dir
+--- @field scroll_snap_x integer
+--- @field scroll_snap_y integer
+---
+
+--- Image style
+--- @class ImageStyle :StyleProp
+--- @field src string
+--- @field offset_x integer offset of image
+--- @field offset_y integer
+--- @field angle integer
+--- @field zoom integer
+--- @field antialias boolean
+--- @field pivot table
+---
+
+--- Label style
+--- @class LabelStyle :StyleProp
+--- @field text string
+
+
+--- Checkbox style
+--- @class CalendarStyle :StyleProp
+--- @field today CalendarDatePara
+--- @field showed CalendarDatePara
+
+--- Checkbox style
+--- @class CheckboxStyle :StyleProp
+--- @field text string
+
+--- Dropdown style
+--- @class DropdownStyle :StyleProp
+--- @field text string | nil
+--- @field options string
+--- @field selected integer
+--- @field dir Dir
+--- @field symbol lightuserdata | string
+--- @field highlight boolean
+
+--- Textarea style
+--- @class TextareaStyle :StyleProp
+--- @field text string
+--- @field placeholder string
+--- @field cursor integer cursor position
+--- @field password_mode boolean enable password
+--- @field one_line boolean enable one line mode
+--- @field password_bullet string Set the replacement characters to show in password mode
+--- @field accepted_chars string DO NOT USE. Set a list of characters. Only these characters will be accepted by the text area E.g. "+-.,0123456789"
+--- @field max_length integer Set max length of a Text Area.
+--- @field password_show_time integer Set how long show the password before changing it to '*'
+
+--- Keyboard style
+--- @class KeyboardStyle :StyleProp
+--- @field textarea Textarea textarea object
+--- @field mode KeyboardMode
+--- @field popovers boolean Show the button title in a popover when pressed.
+
+--- Led style
+--- @class LedStyle :StyleProp
+--- @field color integer|string color of led
+--- @field brightness integer brightness in range of 0..255
+
+--- List style
+--- @class ListStyle :StyleProp
+
+--- Roller style
+--- @class RollerStyle :StyleProp
+--- @field options table | string
+--- @field selected table | integer
+--- @field visible_cnt integer
+
+---
+--- Anim(for object) parameter
+--- @alias AnimExecCb fun(obj:any, value:integer): nil
+--- @alias AnimDoneCb fun(anim:Anim, var:any): nil
+
+--- @class AnimPara
+--- @field run boolean run this anim right now, or later using anim:start(). default: false
+--- @field start_value integer start value
+--- @field end_value integer
+--- @field duration integer Anim duration in milisecond
+--- @field delay integer Set a delay before starting the animation
+--- @field repeat_count integer Anim repeat count, default: 1, set to 0 to disable repeat, set to lvgl.ANIM_REPEAT_INFINITE for infinite repeat, set to any other integer for specified repeate count
+--- @field playback_delay integer
+--- @field playback_time integer
+--- @field early_apply boolean set start_value right now or not. default: true
+--- @field path string | "linear" | "ease_in" | "ease_out" | "ease_in_out" | "overshoot" | "bounce" | "step"
+--- @field exec_cb AnimExecCb
+--- @field done_cb AnimDoneCb
+
+
+---
+--- Timer para
+--- @alias TimerCallback fun(t:Timer): nil
+--- @class TimerPara
+--- @field paused boolean Do not start timer immediaely
+--- @field period integer timer period in ms unit
+--- @field repeat_count integer | -1 |
+--- @field cb TimerCallback
+---
+
+
+---
+--- @alias ImgSrc string | lightuserdata
+
+--- @alias flexAlignOptions "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly"
+---
+--- @class FlexLayoutPara
+--- @field flex_direction "row" | "column" | "row-reverse" | "column-reverse"
+--- @field flex_wrap "nowrap" | "wrap" | "wrap-reverse"
+--- @field justify_content flexAlignOptions
+--- @field align_items flexAlignOptions
+--- @field align_content flexAlignOptions
+
+
+---
+--- CalendarToday para
+--- @class CalendarDatePara
+--- @field year integer
+--- @field month integer
+--- @field day integer
+---
+
+---
+--- Coordinates
+--- @class Coords
+--- @field x1 integer
+--- @field y1 integer
+--- @field x2 integer
+--- @field y2 integer
+---
+
+return lvgl
diff --git a/lib/luavgl/src/obj.c b/lib/luavgl/src/obj.c
new file mode 100644
index 00000000..a316f59c
--- /dev/null
+++ b/lib/luavgl/src/obj.c
@@ -0,0 +1,850 @@
+#include "luavgl.h"
+#include "private.h"
+
+/* extended feature for object */
+#include "event.c"
+#include "style.c"
+#include "widgets/widgets.c"
+
+static int luavgl_anim_create(lua_State *L);
+static int luavgl_obj_delete(lua_State *L);
+
+static void _lv_obj_set_align(void *obj, lua_State *L)
+{
+ if (lua_isinteger(L, -1)) {
+ lv_obj_align(obj, lua_tointeger(L, -1), 0, 0);
+ return;
+ }
+
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "should be table.");
+ debug("para should be table.");
+ return;
+ }
+
+ lua_getfield(L, -1, "type");
+ lv_align_t align = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "x_ofs");
+ lv_coord_t x_ofs = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "y_ofs");
+ lv_coord_t y_ofs = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_obj_align(obj, align, x_ofs, y_ofs);
+}
+
+/**
+ * @brief Set obj properties based on property table on stack top
+ *
+ * Internally used.
+ */
+static inline void luavgl_setup_obj(lua_State *L, lv_obj_t *obj)
+{
+ luavgl_iterate(L, -1, luavgl_obj_set_property_kv, obj);
+}
+
+static void obj_delete_cb(lv_event_t *e)
+{
+ lua_State *L = e->user_data;
+ lua_pushlightuserdata(L, e->current_target);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ goto pop_exit;
+ }
+
+ luavgl_obj_t *lobj = luavgl_to_lobj(L, -1);
+ if (lobj->lua_created)
+ goto pop_exit;
+
+ luavgl_obj_delete(L);
+ return;
+
+pop_exit:
+ lua_pop(L, 1);
+ return;
+}
+
+/**
+ * get the obj userdata and uservalue, if uservalue is not a table, then add
+ * one. result stack: table(from uservalue)
+ * return uservalue type: LUA_TTABLE
+ */
+LUALIB_API int luavgl_obj_getuserdatauv(lua_State *L, int idx)
+{
+ int type = lua_getuservalue(L, idx);
+ if (type == LUA_TTABLE)
+ return type;
+
+ lua_pop(L, 1);
+ /* initial element: 1 */
+ lua_createtable(L, 0, 1);
+
+#if 0 /* reserved slot, not used for now */
+ lua_pushinteger(L, 1);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+#endif
+ lua_pushvalue(L, -1); /* leave one on stack */
+ lua_setuservalue(L, idx > 0 ? idx : idx - 3);
+ return LUA_TTABLE;
+}
+
+static int luavgl_obj_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_obj_create);
+}
+
+static int luavgl_obj_delete(lua_State *L)
+{
+ luavgl_obj_t *lobj;
+
+ /**
+ * Some widget may create child obj that doesn't belong to lua. Ignore them
+ * and report no error.
+ */
+ if (!(lobj = lua_touserdata(L, -1))) {
+ return 0;
+ }
+
+ if (lobj->obj == NULL) {
+ /* could be already deleted, but not gc'ed */
+ return 0;
+ }
+
+ uint32_t cnt = lv_obj_get_child_cnt(lobj->obj);
+
+ for (int i = cnt - 1; i >= 0; i--) {
+ lv_obj_t *child = lv_obj_get_child(lobj->obj, i);
+ lua_checkstack(L, 2);
+ lua_pushlightuserdata(L, child);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ continue;
+ }
+
+ luavgl_obj_delete(L);
+ }
+
+ luavgl_obj_remove_event_all(L, lobj);
+
+ /* delete obj firstly, then cleanup memory */
+ if (lobj->lua_created) {
+ lv_obj_del(lobj->obj);
+ }
+
+ lua_checkstack(L, 2);
+ /* remove userdata from registry. */
+ lua_pushlightuserdata(L, lobj->obj);
+ lua_pushnil(L);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ debug("delete obj: %p\n", lobj->obj);
+ lobj->obj = NULL;
+
+ lua_pop(L, 1); /* remove the userdata para */
+ return 0;
+}
+
+static int luavgl_obj_clean(lua_State *L)
+{
+ luavgl_obj_t *lobj = luavgl_to_lobj(L, -1);
+ if (lobj == NULL || lobj->obj == NULL)
+ return 0;
+
+ lv_obj_t *obj = lobj->obj;
+ uint32_t cnt = lv_obj_get_child_cnt(obj);
+
+ for (int i = cnt - 1; i >= 0; i--) {
+ lv_obj_t *child = lv_obj_get_child(obj, i);
+
+ lua_checkstack(L, 2);
+ lua_pushlightuserdata(L, child);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ luavgl_obj_delete(L);
+ }
+
+ lua_pop(L, 1); /* remove the userdata para */
+ return 0;
+}
+
+static int luavgl_obj_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_setup_obj(L, obj);
+ return 0;
+}
+
+/**
+ * obj:align_to({base=base, type=type, x_ofs=0, y_ofs=0})
+ */
+static int luavgl_obj_align_to(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, 2)) {
+ debug("para should be table.");
+ return luaL_argerror(L, 2, "should be table.");
+ }
+
+ lua_getfield(L, 2, "type");
+ lv_align_t align = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "base");
+ lv_obj_t *base = luavgl_to_lobj(L, -1)->obj;
+ lua_pop(L, 1);
+ if (base == NULL) {
+ debug("base is not lua obj");
+ return luaL_argerror(L, -1, "base is not lua obj");
+ }
+
+ lua_getfield(L, 2, "x_ofs");
+ lv_coord_t x_ofs = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "y_ofs");
+ lv_coord_t y_ofs = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_obj_align_to(obj, base, align, x_ofs, y_ofs);
+ return 0;
+}
+
+static int luavgl_obj_set_parent(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_t *parent = luavgl_to_obj(L, 2);
+ lv_obj_set_parent(obj, parent);
+ return 0;
+}
+
+static int luavgl_obj_get_screen(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_t *screen = lv_obj_get_screen(obj);
+
+ /* check if userdata is added to this obj, so lua can access it. */
+ lua_pushlightuserdata(L, screen);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnil(L, -1)) {
+ /* create luavgl object and attach screen on it. */
+ luavgl_obj_t *lobj = luavgl_add_lobj(L, screen);
+ /* mark it's non-lua created, thus cannot be deleted by lua */
+ lobj->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_obj_get_parent(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_t *parent = lv_obj_get_parent(obj);
+
+ /* check if userdata is added to this obj, so lua can access it. */
+ lua_pushlightuserdata(L, parent);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnil(L, -1)) {
+ /* create luavgl object and attach screen on it. */
+ luavgl_obj_t *lobj = luavgl_add_lobj(L, parent);
+ /* mark it's non-lua created, thus cannot be deleted by lua */
+ lobj->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_obj_set_get_parent(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ if (!lua_isnoneornil(L, 2)) {
+ lv_obj_t *parent = luavgl_to_obj(L, 2);
+ lv_obj_set_parent(obj, parent);
+ }
+
+ return luavgl_obj_get_parent(L);
+}
+
+static int luavgl_obj_get_child(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ int id = luavgl_tointeger(L, 2);
+ lv_obj_t *child = lv_obj_get_child(obj, id);
+ if (child == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ /* check if userdata is added to this obj, so lua can access it. */
+ lua_pushlightuserdata(L, child);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnil(L, -1)) {
+ luavgl_add_lobj(L, child)->lua_created = false;
+ }
+
+ return 1;
+}
+
+static int luavgl_obj_get_child_cnt(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_obj_get_child_cnt(obj));
+ return 1;
+}
+
+static int luavgl_obj_get_state(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_state_t state = lv_obj_get_state(obj);
+ lua_pushinteger(L, state);
+
+ return 1;
+}
+
+/**
+ * obj:scroll_to({x=10})
+ * obj:scroll_to({x=10, anim=true})
+ * obj:scroll_to({x=10, y=100, anim=false})
+ */
+static int luavgl_obj_scroll_to(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "should be table {x=0,y=0,anim=true}");
+ }
+
+ lua_getfield(L, -1, "anim");
+ bool anim = luavgl_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_coord_t v;
+ lua_getfield(L, -1, "x");
+ if (!lua_isnil(L, -1)) {
+ v = lua_tointeger(L, -1);
+ lv_obj_scroll_to_x(obj, v, anim ? LV_ANIM_ON : LV_ANIM_OFF);
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "y");
+ if (!lua_isnil(L, -1)) {
+ v = lua_tointeger(L, -1);
+ lv_obj_scroll_to_x(obj, v, anim ? LV_ANIM_ON : LV_ANIM_OFF);
+ }
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+static int luavgl_obj_is_visible(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_obj_is_visible(obj));
+
+ return 1;
+}
+
+static int luavgl_obj_add_flag(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_flag_t flag = lua_tointeger(L, 2);
+ lv_obj_add_flag(obj, flag);
+
+ return 0;
+}
+
+static int luavgl_obj_clear_flag(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_flag_t flag = lua_tointeger(L, 2);
+ lv_obj_clear_flag(obj, flag);
+
+ return 0;
+}
+
+static int luavgl_obj_add_state(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_state_t state = lua_tointeger(L, 2);
+ lv_obj_add_state(obj, state);
+ return 0;
+}
+
+static int luavgl_obj_clear_state(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_state_t state = lua_tointeger(L, 2);
+ lv_obj_clear_state(obj, state);
+ return 0;
+}
+
+/**
+ * obj:scroll_by(x, y, anim_en)
+ */
+static int luavgl_obj_scroll_by(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int x = luavgl_tointeger(L, 2);
+ int y = luavgl_tointeger(L, 3);
+ int anim_en = luavgl_tointeger(L, 4);
+
+ lv_obj_scroll_by(obj, x, y, anim_en);
+ return 0;
+}
+
+static int luavgl_obj_scroll_by_bounded(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int dx = luavgl_tointeger(L, 2);
+ int dy = luavgl_tointeger(L, 3);
+ int anim_en = luavgl_tointeger(L, 4);
+
+ lv_obj_scroll_by_bounded(obj, dx, dy, anim_en);
+ return 0;
+}
+
+static int luavgl_obj_scroll_to_view(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int anim_en = luavgl_tointeger(L, 2);
+
+ lv_obj_scroll_to_view(obj, anim_en);
+ return 0;
+}
+
+static int luavgl_obj_scroll_to_view_recursive(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int anim_en = luavgl_tointeger(L, 2);
+
+ lv_obj_scroll_to_view_recursive(obj, anim_en);
+ return 0;
+}
+
+static int luavgl_obj_scroll_by_raw(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int x = luavgl_tointeger(L, 2);
+ int y = luavgl_tointeger(L, 3);
+
+ _lv_obj_scroll_by_raw(obj, x, y);
+ return 0;
+}
+
+static int luavgl_obj_is_scrolling(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_obj_is_scrolling(obj));
+ return 1;
+}
+
+static int luavgl_obj_scrollbar_invalidate(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_scrollbar_invalidate(obj);
+ return 0;
+}
+
+static int luavgl_obj_readjust_scroll(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ int anim_en = luavgl_tointeger(L, 2);
+ lv_obj_readjust_scroll(obj, anim_en);
+ return 0;
+}
+
+static int luavgl_obj_is_editable(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_obj_is_editable(obj));
+ return 1;
+}
+
+static int luavgl_obj_is_group_def(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_obj_is_group_def(obj));
+ return 1;
+}
+
+static int luavgl_obj_is_layout_positioned(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_obj_is_layout_positioned(obj));
+ return 1;
+}
+
+static int luavgl_obj_mark_layout_as_dirty(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_mark_layout_as_dirty(obj);
+ return 0;
+}
+
+static int luavgl_obj_center(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_center(obj);
+ return 0;
+}
+
+static int luavgl_obj_invalidate(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_invalidate(obj);
+ return 0;
+}
+
+static int luavgl_obj_set_flex_flow(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_flex_flow_t flow = luavgl_tointeger(L, 2);
+
+ lv_obj_set_flex_flow(obj, flow);
+ return 0;
+}
+
+static int luavgl_obj_set_flex_align(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_flex_align_t m = luavgl_tointeger(L, 2);
+ lv_flex_align_t c = luavgl_tointeger(L, 3);
+ lv_flex_align_t t = luavgl_tointeger(L, 4);
+
+ lv_obj_set_flex_align(obj, m, c, t);
+ return 0;
+}
+
+static int luavgl_obj_set_flex_grow(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ uint8_t grow = luavgl_tointeger(L, 2);
+
+ lv_obj_set_flex_grow(obj, grow);
+ return 0;
+}
+
+static int luavgl_obj_indev_search(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_point_t point;
+ if (lua_istable(L, 2)) {
+ lua_geti(L, 2, 1);
+ point.x = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_geti(L, 2, 2);
+ point.y = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ } else {
+ point.x = lua_tointeger(L, 2);
+ point.y = lua_tointeger(L, 3);
+ }
+
+ obj = lv_indev_search_obj(obj, &point);
+ if (obj == NULL) {
+ lua_pushnil(L);
+ } else {
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, obj)->lua_created = false;
+ }
+ }
+
+ return 1;
+}
+
+static int luavgl_obj_get_coords(lua_State *L)
+{
+ lv_area_t area;
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_get_coords(obj, &area);
+ lua_newtable(L);
+
+ lua_pushinteger(L, area.x1);
+ lua_setfield(L, -2, "x1");
+
+ lua_pushinteger(L, area.x2);
+ lua_setfield(L, -2, "x2");
+
+ lua_pushinteger(L, area.y1);
+ lua_setfield(L, -2, "y1");
+
+ lua_pushinteger(L, area.y2);
+ lua_setfield(L, -2, "y2");
+
+ return 1;
+}
+
+/**
+ * get object real position using lv_obj_get_x/x2/y/y2
+ */
+static int luavgl_obj_get_pos(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_newtable(L);
+
+ lua_pushinteger(L, lv_obj_get_x(obj));
+ lua_setfield(L, -2, "x1");
+
+ lua_pushinteger(L, lv_obj_get_x2(obj));
+ lua_setfield(L, -2, "x2");
+
+ lua_pushinteger(L, lv_obj_get_y(obj));
+ lua_setfield(L, -2, "y1");
+
+ lua_pushinteger(L, lv_obj_get_y2(obj));
+ lua_setfield(L, -2, "y2");
+
+ return 1;
+}
+
+/**
+ * Remove all animations associates to this object
+ */
+static int luavgl_obj_remove_anim_all(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_anim_del(obj, NULL);
+ return 1;
+}
+
+static int luavgl_obj_gc(lua_State *L)
+{
+ if (lua_type(L, 1) != LUA_TUSERDATA) {
+ /* If t = setmetatable({}, obj_meta_table), this will happen when t is
+ * gc;ed. Currently all metatables for classes based on obj, that has no own
+ * __gc will goes here.
+ */
+ return 0;
+ }
+
+ debug("\n");
+
+ luavgl_obj_t *lobj = lua_touserdata(L, 1);
+ if (lobj == NULL || lobj->obj == NULL) {
+ /* obj is already deleted. It's ok. */
+ return 0;
+ }
+
+ debug("GC for obj: %p\n", lobj->obj);
+ luavgl_obj_delete(L);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_obj_methods[] = {
+ {"set", luavgl_obj_set },
+ {"set_style", luavgl_obj_set_style },
+ {"align_to", luavgl_obj_align_to },
+ {"delete", luavgl_obj_delete },
+ {"clean", luavgl_obj_clean },
+
+ /* misc. functions */
+ {"parent", luavgl_obj_set_get_parent },
+ {"set_parent", luavgl_obj_set_parent },
+ {"get_parent", luavgl_obj_get_parent },
+ {"get_child", luavgl_obj_get_child },
+ {"get_child_cnt", luavgl_obj_get_child_cnt },
+ {"get_screen", luavgl_obj_get_screen },
+ {"get_state", luavgl_obj_get_state },
+ {"scroll_to", luavgl_obj_scroll_to },
+ {"is_scrolling", luavgl_obj_is_scrolling },
+ {"is_visible", luavgl_obj_is_visible },
+ {"add_flag", luavgl_obj_add_flag },
+ {"clear_flag", luavgl_obj_clear_flag },
+ {"add_state", luavgl_obj_add_state },
+ {"clear_state", luavgl_obj_clear_state },
+ {"add_style", luavgl_obj_add_style },
+ {"remove_style", luavgl_obj_remove_style },
+ {"remove_style_all", luavgl_obj_remove_style_all },
+ {"scroll_by", luavgl_obj_scroll_by },
+ {"scroll_by_bounded", luavgl_obj_scroll_by_bounded },
+ {"scroll_to_view", luavgl_obj_scroll_to_view },
+ {"scroll_to_view_recursive", luavgl_obj_scroll_to_view_recursive},
+ {"scroll_by_raw", luavgl_obj_scroll_by_raw },
+ {"scrollbar_invalidate", luavgl_obj_scrollbar_invalidate },
+ {"readjust_scroll", luavgl_obj_readjust_scroll },
+ {"is_editable", luavgl_obj_is_editable },
+ {"is_group_def", luavgl_obj_is_group_def },
+ {"is_layout_positioned", luavgl_obj_is_layout_positioned },
+ {"mark_layout_as_dirty", luavgl_obj_mark_layout_as_dirty },
+ {"center", luavgl_obj_center },
+ {"invalidate", luavgl_obj_invalidate },
+ {"set_flex_flow", luavgl_obj_set_flex_flow },
+ {"set_flex_align", luavgl_obj_set_flex_align },
+ {"set_flex_grow", luavgl_obj_set_flex_grow },
+ {"indev_search", luavgl_obj_indev_search },
+ {"get_coords", luavgl_obj_get_coords },
+ {"get_pos", luavgl_obj_get_pos },
+
+ {"onevent", luavgl_obj_on_event },
+ {"onPressed", luavgl_obj_on_pressed },
+ {"onClicked", luavgl_obj_on_clicked },
+ {"onShortClicked", luavgl_obj_on_short_clicked },
+ {"anim", luavgl_anim_create },
+ {"Anim", luavgl_anim_create },
+ {"remove_all_anim", luavgl_obj_remove_anim_all }, /* remove all */
+ {NULL, NULL },
+};
+
+static void luavgl_obj_init(lua_State *L)
+{
+ /* base lv_obj */
+ luavgl_obj_newmetatable(L, &lv_obj_class, "lv_obj", luavgl_obj_methods);
+ lua_pushcfunction(L, luavgl_obj_gc);
+ lua_setfield(L, -2, "__gc");
+
+ /**
+ * Widget creation functions is a metatable, so we can add extended widget to
+ * it.
+ *
+ * widgets = {}
+ * lib = {widget_create_methods}
+ * widgets.__index = lib
+ * obj.__metatable = widgets
+ */
+ lua_getfield(L, -1, "__index");
+ luaL_newmetatable(L, "widgets");
+ luaL_newlib(L, widget_create_methods);
+ lua_setfield(L, -2, "__index");
+ lua_setmetatable(L, -2);
+ lua_pop(L, 1); /* remove obj.__index table */
+
+ lua_pop(L, 1); /* remove obj metatable */
+}
+
+static const luavgl_value_setter_t obj_property_table[] = {
+ {"x", 0, {.setter = (setter_int_t)lv_obj_set_x} },
+ {"y", 0, {.setter = (setter_int_t)lv_obj_set_y} },
+ {"w", 0, {.setter = (setter_int_t)lv_obj_set_width} },
+ {"h", 0, {.setter = (setter_int_t)lv_obj_set_height} },
+ {"align", SETTER_TYPE_STACK, {.setter_stack = _lv_obj_set_align} },
+
+ {"scrollbar_mode", 0, {.setter = (setter_int_t)lv_obj_set_scrollbar_mode}},
+ {"scroll_dir", 0, {.setter = (setter_int_t)lv_obj_set_scroll_dir} },
+ {"scroll_snap_x", 0, {.setter = (setter_int_t)lv_obj_set_scroll_snap_x} },
+ {"scroll_snap_y", 0, {.setter = (setter_int_t)lv_obj_set_scroll_snap_y} },
+};
+
+/**
+ * Set object property.
+ * Differ from set object style, this one is usually used to set widget
+ * property like lv_WIDGETNAME_set_src()
+ *
+ * Used internally.
+ *
+ * Expected stack:
+ * stack[-2]: key(property name)
+ * stack[-1]: value(could be any lua data)
+ */
+LUALIB_API int luavgl_obj_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, obj_property_table);
+
+ if (ret == 0)
+ return 0;
+
+ /* fallback to set obj local style, with default state. */
+ return luavgl_obj_set_style_kv(L, obj, 0);
+}
+
+LUALIB_API int luavgl_obj_create_helper(lua_State *L,
+ lv_obj_t *(*create)(lv_obj_t *parent))
+{
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ lv_obj_t *parent;
+
+ if (lua_isnoneornil(L, 1)) {
+ parent = ctx->root;
+ } else {
+ parent = luavgl_to_obj(L, 1);
+ /* remove parent, in order to keep clean stack to call obj.set */
+ lua_remove(L, 1);
+ }
+
+ debug("create obj on: %p\n", parent);
+
+ lv_obj_t *obj = create(parent);
+ luavgl_add_lobj(L, obj)->lua_created = true;
+
+ if (!lua_istable(L, -2)) {
+ /* no need to call setup */
+ return 1;
+ }
+
+ lua_insert(L, -2); /* obj, prop */
+
+ /* now call obj.set to setup property */
+ lua_getfield(L, -2, "set"); /* obj, prop, set */
+ if (!luavgl_is_callable(L, -1)) {
+ /* set method not found, call basic obj set method. */
+ lua_pop(L, 2); /* remove prop table, and set method */
+ luavgl_setup_obj(L, obj);
+ } else {
+ lua_insert(L, -3); /* set, obj, prop */
+ lua_call(L, 2, 0); /* now stack is clean */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ }
+
+ debug("create obj: %p\n", obj);
+ return 1;
+}
+
+/**
+ * Add existing lvgl obj to lua, return lobj(luavgl obj).
+ * If no metatable not found for this obj class, then lv_obj_class metatable is
+ * used
+ */
+LUALIB_API luavgl_obj_t *luavgl_add_lobj(lua_State *L, lv_obj_t *obj)
+{
+ luavgl_obj_t *lobj;
+
+ /* In rare case, obj may be deleted but not gc'ed in lua, and lvgl quickly
+ * creates another obj but happen to malloced same address, thus using obj
+ * light userdata as key may have some potential issues. */
+ lua_pushlightuserdata(L, obj);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_isnoneornil(L, -1)) {
+ /* already exists */
+ return luavgl_to_lobj(L, -1);
+ }
+ lua_pop(L, 1); /* pop nil value */
+
+ lobj = lua_newuserdata(L, sizeof(*lobj));
+
+ if (luavgl_obj_getmetatable(L, obj->class_p) == LUA_TNIL) {
+ lua_pop(L, 1);
+ debug("cannot find metatable for class: %p\n", obj->class_p);
+ /* use base obj metatable instead */
+ luavgl_obj_getmetatable(L, &lv_obj_class);
+ }
+
+ lua_setmetatable(L, -2);
+
+ memset(lobj, 0, sizeof(*lobj));
+ luavgl_obj_event_init(lobj);
+ lobj->obj = obj;
+ lv_obj_add_event_cb(obj, obj_delete_cb, LV_EVENT_DELETE, L);
+
+ /* registry[obj] = lobj */
+ lua_pushlightuserdata(L, obj);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ return lobj;
+}
diff --git a/lib/luavgl/src/private.h b/lib/luavgl/src/private.h
new file mode 100644
index 00000000..20aa802f
--- /dev/null
+++ b/lib/luavgl/src/private.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <lvgl.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/* clang-format off */
+#define debug(format, ...)
+// fprintf(stderr, "[luavgl] %s: " format, __FUNCTION__, ##__VA_ARGS__)
+// syslog(LOG_DEBUG, "[luavgl] %s: " format, __FUNCTION__, ##__VA_ARGS__)
+/* clang-format on */
+
+struct event_callback_s {
+ lv_event_code_t code;
+ struct _lv_event_dsc_t *dsc;
+ int ref; /* ref to callback */
+}; /* variable array if n_callback > 1 */
+
+static void dumpstack(lua_State *L);
+static void dumptable(lua_State *L, int index);
+
+/* metatable */
+int luavgl_obj_getmetatable(lua_State *L, const lv_obj_class_t *clz);
+int luavgl_obj_setmetatable(lua_State *L, int idx, const lv_obj_class_t *clz);
+
+static void luavgl_obj_event_init(luavgl_obj_t *lobj);
+static void luavgl_obj_remove_event_all(lua_State *L, luavgl_obj_t *obj);
+
+/* util functions */
+static void luavgl_check_callable(lua_State *L, int index);
+static int luavgl_check_continuation(lua_State *L, int index);
+
+static int luavgl_obj_set_style_kv(lua_State *L, lv_obj_t *obj, int selector);
+
+static int luavgl_pcall_int(lua_State *L, int nargs, int nresult);
diff --git a/lib/luavgl/src/style.c b/lib/luavgl/src/style.c
new file mode 100644
index 00000000..2ea3cff8
--- /dev/null
+++ b/lib/luavgl/src/style.c
@@ -0,0 +1,702 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct {
+ lv_style_t style;
+} luavgl_style_t;
+
+typedef enum {
+ STYLE_TYPE_INT = 1,
+ STYLE_TYPE_COLOR,
+ STYLE_TYPE_POINTER, /* a pointer from light-userdata */
+ STYLE_TYPE_IMGSRC, /* string or light-userdata, similar to pointer */
+
+ STYLE_TYPE_TABLE, /* value is in table */
+ /* Flag of none-simple type. All extended styles are special case. */
+ STYLE_TYPE_SPECIAL = 0x10,
+} style_type_t;
+
+enum {
+ LV_STYLE_SIZE = _LV_STYLE_LAST_BUILT_IN_PROP + 1,
+ LV_STYLE_PAD_ALL,
+ LV_STYLE_PAD_VER,
+ LV_STYLE_PAD_HOR,
+ LV_STYLE_PAD_GAP,
+
+ _LV_STYLE_FLEX,
+
+ _LV_STYLE_FLEX_FLOW, /* style not const, need to call related API */
+ _LV_STYLE_FLEX_MAIN_PLACE,
+ _LV_STYLE_FLEX_CROSS_PLACE,
+ _LV_STYLE_FLEX_TRACK_PLACE,
+ _LV_STYLE_FLEX_GROW,
+};
+
+/* callback made when style matched. */
+typedef void (*style_set_cb_t)(lv_style_prop_t prop, lv_style_value_t value,
+ void *args);
+
+static const struct style_map_s {
+ const char *name;
+ lv_style_prop_t prop;
+ style_type_t type;
+} g_style_map[] = {
+ {"width", LV_STYLE_WIDTH, STYLE_TYPE_INT },
+ {"min_width", LV_STYLE_MIN_WIDTH, STYLE_TYPE_INT },
+ {"max_width", LV_STYLE_MAX_WIDTH, STYLE_TYPE_INT },
+ {"height", LV_STYLE_HEIGHT, STYLE_TYPE_INT },
+ {"min_height", LV_STYLE_MIN_HEIGHT, STYLE_TYPE_INT },
+ {"max_height", LV_STYLE_MAX_HEIGHT, STYLE_TYPE_INT },
+ {"x", LV_STYLE_X, STYLE_TYPE_INT },
+ {"y", LV_STYLE_Y, STYLE_TYPE_INT },
+ {"align", LV_STYLE_ALIGN, STYLE_TYPE_INT },
+ {"transform_width", LV_STYLE_TRANSFORM_WIDTH, STYLE_TYPE_INT },
+ {"transform_height", LV_STYLE_TRANSFORM_HEIGHT, STYLE_TYPE_INT },
+ {"translate_x", LV_STYLE_TRANSLATE_X, STYLE_TYPE_INT },
+ {"translate_y", LV_STYLE_TRANSLATE_Y, STYLE_TYPE_INT },
+ {"transform_zoom", LV_STYLE_TRANSFORM_ZOOM, STYLE_TYPE_INT },
+ {"transform_angle", LV_STYLE_TRANSFORM_ANGLE, STYLE_TYPE_INT },
+ {"transform_pivot_x", LV_STYLE_TRANSFORM_PIVOT_X, STYLE_TYPE_INT },
+ {"transform_pivot_y", LV_STYLE_TRANSFORM_PIVOT_Y, STYLE_TYPE_INT },
+ {"pad_top", LV_STYLE_PAD_TOP, STYLE_TYPE_INT },
+ {"pad_bottom", LV_STYLE_PAD_BOTTOM, STYLE_TYPE_INT },
+ {"pad_left", LV_STYLE_PAD_LEFT, STYLE_TYPE_INT },
+ {"pad_right", LV_STYLE_PAD_RIGHT, STYLE_TYPE_INT },
+ {"pad_row", LV_STYLE_PAD_ROW, STYLE_TYPE_INT },
+ {"pad_column", LV_STYLE_PAD_COLUMN, STYLE_TYPE_INT },
+ {"pad_gap", LV_STYLE_PAD_GAP, STYLE_TYPE_INT },
+ {"bg_color", LV_STYLE_BG_COLOR, STYLE_TYPE_COLOR },
+ {"bg_opa", LV_STYLE_BG_OPA, STYLE_TYPE_INT },
+ {"bg_grad_color", LV_STYLE_BG_GRAD_COLOR, STYLE_TYPE_COLOR },
+ {"bg_grad_dir", LV_STYLE_BG_GRAD_DIR, STYLE_TYPE_INT },
+ {"bg_main_stop", LV_STYLE_BG_MAIN_STOP, STYLE_TYPE_INT },
+ {"bg_grad_stop", LV_STYLE_BG_GRAD_STOP, STYLE_TYPE_INT },
+ {"bg_dither_mode", LV_STYLE_BG_DITHER_MODE, STYLE_TYPE_INT },
+ {"bg_img_src", LV_STYLE_BG_IMG_SRC, STYLE_TYPE_IMGSRC },
+ {"bg_img_opa", LV_STYLE_BG_IMG_OPA, STYLE_TYPE_INT },
+ {"bg_img_recolor", LV_STYLE_BG_IMG_RECOLOR, STYLE_TYPE_COLOR },
+ {"bg_img_recolor_opa", LV_STYLE_BG_IMG_RECOLOR_OPA, STYLE_TYPE_INT },
+ {"bg_img_tiled", LV_STYLE_BG_IMG_TILED, STYLE_TYPE_INT },
+ {"border_color", LV_STYLE_BORDER_COLOR, STYLE_TYPE_COLOR },
+ {"border_opa", LV_STYLE_BORDER_OPA, STYLE_TYPE_INT },
+ {"border_width", LV_STYLE_BORDER_WIDTH, STYLE_TYPE_INT },
+ {"border_side", LV_STYLE_BORDER_SIDE, STYLE_TYPE_INT },
+ {"border_post", LV_STYLE_BORDER_POST, STYLE_TYPE_INT },
+ {"outline_width", LV_STYLE_OUTLINE_WIDTH, STYLE_TYPE_INT },
+ {"outline_color", LV_STYLE_OUTLINE_COLOR, STYLE_TYPE_COLOR },
+ {"outline_opa", LV_STYLE_OUTLINE_OPA, STYLE_TYPE_INT },
+ {"outline_pad", LV_STYLE_OUTLINE_PAD, STYLE_TYPE_INT },
+ {"shadow_width", LV_STYLE_SHADOW_WIDTH, STYLE_TYPE_INT },
+ {"shadow_ofs_x", LV_STYLE_SHADOW_OFS_X, STYLE_TYPE_INT },
+ {"shadow_ofs_y", LV_STYLE_SHADOW_OFS_Y, STYLE_TYPE_INT },
+ {"shadow_spread", LV_STYLE_SHADOW_SPREAD, STYLE_TYPE_INT },
+ {"shadow_color", LV_STYLE_SHADOW_COLOR, STYLE_TYPE_COLOR },
+ {"shadow_opa", LV_STYLE_SHADOW_OPA, STYLE_TYPE_INT },
+ {"img_opa", LV_STYLE_IMG_OPA, STYLE_TYPE_INT },
+ {"img_recolor", LV_STYLE_IMG_RECOLOR, STYLE_TYPE_COLOR },
+ {"img_recolor_opa", LV_STYLE_IMG_RECOLOR_OPA, STYLE_TYPE_INT },
+ {"line_width", LV_STYLE_LINE_WIDTH, STYLE_TYPE_INT },
+ {"line_dash_width", LV_STYLE_LINE_DASH_WIDTH, STYLE_TYPE_INT },
+ {"line_dash_gap", LV_STYLE_LINE_DASH_GAP, STYLE_TYPE_INT },
+ {"line_rounded", LV_STYLE_LINE_ROUNDED, STYLE_TYPE_INT },
+ {"line_color", LV_STYLE_LINE_COLOR, STYLE_TYPE_INT },
+ {"line_opa", LV_STYLE_LINE_OPA, STYLE_TYPE_INT },
+ {"arc_width", LV_STYLE_ARC_WIDTH, STYLE_TYPE_INT },
+ {"arc_img_src", LV_STYLE_ARC_IMG_SRC, STYLE_TYPE_IMGSRC },
+ {"arc_rounded", LV_STYLE_ARC_ROUNDED, STYLE_TYPE_INT },
+ {"arc_color", LV_STYLE_ARC_COLOR, STYLE_TYPE_COLOR },
+ {"arc_opa", LV_STYLE_ARC_OPA, STYLE_TYPE_INT },
+ {"text_color", LV_STYLE_TEXT_COLOR, STYLE_TYPE_COLOR },
+ {"text_opa", LV_STYLE_TEXT_OPA, STYLE_TYPE_INT },
+ {"text_font", LV_STYLE_TEXT_FONT, STYLE_TYPE_POINTER }, /* light-userdata */
+ {"text_letter_space", LV_STYLE_TEXT_LETTER_SPACE, STYLE_TYPE_INT },
+ {"text_line_space", LV_STYLE_TEXT_LINE_SPACE, STYLE_TYPE_INT },
+ {"text_decor", LV_STYLE_TEXT_DECOR, STYLE_TYPE_INT },
+ {"text_align", LV_STYLE_TEXT_ALIGN, STYLE_TYPE_INT },
+ {"radius", LV_STYLE_RADIUS, STYLE_TYPE_INT },
+ {"clip_corner", LV_STYLE_CLIP_CORNER, STYLE_TYPE_INT },
+ {"opa", LV_STYLE_OPA, STYLE_TYPE_INT },
+ {"color_filter_opa", LV_STYLE_COLOR_FILTER_OPA, STYLE_TYPE_INT },
+ {"anim_time", LV_STYLE_ANIM_TIME, STYLE_TYPE_INT },
+ {"anim_speed", LV_STYLE_ANIM_SPEED, STYLE_TYPE_INT },
+ {"blend_mode", LV_STYLE_BLEND_MODE, STYLE_TYPE_INT },
+ {"layout", LV_STYLE_LAYOUT, STYLE_TYPE_INT },
+ {"base_dir", LV_STYLE_BASE_DIR, STYLE_TYPE_INT },
+
+ /* need to build pointer from table parameter */
+ {"bg_grad", LV_STYLE_BG_GRAD, STYLE_TYPE_SPECIAL }, /* pointer from table */
+ {"color_filter_dsc", LV_STYLE_COLOR_FILTER_DSC, STYLE_TYPE_SPECIAL }, /**/
+ {"anim", LV_STYLE_ANIM, STYLE_TYPE_SPECIAL }, /* anim para */
+ {"transition", LV_STYLE_TRANSITION, STYLE_TYPE_SPECIAL }, /* transition */
+
+ /* styles combined */
+ {"size", LV_STYLE_SIZE, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"pad_all", LV_STYLE_PAD_ALL, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"pad_ver", LV_STYLE_PAD_VER, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"pad_hor", LV_STYLE_PAD_HOR, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+
+ /* styles for layout */
+ {"flex", _LV_STYLE_FLEX, STYLE_TYPE_SPECIAL | STYLE_TYPE_TABLE},
+ {"flex_flow", _LV_STYLE_FLEX_FLOW, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"flex_main_place", _LV_STYLE_FLEX_MAIN_PLACE,
+ STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"flex_cross_place", _LV_STYLE_FLEX_CROSS_PLACE,
+ STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"flex_track_place", _LV_STYLE_FLEX_TRACK_PLACE,
+ STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+ {"flex_grow", _LV_STYLE_FLEX_GROW, STYLE_TYPE_SPECIAL | STYLE_TYPE_INT },
+};
+
+#define STYLE_MAP_LEN (sizeof(g_style_map) / sizeof(g_style_map[0]))
+
+/**
+ * lv_style
+ */
+
+static luavgl_style_t *luavgl_check_style(lua_State *L, int index)
+{
+ luavgl_style_t *v = *(luavgl_style_t **)luaL_checkudata(L, index, "lv_style");
+ return v;
+}
+
+static void lv_style_set_cb(lv_style_prop_t prop, lv_style_value_t value,
+ void *args)
+{
+ lv_style_t *s = args;
+ lv_style_set_prop(s, prop, value);
+}
+
+static void lv_style_set_inherit_cb(lv_style_prop_t prop,
+ lv_style_value_t value, void *args)
+{
+ lv_style_t *s = args;
+ lv_style_set_prop_meta(s, prop, LV_STYLE_PROP_META_INHERIT);
+}
+
+static uint8_t to_int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ return -1;
+}
+
+static lv_flex_align_t luavgl_to_flex_align(lua_State *L, int idx)
+{
+ if (lua_type(L, idx) != LUA_TSTRING)
+ return LV_FLEX_ALIGN_START;
+
+ const char *str = lua_tostring(L, idx);
+ if (strcmp("flex-start", str) == 0)
+ return LV_FLEX_ALIGN_START;
+
+ if (strcmp("flex-end", str) == 0)
+ return LV_FLEX_ALIGN_END;
+
+ if (strcmp("center", str) == 0)
+ return LV_FLEX_ALIGN_CENTER;
+
+ if (strcmp("space-evenly", str) == 0)
+ return LV_FLEX_ALIGN_SPACE_EVENLY;
+
+ if (strcmp("space-around", str) == 0)
+ return LV_FLEX_ALIGN_SPACE_AROUND;
+
+ if (strcmp("space-between", str) == 0)
+ return LV_FLEX_ALIGN_SPACE_BETWEEN;
+
+ return LV_FLEX_ALIGN_START;
+}
+
+static int luavgl_set_flex_layout_kv(lua_State *L, style_set_cb_t cb,
+ void *args)
+{
+ if (!lua_istable(L, -1)) {
+ debug("para should be table.");
+ return luaL_argerror(L, -1, "should be table.");
+ }
+
+ const char *str;
+ lv_flex_flow_t flow = LV_FLEX_FLOW_ROW;
+ lv_flex_align_t align;
+
+ /**
+ * flex-direction:
+ * row | row-reverse | column | column-reverse
+ */
+ lua_getfield(L, -1, "flex_direction");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ str = lua_tostring(L, -1);
+ /* starts with */
+ if (strncmp("row", str, 3) == 0) {
+ flow = LV_FLEX_FLOW_ROW;
+ } else if (strncmp("column", str, 3) == 0) {
+ flow = LV_FLEX_FLOW_COLUMN;
+ }
+
+ /* if reverse presents */
+ if (strstr(str, "-reverse")) {
+ flow |= _LV_FLEX_REVERSE;
+ }
+ }
+ lua_pop(L, 1);
+
+ /**
+ * flex-wrap:
+ * nowrap | wrap | wrap-reverse;
+ */
+ lua_getfield(L, -1, "flex_wrap");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ str = lua_tostring(L, -1);
+ if (strcmp("wrap", str) == 0) {
+ flow |= _LV_FLEX_WRAP;
+ } else if (strcmp("wrap-reverse", str) == 0) {
+ flow |= _LV_FLEX_WRAP | _LV_FLEX_REVERSE;
+ }
+ /* else: normal */
+ }
+ lua_pop(L, 1);
+
+ lv_style_value_t value = {0};
+ value.num = LV_LAYOUT_FLEX;
+ cb(LV_STYLE_LAYOUT, value, args);
+ value.num = flow;
+ cb(LV_STYLE_FLEX_FLOW, value, args);
+
+ /**
+ * justify-content
+ * flex-start | flex-end | center |
+ * space-between | space-around | space-evenly
+ */
+ lua_getfield(L, -1, "justify_content");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ align = luavgl_to_flex_align(L, -1);
+ value.num = align;
+ cb(LV_STYLE_FLEX_MAIN_PLACE, value, args);
+ }
+ lua_pop(L, 1);
+
+ /**
+ * align-items
+ * flex-start | flex-end | center |
+ * space-between | space-around | space-evenly
+ */
+ lua_getfield(L, -1, "align_items");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ align = luavgl_to_flex_align(L, -1);
+ value.num = align;
+ cb(LV_STYLE_FLEX_CROSS_PLACE, value, args);
+ }
+ lua_pop(L, 1);
+
+ /**
+ * align-content
+ * flex-start | flex-end | center |
+ * space-between | space-around | space-evenly
+ */
+ lua_getfield(L, -1, "align_content");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ align = luavgl_to_flex_align(L, -1);
+ value.num = align;
+ cb(LV_STYLE_FLEX_TRACK_PLACE, value, args);
+ }
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+/* is the style value on stack top is inherit special value */
+static inline bool luavgl_is_style_inherit(lua_State *L)
+{
+ const char *str;
+ return (lua_type(L, -1) == LUA_TSTRING) && (str = lua_tostring(L, -1)) &&
+ (strcmp(str, "inherit") == 0);
+}
+
+/**
+ * internal used API, called from style:set()
+ * key: stack[-2]
+ * value: stack[-1]
+ *
+ * @return 0 if succeed, -1 if failed.
+ */
+static int luavgl_set_style_kv(lua_State *L, style_set_cb_t cb, void *args)
+{
+ const char *key = lua_tostring(L, -2);
+ if (key == NULL) {
+ debug("Null key, ignored.\n");
+ return -1;
+ }
+
+ /* map name to style value. */
+ lv_style_value_t value = {0};
+ const struct style_map_s *p = NULL;
+ for (int i = 0; i < STYLE_MAP_LEN; i++) {
+ if (strcmp(key, g_style_map[i].name) == 0) {
+ p = &g_style_map[i];
+ break;
+ }
+ }
+
+ if (p == NULL) /* not found */
+ return -1;
+
+ style_type_t type = p->type & 0x0f;
+ int v;
+
+ if (!luavgl_is_style_inherit(L)) {
+ /* get normal values */
+ switch (type) {
+ case STYLE_TYPE_INT:
+ v = luavgl_tointeger(L, -1);
+ value.num = v;
+ break;
+ case STYLE_TYPE_COLOR:
+ value.color = luavgl_tocolor(L, -1);
+ break;
+ case STYLE_TYPE_POINTER:
+ value.ptr = lua_touserdata(L, -1);
+ break;
+ case STYLE_TYPE_IMGSRC:
+ value.ptr = luavgl_toimgsrc(L, -1);
+ break;
+ case STYLE_TYPE_TABLE:
+ break;
+ default:
+ /* error, unkown type */
+ return luaL_error(L, "unknown style");
+ }
+ }
+
+ if (p->type & STYLE_TYPE_SPECIAL) {
+ switch ((int)p->prop) {
+ /* style combinations */
+ case LV_STYLE_SIZE:
+ cb(LV_STYLE_WIDTH, value, args);
+ cb(LV_STYLE_HEIGHT, value, args);
+ break;
+
+ case LV_STYLE_PAD_ALL:
+ cb(LV_STYLE_PAD_TOP, value, args);
+ cb(LV_STYLE_PAD_BOTTOM, value, args);
+ cb(LV_STYLE_PAD_LEFT, value, args);
+ cb(LV_STYLE_PAD_RIGHT, value, args);
+ break;
+
+ case LV_STYLE_PAD_VER:
+ cb(LV_STYLE_PAD_TOP, value, args);
+ cb(LV_STYLE_PAD_BOTTOM, value, args);
+ break;
+
+ case LV_STYLE_PAD_HOR:
+ cb(LV_STYLE_PAD_LEFT, value, args);
+ cb(LV_STYLE_PAD_RIGHT, value, args);
+ break;
+
+ case LV_STYLE_PAD_GAP:
+ cb(LV_STYLE_PAD_ROW, value, args);
+ cb(LV_STYLE_PAD_COLUMN, value, args);
+ break;
+
+ /* pointers needs to build from lua stack table */
+ case LV_STYLE_BG_GRAD:
+ break;
+
+ case LV_STYLE_COLOR_FILTER_DSC:
+ break;
+
+ case LV_STYLE_ANIM:
+ break;
+
+ case LV_STYLE_TRANSITION:
+ break;
+
+ /* layout styles that not constant */
+ case _LV_STYLE_FLEX_FLOW:
+ cb(LV_STYLE_FLEX_FLOW, value, args);
+ break;
+ case _LV_STYLE_FLEX_MAIN_PLACE:
+ cb(LV_STYLE_FLEX_MAIN_PLACE, value, args);
+ break;
+ case _LV_STYLE_FLEX_CROSS_PLACE:
+ cb(LV_STYLE_FLEX_CROSS_PLACE, value, args);
+ break;
+ case _LV_STYLE_FLEX_TRACK_PLACE:
+ cb(LV_STYLE_FLEX_TRACK_PLACE, value, args);
+ break;
+ case _LV_STYLE_FLEX_GROW:
+ cb(LV_STYLE_FLEX_GROW, value, args);
+ break;
+
+ case _LV_STYLE_FLEX: {
+ /* value is all on table */
+ luavgl_set_flex_layout_kv(L, cb, args);
+ break;
+ }
+ default:
+ break;
+ }
+ } else if (p->prop <= _LV_STYLE_LAST_BUILT_IN_PROP) {
+ cb(p->prop, value, args);
+ } else {
+ return luaL_error(L, "unknown style");
+ }
+
+ return 0;
+}
+
+/**
+ * style:set({x = 0, y = 0, bg_opa = 123})
+ */
+static int luavgl_style_set(lua_State *L)
+{
+ luavgl_style_t *s = luavgl_check_style(L, 1);
+
+ if (!lua_istable(L, 2)) {
+ luaL_argerror(L, 2, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ lua_pushnil(L); /* nil as initial key to iterate through table */
+ while (lua_next(L, -2)) {
+ /* -1: value, -2: key */
+ if (!lua_isstring(L, -2)) {
+ /* we expect string as key, ignore it if not */
+ debug("ignore non-string key in table.\n");
+ lua_pop(L, 1);
+ continue;
+ }
+
+ /* special value check */
+ bool inherit = luavgl_is_style_inherit(L);
+
+ luavgl_set_style_kv(L, inherit ? lv_style_set_inherit_cb : lv_style_set_cb,
+ s);
+ lua_pop(L, 1); /* remove value, keep the key to continue. */
+ }
+
+ return 0;
+}
+
+/**
+ * luavgl.Style({
+ * bg_color = 0xff0000,
+ * border_width = 1,
+ * })
+ *
+ * For simplicity, style need to be manually deleted `style:delete()` in order
+ * to be gc'ed.
+ */
+static int luavgl_style_create(lua_State *L)
+{
+ luavgl_style_t *s = malloc(sizeof(luavgl_style_t));
+ if (s == NULL) {
+ return luaL_error(L, "No memory.");
+ }
+
+ lv_style_init(&s->style);
+
+ *(void **)lua_newuserdata(L, sizeof(void *)) = s;
+ luaL_getmetatable(L, "lv_style");
+ lua_setmetatable(L, -2);
+
+ lua_pushlightuserdata(L, s);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ lua_rotate(L, 1, 1); /* stack after: style-user-data, para */
+
+ luavgl_style_set(L);
+
+ lua_pop(L, 1); /* remove parameter table */
+ return 1;
+}
+
+/**
+ * style:remove_prop("width")
+ */
+static int luavgl_style_remove_prop(lua_State *L)
+{
+ luavgl_style_t *s = luavgl_check_style(L, 1);
+ const char *name = lua_tostring(L, 2);
+
+ for (int i = 0; i < STYLE_MAP_LEN; i++) {
+ const struct style_map_s *p = &g_style_map[i];
+ if (strcmp(name, p->name) == 0) {
+ lv_style_remove_prop(&s->style, p->prop);
+ return 0;
+ }
+ }
+
+ return luaL_error(L, "unknown prop name: %s", name);
+}
+
+static int luavgl_style_delete(lua_State *L)
+{
+ luavgl_style_t *s = luavgl_check_style(L, 1);
+
+ lua_pushlightuserdata(L, s);
+ lua_pushnil(L);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ return 1;
+}
+
+static int luavgl_style_gc(lua_State *L)
+{
+ luavgl_style_t *s = luavgl_check_style(L, 1);
+ lv_style_reset(&s->style);
+ free(s);
+ debug("gc style:%p\n", s);
+ return 0;
+}
+
+/**
+ * lv_obj_style
+ */
+
+struct obj_style_s {
+ lv_obj_t *obj;
+ int selector;
+};
+
+static void obj_style_set_cb(lv_style_prop_t prop, lv_style_value_t value,
+ void *args)
+{
+ struct obj_style_s *info = args;
+ lv_obj_set_local_style_prop(info->obj, prop, value, info->selector);
+}
+
+static void obj_style_inherit_set_cb(lv_style_prop_t prop,
+ lv_style_value_t value, void *args)
+{
+ struct obj_style_s *info = args;
+ lv_obj_set_local_style_prop_meta(info->obj, prop, LV_STYLE_PROP_META_INHERIT,
+ info->selector);
+}
+
+static int luavgl_obj_set_style_kv(lua_State *L, lv_obj_t *obj, int selector)
+{
+ struct obj_style_s info = {
+ .obj = obj,
+ .selector = selector,
+ };
+
+ /* special value check */
+ bool inherit = luavgl_is_style_inherit(L);
+
+ return luavgl_set_style_kv(
+ L, inherit ? obj_style_inherit_set_cb : obj_style_set_cb, &info);
+}
+
+/**
+ * obj:set_style({x = 0, y = 0, bg_opa = 123}, 0)
+ */
+static int luavgl_obj_set_style(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ if (obj == NULL) {
+ luaL_argerror(L, 1, "obj could already been deleted.");
+ return 0;
+ }
+
+ if (!lua_istable(L, 2)) {
+ luaL_argerror(L, 2, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ int selector = 0;
+ if (!lua_isnoneornil(L, 3)) {
+ selector = lua_tointeger(L, 3);
+ lua_pop(L, 1); /* later we use stack[-1] to get table. */
+ }
+
+ lua_pushnil(L); /* nil as initial key to iterate through table */
+ while (lua_next(L, -2)) {
+ /* -1: value, -2: key */
+ if (!lua_isstring(L, -2)) {
+ /* we expect string as key, ignore it if not */
+ debug("ignore non-string key in table.\n");
+ lua_pop(L, 1);
+ continue;
+ }
+
+ luavgl_obj_set_style_kv(L, obj, selector);
+ lua_pop(L, 1); /* remove value, keep the key to continue. */
+ }
+
+ return 0;
+}
+
+/**
+ * obj:add_style(style, 0)
+ */
+static int luavgl_obj_add_style(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ luavgl_style_t *s = luavgl_check_style(L, 2);
+
+ int selector = 0;
+ if (!lua_isnoneornil(L, 3)) {
+ selector = lua_tointeger(L, 3);
+ }
+
+ lv_obj_add_style(obj, &s->style, selector);
+
+ return 0;
+}
+
+/**
+ * obj:remove_style(style, 0)
+ */
+static int luavgl_obj_remove_style(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ luavgl_style_t *s = luavgl_check_style(L, 2);
+
+ int selector = 0;
+ if (!lua_isnoneornil(L, 3)) {
+ selector = lua_tointeger(L, 3);
+ }
+
+ lv_obj_remove_style(obj, &s->style, selector);
+ return 0;
+}
+
+/**
+ * obj:remove_style_all()
+ */
+static int luavgl_obj_remove_style_all(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ lv_obj_remove_style_all(obj);
+ return 0;
+}
+
+static const luaL_Reg luavgl_style_methods[] = {
+ {"set", luavgl_style_set },
+ {"delete", luavgl_style_delete },
+ {"remove_prop", luavgl_style_remove_prop},
+
+ {NULL, NULL }
+};
+
+static void luavgl_style_init(lua_State *L)
+{
+ luaL_newmetatable(L, "lv_style");
+
+ lua_pushcfunction(L, luavgl_style_gc);
+ lua_setfield(L, -2, "__gc");
+
+ luaL_newlib(L, luavgl_style_methods); /* methods belong to this type */
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop __index table */
+}
diff --git a/lib/luavgl/src/timer.c b/lib/luavgl/src/timer.c
new file mode 100644
index 00000000..4ee334ca
--- /dev/null
+++ b/lib/luavgl/src/timer.c
@@ -0,0 +1,222 @@
+#include "luavgl.h"
+#include "private.h"
+
+typedef struct luavgl_timer_s {
+ lua_State *L;
+ int ref; /* ref to callback */
+} luavgl_timer_t;
+
+static lv_timer_t *luavgl_check_timer(lua_State *L, int index)
+{
+ lv_timer_t *v = *(lv_timer_t **)luaL_checkudata(L, index, "lv_timer");
+ return v;
+}
+
+static void luavgl_timer_cb(lv_timer_t *t)
+{
+ luavgl_timer_t *data = t->user_data;
+ lua_State *L = data->L;
+ int ref = data->ref;
+ if (ref == LUA_NOREF) {
+ return;
+ }
+
+ int top = lua_gettop(L);
+ /* stack: 1. function, 2. timer userdata */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_pushlightuserdata(L, t);
+ lua_rawget(L, LUA_REGISTRYINDEX); /* this should not fail*/
+
+ luavgl_pcall_int(L, 1, 0);
+ lua_settop(L, top);
+}
+
+static void luavgl_timer_set_cb(void *_t, lua_State *L)
+{
+ lv_timer_t *t = _t;
+ int ref = luavgl_check_continuation(L, -1);
+ luavgl_timer_t *data = t->user_data;
+
+ if (data->ref != LUA_NOREF) {
+ luaL_unref(L, LUA_REGISTRYINDEX, data->ref);
+ }
+ data->ref = ref;
+}
+
+static void luavgl_timer_set_paused(lv_timer_t *t, int paused)
+{
+ /* pause or resume. */
+ paused ? lv_timer_pause(t) : lv_timer_resume(t);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t timer_property_table[] = {
+ { "paused", 0, { .setter = (setter_int_t)luavgl_timer_set_paused } },
+ { "period", 0, { .setter = (setter_int_t)lv_timer_set_period } },
+ { "repeat_count", 0, { .setter = (setter_int_t)lv_timer_set_repeat_count } },
+ { "cb", SETTER_TYPE_STACK, { .setter_stack = luavgl_timer_set_cb } },
+};
+
+/* clang-format on */
+
+static int timer_set_para_cb(lua_State *L, void *data)
+{
+ int ret = luavgl_set_property(L, data, timer_property_table);
+
+ if (ret != 0) {
+ debug("failed\n");
+ }
+
+ return ret;
+}
+
+/* setup timer using parameter in table idx */
+int luavgl_timer_setup(lua_State *L, int idx, lv_timer_t *t)
+{
+ luavgl_iterate(L, idx, timer_set_para_cb, t);
+ return 1;
+}
+
+/**
+ * t = luavgl:timer({timer parameters})
+ * t:pause()
+ * t:resume()
+ * t:ready() -- make it ready right now
+ *
+ */
+static int luavgl_timer_create(lua_State *L)
+{
+ luavgl_timer_t *data = malloc(sizeof(luavgl_timer_t));
+ if (data == NULL) {
+ return luaL_error(L, "No memory.");
+ }
+ data->ref = LUA_NOREF;
+ data->L = L;
+
+ lv_timer_t *t = lv_timer_create(luavgl_timer_cb, 0, data);
+
+ *(void **)lua_newuserdata(L, sizeof(void *)) = t;
+ luaL_getmetatable(L, "lv_timer");
+ lua_setmetatable(L, -2);
+
+ lua_pushlightuserdata(L, t);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ /* parameter table is on stack[1] */
+ luavgl_timer_setup(L, 1, t);
+ lua_remove(L, 1); /* remove it to keep stack balance */
+
+ if (t->period == 0) {
+ /* if period is not set, then it's paused. */
+ lv_timer_pause(t);
+ }
+
+ return 1;
+}
+
+static int luavgl_timer_set(lua_State *L)
+{
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ if (t == NULL) {
+ return luaL_argerror(L, 1, "timer is null.");
+ }
+
+ luavgl_timer_setup(L, 2, t);
+ return 0;
+}
+
+static int luavgl_timer_ready(lua_State *L)
+{
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ if (t == NULL) {
+ return luaL_argerror(L, 1, "timer is null.");
+ }
+
+ lv_timer_ready(t);
+ return 0;
+}
+
+static int luavgl_timer_resume(lua_State *L)
+{
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ if (t == NULL) {
+ return luaL_argerror(L, 1, "timer is null.");
+ }
+
+ lv_timer_resume(t);
+ return 0;
+}
+
+static int luavgl_timer_pause(lua_State *L)
+{
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ if (t == NULL) {
+ return luaL_argerror(L, 1, "timer is null.");
+ }
+
+ lv_timer_pause(t);
+
+ return 0;
+}
+
+/* remove timer from obj, */
+static int luavgl_timer_delete(lua_State *L)
+{
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ if (t == NULL) {
+ return luaL_argerror(L, 1, "timer is null.");
+ }
+
+ luavgl_timer_t *data = t->user_data;
+ if (data->ref) {
+ luaL_unref(L, LUA_REGISTRYINDEX, data->ref);
+ data->ref = LUA_NOREF;
+ }
+
+ lua_pushlightuserdata(L, t);
+ lua_pushnil(L);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ /* we can only release memory in gc, since we need t->use_data */
+ lv_timer_pause(t);
+
+ debug("delete timer:%p\n", t);
+ return 0;
+}
+
+static int luavgl_timer_gc(lua_State *L)
+{
+ /* stop timer if not stopped. */
+ luavgl_timer_delete(L);
+
+ lv_timer_t *t = luavgl_check_timer(L, 1);
+ free(t->user_data);
+ lv_timer_del(t);
+
+ debug("gc timer:%p\n", t);
+ return 0;
+}
+
+static const luaL_Reg luavgl_timer_methods[] = {
+ {"set", luavgl_timer_set },
+ {"pause", luavgl_timer_pause },
+ {"resume", luavgl_timer_resume},
+ {"delete", luavgl_timer_delete},
+ {"ready", luavgl_timer_ready },
+
+ {NULL, NULL }
+};
+
+static void luavgl_timer_init(lua_State *L)
+{
+ luaL_newmetatable(L, "lv_timer");
+
+ lua_pushcfunction(L, luavgl_timer_gc);
+ lua_setfield(L, -2, "__gc");
+
+ luaL_newlib(L, luavgl_timer_methods); /* methods belong to this type */
+ lua_setfield(L, -2, "__index");
+
+ lua_pop(L, 1); /* pop __index table */
+}
diff --git a/lib/luavgl/src/util.c b/lib/luavgl/src/util.c
new file mode 100644
index 00000000..2042a6d9
--- /dev/null
+++ b/lib/luavgl/src/util.c
@@ -0,0 +1,413 @@
+#include "luavgl.h"
+#include "private.h"
+
+LUALIB_API luavgl_obj_t *luavgl_to_lobj(lua_State *L, int idx)
+{
+ luavgl_obj_t *lobj = lua_touserdata(L, idx);
+ if (lobj == NULL) {
+ goto fail;
+ }
+
+ if (lobj->obj == NULL) {
+ /* could be already deleted, but not gc'ed */
+ return NULL;
+ }
+
+ return lobj;
+
+fail:
+ debug("arg not lv_obj userdata.\n");
+ luaL_argerror(L, idx, "Expected lv_obj userdata");
+ return NULL;
+}
+
+LUALIB_API int luavgl_is_callable(lua_State *L, int index)
+{
+ if (luaL_getmetafield(L, index, "__call") != LUA_TNIL) {
+ /* getmetatable(x).__call must be a function for x() to work */
+ int callable = lua_isfunction(L, -1);
+ lua_pop(L, 1);
+ return callable;
+ }
+ return lua_isfunction(L, index);
+}
+
+static void luavgl_check_callable(lua_State *L, int index)
+{
+ if (luavgl_is_callable(L, index))
+ return;
+
+ luaL_argerror(L, index, "function or callable table expected");
+}
+
+static int luavgl_check_continuation(lua_State *L, int index)
+{
+ if (lua_isnoneornil(L, index))
+ return LUA_NOREF;
+
+ luavgl_check_callable(L, index);
+ lua_pushvalue(L, index);
+ return luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+static void dumpvalue(lua_State *L, int i, bool cr)
+{
+ const char ending = cr ? '\n' : '\0';
+ switch (lua_type(L, i)) {
+ case LUA_TNUMBER:
+ printf("number: %g%c", lua_tonumber(L, i), ending);
+ break;
+ case LUA_TSTRING:
+ printf("string: %s%c", lua_tostring(L, i), ending);
+ break;
+ case LUA_TBOOLEAN:
+ printf("boolean: %s%c", (lua_toboolean(L, i) ? "true" : "false"), ending);
+ break;
+ case LUA_TNIL:
+ printf("nil: %s%c", "nil", ending);
+ break;
+ default:
+ printf("pointer: %p%c", lua_topointer(L, i), ending);
+ break;
+ }
+}
+
+static void dumptable(lua_State *L, int index)
+{
+ int i = index < 0 ? index - 1 : index;
+ lua_pushnil(L); /* nil as initial key to iterate through table */
+ while (lua_next(L, i)) {
+ /* -1: value, -2: key */
+ dumpvalue(L, -2, 0);
+ printf(" ");
+ dumpvalue(L, -1, 1);
+ lua_pop(L, 1); /* remove value, keep the key to continue. */
+ }
+ fflush(stdout);
+}
+
+static void dumpstack(lua_State *L)
+{
+ int top = lua_gettop(L);
+ printf("\n");
+ for (int i = 1; i <= top; i++) {
+ printf("%d\t%s\t", i, luaL_typename(L, i));
+ switch (lua_type(L, i)) {
+ case LUA_TNUMBER:
+ printf("number: %g\n", lua_tonumber(L, i));
+ break;
+ case LUA_TSTRING:
+ printf("string: %s\n", lua_tostring(L, i));
+ break;
+ case LUA_TBOOLEAN:
+ printf("boolean: %s\n", (lua_toboolean(L, i) ? "true" : "false"));
+ break;
+ case LUA_TNIL:
+ printf("nil: %s\n", "nil");
+ break;
+ default:
+ printf("pointer: %p\n", lua_topointer(L, i));
+ break;
+ }
+ }
+ fflush(stdout);
+}
+
+/**
+ * Create a table(used as object metatable), using clz as key in lua
+ * registry. The name is set to metatable.__name if not NULL
+ */
+LUALIB_API int luavgl_obj_createmetatable(lua_State *L,
+ const lv_obj_class_t *clz,
+ const char *name, const luaL_Reg *l,
+ int n)
+{
+ if (luavgl_obj_getmetatable(L, clz) != LUA_TNIL) /* meta already exists */
+ return 0; /* leave previous value on top, but return 0 */
+ lua_pop(L, 1);
+
+ /* create metatable, 4 elements, normally for __magic, __index, __gc and
+ * __name. */
+ lua_createtable(L, 0, 4);
+ if (name) {
+ lua_pushstring(L, name);
+ lua_setfield(L, -2, "__name"); /* metatable.__name = tname */
+ }
+
+ /** A magic string we used to check if userdata is a luavgl object. */
+ lua_pushstring(L, "luavglObj");
+ lua_setfield(L, -2, "__magic");
+
+ /* add to registry */
+ lua_pushlightuserdata(L, (void *)clz);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX); /* registry[clz] = metatable */
+
+ /* New index table.
+ * M = {} -- stack top
+ *
+ * t = {l} -- table contains the lib functions
+ * b = getmatatable(clz.base_clz)
+ * setmetatable(t, b)
+ * M.__index = t
+ */
+ lua_createtable(L, 0, n); /* t = {} */
+ luaL_setfuncs(L, l, 0); /* set methods to t */
+ if (clz != &lv_obj_class) {
+ /* b = getmatatable(clz.base_clz) */
+ if (luavgl_obj_getmetatable(L, clz->base_class) == LUA_TNIL) {
+ return luaL_error(L, "need to init base class firstly: %s.", name);
+ }
+
+ /* setmetatable(t, b) */
+ lua_setmetatable(L, -2);
+ }
+
+ lua_setfield(L, -2, "__index"); /* M.__index = t */
+ return 1;
+}
+
+int luavgl_obj_getmetatable(lua_State *L, const lv_obj_class_t *clz)
+{
+ lua_pushlightuserdata(L, (void *)clz);
+ return lua_rawget(L, LUA_REGISTRYINDEX);
+}
+
+/**
+ * get metatable of clz, and set it as the metatable of table on stack idx
+ * return 0 if failed.
+ */
+int luavgl_obj_setmetatable(lua_State *L, int idx, const lv_obj_class_t *clz)
+{
+ if (luavgl_obj_getmetatable(L, clz) == LUA_TNIL) {
+ lua_pop(L, 1);
+ return 0;
+ }
+
+ lua_setmetatable(L, idx);
+ return 1;
+}
+
+static int msghandler(lua_State *L)
+{
+ if (!lua_isstring(L, 1)) /* 'message' not a string? */
+ return 1; /* keep it intact */
+
+ const char *msg = lua_tostring(L, 1);
+ if (msg == NULL) { /* is error object not a string? */
+ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
+ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
+ return 1; /* that is the message */
+ else
+ msg = lua_pushfstring(L, "(error object is a %s value)",
+ luaL_typename(L, 1));
+ }
+
+ /* append a standard traceback */
+ luaL_traceback(L, L, msg, 1);
+
+ /* show it on screen */
+ lv_obj_t *root = NULL;
+ luavgl_ctx_t *ctx = luavgl_context(L);
+ root = lv_obj_create(ctx->root ? ctx->root : lv_scr_act());
+ lv_obj_set_size(root, LV_PCT(80), LV_PCT(80));
+ lv_obj_center(root);
+
+ lv_obj_set_style_bg_color(root, lv_color_black(), 0);
+ lv_obj_set_style_bg_opa(root, LV_OPA_70, 0);
+ lv_obj_set_style_border_width(root, 1, 0);
+
+ lv_obj_t *label = lv_label_create(root);
+ lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
+ lv_obj_set_style_text_font(label, LV_FONT_DEFAULT, 0);
+ lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_LIGHT_GREEN),
+ 0);
+ lv_obj_set_width(label, LV_PCT(80));
+ lv_obj_center(label);
+
+ lv_label_set_text(label, lua_tostring(L, -1));
+ return 1;
+}
+
+LUALIB_API int luavgl_pcall(lua_State *L, int nargs, int nresult)
+{
+ int base = lua_gettop(L) - nargs; /* function index */
+ lua_pushcfunction(L, msghandler); /* push message handler */
+ lua_insert(L, base); /* put it under function and args */
+ int status = lua_pcall(L, nargs, nresult, base);
+ if (status != LUA_OK) {
+ debug("crashed\n%s", lua_tostring(L, -1));
+ }
+ lua_remove(L, base); /* remove message handler from the stack */
+
+ return status;
+}
+
+LUALIB_API void *luavgl_test_obj(lua_State *L, int ud)
+{
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ lua_getfield(L, -1, "__magic"); /* get the magic string */
+ lua_pushstring(L, "luavglObj");
+ /* strings are interned, so no need to use strcmp. */
+ if (!lua_rawequal(L, -1, -2)) /* not the same? */
+ p = NULL; /* value is a userdata with wrong metatable */
+ lua_pop(L, 3); /* remove metatable and two strings */
+ return p;
+ }
+ }
+
+ return NULL; /* value is not a userdata with a metatable */
+}
+
+LUALIB_API lv_obj_t *luavgl_to_obj(lua_State *L, int idx)
+{
+ luavgl_obj_t *lobj = luavgl_test_obj(L, idx);
+ if (lobj == NULL || lobj->obj == NULL) {
+ luaL_argerror(L, idx, "expect lua lvgl object, got null");
+ return NULL;
+ }
+
+ return lobj->obj;
+}
+
+LUALIB_API int luavgl_tointeger(lua_State *L, int idx)
+{
+ int v = 0;
+ if (lua_isboolean(L, idx)) {
+ v = lua_toboolean(L, idx);
+ } else if (lua_isinteger(L, idx)) {
+ v = lua_tointeger(L, idx);
+ } else {
+ v = lua_tonumber(L, idx);
+ }
+
+ return v;
+}
+
+LUALIB_API lv_color_t luavgl_tocolor(lua_State *L, int idx)
+{
+ lv_color_t color = {0};
+ if (lua_type(L, idx) == LUA_TSTRING) {
+ /* support #RGB and #RRGGBB */
+ const char *s = lua_tostring(L, idx);
+ if (s == NULL) {
+ luaL_error(L, "unknown color.");
+ return color;
+ }
+
+ int len = strlen(s);
+ if (len == 4 && s[0] == '#') {
+ /* #RGB */
+ int r = to_int(s[1]);
+ r |= r << 4;
+ int g = to_int(s[2]);
+ g |= g << 4;
+ int b = to_int(s[3]);
+ b |= b << 4;
+ color = lv_color_make(r, g, b);
+ } else if (len == 7 && s[0] == '#') {
+ /* #RRGGBB */
+ int r = (to_int(s[1]) << 4) | to_int(s[2]);
+ int g = (to_int(s[3]) << 4) | to_int(s[4]);
+ int b = (to_int(s[5]) << 4) | to_int(s[6]);
+ color = lv_color_make(r, g, b);
+ } else {
+ luaL_error(L, "unknown color format.");
+ return color;
+ }
+ } else {
+ color = lv_color_hex(luavgl_tointeger(L, idx)); /* make to lv_color_t */
+ }
+
+ return color;
+}
+
+LUALIB_API const char *luavgl_toimgsrc(lua_State *L, int idx)
+{
+ const char *src = NULL;
+ if (lua_isuserdata(L, idx)) {
+ src = lua_touserdata(L, idx);
+ debug("set img src to user data: %p\n", src);
+ } else if (lua_isstring(L, idx)) {
+ src = lua_tostring(L, idx);
+ } else {
+ debug("img src should be string or userdata.\n");
+ return NULL;
+ }
+
+ return src;
+}
+
+LUALIB_API void luavgl_iterate(lua_State *L, int index,
+ int (*cb)(lua_State *, void *), void *cb_para)
+{
+ int i = index < 0 ? index - 1 : index;
+ lua_pushnil(L); /* nil as initial key to iterate through table */
+ while (lua_next(L, i)) {
+ /* -1: value, -2: key */
+ if (!lua_isstring(L, -2)) {
+ /* we expect string as key, ignore it if not */
+ debug("ignore non-string key in table.\n");
+ lua_pop(L, 1);
+ continue;
+ }
+
+ cb(L, cb_para);
+ lua_pop(L, 1); /* remove value, keep the key to continue. */
+ }
+}
+
+LUALIB_API int luavgl_set_property_array(lua_State *L, void *obj,
+ const luavgl_value_setter_t table[],
+ uint32_t len)
+{
+ const char *key = lua_tostring(L, -2);
+ if (key == NULL) {
+ debug("Null key, ignored.\n");
+ return -1;
+ }
+
+ for (int i = 0; i < len; i++) {
+ const luavgl_value_setter_t *p = &table[i];
+ if (strcmp(key, p->key))
+ continue;
+
+ if (p->type == SETTER_TYPE_INT) {
+ int v;
+ if (lua_isboolean(L, -1)) {
+ v = lua_toboolean(L, -1);
+ } else if (lua_isinteger(L, -1)) {
+ v = lua_tointeger(L, -1);
+ } else {
+ v = lua_tonumber(L, -1);
+ }
+ p->setter(obj, v);
+ } else if (p->type == SETTER_TYPE_COLOR) {
+ /* color */
+ lv_color_t color = luavgl_tocolor(L, -1);
+ p->setter(obj, color.full);
+ } else if (p->type == SETTER_TYPE_IMGSRC) {
+ /* img src */
+ p->setter_pointer(obj, (void *)luavgl_toimgsrc(L, -1));
+ } else if (p->type == SETTER_TYPE_STACK) {
+ p->setter_stack(obj, L);
+ } else if (p->type == SETTER_TYPE_POINTER) {
+ void *data = lua_touserdata(L, -1);
+ p->setter_pointer(obj, data);
+ } else {
+ debug("unsupported type: %d\n", p->type);
+ }
+ return 0;
+ }
+
+ return -1; /* property not found */
+}
+
+LUALIB_API void _lv_dummy_set(void *obj, lua_State *L) {}
+
+static int luavgl_pcall_int(lua_State *L, int nargs, int nresult)
+{
+ return luavgl_context(L)->pcall(L, nargs, nresult);
+}
diff --git a/lib/luavgl/src/widgets/calendar.c b/lib/luavgl/src/widgets/calendar.c
new file mode 100644
index 00000000..2fb8fd9b
--- /dev/null
+++ b/lib/luavgl/src/widgets/calendar.c
@@ -0,0 +1,169 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_calendar_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_calendar_create);
+}
+
+static void _lv_calendar_set_today(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ uint32_t year, month, day;
+ lua_getfield(L, -1, "year");
+ year = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "month");
+ month = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "day");
+ day = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_calendar_set_today_date(obj, year, month, day);
+}
+
+static void _lv_calendar_set_showed(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "expect date table.");
+ return;
+ }
+
+ uint32_t year, month;
+ lua_getfield(L, -1, "year");
+ year = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "month");
+ month = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_calendar_set_showed_date(obj, year, month);
+}
+
+static const luavgl_value_setter_t calendar_property_table[] = {
+ {"today", SETTER_TYPE_STACK, {.setter_stack = _lv_calendar_set_today} },
+ {"showed", SETTER_TYPE_STACK, {.setter_stack = _lv_calendar_set_showed}},
+};
+
+LUALIB_API int luavgl_calendar_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, calendar_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for calendar.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_calendar_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_calendar_set_property_kv, obj);
+
+ return 0;
+}
+
+static inline int calendar_new_date(lua_State *L,
+ const lv_calendar_date_t *date)
+{
+ lua_createtable(L, 0, 3);
+ lua_pushinteger(L, date->year);
+ lua_setfield(L, -2, "year");
+
+ lua_pushinteger(L, date->month);
+ lua_setfield(L, -2, "month");
+
+ lua_pushinteger(L, date->day);
+ lua_setfield(L, -2, "day");
+
+ return 1;
+}
+
+static int luavgl_calendar_get_today(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ const lv_calendar_date_t *date = lv_calendar_get_today_date(obj);
+
+ return calendar_new_date(L, date);
+}
+
+static int luavgl_calendar_get_showed(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ const lv_calendar_date_t *date = lv_calendar_get_showed_date(obj);
+
+ return calendar_new_date(L, date);
+}
+
+static int luavgl_calendar_get_pressed(lua_State *L)
+{
+ lv_calendar_date_t date;
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_calendar_get_pressed_date(obj, &date);
+
+ return calendar_new_date(L, &date);
+}
+
+static int luavgl_calendar_get_btnm(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_obj_t *btm = lv_calendar_get_btnmatrix(obj);
+ lua_pushlightuserdata(L, btm);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ luavgl_add_lobj(L, btm)->lua_created = false;
+ }
+
+ return 1; /* obj userdata is already on stack */
+}
+
+static int luavgl_calendar_create_arrow(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_calendar_header_arrow_create);
+}
+
+static int luavgl_calendar_create_dropdown(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_calendar_header_dropdown_create);
+}
+
+static const luaL_Reg luavgl_calendar_methods[] = {
+ {"set", luavgl_calendar_set },
+ {"get_today", luavgl_calendar_get_today },
+ {"get_showed", luavgl_calendar_get_showed },
+ {"get_pressed", luavgl_calendar_get_pressed },
+ {"get_btnm", luavgl_calendar_get_btnm },
+ {"Arrow", luavgl_calendar_create_arrow },
+ {"Dropdown", luavgl_calendar_create_dropdown},
+
+ {NULL, NULL },
+};
+
+static void luavgl_calendar_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_calendar_class, "lv_calendar",
+ luavgl_calendar_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/checkbox.c b/lib/luavgl/src/widgets/checkbox.c
new file mode 100644
index 00000000..e7885c8e
--- /dev/null
+++ b/lib/luavgl/src/widgets/checkbox.c
@@ -0,0 +1,74 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_checkbox_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_checkbox_create);
+}
+
+static void _lv_checkbox_set_txt(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "only support string for text.");
+ return;
+ }
+
+ lv_checkbox_set_text(obj, lua_tostring(L, -1));
+}
+
+static const luavgl_value_setter_t checkbox_property_table[] = {
+ {"text", SETTER_TYPE_STACK, {.setter_stack = _lv_checkbox_set_txt}},
+};
+
+LUALIB_API int luavgl_checkbox_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, checkbox_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for checkbox.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_checkbox_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_checkbox_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_checkbox_get_text(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushstring(L, lv_checkbox_get_text(obj));
+ return 1;
+}
+
+static const luaL_Reg luavgl_checkbox_methods[] = {
+ {"set", luavgl_checkbox_set },
+ {"get_text", luavgl_checkbox_get_text},
+
+ {NULL, NULL },
+};
+
+static void luavgl_checkbox_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_checkbox_class, "lv_checkbox",
+ luavgl_checkbox_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/dropdown.c b/lib/luavgl/src/widgets/dropdown.c
new file mode 100644
index 00000000..a211c10b
--- /dev/null
+++ b/lib/luavgl/src/widgets/dropdown.c
@@ -0,0 +1,188 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_dropdown_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_dropdown_create);
+}
+
+static void _lv_dropdown_set_text(void *obj, lua_State *L)
+{
+ const char *str = lua_tostring(L, -1);
+
+ lv_dropdown_set_text(obj, str);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t dropdown_property_table[] = {
+ {"text", SETTER_TYPE_STACK, {.setter_stack = _lv_dropdown_set_text}},
+ {"options", SETTER_TYPE_STACK, {.setter_stack = _lv_dummy_set}},
+ {"selected", 0, {.setter = (setter_int_t)lv_dropdown_set_selected}},
+ {"dir", 0, {.setter = (setter_int_t)lv_dropdown_set_dir}},
+ {"symbol", SETTER_TYPE_IMGSRC, {.setter_pointer = (setter_pointer_t)lv_dropdown_set_symbol}},
+ {"highlight", 0, {.setter = (setter_int_t)lv_dropdown_set_selected_highlight}},
+};
+/* clang-format on */
+
+LUALIB_API int luavgl_dropdown_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, dropdown_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for dropdown.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_dropdown_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ /* set options firstly, otherwise set selected may fail */
+ lua_getfield(L, -1, "options");
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ lv_dropdown_set_options(obj, lua_tostring(L, -1));
+ }
+ lua_pop(L, 1);
+
+ luavgl_iterate(L, -1, luavgl_dropdown_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_dropdown_get(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (lua_type(L, 2) != LUA_TSTRING) {
+ return luaL_argerror(L, 2, "expect string");
+ }
+
+ const char *key = lua_tostring(L, 2);
+ if (strcmp(key, "list") == 0) {
+ lv_obj_t *list = lv_dropdown_get_list(obj);
+ lua_pushlightuserdata(L, list);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (lua_isnoneornil(L, -1)) {
+ lua_pop(L, 1);
+ luavgl_add_lobj(L, list)->lua_created = false;
+ }
+ return 1;
+ }
+
+ if (strcmp(key, "text") == 0) {
+ lua_pushstring(L, lv_dropdown_get_text(obj));
+ return 1;
+ }
+
+ if (strcmp(key, "options") == 0) {
+ lua_pushstring(L, lv_dropdown_get_options(obj));
+ return 1;
+ }
+
+ if (strcmp(key, "selected") == 0) {
+ lua_pushinteger(L, lv_dropdown_get_selected(obj));
+ return 1;
+ }
+
+ if (strcmp(key, "option_cnt") == 0) {
+ lua_pushinteger(L, lv_dropdown_get_option_cnt(obj));
+ return 1;
+ }
+
+ if (strcmp(key, "selected_str") == 0) {
+ char buf[64];
+ lv_dropdown_get_selected_str(obj, buf, sizeof(buf));
+ lua_pushstring(L, buf);
+ return 1;
+ }
+
+ if (strcmp(key, "option_index") == 0) {
+ const char *option = lua_tostring(L, 3);
+ lua_pushinteger(L, lv_dropdown_get_option_index(obj, option));
+ return 1;
+ }
+
+ if (strcmp(key, "symbol") == 0) {
+ lua_pushlightuserdata(L, (void *)lv_dropdown_get_symbol(obj));
+ return 1;
+ }
+
+ if (strcmp(key, "dir") == 0) {
+ lua_pushinteger(L, lv_dropdown_get_dir(obj));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int luavgl_dropdown_open(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_dropdown_open(obj);
+ return 0;
+}
+
+static int luavgl_dropdown_close(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_dropdown_close(obj);
+ return 0;
+}
+
+static int luavgl_dropdown_is_open(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushboolean(L, lv_dropdown_is_open(obj));
+ return 1;
+}
+
+static int luavgl_dropdown_add_option(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ const char *str = lua_tostring(L, 2);
+ uint32_t pos = lua_tointeger(L, 3);
+ lv_dropdown_add_option(obj, str, pos);
+
+ return 0;
+}
+
+static int luavgl_dropdown_clear_option(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ lv_dropdown_clear_options(obj);
+ return 0;
+}
+
+static const luaL_Reg luavgl_dropdown_methods[] = {
+ {"set", luavgl_dropdown_set },
+ {"get", luavgl_dropdown_get },
+ {"open", luavgl_dropdown_open },
+ {"close", luavgl_dropdown_close },
+ {"is_open", luavgl_dropdown_is_open },
+ {"add_option", luavgl_dropdown_add_option },
+ {"clear_option", luavgl_dropdown_clear_option},
+
+ {NULL, NULL },
+};
+
+static void luavgl_dropdown_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_dropdown_class, "lv_dropdown",
+ luavgl_dropdown_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/img.c b/lib/luavgl/src/widgets/img.c
new file mode 100644
index 00000000..9332cd99
--- /dev/null
+++ b/lib/luavgl/src/widgets/img.c
@@ -0,0 +1,196 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_img_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_img_create);
+}
+
+static void _lv_img_set_pivot(void *obj, lua_State *L)
+{
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "should be table.");
+ debug("para should be table.");
+ return;
+ }
+
+ lua_getfield(L, -1, "x");
+ lv_coord_t x = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "y");
+ lv_coord_t y = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lv_img_set_pivot(obj, x, y);
+}
+
+static const luavgl_value_setter_t img_property_table[] = {
+ {"src",
+ SETTER_TYPE_IMGSRC, {.setter_pointer = (setter_pointer_t)lv_img_set_src}},
+ {"offset_x", 0, {.setter = (setter_int_t)lv_img_set_offset_x} },
+ {"offset_y", 0, {.setter = (setter_int_t)lv_img_set_offset_y} },
+ {"angle", 0, {.setter = (setter_int_t)lv_img_set_angle} },
+ {"zoom", 0, {.setter = (setter_int_t)lv_img_set_zoom} },
+ {"antialias", 0, {.setter = (setter_int_t)lv_img_set_antialias} },
+ {"pivot", SETTER_TYPE_STACK, {.setter_stack = _lv_img_set_pivot} },
+};
+
+LUALIB_API int luavgl_img_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, img_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for image.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_img_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ /* lvgl requires img src should be set firstly */
+ lua_getfield(L, -1, "src");
+ if (!lua_isnoneornil(L, -1)) {
+ const char *src = NULL;
+ if (lua_isuserdata(L, -1)) {
+ src = lua_touserdata(L, -1);
+ debug("set img src to user data: %p\n", src);
+ } else {
+ src = lua_tostring(L, -1);
+ }
+ lv_img_set_src(obj, src);
+ }
+ lua_pop(L, 1);
+
+ luavgl_iterate(L, -1, luavgl_img_set_property_kv, obj);
+
+ return 0;
+}
+
+/**
+ * img.set_src(img, "path")
+ */
+static int luavgl_img_set_src(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ const char *src = luavgl_toimgsrc(L, 2);
+ if (src != NULL) {
+ lv_img_set_src(obj, src);
+ }
+
+ return 0;
+}
+
+/**
+ * img:set_offset({x=10})
+ * img:set_offset({x=10})
+ * img:set_offset({x=10, y=100})
+ */
+static int luavgl_img_set_offset(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "should be table {x=0,y=0,anim=true}");
+ }
+
+ lv_coord_t v;
+ lua_getfield(L, -1, "x");
+ if (!lua_isnil(L, -1)) {
+ v = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lv_img_set_offset_x(obj, v);
+ }
+
+ lua_getfield(L, -1, "y");
+ if (!lua_isnil(L, -1)) {
+ v = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lv_img_set_offset_y(obj, v);
+ }
+
+ return 0;
+}
+
+/**
+ * img:set_pivot({x=10, y=100})
+ */
+static int luavgl_img_set_pivot(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_argerror(L, -1, "should be table {x=0,y=0,anim=true}");
+ }
+
+ lv_coord_t x = 0, y = 0;
+ lua_getfield(L, -1, "x");
+ x = lua_tointeger(L, -1);
+
+ lua_getfield(L, -1, "y");
+ y = lua_tointeger(L, -1);
+
+ lv_img_set_pivot(obj, x, y);
+
+ return 0;
+}
+
+/**
+ * return image size w, h
+ * img:get_img_size() -- get size of this image
+ * img:get_img_size("src") -- get size of img "src"
+ */
+static int luavgl_get_img_size(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ const void *src = NULL;
+ if (lua_isnoneornil(L, 2)) {
+ src = lv_img_get_src(obj);
+ } else {
+ src = luavgl_toimgsrc(L, 2);
+ }
+
+ lv_img_header_t header;
+ if (src == NULL || lv_img_decoder_get_info(src, &header) != LV_RES_OK) {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, header.w);
+ lua_pushinteger(L, header.h);
+ }
+
+ return 2;
+}
+
+static const luaL_Reg luavgl_img_methods[] = {
+ {"set", luavgl_img_set },
+
+ {"set_src", luavgl_img_set_src },
+ {"set_offset", luavgl_img_set_offset},
+ {"set_pivot", luavgl_img_set_pivot },
+ {"get_img_size", luavgl_get_img_size },
+
+ {NULL, NULL },
+};
+
+static void luavgl_img_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_img_class, "lv_img", luavgl_img_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/keyboard.c b/lib/luavgl/src/widgets/keyboard.c
new file mode 100644
index 00000000..e7121de0
--- /dev/null
+++ b/lib/luavgl/src/widgets/keyboard.c
@@ -0,0 +1,76 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_keyboard_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_keyboard_create);
+}
+
+static void _lv_keyboard_set_textarea(void *obj, lua_State *L)
+{
+ lv_obj_t *ta = luavgl_to_obj(L, -1);
+ if (ta->class_p != &lv_textarea_class) {
+ luaL_argerror(L, -1, "expect textarea obj");
+ return;
+ }
+ lv_keyboard_set_textarea(obj, ta);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t keyboard_property_table[] = {
+ {"textarea", SETTER_TYPE_STACK, {.setter_stack = _lv_keyboard_set_textarea}},
+ {"mode", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_keyboard_set_mode}},
+ {"popovers", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_keyboard_set_popovers}},
+};
+/* clang-format on */
+
+LUALIB_API int luavgl_keyboard_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, keyboard_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for keyboard.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_keyboard_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_keyboard_set_property_kv, obj);
+
+ return 0;
+}
+
+static const luaL_Reg luavgl_keyboard_methods[] = {
+ {"set", luavgl_keyboard_set},
+
+ {NULL, NULL },
+};
+
+static void luavgl_keyboard_init(lua_State *L)
+{
+ static const luaL_Reg btm_methods[] = {
+ {NULL, NULL},
+ };
+ luavgl_obj_newmetatable(L, &lv_btnmatrix_class, "lv_btnm", btm_methods);
+ lua_pop(L, 1);
+
+ luavgl_obj_newmetatable(L, &lv_keyboard_class, "lv_keyboard",
+ luavgl_keyboard_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/label.c b/lib/luavgl/src/widgets/label.c
new file mode 100644
index 00000000..701c56f3
--- /dev/null
+++ b/lib/luavgl/src/widgets/label.c
@@ -0,0 +1,155 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_label_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_label_create);
+}
+
+static void _lv_label_set_txt(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "only support string for text.");
+ return;
+ }
+
+ lv_label_set_text(obj, lua_tostring(L, -1));
+}
+
+static const luavgl_value_setter_t label_property_table[] = {
+ {"text", SETTER_TYPE_STACK, {.setter_stack = _lv_label_set_txt} },
+ {"long_mode", 0, {.setter = (setter_int_t)lv_label_set_long_mode} },
+ {"recolor", 0, {.setter = (setter_int_t)lv_label_set_recolor} },
+#if LVGL_VERSION_MAJOR == 9
+ {"text_selection_start",
+ 0, {.setter = (setter_int_t)lv_label_set_text_selection_start}},
+ {"text_selection_end",
+ 0, {.setter = (setter_int_t)lv_label_set_text_selection_end} },
+#else
+ {"text_selection_start",
+ 0,
+ {.setter = (setter_int_t)lv_label_set_text_sel_start}},
+ {"text_selection_end",
+ 0,
+ {.setter = (setter_int_t)lv_label_set_text_sel_end}},
+#endif
+};
+
+LUALIB_API int luavgl_label_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, label_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for label.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_label_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_label_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_label_get_text(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushstring(L, lv_label_get_text(obj));
+ return 1;
+}
+
+static int luavgl_label_get_long_mode(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_label_get_long_mode(obj));
+ return 1;
+}
+
+static int luavgl_label_get_recolor(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_label_get_recolor(obj));
+ return 1;
+}
+
+static int luavgl_label_ins_text(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ uint32_t pos = luavgl_tointeger(L, 2);
+ const char *txt = lua_tostring(L, 3);
+
+ lv_label_ins_text(obj, pos, txt);
+ return 0;
+}
+
+static int luavgl_label_cut_text(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ uint32_t pos = luavgl_tointeger(L, 2);
+ uint32_t cnt = luavgl_tointeger(L, 3);
+
+ lv_label_cut_text(obj, pos, cnt);
+ return 0;
+}
+
+/* demo purpose, there is no need to use set_text_static */
+static int luavgl_label_set_text_static(lua_State *L)
+{
+ const char *str = lua_tostring(L, 2);
+ luavgl_obj_t *lobj = luavgl_to_lobj(L, 1);
+ if (lobj->obj == NULL) {
+ return luaL_error(L, "obj null.");
+ }
+
+ luavgl_obj_getuserdatauv(L, 1);
+
+ /* uservalue is on top */
+ lua_pushvalue(L, 2);
+ lua_setfield(L, -2, "text_static");
+
+ lv_label_set_text_static(lobj->obj, str);
+ return 0;
+}
+
+static int luavgl_label_tostring(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushfstring(L, "lv_label:%p, text: %s", obj, lv_label_get_text(obj));
+ return 1;
+}
+
+static const luaL_Reg luavgl_label_methods[] = {
+ {"set", luavgl_label_set },
+ {"set_text_static", luavgl_label_set_text_static},
+ {"get_text", luavgl_label_get_text },
+ {"get_long_mode", luavgl_label_get_long_mode },
+ {"get_recolor", luavgl_label_get_recolor },
+ {"ins_text", luavgl_label_ins_text },
+ {"cut_text", luavgl_label_cut_text },
+
+ {NULL, NULL },
+};
+
+static void luavgl_label_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_label_class, "lv_label", luavgl_label_methods);
+ lua_pushcfunction(L, luavgl_label_tostring);
+ lua_setfield(L, -2, "__tostring");
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/led.c b/lib/luavgl/src/widgets/led.c
new file mode 100644
index 00000000..38f22a05
--- /dev/null
+++ b/lib/luavgl/src/widgets/led.c
@@ -0,0 +1,91 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_led_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_led_create);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t led_property_table[] = {
+ {"color", SETTER_TYPE_COLOR, {.setter = (setter_int_t)lv_led_set_color}},
+ {"brightness", 0, {.setter = (setter_int_t)lv_led_set_brightness}},
+};
+/* clang-format on */
+
+LUALIB_API int luavgl_led_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, led_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for led.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_led_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_led_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_led_on(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_led_on(obj);
+ return 0;
+}
+
+static int luavgl_led_off(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_led_off(obj);
+ return 0;
+}
+
+static int luavgl_led_toggle(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_led_toggle(obj);
+ return 0;
+}
+
+static int luavgl_led_get_brightness(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_led_get_brightness(obj));
+ return 1;
+}
+
+static const luaL_Reg luavgl_led_methods[] = {
+ {"set", luavgl_led_set },
+ {"on", luavgl_led_on },
+ {"off", luavgl_led_off },
+ {"toggle", luavgl_led_toggle },
+
+ {"get_brightness", luavgl_led_get_brightness},
+
+ {NULL, NULL },
+};
+
+static void luavgl_led_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_led_class, "lv_led", luavgl_led_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/list.c b/lib/luavgl/src/widgets/list.c
new file mode 100644
index 00000000..825a7bff
--- /dev/null
+++ b/lib/luavgl/src/widgets/list.c
@@ -0,0 +1,89 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_list_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_list_create);
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t list_property_table[] = {
+ {"dummy", SETTER_TYPE_STACK, {.setter_stack = _lv_dummy_set}},
+};
+/* clang-format on */
+
+LUALIB_API int luavgl_list_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, list_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for list.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_list_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_list_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_list_add_text(lua_State *L)
+{
+ lv_obj_t *list = luavgl_to_obj(L, 1);
+ const char *str = lua_tostring(L, 2);
+ lv_obj_t *obj = lv_list_add_text(list, str);
+ luavgl_add_lobj(L, obj)->lua_created = true;
+ return 1;
+}
+
+static int luavgl_list_add_btn(lua_State *L)
+{
+ lv_obj_t *list = luavgl_to_obj(L, 1);
+ const void *icon = luavgl_toimgsrc(L, 2);
+ const char *str = lua_tostring(L, 3);
+ lv_obj_t *obj = lv_list_add_btn(list, icon, str);
+ luavgl_add_lobj(L, obj)->lua_created = true;
+ return 1;
+}
+
+static int luavgl_get_btn_text(lua_State *L)
+{
+ lv_obj_t *list = luavgl_to_obj(L, 1);
+ lv_obj_t *btn = luavgl_to_obj(L, 2);
+
+ lua_pushstring(L, lv_list_get_btn_text(list, btn));
+ return 1;
+}
+
+static const luaL_Reg luavgl_list_methods[] = {
+ {"set", luavgl_list_set },
+
+ {"add_text", luavgl_list_add_text},
+ {"add_btn", luavgl_list_add_btn },
+ {"get_btn_text", luavgl_get_btn_text },
+
+ {NULL, NULL },
+};
+
+static void luavgl_list_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_list_class, "lv_list", luavgl_list_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/roller.c b/lib/luavgl/src/widgets/roller.c
new file mode 100644
index 00000000..574fc066
--- /dev/null
+++ b/lib/luavgl/src/widgets/roller.c
@@ -0,0 +1,136 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_roller_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_roller_create);
+}
+
+static void _lv_roller_set_options(void *obj, lua_State *L)
+{
+ int type = lua_type(L, -1);
+ if (type == LUA_TSTRING) {
+ lv_roller_set_options(obj, lua_tostring(L, -1), 0);
+ } else if (type == LUA_TTABLE) {
+ lua_getfield(L, -1, "options");
+ if (!lua_isstring(L, -1)) {
+ luaL_error(L, "expect string.");
+ return;
+ }
+
+ const char *options = lua_tostring(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "mode");
+ lv_roller_mode_t mode = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lv_roller_set_options(obj, options, mode);
+ return;
+ }
+
+ luaL_error(L, "expect string or table.");
+}
+
+static void _lv_roller_set_selected(void *obj, lua_State *L)
+{
+ int type = lua_type(L, -1);
+ uint16_t selected;
+ int anim = 0;
+
+ if (type == LUA_TTABLE) {
+ lua_getfield(L, -1, "selected");
+ selected = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "anim");
+ anim = luavgl_tointeger(L, -1);
+ lua_pop(L, 1);
+ } else {
+ selected = lua_tointeger(L, -1);
+ }
+
+ lv_roller_set_selected(obj, selected, anim);
+}
+
+static const luavgl_value_setter_t roller_property_table[] = {
+ {"options", SETTER_TYPE_STACK, {.setter_stack = _lv_roller_set_options} },
+ {"selected", SETTER_TYPE_STACK, {.setter_stack = _lv_roller_set_selected} },
+ {"visible_cnt",
+ 0, {.setter = (setter_int_t)lv_roller_set_visible_row_count}},
+};
+
+LUALIB_API int luavgl_roller_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, roller_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for roller.\n");
+ }
+
+ return ret;
+}
+
+static int luavgl_roller_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_roller_set_property_kv, obj);
+
+ return 0;
+}
+
+static int luavgl_roller_get_options(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushstring(L, lv_roller_get_options(obj));
+ return 1;
+}
+
+static int luavgl_roller_get_selected(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_roller_get_selected(obj));
+ return 1;
+}
+
+static int luavgl_roller_get_selected_str(lua_State *L)
+{
+ char buf[64];
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lv_roller_get_selected_str(obj, buf, sizeof(buf));
+ lua_pushstring(L, buf);
+ return 1;
+}
+
+static int luavgl_roller_get_options_cnt(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushinteger(L, lv_roller_get_option_cnt(obj));
+ return 1;
+}
+
+static const luaL_Reg luavgl_roller_methods[] = {
+ {"set", luavgl_roller_set },
+ {"get_options", luavgl_roller_get_options },
+ {"get_selected", luavgl_roller_get_selected },
+ {"get_selected_str", luavgl_roller_get_selected_str},
+ {"get_options_cnt", luavgl_roller_get_options_cnt },
+
+ {NULL, NULL },
+};
+
+static void luavgl_roller_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_roller_class, "lv_roller", luavgl_roller_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/textarea.c b/lib/luavgl/src/widgets/textarea.c
new file mode 100644
index 00000000..7c6170c1
--- /dev/null
+++ b/lib/luavgl/src/widgets/textarea.c
@@ -0,0 +1,117 @@
+#include "luavgl.h"
+#include "private.h"
+
+static int luavgl_textarea_create(lua_State *L)
+{
+ return luavgl_obj_create_helper(L, lv_textarea_create);
+}
+
+static void _lv_textarea_set_txt(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "expect string");
+ return;
+ }
+
+ lv_textarea_set_text(obj, lua_tostring(L, -1));
+}
+
+static void _lv_textarea_set_placeholder_txt(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "expect string");
+ return;
+ }
+
+ lv_textarea_set_placeholder_text(obj, lua_tostring(L, -1));
+}
+
+static void _lv_textarea_set_password_bullet(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "expect string");
+ return;
+ }
+
+ lv_textarea_set_password_bullet(obj, lua_tostring(L, -1));
+}
+
+static void _lv_textarea_set_accepted_chars(void *obj, lua_State *L)
+{
+ if (!lua_isstring(L, -1)) {
+ luaL_argerror(L, -1, "expect string");
+ return;
+ }
+
+ lv_textarea_set_accepted_chars(obj, lua_tostring(L, -1));
+}
+
+/* clang-format off */
+static const luavgl_value_setter_t textarea_property_table[] = {
+ {"text", SETTER_TYPE_STACK, {.setter_stack = _lv_textarea_set_txt}},
+ {"placeholder", SETTER_TYPE_STACK, {.setter_stack = _lv_textarea_set_placeholder_txt}},
+ {"cursor", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_textarea_set_cursor_pos}},
+ {"password_mode", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_textarea_set_password_mode}},
+ {"one_line", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_textarea_set_one_line}},
+ {"password_bullet", SETTER_TYPE_STACK, {.setter_stack = _lv_textarea_set_password_bullet}},
+ {"accepted_chars", SETTER_TYPE_STACK, {.setter_stack = _lv_textarea_set_accepted_chars}},
+ {"max_length", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_textarea_set_max_length}},
+ {"password_show_time", SETTER_TYPE_INT, {.setter = (setter_int_t)lv_textarea_set_password_show_time}},
+};
+/* clang-format on */
+
+LUALIB_API int luavgl_textarea_set_property_kv(lua_State *L, void *data)
+{
+ lv_obj_t *obj = data;
+ int ret = luavgl_set_property(L, obj, textarea_property_table);
+
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* a base obj property? */
+ ret = luavgl_obj_set_property_kv(L, obj);
+ if (ret != 0) {
+ debug("unkown property for textarea: %s\n", lua_tostring(L, -2));
+ }
+
+ return -1;
+}
+
+static int luavgl_textarea_set(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "expect a table on 2nd para.");
+ return 0;
+ }
+
+ luavgl_iterate(L, -1, luavgl_textarea_set_property_kv, obj);
+
+ return 0;
+}
+
+/**
+ * obj:get_text()
+ */
+static int luavgl_textarea_get_text(lua_State *L)
+{
+ lv_obj_t *obj = luavgl_to_obj(L, 1);
+ lua_pushstring(L, lv_textarea_get_text(obj));
+ return 1;
+}
+
+static const luaL_Reg luavgl_textarea_methods[] = {
+ {"set", luavgl_textarea_set },
+ {"get_text", luavgl_textarea_get_text},
+
+ {NULL, NULL },
+};
+
+static void luavgl_textarea_init(lua_State *L)
+{
+ luavgl_obj_newmetatable(L, &lv_textarea_class, "lv_textarea",
+ luavgl_textarea_methods);
+ lua_pop(L, 1);
+}
diff --git a/lib/luavgl/src/widgets/widgets.c b/lib/luavgl/src/widgets/widgets.c
new file mode 100644
index 00000000..d26cdf95
--- /dev/null
+++ b/lib/luavgl/src/widgets/widgets.c
@@ -0,0 +1,133 @@
+#include "luavgl.h"
+#include "private.h"
+
+#if LV_USE_CALENDAR
+#include "calendar.c"
+#endif
+
+#if LV_USE_CHECKBOX
+#include "checkbox.c"
+#endif
+
+#if LV_USE_DROPDOWN
+#include "dropdown.c"
+#endif
+
+#if LV_USE_IMG
+#include "img.c"
+#endif
+
+#if LV_USE_KEYBOARD
+#include "keyboard.c"
+#endif
+
+#if LV_USE_LABEL
+#include "label.c"
+#endif
+
+#if LV_USE_LED
+#include "led.c"
+#endif
+
+#if LV_USE_LIST
+#include "list.c"
+#endif
+
+#if LV_USE_ROLLER
+#include "roller.c"
+#endif
+
+#if LV_USE_TEXTAREA
+#include "textarea.c"
+#endif
+
+static int luavgl_obj_create(lua_State *L);
+
+static const luaL_Reg widget_create_methods[] = {
+ {"Object", luavgl_obj_create },
+
+#if LV_USE_CALENDAR
+ {"Calendar", luavgl_calendar_create},
+#endif
+
+#if LV_USE_CHECKBOX
+ {"Checkbox", luavgl_checkbox_create},
+#endif
+
+#if LV_USE_DROPDOWN
+ {"Dropdown", luavgl_dropdown_create},
+#endif
+
+#if LV_USE_IMG
+ {"Image", luavgl_img_create },
+#endif
+
+#if LV_USE_KEYBOARD
+ {"Keyboard", luavgl_keyboard_create},
+#endif
+
+#if LV_USE_LABEL
+ {"Label", luavgl_label_create },
+#endif
+
+#if LV_USE_LED
+ {"Led", luavgl_led_create },
+#endif
+
+#if LV_USE_LIST
+ {"List", luavgl_list_create },
+#endif
+
+#if LV_USE_ROLLER
+ {"Roller", luavgl_roller_create },
+#endif
+
+#if LV_USE_TEXTAREA
+ {"Textarea", luavgl_textarea_create},
+#endif
+ {NULL, NULL }
+};
+
+static void luavgl_widgets_init(lua_State *L)
+{
+#if LV_USE_IMG
+ luavgl_img_init(L);
+#endif
+
+#if LV_USE_LABEL
+ luavgl_label_init(L);
+#endif
+
+#if LV_USE_LED
+ luavgl_led_init(L);
+#endif
+
+#if LV_USE_LIST
+ luavgl_list_init(L);
+#endif
+
+#if LV_USE_TEXTAREA
+ luavgl_textarea_init(L);
+#endif
+
+#if LV_USE_KEYBOARD
+ luavgl_keyboard_init(L);
+#endif
+
+#if LV_USE_CHECKBOX
+ luavgl_checkbox_init(L);
+#endif
+
+#if LV_USE_CALENDAR
+ luavgl_calendar_init(L);
+#endif
+
+#if LV_USE_ROLLER
+ luavgl_roller_init(L);
+#endif
+
+#if LV_USE_DROPDOWN
+ luavgl_dropdown_init(L);
+#endif
+
+}