History

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.

wm4 committed 8 years ago

Commit 1aae88b1 parent 1157f074 files · +248 −47Browse files

Unified · Split

4 files
  1. DOCS/man/input.rst +75 −0
  2. DOCS/man/lua.rst +11 −1
  3. input/cmd.c +143 −42
  4. libmpv/client.h +19 −4
DOCS/man/input.rst+75−0
@@ -78,6 +78,16 @@ that matches, and the multi-key command will never be called. Intermediate keys · 77 unmodified lines
7878 can be remapped to ``ignore`` in order to avoid this issue. The maximum number
7979 of (non-modifier) keys for combinations is currently 4.
8080
81+Named arguments
82+---------------
83+
84+Some commands support named arguments (most currently don't). Named arguments
85+cannot be used with the "flat" input.conf syntax shown above, but require using
86+e.g. ``mp.command_native()`` in Lua scripting, or e.g. ``mpv_command_node()``
87+with the libmpv API. Some commands ask you to only use named arguments (because
88+the command order is not guaranteed), which means you can't use them as
89+key bindings in input.conf at all.
90+
8191 List of Input Commands
8292 ----------------------
8393
@@ -283,6 +293,71 @@ List of Input Commands · 199 unmodified lines
283293 execute arbitrary shell commands. It is recommended to write a small
284294 shell script, and call that with ``run``.
285295
296+``subprocess``
297+ Similar to ``run``, but gives more control about process execution to the
298+ caller, and does does not detach the process.
299+
300+ This has the following named arguments. The order of them is not guaranteed,
301+ so you should always call them with named arguments, see `Named arguments`_.
302+
303+ ``args`` (``MPV_FORMAT_NODE_ARRAY[MPV_FORMAT_STRING]``)
304+ Array of strings with the command as first argument, and subsequent
305+ command line arguments following. This is just like the ``run`` command
306+ argument list.
307+
308+ ``playback_only`` (``MPV_FORMAT_FLAG``)
309+ Boolean indicating whether the process should be killed when playback
310+ terminates (optional, default: yes). If enabled, stopping playback
311+ will automatically kill the process, and you can't start it outside of
312+ playback.
313+
314+ ``capture_size`` (``MPV_FORMAT_INT64``)
315+ Integer setting the maximum number of stdout plus stderr bytes that can
316+ be captured (optional, default: 64MB). If the number of bytes exceeds
317+ this, capturing is stopped. The limit is per captured stream.
318+
319+ ``capture_stdout`` (``MPV_FORMAT_FLAG``)
320+ Capture all data the process outputs to stdout and return it once the
321+ process ends (optional, default: no).
322+
323+ ``capture_stderr`` (``MPV_FORMAT_FLAG``)
324+ Same as ``capture_stdout``, but for stderr.
325+
326+ The command returns the following result (as ``MPV_FORMAT_NODE_MAP``):
327+
328+ ``status`` (``MPV_FORMAT_INT64``)
329+ The raw exit status of the process. It will be negative on error. The
330+ meaning of negative values is undefined, other than meaning error (and
331+ does not necessarily correspond to OS low level exit status values).
332+
333+ On Windows, it can happen that a negative return value is returned
334+ even if the process exits gracefully, because the win32 ``UINT`` exit
335+ code is assigned to an ``int`` variable before being set as ``int64_t``
336+ field in the result map. This might be fixed later.
337+
338+ ``stdout`` (``MPV_FORMAT_BYTE_ARRAY``)
339+ Captured stdout stream, limited to ``capture_size``.
340+
341+ ``stderr`` (``MPV_FORMAT_BYTE_ARRAY``)
342+ Same as ``stdout``, but for stderr.
343+
344+ ``error_string`` (``MPV_FORMAT_STRING``)
345+ Empty string if the process exited gracefully. The string ``killed`` if
346+ the process was terminated in an unusual way. The string ``init`` if the
347+ process could not be started.
348+
349+ On Windows, ``killed`` is only returned when the process has been
350+ killed by mpv as a result of ``playback_only`` being set to ``yes``.
351+
352+ ``killed_by_us`` (``MPV_FORMAT_FLAG``)
353+ Set to ``yes`` if the process has been killed by mpv as a result
354+ of ``playback_only`` being set to ``yes``.
355+
356+ Note that the command itself will always return success as long as the
357+ parameters are correct. Whether the process could be spawned or whether
358+ it was somehow killed or returned an error status has to be queried from
359+ the result value.
360+
286361 ``quit [<code>]``
287362 Exit the player. If an argument is given, it's used as process exit code.
288363
DOCS/man/lua.rst+11−1
@@ -109,7 +109,17 @@ The ``mp`` module is preloaded, although it can be loaded manually with · 108 unmodified lines
109109 ``mp.command_native(table [,def])``
110110 Similar to ``mp.commandv``, but pass the argument list as table. This has
111111 the advantage that in at least some cases, arguments can be passed as
112− native types.
112+ native types. It also allows you to use named argument.
113+
114+ If the table is an array, each array item is like an argument in
115+ ``mp.commandv()`` (but can be a native type instead of a string).
116+
117+ If the table contains string keys, it's interpreted as command with named
118+ arguments. This requires at least an entry with the key ``name`` to be
119+ present, which must be a string, and contains the command name. The special
120+ entry ``_flags`` is optional, and if present, must be an array of
121+ `Input Command Prefixes`_ to apply. All other entries are interpreted as
122+ arguments.
113123
114124 Returns a result table on success (usually empty), or ``def, error`` on
115125 error. ``def`` is the second parameter provided to the function, and is
input/cmd.c+143−42
@@ -18,6 +18,7 @@ · 17 unmodified lines
1818 #include <stddef.h>
1919
2020 #include "misc/bstr.h"
21+#include "misc/node.h"
2122 #include "common/common.h"
2223 #include "common/msg.h"
2324 #include "options/m_option.h"
@@ -30,8 +31,10 @@ · 6 unmodified lines
3031 static void destroy_cmd(void *ptr)
3132 {
3233 struct mp_cmd *cmd = ptr;
33− for (int n = 0; n < cmd->nargs; n++)
34− m_option_free(cmd->args[n].type, &cmd->args[n].v);
34+ for (int n = 0; n < cmd->nargs; n++) {
35+ if (cmd->args[n].type)
36+ m_option_free(cmd->args[n].type, &cmd->args[n].v);
37+ }
3538 }
3639
3740 struct flag {
@@ -111,22 +114,37 @@ static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i) · 73 unmodified lines
111114 return opt && opt->type ? opt : NULL;
112115 }
113116
114−// Verify that there are missing args, fill in missing optional args.
117+// Return the name of the argument, possibly as stack allocated string (which is
118+// why this is a macro, and out of laziness). Otherwise as get_arg_type().
119+#define get_arg_name(cmd, i) \
120+ ((i) < MP_CMD_DEF_MAX_ARGS && (cmd)->args[(i)].name && \
121+ (cmd)->args[(i)].name[0] \
122+ ? (cmd)->args[(i)].name : mp_tprintf(10, "%d", (i) + 1))
123+
124+// Verify that there are no missing args, fill in missing optional args.
115125 static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd)
116126 {
117− for (int i = cmd->nargs; i < MP_CMD_DEF_MAX_ARGS; i++) {
127+ for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) {
128+ // (type==NULL is used for yet unset arguments)
129+ if (i < cmd->nargs && cmd->args[i].type)
130+ continue;
118131 const struct m_option *opt = get_arg_type(cmd->def, i);
119− if (!opt || is_vararg(cmd->def, i))
132+ if (i >= cmd->nargs && (!opt || is_vararg(cmd->def, i)))
120133 break;
121134 if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) {
122− mp_err(log, "Command %s: more than %d arguments required.\n",
123− cmd->name, cmd->nargs);
135+ mp_err(log, "Command %s: required argument %s not set.\n",
136+ cmd->name, get_arg_name(cmd->def, i));
124137 return false;
125138 }
126139 struct mp_cmd_arg arg = {.type = opt};
127140 if (opt->defval)
128141 m_option_copy(opt, &arg.v, opt->defval);
129− MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
142+ assert(i <= cmd->nargs);
143+ if (i == cmd->nargs) {
144+ MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
145+ } else {
146+ cmd->args[i] = arg;
147+ }
130148 }
131149
132150 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) · 2 unmodified lines
135153 return true;
136154 }
137155
138−struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node)
156+static bool set_node_arg(struct mp_log *log, struct mp_cmd *cmd, int i,
157+ mpv_node *val)
139158 {
140− struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd);
141− talloc_set_destructor(cmd, destroy_cmd);
142− *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 };
159+ const char *name = get_arg_name(cmd->def, i);
143160
144− if (node->format != MPV_FORMAT_NODE_ARRAY)
145− goto error;
161+ const struct m_option *opt = get_arg_type(cmd->def, i);
162+ if (!opt) {
163+ mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i);
164+ return false;
165+ }
166+
167+ if (i < cmd->nargs && cmd->args[i].type) {
168+ mp_err(log, "Command %s: argument %s was already set.\n", cmd->name, name);
169+ return false;
170+ }
171+
172+ struct mp_cmd_arg arg = {.type = opt};
173+ void *dst = &arg.v;
174+ if (val->format == MPV_FORMAT_STRING) {
175+ int r = m_option_parse(log, opt, bstr0(cmd->name),
176+ bstr0(val->u.string), dst);
177+ if (r < 0) {
178+ mp_err(log, "Command %s: argument %s can't be parsed: %s.\n",
179+ cmd->name, name, m_option_strerror(r));
180+ return false;
181+ }
182+ } else {
183+ int r = m_option_set_node(opt, dst, val);
184+ if (r < 0) {
185+ mp_err(log, "Command %s: argument %s has incompatible type.\n",
186+ cmd->name, name);
187+ return false;
188+ }
189+ }
190+
191+ // (leave unset arguments blank, to be set later or checked by finish_cmd())
192+ while (i >= cmd->nargs) {
193+ struct mp_cmd_arg t = {0};
194+ MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, t);
195+ }
196+
197+ cmd->args[i] = arg;
198+ return true;
199+}
200+
201+static bool cmd_node_array(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node)
202+{
203+ assert(node->format == MPV_FORMAT_NODE_ARRAY);
146204 mpv_node_list *args = node->u.list;
147205 int cur = 0;
148206
@@ -158,47 +216,90 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) · 9 unmodified lines
158216 if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING)
159217 cmd_name = bstr0(args->values[cur++].u.string);
160218 if (!find_cmd(log, cmd, cmd_name))
161− goto error;
219+ return false;
162220
163221 int first = cur;
164222 for (int i = 0; i < args->num - first; i++) {
165− const struct m_option *opt = get_arg_type(cmd->def, i);
166− if (!opt) {
167− mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i);
168− goto error;
169− }
170− mpv_node *val = &args->values[cur++];
171− struct mp_cmd_arg arg = {.type = opt};
172− void *dst = &arg.v;
173− if (val->format == MPV_FORMAT_STRING) {
174− int r = m_option_parse(log, opt, bstr0(cmd->name),
175− bstr0(val->u.string), dst);
176− if (r < 0) {
177− mp_err(log, "Command %s: argument %d can't be parsed: %s.\n",
178− cmd->name, i + 1, m_option_strerror(r));
179− goto error;
223+ if (!set_node_arg(log, cmd, cmd->nargs, &args->values[cur++]))
224+ return false;
225+ }
226+
227+ return true;
228+}
229+
230+static bool cmd_node_map(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node)
231+{
232+ assert(node->format == MPV_FORMAT_NODE_MAP);
233+ mpv_node_list *args = node->u.list;
234+
235+ mpv_node *name = node_map_get(node, "name");
236+ if (!name || name->format != MPV_FORMAT_STRING)
237+ return false;
238+
239+ if (!find_cmd(log, cmd, bstr0(name->u.string)))
240+ return false;
241+
242+ for (int n = 0; n < args->num; n++) {
243+ const char *key = args->keys[n];
244+ mpv_node *val = &args->values[n];
245+
246+ if (strcmp(key, "name") == 0) {
247+ // already handled above
248+ } else if (strcmp(key, "_flags") == 0) {
249+ if (val->format != MPV_FORMAT_NODE_ARRAY)
250+ return false;
251+ mpv_node_list *flags = val->u.list;
252+ for (int i = 0; i < flags->num; i++) {
253+ if (flags->values[i].format != MPV_FORMAT_STRING)
254+ return false;
255+ if (!apply_flag(cmd, bstr0(flags->values[i].u.string)))
256+ return false;
180257 }
181258 } else {
182− int r = m_option_set_node(opt, dst, val);
183− if (r < 0) {
184− mp_err(log, "Command %s: argument %d has incompatible type.\n",
185− cmd->name, i + 1);
186− goto error;
259+ int arg = -1;
260+
261+ for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) {
262+ const char *arg_name = cmd->def->args[i].name;
263+ if (arg_name && arg_name[0] && strcmp(key, arg_name) == 0) {
264+ arg = i;
265+ break;
266+ }
267+ }
268+
269+ if (arg < 0) {
270+ mp_err(log, "Command %s: no argument %s.\n", cmd->name, key);
271+ return false;
187272 }
273+
274+ if (!set_node_arg(log, cmd, arg, val))
275+ return false;
188276 }
189− MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
190277 }
191278
192− if (!finish_cmd(log, cmd))
193− goto error;
279+ return true;
280+}
281+
282+struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node)
283+{
284+ struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd);
285+ talloc_set_destructor(cmd, destroy_cmd);
286+ *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 };
287+
288+ bool res = false;
289+ if (node->format == MPV_FORMAT_NODE_ARRAY) {
290+ res = cmd_node_array(log, cmd, node);
291+ } else if (node->format == MPV_FORMAT_NODE_MAP) {
292+ res = cmd_node_map(log, cmd, node);
293+ }
294+
295+ res = res && finish_cmd(log, cmd);
296+
297+ if (!res)
298+ TA_FREEP(&cmd);
194299
195300 return cmd;
196−error:
197− talloc_free(cmd);
198− return NULL;
199301 }
200302
201−
202303 static bool read_token(bstr str, bstr *out_rest, bstr *out_token)
203304 {
204305 bstr t = bstr_lstrip(str);
libmpv/client.h+19−4
@@ -941,10 +941,25 @@ int mpv_command(mpv_handle *ctx, const char **args); · 940 unmodified lines
941941 *
942942 * Does not use OSD and string expansion by default.
943943 *
944− * @param[in] args mpv_node with format set to MPV_FORMAT_NODE_ARRAY; each entry
945− * is an argument using an arbitrary format (the format must be
946− * compatible to the used command). Usually, the first item is
947− * the command name (as MPV_FORMAT_STRING).
944+ * The args argument can have one of the following formats:
945+ *
946+ * MPV_FORMAT_NODE_ARRAY:
947+ * Positional arguments. Each entry is an argument using an arbitrary
948+ * format (the format must be compatible to the used command). Usually,
949+ * the first item is the command name (as MPV_FORMAT_STRING). The order
950+ * of arguments is as documented in each command description.
951+ *
952+ * MPV_FORMAT_NODE_MAP:
953+ * Named arguments. This requires at least an entry with the key "name"
954+ * to be present, which must be a string, and contains the command name.
955+ * The special entry "_flags" is optional, and if present, must be an
956+ * array of strings, each being a command prefix to apply. All other
957+ * entries are interpreted as arguments. They must use the argument names
958+ * as documented in each command description. Currently, most commands do
959+ * not support named arguments at all.
960+ *
961+ * @param[in] args mpv_node with format set to one of the values documented
962+ * above (see there for details)
948963 * @param[out] result Optional, pass NULL if unused. If not NULL, and if the
949964 * function succeeds, this is set to command-specific return
950965 * data. You must call mpv_free_node_contents() to free it

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t