diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
| commit | 8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch) | |
| tree | 02b6cf23f591915747ec2994381854a79979c4a0 /lib/luavgl/src | |
| parent | 8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff) | |
| download | tangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz | |
Convert the main menu screen to lua lol
Diffstat (limited to 'lib/luavgl/src')
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 + +} |
