summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-11-28 16:52:46 +0100
committerwm4 <wm4@nowhere>2019-11-29 12:14:43 +0100
commit63270ff898785814dd40a648362d540be8108464 (patch)
tree59f9689897549bd8370c35faf9f0f859b9536565
parent82735d1287a1ff6d057f42c475d25bc210d75cfe (diff)
downloadmpv-63270ff898785814dd40a648362d540be8108464.tar.bz2
mpv-63270ff898785814dd40a648362d540be8108464.tar.xz
m_option: add option comparison
Looks like this will be needed for fine-grained option change notifications. There are some other parts in the player which implement parts of this.
-rw-r--r--options/m_option.c137
-rw-r--r--options/m_option.h20
2 files changed, 156 insertions, 1 deletions
diff --git a/options/m_option.c b/options/m_option.c
index d0f5e7a418..1f6d8e800b 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -40,6 +40,7 @@
#include "common/msg.h"
#include "common/msg_control.h"
#include "misc/json.h"
+#include "misc/node.h"
#include "m_option.h"
#include "m_config.h"
@@ -169,6 +170,11 @@ static int flag_get(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool flag_equal(const m_option_t *opt, void *a, void *b)
+{
+ return VAL(a) == VAL(b);
+}
+
const m_option_type_t m_option_type_flag = {
// need yes or no in config files
.name = "Flag",
@@ -180,6 +186,7 @@ const m_option_type_t m_option_type_flag = {
.add = add_flag,
.set = flag_set,
.get = flag_get,
+ .equal = flag_equal,
};
// Integer
@@ -359,6 +366,16 @@ static int int_get(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool int_equal(const m_option_t *opt, void *a, void *b)
+{
+ return *(int *)a == *(int *)b;
+}
+
+static bool int64_equal(const m_option_t *opt, void *a, void *b)
+{
+ return *(int64_t *)a == *(int64_t *)b;
+}
+
const m_option_type_t m_option_type_int = {
.name = "Integer",
.size = sizeof(int),
@@ -369,6 +386,7 @@ const m_option_type_t m_option_type_int = {
.multiply = multiply_int,
.set = int_set,
.get = int_get,
+ .equal = int_equal,
};
const m_option_type_t m_option_type_int64 = {
@@ -381,6 +399,7 @@ const m_option_type_t m_option_type_int64 = {
.multiply = multiply_int64,
.set = int64_set,
.get = int64_get,
+ .equal = int64_equal,
};
static int parse_byte_size(struct mp_log *log, const m_option_t *opt,
@@ -478,6 +497,7 @@ const m_option_type_t m_option_type_byte_size = {
.multiply = multiply_int64,
.set = int64_set,
.get = int64_get,
+ .equal = int64_equal,
};
static int parse_intpair(struct mp_log *log, const struct m_option *opt,
@@ -746,6 +766,7 @@ const struct m_option_type m_option_type_choice = {
.add = add_choice,
.set = choice_set,
.get = choice_get,
+ .equal = int_equal,
};
static int apply_flag(const struct m_option *opt, int *val, bstr flag)
@@ -868,6 +889,7 @@ const struct m_option_type m_option_type_flags = {
.copy = copy_opt,
.set = flags_set,
.get = flags_get,
+ .equal = int_equal,
};
// Float
@@ -1002,6 +1024,14 @@ static int double_get(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool double_equal(const m_option_t *opt, void *a, void *b)
+{
+ double fa = VAL(a), fb = VAL(b);
+ if (isnan(fa) || isnan(fb))
+ return isnan(fa) == isnan(fb);
+ return fa == fb;
+}
+
const m_option_type_t m_option_type_double = {
// double precision float or ratio (numerator[:/]denominator)
.name = "Double",
@@ -1014,6 +1044,7 @@ const m_option_type_t m_option_type_double = {
.multiply = multiply_double,
.set = double_set,
.get = double_get,
+ .equal = double_equal,
};
#undef VAL
@@ -1071,6 +1102,11 @@ static int float_get(const m_option_t *opt, void *ta_parent,
return double_get(opt, ta_parent, dst, &tmp);
}
+static bool float_equal(const m_option_t *opt, void *a, void *b)
+{
+ return double_equal(opt, &(double){VAL(a)}, &(double){VAL(b)});
+}
+
const m_option_type_t m_option_type_float = {
// floating point number or ratio (numerator[:/]denominator)
.name = "Float",
@@ -1083,6 +1119,7 @@ const m_option_type_t m_option_type_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,
@@ -1108,6 +1145,7 @@ const m_option_type_t m_option_type_aspect = {
.multiply = multiply_float,
.set = float_set,
.get = float_get,
+ .equal = float_equal,
};
///////////// String
@@ -1188,6 +1226,11 @@ static int str_get(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool str_equal(const m_option_t *opt, void *a, void *b)
+{
+ return bstr_equals(bstr0(VAL(a)), bstr0(VAL(b)));
+}
+
static void free_str(void *src)
{
if (src && VAL(src)) {
@@ -1205,6 +1248,7 @@ const m_option_type_t m_option_type_string = {
.free = free_str,
.set = str_set,
.get = str_get,
+ .equal = str_equal,
};
//////////// String list
@@ -1530,6 +1574,26 @@ static int parse_str_list(struct mp_log *log, const m_option_t *opt,
return parse_str_list_impl(log, opt, name, param, dst, OP_NONE);
}
+static bool str_list_equal(const m_option_t *opt, void *a, void *b)
+{
+ char **la = VAL(a);
+ char **lb = VAL(b);
+
+ bool a_empty = !la || !la[0];
+ bool b_empty = !lb || !lb[0];
+ if (a_empty || b_empty)
+ return a_empty == b_empty;
+
+ for (int n = 0; la[n] || lb[n]; n++) {
+ if (!la[n] || !lb[n])
+ return false;
+ if (strcmp(la[n], lb[n]) != 0)
+ return false;
+ }
+
+ return true;
+}
+
const m_option_type_t m_option_type_string_list = {
.name = "String list",
.size = sizeof(char **),
@@ -1539,6 +1603,7 @@ const m_option_type_t m_option_type_string_list = {
.free = free_str_list,
.get = str_list_get,
.set = str_list_set,
+ .equal = str_list_equal,
.actions = (const struct m_option_action[]){
{"add"},
{"append"},
@@ -1688,6 +1753,7 @@ const m_option_type_t m_option_type_keyvalue_list = {
.free = free_str_list,
.get = keyvalue_list_get,
.set = keyvalue_list_set,
+ .equal = str_list_equal,
.actions = (const struct m_option_action[]){
{"add"},
{"append"},
@@ -1760,6 +1826,7 @@ const m_option_type_t m_option_type_msglevels = {
.free = free_str_list,
.get = keyvalue_list_get,
.set = set_msglevels,
+ .equal = str_list_equal,
};
static int parse_print(struct mp_log *log, const m_option_t *opt,
@@ -1972,12 +2039,20 @@ static char *print_color(const m_option_t *opt, const void *val)
return talloc_asprintf(NULL, "#%02X%02X%02X%02X", c->a, c->r, c->g, c->b);
}
+static bool color_equal(const m_option_t *opt, void *a, void *b)
+{
+ struct m_color *ca = a;
+ struct m_color *cb = b;
+ return ca->a == cb->a && ca->r == cb->r && ca->g == cb->g && ca->b == cb->b;
+}
+
const m_option_type_t m_option_type_color = {
.name = "Color",
.size = sizeof(struct m_color),
.parse = parse_color,
.print = print_color,
.copy = copy_opt,
+ .equal = color_equal,
};
@@ -2154,12 +2229,24 @@ exit:
return is_help ? M_OPT_EXIT : M_OPT_INVALID;
}
+static bool geometry_equal(const m_option_t *opt, void *a, void *b)
+{
+ struct m_geometry *ga = a;
+ struct m_geometry *gb = b;
+ return ga->x == gb->x && ga->y == gb->y && ga->w == gb->w && ga->h == gb->h &&
+ ga->xy_valid == gb->xy_valid && ga->wh_valid == gb->wh_valid &&
+ ga->w_per == gb->w_per && ga->h_per == gb->h_per &&
+ ga->x_per == gb->x_per && ga->y_per == gb->y_per &&
+ ga->x_sign == gb->x_sign && ga->y_sign == gb->y_sign;
+}
+
const m_option_type_t m_option_type_geometry = {
.name = "Window geometry",
.size = sizeof(struct m_geometry),
.parse = parse_geometry,
.print = print_geometry,
.copy = copy_opt,
+ .equal = geometry_equal,
};
static int parse_size_box(struct mp_log *log, const m_option_t *opt,
@@ -2196,6 +2283,7 @@ const m_option_type_t m_option_type_size_box = {
.parse = parse_size_box,
.print = print_geometry,
.copy = copy_opt,
+ .equal = geometry_equal,
};
@@ -2246,6 +2334,7 @@ const m_option_type_t m_option_type_imgfmt = {
.parse = parse_imgfmt,
.print = print_imgfmt,
.copy = copy_opt,
+ .equal = int_equal,
};
static int parse_fourcc(struct mp_log *log, const m_option_t *opt,
@@ -2287,6 +2376,7 @@ const m_option_type_t m_option_type_fourcc = {
.parse = parse_fourcc,
.print = print_fourcc,
.copy = copy_opt,
+ .equal = int_equal,
};
#include "audio/format.h"
@@ -2334,6 +2424,7 @@ const m_option_type_t m_option_type_afmt = {
.parse = parse_afmt,
.print = print_afmt,
.copy = copy_opt,
+ .equal = int_equal,
};
#include "audio/chmap.h"
@@ -2436,13 +2527,21 @@ static void copy_channels(const m_option_t *opt, void *dst, const void *src)
talloc_memdup(NULL, ch->chmaps, sizeof(ch->chmaps[0]) * ch->num_chmaps);
}
+static bool channels_equal(const m_option_t *opt, void *a, void *b)
+{
+ struct mp_chmap *ca = a;
+ struct mp_chmap *cb = b;
+ return mp_chmap_equals(ca, cb);
+}
+
const m_option_type_t m_option_type_channels = {
.name = "Audio channels or channel map",
.size = sizeof(struct m_channels),
.parse = parse_channels,
.print = print_channels,
.copy = copy_channels,
- .free = free_channels,
+ .free = free_channels,
+ .equal = channels_equal,
};
static int parse_timestring(struct bstr str, double *time, char endchar)
@@ -2535,6 +2634,7 @@ const m_option_type_t m_option_type_time = {
.add = add_double,
.set = time_set,
.get = time_get,
+ .equal = double_equal,
};
@@ -2611,12 +2711,20 @@ static char *print_rel_time(const m_option_t *opt, const void *val)
return talloc_strdup(NULL, "none");
}
+static bool rel_time_equal(const m_option_t *opt, void *a, void *b)
+{
+ struct m_rel_time *ta = a;
+ struct m_rel_time *tb = b;
+ return ta->type == tb->type && ta->pos == tb->pos;
+}
+
const m_option_type_t m_option_type_rel_time = {
.name = "Relative time or percent position",
.size = sizeof(struct m_rel_time),
.parse = parse_rel_time,
.print = print_rel_time,
.copy = copy_opt,
+ .equal = rel_time_equal,
};
@@ -3447,6 +3555,26 @@ static int get_obj_settings_list(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool obj_settings_list_equal(const m_option_t *opt, void *pa, void *pb)
+{
+ struct m_obj_settings *a = VAL(pa);
+ struct m_obj_settings *b = VAL(pb);
+
+ if (a == b || !a || !b)
+ return a == b;
+
+ if (!str_equal(NULL, &a->name, &b->name))
+ return false;
+
+ if (!str_equal(NULL, &a->label, &b->label))
+ return false;
+
+ if (a->enabled != b->enabled)
+ return false;
+
+ return str_list_equal(NULL, &a->attribs, &b->attribs);
+}
+
const m_option_type_t m_option_type_obj_settings_list = {
.name = "Object settings list",
.size = sizeof(m_obj_settings_t *),
@@ -3456,6 +3584,7 @@ const m_option_type_t m_option_type_obj_settings_list = {
.free = free_obj_settings_list,
.set = set_obj_settings_list,
.get = get_obj_settings_list,
+ .equal = obj_settings_list_equal,
.actions = (const struct m_option_action[]){
{"add"},
{"clr", M_OPT_TYPE_OPTIONAL_PARAM},
@@ -3587,6 +3716,11 @@ static int node_get(const m_option_t *opt, void *ta_parent,
return 1;
}
+static bool node_equal(const m_option_t *opt, void *a, void *b)
+{
+ return equal_mpv_node(&VAL(a), &VAL(b));
+}
+
const m_option_type_t m_option_type_node = {
.name = "Complex",
.size = sizeof(struct mpv_node),
@@ -3597,6 +3731,7 @@ const m_option_type_t m_option_type_node = {
.free = free_node,
.set = node_set,
.get = node_get,
+ .equal = node_equal,
};
// Special-cased by m_config.c.
diff --git a/options/m_option.h b/options/m_option.h
index a5f9618462..c9538cf628 100644
--- a/options/m_option.h
+++ b/options/m_option.h
@@ -338,6 +338,17 @@ struct m_option_type {
int (*get)(const m_option_t *opt, void *ta_parent, struct mpv_node *dst,
void *src);
+ // Return whether the values are the same. (There are no "unordered"
+ // results; for example, two floats with the value NaN compare equal. Other
+ // ambiguous floats, such as +0 and -0 compare equal. Some option types may
+ // incorrectly report unequal for values that are equal, such as sets (if
+ // the element order is different, which incorrectly matters), but values
+ // duplicated with m_option_copy() always return as equal. Empty strings
+ // and NULL strings are equal. Ambiguous unicode representations compare
+ // unequal.)
+ // If not set, values are always considered equal (=> not really optional).
+ bool (*equal)(const m_option_t *opt, void *a, void *b);
+
// Optional: list of suffixes, terminated with a {0} entry. An empty list
// behaves like the list being NULL.
const struct m_option_action *actions;
@@ -543,6 +554,15 @@ static inline int m_option_get_node(const m_option_t *opt, void *ta_parent,
return M_OPT_UNKNOWN;
}
+static inline bool m_option_equal(const m_option_t *opt, void *a, void *b)
+{
+ // Handle trivial equivalence.
+ // If not implemented, assume this type has no actual values => always equal.
+ if (a == b || !opt->type->equal)
+ return true;
+ return opt->type->equal(opt, a, b);
+}
+
int m_option_required_params(const m_option_t *opt);
extern const char m_option_path_separator;