Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tangara-fw/lib/luavgl/src/util.c

427 lines
12 KiB

#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);
}