From f42e4374d55a3b68b0c4fcb342d9cdcd5d15c9c7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 28 Aug 2016 19:42:03 +0200 Subject: command: export profile list as a property Targeted at scripts, which can do whatever they want with it. This comes with the promise that they could get randomly broken any time. See #977. --- options/m_config.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 1cb01ebfdc..3dbbb5020a 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -36,6 +36,7 @@ #include "options/m_option.h" #include "common/msg.h" #include "common/msg_control.h" +#include "misc/node.h" static const union m_option_value default_value; @@ -933,6 +934,32 @@ int m_config_set_profile(struct m_config *config, char *name, int flags) return 0; } +struct mpv_node m_config_get_profiles(struct m_config *config) +{ + struct mpv_node root; + node_init(&root, MPV_FORMAT_NODE_ARRAY, NULL); + + for (m_profile_t *profile = config->profiles; profile; profile = profile->next) + { + struct mpv_node *entry = node_array_add(&root, MPV_FORMAT_NODE_MAP); + + node_map_add_string(entry, "name", profile->name); + if (profile->desc) + node_map_add_string(entry, "profile-desc", profile->desc); + + struct mpv_node *opts = + node_map_add(entry, "options", MPV_FORMAT_NODE_ARRAY); + + for (int n = 0; n < profile->num_opts; n++) { + struct mpv_node *opt_entry = node_array_add(opts, MPV_FORMAT_NODE_MAP); + node_map_add_string(opt_entry, "key", profile->opts[n * 2 + 0]); + node_map_add_string(opt_entry, "value", profile->opts[n * 2 + 1]); + } + } + + return root; +} + void *m_config_alloc_struct(void *talloc_ctx, const struct m_sub_options *subopts) { -- cgit v1.2.3 From bda614bfd81ae4660ed6356bc124e015645ff3b0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Aug 2016 09:30:39 +0200 Subject: m_config: profile option values can be NULL Sigh. --- options/m_config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 3dbbb5020a..106a8f20ee 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -953,7 +953,8 @@ struct mpv_node m_config_get_profiles(struct m_config *config) for (int n = 0; n < profile->num_opts; n++) { struct mpv_node *opt_entry = node_array_add(opts, MPV_FORMAT_NODE_MAP); node_map_add_string(opt_entry, "key", profile->opts[n * 2 + 0]); - node_map_add_string(opt_entry, "value", profile->opts[n * 2 + 1]); + if (profile->opts[n * 2 + 1]) + node_map_add_string(opt_entry, "value", profile->opts[n * 2 + 1]); } } -- cgit v1.2.3 From 3bb134969eb624e859a546fd7142fe2eae076346 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 30 Aug 2016 23:45:58 +0200 Subject: m_option: remove M_OPT_TYPE_DYNAMIC flag It's actually redundant with whether m_option_type.free is set. Some option types were flagged inconsistently. Its only use was for running an additional sanity check without any real functionality. --- options/m_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 106a8f20ee..d2fe3fdcfa 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -424,7 +424,7 @@ static void m_config_add_option(struct m_config *config, } else { // Initialize options if (co.data && co.default_data) { - if (arg->type->flags & M_OPT_TYPE_DYNAMIC) { + if (arg->type->free) { // Would leak memory by overwriting *co.data repeatedly. for (int i = 0; i < config->num_opts; i++) { if (co.data == config->opts[i].data) -- cgit v1.2.3 From 5f88e6a0db292d272b3dc8f66c257c27327d1733 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 30 Aug 2016 23:47:09 +0200 Subject: m_config: rename is_generated to is_hidden More appropriate. Originally it really was for automatically added options, but now it even needed some stupid comments to indicate that it was used for simply hiding options. --- options/m_config.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index d2fe3fdcfa..c98ee6713f 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -334,7 +334,7 @@ static void add_negation_option(struct m_config *config, struct m_config_option co = *orig; co.name = talloc_asprintf(config, "no-%s", orig->name); co.opt = no_opt; - co.is_generated = true; + co.is_hidden = true; MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); // Add --sub-no-opt (unfortunately needed for: "--sub=...:no-opt") if (parent_name[0]) { @@ -441,7 +441,7 @@ static void m_config_add_option(struct m_config *config, add_negation_option(config, &co, parent_name); if (co.opt->type == &m_option_type_alias) { - co.is_generated = true; // hide it + co.is_hidden = true; const char *alias = (const char *)co.opt->priv; char no_alias[40]; snprintf(no_alias, sizeof(no_alias), "no-%s", alias); @@ -456,7 +456,7 @@ static void m_config_add_option(struct m_config *config, } if (co.opt->type == &m_option_type_removed) - co.is_generated = true; // hide it + co.is_hidden = true; } struct m_config_option *m_config_get_co(const struct m_config *config, @@ -519,7 +519,7 @@ const char *m_config_get_positional_option(const struct m_config *config, int p) int pos = 0; for (int n = 0; n < config->num_opts; n++) { struct m_config_option *co = &config->opts[n]; - if (!co->is_generated) { + if (!co->is_hidden) { if (pos == p) return co->name; pos++; @@ -794,7 +794,7 @@ void m_config_print_option_list(const struct m_config *config) const struct m_option *opt = co->opt; if (opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue; - if (co->is_generated) + if (co->is_hidden) continue; if (opt->type == &m_option_type_alias || opt->type == &m_option_type_removed) @@ -848,7 +848,7 @@ char **m_config_list_options(void *ta_parent, const struct m_config *config) const struct m_option *opt = co->opt; if (opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue; - if (co->is_generated) + if (co->is_hidden) continue; // For use with CONF_TYPE_STRING_LIST, it's important not to set list // as allocation parent. -- cgit v1.2.3 From e65a8d7b61762ddf07825c59a6ebd47b026b0ea7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 30 Aug 2016 23:48:42 +0200 Subject: m_config: pass parent option in m_config_add_option() Instead of just the parent name. This is a minor refactor as preparation for other things. --- options/m_config.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index c98ee6713f..8a4d5998d6 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -174,7 +174,7 @@ static void substruct_write_ptr(void *ptr, void *val) } static void add_options(struct m_config *config, - const char *parent_name, + struct m_config_option *parent, void *optstruct, const void *optstruct_def, const struct m_option *defs); @@ -202,7 +202,7 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log, memcpy(config->optstruct, defaults, size); } if (options) - add_options(config, "", config->optstruct, defaults, options); + add_options(config, NULL, config->optstruct, defaults, options); return config; } @@ -344,19 +344,19 @@ static void add_negation_option(struct m_config *config, } static void m_config_add_option(struct m_config *config, - const char *parent_name, + struct m_config_option *parent, void *optstruct, const void *optstruct_def, const struct m_option *arg); static void add_options(struct m_config *config, - const char *parent_name, + struct m_config_option *parent, void *optstruct, const void *optstruct_def, const struct m_option *defs) { for (int i = 0; defs && defs[i].name; i++) - m_config_add_option(config, parent_name, optstruct, optstruct_def, &defs[i]); + m_config_add_option(config, parent, optstruct, optstruct_def, &defs[i]); } // Initialize a field with a given value. In case this is dynamic data, it has @@ -372,7 +372,7 @@ static void init_opt_inplace(const struct m_option *opt, void *dst, } static void m_config_add_option(struct m_config *config, - const char *parent_name, + struct m_config_option *parent, void *optstruct, const void *optstruct_def, const struct m_option *arg) @@ -380,6 +380,8 @@ static void m_config_add_option(struct m_config *config, assert(config != NULL); assert(arg != NULL); + const char *parent_name = parent ? parent->name : ""; + struct m_config_option co = { .opt = arg, .name = arg->name, @@ -419,8 +421,7 @@ static void m_config_add_option(struct m_config *config, if (!new_optstruct_def) new_optstruct_def = subopts->defaults; - add_options(config, co.name, new_optstruct, - new_optstruct_def, subopts->opts); + add_options(config, &co, new_optstruct, new_optstruct_def, subopts->opts); } else { // Initialize options if (co.data && co.default_data) { @@ -451,7 +452,7 @@ static void m_config_add_option(struct m_config *config, new->priv = talloc_strdup(config, no_alias); new->type = &m_option_type_alias; new->offset = -1; - m_config_add_option(config, "", NULL, NULL, new); + m_config_add_option(config, NULL, NULL, NULL, new); } } -- cgit v1.2.3 From e024906408c4d079e77b136de9b0562a93daadce Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 16:45:58 +0200 Subject: m_config: handle --no-... options differently Instead of adding "no-"-prefixed aliases to the internal option list, which will act like normal options, do it in the parsing stage. This turns out to be simpler (and cheaper), and avoids adding aliased options. --- options/m_config.c | 97 +++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 53 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 8a4d5998d6..cd18e57b9d 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -298,51 +298,6 @@ void m_config_backup_all_opts(struct m_config *config) ensure_backup(config, &config->opts[n]); } -// Given an option --opt, add --no-opt (if applicable). -static void add_negation_option(struct m_config *config, - struct m_config_option *orig, - const char *parent_name) -{ - const struct m_option *opt = orig->opt; - int value; - if (opt->type == CONF_TYPE_FLAG) { - value = 0; - } else if (opt->type == CONF_TYPE_CHOICE) { - // Find out whether there's a "no" choice. - // m_option_parse() should be used for this, but it prints - // unsilenceable error messages. - struct m_opt_choice_alternatives *alt = opt->priv; - for ( ; alt->name; alt++) { - if (strcmp(alt->name, "no") == 0) - break; - } - if (!alt->name) - return; - value = alt->value; - } else { - return; - } - struct m_option *no_opt = talloc_ptrtype(config, no_opt); - *no_opt = (struct m_option) { - .name = opt->name, - .type = CONF_TYPE_STORE, - .flags = opt->flags & (M_OPT_NOCFG | M_OPT_GLOBAL | M_OPT_PRE_PARSE), - .offset = opt->offset, - .max = value, - }; - // Add --no-sub-opt - struct m_config_option co = *orig; - co.name = talloc_asprintf(config, "no-%s", orig->name); - co.opt = no_opt; - co.is_hidden = true; - MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); - // Add --sub-no-opt (unfortunately needed for: "--sub=...:no-opt") - if (parent_name[0]) { - co.name = talloc_asprintf(config, "%s-no-%s", parent_name, opt->name); - MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); - } -} - static void m_config_add_option(struct m_config *config, struct m_config_option *parent, void *optstruct, @@ -439,8 +394,6 @@ static void m_config_add_option(struct m_config *config, if (arg->name[0]) // no own name -> hidden MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); - add_negation_option(config, &co, parent_name); - if (co.opt->type == &m_option_type_alias) { co.is_hidden = true; const char *alias = (const char *)co.opt->priv; @@ -610,14 +563,42 @@ int m_config_set_option_raw(struct m_config *config, struct m_config_option *co, static int parse_subopts(struct m_config *config, char *name, char *prefix, struct bstr param, int flags); +// Used to turn "--no-foo" into "--foo=no". +static struct m_config_option *m_config_find_negation_opt(struct m_config *config, + struct bstr *name) +{ + assert(!m_config_get_co(config, *name)); + + if (!bstr_eatstart0(name, "no-")) + return NULL; + + struct m_config_option *co = m_config_get_co(config, *name); + + // Not all choice types have this value - if they don't, then parsing them + // will simply result in an error. Good enough. + if (co && co->opt->type != CONF_TYPE_FLAG && + co->opt->type != CONF_TYPE_CHOICE) + co = NULL; + + return co; +} + static int m_config_parse_option(struct m_config *config, struct bstr name, struct bstr param, int flags) { assert(config != NULL); struct m_config_option *co = m_config_get_co(config, name); - if (!co) - return M_OPT_UNKNOWN; + if (!co) { + co = m_config_find_negation_opt(config, &name); + if (!co) + return M_OPT_UNKNOWN; + + if (param.len) + return M_OPT_DISALLOW_PARAM; + + param = bstr0("no"); + } // This is the only mandatory function assert(co->opt->type->parse); @@ -720,12 +701,22 @@ int m_config_set_option(struct m_config *config, struct bstr name, int m_config_set_option_node(struct m_config *config, bstr name, struct mpv_node *data, int flags) { - struct m_config_option *co = m_config_get_co(config, name); - if (!co) - return M_OPT_UNKNOWN; - + struct mpv_node tmp; int r; + struct m_config_option *co = m_config_get_co(config, name); + if (!co) { + co = m_config_find_negation_opt(config, &name); + if (!co) + return M_OPT_UNKNOWN; + if (!(data->format == MPV_FORMAT_STRING && !bstr0(data->u.string).len) && + !(data->format == MPV_FORMAT_FLAG && data->u.flag == 1)) + return M_OPT_INVALID; + tmp.format = MPV_FORMAT_STRING; + tmp.u.string = "no"; + data = &tmp; + } + // Do this on an "empty" type to make setting the option strictly overwrite // the old value, as opposed to e.g. appending to lists. union m_option_value val = {0}; -- cgit v1.2.3 From 7dde096d8a6d9e82cac82bec439fef47d078f352 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 17:09:10 +0200 Subject: m_config: introduce and use OPT_ALIAS for some options OPT_ALIAS redirects the options at a higher level, instead of introducing "duplicate" options with different name but same backing storage. This uses the OPT_REPLACED mechanisms - only the deprecation warning had to be made conditional. Note that e.g. --no-video still works, because the "--no-..." redirection and OPT_ALIAS are orthogonal. The deprecated --sub -> --sub-file alias had to be dropped, because it essentially conflicts with --no-sub. If anyone complains, this could probably still be undone by letting m_config_find_negation_opt do a special mapping for --no-sub. (Which would be dumb, but simple and effective.) --- options/m_config.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index cd18e57b9d..470951d233 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -362,6 +362,9 @@ static void m_config_add_option(struct m_config *config, co.name = talloc_asprintf(config, "%s-%s", parent_name, co.name); } + if (co.opt->deprecation_message) + co.is_hidden = true; + // Option with children -> add them if (arg->type->flags & M_OPT_TYPE_HAS_CHILD) { const struct m_sub_options *subopts = arg->priv; @@ -395,7 +398,6 @@ static void m_config_add_option(struct m_config *config, MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); if (co.opt->type == &m_option_type_alias) { - co.is_hidden = true; const char *alias = (const char *)co.opt->priv; char no_alias[40]; snprintf(no_alias, sizeof(no_alias), "no-%s", alias); @@ -408,9 +410,6 @@ static void m_config_add_option(struct m_config *config, m_config_add_option(config, NULL, NULL, NULL, new); } } - - if (co.opt->type == &m_option_type_removed) - co.is_hidden = true; } struct m_config_option *m_config_get_co(const struct m_config *config, @@ -434,7 +433,9 @@ struct m_config_option *m_config_get_co(const struct m_config *config, const char *prefix = config->is_toplevel ? "--" : ""; if (co->opt->type == &m_option_type_alias) { const char *alias = (const char *)co->opt->priv; - if (!co->warning_was_printed) { + // deprecation_message is not used, but decides whether it's a + // proper or deprecated alias. + if (co->opt->deprecation_message && !co->warning_was_printed) { MP_WARN(config, "Warning: option %s%s was replaced with " "%s%s and might be removed in the future.\n", prefix, co->name, prefix, alias); @@ -788,9 +789,6 @@ void m_config_print_option_list(const struct m_config *config) continue; if (co->is_hidden) continue; - if (opt->type == &m_option_type_alias || - opt->type == &m_option_type_removed) - continue; MP_INFO(config, " %s%-30s", prefix, co->name); if (opt->type == &m_option_type_choice) { MP_INFO(config, " Choices:"); -- cgit v1.2.3 From b10dcecf7d71a076525bc2ac4b6bd72f4c453278 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 17:28:42 +0200 Subject: client API: deprecate "no-..." option handling The client API can do this (and there are apparently some libmpv using projects which rely on this). But it's just unnecessary bloat as it requires a separate code path from the option parser. It would be better to remove this code. Formally deprecate it, including API bump and warning in the API changes file to make it really clear. --- options/m_config.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 470951d233..d1bb72c13b 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -707,6 +707,7 @@ int m_config_set_option_node(struct m_config *config, bstr name, struct m_config_option *co = m_config_get_co(config, name); if (!co) { + bstr orig_name = name; co = m_config_find_negation_opt(config, &name); if (!co) return M_OPT_UNKNOWN; @@ -716,6 +717,12 @@ int m_config_set_option_node(struct m_config *config, bstr name, tmp.format = MPV_FORMAT_STRING; tmp.u.string = "no"; data = &tmp; + + if (!co->warning_was_printed) { + MP_WARN(config, "Option '%.*s': setting 'no-' option via API is " + "deprecated and will stop working.\n", BSTR_P(orig_name)); + co->warning_was_printed = true; + } } // Do this on an "empty" type to make setting the option strictly overwrite -- cgit v1.2.3 From 2057209057555dc138422daa354fe8db816c629c Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 17:39:30 +0200 Subject: m_config: deprecate top-level suboptions This is a really old weird MPlayer feature. While the MPlayer requires you to use the sub-option syntax in these cases, mpv "flattens" them to normal options. The still-supported alternate sub-option syntax remains a weird artifact that hopefully nobody uses. For example you can do "-sub-text font=Foo:color=0.5" instead of using "--sub-text-font=Foo --sub-text-color=0.5". For --sub-text this is an accidental feature, but it used to be documented for --demuxer-rawaudio and some others. This should just be removed, but for now only print a warning to preempt complaints from weird users wanting this feature back. --- options/m_config.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index d1bb72c13b..4912b21cf6 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -626,6 +626,11 @@ static int m_config_parse_option(struct m_config *config, struct bstr name, // Option with children are a bit different to parse if (co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) { char prefix[110]; + if (!config->subopt_deprecation_warning) { + MP_WARN(config, "Suboptions (--%.*s=...) are deprecated. Use " + "flat options instead.\n", BSTR_P(name)); + config->subopt_deprecation_warning = true; + } assert(strlen(co->name) < 100); sprintf(prefix, "%s-", co->name); return parse_subopts(config, (char *)co->name, prefix, param, flags); -- cgit v1.2.3 From c55d85992078e465f9e0b7b6998ccdc5f5e77703 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 18:10:11 +0200 Subject: m_option: replace --no-video-aspect alias Instead, add a hacky OPT_ASPECT option type, which only exists to accept a "no" parameter, which in combination with the "--no-..." handling code makes --no-video-aspect work again. We can also remove the code in m_config.c, which only existed to make "--no-aspect" (a deprecated alias) to work. --- options/m_config.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 4912b21cf6..b5cef17fd8 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -396,20 +396,6 @@ static void m_config_add_option(struct m_config *config, if (arg->name[0]) // no own name -> hidden MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); - - if (co.opt->type == &m_option_type_alias) { - const char *alias = (const char *)co.opt->priv; - char no_alias[40]; - snprintf(no_alias, sizeof(no_alias), "no-%s", alias); - if (m_config_get_co(config, bstr0(no_alias))) { - struct m_option *new = talloc_zero(config, struct m_option); - new->name = talloc_asprintf(config, "no-%s", co.name); - new->priv = talloc_strdup(config, no_alias); - new->type = &m_option_type_alias; - new->offset = -1; - m_config_add_option(config, NULL, NULL, NULL, new); - } - } } struct m_config_option *m_config_get_co(const struct m_config *config, @@ -578,7 +564,8 @@ static struct m_config_option *m_config_find_negation_opt(struct m_config *confi // Not all choice types have this value - if they don't, then parsing them // will simply result in an error. Good enough. if (co && co->opt->type != CONF_TYPE_FLAG && - co->opt->type != CONF_TYPE_CHOICE) + co->opt->type != CONF_TYPE_CHOICE && + co->opt->type != &m_option_type_aspect) co = NULL; return co; -- cgit v1.2.3 From 7539928c1c8a12f030da4de4b8be0f697a122212 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 18:17:42 +0200 Subject: m_config: remove some aliasing checks We strictly assume no aliasing (the previous commit removed the last case), so remove the checks. --- options/m_config.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index b5cef17fd8..1c2541a2c6 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -382,16 +382,8 @@ static void m_config_add_option(struct m_config *config, add_options(config, &co, new_optstruct, new_optstruct_def, subopts->opts); } else { // Initialize options - if (co.data && co.default_data) { - if (arg->type->free) { - // Would leak memory by overwriting *co.data repeatedly. - for (int i = 0; i < config->num_opts; i++) { - if (co.data == config->opts[i].data) - assert(0); - } - } + if (co.data && co.default_data) init_opt_inplace(arg, co.data, co.default_data); - } } if (arg->name[0]) // no own name -> hidden @@ -510,17 +502,8 @@ static int handle_set_opt_flags(struct m_config *config, static void handle_on_set(struct m_config *config, struct m_config_option *co, int flags) { - if (flags & M_SETOPT_FROM_CMDLINE) { + if (flags & M_SETOPT_FROM_CMDLINE) co->is_set_from_cmdline = true; - // Mark aliases too - if (co->data) { - for (int n = 0; n < config->num_opts; n++) { - struct m_config_option *co2 = &config->opts[n]; - if (co2->data == co->data) - co2->is_set_from_cmdline = true; - } - } - } if (config->global && (co->opt->flags & M_OPT_TERM)) mp_msg_update_msglevels(config->global); -- cgit v1.2.3 From e22ae2cba0cbca7c773e9ac29e57c48b689fae41 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 31 Aug 2016 22:22:02 +0200 Subject: m_config: remove an unused function Well, almost unused. --- options/m_config.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 1c2541a2c6..e5600228da 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -725,24 +725,14 @@ int m_config_set_option_node(struct m_config *config, bstr name, return r; } -const struct m_option *m_config_get_option(const struct m_config *config, - struct bstr name) -{ - assert(config != NULL); - - struct m_config_option *co = m_config_get_co(config, name); - return co ? co->opt : NULL; -} - int m_config_option_requires_param(struct m_config *config, bstr name) { - const struct m_option *opt = m_config_get_option(config, name); - if (opt) { - if (bstr_endswith0(name, "-clr")) - return 0; - return m_option_required_params(opt); - } - return M_OPT_UNKNOWN; + struct m_config_option *co = m_config_get_co(config, name); + if (!co) + return M_OPT_UNKNOWN; + if (bstr_endswith0(name, "-clr")) + return 0; + return m_option_required_params(co->opt); } static int sort_opt_compare(const void *pa, const void *pb) -- cgit v1.2.3 From 17dbb39dec1fcd3280f86e480a7191b6f78cbbcc Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 1 Sep 2016 14:29:08 +0200 Subject: m_config: fix "no-" option handling with sub-options E.g. --vf=scale=no-arnd didn't work, because it didn't recognize no-arnd as flag option. The top-level command line parser is not affected by this, because it uses the result of m_config_option_requires_param() differently and assumes unknown parameters do not necessarily require a parameter. (The suboption parser can't do this.) --- options/m_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index e5600228da..352965ea47 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -729,7 +729,7 @@ int m_config_option_requires_param(struct m_config *config, bstr name) { struct m_config_option *co = m_config_get_co(config, name); if (!co) - return M_OPT_UNKNOWN; + return m_config_find_negation_opt(config, &name) ? 0 : M_OPT_UNKNOWN; if (bstr_endswith0(name, "-clr")) return 0; return m_option_required_params(co->opt); -- cgit v1.2.3 From d32bee5f019f2c51839df16b7c7955dc33651cbe Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 1 Sep 2016 20:00:43 +0200 Subject: command: add options to property list Now options are accessible through the property list as well, which unifies them to a degree. Not all options support runtime changes (meaning affected components need to be restarted for the options to take effects). Remove from the manpage those properties which are cleanly mapped to options anyway. From the user-perspective they're just options available through the property interface. --- options/m_config.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 352965ea47..d4c19808b0 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -447,6 +447,16 @@ struct m_config_option *m_config_get_co(const struct m_config *config, return NULL; } +int m_config_get_co_count(struct m_config *config) +{ + return config->num_opts; +} + +struct m_config_option *m_config_get_co_index(struct m_config *config, int index) +{ + return &config->opts[index]; +} + const char *m_config_get_positional_option(const struct m_config *config, int p) { int pos = 0; @@ -509,7 +519,7 @@ static void handle_on_set(struct m_config *config, struct m_config_option *co, mp_msg_update_msglevels(config->global); } -// The type data points to is as in: m_config_get_co(config, name)->opt +// The type data points to is as in: co->opt int m_config_set_option_raw(struct m_config *config, struct m_config_option *co, void *data, int flags) { -- cgit v1.2.3 From 4fa6bcbb902d500ca0a1b9d2feeab5a4e5a98345 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 14:49:34 +0200 Subject: m_config: add helper function for initializing af/ao/vf/vo suboptions Normally I'd prefer a bunch of smaller functions with fewer parameters over a single function with a lot of parameters. But future changes will require messing with the parameters in a slightly more complex way, so a combined function will be needed anyway. The now-unused "global" parameter is required for later as well. --- options/m_config.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index d4c19808b0..7da23dc8d0 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -221,7 +221,7 @@ struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx, return m_config_new(talloc_ctx, log, 0, desc->priv_defaults, desc->options); } -int m_config_set_obj_params(struct m_config *conf, char **args) +static int m_config_set_obj_params(struct m_config *conf, char **args) { for (int n = 0; args && args[n * 2 + 0]; n++) { int r = m_config_set_option(conf, bstr0(args[n * 2 + 0]), @@ -232,8 +232,8 @@ int m_config_set_obj_params(struct m_config *conf, char **args) return 0; } -int m_config_apply_defaults(struct m_config *config, const char *name, - struct m_obj_settings *defaults) +static int m_config_apply_defaults(struct m_config *config, const char *name, + struct m_obj_settings *defaults) { int r = 0; for (int n = 0; defaults && defaults[n].name; n++) { @@ -246,6 +246,22 @@ int m_config_apply_defaults(struct m_config *config, const char *name, return r; } +struct m_config *m_config_from_obj_desc_and_args(void *ta_parent, + struct mp_log *log, struct mpv_global *global, struct m_obj_desc *desc, + const char *name, struct m_obj_settings *defaults, char **args) +{ + struct m_config *config = m_config_from_obj_desc(ta_parent, log, desc); + if (m_config_apply_defaults(config, name, defaults) < 0) + goto error; + if (m_config_set_obj_params(config, args) < 0) + goto error; + + return config; +error: + talloc_free(config); + return NULL; +} + static void ensure_backup(struct m_config *config, struct m_config_option *co) { if (co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) -- cgit v1.2.3 From 423e53ba0bad034685e5229720d55548afb1efbe Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 15:45:22 +0200 Subject: m_config: introduce basic mechanism to synchronize global option updates The way option runtime changes are handled is pretty bad in the current codebase. There's a big option struct (MPOpts), which contains almost everything, and for which no synchronization mechanism exists. This was handled by either making some options read-only after initialization, duplicating the option struct, using sub-options (in the VO), and so on. Introduce a mechanism that creates a copy of the global options (or parts of it), and provides a well-defined way to update them in a thread-safe way. Most code can remain the same, just that all the component glue code has to explicitly make use of it first. There is still lots of room for improvement. For example, the update mechanism could be better. --- options/m_config.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 203 insertions(+), 4 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 7da23dc8d0..25b7d75c7f 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "libmpv/client.h" @@ -34,9 +35,11 @@ #include "m_config.h" #include "options/m_option.h" +#include "common/global.h" #include "common/msg.h" #include "common/msg_control.h" #include "misc/node.h" +#include "osdep/atomics.h" static const union m_option_value default_value; @@ -46,6 +49,21 @@ static const union m_option_value default_value; // Maximal include depth. #define MAX_RECURSION_DEPTH 8 +// For use with m_config_cache. +struct m_config_shadow { + pthread_mutex_t lock; + struct m_config *root; + char *data; +}; + +// Represents a sub-struct (OPT_SUBSTRUCT()). +struct m_config_group { + const struct m_sub_options *group; // or NULL for top-level options + int parent_group; // index of parent group in m_config.groups + void *opts; // pointer to group user option struct + atomic_llong ts; // incremented on every write access +}; + struct m_profile { struct m_profile *next; char *name; @@ -183,8 +201,17 @@ static void config_destroy(void *p) { struct m_config *config = p; m_config_restore_backups(config); - for (int n = 0; n < config->num_opts; n++) - m_option_free(config->opts[n].opt, config->opts[n].data); + for (int n = 0; n < config->num_opts; n++) { + struct m_config_option *co = &config->opts[n]; + + m_option_free(co->opt, co->data); + + if (config->shadow && co->shadow_offset >= 0) + m_option_free(co->opt, config->shadow->data + co->shadow_offset); + } + + if (config->shadow) + pthread_mutex_destroy(&config->shadow->lock); } struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log, @@ -195,12 +222,21 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log, talloc_set_destructor(config, config_destroy); *config = (struct m_config) {.log = log, .size = size, .defaults = defaults, .options = options}; + // size==0 means a dummy object is created if (size) { config->optstruct = talloc_zero_size(config, size); if (defaults) memcpy(config->optstruct, defaults, size); } + + config->num_groups = 1; + MP_TARRAY_GROW(config, config->groups, 1); + config->groups[0] = (struct m_config_group){ + .parent_group = -1, + .opts = config->optstruct, + }; + if (options) add_options(config, NULL, config->optstruct, defaults, options); return config; @@ -356,6 +392,8 @@ static void m_config_add_option(struct m_config *config, struct m_config_option co = { .opt = arg, .name = arg->name, + .shadow_offset = -1, + .group = parent ? parent->group : 0, }; if (arg->offset >= 0) { @@ -363,6 +401,14 @@ static void m_config_add_option(struct m_config *config, co.data = (char *)optstruct + arg->offset; if (optstruct_def) co.default_data = (char *)optstruct_def + arg->offset; + int size = arg->type->size; + if (optstruct && size) { + // The required alignment is unknown, so go with the minimum C + // could require. Slightly wasteful, but not that much. + int align = (size - config->shadow_size % size) % size; + co.shadow_offset = config->shadow_size + align; + config->shadow_size = co.shadow_offset + size; + } } if (arg->defval) @@ -385,6 +431,10 @@ static void m_config_add_option(struct m_config *config, if (arg->type->flags & M_OPT_TYPE_HAS_CHILD) { const struct m_sub_options *subopts = arg->priv; + // Can't be used multiple times. + for (int n = 0; n < config->num_groups; n++) + assert(config->groups[n].group != subopts); + void *new_optstruct = NULL; if (co.data) { new_optstruct = m_config_alloc_struct(config, subopts); @@ -395,6 +445,16 @@ static void m_config_add_option(struct m_config *config, if (!new_optstruct_def) new_optstruct_def = subopts->defaults; + int parent_group = co.group; + co.group = config->num_groups++; + MP_TARRAY_GROW(config, config->groups, co.group); + struct m_config_group *group = &config->groups[co.group]; + *group = (struct m_config_group){ + .group = subopts, + .parent_group = parent_group, + .opts = new_optstruct, + }; + add_options(config, &co, new_optstruct, new_optstruct_def, subopts->opts); } else { // Initialize options @@ -531,8 +591,7 @@ static void handle_on_set(struct m_config *config, struct m_config_option *co, if (flags & M_SETOPT_FROM_CMDLINE) co->is_set_from_cmdline = true; - if (config->global && (co->opt->flags & M_OPT_TERM)) - mp_msg_update_msglevels(config->global); + m_config_notify_change_co(config, co); } // The type data points to is as in: co->opt @@ -949,6 +1008,146 @@ struct mpv_node m_config_get_profiles(struct m_config *config) return root; } +void m_config_create_shadow(struct m_config *config) +{ + assert(config->global && config->options && config->size); + assert(!config->shadow && !config->global->config); + + config->shadow = talloc_zero(config, struct m_config_shadow); + config->shadow->data = talloc_zero_size(config->shadow, config->shadow_size); + + config->shadow->root = config; + pthread_mutex_init(&config->shadow->lock, NULL); + + config->global->config = config->shadow; + + for (int n = 0; n < config->num_opts; n++) { + struct m_config_option *co = &config->opts[n]; + if (co->shadow_offset < 0) + continue; + m_option_copy(co->opt, config->shadow->data + co->shadow_offset, co->data); + } +} + +// Return whether parent is a parent of group. Also returns true if they're equal. +static bool is_group_included(struct m_config *config, int group, int parent) +{ + for (;;) { + if (group == parent) + return true; + if (group < 0) + break; + group = config->groups[group].parent_group; + } + return false; +} + +struct m_config_cache *m_config_cache_alloc(void *ta_parent, + struct mpv_global *global, + const struct m_sub_options *group) +{ + struct m_config_shadow *shadow = global->config; + struct m_config *root = shadow->root; + + struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache); + cache->shadow = shadow; + cache->shadow_config = m_config_new(cache, mp_null_log, root->size, + root->defaults, root->options); + + struct m_config *config = cache->shadow_config; + + assert(config->num_opts == root->num_opts); + for (int n = 0; n < root->num_opts; n++) { + assert(config->opts[n].opt->type == root->opts[n].opt->type); + assert(config->opts[n].shadow_offset == root->opts[n].shadow_offset); + } + + cache->ts = -1; + cache->group = -1; + + for (int n = 0; n < config->num_groups; n++) { + if (config->groups[n].group == group) { + cache->opts = config->groups[n].opts; + cache->group = n; + break; + } + } + + assert(cache->group >= 0); + assert(cache->opts); + + // If we're not on the top-level, restrict set of options to the sub-group + // to reduce update costs. (It would be better not to add them in the first + // place.) + if (cache->group > 0) { + int num_opts = config->num_opts; + config->num_opts = 0; + for (int n = 0; n < num_opts; n++) { + struct m_config_option *co = &config->opts[n]; + if (is_group_included(config, co->group, cache->group)) { + config->opts[config->num_opts++] = *co; + } else { + m_option_free(co->opt, co->data); + } + } + for (int n = 0; n < config->num_groups; n++) { + if (!is_group_included(config, n, cache->group)) + TA_FREEP(&config->groups[n].opts); + } + } + + m_config_cache_update(cache); + + return cache; +} + +bool m_config_cache_update(struct m_config_cache *cache) +{ + struct m_config_shadow *shadow = cache->shadow; + + // Using atomics and checking outside of the lock - it's unknown whether + // this makes it faster or slower. Just cargo culting it. + if (atomic_load(&shadow->root->groups[cache->group].ts) <= cache->ts) + return false; + + pthread_mutex_lock(&shadow->lock); + cache->ts = atomic_load(&shadow->root->groups[cache->group].ts); + for (int n = 0; n < cache->shadow_config->num_opts; n++) { + struct m_config_option *co = &cache->shadow_config->opts[n]; + if (co->shadow_offset >= 0) + m_option_copy(co->opt, co->data, shadow->data + co->shadow_offset); + } + pthread_mutex_unlock(&shadow->lock); + return true; +} + +void m_config_notify_change_co(struct m_config *config, + struct m_config_option *co) +{ + struct m_config_shadow *shadow = config->shadow; + + if (shadow) { + pthread_mutex_lock(&shadow->lock); + if (co->shadow_offset >= 0) + m_option_copy(co->opt, shadow->data + co->shadow_offset, co->data); + pthread_mutex_unlock(&shadow->lock); + + int group = co->group; + while (group >= 0) { + atomic_fetch_add(&config->groups[group].ts, 1); + group = config->groups[group].parent_group; + } + } + + if (config->global && (co->opt->flags & M_OPT_TERM)) + mp_msg_update_msglevels(config->global); +} + +struct m_config *mp_get_root_config(struct mpv_global *global) +{ + return global->config->root; +} + void *m_config_alloc_struct(void *talloc_ctx, const struct m_sub_options *subopts) { -- cgit v1.2.3 From 72c6bf1345235801dc8916a907360fd7e2514933 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 15:58:15 +0200 Subject: m_config: add some convenience functions To be used by the following commits. --- options/m_config.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 25b7d75c7f..19eb61d082 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -1143,6 +1143,27 @@ void m_config_notify_change_co(struct m_config *config, mp_msg_update_msglevels(config->global); } +void *mp_get_config_group(void *ta_parent, struct mpv_global *global, + const struct m_sub_options *group) +{ + assert(ta_parent); // without you'd necessarily leak memory + struct m_config_cache *cache = m_config_cache_alloc(ta_parent, global, group); + return cache->opts; +} + +void mp_read_option_raw(struct mpv_global *global, const char *name, + const struct m_option_type *type, void *dst) +{ + struct m_config_shadow *shadow = global->config; + struct m_config_option *co = m_config_get_co(shadow->root, bstr0(name)); + assert(co); + assert(co->shadow_offset >= 0); + assert(co->opt->type == type); + + memset(dst, 0, co->opt->type->size); + m_option_copy(co->opt, dst, shadow->data + co->shadow_offset); +} + struct m_config *mp_get_root_config(struct mpv_global *global) { return global->config->root; -- cgit v1.2.3 From 849480d0c9d5ef76bd3296034b2ad5019fb9c21d Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 15:59:40 +0200 Subject: vo_opengl: deprecate sub-options, add them as global options vo_opengl sub-option were always rather annoying to handle. It seems better to make them global options instead. This is simpler and easier to use. The only disadvantage we are aware of is that it's not clear that many/all of these new global options work with vo_opengl only. --vo=opengl-hq is also deprecated. There is extensive compatibility with the old behavior. One exception is that --vo-defaults will not apply to opengl-hq (though with opengl it still works). vo-cmdline is also dysfunctional and will be removed in a following commit. These changes also affect opengl-cb. The update mechanism is still rather inefficient: it requires syncing with the VO after each option change, rather than batching updates. There's also no granularity (video.c just updates "everything", and if auto-ICC profiles are enabled, vo_opengl.c will fetch them on each update). Most of the manpage changes were done by Niklas Haas . --- options/m_config.c | 56 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 19eb61d082..125702c362 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -257,39 +257,44 @@ struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx, return m_config_new(talloc_ctx, log, 0, desc->priv_defaults, desc->options); } -static int m_config_set_obj_params(struct m_config *conf, char **args) +static int m_config_set_obj_params(struct m_config *config, struct mp_log *log, + struct mpv_global *global, char **args) { for (int n = 0; args && args[n * 2 + 0]; n++) { - int r = m_config_set_option(conf, bstr0(args[n * 2 + 0]), - bstr0(args[n * 2 + 1])); - if (r < 0) - return r; + const char *opt = args[n * 2 + 0]; + const char *val = args[n * 2 + 1]; + struct m_config_option *co = m_config_get_co(config, bstr0(opt)); + struct m_config *target = config; + if (co && co->opt->type == &m_option_type_subopt_legacy) { + const char *newopt = co->opt->priv; + assert(global); + target = mp_get_root_config(global); + mp_warn(log, "Using suboptions is deprecated. Use the global '--%s' " + "option instead of '%s' suboption.\n", newopt, opt); + opt = newopt; + } + if (m_config_set_option(target, bstr0(opt), bstr0(val)) < 0) + return -1; } + return 0; } -static int m_config_apply_defaults(struct m_config *config, const char *name, - struct m_obj_settings *defaults) +struct m_config *m_config_from_obj_desc_and_args(void *ta_parent, + struct mp_log *log, struct mpv_global *global, struct m_obj_desc *desc, + const char *name, struct m_obj_settings *defaults, char **args) { - int r = 0; + struct m_config *config = m_config_from_obj_desc(ta_parent, log, desc); + for (int n = 0; defaults && defaults[n].name; n++) { struct m_obj_settings *entry = &defaults[n]; if (name && strcmp(entry->name, name) == 0) { - r = m_config_set_obj_params(config, entry->attribs); - break; + if (m_config_set_obj_params(config, log, global, entry->attribs) < 0) + goto error; } } - return r; -} -struct m_config *m_config_from_obj_desc_and_args(void *ta_parent, - struct mp_log *log, struct mpv_global *global, struct m_obj_desc *desc, - const char *name, struct m_obj_settings *defaults, char **args) -{ - struct m_config *config = m_config_from_obj_desc(ta_parent, log, desc); - if (m_config_apply_defaults(config, name, defaults) < 0) - goto error; - if (m_config_set_obj_params(config, args) < 0) + if (m_config_set_obj_params(config, log, global, args) < 0) goto error; return config; @@ -1143,6 +1148,17 @@ void m_config_notify_change_co(struct m_config *config, mp_msg_update_msglevels(config->global); } +bool m_config_is_in_group(struct m_config *config, + const struct m_sub_options *group, + struct m_config_option *co) +{ + for (int n = 0; n < config->num_groups; n++) { + if (config->groups[n].group == group) + return is_group_included(config, co->group, n); + } + return false; +} + void *mp_get_config_group(void *ta_parent, struct mpv_global *global, const struct m_sub_options *group) { -- cgit v1.2.3 From 9770ce6c44cd26e7b9e5b525c3222281d32dcb6f Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 19:00:34 +0200 Subject: m_config: make sure profile values are never NULL Apparently this was supposed to be handled - but badly at best. Make unset values always have the value "" instead of NULL to avoid special-cases. In particular, this fixes passing NULL to a %s format specifier to printf in show_profile(). Glibc prints this as "(null)", but it's undefined behavior, and other libcs can crash. --- options/m_config.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 125702c362..f3bbe7d8c9 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -943,7 +943,7 @@ struct m_profile *m_config_add_profile(struct m_config *config, char *name) void m_profile_set_desc(struct m_profile *p, bstr desc) { talloc_free(p->desc); - p->desc = bstrdup0(p, desc); + p->desc = bstrto0(p, desc); } int m_config_set_profile_option(struct m_config *config, struct m_profile *p, @@ -955,8 +955,8 @@ int m_config_set_profile_option(struct m_config *config, struct m_profile *p, if (i < 0) return i; p->opts = talloc_realloc(p, p->opts, char *, 2 * (p->num_opts + 2)); - p->opts[p->num_opts * 2] = bstrdup0(p, name); - p->opts[p->num_opts * 2 + 1] = bstrdup0(p, val); + p->opts[p->num_opts * 2] = bstrto0(p, name); + p->opts[p->num_opts * 2 + 1] = bstrto0(p, val); p->num_opts++; p->opts[p->num_opts * 2] = p->opts[p->num_opts * 2 + 1] = NULL; return 1; @@ -1005,8 +1005,7 @@ struct mpv_node m_config_get_profiles(struct m_config *config) for (int n = 0; n < profile->num_opts; n++) { struct mpv_node *opt_entry = node_array_add(opts, MPV_FORMAT_NODE_MAP); node_map_add_string(opt_entry, "key", profile->opts[n * 2 + 0]); - if (profile->opts[n * 2 + 1]) - node_map_add_string(opt_entry, "value", profile->opts[n * 2 + 1]); + node_map_add_string(opt_entry, "value", profile->opts[n * 2 + 1]); } } -- cgit v1.2.3 From eb14b18a33973021c30124775513795b689cec27 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 2 Sep 2016 19:24:12 +0200 Subject: config: allow profile forward-references in default profile This works by first parsing a config file into the default profile, and applying it once parsing the whole file is finished. This won't work across config files (not even if you include other config files via "include=file.conf"). --- options/m_config.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index f3bbe7d8c9..a456430ceb 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -928,8 +928,8 @@ struct m_profile *m_config_get_profile0(const struct m_config *config, struct m_profile *m_config_add_profile(struct m_config *config, char *name) { - if (!name || !name[0] || strcmp(name, "default") == 0) - return NULL; // never a real profile + if (!name || !name[0]) + name = "default"; struct m_profile *p = m_config_get_profile0(config, name); if (p) return p; @@ -986,6 +986,13 @@ int m_config_set_profile(struct m_config *config, char *name, int flags) return 0; } +void m_config_finish_default_profile(struct m_config *config, int flags) +{ + struct m_profile *p = m_config_add_profile(config, NULL); + m_config_set_profile(config, p->name, flags); + p->num_opts = 0; +} + struct mpv_node m_config_get_profiles(struct m_config *config) { struct mpv_node root; -- cgit v1.2.3 From cc813647d54843e4731cc36160f0c1e04e4b1404 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Sep 2016 21:02:09 +0200 Subject: m_config: move parts of m_config_add_option into its own function Preparation for the next commit. --- options/m_config.c | 62 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 26 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index a456430ceb..3d1355904e 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -371,6 +371,41 @@ static void add_options(struct m_config *config, m_config_add_option(config, parent, optstruct, optstruct_def, &defs[i]); } +static void add_sub_options(struct m_config *config, + struct m_config_option *parent, + const struct m_sub_options *subopts) +{ + // Can't be used multiple times. + for (int n = 0; n < config->num_groups; n++) + assert(config->groups[n].group != subopts); + + void *new_optstruct = NULL; + if (config->optstruct) // only if not noalloc + new_optstruct = m_config_alloc_struct(config, subopts); + if (parent && parent->data) + substruct_write_ptr(parent->data, new_optstruct); + + const void *new_optstruct_def = NULL; + if (parent && parent->default_data) + new_optstruct_def = substruct_read_ptr(parent->default_data); + if (!new_optstruct_def) + new_optstruct_def = subopts->defaults; + + int group = config->num_groups++; + MP_TARRAY_GROW(config, config->groups, group); + config->groups[group] = (struct m_config_group){ + .group = subopts, + .parent_group = parent ? parent->group : 0, + .opts = new_optstruct, + }; + + struct m_config_option next = { + .name = parent ? parent->name : "", + .group = group, + }; + add_options(config, &next, new_optstruct, new_optstruct_def, subopts->opts); +} + // Initialize a field with a given value. In case this is dynamic data, it has // to be allocated and copied. src can alias dst, also can be NULL. static void init_opt_inplace(const struct m_option *opt, void *dst, @@ -435,32 +470,7 @@ static void m_config_add_option(struct m_config *config, // Option with children -> add them if (arg->type->flags & M_OPT_TYPE_HAS_CHILD) { const struct m_sub_options *subopts = arg->priv; - - // Can't be used multiple times. - for (int n = 0; n < config->num_groups; n++) - assert(config->groups[n].group != subopts); - - void *new_optstruct = NULL; - if (co.data) { - new_optstruct = m_config_alloc_struct(config, subopts); - substruct_write_ptr(co.data, new_optstruct); - } - - const void *new_optstruct_def = substruct_read_ptr(co.default_data); - if (!new_optstruct_def) - new_optstruct_def = subopts->defaults; - - int parent_group = co.group; - co.group = config->num_groups++; - MP_TARRAY_GROW(config, config->groups, co.group); - struct m_config_group *group = &config->groups[co.group]; - *group = (struct m_config_group){ - .group = subopts, - .parent_group = parent_group, - .opts = new_optstruct, - }; - - add_options(config, &co, new_optstruct, new_optstruct_def, subopts->opts); + add_sub_options(config, &co, subopts); } else { // Initialize options if (co.data && co.default_data) -- cgit v1.2.3 From 4ab860cddc177047663bbe8940b0d34c621b6425 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Sep 2016 21:04:17 +0200 Subject: options: add a mechanism to make sub-option replacement slightly easier Instead of requiring each VO or AO to manually add members to MPOpts and the global option table, make it possible to register them automatically via vo_driver/ao_driver.global_opts members. This avoids modifying options.c/options.h every time, including having to duplicate the exact ifdeffery used to enable a driver. --- options/m_config.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 3d1355904e..18a9ad4de7 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -406,6 +406,18 @@ static void add_sub_options(struct m_config *config, add_options(config, &next, new_optstruct, new_optstruct_def, subopts->opts); } +static void add_global_subopts(struct m_config *config, + const struct m_obj_list *list) +{ + struct m_obj_desc desc; + for (int n = 0; ; n++) { + if (!list->get_desc(&desc, n)) + break; + if (desc.global_opts) + add_sub_options(config, NULL, desc.global_opts); + } +} + // Initialize a field with a given value. In case this is dynamic data, it has // to be allocated and copied. src can alias dst, also can be NULL. static void init_opt_inplace(const struct m_option *opt, void *dst, @@ -477,6 +489,10 @@ static void m_config_add_option(struct m_config *config, init_opt_inplace(arg, co.data, co.default_data); } + // (The deprecation_message check is a hack to exclude --vo-defaults etc.) + if (arg->type == &m_option_type_obj_settings_list && !arg->deprecation_message) + add_global_subopts(config, (const struct m_obj_list *)arg->priv); + if (arg->name[0]) // no own name -> hidden MP_TARRAY_APPEND(config, config->opts, config->num_opts, co); } -- cgit v1.2.3 From 633eb30cbe0e52879443633760e9460fe5bdcdfa Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 5 Sep 2016 21:05:47 +0200 Subject: options: add automagic hack for handling sub-option deprecations I decided that it's too much work to convert all the VO/AOs to the new option system manually at once. So here's a shitty hack instead, which achieves almost the same thing. (The only user-visible difference is that e.g. --vo=name:help will list the sub-options normally, instead of showing them as deprecation placeholders. Also, the sub-option parser will verify each option normally, instead of deferring to the global option parser.) Another advantage is that once we drop the deprecated options, converting the remaining things will be easier, because we obviously don't need to add the compatibility hacks. Using this mechanism is separate in the next commit to keep the diff noise down. --- options/m_config.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) (limited to 'options/m_config.c') diff --git a/options/m_config.c b/options/m_config.c index 18a9ad4de7..f8e8c705ae 100644 --- a/options/m_config.c +++ b/options/m_config.c @@ -258,15 +258,28 @@ struct m_config *m_config_from_obj_desc_noalloc(void *talloc_ctx, } static int m_config_set_obj_params(struct m_config *config, struct mp_log *log, - struct mpv_global *global, char **args) + struct mpv_global *global, + struct m_obj_desc *desc, char **args) { for (int n = 0; args && args[n * 2 + 0]; n++) { const char *opt = args[n * 2 + 0]; const char *val = args[n * 2 + 1]; struct m_config_option *co = m_config_get_co(config, bstr0(opt)); + if (!co) + continue; struct m_config *target = config; - if (co && co->opt->type == &m_option_type_subopt_legacy) { - const char *newopt = co->opt->priv; + bool is_legacy = co->opt->type == &m_option_type_subopt_legacy; + bool force_legacy = !!desc->legacy_prefix; + if (is_legacy || force_legacy) { + // Legacy: redirect deprecated sub-options to global ones. + char tmp[100]; + const char *newopt; + if (is_legacy) { + newopt = co->opt->priv; + } else { + snprintf(tmp, sizeof(tmp), "%s-%s", desc->legacy_prefix, opt); + newopt = tmp; + } assert(global); target = mp_get_root_config(global); mp_warn(log, "Using suboptions is deprecated. Use the global '--%s' " @@ -289,14 +302,29 @@ struct m_config *m_config_from_obj_desc_and_args(void *ta_parent, for (int n = 0; defaults && defaults[n].name; n++) { struct m_obj_settings *entry = &defaults[n]; if (name && strcmp(entry->name, name) == 0) { - if (m_config_set_obj_params(config, log, global, entry->attribs) < 0) + if (m_config_set_obj_params(config, log, global, desc, entry->attribs) < 0) goto error; } } - if (m_config_set_obj_params(config, log, global, args) < 0) + if (m_config_set_obj_params(config, log, global, desc, args) < 0) goto error; + if (desc->legacy_prefix) { + assert(global); + struct m_config *root = mp_get_root_config(global); + // In this mode, the AO/VO will still access the options via its priv + // struct (like with real sub-options). We have to copy them over. + for (int n = 0; n < config->num_opts; n++) { + struct m_config_option *co = &config->opts[n]; + char opt[100]; + snprintf(opt, sizeof(opt), "%s-%s", desc->legacy_prefix, co->name); + struct m_config_option *g = m_co