From dc00566963b35c931a4cf489d4361daa8283697a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 5 May 2018 20:20:21 +0200 Subject: command: handle list commands like normal commands Pretty annoying. --- input/cmd.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index 8c15c0109b..ceee2c7403 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -27,10 +27,6 @@ #include "libmpv/client.h" -const struct mp_cmd_def mp_cmd_list = { - .name = "list", -}; - static void destroy_cmd(void *ptr) { struct mp_cmd *cmd = ptr; -- cgit v1.2.3 From 383da1bfd59fa77366d5e61612da4da8d4f1cf6c Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 5 May 2018 20:41:45 +0200 Subject: input: move an enum back to its correct place This was accidentally moved together with the cmd stuff. --- input/cmd.h | 13 ------------- input/input.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index a985d0394c..7fc3ee63c5 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -57,19 +57,6 @@ enum mp_cmd_flags { MP_ON_OSD_BAR | MP_ON_OSD_MSG, }; -enum mp_input_section_flags { - // If a key binding is not defined in the current section, do not search the - // other sections for it (like the default section). Instead, an unbound - // key warning will be printed. - MP_INPUT_EXCLUSIVE = 1, - // Prefer it to other sections. - MP_INPUT_ON_TOP = 2, - // Let mp_input_test_dragging() return true, even if inside the mouse area. - MP_INPUT_ALLOW_VO_DRAGGING = 4, - // Don't force mouse pointer visible, even if inside the mouse area. - MP_INPUT_ALLOW_HIDE_CURSOR = 8, -}; - // Arbitrary upper bound for sanity. #define MP_CMD_MAX_ARGS 100 diff --git a/input/input.h b/input/input.h index 6322eb88db..1641f9fad6 100644 --- a/input/input.h +++ b/input/input.h @@ -47,6 +47,19 @@ struct mp_input_src { void *priv; }; +enum mp_input_section_flags { + // If a key binding is not defined in the current section, do not search the + // other sections for it (like the default section). Instead, an unbound + // key warning will be printed. + MP_INPUT_EXCLUSIVE = 1, + // Prefer it to other sections. + MP_INPUT_ON_TOP = 2, + // Let mp_input_test_dragging() return true, even if inside the mouse area. + MP_INPUT_ALLOW_VO_DRAGGING = 4, + // Don't force mouse pointer visible, even if inside the mouse area. + MP_INPUT_ALLOW_HIDE_CURSOR = 8, +}; + // Add an input source that runs on a thread. The source is automatically // removed if the thread loop exits. // ctx: this is passed to loop_fn. -- cgit v1.2.3 From b440f6dfb3d29651d8dcb7abfeb8ed18e3f2b995 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 6 May 2018 18:27:18 +0200 Subject: command: add infrastructure for async commands This enables two types of command behavior: 1. Plain async behavior, like "loadfile" not completing until the file is fully loaded. 2. Running parts of the command on worker threads, e.g. for I/O, such as "sub-add" doing network accesses on a thread while the core continues. Both have no implementation yet, and most new code is actually inactive. The plan is to implement a number of useful cases in the following commits. The most tricky part is handling internal keybindings (input.conf) and the multi-command feature (concatenating commands with ";"). It requires a bunch of roundabout code to make it do the expected thing in combination with async commands. There is the question how commands should be handled that come in at a higher rate than what can be handled by the core. Currently, it will simply queue up input.conf commands as long as memory lasts. The client API is limited by the size of the reply queue per client. For commands which require a worker thread, the thread pool is limited to 30 threads, and then will queue up work in memory. The number is completely arbitrary. --- input/cmd.c | 7 ++++++- input/cmd.h | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index ceee2c7403..7405e1e5dd 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -48,7 +48,8 @@ static const struct flag cmd_flags[] = { {"expand-properties", 0, MP_EXPAND_PROPERTIES}, {"raw", MP_EXPAND_PROPERTIES, 0}, {"repeatable", 0, MP_ALLOW_REPEAT}, - {"async", 0, MP_ASYNC_CMD}, + {"async", MP_SYNC_CMD, MP_ASYNC_CMD}, + {"sync", MP_ASYNC_CMD, MP_SYNC_CMD}, {0} }; @@ -127,6 +128,10 @@ static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) m_option_copy(opt, &arg.v, opt->defval); MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); } + + if (!(cmd->flags & (MP_ASYNC_CMD | MP_SYNC_CMD))) + cmd->flags |= cmd->def->default_async ? MP_ASYNC_CMD : MP_SYNC_CMD; + return true; } diff --git a/input/cmd.h b/input/cmd.h index 7fc3ee63c5..8ee536f0e1 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -42,6 +42,18 @@ struct mp_cmd_def { bool is_abort; bool is_soft_abort; bool is_ignore; + bool default_async; // default to MP_ASYNC flag if none set by user + // If you set this, handler() must ensure mp_cmd_ctx_complete() is called + // at some point (can be after handler() returns). If you don't set it, the + // common code will call mp_cmd_ctx_complete() when handler() returns. + // You must make sure that the core cannot disappear while you do work. The + // common code keeps the core referenced only until handler() returns. + bool exec_async; + // If set, handler() is run on a separate worker thread. This means you can + // use mp_core_[un]lock() to temporarily unlock and re-lock the core (while + // unlocked, you have no synchronized access to mpctx, but you can do long + // running operations without blocking playback or input handling). + bool spawn_thread; }; enum mp_cmd_flags { @@ -51,7 +63,11 @@ enum mp_cmd_flags { MP_ON_OSD_MSG = 4, // force a message, if applicable MP_EXPAND_PROPERTIES = 8, // expand strings as properties MP_ALLOW_REPEAT = 16, // if used as keybinding, allow key repeat - MP_ASYNC_CMD = 32, + + // Exactly one of the following 2 bits is set. Which one is used depends on + // the command parser (prefixes and mp_cmd_def.default_async). + MP_ASYNC_CMD = 32, // do not wait for command to complete + MP_SYNC_CMD = 64, // block on command completion MP_ON_OSD_FLAGS = MP_ON_OSD_NO | MP_ON_OSD_AUTO | MP_ON_OSD_BAR | MP_ON_OSD_MSG, -- cgit v1.2.3 From 95c4f0f7698879f28cec7159f03fc7fd211bcff7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 9 May 2018 22:39:24 +0200 Subject: input: remove unused field This was forgotten in commit fb9bbf2a0d7f8. --- input/cmd.h | 1 - 1 file changed, 1 deletion(-) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index 8ee536f0e1..01f2d8356f 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -89,7 +89,6 @@ struct mp_cmd_arg { }; typedef struct mp_cmd { - int id; char *name; struct mp_cmd_arg *args; int nargs; -- cgit v1.2.3 From cc2490ea7eb4dc1480bc26d62a3bbb15d387c35b Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 10 May 2018 15:57:36 +0200 Subject: input: add a define for the number of mouse buttons and use it (Why the fuck are there up to 20 mouse buttons?) --- input/keycodes.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'input') diff --git a/input/keycodes.h b/input/keycodes.h index 740ca873fc..604fee53dc 100644 --- a/input/keycodes.h +++ b/input/keycodes.h @@ -135,6 +135,8 @@ #define MP_KEY_IS_MOUSE_BTN_DBL(code) \ ((code) >= MP_MBTN_DBL_BASE && (code) < MP_MBTN_DBL_END) +#define MP_KEY_MOUSE_BTN_COUNT (MP_MBTN_END - MP_MBTN_BASE) + // Apple Remote input module #define MP_AR_BASE (MP_KEY_BASE+0xE0) #define MP_AR_PLAY (MP_AR_BASE + 0) -- cgit v1.2.3 From 1157f07c5b8b97112f9a6bde695aff8072a88fb2 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 14:48:24 +0200 Subject: node: move a mpv_node helper from ipc.c to shared code This particular one is needed in a following commit. --- input/ipc.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'input') diff --git a/input/ipc.c b/input/ipc.c index fc1e11b981..386a03b909 100644 --- a/input/ipc.c +++ b/input/ipc.c @@ -20,23 +20,12 @@ #include "common/msg.h" #include "input/input.h" #include "misc/json.h" +#include "misc/node.h" #include "options/m_option.h" #include "options/options.h" #include "options/path.h" #include "player/client.h" -static mpv_node *mpv_node_map_get(mpv_node *src, const char *key) -{ - if (src->format != MPV_FORMAT_NODE_MAP) - return NULL; - - for (int i = 0; i < src->u.list->num; i++) - if (!strcmp(key, src->u.list->keys[i])) - return &src->u.list->values[i]; - - return NULL; -} - static mpv_node *mpv_node_array_get(mpv_node *src, int index) { if (src->format != MPV_FORMAT_NODE_ARRAY) @@ -217,9 +206,9 @@ static char *json_execute_command(struct mpv_handle *client, void *ta_parent, goto error; } - reqid_node = mpv_node_map_get(&msg_node, "request_id"); + reqid_node = node_map_get(&msg_node, "request_id"); - mpv_node *cmd_node = mpv_node_map_get(&msg_node, "command"); + mpv_node *cmd_node = node_map_get(&msg_node, "command"); if (!cmd_node || (cmd_node->format != MPV_FORMAT_NODE_ARRAY) || !cmd_node->u.list->num) -- cgit v1.2.3 From 1aae88b4879f40c68cebbdcd47895787ecdcdf68 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 14:50:07 +0200 Subject: input: add glue code for named arguments Named arguments should make it easier to have long time compatibility, even if command arguments get added or removed. They're also much nicer for commands with a large number of arguments, especially if many arguments are optional. As of this commit, this can not be used, because there is no command yet which supports them. See the following commit. --- input/cmd.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 143 insertions(+), 42 deletions(-) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index 7405e1e5dd..c637ec22bd 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -18,6 +18,7 @@ #include #include "misc/bstr.h" +#include "misc/node.h" #include "common/common.h" #include "common/msg.h" #include "options/m_option.h" @@ -30,8 +31,10 @@ static void destroy_cmd(void *ptr) { struct mp_cmd *cmd = ptr; - for (int n = 0; n < cmd->nargs; n++) - m_option_free(cmd->args[n].type, &cmd->args[n].v); + for (int n = 0; n < cmd->nargs; n++) { + if (cmd->args[n].type) + m_option_free(cmd->args[n].type, &cmd->args[n].v); + } } struct flag { @@ -111,22 +114,37 @@ static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i) return opt && opt->type ? opt : NULL; } -// Verify that there are missing args, fill in missing optional args. +// Return the name of the argument, possibly as stack allocated string (which is +// why this is a macro, and out of laziness). Otherwise as get_arg_type(). +#define get_arg_name(cmd, i) \ + ((i) < MP_CMD_DEF_MAX_ARGS && (cmd)->args[(i)].name && \ + (cmd)->args[(i)].name[0] \ + ? (cmd)->args[(i)].name : mp_tprintf(10, "%d", (i) + 1)) + +// Verify that there are no missing args, fill in missing optional args. static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) { - for (int i = cmd->nargs; i < MP_CMD_DEF_MAX_ARGS; i++) { + for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) { + // (type==NULL is used for yet unset arguments) + if (i < cmd->nargs && cmd->args[i].type) + continue; const struct m_option *opt = get_arg_type(cmd->def, i); - if (!opt || is_vararg(cmd->def, i)) + if (i >= cmd->nargs && (!opt || is_vararg(cmd->def, i))) break; if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) { - mp_err(log, "Command %s: more than %d arguments required.\n", - cmd->name, cmd->nargs); + mp_err(log, "Command %s: required argument %s not set.\n", + cmd->name, get_arg_name(cmd->def, i)); return false; } struct mp_cmd_arg arg = {.type = opt}; if (opt->defval) m_option_copy(opt, &arg.v, opt->defval); - MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); + assert(i <= cmd->nargs); + if (i == cmd->nargs) { + MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); + } else { + cmd->args[i] = arg; + } } if (!(cmd->flags & (MP_ASYNC_CMD | MP_SYNC_CMD))) @@ -135,14 +153,54 @@ static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) return true; } -struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) +static bool set_node_arg(struct mp_log *log, struct mp_cmd *cmd, int i, + mpv_node *val) { - struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); - talloc_set_destructor(cmd, destroy_cmd); - *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 }; + const char *name = get_arg_name(cmd->def, i); - if (node->format != MPV_FORMAT_NODE_ARRAY) - goto error; + const struct m_option *opt = get_arg_type(cmd->def, i); + if (!opt) { + mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i); + return false; + } + + if (i < cmd->nargs && cmd->args[i].type) { + mp_err(log, "Command %s: argument %s was already set.\n", cmd->name, name); + return false; + } + + struct mp_cmd_arg arg = {.type = opt}; + void *dst = &arg.v; + if (val->format == MPV_FORMAT_STRING) { + int r = m_option_parse(log, opt, bstr0(cmd->name), + bstr0(val->u.string), dst); + if (r < 0) { + mp_err(log, "Command %s: argument %s can't be parsed: %s.\n", + cmd->name, name, m_option_strerror(r)); + return false; + } + } else { + int r = m_option_set_node(opt, dst, val); + if (r < 0) { + mp_err(log, "Command %s: argument %s has incompatible type.\n", + cmd->name, name); + return false; + } + } + + // (leave unset arguments blank, to be set later or checked by finish_cmd()) + while (i >= cmd->nargs) { + struct mp_cmd_arg t = {0}; + MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, t); + } + + cmd->args[i] = arg; + return true; +} + +static bool cmd_node_array(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node) +{ + assert(node->format == MPV_FORMAT_NODE_ARRAY); mpv_node_list *args = node->u.list; int cur = 0; @@ -158,47 +216,90 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING) cmd_name = bstr0(args->values[cur++].u.string); if (!find_cmd(log, cmd, cmd_name)) - goto error; + return false; int first = cur; for (int i = 0; i < args->num - first; i++) { - const struct m_option *opt = get_arg_type(cmd->def, i); - if (!opt) { - mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i); - goto error; - } - mpv_node *val = &args->values[cur++]; - struct mp_cmd_arg arg = {.type = opt}; - void *dst = &arg.v; - if (val->format == MPV_FORMAT_STRING) { - int r = m_option_parse(log, opt, bstr0(cmd->name), - bstr0(val->u.string), dst); - if (r < 0) { - mp_err(log, "Command %s: argument %d can't be parsed: %s.\n", - cmd->name, i + 1, m_option_strerror(r)); - goto error; + if (!set_node_arg(log, cmd, cmd->nargs, &args->values[cur++])) + return false; + } + + return true; +} + +static bool cmd_node_map(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node) +{ + assert(node->format == MPV_FORMAT_NODE_MAP); + mpv_node_list *args = node->u.list; + + mpv_node *name = node_map_get(node, "name"); + if (!name || name->format != MPV_FORMAT_STRING) + return false; + + if (!find_cmd(log, cmd, bstr0(name->u.string))) + return false; + + for (int n = 0; n < args->num; n++) { + const char *key = args->keys[n]; + mpv_node *val = &args->values[n]; + + if (strcmp(key, "name") == 0) { + // already handled above + } else if (strcmp(key, "_flags") == 0) { + if (val->format != MPV_FORMAT_NODE_ARRAY) + return false; + mpv_node_list *flags = val->u.list; + for (int i = 0; i < flags->num; i++) { + if (flags->values[i].format != MPV_FORMAT_STRING) + return false; + if (!apply_flag(cmd, bstr0(flags->values[i].u.string))) + return false; } } else { - int r = m_option_set_node(opt, dst, val); - if (r < 0) { - mp_err(log, "Command %s: argument %d has incompatible type.\n", - cmd->name, i + 1); - goto error; + int arg = -1; + + for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) { + const char *arg_name = cmd->def->args[i].name; + if (arg_name && arg_name[0] && strcmp(key, arg_name) == 0) { + arg = i; + break; + } + } + + if (arg < 0) { + mp_err(log, "Command %s: no argument %s.\n", cmd->name, key); + return false; } + + if (!set_node_arg(log, cmd, arg, val)) + return false; } - MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); } - if (!finish_cmd(log, cmd)) - goto error; + return true; +} + +struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) +{ + struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); + talloc_set_destructor(cmd, destroy_cmd); + *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 }; + + bool res = false; + if (node->format == MPV_FORMAT_NODE_ARRAY) { + res = cmd_node_array(log, cmd, node); + } else if (node->format == MPV_FORMAT_NODE_MAP) { + res = cmd_node_map(log, cmd, node); + } + + res = res && finish_cmd(log, cmd); + + if (!res) + TA_FREEP(&cmd); return cmd; -error: - talloc_free(cmd); - return NULL; } - static bool read_token(bstr str, bstr *out_rest, bstr *out_token) { bstr t = bstr_lstrip(str); -- cgit v1.2.3 From d9bc97bda6e4750af2fbbfcb51ddb6b2c04c277b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 15:14:07 +0200 Subject: command: add a subprocess command This supports named arguments. It benefits from the infrastructure of async commands. The plan is to reimplement Lua's utils.subprocess() on top of it. --- input/cmd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index 01f2d8356f..0e65565252 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -80,6 +80,7 @@ struct mp_cmd_arg { const struct m_option *type; union { int i; + int64_t i64; float f; double d; char *s; -- cgit v1.2.3 From e4fb23ed7de874bb2d05824d7edb84cfd1b21101 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 12 May 2018 18:46:37 +0200 Subject: command: add a way to abort asynchronous commands Many asynchronous commands are potentially long running operations, such as loading something from network or running a foreign process. Obviously it shouldn't just be possible for them to freeze the player if they don't terminate as expected. Also, there will be situations where you want to explicitly stop some of those operations explicitly. So add an infrastructure for this. Commands have to support this explicitly. The next commit uses this to actually add support to a command. --- input/cmd.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index 0e65565252..f6408988ab 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -54,6 +54,10 @@ struct mp_cmd_def { // unlocked, you have no synchronized access to mpctx, but you can do long // running operations without blocking playback or input handling). bool spawn_thread; + // If this is set, mp_cmd_ctx.abort is set. Set this if handler() can do + // asynchronous abort of the command, and explicitly uses mp_cmd_ctx.abort. + // (Not setting it when it's not needed can save resources.) + bool can_abort; }; enum mp_cmd_flags { -- cgit v1.2.3 From b44ea70209c40a6e1600f6c59d5c73ef6685645b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 13 May 2018 12:19:11 +0200 Subject: ipc: alias set_property_string to set_property The only effective difference is that the former explicitly checks whether the JSON value type is string, and errors out if not. The rest is exactly the same (mpv_set_property_string is mpv_set_property with MPV_FORMAT_STRING). It seems silly to keep this, so just remove it. --- input/ipc.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'input') diff --git a/input/ipc.c b/input/ipc.c index 386a03b909..d0a66efde9 100644 --- a/input/ipc.c +++ b/input/ipc.c @@ -275,7 +275,9 @@ static char *json_execute_command(struct mpv_handle *client, void *ta_parent, mpv_node_map_add_string(ta_parent, &reply_node, "data", result); mpv_free(result); } - } else if (!strcmp("set_property", cmd)) { + } else if (!strcmp("set_property", cmd) || + !strcmp("set_property_string", cmd)) + { if (cmd_node->u.list->num != 3) { rc = MPV_ERROR_INVALID_PARAMETER; goto error; @@ -288,25 +290,6 @@ static char *json_execute_command(struct mpv_handle *client, void *ta_parent, rc = mpv_set_property(client, cmd_node->u.list->values[1].u.string, MPV_FORMAT_NODE, &cmd_node->u.list->values[2]); - } else if (!strcmp("set_property_string", cmd)) { - if (cmd_node->u.list->num != 3) { - rc = MPV_ERROR_INVALID_PARAMETER; - goto error; - } - - if (cmd_node->u.list->values[1].format != MPV_FORMAT_STRING) { - rc = MPV_ERROR_INVALID_PARAMETER; - goto error; - } - - if (cmd_node->u.list->values[2].format != MPV_FORMAT_STRING) { - rc = MPV_ERROR_INVALID_PARAMETER; - goto error; - } - - rc = mpv_set_property_string(client, - cmd_node->u.list->values[1].u.string, - cmd_node->u.list->values[2].u.string); } else if (!strcmp("observe_property", cmd)) { if (cmd_node->u.list->num != 3) { rc = MPV_ERROR_INVALID_PARAMETER; -- cgit v1.2.3 From 11c74068b2791909dff2b4ef33f11884230166f0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 13 May 2018 12:21:00 +0200 Subject: ipc: cosmetic: switch a negated if/else --- input/ipc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'input') diff --git a/input/ipc.c b/input/ipc.c index d0a66efde9..5a820c9ad2 100644 --- a/input/ipc.c +++ b/input/ipc.c @@ -269,11 +269,11 @@ static char *json_execute_command(struct mpv_handle *client, void *ta_parent, char *result = mpv_get_property_string(client, cmd_node->u.list->values[1].u.string); - if (!result) { - mpv_node_map_add_null(ta_parent, &reply_node, "data"); - } else { + if (result) { mpv_node_map_add_string(ta_parent, &reply_node, "data", result); mpv_free(result); + } else { + mpv_node_map_add_null(ta_parent, &reply_node, "data"); } } else if (!strcmp("set_property", cmd) || !strcmp("set_property_string", cmd)) -- cgit v1.2.3 From fc574ee5634112c21ee4b61d8f9b7517ec3192a2 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 13 May 2018 12:46:50 +0200 Subject: ipc: some user-visible changes to prepare for making all commands async I wanted to put all commands through mpv_command_node_async() instead of mpv_command_node(). Using synchronous commands over a synchronous transport doesn't make sense anyway. This would have used the request_id field in IPC requests as reply ID for the async commands. But the latter need to be [u]int64, while the former can be any type. To avoid that we need an extra lookup table for mapping reply IDs to request_id values, we now require that request_id fields are integers. Since this would be an incompatible change, just deprecate non-integers for now, and plan the change for a later time. --- input/ipc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'input') diff --git a/input/ipc.c b/input/ipc.c index 5a820c9ad2..6568feacfa 100644 --- a/input/ipc.c +++ b/input/ipc.c @@ -207,6 +207,10 @@ static char *json_execute_command(struct mpv_handle *client, void *ta_parent, } reqid_node = node_map_get(&msg_node, "request_id"); + if (reqid_node && reqid_node->format != MPV_FORMAT_INT64) { + mp_warn(log, "'request_id' must be an integer. Using other types is " + "deprecated and will trigger an error in the future!\n"); + } mpv_node *cmd_node = node_map_get(&msg_node, "command"); if (!cmd_node || @@ -404,6 +408,8 @@ error: */ if (reqid_node) { mpv_node_map_add(ta_parent, &reply_node, "request_id", reqid_node); + } else { + mpv_node_map_add_int64(ta_parent, &reply_node, "request_id", 0); } mpv_node_map_add_string(ta_parent, &reply_node, "error", mpv_error_string(rc)); -- cgit v1.2.3 From 332907e1d7225ae39565d462aac5c45c3a5cad97 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 17 May 2018 20:10:39 +0200 Subject: command: give named arguments to almost all commands Before this change, only 1 command or so had named arguments. There is no reason why other commands can't have them, except that it's a bit of work to add them. Commands with variable number of arguments are inherently incompatible to named arguments, such as the "run" command. They still have dummy names, but obviously you can't assign multiple values to a single named argument (unless the argument has an array type, which would be something different). For now, disallow using named argument APIs with these commands. This might change later. 2 commands are adjusted to not need a separate default value by changing flag constants. (The numeric values are C only and can't be set by users.) Make the command syntax in the manpage more consistent. Now none of the allowed choice/flag names are in the command header, and all arguments are shown with their proper name and quoted with <...>. Some places in the manpage and the client.h doxygen are updated to reflect that most commands support named arguments. In addition, try to improve the documentation of the syntax and need for escaping etc. as well. (Or actually most uses of the word "argument" should be "parameter".) --- input/cmd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index c637ec22bd..7e50f0784b 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -239,6 +239,13 @@ static bool cmd_node_map(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node) if (!find_cmd(log, cmd, bstr0(name->u.string))) return false; + if (cmd->def->vararg) { + mp_err(log, "Command %s: this command uses a variable number of " + "arguments, which does not work with named arguments.\n", + cmd->name); + return false; + } + for (int n = 0; n < args->num; n++) { const char *key = args->keys[n]; mpv_node *val = &args->values[n]; -- cgit v1.2.3 From 0a7a4779a3772b26c6675910146b0d5a825d8b08 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 17 May 2018 20:34:31 +0200 Subject: input: slightly improve --input-cmdlist output Output argument names, whether varargs are used, and indicate optional arguments correctly (instead of only half of them). --- input/cmd.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index 7e50f0784b..0a6bd73931 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -597,12 +597,13 @@ void mp_print_cmd_list(struct mp_log *out) const struct mp_cmd_def *def = &mp_cmds[i]; mp_info(out, "%-20.20s", def->name); for (int j = 0; j < MP_CMD_DEF_MAX_ARGS && def->args[j].type; j++) { - const char *type = def->args[j].type->name; - if (def->args[j].defval) - mp_info(out, " [%s]", type); - else - mp_info(out, " %s", type); + const struct m_option *arg = &def->args[j]; + bool is_opt = arg->defval || (arg->flags & MP_CMD_OPT_ARG); + mp_info(out, " %s%s=%s%s", is_opt ? "[" : "", arg->name, + arg->type->name, is_opt ? "]" : ""); } + if (def->vararg) + mp_info(out, "..."); // essentially append to last argument mp_info(out, "\n"); } } -- cgit v1.2.3 From 12bd4fe9abce797a3c98dc437579b163e0a8162a Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 17 May 2018 20:40:53 +0200 Subject: cmd: do not use a random value for MP_CMD_OPT_ARG This flag is used only by the command parser. Its value overlapped with some of the existing m_option flags, but only flags that did not matter for the command parser (i.e. the flag bits used had mostly private uses in each component). It's still a bit unclean and dangerous to use an essentially random value, so reuse M_OPT_OPTIONAL_PARAM for it. Since M_OPT_OPTIONAL_PARAM has a slightly longer name than MP_CMD_OPT_ARG, I'm going to keep the old name. --- input/cmd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index f6408988ab..0f74424f87 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -24,7 +24,7 @@ #include "options/m_option.h" #define MP_CMD_DEF_MAX_ARGS 9 -#define MP_CMD_OPT_ARG 0x1000 +#define MP_CMD_OPT_ARG M_OPT_OPTIONAL_PARAM struct mp_log; struct mp_cmd; -- cgit v1.2.3 From 12d1404b04e90f5357882e5c1048d92305248cb9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 18 May 2018 21:38:17 +0200 Subject: player: make various commands for managing external tracks abortable Until now, they could be aborted only by ending playback, and calling mpv_abort_async_command didn't do anything. This requires furthering the mess how playback abort is done. The main reason why mp_cancel exists at all is to avoid that a "frozen" demuxer (blocked on network I/O or whatever) cannot freeze the core. The core should always get its way. Previously, there was a single mp_cancel handle, that could be signaled, and all demuxers would unfreeze. With external files, we might want to abort loading of a certain external file, which automatically means they need a separate mp_cancel. So give every demuxer its own mp_cancel, and "slave" it to whatever parent mp_cancel handles aborting. Since the mpv demuxer API conflates creating the demuxer and reading the file headers, mp_cancel strictly need to be created before the demuxer is created (or we couldn't abort loading). Although we give every demuxer its own mp_cancel (as "enforced" by cancel_and_free_demuxer), it's still rather messy to create/destroy it along with the demuxer. --- input/cmd.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'input') diff --git a/input/cmd.h b/input/cmd.h index 0f74424f87..a2cbaae510 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -58,6 +58,9 @@ struct mp_cmd_def { // asynchronous abort of the command, and explicitly uses mp_cmd_ctx.abort. // (Not setting it when it's not needed can save resources.) bool can_abort; + // If playback ends, and the command is still running, an abort is + // automatically triggered. + bool abort_on_playback_end; }; enum mp_cmd_flags { -- cgit v1.2.3 From b3968ecf05c11cf42c8120be3a1d716c4cc71b5d Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 19 May 2018 18:44:03 +0200 Subject: input: remove now unused "abort command" and cancel infrastructure The previous commit removed all uses. --- input/cmd.c | 28 ---------------------------- input/cmd.h | 7 ------- input/input.c | 33 ++------------------------------- input/input.h | 4 ---- 4 files changed, 2 insertions(+), 70 deletions(-) (limited to 'input') diff --git a/input/cmd.c b/input/cmd.c index 0a6bd73931..f0bb53e040 100644 --- a/input/cmd.c +++ b/input/cmd.c @@ -552,34 +552,6 @@ void mp_cmd_dump(struct mp_log *log, int msgl, char *header, struct mp_cmd *cmd) mp_msg(log, msgl, "]\n"); } -// 0: no, 1: maybe, 2: sure -static int is_abort_cmd(struct mp_cmd *cmd) -{ - if (cmd->def->is_abort) - return 2; - if (cmd->def->is_soft_abort) - return 1; - if (cmd->def == &mp_cmd_list) { - int r = 0; - for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) { - int x = is_abort_cmd(sub); - r = MPMAX(r, x); - } - return r; - } - return 0; -} - -bool mp_input_is_maybe_abort_cmd(struct mp_cmd *cmd) -{ - return is_abort_cmd(cmd) >= 1; -} - -bool mp_input_is_abort_cmd(struct mp_cmd *cmd) -{ - return is_abort_cmd(cmd) >= 2; -} - bool mp_input_is_repeatable_cmd(struct mp_cmd *cmd) { return (cmd->def->allow_auto_repeat) || cmd->def == &mp_cmd_list || diff --git a/input/cmd.h b/input/cmd.h index a2cbaae510..2d9c922230 100644 --- a/input/cmd.h +++ b/input/cmd.h @@ -39,8 +39,6 @@ struct mp_cmd_def { bool on_updown; // always emit it on both up and down key events bool vararg; // last argument can be given 0 to multiple times bool scalable; - bool is_abort; - bool is_soft_abort; bool is_ignore; bool default_async; // default to MP_ASYNC flag if none set by user // If you set this, handler() must ensure mp_cmd_ctx_complete() is called @@ -121,11 +119,6 @@ typedef struct mp_cmd { extern const struct mp_cmd_def mp_cmds[]; extern const struct mp_cmd_def mp_cmd_list; -// Executing this command will maybe abort playback (play something else, or quit). -bool mp_input_is_maybe_abort_cmd(struct mp_cmd *cmd); -// This command will definitely abort playback. -bool mp_input_is_abort_cmd(struct mp_cmd *cmd); - bool mp_input_is_repeatable_cmd(struct mp_cmd *cmd); bool mp_input_is_scalable_cmd(struct mp_cmd *cmd); diff --git a/input/input.c b/input/input.c index 20c39dd4ac..c8f1a64ad5 100644 --- a/input/input.c +++ b/input/input.c @@ -153,9 +153,6 @@ struct input_ctx { struct cmd_queue cmd_queue; - void (*cancel)(void *cancel_ctx); - void *cancel_ctx; - void (*wakeup_cb)(void *ctx); void *wakeup_ctx; }; @@ -531,13 +528,11 @@ static void release_down_cmd(struct input_ctx *ictx, bool drop_current) } // We don't want the append to the command queue indefinitely, because that -// could lead to situations where recovery would take too long. On the other -// hand, don't drop commands that will abort playback. +// could lead to situations where recovery would take too long. static bool should_drop_cmd(struct input_ctx *ictx, struct mp_cmd *cmd) { struct cmd_queue *queue = &ictx->cmd_queue; - return queue_count_cmds(queue) >= ictx->opts->key_fifo_size && - !mp_input_is_abort_cmd(cmd); + return queue_count_cmds(queue) >= ictx->opts->key_fifo_size; } static struct mp_cmd *resolve_key(struct input_ctx *ictx, int code) @@ -883,26 +878,10 @@ static void adjust_max_wait_time(struct input_ctx *ictx, double *time) } } -static bool test_abort_cmd(struct input_ctx *ictx, struct mp_cmd *new) -{ - if (!mp_input_is_maybe_abort_cmd(new)) - return false; - if (mp_input_is_abort_cmd(new)) - return true; - // Abort only if there are going to be at least 2 commands in the queue. - for (struct mp_cmd *cmd = ictx->cmd_queue.first; cmd; cmd = cmd->queue_next) { - if (mp_input_is_maybe_abort_cmd(cmd)) - return true; - } - return false; -} - int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd) { input_lock(ictx); if (cmd) { - if (ictx->cancel && test_abort_cmd(ictx, cmd)) - ictx->cancel(ictx->cancel_ctx); queue_add_tail(&ictx->cmd_queue, cmd); mp_input_wakeup(ictx); } @@ -1423,14 +1402,6 @@ void mp_input_uninit(struct input_ctx *ictx) talloc_free(ictx); } -void mp_input_set_cancel(struct input_ctx *ictx, void (*cb)(void *c), void *c) -{ - input_lock(ictx); - ictx->cancel = cb; - ictx->cancel_ctx = c; - input_unlock(ictx); -} - bool mp_input_use_alt_gr(struct input_ctx *ictx) { input_lock(ictx); diff --git a/input/input.h b/input/input.h index 1641f9fad6..ff194785c9 100644 --- a/input/input.h +++ b/input/input.h @@ -193,10 +193,6 @@ double mp_input_get_delay(struct input_ctx *ictx); // Wake up sleeping input loop from another thread. void mp_input_wakeup(struct input_ctx *ictx); -// Used to asynchronously abort playback. Needed because the core still can -// block on network in some situations. -void mp_input_set_cancel(struct input_ctx *ictx, void (*cb)(void *c), void *c); - // If this returns true, use Right Alt key as Alt Gr to produce special // characters. If false, count Right Alt as the modifier Alt key. bool mp_input_use_alt_gr(struct input_ctx *ictx); -- cgit v1.2.3 From f8ab59eacdde31af39f4defeb964adf4de140a50 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 21 May 2018 16:25:52 +0200 Subject: player: get rid of mpv_global.opts This was always a legacy thing. Remove it by applying an orgy of mp_get_config_group() calls, and sometimes m_config_cache_alloc() or mp_read_option_raw(). win32 changes untested. --- input/input.c | 7 +++++-- input/ipc-unix.c | 7 +++++-- input/ipc-win.c | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) (limited to 'input') diff --git a/input/input.c b/input/input.c index c8f1a64ad5..9e96da267d 100644 --- a/input/input.c +++ b/input/input.c @@ -1370,8 +1370,11 @@ void mp_input_load_config(struct input_ctx *ictx) } #if HAVE_WIN32_PIPES - if (ictx->global->opts->input_file && *ictx->global->opts->input_file) - mp_input_pipe_add(ictx, ictx->global->opts->input_file); + char *ifile; + mp_read_option_raw(ictx->global, "input-file", &m_option_type_string, &ifile); + if (ifile && ifile[0]) + mp_input_pipe_add(ictx, ifile); + talloc_free(ifile); #endif input_unlock(ictx); diff --git a/input/ipc-unix.c b/input/ipc-unix.c index 778f2f1e46..d39623fe4f 100644 --- a/input/ipc-unix.c +++ b/input/ipc-unix.c @@ -36,6 +36,7 @@ #include "common/msg.h" #include "input/input.h" #include "libmpv/client.h" +#include "options/m_config.h" #include "options/options.h" #include "options/path.h" #include "player/client.h" @@ -386,7 +387,7 @@ done: struct mp_ipc_ctx *mp_init_ipc(struct mp_client_api *client_api, struct mpv_global *global) { - struct MPOpts *opts = global->opts; + struct MPOpts *opts = mp_get_config_group(NULL, global, GLOBAL_CONFIG); struct mp_ipc_ctx *arg = talloc_ptrtype(NULL, arg); *arg = (struct mp_ipc_ctx){ @@ -397,10 +398,12 @@ struct mp_ipc_ctx *mp_init_ipc(struct mp_client_api *client_api, }; char *input_file = mp_get_user_path(arg, global, opts->input_file); + talloc_free(opts); + if (input_file && *input_file) ipc_start_client_text(arg, input_file); - if (!opts->ipc_path || !*opts->ipc_path) + if (!arg->path || !arg->path[0]) goto out; if (mp_make_wakeup_pipe(arg->death_pipe) < 0) diff --git a/input/ipc-win.c b/input/ipc-win.c index 3cbdad3749..8bfbaf409b 100644 --- a/input/ipc-win.c +++ b/input/ipc-win.c @@ -29,6 +29,7 @@ #include "common/msg.h" #include "input/input.h" #include "libmpv/client.h" +#include "options/m_config.h" #include "options/options.h" #include "player/client.h" @@ -449,7 +450,7 @@ done: struct mp_ipc_ctx *mp_init_ipc(struct mp_client_api *client_api, struct mpv_global *global) { - struct MPOpts *opts = global->opts; + struct MPOpts *opts = mp_get_config_group(NULL, global, GLOBAL_CONFIG); struct mp_ipc_ctx *arg = talloc_ptrtype(NULL, arg); *arg = (struct mp_ipc_ctx){ @@ -478,12 +479,14 @@ struct mp_ipc_ctx *mp_init_ipc(struct mp_client_api *client_api, if (pthread_create(&arg->thread, NULL, ipc_thread, arg)) goto out; + talloc_free(opts); return arg; out: if (arg->death_event) CloseHandle(arg->death_event); talloc_free(arg); + talloc_free(opts); return NULL; } -- cgit v1.2.3