diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
| commit | 8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch) | |
| tree | 02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl | |
| parent | 8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff) | |
| download | tangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz | |
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl')
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 Binary files differnew file mode 100644 index 00000000..6f5857a9 --- /dev/null +++ b/lib/luavgl/examples/assets/hand-hour.png diff --git a/lib/luavgl/examples/assets/hand-minute.png b/lib/luavgl/examples/assets/hand-minute.png Binary files differnew file mode 100644 index 00000000..52f6f1ba --- /dev/null +++ b/lib/luavgl/examples/assets/hand-minute.png diff --git a/lib/luavgl/examples/assets/hand-second.png b/lib/luavgl/examples/assets/hand-second.png Binary files differnew file mode 100644 index 00000000..9caf4649 --- /dev/null +++ b/lib/luavgl/examples/assets/hand-second.png diff --git a/lib/luavgl/examples/assets/lvgl-logo.png b/lib/luavgl/examples/assets/lvgl-logo.png Binary files differnew file mode 100644 index 00000000..b032d1b8 --- /dev/null +++ b/lib/luavgl/examples/assets/lvgl-logo.png diff --git a/lib/luavgl/examples/assets/second.png b/lib/luavgl/examples/assets/second.png Binary files differnew file mode 100644 index 00000000..d09ea5a3 --- /dev/null +++ b/lib/luavgl/examples/assets/second.png 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 Binary files differnew file mode 100644 index 00000000..6d24cc26 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/bg_day.png diff --git a/lib/luavgl/examples/flappyBird/bird1.png b/lib/luavgl/examples/flappyBird/bird1.png Binary files differnew file mode 100644 index 00000000..72b202d5 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/bird1.png diff --git a/lib/luavgl/examples/flappyBird/bird2.png b/lib/luavgl/examples/flappyBird/bird2.png Binary files differnew file mode 100644 index 00000000..46050452 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/bird2.png diff --git a/lib/luavgl/examples/flappyBird/bird3.png b/lib/luavgl/examples/flappyBird/bird3.png Binary files differnew file mode 100644 index 00000000..2fc5128d --- /dev/null +++ b/lib/luavgl/examples/flappyBird/bird3.png diff --git a/lib/luavgl/examples/flappyBird/button_play.png b/lib/luavgl/examples/flappyBird/button_play.png Binary files differnew file mode 100644 index 00000000..ae3d330b --- /dev/null +++ b/lib/luavgl/examples/flappyBird/button_play.png 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 Binary files differnew file mode 100644 index 00000000..b93a861a --- /dev/null +++ b/lib/luavgl/examples/flappyBird/land.png diff --git a/lib/luavgl/examples/flappyBird/medals.png b/lib/luavgl/examples/flappyBird/medals.png Binary files differnew file mode 100644 index 00000000..c440df81 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/medals.png diff --git a/lib/luavgl/examples/flappyBird/pipe_down.png b/lib/luavgl/examples/flappyBird/pipe_down.png Binary files differnew file mode 100644 index 00000000..e02d6946 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/pipe_down.png diff --git a/lib/luavgl/examples/flappyBird/pipe_up.png b/lib/luavgl/examples/flappyBird/pipe_up.png Binary files differnew file mode 100644 index 00000000..6e8e8d5d --- /dev/null +++ b/lib/luavgl/examples/flappyBird/pipe_up.png diff --git a/lib/luavgl/examples/flappyBird/score.png b/lib/luavgl/examples/flappyBird/score.png Binary files differnew file mode 100644 index 00000000..cf567b0c --- /dev/null +++ b/lib/luavgl/examples/flappyBird/score.png diff --git a/lib/luavgl/examples/flappyBird/text_game_over.png b/lib/luavgl/examples/flappyBird/text_game_over.png Binary files differnew file mode 100644 index 00000000..b8c30fc2 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/text_game_over.png diff --git a/lib/luavgl/examples/flappyBird/title.png b/lib/luavgl/examples/flappyBird/title.png Binary files differnew file mode 100644 index 00000000..557340f2 --- /dev/null +++ b/lib/luavgl/examples/flappyBird/title.png 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 + +} |
