From f628d5e8592b774132565ab153a8464c8d83548b Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 24 Feb 2014 20:47:20 +0100 Subject: lua: add a bunch of functions to get/set properties by their native type There are some complications because the client API distinguishes between integers and floats, while Lua has only "numbers" (which are usually floats). But I think this should work now. --- DOCS/man/en/lua.rst | 42 ++++++++++++- player/lua.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++-- player/lua/defaults.lua | 2 + 3 files changed, 192 insertions(+), 7 deletions(-) diff --git a/DOCS/man/en/lua.rst b/DOCS/man/en/lua.rst index 93a169a391..b84984e6ce 100644 --- a/DOCS/man/en/lua.rst +++ b/DOCS/man/en/lua.rst @@ -93,12 +93,50 @@ The ``mp`` module is preloaded, although it can be loaded manually with missing. Unlike ``get_property()``, assigning the return value to a variable will always result in a string. +``mp.get_property_bool(name [,def])`` + Similar to ``mp.get_property``, but return the property value as boolean. + + Returns a boolean on success, or ``def, error`` on error. + +``mp.get_property_number(name [,def])`` + Similar to ``mp.get_property``, but return the property value as number. + + Note that while Lua does not distinguish between integers and floats, + mpv internals do. This function simply request a double float from mpv, + and mpv will usually convert integer property values to float. + + Returns a number on success, or ``def, error`` on error. + +``mp.get_property_native(name [,def])`` + Similar to ``mp.get_property``, but return the property value using the best + Lua type for the property. Most time, this will return a string, boolean, + or number. Some properties (for example ``chapter-list``) are returned as + tables. + + Returns a value on success, or ``def, error`` on error. Note that ``nil`` + might be a possible, valid value too in some corner cases. + + (There is no ``mp.set_property_native`` yet.) + ``mp.set_property(name, value)`` - Set the given property to the given value. See ``mp.get_property`` and - `Properties`_ for more information about properties. + Set the given property to the given string value. See ``mp.get_property`` + and `Properties`_ for more information about properties. Returns true on success, or ``nil, error`` on error. +``mp.set_property_bool(name, value)`` + Similar to ``mp.set_property``, but set the given property to the given + boolean value. + +``mp.set_property_number(name, value)`` + Similar to ``mp.set_property``, but set the given property to the given + numeric value. + + Note that while Lua does not distinguish between integers and floats, + mpv internals do. This function will test whether the number can be + represented as integer, and if so, it will pass an integer value to mpv, + otherwise a double float. + ``mp.get_time()`` Return the current mpv internal time in seconds as a number. This is basically the system time, with an arbitrary offset. diff --git a/player/lua.c b/player/lua.c index e813d84bce..d8996eb7b9 100644 --- a/player/lua.c +++ b/player/lua.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "osdep/io.h" @@ -232,8 +233,16 @@ static void *lua_thread(void *p) lua_pushstring(L, ctx->name); // mp name lua_setfield(L, -2, "script_name"); // mp + // used by pushnode() + lua_newtable(L); // mp table + lua_pushvalue(L, -1); // mp table table + lua_setfield(L, LUA_REGISTRYINDEX, "UNKNOWN_TYPE"); // mp table + lua_setfield(L, -2, "UNKNOWN_TYPE"); // mp + lua_pop(L, 1); // - + assert(lua_gettop(L) == 0); + // Add a preloader for each builtin Lua module lua_getglobal(L, "package"); // package assert(lua_type(L, -1) == LUA_TTABLE); @@ -500,6 +509,33 @@ static int script_set_property(lua_State *L) return check_error(L, mpv_set_property_string(ctx->client, p, v)); } +static int script_set_property_bool(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *p = luaL_checkstring(L, 1); + int v = lua_toboolean(L, 2); + + return check_error(L, mpv_set_property(ctx->client, p, MPV_FORMAT_FLAG, &v)); +} + +static int script_set_property_number(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *p = luaL_checkstring(L, 1); + double d = luaL_checknumber(L, 2); + // If the number might be an integer, then set it as integer. The mpv core + // will (probably) convert INT64 to DOUBLE when setting, but not the other + // way around. + int64_t v = d; + int res; + if (d == (double)v) { + res = mpv_set_property(ctx->client, p, MPV_FORMAT_INT64, &v); + } else { + res = mpv_set_property(ctx->client, p, MPV_FORMAT_DOUBLE, &d); + } + return check_error(L, res); +} + static int script_property_list(lua_State *L) { const struct m_option *props = mp_get_property_list(); @@ -518,8 +554,6 @@ static int script_get_property(lua_State *L) const char *name = luaL_checkstring(L, 1); int type = lua_tointeger(L, lua_upvalueindex(1)) ? MPV_FORMAT_OSD_STRING : MPV_FORMAT_STRING; - char *def_fallback = type == MPV_FORMAT_OSD_STRING ? "" : NULL; - char *def = (char *)luaL_optstring(L, 2, def_fallback); char *result = NULL; int err = mpv_get_property(ctx->client, name, type, &result); @@ -527,13 +561,119 @@ static int script_get_property(lua_State *L) lua_pushstring(L, result); talloc_free(result); return 1; + } else { + if (lua_isnoneornil(L, 2) && type == MPV_FORMAT_OSD_STRING) { + lua_pushstring(L, ""); + } else { + lua_pushvalue(L, 2); + } + lua_pushstring(L, mpv_error_string(err)); + return 2; } - if (def) { - lua_pushstring(L, def); +} + +static int script_get_property_bool(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *name = luaL_checkstring(L, 1); + + int result = 0; + int err = mpv_get_property(ctx->client, name, MPV_FORMAT_FLAG, &result); + if (err >= 0) { + lua_pushboolean(L, !!result); + return 1; + } else { + lua_pushvalue(L, 2); + lua_pushstring(L, mpv_error_string(err)); + return 2; + } +} + +static int script_get_property_number(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *name = luaL_checkstring(L, 1); + + // Note: the mpv core will (hopefully) convert INT64 to DOUBLE + double result = 0; + int err = mpv_get_property(ctx->client, name, MPV_FORMAT_DOUBLE, &result); + if (err >= 0) { + lua_pushnumber(L, result); + return 1; + } else { + lua_pushvalue(L, 2); lua_pushstring(L, mpv_error_string(err)); return 2; } - return check_error(L, err); +} + +static bool pushnode(lua_State *L, mpv_node *node, int depth) +{ + depth--; + if (depth < 0) + return false; + luaL_checkstack(L, 6, "stack overflow"); + + switch (node->format) { + case MPV_FORMAT_STRING: + lua_pushstring(L, node->u.string); + break; + case MPV_FORMAT_INT64: + lua_pushnumber(L, node->u.int64); + break; + case MPV_FORMAT_DOUBLE: + lua_pushnumber(L, node->u.double_); + break; + case MPV_FORMAT_NONE: + lua_pushnil(L); + break; + case MPV_FORMAT_FLAG: + lua_pushboolean(L, node->u.flag); + break; + case MPV_FORMAT_NODE_ARRAY: + lua_newtable(L); // table + for (int n = 0; n < node->u.list->num; n++) { + if (!pushnode(L, &node->u.list->values[n], depth)) // table value + return false; + lua_rawseti(L, -2, n + 1); // table + } + break; + case MPV_FORMAT_NODE_MAP: + lua_newtable(L); // table + for (int n = 0; n < node->u.list->num; n++) { + lua_pushstring(L, node->u.list->keys[n]); // table key + if (!pushnode(L, &node->u.list->values[n], depth)) // table key value + return false; + lua_rawset(L, -3); + } + break; + default: + // unknown value - what do we do? + // for now, set a unique dummy value + lua_getfield(L, LUA_REGISTRYINDEX, "UNKNOWN_TYPE"); + break; + } + return true; +} + +static int script_get_property_native(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *name = luaL_checkstring(L, 1); + + mpv_node node; + int err = mpv_get_property(ctx->client, name, MPV_FORMAT_NODE, &node); + const char *errstr = mpv_error_string(err); + if (err >= 0) { + bool ok = pushnode(L, &node, 50); + mpv_free_node_contents(&node); + if (ok) + return 1; + errstr = "value too large"; + } + lua_pushvalue(L, 2); + lua_pushstring(L, errstr); + return 2; } static int script_set_osd_ass(lua_State *L) @@ -781,7 +921,12 @@ static struct fn_entry fn_list[] = { FN_ENTRY(find_config_file), FN_ENTRY(command), FN_ENTRY(commandv), + FN_ENTRY(get_property_bool), + FN_ENTRY(get_property_number), + FN_ENTRY(get_property_native), FN_ENTRY(set_property), + FN_ENTRY(set_property_bool), + FN_ENTRY(set_property_number), FN_ENTRY(property_list), FN_ENTRY(set_osd_ass), FN_ENTRY(get_osd_resolution), diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua index c505e29930..3f3dab7490 100644 --- a/player/lua/defaults.lua +++ b/player/lua/defaults.lua @@ -1,3 +1,5 @@ +mp.UNKNOWN_TYPE = "this value is inserted if the C type is not supported" + function mp.get_script_name() return mp.script_name end -- cgit v1.2.3