diff options
-rw-r--r-- | input/cmd_list.h | 92 | ||||
-rw-r--r-- | input/cmd_parse.c | 3 | ||||
-rw-r--r-- | player/command.c | 1770 | ||||
-rw-r--r-- | player/command.h | 19 |
4 files changed, 1018 insertions, 866 deletions
diff --git a/input/cmd_list.h b/input/cmd_list.h index c6d7c66100..5f44602907 100644 --- a/input/cmd_list.h +++ b/input/cmd_list.h @@ -25,10 +25,13 @@ #define MP_CMD_OPT_ARG 0x1000 +struct mp_cmd_ctx; + struct mp_cmd_def { - int id; // one of MP_CMD_... const char *name; // user-visible name (as used in input.conf) + void (*handler)(void *ctx); const struct m_option args[MP_CMD_DEF_MAX_ARGS]; + const void *priv; // for free use by handler() bool allow_auto_repeat; // react to repeated key events bool on_updown; // always emit it on both up and down key events bool vararg; // last argument can be given 0 to multiple times @@ -40,93 +43,6 @@ struct mp_cmd_def { extern const struct mp_cmd_def mp_cmds[]; -// All command IDs -enum mp_command_type { - MP_CMD_IGNORE, - MP_CMD_SEEK, - MP_CMD_REVERT_SEEK, - MP_CMD_QUIT, - MP_CMD_QUIT_WATCH_LATER, - MP_CMD_PLAYLIST_NEXT, - MP_CMD_PLAYLIST_PREV, - MP_CMD_SCREENSHOT, - MP_CMD_SCREENSHOT_TO_FILE, - MP_CMD_SCREENSHOT_RAW, - MP_CMD_LOADFILE, - MP_CMD_LOADLIST, - MP_CMD_PLAYLIST_CLEAR, - MP_CMD_PLAYLIST_REMOVE, - MP_CMD_PLAYLIST_MOVE, - MP_CMD_PLAYLIST_SHUFFLE, - MP_CMD_SUB_STEP, - MP_CMD_SUB_SEEK, - MP_CMD_TV_LAST_CHANNEL, - MP_CMD_FRAME_STEP, - MP_CMD_FRAME_BACK_STEP, - MP_CMD_RUN, - MP_CMD_SUB_ADD, - MP_CMD_SUB_REMOVE, - MP_CMD_SUB_RELOAD, - MP_CMD_SET, - MP_CMD_CHANGE_LIST, - MP_CMD_PRINT_TEXT, - MP_CMD_SHOW_TEXT, - MP_CMD_EXPAND_TEXT, - MP_CMD_SHOW_PROGRESS, - MP_CMD_ADD, - MP_CMD_CYCLE, - MP_CMD_MULTIPLY, - MP_CMD_CYCLE_VALUES, - MP_CMD_STOP, - MP_CMD_AUDIO_ADD, - MP_CMD_AUDIO_REMOVE, - MP_CMD_AUDIO_RELOAD, - - MP_CMD_ENABLE_INPUT_SECTION, - MP_CMD_DISABLE_INPUT_SECTION, - MP_CMD_DEFINE_INPUT_SECTION, - - MP_CMD_AB_LOOP, - - MP_CMD_DROP_BUFFERS, - - MP_CMD_MOUSE, - MP_CMD_KEYPRESS, - MP_CMD_KEYDOWN, - MP_CMD_KEYUP, - - /// Audio Filter commands - MP_CMD_AF, - MP_CMD_AF_COMMAND, - MP_CMD_AO_RELOAD, - - /// Video filter commands - MP_CMD_VF, - MP_CMD_VF_COMMAND, - - /// Internal for Lua scripts - MP_CMD_SCRIPT_BINDING, - MP_CMD_SCRIPT_MESSAGE, - MP_CMD_SCRIPT_MESSAGE_TO, - - MP_CMD_OVERLAY_ADD, - MP_CMD_OVERLAY_REMOVE, - - MP_CMD_WRITE_WATCH_LATER_CONFIG, - - MP_CMD_HOOK_ADD, - MP_CMD_HOOK_ACK, - - MP_CMD_RESCAN_EXTERNAL_FILES, - - MP_CMD_APPLY_PROFILE, - - MP_CMD_LOAD_SCRIPT, - - // Internal - MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p -}; - // Executing this command will maybe abort playback (play something else, or quit). struct mp_cmd; bool mp_input_is_maybe_abort_cmd(struct mp_cmd *cmd); diff --git a/input/cmd_parse.c b/input/cmd_parse.c index ae518fdf98..f401e5b9c7 100644 --- a/input/cmd_parse.c +++ b/input/cmd_parse.c @@ -29,7 +29,6 @@ #include "libmpv/client.h" const struct mp_cmd_def mp_cmd_list = { - .id = MP_CMD_COMMAND_LIST, .name = "list", }; @@ -87,7 +86,6 @@ static bool find_cmd(struct mp_log *log, struct mp_cmd *cmd, bstr name) if (strcmp(nname, mp_cmds[n].name) == 0) { cmd->def = &mp_cmds[n]; cmd->name = (char *)cmd->def->name; - cmd->id = cmd->def->id; return true; } } @@ -341,7 +339,6 @@ mp_cmd_t *mp_input_parse_cmd_(struct mp_log *log, bstr str, const char *loc) struct mp_cmd *list = talloc_ptrtype(NULL, list); talloc_set_destructor(list, destroy_cmd); *list = (struct mp_cmd) { - .id = mp_cmd_list.id, .name = (char *)mp_cmd_list.name, .def = &mp_cmd_list, .original = bstrdup(list, original), diff --git a/player/command.c b/player/command.c index c820a1ceb5..d5cedb235a 100644 --- a/player/command.c +++ b/player/command.c @@ -4582,11 +4582,16 @@ static void replace_overlay(struct MPContext *mpctx, int id, struct overlay *new recreate_overlays(mpctx); } -static int overlay_add(struct MPContext *mpctx, int id, int x, int y, - char *file, int offset, char *fmt, int w, int h, - int stride) +static void cmd_overlay_add(void *pcmd) { - int r = -1; + struct mp_cmd_ctx *cmd = pcmd; + struct MPContext *mpctx = cmd->mpctx; + int id = cmd->args[0].v.i, x = cmd->args[1].v.i, y = cmd->args[2].v.i; + char *file = cmd->args[3].v.s; + int offset = cmd->args[4].v.i; + char *fmt = cmd->args[5].v.s; + int w = cmd->args[6].v.i, h = cmd->args[7].v.i, stride = cmd->args[8].v.i; + if (strcmp(fmt, "bgra") != 0) { MP_ERR(mpctx, "overlay-add: unsupported OSD format '%s'\n", fmt); goto error; @@ -4644,15 +4649,18 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y, munmap(p, map_size); replace_overlay(mpctx, id, &overlay); - r = 0; + return; error: - return r; + cmd->success = false; } -static void overlay_remove(struct MPContext *mpctx, int id) +static void cmd_overlay_remove(void *p) { - struct command_ctx *cmd = mpctx->command_ctx; - if (id >= 0 && id < cmd->num_overlays) + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + struct command_ctx *cmdctx = mpctx->command_ctx; + int id = cmd->args[0].v.i; + if (id >= 0 && id < cmdctx->num_overlays) replace_overlay(mpctx, id, &(struct overlay){0}); } @@ -4662,7 +4670,7 @@ static void overlay_uninit(struct MPContext *mpctx) if (!mpctx->osd) return; for (int id = 0; id < cmd->num_overlays; id++) - overlay_remove(mpctx, id); + replace_overlay(mpctx, id, &(struct overlay){0}); osd_set_external2(mpctx->osd, NULL); for (int n = 0; n < 2; n++) mp_image_unrefp(&cmd->overlay_osd[n].packed); @@ -4708,36 +4716,32 @@ static bool check_property_scalable(char *property, struct MPContext *mpctx) prop.type == &m_option_type_aspect; } -static int show_property_status(struct MPContext *mpctx, struct mp_cmd *cmd, - const char *name, int r) +static void show_property_status(struct mp_cmd_ctx *cmd, const char *name, int r) { + struct MPContext *mpctx = cmd->mpctx; struct MPOpts *opts = mpctx->opts; int osd_duration = opts->osd_duration; - int on_osd = cmd->flags & MP_ON_OSD_FLAGS; - bool auto_osd = on_osd == MP_ON_OSD_AUTO; - bool msg_osd = auto_osd || (on_osd & MP_ON_OSD_MSG); - int osdl = msg_osd ? 1 : OSD_LEVEL_INVISIBLE; + int osdl = cmd->msg_osd ? 1 : OSD_LEVEL_INVISIBLE; if (r == M_PROPERTY_OK || r == M_PROPERTY_UNAVAILABLE) { - show_property_osd(mpctx, name, on_osd); + show_property_osd(mpctx, name, cmd->on_osd); if (r == M_PROPERTY_UNAVAILABLE) - return -1; + cmd->success = false; } else if (r == M_PROPERTY_UNKNOWN) { set_osd_msg(mpctx, osdl, osd_duration, "Unknown property: '%s'", name); - return -1; + cmd->success = false; } else if (r <= 0) { set_osd_msg(mpctx, osdl, osd_duration, "Failed to set property '%s'", name); - return -1; + cmd->success = false; } - return 0; } -static int change_property_cmd(struct MPContext *mpctx, struct mp_cmd *cmd, - const char *name, int action, void *arg) +static void change_property_cmd(struct mp_cmd_ctx *cmd, + const char *name, int action, void *arg) { - int r = mp_property_do(name, action, arg, mpctx); - return show_property_status(mpctx, cmd, name, r); + int r = mp_property_do(name, action, arg, cmd->mpctx); + show_property_status(cmd, name, r); } static bool compare_values(struct m_option *type, void *a, void *b) @@ -4752,8 +4756,10 @@ static bool compare_values(struct m_option *type, void *a, void *b) return res; } -static int cycle_values_cmd(struct MPContext *mpctx, struct mp_cmd *cmd) +static void cmd_cycle_values(void *p) { + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; int first = 0, dir = 1; if (strcmp(cmd->args[first].v.s, "!reverse") == 0) { @@ -4764,26 +4770,31 @@ static int cycle_values_cmd(struct MPContext *mpctx, struct mp_cmd *cmd) const char *name = cmd->args[first].v.s; first += 1; - if (first >= cmd->nargs) { + if (first >= cmd->num_args) { MP_ERR(mpctx, "cycle-values command does not have any value arguments.\n"); - return -1; + cmd->success = false; + return; } struct m_option prop = {0}; int r = mp_property_do(name, M_PROPERTY_GET_TYPE, &prop, mpctx); - if (r <= 0) - return show_property_status(mpctx, cmd, name, r); + if (r <= 0) { + show_property_status(cmd, name, r); + return; + } union m_option_value curval = {0}; r = mp_property_do(name, M_PROPERTY_GET, &curval, mpctx); - if (r <= 0) - return show_property_status(mpctx, cmd, name, r); + if (r <= 0) { + show_property_status(cmd, name, r); + return; + } // Find the current value. Note that we even though compare_values() uses // strings internally, we need to convert the cycle-values arguments to // native anyway to "normalize" the value for comparison. int current = -1; - for (int n = first; n < cmd->nargs; n++) { + for (int n = first; n < cmd->num_args; n++) { union m_option_value val = {0}; if (m_option_parse(mpctx->log, &prop, bstr0(name), bstr0(cmd->args[n].v.s), &val) <= 0) @@ -4803,32 +4814,37 @@ static int cycle_values_cmd(struct MPContext *mpctx, struct mp_cmd *cmd) if (current >= 0) { current += dir; if (current < first) - current = cmd->nargs - 1; - if (current >= cmd->nargs) + current = cmd->num_args - 1; + if (current >= cmd->num_args) current = first; } else { MP_VERBOSE(mpctx, "Current value not found. Picking default.\n"); - current = dir > 0 ? first : cmd->nargs - 1; + current = dir > 0 ? first : cmd->num_args - 1; } - return change_property_cmd(mpctx, cmd, name, M_PROPERTY_SET_STRING, - cmd->args[current].v.s); + change_property_cmd(cmd, name, M_PROPERTY_SET_STRING, cmd->args[current].v.s); } int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res) { - struct command_ctx *cmdctx = mpctx->command_ctx; - struct MPOpts *opts = mpctx->opts; - int osd_duration = opts->osd_duration; - int on_osd = cmd->flags & MP_ON_OSD_FLAGS; - bool auto_osd = on_osd == MP_ON_OSD_AUTO; - bool msg_osd = auto_osd || (on_osd & MP_ON_OSD_MSG); - bool bar_osd = auto_osd || (on_osd & MP_ON_OSD_BAR); - bool seek_msg_osd = auto_osd ? opts->osd_on_seek & 2 : msg_osd; - bool seek_bar_osd = auto_osd ? opts->osd_on_seek & 1 : bar_osd; + struct mpv_node dummy_node = {0}; + struct mp_cmd_ctx *ctx = &(struct mp_cmd_ctx){ + .mpctx = mpctx, + .cmd = cmd, + .args = cmd->args, + .num_args = cmd->nargs, + .priv = cmd->def->priv, + .success = true, + .result = res ? res : &dummy_node, + }; - int osdl = msg_osd ? 1 : OSD_LEVEL_INVISIBLE; - bool async = cmd->flags & MP_ASYNC_CMD; + struct MPOpts *opts = mpctx->opts; + ctx->on_osd = cmd->flags & MP_ON_OSD_FLAGS; + bool auto_osd = ctx->on_osd == MP_ON_OSD_AUTO; + ctx->msg_osd = auto_osd || (ctx->on_osd & MP_ON_OSD_MSG); + ctx->bar_osd = auto_osd || (ctx->on_osd & MP_ON_OSD_BAR); + ctx->seek_msg_osd = auto_osd ? opts->osd_on_seek & 2 : ctx->msg_osd; + ctx->seek_bar_osd = auto_osd ? opts->osd_on_seek & 1 : ctx->bar_osd; mp_cmd_dump(mpctx->log, cmd->def->is_ignore ? MSGL_TRACE : MSGL_DEBUG, "Run command:", cmd); @@ -4845,774 +4861,959 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re } } - switch (cmd->id) { - case MP_CMD_SEEK: { - double v = cmd->args[0].v.d * cmd->scale; - int abs = cmd->args[1].v.i & 3; - enum seek_precision precision = MPSEEK_DEFAULT; - switch (((cmd->args[2].v.i | cmd->args[1].v.i) >> 3) & 3) { - case 1: precision = MPSEEK_KEYFRAME; break; - case 2: precision = MPSEEK_EXACT; break; - } - if (!mpctx->playback_initialized) - return -1; - mark_seek(mpctx); - switch (abs) { - case 0: { // Relative seek - queue_seek(mpctx, MPSEEK_RELATIVE, v, precision, MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, (v > 0) ? OSD_FFW : OSD_REW); - break; - } - case 1: { // Absolute seek by percentage - double ratio = v / 100.0; - double cur_pos = get_current_pos_ratio(mpctx, false); - queue_seek(mpctx, MPSEEK_FACTOR, ratio, precision, MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, cur_pos < ratio ? OSD_FFW : OSD_REW); - break; - } - case 2: { // Absolute seek to a timestamp in seconds - if (v < 0) { - // Seek from end - double len = get_time_length(mpctx); - if (len < 0) - return -1; - v = MPMAX(0, len + v); + if (cmd->def == &mp_cmd_list) { + for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) + run_command(mpctx, sub, NULL); + } else { + assert(cmd->def->handler); + cmd->def->handler(ctx); + } + + if (!ctx->success) + mpv_free_node_contents(ctx->result); + + mpv_free_node_contents(&dummy_node); + + return ctx->success ? 0 : -1; +} + +static void cmd_seek(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + + double v = cmd->args[0].v.d * cmd->cmd->scale; + int abs = cmd->args[1].v.i & 3; + enum seek_precision precision = MPSEEK_DEFAULT; + switch (((cmd->args[2].v.i | cmd->args[1].v.i) >> 3) & 3) { + case 1: precision = MPSEEK_KEYFRAME; break; + case 2: precision = MPSEEK_EXACT; break; + } + if (!mpctx->playback_initialized) { + cmd->success = false; + return; + } + + mark_seek(mpctx); + switch (abs) { + case 0: { // Relative seek + queue_seek(mpctx, MPSEEK_RELATIVE, v, precision, MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, (v > 0) ? OSD_FFW : OSD_REW); + break; + } + case 1: { // Absolute seek by percentage + double ratio = v / 100.0; + double cur_pos = get_current_pos_ratio(mpctx, false); + queue_seek(mpctx, MPSEEK_FACTOR, ratio, precision, MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, cur_pos < ratio ? OSD_FFW : OSD_REW); + break; + } + case 2: { // Absolute seek to a timestamp in seconds + if (v < 0) { + // Seek from end + double len = get_time_length(mpctx); + if (len < 0) { + cmd->success = false; + return; } - queue_seek(mpctx, MPSEEK_ABSOLUTE, v, precision, MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, - v > get_current_time(mpctx) ? OSD_FFW : OSD_REW); - break; + v = MPMAX(0, len + v); } - case 3: { // Relative seek by percentage - queue_seek(mpctx, MPSEEK_FACTOR, - get_current_pos_ratio(mpctx, false) + v / 100.0, - precision, MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, v > 0 ? OSD_FFW : OSD_REW); - break; - }} - if (seek_bar_osd) + queue_seek(mpctx, MPSEEK_ABSOLUTE, v, precision, MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, + v > get_current_time(mpctx) ? OSD_FFW : OSD_REW); + break; + } + case 3: { // Relative seek by percentage + queue_seek(mpctx, MPSEEK_FACTOR, + get_current_pos_ratio(mpctx, false) + v / 100.0, + precision, MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, v > 0 ? OSD_FFW : OSD_REW); + break; + }} + if (cmd->seek_bar_osd) + mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; + if (cmd->seek_msg_osd) + mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; +} + +static void cmd_revert_seek(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + struct command_ctx *cmdctx = mpctx->command_ctx; + + if (!mpctx->playback_initialized) { + cmd->success = false; + return; + } + + double oldpts = cmdctx->last_seek_pts; + if (cmdctx->marked_pts != MP_NOPTS_VALUE) + oldpts = cmdctx->marked_pts; + if (cmd->args[0].v.i == 1) { + cmdctx->marked_pts = get_current_time(mpctx); + } else if (oldpts != MP_NOPTS_VALUE) { + cmdctx->last_seek_pts = get_current_time(mpctx); + cmdctx->marked_pts = MP_NOPTS_VALUE; + queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, MPSEEK_EXACT, + MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, OSD_REW); + if (cmd->seek_bar_osd) mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; - if (seek_msg_osd) + if (cmd->seek_msg_osd) mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; - break; + } else { + cmd->success = false; } +} - case MP_CMD_REVERT_SEEK: { - if (!mpctx->playback_initialized) - return -1; - double oldpts = cmdctx->last_seek_pts; - if (cmdctx->marked_pts != MP_NOPTS_VALUE) - oldpts = cmdctx->marked_pts; - if (cmd->args[0].v.i == 1) { - cmdctx->marked_pts = get_current_time(mpctx); - } else if (oldpts != MP_NOPTS_VALUE) { - cmdctx->last_seek_pts = get_current_time(mpctx); - cmdctx->marked_pts = MP_NOPTS_VALUE; - queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, MPSEEK_EXACT, - MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, OSD_REW); - if (seek_bar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; - if (seek_msg_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; - } else { - return -1; - } - break; +static void cmd_set(void *p) +{ + struct mp_cmd_ctx *cmd = p; + + change_property_cmd(cmd, cmd->args[0].v.s, + M_PROPERTY_SET_STRING, cmd->args[1].v.s); +} + +static void cmd_change_list(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + char *name = cmd->args[0].v.s; + char *op = cmd->args[1].v.s; + char *value = cmd->args[2].v.s; + int osd_duration = mpctx->opts->osd_duration; + int osdl = cmd->msg_osd ? 1 : OSD_LEVEL_INVISIBLE; + + struct m_config_option *co = m_config_get_co(mpctx->mconfig, bstr0(name)); + if (!co) { + set_osd_msg(mpctx, osdl, osd_duration, "Unknown option: '%s'", name); + cmd->success = false; + return; } - case MP_CMD_SET: { - return change_property_cmd(mpctx, cmd, cmd->args[0].v.s, - M_PROPERTY_SET_STRING, cmd->args[1].v.s); + const struct m_option_type *type = co->opt->type; + bool found = false; + for (int i = 0; type->actions && type->actions[i].name; i++) { + const struct m_option_action *action = &type->actions[i]; + if (strcmp(action->name, op) == 0) + found = true; + } + if (!found) { + set_osd_msg(mpctx, osdl, osd_duration, "Unknown action: '%s'", op); + cmd->success = false; + return; } - case MP_CMD_CHANGE_LIST: { - char *name = cmd->args[0].v.s; - char *op = cmd->args[1].v.s; - char *value = cmd->args[2].v.s; - struct m_config_option *co = m_config_get_co(mpctx->mconfig, bstr0(name)); - if (!co) { - set_osd_msg(mpctx, osdl, osd_duration, "Unknown option: '%s'", name); - return -1; - } - const struct m_option_type *type = co->opt->type; - bool found = false; - for (int i = 0; type->actions && type->actions[i].name; i++) { - const struct m_option_action *action = &type->actions[i]; - if (strcmp(action->name, op) == 0) - found = true; - } - if (!found) { - set_osd_msg(mpctx, osdl, osd_duration, "Unknown action: '%s'", op); - return -1; - } - char *optname = mp_tprintf(80, "%s-%s", name, op); // the dirty truth - int r = m_config_set_option_cli(mpctx->mconfig, bstr0(optname), - bstr0(value), M_SETOPT_RUNTIME); - if (r < 0) { - set_osd_msg(mpctx, osdl, osd_duration, - "Failed setting option: '%s'", name); - return -1; - } - show_property_osd(mpctx, name, on_osd); - break; + char *optname = mp_tprintf(80, "%s-%s", name, op); // the dirty truth + int r = m_config_set_option_cli(mpctx->mconfig, bstr0(optname), + bstr0(value), M_SETOPT_RUNTIME); + if (r < 0) { + set_osd_msg(mpctx, osdl, osd_duration, + "Failed setting option: '%s'", name); + cmd->success = false; + return; } - case MP_CMD_ADD: - case MP_CMD_CYCLE: - { - char *property = cmd->args[0].v.s; - if (cmd->repeated && !check_property_autorepeat(property, mpctx)) { - MP_VERBOSE(mpctx, "Dropping command '%.*s' from auto-repeated key.\n", - BSTR_P(cmd->original)); - break; - } - double scale = 1; - int scale_units = cmd->scale_units; - if (check_property_scalable(property, mpctx)) { - scale = cmd->scale; - scale_units = 1; - } - for (int i = 0; i < scale_units; i++) { - struct m_property_switch_arg s = { - .inc = cmd->args[1].v.d * scale, - .wrap = cmd->id == MP_CMD_CYCLE, - }; - int r = - change_property_cmd(mpctx, cmd, property, M_PROPERTY_SWITCH, &s); - if (r < 0) - return r; - } - break; + show_property_osd(mpctx, name, cmd->on_osd); +} + +static void cmd_add_cycle(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + bool is_cycle = !!cmd->priv; + + char *property = cmd->args[0].v.s; + if (cmd->cmd->repeated && !check_property_autorepeat(property, mpctx)) { + MP_VERBOSE(mpctx, "Dropping command '%.*s' from auto-repeated key.\n", + BSTR_P(cmd->cmd->original)); + return; } - case MP_CMD_MULTIPLY: { - return change_property_cmd(mpctx, cmd, cmd->args[0].v.s, - M_PROPERTY_MULTIPLY, &cmd->args[1].v.d); + double scale = 1; + int scale_units = cmd->cmd->scale_units; + if (check_property_scalable(property, mpctx)) { + scale = cmd->cmd->scale; + scale_units = 1; } - case MP_CMD_CYCLE_VALUES: - return cycle_values_cmd(mpctx, cmd); + for (int i = 0; i < scale_units; i++) { + struct m_property_switch_arg s = { + .inc = cmd->args[1].v.d * scale, + .wrap = is_cycle, + }; + change_property_cmd(cmd, property, M_PROPERTY_SWITCH, &s); + if (!cmd->success) + return; + } +} + +static void cmd_multiply(void *p) +{ + struct mp_cmd_ctx *cmd = p; + + change_property_cmd(cmd, cmd->args[0].v.s, + M_PROPERTY_MULTIPLY, &cmd->args[1].v.d); +} + +static void cmd_frame_step(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; - case MP_CMD_FRAME_STEP: - if (!mpctx->playback_initialized) - return -1; - if (cmd->is_up_down) { - if (cmd->is_up) { - if (mpctx->step_frames < 1) - set_pause_state(mpctx, true); + if (!mpctx->playback_initialized) { + cmd->success = false; + return; + } + + if (cmd->cmd->is_up_down) { + if (cmd->cmd->is_up) { + if (mpctx->step_frames < 1) + set_pause_state(mpctx, true); + } else { + if (cmd->cmd->repeated) { + set_pause_state(mpctx, false); } else { - if (cmd->repeated) { - set_pause_state(mpctx, false); - } else { - add_step_frame(mpctx, 1); - } + add_step_frame(mpctx, 1); } - } else { - add_step_frame(mpctx, 1); } - break; + } else { + add_step_frame(mpctx, 1); + } +} - case MP_CMD_FRAME_BACK_STEP: - if (!mpctx->playback_initialized) - return -1; - add_step_frame(mpctx, -1); - break; +static void cmd_frame_back_step(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; - case MP_CMD_QUIT: - case MP_CMD_QUIT_WATCH_LATER: - if (cmd->id == MP_CMD_QUIT_WATCH_LATER || opts->position_save_on_quit) - mp_write_watch_later_conf(mpctx); - mpctx->stop_play = PT_QUIT; - mpctx->quit_custom_rc = cmd->args[0].v.i; - mpctx->has_quit_custom_rc = true; - mp_wakeup_core(mpctx); - break; + if (!mpctx->playback_initialized) { + cmd->success = false; + return; + } - case MP_CMD_PLAYLIST_NEXT: - case MP_CMD_PLAYLIST_PREV: - { - int dir = cmd->id == MP_CMD_PLAYLIST_PREV ? -1 : +1; - int force = cmd->args[0].v.i; + add_step_frame(mpctx, -1); +} - struct playlist_entry *e = mp_next_file(mpctx, dir, force, true); - if (!e && !force) - return -1; - mp_set_playlist_entry(mpctx, e); - if (on_osd & MP_ON_OSD_MSG) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_CURRENT_FILE; - break; +static void cmd_quit(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + bool write_watch_later = *(bool *)cmd->priv; + if (write_watch_later || mpctx->opts->position_save_on_quit) + mp_write_watch_later_conf(mpctx); + mpctx->stop_play = PT_QUIT; + mpctx->quit_custom_rc = cmd->args[0].v.i; + mpctx->has_quit_custom_rc = true; + mp_wakeup_core(mpctx); +} + +static void cmd_playlist_next_prev(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + int dir = *(int *)cmd->priv; + int force = cmd->args[0].v.i; + + struct playlist_entry *e = mp_next_file(mpctx, dir, force, true); + if (!e && !force) { + cmd->success = false; + return; } - case MP_CMD_SUB_STEP: - case MP_CMD_SUB_SEEK: { - if (!mpctx->playback_initialized) - return -1; - struct track *track = mpctx->current_track[0][STREAM_SUB]; - struct dec_sub *sub = track ? track->d_sub : NULL; - double refpts = get_current_time(mpctx); - if (sub && refpts != MP_NOPTS_VALUE) { - double a[2]; - a[0] = refpts; - a[1] = cmd->args[0].v.i; - if (sub_control(sub, SD_CTRL_SUB_STEP, a) > 0) { - if (cmd->id == MP_CMD_SUB_STEP) { - opts->subs_rend->sub_delay -= a[0] - refpts; - m_config_notify_change_opt_ptr(mpctx->mconfig, - &opts->subs_rend->sub_delay); - show_property_osd(mpctx, "sub-delay", on_osd); - } else { - // We can easily get stuck by failing to seek to the video - // frame which actually shows the sub first (because video - // frame PTS and sub PTS rarely match exactly). Add some - // rounding for the mess of it. - a[0] += 0.01 * (a[1] >= 0 ? 1 : -1); - mark_seek(mpctx); - queue_seek(mpctx, MPSEEK_ABSOLUTE, a[0], MPSEEK_EXACT, - MPSEEK_FLAG_DELAY); - set_osd_function(mpctx, (a[0] > refpts) ? OSD_FFW : OSD_REW); - if (seek_bar_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; - if (seek_msg_osd) - mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; - } + mp_set_playlist_entry(mpctx, e); + if (cmd->on_osd & MP_ON_OSD_MSG) + mpctx->add_osd_seek_info |= OSD_SEEK_INFO_CURRENT_FILE; +} + +static void cmd_sub_step_seek(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + bool step = *(bool *)cmd->priv; + + if (!mpctx->playback_initialized) { + cmd->success = false; + return; + } + + struct track *track = mpctx->current_track[0][STREAM_SUB]; + struct dec_sub *sub = track ? track->d_sub : NULL; + double refpts = get_current_time(mpctx); + if (sub && refpts != MP_NOPTS_VALUE) { + double a[2]; + a[0] = refpts; + a[1] = cmd->args[0].v.i; + if (sub_control(sub, SD_CTRL_SUB_STEP, a) > 0) { + if (step) { + mpctx->opts->subs_rend->sub_delay -= a[0] - refpts; + m_config_notify_change_opt_ptr(mpctx->mconfig, + &mpctx->opts->subs_rend->sub_delay); + show_property_osd(mpctx, "sub-delay", cmd->on_osd); + } else { + // We can easily get stuck by failing to seek to the video + // frame which actually shows the sub first (because video + // frame PTS and sub PTS rarely match exactly). Add some + // rounding for the mess of it. + a[0] += 0.01 * (a[1] >= 0 ? 1 : -1); + mark_seek(mpctx); + queue_seek(mpctx, MPSEEK_ABSOLUTE, a[0], MPSEEK_EXACT, + MPSEEK_FLAG_DELAY); + set_osd_function(mpctx, (a[0] > refpts) ? OSD_FFW : OSD_REW); + if (cmd->seek_bar_osd) + mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR; + if (cmd->seek_msg_osd) + mpctx->add_osd_seek_info |= OSD_SEEK_INFO_TEXT; } } - break; } +} - case MP_CMD_PRINT_TEXT: { - MP_INFO(mpctx, "%s\n", cmd->args[0].v.s); - break; - } +static void cmd_print_text(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; - case MP_CMD_SHOW_TEXT: { - // if no argument supplied use default osd_duration, else <arg> ms. - set_osd_msg(mpctx, cmd->args[2].v.i, - (cmd->args[1].v.i < 0 ? osd_duration : cmd->args[1].v.i), - "%s", cmd->args[0].v.s); - break; + MP_INFO(mpctx, "%s\n", cmd->args[0].v.s); +} + +static void cmd_show_text(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + int osd_duration = mpctx->opts->osd_duration; + + // if no argument supplied use default osd_duration, else <arg> ms. + set_osd_msg(mpctx, cmd->args[2].v.i, + (cmd->args[1].v.i < 0 ? osd_duration : cmd->args[1].v.i), + "%s", cmd->args[0].v.s); +} + +static void cmd_expand_text(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + + *cmd->result = (mpv_node){ + .format = MPV_FORMAT_STRING, + .u.string = mp_property_expand_string(mpctx, cmd->args[0].v.s) + }; +} + +static void cmd_loadfile(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + char *filename = cmd->args[0].v.s; + int append = cmd->args[1].v.i; + + if (!append) + playlist_clear(mpctx->playlist); + + struct playlist_entry *entry = playlist_entry_new(filename); + if (cmd->args[2].v.str_list) { + char **pairs = cmd->args[2].v.str_list; + for (int i = 0; pairs[i] && pairs[i + 1]; i += 2) + playlist_entry_add_param(entry, bstr0(pairs[i]), bstr0(pairs[i + 1])); } + playlist_add(mpctx->playlist, entry); - case MP_CMD_EXPAND_TEXT: { - if (!res) - return -1; - *res = (mpv_node){ - .format = MPV_FORMAT_STRING, - .u.string = mp_property_expand_string(mpctx, cmd->args[0].v.s) - }; - break; + if (!append || (append == 2 && !mpctx->playlist->current)) { + if (mpctx->opts->position_save_on_quit) // requested in issue #1148 + mp_write_watch_later_conf(mpctx); + mp_set_playlist_entry(mpctx, entry); } + mp_notify(mpctx, MP_EVENT_CHANGE_PLAYLIST, NULL); + mp_wakeup_core(mpctx); +} - case MP_CMD_LOADFILE: { - char *filename = cmd->args[0].v.s; - int append = cmd->args[1].v.i; +static void cmd_loadlist(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + char *filename = cmd->args[0].v.s; + bool append = cmd->args[1].v.i; + struct playlist *pl = playlist_parse_file(filename, mpctx->global); + if (pl) { + prepare_playlist(mpctx, pl); + struct playlist_entry *new = pl->current; if (!append) playlist_clear(mpctx->playlist); + playlist_append_entries(mpctx->playlist, pl); + talloc_free(pl); - struct playlist_entry *entry = playlist_entry_new(filename); - if (cmd->args[2].v.str_list) { - char **pairs = cmd->args[2].v.str_list; - for (int i = 0; pairs[i] && pairs[i + 1]; i += 2) { - playlist_entry_add_param(entry, bstr0(pairs[i]), - bstr0(pairs[i + 1])); - } - } - playlist_add(mpctx->playlist, entry); + if (!append && mpctx->playlist->first) + mp_set_playlist_entry(mpctx, new ? new : mpctx->playlist->first); - if (!append || (append == 2 && !mpctx->play |