diff options
Diffstat (limited to 'options')
-rw-r--r-- | options/m_config.h | 2 | ||||
-rw-r--r-- | options/m_config_core.c | 139 | ||||
-rw-r--r-- | options/m_config_core.h | 7 | ||||
-rw-r--r-- | options/m_config_frontend.c | 40 | ||||
-rw-r--r-- | options/m_config_frontend.h | 7 | ||||
-rw-r--r-- | options/m_option.c | 351 | ||||
-rw-r--r-- | options/m_option.h | 66 | ||||
-rw-r--r-- | options/m_property.c | 47 | ||||
-rw-r--r-- | options/m_property.h | 22 | ||||
-rw-r--r-- | options/options.c | 684 | ||||
-rw-r--r-- | options/options.h | 264 | ||||
-rw-r--r-- | options/parse_commandline.c | 11 | ||||
-rw-r--r-- | options/parse_configfile.c | 47 | ||||
-rw-r--r-- | options/parse_configfile.h | 5 | ||||
-rw-r--r-- | options/path.c | 236 | ||||
-rw-r--r-- | options/path.h | 54 |
16 files changed, 939 insertions, 1043 deletions
diff --git a/options/m_config.h b/options/m_config.h index d2ce2b4467..a6bade90c9 100644 --- a/options/m_config.h +++ b/options/m_config.h @@ -1 +1 @@ -#include "m_config_core.h"
\ No newline at end of file +#include "m_config_core.h" diff --git a/options/m_config_core.c b/options/m_config_core.c index 4f8c462db1..328ec6479f 100644 --- a/options/m_config_core.c +++ b/options/m_config_core.c @@ -15,29 +15,29 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ -#include <stdlib.h> -#include <stdio.h> +#include <assert.h> #include <errno.h> +#include <stdatomic.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <strings.h> -#include <assert.h> -#include <stdbool.h> -#include <pthread.h> -#include "m_config_core.h" -#include "options/m_option.h" #include "common/common.h" #include "common/global.h" -#include "common/msg.h" #include "common/msg_control.h" +#include "common/msg.h" +#include "m_config_core.h" #include "misc/dispatch.h" -#include "osdep/atomic.h" +#include "options/m_option.h" +#include "osdep/threads.h" // For use with m_config_cache. struct m_config_shadow { - pthread_mutex_t lock; + mp_mutex lock; // Incremented on every option change. - mp_atomic_uint64 ts; + _Atomic uint64_t ts; // -- immutable after init // List of m_sub_options instances. // Index 0 is the top-level and is always present. @@ -99,14 +99,19 @@ struct config_cache { void *wakeup_cb_ctx; }; +struct force_update { + char *name; + uint64_t ts; +}; + // Per m_config_data state for each m_config_group. struct m_group_data { - char *udata; // pointer to group user option struct - uint64_t ts; // timestamp of the data copy + char *udata; // pointer to group user option struct + uint64_t ts; // timestamp of the data copy + struct force_update **force_update; // tracks opts that are written with force update + int force_update_len; }; -static const union m_option_value default_value = {0}; - static void add_sub_group(struct m_config_shadow *shadow, const char *name_prefix, int parent_group_index, int parent_ptr, const struct m_sub_options *subopts); @@ -241,7 +246,7 @@ const void *m_config_shadow_get_opt_default(struct m_config_shadow *shadow, if (subopt->defaults) return (char *)subopt->defaults + opt->offset; - return &default_value; + return &m_option_value_default; } void *m_config_cache_get_opt_data(struct m_config_cache *cache, int32_t id) @@ -419,14 +424,14 @@ static void shadow_destroy(void *p) assert(shadow->num_listeners == 0); talloc_free(shadow->data); - pthread_mutex_destroy(&shadow->lock); + mp_mutex_destroy(&shadow->lock); } struct m_config_shadow *m_config_shadow_new(const struct m_sub_options *root) { struct m_config_shadow *shadow = talloc_zero(NULL, struct m_config_shadow); talloc_set_destructor(shadow, shadow_destroy); - pthread_mutex_init(&shadow->lock, NULL); + mp_mutex_init(&shadow->lock); add_sub_group(shadow, NULL, -1, -1, root); @@ -568,9 +573,9 @@ struct m_config_cache *m_config_cache_from_shadow(void *ta_parent, in->shadow = shadow; in->src = shadow->data; - pthread_mutex_lock(&shadow->lock); + mp_mutex_lock(&shadow->lock); in->data = allocate_option_data(cache, shadow, group_index, in->src); - pthread_mutex_unlock(&shadow->lock); + mp_mutex_unlock(&shadow->lock); cache->opts = in->data->gdata[0].udata; @@ -590,6 +595,34 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent, return m_config_cache_from_shadow(ta_parent, global->config, group); } +static void append_force_update(struct m_config_cache *cache, struct m_group_data *gdata, + const char *opt_name) +{ + for (int i = 0; i < gdata->force_update_len; ++i) { + if (strcmp(opt_name, gdata->force_update[i]->name) == 0) { + gdata->force_update[i]->ts = gdata->ts; + return; + } + } + struct force_update *new_update = talloc_zero(cache, struct force_update); + new_update->name = talloc_strdup(cache, opt_name); + new_update->ts = gdata->ts; + MP_TARRAY_APPEND(cache, gdata->force_update, gdata->force_update_len, new_update); +} + +static bool check_force_update(struct m_group_data *gdata, const char *opt_name, + uint64_t timestamp) +{ + for (int i = 0; i < gdata->force_update_len; ++i) { + if ((strcmp(opt_name, gdata->force_update[i]->name) == 0) && + gdata->force_update[i]->ts == timestamp) + { + return true; + } + } + return false; +} + static void update_next_option(struct m_config_cache *cache, void **p_opt) { struct config_cache *in = cache->internal; @@ -611,16 +644,18 @@ static void update_next_option(struct m_config_cache *cache, void **p_opt) while (opts && opts[in->upd_opt].name) { const struct m_option *opt = &opts[in->upd_opt]; + void *dsrc = gsrc->udata + opt->offset; + void *ddst = gdst->udata + opt->offset; if (opt->offset >= 0 && opt->type->size) { - void *dsrc = gsrc->udata + opt->offset; - void *ddst = gdst->udata + opt->offset; - - if (!m_option_equal(opt, ddst, dsrc)) { + bool opt_equal = m_option_equal(opt, ddst, dsrc); + bool force_update = opt->force_update && + check_force_update(gsrc, opt->name, in->ts); + if (!opt_equal || force_update) { uint64_t ch = get_opt_change_mask(dst->shadow, in->upd_group, dst->group_index, opt); - if (cache->debug) { + if (cache->debug && !opt_equal) { char *vdst = m_option_print(opt, ddst); char *vsrc = m_option_print(opt, dsrc); mp_warn(cache->debug, "Option '%s' changed from " @@ -677,7 +712,7 @@ bool m_config_cache_update(struct m_config_cache *cache) if (!cache_check_update(cache)) return false; - pthread_mutex_lock(&shadow->lock); + mp_mutex_lock(&shadow->lock); bool res = false; while (1) { void *p; @@ -686,7 +721,7 @@ bool m_config_cache_update(struct m_config_cache *cache) break; res = true; } - pthread_mutex_unlock(&shadow->lock); + mp_mutex_unlock(&shadow->lock); return res; } @@ -699,9 +734,9 @@ bool m_config_cache_get_next_changed(struct m_config_cache *cache, void **opt) if (!cache_check_update(cache) && in->upd_group < 0) return false; - pthread_mutex_lock(&shadow->lock); + mp_mutex_lock(&shadow->lock); update_next_option(cache, opt); - pthread_mutex_unlock(&shadow->lock); + mp_mutex_unlock(&shadow->lock); return !!*opt; } @@ -746,13 +781,14 @@ bool m_config_cache_write_opt(struct m_config_cache *cache, void *ptr) struct m_config_group *g = &shadow->groups[group_idx]; const struct m_option *opt = &g->group->opts[opt_idx]; - pthread_mutex_lock(&shadow->lock); + mp_mutex_lock(&shadow->lock); struct m_group_data *gdst = m_config_gdata(in->data, group_idx); struct m_group_data *gsrc = m_config_gdata(in->src, group_idx); assert(gdst && gsrc); - bool changed = !m_option_equal(opt, gsrc->udata + opt->offset, ptr); + bool changed = !m_option_equal(opt, gsrc->udata + opt->offset, ptr) || + opt->force_update; if (changed) { m_option_copy(opt, gsrc->udata + opt->offset, ptr); @@ -765,7 +801,10 @@ bool m_config_cache_write_opt(struct m_config_cache *cache, void *ptr) } } - pthread_mutex_unlock(&shadow->lock); + if (opt->force_update) + append_force_update(cache, gsrc, opt->name); + + mp_mutex_unlock(&shadow->lock); return changed; } @@ -776,7 +815,7 @@ void m_config_cache_set_wakeup_cb(struct m_config_cache *cache, struct config_cache *in = cache->internal; struct m_config_shadow *shadow = in->shadow; - pthread_mutex_lock(&shadow->lock); + mp_mutex_lock(&shadow->lock); if (in->in_list) { for (int n = 0; n < shadow->num_listeners; n++) { if (shadow->listeners[n] == in) { @@ -798,7 +837,7 @@ void m_config_cache_set_wakeup_cb(struct m_config_cache *cache, in->wakeup_cb = cb; in->wakeup_cb_ctx = cb_ctx; } - pthread_mutex_unlock(&shadow->lock); + mp_mutex_unlock(&shadow->lock); } static void dispatch_notify(void *p) @@ -850,38 +889,6 @@ void *mp_get_config_group(void *ta_parent, struct mpv_global *global, 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; - - int32_t optid = -1; - while (m_config_shadow_get_next_opt(shadow, &optid)) { - char buf[M_CONFIG_MAX_OPT_NAME_LEN]; - const char *opt_name = - m_config_shadow_get_opt_name(shadow, optid, buf, sizeof(buf)); - - if (strcmp(name, opt_name) == 0) { - const struct m_option *opt = m_config_shadow_get_opt(shadow, optid); - - int group_index, opt_index; - get_opt_from_id(shadow, optid, &group_index, &opt_index); - - struct m_group_data *gdata = m_config_gdata(shadow->data, group_index); - assert(gdata); - - assert(opt->offset >= 0); - assert(opt->type == type); - - memset(dst, 0, opt->type->size); - m_option_copy(opt, dst, gdata->udata + opt->offset); - return; - } - } - - MP_ASSERT_UNREACHABLE(); // not found -} - static const struct m_config_group *find_group(struct mpv_global *global, const struct m_option *cfg) { diff --git a/options/m_config_core.h b/options/m_config_core.h index c4902be9d1..a9558423d8 100644 --- a/options/m_config_core.h +++ b/options/m_config_core.h @@ -131,13 +131,6 @@ bool m_config_cache_write_opt(struct m_config_cache *cache, void *ptr); void *mp_get_config_group(void *ta_parent, struct mpv_global *global, const struct m_sub_options *group); -// Read a single global option in a thread-safe way. For multiple options, -// use m_config_cache. The option must exist and match the provided type (the -// type is used as a sanity check only). Performs semi-expensive lookup. -// Warning: new code must not use this. -void mp_read_option_raw(struct mpv_global *global, const char *name, - const struct m_option_type *type, void *dst); - // Allocate a priv struct that is backed by global options (like AOs and VOs, // anything that uses m_obj_list.use_global_options == true). // The result contains a snapshot of the current option values of desc->options. diff --git a/options/m_config_frontend.c b/options/m_config_frontend.c index 5a6db46d83..d800cdb75d 100644 --- a/options/m_config_frontend.c +++ b/options/m_config_frontend.c @@ -15,28 +15,28 @@ * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ +#include <assert.h> +#include <errno.h> #include <float.h> -#include <stdlib.h> +#include <stdatomic.h> +#include <stdbool.h> #include <stdio.h> -#include <errno.h> +#include <stdlib.h> #include <string.h> #include <strings.h> -#include <assert.h> -#include <stdbool.h> -#include <pthread.h> #include "libmpv/client.h" -#include "m_config.h" -#include "m_config_frontend.h" -#include "options/m_option.h" #include "common/common.h" #include "common/global.h" -#include "common/msg.h" #include "common/msg_control.h" +#include "common/msg.h" +#include "m_config_frontend.h" +#include "m_config.h" #include "misc/dispatch.h" #include "misc/node.h" -#include "osdep/atomic.h" +#include "options/m_option.h" +#include "osdep/threads.h" extern const char mp_help_text[]; @@ -168,18 +168,9 @@ static int m_config_set_obj_params(struct m_config *config, struct mp_log *log, 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) + char **args) { struct m_config *config = m_config_from_obj_desc(ta_parent, log, global, desc); - - 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, desc, entry->attribs) < 0) - goto error; - } - } - if (m_config_set_obj_params(config, log, global, desc, args) < 0) goto error; @@ -758,7 +749,7 @@ int m_config_set_option_cli(struct m_config *config, struct bstr name, BSTR_P(name), BSTR_P(param), flags); } - union m_option_value val = {0}; + union m_option_value val = m_option_value_default; // Some option types are "impure" and work on the existing data. // (Prime examples: --vf-add, --sub-file) @@ -792,7 +783,7 @@ int m_config_set_option_node(struct m_config *config, bstr name, // 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}; + union m_option_value val = m_option_value_default; if (data->format == MPV_FORMAT_STRING) { bstr param = bstr0(data->u.string); @@ -877,11 +868,10 @@ void m_config_print_option_list(const struct m_config *config, const char *name) } char *def = NULL; const void *defptr = m_config_get_co_default(config, co); - const union m_option_value default_value = {0}; if (!defptr) - defptr = &default_value; + defptr = &m_option_value_default; if (defptr) - def = m_option_pretty_print(opt, defptr); + def = m_option_pretty_print(opt, defptr, false); if (def) { MP_INFO(config, " (default: %s)", def); talloc_free(def); diff --git a/options/m_config_frontend.h b/options/m_config_frontend.h index 2812033b75..6108d9feec 100644 --- a/options/m_config_frontend.h +++ b/options/m_config_frontend.h @@ -17,9 +17,10 @@ #pragma once +#include <stdatomic.h> +#include <stdbool.h> #include <stddef.h> #include <stdint.h> -#include <stdbool.h> #include "common/common.h" #include "common/global.h" @@ -29,7 +30,6 @@ #include "misc/bstr.h" #include "misc/dispatch.h" #include "options/m_option.h" -#include "osdep/atomic.h" // m_config provides an API to manipulate the config variables in MPlayer. // It makes use of the Options API to provide a context stack that @@ -115,10 +115,9 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log, // different sub-options for every filter (represented by separate desc // structs). // args is an array of key/value pairs (args=[k0, v0, k1, v1, ..., NULL]). -// name/defaults is only needed for the legacy af-defaults/vf-defaults options. 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); + char **args); // Like m_config_from_obj_desc_and_args(), but don't allocate option the // struct, i.e. m_config.optstruct==NULL. This is used by the sub-option diff --git a/options/m_option.c b/options/m_option.c index 898d0da4f6..3d9ae26669 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -270,6 +270,7 @@ static bool flag_equal(const m_option_t *opt, void *a, void *b) return VAL(a) == VAL(b); } +// Only exists for libmpv interopability and should not be used anywhere. const m_option_type_t m_option_type_flag = { // need yes or no in config files .name = "Flag", @@ -770,7 +771,7 @@ static const struct m_opt_choice_alternatives *get_choice(const m_option_t *opt, return NULL; } } - abort(); + MP_ASSERT_UNREACHABLE(); } static int choice_get(const m_option_t *opt, void *ta_parent, @@ -1022,12 +1023,12 @@ static char *print_double(const m_option_t *opt, const void *val) return talloc_asprintf(NULL, "%f", f); } -static char *print_double_f3(const m_option_t *opt, const void *val) +static char *pretty_print_double(const m_option_t *opt, const void *val) { double f = VAL(val); if (isnan(f)) return print_double(opt, val); - return talloc_asprintf(NULL, "%.3f", f); + return mp_format_double(NULL, f, 4, false, false, !(opt->flags & M_OPT_FIXED_LEN_PRINT)); } static void add_double(const m_option_t *opt, void *val, double add, bool wrap) @@ -1099,7 +1100,33 @@ const m_option_type_t m_option_type_double = { .size = sizeof(double), .parse = parse_double, .print = print_double, - .pretty_print = print_double_f3, + .pretty_print = pretty_print_double, + .copy = copy_opt, + .add = add_double, + .multiply = multiply_double, + .set = double_set, + .get = double_get, + .equal = double_equal, +}; + +static int parse_double_aspect(struct mp_log *log, const m_option_t *opt, + struct bstr name, struct bstr param, void *dst) +{ + if (bstr_equals0(param, "no")) { + if (dst) + VAL(dst) = 0.0; + return 1; + } + return parse_double(log, opt, name, param, dst); +} + +const m_option_type_t m_option_type_aspect = { + .name = "Aspect", + .size = sizeof(double), + .flags = M_OPT_TYPE_CHOICE | M_OPT_TYPE_USES_RANGE, + .parse = parse_double_aspect, + .print = print_double, + .pretty_print = pretty_print_double, .copy = copy_opt, .add = add_double, .multiply = multiply_double, @@ -1127,10 +1154,10 @@ static char *print_float(const m_option_t *opt, const void *val) return print_double(opt, &tmp); } -static char *print_float_f3(const m_option_t *opt, const void *val) +static char *pretty_print_float(const m_option_t *opt, const void *val) { double tmp = VAL(val); - return print_double_f3(opt, &tmp); + return pretty_print_double(opt, &tmp); } static void add_float(const m_option_t *opt, void *val, double add, bool wrap) @@ -1175,33 +1202,7 @@ const m_option_type_t m_option_type_float = { .size = sizeof(float), .parse = parse_float, .print = print_float, - .pretty_print = print_float_f3, - .copy = copy_opt, - .add = add_float, - .multiply = multiply_float, - .set = float_set, - .get = float_get, - .equal = float_equal, -}; - -static int parse_float_aspect(struct mp_log *log, const m_option_t *opt, - struct bstr name, struct bstr param, void *dst) -{ - if (bstr_equals0(param, "no")) { - if (dst) - VAL(dst) = 0.0f; - return 1; - } - return parse_float(log, opt, name, param, dst); -} - -const m_option_type_t m_option_type_aspect = { - .name = "Aspect", - .size = sizeof(float), - .flags = M_OPT_TYPE_CHOICE | M_OPT_TYPE_USES_RANGE, - .parse = parse_float_aspect, - .print = print_float, - .pretty_print = print_float_f3, + .pretty_print = pretty_print_float, .copy = copy_opt, .add = add_float, .multiply = multiply_float, @@ -1291,11 +1292,10 @@ const m_option_type_t m_option_type_string = { #define OP_NONE 0 #define OP_ADD 1 #define OP_PRE 2 -#define OP_DEL 3 -#define OP_CLR 4 -#define OP_TOGGLE 5 -#define OP_APPEND 6 -#define OP_REMOVE 7 +#define OP_CLR 3 +#define OP_TOGGLE 4 +#define OP_APPEND 5 +#define OP_REMOVE 6 static void free_str_list(void *dst) { @@ -1337,55 +1337,6 @@ static int str_list_add(char **add, int n, void *dst, int pre) return 1; } -static int str_list_del(struct mp_log *log, char **del, int n, void *dst) -{ - char **lst, *ep; - int i, ln, s; - long idx; - - lst = VAL(dst); - - for (ln = 0; lst && lst[ln]; ln++) - /**/; - s = ln; - - for (i = 0; del[i] != NULL; i++) { - idx = strtol(del[i], &ep, 0); - if (*ep) { - mp_err(log, "Invalid index: %s\n", del[i]); - talloc_free(del[i]); - continue; - } - talloc_free(del[i]); - if (idx < 0 || idx >= ln) { - mp_err(log, "Index %ld is out of range.\n", idx); - continue; - } else if (!lst[idx]) - continue; - talloc_free(lst[idx]); - lst[idx] = NULL; - s--; - } - talloc_free(del); - - if (s == 0) { - talloc_free(lst); - VAL(dst) = NULL; - return 1; - } - - // Don't bother shrinking the list allocation - for (i = 0, n = 0; i < ln; i++) { - if (!lst[i]) - continue; - lst[n] = lst[i]; - n++; - } - lst[s] = NULL; - - return 1; -} - static struct bstr get_nextsep(struct bstr *ptr, char sep, bool modify) { struct bstr str = *ptr; @@ -1432,11 +1383,6 @@ static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, multi = false; } else if (bstr_endswith0(name, "-pre")) { op = OP_PRE; - } else if (bstr_endswith0(name, "-del")) { - op = OP_DEL; - mp_warn(log, "Option %.*s: -del is deprecated! " - "Use -remove (removes by content instead of by index).\n", - BSTR_P(name)); } else if (bstr_endswith0(name, "-clr")) { op = OP_CLR; } else if (bstr_endswith0(name, "-set")) { @@ -1450,14 +1396,20 @@ static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, if (op == OP_TOGGLE || op == OP_REMOVE) { if (dst) { char **list = VAL(dst); - int index = find_list_bstr(list, param); - if (index >= 0) { - char *old = list[index]; - for (int n = index; list[n]; n++) - list[n] = list[n + 1]; - talloc_free(old); + bool found = false; + int index = 0; + do { + index = find_list_bstr(list, param); + if (index >= 0) { + found = true; + char *old = list[index]; + for (int n = index; list[n]; n++) + list[n] = list[n + 1]; + talloc_free(old); + } + } while (index >= 0); + if (found) return 1; - } } if (op == OP_REMOVE) return 1; // ignore if not found @@ -1518,8 +1470,6 @@ static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, return str_list_add(res, n, dst, 0); case OP_PRE: return str_list_add(res, n, dst, 1); - case OP_DEL: - return str_list_del(log, res, n, dst); } if (VAL(dst)) @@ -1562,6 +1512,7 @@ static char *print_str_list(const m_option_t *opt, const void *src) { char **lst = NULL; char *ret = NULL; + const char sep = opt->priv ? *(char *)opt->priv : OPTION_LIST_SEPARATOR; if (!(src && VAL(src))) return talloc_strdup(NULL, ""); @@ -1569,7 +1520,7 @@ static char *print_str_list(const m_option_t *opt, const void *src) for (int i = 0; lst[i]; i++) { if (ret) - ret = talloc_strdup_append_buffer(ret, ","); + ret = talloc_strndup_append_buffer(ret, &sep, 1); ret = talloc_strdup_append_buffer(ret, lst[i]); } return ret; @@ -1649,7 +1600,6 @@ const m_option_type_t m_option_type_string_list = { {"add"}, {"append"}, {"clr", M_OPT_TYPE_OPTIONAL_PARAM}, - {"del"}, {"pre"}, {"set"}, {"toggle"}, @@ -1755,7 +1705,7 @@ static int parse_keyvalue_list(struct mp_log *log, const m_option_t *opt, } if (param.len) { - mp_err(log, "Unparseable garbage at end of option value: '%.*s'\n", + mp_err(log, "Unparsable garbage at end of option value: '%.*s'\n", BSTR_P(param)); r = M_OPT_INVALID; } @@ -1947,7 +1897,7 @@ const m_option_type_t m_option_type_dummy_flag = { // Read s sub-option name, or a positional sub-opt value. // termset is a string containing the set of chars that terminate an option. -// Return 0 on succes, M_OPT_ error code otherwise. +// Return 0 on success, M_OPT_ error code otherwise. // optname is for error reporting. static int read_subparam(struct mp_log *log, bstr optname, char *termset, bstr *str, bstr *out_subparam) @@ -2273,7 +2223,7 @@ void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh, *widw = *widh * asp; } // Center window after resize. If valid x:y values are passed to - // geometry, then those values will be overriden. + // geometry, then those values will be overridden. *xpos += prew / 2 - *widw / 2; *ypos += preh / 2 - *widh / 2; } @@ -2380,6 +2330,64 @@ const m_option_type_t m_option_type_size_box = { .equal = geometry_equal, }; +void m_rect_apply(struct mp_rect *rc, int w, int h, struct m_geometry *gm) +{ + *rc = (struct mp_rect){0, 0, w, h}; + if (!w || !h) + return; + m_geometry_apply(&rc->x0, &rc->y0, &rc->x1, &rc->y1, w, h, gm); + if (!gm->xy_valid && gm->wh_valid && rc->x1 == 0 && rc->y1 == 0) + return; + if (!gm->wh_valid || rc->x1 == 0 || rc->x1 == INT_MIN) + rc->x1 = w - rc->x0; + if (!gm->wh_valid || rc->y1 == 0 || rc->y1 == INT_MIN) + rc->y1 = h - rc->y0; + if (gm->wh_valid && (gm->w || gm->h)) + rc->x1 += rc->x0; + if (gm->wh_valid && (gm->w || gm->h)) + rc->y1 += rc->y0; +} + +static int parse_rect(struct mp_log *log, const m_option_t *opt, + struct bstr name, struct bstr param, void *dst) +{ + bool is_help = bstr_equals0(param, "help"); + if (is_help) + goto exit; + + struct m_geometry gm; + if (!parse_geometry_str(&gm, param)) + goto exit; + + bool invalid = gm.x_sign || gm.y_sign || gm.ws; + invalid |= gm.wh_valid && (gm.w < 0 || gm.h < 0); + invalid |= gm.wh_valid && !gm.xy_valid && gm.w <= 0 && gm.h <= 0; + + if (invalid) + goto exit; + + if (dst) + *((struct m_geometry *)dst) = gm; + + return 1; + +exit: + if (!is_help) { + mp_err(log, "Option %.*s: invalid rect: '%.*s'\n", + BSTR_P(name), BSTR_P(param)); + } + mp_info(log, "Valid format: W[%%][xH[%%]][+x+y]\n"); + return is_help ? M_OPT_EXIT : M_OPT_INVALID; +} + +const m_option_type_t m_option_type_rect = { + .name = "Video rect", + .size = sizeof(struct m_geometry), + .parse = parse_rect, + .print = print_geometry, + .copy = copy_opt, + .equal = geometry_equal, +}; #include "video/img_format.h" @@ -2647,21 +2655,33 @@ const m_option_type_t m_option_type_channels = { static int parse_timestring(struct bstr str, double *time, char endchar) { - int a, b, len; - double d; + int h, m, len; + double s; *time = 0; /* ensure initialization for error cases */ - if (bstr_sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3) - *time = 3600 * a + 60 * b + d; - else if (bstr_sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2) - *time = 60 * a + d; - else if (bstr_sscanf(str, "%lf%n", &d, &len) >= 1) - *time = d; - else + bool neg = bstr_eatstart0(&str, "-"); + if (!neg) + bstr_eatstart0(&str, "+"); + if (bstrchr(str, '-') >= 0 || bstrchr(str, '+') >= 0) + return 0; /* the timestamp shouldn't contain anymore +/- after this point */ + if (bstr_sscanf(str, "%d:%d:%lf%n", &h, &m, &s, &len) >= 3) { + if (m >= 60 || s >= 60) + return 0; /* minutes or seconds are out of range */ + *time = 3600 * h + 60 * m + s; + } else if (bstr_sscanf(str, "%d:%lf%n", &m, &s, &len) >= 2) { + if (s >= 60) + return 0; /* seconds are out of range */ + *time = 60 * m + s; + } else if (bstr_sscanf(str, "%lf%n", &s, &len) >= 1) { + *time = s; + } else { return 0; /* unsupported time format */ + } if (len < str.len && str.start[len] != endchar) return 0; /* invalid extra characters at the end */ if (!isfinite(*time)) return 0; + if (neg) + *time = -*time; return len; } @@ -2802,8 +2822,7 @@ static char *print_rel_time(const m_option_t *opt, const void *val) case REL_TIME_ABSOLUTE: return talloc_asprintf(NULL, "%g", t->pos); case REL_TIME_RELATIVE: - return talloc_asprintf(NULL, "%s%g", - (t->pos >= 0) ? "+" : "-", fabs(t->pos)); + return talloc_asprintf(NULL, "%+g", t->pos); case REL_TIME_CHAPTER: return talloc_asprintf(NULL, "#%g", t->pos); case REL_TIME_PERCENT: @@ -3147,7 +3166,7 @@ print_help: ; } else if (list->print_unknown_entry_help) { list->print_unknown_entry_help(log, mp_tprintf(80, "%.*s", BSTR_P(name))); } else { - mp_warn(log, "Option %.*s: item %.*s doesn't exist.\n", + mp_warn(log, "Option %.*s: item '%.*s' isn't supported.\n", BSTR_P(opt_name), BSTR_P(name)); } r = M_OPT_EXIT; @@ -3202,7 +3221,7 @@ static int parse_obj_settings(struct mp_log *log, struct bstr opt, int op, int idx = bstrspn(*pstr, NAMECH); str = bstr_splice(*pstr, 0, idx); if (!str.len) { - mp_err(log, "Option %.*s: filter name expected.\n", BSTR_P(opt)); + mp_err(log, "Option %.*s: item name expected.\n", BSTR_P(opt)); return M_OPT_INVALID; } *pstr = bstr_cut(*pstr, idx); @@ -3219,7 +3238,7 @@ static int parse_obj_settings(struct mp_log *log, struct bstr opt, int op, char name[80]; snprintf(name, sizeof(name), "%.*s", BSTR_P(str)); if (list->check_unknown_entry && !list->check_unknown_entry(name)) { - mp_err(log, "Option %.*s: %.*s doesn't exist.\n", + mp_err(log, "Option %.*s: '%.*s' isn't supported.\n", BSTR_P(opt), BSTR_P(str)); return M_OPT_INVALID; } @@ -3252,11 +3271,10 @@ done: ; return 1; } -// Parse a single entry for -vf-del (return 0 if not applicable) +// Parse a single entry for -vf-remove (return 0 if not applicable) // mark_del is bounded by the number of items in dst static int parse_obj_settings_del(struct mp_log *log, struct bstr opt_name, - struct bstr *param, int op, - void *dst, bool *mark_del) + struct bstr *param, void *dst, bool *mark_del) { bstr s = *param; if (bstr_eatstart0(&s, "@")) { @@ -3268,7 +3286,8 @@ static int parse_obj_settings_del(struct mp_log *log, struct bstr opt_name, if (bstr_startswith0(s, ":")) |