summaryrefslogtreecommitdiff
path: root/lib/luavgl/src
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
commit8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch)
tree02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl/src
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl/src')
-rw-r--r--lib/luavgl/src/anim.c339
-rw-r--r--lib/luavgl/src/constants.c554
-rw-r--r--lib/luavgl/src/disp.c342
-rw-r--r--lib/luavgl/src/event.c191
-rw-r--r--lib/luavgl/src/font.c313
-rw-r--r--lib/luavgl/src/fs.c383
-rw-r--r--lib/luavgl/src/group.c347
-rw-r--r--lib/luavgl/src/indev.c342
-rw-r--r--lib/luavgl/src/luavgl.c150
-rw-r--r--lib/luavgl/src/luavgl.h263
-rw-r--r--lib/luavgl/src/lvgl.lua1465
-rw-r--r--lib/luavgl/src/obj.c850
-rw-r--r--lib/luavgl/src/private.h39
-rw-r--r--lib/luavgl/src/style.c702
-rw-r--r--lib/luavgl/src/timer.c222
-rw-r--r--lib/luavgl/src/util.c413
-rw-r--r--lib/luavgl/src/widgets/calendar.c169
-rw-r--r--lib/luavgl/src/widgets/checkbox.c74
-rw-r--r--lib/luavgl/src/widgets/dropdown.c188
-rw-r--r--lib/luavgl/src/widgets/img.c196
-rw-r--r--lib/luavgl/src/widgets/keyboard.c76
-rw-r--r--lib/luavgl/src/widgets/label.c155
-rw-r--r--lib/luavgl/src/widgets/led.c91
-rw-r--r--lib/luavgl/src/widgets/list.c89
-rw-r--r--lib/luavgl/src/widgets/roller.c136
-rw-r--r--lib/luavgl/src/widgets/textarea.c117
-rw-r--r--lib/luavgl/src/widgets/widgets.c133
27 files changed, 8339 insertions, 0 deletions
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
+
+}