summaryrefslogtreecommitdiff
path: root/lib/luavgl/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/luavgl/src/util.c')
-rw-r--r--lib/luavgl/src/util.c413
1 files changed, 413 insertions, 0 deletions
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);
+}