From 15a5422dd2ba18d10c598f91b8cdae86269b2af1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 8 Jul 2013 19:27:45 +0200 Subject: input: allow binding multiple commands to a key Separate the commands with ';'. --- core/command.c | 6 ++++++ core/input/input.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++----- core/input/input.h | 4 ++++ 3 files changed, 64 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/command.c b/core/command.c index 80f6f5d9d2..848ebb848f 100644 --- a/core/command.c +++ b/core/command.c @@ -2507,6 +2507,12 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) change_video_filters(mpctx, cmd->args[0].v.s, cmd->args[1].v.s); break; + case MP_CMD_COMMAND_LIST: { + for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) + run_command(mpctx, sub); + break; + } + default: mp_msg(MSGT_CPLAYER, MSGL_V, "Received unknown cmd %s\n", cmd->name); diff --git a/core/input/input.c b/core/input/input.c index 4a52b3f9c9..73ac5bc698 100644 --- a/core/input/input.c +++ b/core/input/input.c @@ -808,7 +808,7 @@ static bool read_token(bstr str, bstr *out_rest, bstr *out_token) bstr t = bstr_lstrip(str); int next = bstrcspn(t, WHITESPACE "#"); // Handle comments - if (t.start[next] == '#') + if (t.len && t.start[next] == '#') t = bstr_splice(t, 0, next); if (!t.len) return false; @@ -852,13 +852,16 @@ error: return false; } -mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) +// If dest is non-NULL when calling this function, append the command to the +// list formed by dest->queue_next, otherwise just set *dest = new_cmd; +static int parse_cmd(struct mp_cmd **dest, bstr str, const char *loc) { int pausing = 0; int on_osd = MP_ON_OSD_AUTO; bool raw_args = false; struct mp_cmd *cmd = NULL; bstr start = str; + bstr next = {0}; void *tmp = talloc_new(NULL); str = bstr_lstrip(str); @@ -929,8 +932,13 @@ mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) struct mp_cmd_arg *cmdarg = &cmd->args[i]; if (!cmdarg->type.type) break; - cmd->nargs++; str = bstr_lstrip(str); + if (eat_token(&str, ";")) { + next = str; + str.len = 0; + break; + } + cmd->nargs++; bstr arg = {0}; if (bstr_eatstart0(&str, "\"")) { if (!read_escaped_string(tmp, &str, &arg)) { @@ -962,6 +970,11 @@ mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) cmdarg->v.s = talloc_steal(cmd, cmdarg->v.s); } + if (eat_token(&str, ";")) { + next = str; + str.len = 0; + } + bstr dummy; if (read_token(str, &dummy, &dummy)) { mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s has trailing unused " @@ -986,14 +999,50 @@ mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) bstr orig = (bstr) {start.start, str.start - start.start}; cmd->original = bstrdup(cmd, bstr_strip(orig)); + while (*dest) + dest = &(*dest)->queue_next; + *dest = cmd; + + next = bstr_strip(next); + if (next.len) { + if (parse_cmd(dest, next, loc) < 0) { + *dest = NULL; + goto error; + } + } + talloc_free(tmp); - return cmd; + return 1; error: mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command was defined at %s.\n", loc); talloc_free(cmd); talloc_free(tmp); - return NULL; + return -1; +} + +mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) +{ + struct mp_cmd *cmd = NULL; + if (parse_cmd(&cmd, str, loc) < 0) { + assert(!cmd); + } + // Other input.c code uses queue_next for its own purposes, so explicitly + // wrap lists in a pseudo-command. + if (cmd && cmd->queue_next) { + struct mp_cmd *list = talloc_ptrtype(NULL, list); + *list = (struct mp_cmd) { + .id = MP_CMD_COMMAND_LIST, + .name = "list", + }; + list->args[0].v.p = cmd; + while (cmd) { + talloc_steal(list, cmd); + cmd = cmd->queue_next; + } + cmd = list; + } + return cmd; } #define MP_CMD_MAX_SIZE 4096 diff --git a/core/input/input.h b/core/input/input.h index 2a9b3832c1..f7d69a0de7 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -87,6 +87,9 @@ enum mp_command_type { /// Video output commands MP_CMD_VO_CMDLINE, + + // Internal + MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p }; #define MP_CMD_MAX_ARGS 10 @@ -128,6 +131,7 @@ struct mp_cmd_arg { float f; double d; char *s; + void *p; } v; }; -- cgit v1.2.3 From 0bb41524f0a9ed89559b50bd7407de9d8b8aee40 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 8 Jul 2013 19:44:46 +0200 Subject: input: actually copy sub commands This was missing from the previous commit. It worked by luck, because the sub-commands weren't freed either (as long as the original command was around), but this is proper. Also, set the original string for command lists (needed for input-test only). --- core/input/input.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'core') diff --git a/core/input/input.c b/core/input/input.c index 73ac5bc698..ce7b868c02 100644 --- a/core/input/input.c +++ b/core/input/input.c @@ -1034,6 +1034,7 @@ mp_cmd_t *mp_input_parse_cmd(bstr str, const char *loc) *list = (struct mp_cmd) { .id = MP_CMD_COMMAND_LIST, .name = "list", + .original = bstrdup(list, str), }; list->args[0].v.p = cmd; while (cmd) { @@ -1775,6 +1776,17 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd) ret->args[i].v.s = talloc_strdup(ret, cmd->args[i].v.s); } + if (cmd->id == MP_CMD_COMMAND_LIST) { + bool first = true; + for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next) { + sub = mp_cmd_clone(sub); + talloc_steal(cmd, sub); + if (first) + cmd->args[0].v.p = sub; + first = false; + } + } + return ret; } -- cgit v1.2.3 From c460258f5a85d8d65261b524f23a3608185e58e4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 8 Jul 2013 20:34:26 +0200 Subject: command: add screenshot_to_file command --- core/command.c | 4 +++ core/input/input.c | 6 ++++ core/input/input.h | 1 + core/path.c | 11 +++++++ core/path.h | 7 ++++ core/screenshot.c | 96 ++++++++++++++++++++++++++++++++++++++---------------- core/screenshot.h | 6 ++++ 7 files changed, 103 insertions(+), 28 deletions(-) (limited to 'core') diff --git a/core/command.c b/core/command.c index 848ebb848f..6056596d04 100644 --- a/core/command.c +++ b/core/command.c @@ -2420,6 +2420,10 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i, msg_osd); break; + case MP_CMD_SCREENSHOT_TO_FILE: + screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd); + break; + case MP_CMD_RUN: #ifndef __MINGW32__ if (!fork()) { diff --git a/core/input/input.c b/core/input/input.c index ce7b868c02..e87d9bb560 100644 --- a/core/input/input.c +++ b/core/input/input.c @@ -171,6 +171,12 @@ static const mp_cmd_t mp_cmds[] = { OARG_CHOICE(0, ({"single", 0}, {"each-frame", 1})), }}, + { MP_CMD_SCREENSHOT_TO_FILE, "screenshot_to_file", { + ARG_STRING, + OARG_CHOICE(2, ({"video", 0}, + {"window", 1}, + {"subtitles", 2})), + }}, { MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_CHOICE(0, ({"replace", 0}, {"0", 0}, diff --git a/core/input/input.h b/core/input/input.h index f7d69a0de7..da92efd373 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -36,6 +36,7 @@ enum mp_command_type { MP_CMD_TV_STEP_NORM, MP_CMD_TV_STEP_CHANNEL_LIST, MP_CMD_SCREENSHOT, + MP_CMD_SCREENSHOT_TO_FILE, MP_CMD_LOADFILE, MP_CMD_LOADLIST, MP_CMD_PLAYLIST_CLEAR, diff --git a/core/path.c b/core/path.c index 1cff719041..50350be18c 100644 --- a/core/path.c +++ b/core/path.c @@ -164,6 +164,17 @@ struct bstr mp_dirname(const char *path) return ret; } +char *mp_splitext(const char *path, bstr *root) +{ + assert(path); + const char *split = strrchr(path, '.'); + if (!split) + split = path + strlen(path); + if (root) + *root = (bstr){.start = (char *)path, .len = path - split}; + return (char *)split; +} + char *mp_path_join(void *talloc_ctx, struct bstr p1, struct bstr p2) { if (p1.len == 0) diff --git a/core/path.h b/core/path.h index a3033199df..a38ad503ea 100644 --- a/core/path.h +++ b/core/path.h @@ -40,6 +40,13 @@ char *mp_find_user_config_file(const char *filename); char *mp_basename(const char *path); +/* Return file extension, including the '.'. If root is not NULL, set it to the + * part of the path without extension. So: path == root + returnvalue + * Don't consider it a file extension if the only '.' is the first character. + * Return "" if no extension. + */ +char *mp_splitext(const char *path, bstr *root); + /* Return struct bstr referencing directory part of path, or if that * would be empty, ".". */ diff --git a/core/screenshot.c b/core/screenshot.c index 4f7a0dbcdd..6b07bce5a0 100644 --- a/core/screenshot.c +++ b/core/screenshot.c @@ -278,16 +278,12 @@ static void add_subs(struct MPContext *mpctx, struct mp_image *image) OSD_DRAW_SUB_ONLY, image); } -static void screenshot_save(struct MPContext *mpctx, struct mp_image *image, - bool with_subs) +static void screenshot_save(struct MPContext *mpctx, struct mp_image *image) { screenshot_ctx *ctx = mpctx->screenshot_ctx; struct image_writer_opts *opts = mpctx->opts.screenshot_image_opts; - if (with_subs) - add_subs(mpctx, image); - char *filename = gen_fname(ctx, image_writer_file_ext(opts)); if (filename) { screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename); @@ -295,30 +291,15 @@ static void screenshot_save(struct MPContext *mpctx, struct mp_image *image, screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!"); talloc_free(filename); } - - talloc_free(image); } -void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, - bool osd) +static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode) { + struct mp_image *image = NULL; if (mpctx->video_out && mpctx->video_out->config_ok) { - screenshot_ctx *ctx = mpctx->screenshot_ctx; - if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter) mode = 0; - if (each_frame) { - ctx->each_frame = !ctx->each_frame; - if (!ctx->each_frame) - return; - } else { - ctx->each_frame = false; - } - - ctx->mode = mode; - ctx->osd = osd; - struct voctrl_screenshot_args args = { .full_window = (mode == MODE_FULL_WINDOW) }; @@ -328,14 +309,73 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, if (!args.out_image) vo_control(mpctx->video_out, VOCTRL_SCREENSHOT, &args); - if (args.out_image) { - if (args.has_osd) - mode = 0; - screenshot_save(mpctx, args.out_image, mode == MODE_SUBTITLES); - } else { - screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed."); + image = args.out_image; + if (image) { + if (mode == MODE_SUBTITLES && !args.has_osd) + add_subs(mpctx, image); } } + return image; +} + +void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode, + bool osd) +{ + screenshot_ctx *ctx = mpctx->screenshot_ctx; + struct image_writer_opts opts = *mpctx->opts.screenshot_image_opts; + bool old_osd = ctx->osd; + ctx->osd = osd; + + if (mp_path_exists(filename)) { + screenshot_msg(ctx, SMSG_ERR, "Screenshot: file '%s' already exists.", + filename); + goto end; + } + char *ext = mp_splitext(filename, NULL); + if (ext) + opts.format = ext + 1; // omit '.' + struct mp_image *image = screenshot_get(mpctx, mode); + if (!image) { + screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed."); + goto end; + } + screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename); + if (!write_image(image, &opts, filename)) + screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!"); + talloc_free(image); + +end: + ctx->osd = old_osd; +} + +void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, + bool osd) +{ + screenshot_ctx *ctx = mpctx->screenshot_ctx; + + if (mode == MODE_SUBTITLES && mpctx->osd->render_subs_in_filter) + mode = 0; + + if (each_frame) { + ctx->each_frame = !ctx->each_frame; + if (!ctx->each_frame) + return; + } else { + ctx->each_frame = false; + } + + ctx->mode = mode; + ctx->osd = osd; + + struct mp_image *image = screenshot_get(mpctx, mode); + + if (image) { + screenshot_save(mpctx, image); + } else { + screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed."); + } + + talloc_free(image); } void screenshot_flip(struct MPContext *mpctx) diff --git a/core/screenshot.h b/core/screenshot.h index 916a875505..1b12ac9b73 100644 --- a/core/screenshot.h +++ b/core/screenshot.h @@ -34,6 +34,12 @@ void screenshot_init(struct MPContext *mpctx); void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame, bool osd); +// filename: where to store the screenshot; doesn't try to find an alternate +// name if the file already exists +// mode, osd: same as in screenshot_request() +void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode, + bool osd); + // Called by the playback core code when a new frame is displayed. void screenshot_flip(struct MPContext *mpctx); -- cgit v1.2.3