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/anim.c

339 lines
8.6 KiB

#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 */
}