diff options
-rw-r--r-- | DOCS/man/input.rst | 14 | ||||
-rw-r--r-- | DOCS/man/mpv.rst | 66 | ||||
-rw-r--r-- | options/m_config_frontend.c | 151 | ||||
-rw-r--r-- | options/m_config_frontend.h | 17 | ||||
-rw-r--r-- | options/parse_configfile.c | 11 | ||||
-rw-r--r-- | player/command.c | 14 |
6 files changed, 212 insertions, 61 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 6df7890383..8593dd75a2 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -1220,13 +1220,21 @@ Input Commands that are Possibly Subject to Change ``af-command <label> <command> <argument>`` Same as ``vf-command``, but for audio filters. -``apply-profile <name>`` +``apply-profile <name> [<mode>]`` Apply the contents of a named profile. This is like using ``profile=name`` in a config file, except you can map it to a key binding to change it at runtime. - There is no such thing as "unapplying" a profile - applying a profile - merely sets all option values listed within the profile. + The mode argument: + + ``default`` + Apply the profile. Default if the argument is omitted. + + ``restore`` + Restore options set by a previous ``apply-profile`` command for this + profile. Only works if the profile has ``profile-restore`` set to a + relevant mode. Prints a warning if nothing could be done. See + `Runtime profiles`_ for details. ``load-script <filename>`` Load a script, similar to the ``--script`` option. Whether this waits for diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index a7202864e4..ee28dd5c67 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -693,6 +693,72 @@ or at runtime with the ``apply-profile <name>`` command. # you can also include other profiles profile=big-cache +Runtime profiles +---------------- + +Profiles can be set at runtime with ``apply-profile`` command. Since this +operation is "destructive" (every item in a profile is simply set as an +option, overwriting the previous value), you can't just enable and disable +profiles again. + +As a partial remedy, there is a way to make profiles save old option values +before overwriting them with the profile values, and then restoring the old +values at a later point using ``apply-profile <profile-name> restore``. + +This can be enabled with the ``profile-restore`` option, which takes one of +the following options: + + ``default`` + Does nothing, and nothing can be restored (default). + + ``copy`` + When applying a profile, copy the old values of all profile options to a + backup before setting them from the profile. These options are reset to + their old values using the backup when restoring. + + Every profile has its own list of backed up values. If the backup + already exists (e.g. if ``apply-profile name`` was called more than + once in a row), the existing backup is no changed. The restore operation + will remove the backup. + + It's important to know that restoring does not "undo" setting an option, + but simply copies the old option value. Consider for example ``vf-add``, + appends an entry to ``vf``. This mechanism will simply copy the entire + ``vf`` list, and does _not_ execute the inverse of ``vf-add`` (that + would be ``vf-remove``) on restoring. + + Note that if a profile contains recursive profiles (via the ``profile`` + option), the options in these recursive profiles are treated as if they + were part of this profile. The referenced profile's backup list is not + used when creating or using the backup. Restoring a profile does not + restore referenced profiles, only the options of referenced profiles (as + if they were part of the main profile). + + ``copy-equal`` + Similar to ``copy``, but restore an option only if it has the same value + as the value effectively set by the profile. This tries to deal with + the situation when the user does not want the option to be reset after + interactively changing it. + +.. admonition:: Example + + :: + + [something] + profile-restore=copy-equal + vf-add=rotate=90 + + Then running these commands will result in behavior as commented: + + :: + + set vf vflip + apply-profile something + vf-add=hflip + apply-profile something + # vf == vflip,rotate=90,hflip,rotate=90 + apply-profile something restore + # vf == vflip Conditional auto profiles ------------------------- diff --git a/options/m_config_frontend.c b/options/m_config_frontend.c index 94cc9c015a..a3356c4111 100644 --- a/options/m_config_frontend.c +++ b/options/m_config_frontend.c @@ -51,17 +51,28 @@ struct m_profile { char *name; char *desc; char *cond; + int restore_mode; int num_opts; // Option/value pair array. // name,value = opts[n*2+0],opts[n*2+1] char **opts; + // For profile restoring. + struct m_opt_backup *backups; }; // In the file local case, this contains the old global value. +// It's also used for profile restoring. struct m_opt_backup { struct m_opt_backup *next; struct m_config_option *co; - void *backup; + int flags; + void *backup, *nval; +}; + +static const struct m_option profile_restore_mode_opt = { + .name = "profile-restore", + .type = &m_option_type_choice, + M_CHOICES({"default", 0}, {"copy", 1}, {"copy-equal", 2}), }; static void list_profiles(struct m_config *config) @@ -178,44 +189,66 @@ error: return NULL; } -static void ensure_backup(struct m_config *config, struct m_config_option *co) +static void backup_dtor(void *p) +{ + struct m_opt_backup *bc = p; + m_option_free(bc->co->opt, bc->backup); + if (bc->nval) + m_option_free(bc->co->opt, bc->nval); +} + +#define BACKUP_LOCAL 1 +#define BACKUP_NVAL 2 +static void ensure_backup(struct m_opt_backup **list, int flags, + struct m_config_option *co) { if (!co->data) return; - for (struct m_opt_backup *cur = config->backup_opts; cur; cur = cur->next) { + for (struct m_opt_backup *cur = *list; cur; cur = cur->next) { if (cur->co->data == co->data) // comparing data ptr catches aliases return; } struct m_opt_backup *bc = talloc_ptrtype(NULL, bc); + talloc_set_destructor(bc, backup_dtor); *bc = (struct m_opt_backup) { .co = co, .backup = talloc_zero_size(bc, co->opt->type->size), + .nval = flags & BACKUP_NVAL + ? talloc_zero_size(bc, co->opt->type->size) : NULL, + .flags = flags, }; m_option_copy(co->opt, bc->backup, co->data); - bc->next = config->backup_opts; - config->backup_opts = bc; - co->is_set_locally = true; + bc->next = *list; + *list = bc; + if (bc->flags & BACKUP_LOCAL) + co->is_set_locally = true; } -void m_config_restore_backups(struct m_config *config) +static void restore_backups(struct m_opt_backup **list, struct m_config *config) { - while (config->backup_opts) { - struct m_opt_backup *bc = config->backup_opts; - config->backup_opts = bc->next; + while (*list) { + struct m_opt_backup *bc = *list; + *list = bc->next; - m_config_set_option_raw(config, bc->co, bc->backup, 0); + if (!bc->nval || m_option_equal(bc->co->opt, bc->co->data, bc->nval)) + m_config_set_option_raw(config, bc->co, bc->backup, 0); - m_option_free(bc->co->opt, bc->backup); - bc->co->is_set_locally = false; + if (bc->flags & BACKUP_LOCAL) + bc->co->is_set_locally = false; talloc_free(bc); } } +void m_config_restore_backups(struct m_config *config) +{ + restore_backups(&config->backup_opts, config); +} + void m_config_backup_opt(struct m_config *config, const char *opt) { struct m_config_option *co = m_config_get_co(config, bstr0(opt)); if (co) { - ensure_backup(config, co); + ensure_backup(&config->backup_opts, BACKUP_LOCAL, co); } else { MP_ERR(config, "Option %s not found.\n", opt); } @@ -224,7 +257,7 @@ void m_config_backup_opt(struct m_config *config, const char *opt) void m_config_backup_all_opts(struct m_config *config) { for (int n = 0; n < config->num_opts; n++) - ensure_backup(config, &config->opts[n]); + ensure_backup(&config->backup_opts, BACKUP_LOCAL, &config->opts[n]); } @@ -360,7 +393,7 @@ static int handle_set_opt_flags(struct m_config *config, return M_OPT_INVALID; } if ((flags & M_SETOPT_BACKUP) && set) - ensure_backup(config, co); + ensure_backup(&config->backup_opts, BACKUP_LOCAL, co); return set ? 2 : 1; } @@ -577,6 +610,9 @@ int m_config_set_option_raw(struct m_config *config, if (!co->data) return flags & M_SETOPT_FROM_CMDLINE ? 0 : M_OPT_UNKNOWN; + if (config->profile_backup_tmp) + ensure_backup(config->profile_backup_tmp, config->profile_backup_flags, co); + m_config_mark_co_flags(co, flags); m_option_copy(co->opt, co->data, data); @@ -884,23 +920,26 @@ struct m_profile *m_config_add_profile(struct m_config *config, char *name) return p; } -void m_profile_set_desc(struct m_profile *p, bstr desc) -{ - talloc_free(p->desc); - p->desc = bstrto0(p, desc); -} - -void m_profile_set_cond(struct m_profile *p, bstr cond) -{ - TA_FREEP(&p->cond); - cond = bstr_strip(cond); - if (cond.len) - p->cond = bstrto0(p, cond); -} - int m_config_set_profile_option(struct m_config *config, struct m_profile *p, bstr name, bstr val) { + if (bstr_equals0(name, "profile-desc")) { + talloc_free(p->desc); + p->desc = bstrto0(p, val); + return 0; + } + if (bstr_equals0(name, "profile-cond")) { + TA_FREEP(&p->cond); + val = bstr_strip(val); + if (val.len) + p->cond = bstrto0(p, val); + return 0; + } + if (bstr_equals0(name, profile_restore_mode_opt.name)) { + return m_option_parse(config->log, &profile_restore_mode_opt, name, val, + &p->restore_mode); + } + int i = m_config_set_option_cli(config, name, val, M_SETOPT_CHECK_ONLY | M_SETOPT_FROM_CONFIG_FILE); @@ -914,19 +953,32 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p, return 1; } -int m_config_set_profile(struct m_config *config, char *name, int flags) +static struct m_profile *find_check_profile(struct m_config *config, char *name) { struct m_profile *p = m_config_get_profile0(config, name); if (!p) { MP_WARN(config, "Unknown profile '%s'.\n", name); - return M_OPT_INVALID; + return NULL; } - MP_VERBOSE(config, "Applying profile '%s'...\n", name); - if (config->profile_depth > MAX_PROFILE_DEPTH) { MP_WARN(config, "WARNING: Profile inclusion too deep.\n"); + return NULL; + } + return p; +} + +int m_config_set_profile(struct m_config *config, char *name, int flags) +{ + MP_VERBOSE(config, "Applying profile '%s'...\n", name); + struct m_profile *p = find_check_profile(config, name); + if (!p) return M_OPT_INVALID; + + if (!config->profile_backup_tmp && p->restore_mode) { + config->profile_backup_tmp = &p->backups; + config->profile_backup_flags = p->restore_mode == 2 ? BACKUP_NVAL : 0; } + config->profile_depth++; for (int i = 0; i < p->num_opts; i++) { m_config_set_option_cli(config, @@ -936,6 +988,31 @@ int m_config_set_profile(struct m_config *config, char *name, int flags) } config->profile_depth--; + if (config->profile_backup_tmp == &p->backups) { + config->profile_backup_tmp = NULL; + + for (struct m_opt_backup *bc = p->backups; bc; bc = bc->next) { + if (bc && bc->nval) + m_option_copy(bc->co->opt, bc->nval, bc->co->data); + talloc_steal(p, bc); + } + } + + return 0; +} + +int m_config_restore_profile(struct m_config *config, char *name) +{ + MP_VERBOSE(config, "Restoring from profile '%s'...\n", name); + struct m_profile *p = find_check_profile(config, name); + if (!p) + return M_OPT_INVALID; + + if (!p->backups) + MP_WARN(config, "Profile contains no restore data.\n"); + + restore_backups(&p->backups, config); + return 0; } @@ -960,6 +1037,12 @@ struct mpv_node m_config_get_profiles(struct m_config *config) node_map_add_string(entry, "profile-desc", profile->desc); if (profile->cond) node_map_add_string(entry, "profile-cond", profile->cond); + if (profile->restore_mode) { + char *s = + m_option_print(&profile_restore_mode_opt, &profile->restore_mode); + node_map_add_string(entry, profile_restore_mode_opt.name, s); + talloc_free(s); + } struct mpv_node *opts = node_map_add(entry, "options", MPV_FORMAT_NODE_ARRAY); diff --git a/options/m_config_frontend.h b/options/m_config_frontend.h index 81bc78b6e1..ee6b9aec46 100644 --- a/options/m_config_frontend.h +++ b/options/m_config_frontend.h @@ -70,6 +70,9 @@ typedef struct m_config { struct m_profile *profiles; // Depth when recursively including profiles. int profile_depth; + // Temporary during profile application. + struct m_opt_backup **profile_backup_tmp; + int profile_backup_flags; struct m_opt_backup *backup_opts; @@ -224,17 +227,6 @@ void m_config_finish_default_profile(struct m_config *config, int flags); */ struct m_profile *m_config_add_profile(struct m_config *config, char *name); -/* Set the description of a profile. - * Used by the config file parser when defining a profile. - * - * \param p The profile object. - * \param arg The profile's name. - */ -void m_profile_set_desc(struct m_profile *p, bstr desc); - -// Set auto profile condition of a profile. -void m_profile_set_cond(struct m_profile *p, bstr cond); - /* Add an option to a profile. * Used by the config file parser when defining a profile. * @@ -256,6 +248,9 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p, */ int m_config_set_profile(struct m_config *config, char *name, int flags); +// Attempt to "unset" a profile if possible. +int m_config_restore_profile(struct m_config *config, char *name); + struct mpv_node m_config_get_profiles(struct m_config *config); // Run async option updates here. This will call option_change_callback() on it. diff --git a/options/parse_configfile.c b/options/parse_configfile.c index 96b607d554..627c466948 100644 --- a/options/parse_configfile.c +++ b/options/parse_configfile.c @@ -127,16 +127,7 @@ int m_config_parse(m_config_t *config, const char *location, bstr data, goto error; } - int res; - if (bstr_equals0(option, "profile-desc")) { - m_profile_set_desc(profile, value); - res = 0; - } else if (bstr_equals0(option, "profile-cond")) { - m_profile_set_cond(profile, value); - res = 0; - } else { - res = m_config_set_profile_option(config, profile, option, value); - } + int res = m_config_set_profile_option(config, profile, option, value); if (res < 0) { MP_ERR(config, "%s setting option %.*s='%.*s' failed.\n", loc, BSTR_P(option), BSTR_P(value)); diff --git a/player/command.c b/player/command.c index 8a546608eb..7b3e194f78 100644 --- a/player/command.c +++ b/player/command.c @@ -5647,8 +5647,12 @@ static void cmd_apply_profile(void *p) struct MPContext *mpctx = cmd->mpctx; char *profile = cmd->args[0].v.s; - if (m_config_set_profile(mpctx->mconfig, profile, 0) < 0) - cmd->success = false; + int mode = cmd->args[1].v.i; + if (mode == 0) { + cmd->success = m_config_set_profile(mpctx->mconfig, profile, 0) >= 0; + } else { + cmd->success = m_config_restore_profile(mpctx->mconfig, profile) >= 0; + } } static void cmd_load_script(void *p) @@ -6138,7 +6142,11 @@ const struct mp_cmd_def mp_cmds[] = { { "keyup", cmd_key, { {"name", OPT_STRING(v.s), .flags = MP_CMD_OPT_ARG} }, .priv = &(const int){MP_KEY_STATE_UP}}, - { "apply-profile", cmd_apply_profile, {{"name", OPT_STRING(v.s)}} }, + { "apply-profile", cmd_apply_profile, { + {"name", OPT_STRING(v.s)}, + {"mode", OPT_CHOICE(v.i, {"apply", 0}, {"restore", 1}), + .flags = MP_CMD_OPT_ARG}, } + }, { "load-script", cmd_load_script, {{"filename", OPT_STRING(v.s)}} }, |