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
78can be remapped to ``ignore`` in order to avoid this issue. The maximum number78can be remapped to ``ignore`` in order to avoid this issue. The maximum number
79of (non-modifier) keys for combinations is currently 4.79of (non-modifier) keys for combinations is currently 4.
80 80
81Named arguments
82---------------
83
84Some commands support named arguments (most currently don't). Named arguments
85cannot be used with the "flat" input.conf syntax shown above, but require using
86e.g. ``mp.command_native()`` in Lua scripting, or e.g. ``mpv_command_node()``
87with the libmpv API. Some commands ask you to only use named arguments (because
88the command order is not guaranteed), which means you can't use them as
89key bindings in input.conf at all.
90
81List of Input Commands91List of Input Commands
82----------------------92----------------------
83 93
@@ -283,6 +293,71 @@ List of Input Commands · 199 unmodified lines
283 execute arbitrary shell commands. It is recommended to write a small293 execute arbitrary shell commands. It is recommended to write a small
284 shell script, and call that with ``run``.294 shell script, and call that with ``run``.
285 295
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
286``quit [<code>]``361``quit [<code>]``
287 Exit the player. If an argument is given, it's used as process exit code.362 Exit the player. If an argument is given, it's used as process exit code.
288 363
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
109``mp.command_native(table [,def])``109``mp.command_native(table [,def])``
110 Similar to ``mp.commandv``, but pass the argument list as table. This has110 Similar to ``mp.commandv``, but pass the argument list as table. This has
111 the advantage that in at least some cases, arguments can be passed as111 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.
113 123
114 Returns a result table on success (usually empty), or ``def, error`` on124 Returns a result table on success (usually empty), or ``def, error`` on
115 error. ``def`` is the second parameter provided to the function, and is125 error. ``def`` is the second parameter provided to the function, and is
input/cmd.c+143−42
@@ -18,6 +18,7 @@ · 17 unmodified lines
18#include <stddef.h>18#include <stddef.h>
19 19
20#include "misc/bstr.h"20#include "misc/bstr.h"
21#include "misc/node.h"
21#include "common/common.h"22#include "common/common.h"
22#include "common/msg.h"23#include "common/msg.h"
23#include "options/m_option.h"24#include "options/m_option.h"
@@ -30,8 +31,10 @@ · 6 unmodified lines
30static void destroy_cmd(void *ptr)31static void destroy_cmd(void *ptr)
31{32{
32 struct mp_cmd *cmd = ptr;33 struct mp_cmd *cmd = ptr;
33 for (int n = 0; n < cmd->nargs; n++)34 for (int n = 0; n < cmd->nargs; n++) {
34 m_option_free(cmd->args[n].type, &cmd->args[n].v);35 if (cmd->args[n].type)
36 m_option_free(cmd->args[n].type, &cmd->args[n].v);
37 }
35}38}
36 39
37struct flag {40struct flag {
@@ -111,22 +114,37 @@ static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i) · 73 unmodified lines
111 return opt && opt->type ? opt : NULL;114 return opt && opt->type ? opt : NULL;
112}115}
113 116
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.
115static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd)125static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd)
116{126{
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;
118 const struct m_option *opt = get_arg_type(cmd->def, i);131 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)))
120 break;133 break;
121 if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) {134 if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) {
122 mp_err(log, "Command %s: more than %d arguments required.\n",135 mp_err(log, "Command %s: required argument %s not set.\n",
123 cmd->name, cmd->nargs);136 cmd->name, get_arg_name(cmd->def, i));
124 return false;137 return false;
125 }138 }
126 struct mp_cmd_arg arg = {.type = opt};139 struct mp_cmd_arg arg = {.type = opt};
127 if (opt->defval)140 if (opt->defval)
128 m_option_copy(opt, &arg.v, opt->defval);141 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 }
130 }148 }
131 149
132 if (!(cmd->flags & (MP_ASYNC_CMD | MP_SYNC_CMD)))150 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
135 return true;153 return true;
136}154}
137 155
138struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node)156static bool set_node_arg(struct mp_log *log, struct mp_cmd *cmd, int i,
157 mpv_node *val)
139{158{
140 struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd);159 const char *name = get_arg_name(cmd->def, i);
141 talloc_set_destructor(cmd, destroy_cmd);
142 *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 };
143 160
144 if (node->format != MPV_FORMAT_NODE_ARRAY)161 const struct m_option *opt = get_arg_type(cmd->def, i);
145 goto error;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
201static bool cmd_node_array(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node)
202{
203 assert(node->format == MPV_FORMAT_NODE_ARRAY);
146 mpv_node_list *args = node->u.list;204 mpv_node_list *args = node->u.list;
147 int cur = 0;205 int cur = 0;
148 206
@@ -158,47 +216,90 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) · 9 unmodified lines
158 if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING)216 if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING)
159 cmd_name = bstr0(args->values[cur++].u.string);217 cmd_name = bstr0(args->values[cur++].u.string);
160 if (!find_cmd(log, cmd, cmd_name))218 if (!find_cmd(log, cmd, cmd_name))
161 goto error;219 return false;
162 220
163 int first = cur;221 int first = cur;
164 for (int i = 0; i < args->num - first; i++) {222 for (int i = 0; i < args->num - first; i++) {
165 const struct m_option *opt = get_arg_type(cmd->def, i);223 if (!set_node_arg(log, cmd, cmd->nargs, &args->values[cur++]))
166 if (!opt) {224 return false;
167 mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i);225 }
168 goto error;226
169 }227 return true;
170 mpv_node *val = &args->values[cur++];228}
171 struct mp_cmd_arg arg = {.type = opt};229
172 void *dst = &arg.v;230static bool cmd_node_map(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node)
173 if (val->format == MPV_FORMAT_STRING) {231{
174 int r = m_option_parse(log, opt, bstr0(cmd->name),232 assert(node->format == MPV_FORMAT_NODE_MAP);
175 bstr0(val->u.string), dst);233 mpv_node_list *args = node->u.list;
176 if (r < 0) {234
177 mp_err(log, "Command %s: argument %d can't be parsed: %s.\n",235 mpv_node *name = node_map_get(node, "name");
178 cmd->name, i + 1, m_option_strerror(r));236 if (!name || name->format != MPV_FORMAT_STRING)
179 goto error;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;
180 }257 }
181 } else {258 } else {
182 int r = m_option_set_node(opt, dst, val);259 int arg = -1;
183 if (r < 0) {260
184 mp_err(log, "Command %s: argument %d has incompatible type.\n",261 for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) {
185 cmd->name, i + 1);262 const char *arg_name = cmd->def->args[i].name;
186 goto error;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;
187 }272 }
273
274 if (!set_node_arg(log, cmd, arg, val))
275 return false;
188 }276 }
189 MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
190 }277 }
191 278
192 if (!finish_cmd(log, cmd))279 return true;
193 goto error;280}
281
282struct 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);
194 299
195 return cmd;300 return cmd;
196error:
197 talloc_free(cmd);
198 return NULL;
199}301}
200 302
201
202static bool read_token(bstr str, bstr *out_rest, bstr *out_token)303static bool read_token(bstr str, bstr *out_rest, bstr *out_token)
203{304{
204 bstr t = bstr_lstrip(str);305 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
941 *941 *
942 * Does not use OSD and string expansion by default.942 * Does not use OSD and string expansion by default.
943 *943 *
944 * @param[in] args mpv_node with format set to MPV_FORMAT_NODE_ARRAY; each entry944 * The args argument can have one of the following formats:
945 * is an argument using an arbitrary format (the format must be945 *
946 * compatible to the used command). Usually, the first item is946 * MPV_FORMAT_NODE_ARRAY:
947 * the command name (as MPV_FORMAT_STRING).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)
948 * @param[out] result Optional, pass NULL if unused. If not NULL, and if the963 * @param[out] result Optional, pass NULL if unused. If not NULL, and if the
949 * function succeeds, this is set to command-specific return964 * function succeeds, this is set to command-specific return
950 * data. You must call mpv_free_node_contents() to free it965 * 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