summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/input.rst14
-rw-r--r--DOCS/man/mpv.rst66
-rw-r--r--options/m_config_frontend.c151
-rw-r--r--options/m_config_frontend.h17
-rw-r--r--options/parse_configfile.c11
-rw-r--r--player/command.c14
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)}} },