From 053c743724e928ca70070f5d43193836851dd91c Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Dec 2013 01:15:48 +0100 Subject: Rename mp_lua.c/h to lua.c/h --- player/command.c | 2 +- player/lua.c | 683 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ player/lua.h | 14 ++ player/main.c | 2 +- player/mp_lua.c | 683 ------------------------------------------------------- player/mp_lua.h | 14 -- 6 files changed, 699 insertions(+), 699 deletions(-) create mode 100644 player/lua.c create mode 100644 player/lua.h delete mode 100644 player/mp_lua.c delete mode 100644 player/mp_lua.h (limited to 'player') diff --git a/player/command.c b/player/command.c index 35a6ed31bc..8701fff1ec 100644 --- a/player/command.c +++ b/player/command.c @@ -73,7 +73,7 @@ #include "osdep/io.h" #include "core.h" -#include "mp_lua.h" +#include "lua.h" struct command_ctx { int events; diff --git a/player/lua.c b/player/lua.c new file mode 100644 index 0000000000..d000c85c5f --- /dev/null +++ b/player/lua.c @@ -0,0 +1,683 @@ +#include +#include + +#include +#include +#include + +#include "talloc.h" + +#include "mpvcore/mp_common.h" +#include "mpvcore/m_property.h" +#include "mpvcore/mp_msg.h" +#include "mpvcore/m_option.h" +#include "mpvcore/input/input.h" +#include "mpvcore/path.h" +#include "mpvcore/bstr.h" +#include "osdep/timer.h" +#include "sub/osd.h" +#include "core.h" +#include "command.h" +#include "lua.h" + +// List of builtin modules and their contents as strings. +// All these are generated from player/lua/*.lua +static const char *builtin_lua_scripts[][2] = { + {"mp.defaults", +# include "player/lua/defaults.inc" + }, + {"mp.assdraw", +# include "player/lua/assdraw.inc" + }, + {"@osc", +# include "player/lua/osc.inc" + }, + {0} +}; + +// Represents a loaded script. Each has its own Lua state. +struct script_ctx { + const char *name; + lua_State *state; + struct mp_log *log; + struct MPContext *mpctx; +}; + +struct lua_ctx { + struct script_ctx **scripts; + int num_scripts; +}; + +static struct script_ctx *find_script(struct lua_ctx *lctx, const char *name) +{ + for (int n = 0; n < lctx->num_scripts; n++) { + if (strcmp(lctx->scripts[n]->name, name) == 0) + return lctx->scripts[n]; + } + return NULL; +} + +static struct script_ctx *get_ctx(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "ctx"); + struct script_ctx *ctx = lua_touserdata(L, -1); + lua_pop(L, 1); + assert(ctx); + return ctx; +} + +static struct MPContext *get_mpctx(lua_State *L) +{ + return get_ctx(L)->mpctx; +} + +static int wrap_cpcall(lua_State *L) +{ + lua_CFunction fn = lua_touserdata(L, -1); + lua_pop(L, 1); + return fn(L); +} + +// Call the given function fn under a Lua error handler (similar to lua_cpcall). +// Pass the given number of args from the Lua stack to fn. +// Returns 0 (and empty stack) on success. +// Returns LUA_ERR[RUN|MEM|ERR] otherwise, with the error value on the stack. +static int mp_cpcall(lua_State *L, lua_CFunction fn, int args) +{ + // Don't use lua_pushcfunction() - it allocates memory on Lua 5.1. + // Instead, emulate C closures by making wrap_cpcall call fn. + lua_pushlightuserdata(L, fn); // args... fn + // Will always succeed if mp_lua_init() set it up correctly. + lua_getfield(L, LUA_REGISTRYINDEX, "wrap_cpcall"); // args... fn wrap_cpcall + lua_insert(L, -(args + 2)); // wrap_cpcall args... fn + return lua_pcall(L, args + 1, 0, 0); +} + +static void report_error(lua_State *L) +{ + const char *err = lua_tostring(L, -1); + mp_msg(MSGT_CPLAYER, MSGL_WARN, "[lua] Error: %s\n", + err ? err : "[unknown]"); + lua_pop(L, 1); +} + +static void add_functions(struct script_ctx *ctx); + +static char *script_name_from_filename(void *talloc_ctx, struct lua_ctx *lctx, + const char *fname) +{ + fname = mp_basename(fname); + if (fname[0] == '@') + fname += 1; + char *name = talloc_strdup(talloc_ctx, fname); + // Drop .lua extension + char *dot = strrchr(name, '.'); + if (dot) + *dot = '\0'; + // Turn it into a safe identifier - this is used with e.g. dispatching + // input via: "send scriptname ..." + for (int n = 0; name[n]; n++) { + char c = name[n]; + if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && + !(c >= '0' && c <= '9')) + name[n] = '_'; + } + // Make unique (stupid but simple) + while (find_script(lctx, name)) + name = talloc_strdup_append(name, "_"); + return name; +} + +static int load_file(struct script_ctx *ctx, const char *fname) +{ + int r = 0; + lua_State *L = ctx->state; + if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) { + report_error(L); + r = -1; + } + assert(lua_gettop(L) == 0); + return r; +} + +static int load_builtin(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + for (int n = 0; builtin_lua_scripts[n][0]; n++) { + if (strcmp(name, builtin_lua_scripts[n][0]) == 0) { + if (luaL_loadstring(L, builtin_lua_scripts[n][1])) + lua_error(L); + lua_call(L, 0, 1); + return 1; + } + } + return 0; +} + +// Execute "require " .. name +static bool require(lua_State *L, const char *name) +{ + char buf[80]; + // Lazy, but better than calling the "require" function manually + snprintf(buf, sizeof(buf), "require '%s'", name); + if (luaL_loadstring(L, buf) || lua_pcall(L, 0, 0, 0)) { + report_error(L); + return false; + } + return true; +} + +static void mp_lua_load_script(struct MPContext *mpctx, const char *fname) +{ + struct lua_ctx *lctx = mpctx->lua_ctx; + struct script_ctx *ctx = talloc_ptrtype(NULL, ctx); + *ctx = (struct script_ctx) { + .mpctx = mpctx, + .name = script_name_from_filename(ctx, lctx, fname), + }; + char *log_name = talloc_asprintf(ctx, "lua/%s", ctx->name); + ctx->log = mp_log_new(ctx, mpctx->log, log_name); + + lua_State *L = ctx->state = luaL_newstate(); + if (!L) + goto error_out; + + // used by get_ctx() + lua_pushlightuserdata(L, ctx); // ctx + lua_setfield(L, LUA_REGISTRYINDEX, "ctx"); // - + + lua_pushcfunction(L, wrap_cpcall); // closure + lua_setfield(L, LUA_REGISTRYINDEX, "wrap_cpcall"); // - + + luaL_openlibs(L); + + lua_newtable(L); // mp + lua_pushvalue(L, -1); // mp mp + lua_setglobal(L, "mp"); // mp + + add_functions(ctx); // mp + + lua_pushstring(L, ctx->name); // mp name + lua_setfield(L, -2, "script_name"); // mp + + lua_pop(L, 1); // - + + // Add a preloader for each builtin Lua module + lua_getglobal(L, "package"); // package + assert(lua_type(L, -1) == LUA_TTABLE); + lua_getfield(L, -1, "preload"); // package preload + assert(lua_type(L, -1) == LUA_TTABLE); + for (int n = 0; builtin_lua_scripts[n][0]; n++) { + lua_pushcfunction(L, load_builtin); // package preload load_builtin + lua_setfield(L, -2, builtin_lua_scripts[n][0]); + } + lua_pop(L, 2); // - + + assert(lua_gettop(L) == 0); + + if (!require(L, "mp.defaults")) { + report_error(L); + goto error_out; + } + + assert(lua_gettop(L) == 0); + + if (fname[0] == '@') { + if (!require(L, fname)) + goto error_out; + } else { + if (load_file(ctx, fname) < 0) + goto error_out; + } + + MP_TARRAY_APPEND(lctx, lctx->scripts, lctx->num_scripts, ctx); + return; + +error_out: + if (ctx->state) + lua_close(ctx->state); + talloc_free(ctx); +} + +static void kill_script(struct script_ctx *ctx) +{ + if (!ctx) + return; + struct lua_ctx *lctx = ctx->mpctx->lua_ctx; + lua_close(ctx->state); + for (int n = 0; n < lctx->num_scripts; n++) { + if (lctx->scripts[n] == ctx) { + MP_TARRAY_REMOVE_AT(lctx->scripts, lctx->num_scripts, n); + break; + } + } + talloc_free(ctx); +} + +static const char *log_level[] = { + [MSGL_FATAL] = "fatal", + [MSGL_ERR] = "error", + [MSGL_WARN] = "warn", + [MSGL_INFO] = "info", + [MSGL_V] = "verbose", + [MSGL_DBG2] = "debug", +}; + +static int script_log(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + + const char *level = luaL_checkstring(L, 1); + int msgl = -1; + for (int n = 0; n < MP_ARRAY_SIZE(log_level); n++) { + if (log_level[n] && strcasecmp(log_level[n], level) == 0) { + msgl = n; + break; + } + } + if (msgl < 0) + luaL_error(L, "Invalid log level '%s'", level); + + int last = lua_gettop(L); + lua_getglobal(L, "tostring"); // args... tostring + for (int i = 2; i <= last; i++) { + lua_pushvalue(L, -1); // args... tostring tostring + lua_pushvalue(L, i); // args... tostring tostring args[i] + lua_call(L, 1, 1); // args... tostring str + const char *s = lua_tostring(L, -1); + if (s == NULL) + return luaL_error(L, "Invalid argument"); + mp_msg_log(ctx->log, msgl, "%s%s", s, i > 0 ? " " : ""); + lua_pop(L, 1); // args... tostring + } + mp_msg_log(ctx->log, msgl, "\n"); + + return 0; +} + +static int script_find_config_file(lua_State *L) +{ + const char *s = luaL_checkstring(L, 1); + char *path = mp_find_user_config_file(s); + if (path) { + lua_pushstring(L, path); + } else { + lua_pushnil(L); + } + talloc_free(path); + return 1; +} + +static int run_event(lua_State *L) +{ + lua_getglobal(L, "mp_event"); // name arg mp_event + if (lua_isnil(L, -1)) + return 0; + lua_insert(L, -3); // mp_event name arg + lua_call(L, 2, 0); + return 0; +} + +void mp_lua_event(struct MPContext *mpctx, const char *name, const char *arg) +{ + // There is no proper subscription mechanism yet, so all scripts get it. + struct lua_ctx *lctx = mpctx->lua_ctx; + for (int n = 0; n < lctx->num_scripts; n++) { + struct script_ctx *ctx = lctx->scripts[n]; + lua_State *L = ctx->state; + lua_pushstring(L, name); + if (arg) { + lua_pushstring(L, arg); + } else { + lua_pushnil(L); + } + if (mp_cpcall(L, run_event, 2) != 0) + report_error(L); + } +} + +static int run_script_dispatch(lua_State *L) +{ + int id = lua_tointeger(L, 1); + const char *event = lua_tostring(L, 2); + lua_getglobal(L, "mp_script_dispatch"); + if (lua_isnil(L, -1)) + return 0; + lua_pushinteger(L, id); + lua_pushstring(L, event); + lua_call(L, 2, 0); + return 0; +} + +void mp_lua_script_dispatch(struct MPContext *mpctx, char *script_name, + int id, char *event) +{ + struct script_ctx *ctx = find_script(mpctx->lua_ctx, script_name); + if (!ctx) { + mp_msg(MSGT_CPLAYER, MSGL_V, + "Can't find script '%s' when handling input.\n", script_name); + return; + } + lua_State *L = ctx->state; + lua_pushinteger(L, id); + lua_pushstring(L, event); + if (mp_cpcall(L, run_script_dispatch, 2) != 0) + report_error(L); +} + +static int script_send_command(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + const char *s = luaL_checkstring(L, 1); + + mp_cmd_t *cmd = mp_input_parse_cmd(mpctx->input, bstr0((char*)s), ""); + if (!cmd) + luaL_error(L, "error parsing command"); + mp_input_queue_cmd(mpctx->input, cmd); + + return 0; +} + +static int script_property_list(lua_State *L) +{ + const struct m_option *props = mp_get_property_list(); + lua_newtable(L); + for (int i = 0; props[i].name; i++) { + lua_pushinteger(L, i + 1); + lua_pushstring(L, props[i].name); + lua_settable(L, -3); + } + return 1; +} + +static int script_property_string(lua_State *L) +{ + const struct m_option *props = mp_get_property_list(); + struct MPContext *mpctx = get_mpctx(L); + const char *name = luaL_checkstring(L, 1); + int type = lua_tointeger(L, lua_upvalueindex(1)) + ? M_PROPERTY_PRINT : M_PROPERTY_GET_STRING; + + char *result = NULL; + if (m_property_do(props, name, type, &result, mpctx) >= 0 && result) { + lua_pushstring(L, result); + talloc_free(result); + return 1; + } + if (type == M_PROPERTY_PRINT) { + lua_pushstring(L, ""); + return 1; + } + return 0; +} + +static int script_set_osd_ass(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + int res_x = luaL_checkinteger(L, 1); + int res_y = luaL_checkinteger(L, 2); + const char *text = luaL_checkstring(L, 3); + if (!mpctx->osd->external || + strcmp(mpctx->osd->external, text) != 0 || + mpctx->osd->external_res_x != res_x || + mpctx->osd->external_res_y != res_y) + { + talloc_free(mpctx->osd->external); + mpctx->osd->external = talloc_strdup(mpctx->osd, text); + mpctx->osd->external_res_x = res_x; + mpctx->osd->external_res_y = res_y; + osd_changed(mpctx->osd, OSDTYPE_EXTERNAL); + } + return 0; +} + +static int script_get_osd_resolution(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + int w, h; + osd_object_get_resolution(mpctx->osd, mpctx->osd->objs[OSDTYPE_EXTERNAL], + &w, &h); + lua_pushnumber(L, w); + lua_pushnumber(L, h); + return 2; +} + +static int script_get_screen_size(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + struct osd_object *obj = mpctx->osd->objs[OSDTYPE_EXTERNAL]; + double aspect = 1.0 * obj->vo_res.w / MPMAX(obj->vo_res.h, 1) / + obj->vo_res.display_par; + lua_pushnumber(L, obj->vo_res.w); + lua_pushnumber(L, obj->vo_res.h); + lua_pushnumber(L, aspect); + return 3; +} + +static int script_get_mouse_pos(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + int px, py; + mp_input_get_mouse_pos(mpctx->input, &px, &py); + double sw, sh; + osd_object_get_scale_factor(mpctx->osd, mpctx->osd->objs[OSDTYPE_EXTERNAL], + &sw, &sh); + lua_pushnumber(L, px * sw); + lua_pushnumber(L, py * sh); + return 2; +} + +static int script_get_timer(lua_State *L) +{ + lua_pushnumber(L, mp_time_sec()); + return 1; +} + +static int script_get_chapter_list(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + lua_newtable(L); // list + int num = get_chapter_count(mpctx); + for (int n = 0; n < num; n++) { + double time = chapter_start_time(mpctx, n); + char *name = chapter_display_name(mpctx, n); + lua_newtable(L); // list ch + lua_pushnumber(L, time); // list ch time + lua_setfield(L, -2, "time"); // list ch + lua_pushstring(L, name); // list ch name + lua_setfield(L, -2, "name"); // list ch + lua_pushinteger(L, n + 1); // list ch n1 + lua_insert(L, -2); // list n1 ch + lua_settable(L, -3); // list + talloc_free(name); + } + return 1; +} + +static const char *stream_type(enum stream_type t) +{ + switch (t) { + case STREAM_VIDEO: return "video"; + case STREAM_AUDIO: return "audio"; + case STREAM_SUB: return "sub"; + default: return "unknown"; + } +} + +static int script_get_track_list(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + lua_newtable(L); // list + for (int n = 0; n < mpctx->num_tracks; n++) { + struct track *track = mpctx->tracks[n]; + lua_newtable(L); // list track + + lua_pushstring(L, stream_type(track->type)); + lua_setfield(L, -2, "type"); + lua_pushinteger(L, track->user_tid); + lua_setfield(L, -2, "id"); + lua_pushboolean(L, track->default_track); + lua_setfield(L, -2, "default"); + lua_pushboolean(L, track->attached_picture); + lua_setfield(L, -2, "attached_picture"); + if (track->lang) { + lua_pushstring(L, track->lang); + lua_setfield(L, -2, "language"); + } + if (track->title) { + lua_pushstring(L, track->title); + lua_setfield(L, -2, "title"); + } + lua_pushboolean(L, track->is_external); + lua_setfield(L, -2, "external"); + if (track->external_filename) { + lua_pushstring(L, track->external_filename); + lua_setfield(L, -2, "external_filename"); + } + lua_pushboolean(L, track->auto_loaded); + lua_setfield(L, -2, "auto_loaded"); + + lua_pushinteger(L, n + 1); // list track n1 + lua_insert(L, -2); // list n1 track + lua_settable(L, -3); // list + } + return 1; +} + +static int script_input_define_section(lua_State *L) +{ + struct MPContext *mpctx = get_mpctx(L); + char *section = (char *)luaL_checkstring(L, 1); + char *contents = (char *)luaL_checkstring(L, 2); + mp_input_define_section(mpctx->input, section, "