#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: LV_LOG_ERROR("arg not lv_obj userdata"); 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: LV_LOG_USER("number: %g%c", lua_tonumber(L, i), ending); break; case LUA_TSTRING: LV_LOG_USER("string: %s%c", lua_tostring(L, i), ending); break; case LUA_TBOOLEAN: LV_LOG_USER("boolean: %s%c", (lua_toboolean(L, i) ? "true" : "false"), ending); break; case LUA_TNIL: LV_LOG_USER("nil: %s%c", "nil", ending); break; default: LV_LOG_USER("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); LV_LOG_USER(" "); 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); for (int i = 1; i <= top; i++) { switch (lua_type(L, i)) { case LUA_TNUMBER: LV_LOG_USER("%s: %g", luaL_typename(L, i), lua_tonumber(L, i)); break; case LUA_TSTRING: LV_LOG_USER("%s: %s", luaL_typename(L, i), lua_tostring(L, i)); break; case LUA_TBOOLEAN: LV_LOG_USER("%s: %s", luaL_typename(L, i), (lua_toboolean(L, i) ? "true" : "false")); break; case LUA_TNIL: LV_LOG_USER("%s: %s", luaL_typename(L, i), "nil"); break; default: LV_LOG_USER("%s: %p", luaL_typename(L, i), 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) { LV_LOG_ERROR("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 lv_style_t *luavgl_to_style(lua_State *L, int idx) { luavgl_style_t *lsty = luavgl_check_style(L, idx); if (lsty == NULL) { luaL_argerror(L, idx, "expect lua lvgl style, got null"); return NULL; } return &lsty->style; } 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 = lv_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); LV_LOG_INFO("set img src to user data: %p", src); } else if (lua_isstring(L, idx)) { src = lua_tostring(L, idx); } else { LV_LOG_ERROR("img src should be string or userdata"); 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 */ LV_LOG_INFO("ignore non-string key in table"); 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) { LV_LOG_ERROR("Null key, ignored"); return -1; } for (int i = 0; i < len; i++) { const luavgl_value_setter_t *p = &table[i]; if (lv_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 */ union { lv_color_t c; uint32_t v; } color; color.c = luavgl_tocolor(L, -1); p->setter(obj, color.v); } 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 { LV_LOG_ERROR("unsupported type: %d", 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); }