m_property: add `>` for fixed precision floating-point expansion

This enhancement makes it easier to create constant width property
expansions, useful for the `--term-status-msg`. Additionally, it changes
to `%f` printing with manual zero trimming, which is easier to control
than `%g`. With this method, we can directly specify precision, not just
significant numbers. This approach also avoids overly high precision for
values less than 1, which is not necessary for a generic floating-point
print function.

A new print helper function is added, which can be used with adjusted
precision for specific cases where a different default is needed. This
also unifies the code slightly.
This commit is contained in:
Kacper Michajłow 2023-11-19 21:22:24 +01:00
parent c84bb1ce67
commit 8708f4dc91
12 changed files with 176 additions and 34 deletions

View File

@ -0,0 +1 @@
add `>` for fixed precision floating-point property expansion

View File

@ -3766,7 +3766,9 @@ Normally, properties are formatted as human-readable text, meant to be
displayed on OSD or on the terminal. It is possible to retrieve an unformatted
(raw) value from a property by prefixing its name with ``=``. These raw values
can be parsed by other programs and follow the same conventions as the options
associated with the properties.
associated with the properties. Additionally, there is a ``>`` prefix to format
human-readable text, with fixed precision for floating-point values. This is
useful for printing values where a constant width is important.
.. admonition:: Examples
@ -3775,6 +3777,10 @@ associated with the properties.
- ``${=time-pos}`` expands to ``863.4`` (same time, plus 400 milliseconds -
milliseconds are normally not shown in the formatted case)
- ``${avsync}`` expands to ``+0.003``
- ``${>avsync}`` expands to ``+0.0030``
- ``${=avsync}`` expands to ``0.003028``
Sometimes, the difference in amount of information carried by raw and formatted
property values can be rather big. In some cases, raw values have more
information, like higher precision than seconds with ``time-pos``. Sometimes

View File

@ -94,6 +94,25 @@ char *mp_format_time(double time, bool fractions)
return mp_format_time_fmt(fractions ? "%H:%M:%S.%T" : "%H:%M:%S", time);
}
char *mp_format_double(void *talloc_ctx, double val, int precision,
bool plus_sign, bool percent_sign, bool trim)
{
bstr str = {0};
const char *fmt = plus_sign ? "%+.*f" : "%.*f";
bstr_xappend_asprintf(talloc_ctx, &str, fmt, precision, val);
size_t pos = str.len;
if (trim) {
while (--pos && str.start[pos] == '0')
str.len--;
if (str.start[pos] == '.')
str.len--;
}
if (percent_sign)
bstr_xappend(talloc_ctx, &str, bstr0("%"));
str.start[str.len] = '\0';
return str.start;
}
// Set rc to the union of rc and rc2
void mp_rect_union(struct mp_rect *rc, const struct mp_rect *rc2)
{

View File

@ -99,6 +99,12 @@ extern const char mpv_copyright[];
char *mp_format_time(double time, bool fractions);
char *mp_format_time_fmt(const char *fmt, double time);
// Formats a double value to a string with the specified precision.
// Trailing zeros (and the dot) can be trimmed.
// Optionally, a plus sign and a percent sign can be added.
char *mp_format_double(void *talloc_ctx, double val, int precision,
bool plus_sign, bool percent_sign, bool trim);
struct mp_rect {
int x0, y0;
int x1, y1;

View File

@ -871,7 +871,7 @@ void m_config_print_option_list(const struct m_config *config, const char *name)
if (!defptr)
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);

View File

@ -1023,17 +1023,12 @@ static char *print_double(const m_option_t *opt, const void *val)
return talloc_asprintf(NULL, "%f", f);
}
static char *print_double_7g(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);
// Truncate anything < 1e-4 to avoid switching to scientific notation
if (fabs(f) < 1e-4) {
return talloc_strdup(NULL, "0");
} else {
return talloc_asprintf(NULL, "%.7g", 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)
@ -1105,7 +1100,7 @@ const m_option_type_t m_option_type_double = {
.size = sizeof(double),
.parse = parse_double,
.print = print_double,
.pretty_print = print_double_7g,
.pretty_print = pretty_print_double,
.copy = copy_opt,
.add = add_double,
.multiply = multiply_double,
@ -1131,7 +1126,7 @@ const m_option_type_t m_option_type_aspect = {
.flags = M_OPT_TYPE_CHOICE | M_OPT_TYPE_USES_RANGE,
.parse = parse_double_aspect,
.print = print_double,
.pretty_print = print_double_7g,
.pretty_print = pretty_print_double,
.copy = copy_opt,
.add = add_double,
.multiply = multiply_double,
@ -1159,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_7g(opt, &tmp);
return pretty_print_double(opt, &tmp);
}
static void add_float(const m_option_t *opt, void *val, double add, bool wrap)
@ -1207,7 +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,
.pretty_print = pretty_print_float,
.copy = copy_opt,
.add = add_float,
.multiply = multiply_float,
@ -2827,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:

View File

@ -463,6 +463,9 @@ char *format_file_size(int64_t size);
// type channels: disallow "auto" (still accept ""), limit list to at most 1 item.
#define M_OPT_CHANNELS_LIMITED (1 << 27)
// type_float/type_double: controls if pretty print should trim trailing zeros
#define M_OPT_FIXED_LEN_PRINT (1 << 28)
// Like M_OPT_TYPE_OPTIONAL_PARAM.
#define M_OPT_OPTIONAL_PARAM (1 << 30)
@ -536,12 +539,16 @@ static inline char *m_option_print(const m_option_t *opt, const void *val_ptr)
}
static inline char *m_option_pretty_print(const m_option_t *opt,
const void *val_ptr)
const void *val_ptr,
bool fixed_len)
{
m_option_t o = *opt;
if (fixed_len)
o.flags |= M_OPT_FIXED_LEN_PRINT;
if (opt->type->pretty_print)
return opt->type->pretty_print(opt, val_ptr);
return opt->type->pretty_print(&o, val_ptr);
else
return m_option_print(opt, val_ptr);
return m_option_print(&o, val_ptr);
}
// Helper around \ref m_option_type::copy.

View File

@ -108,13 +108,14 @@ int m_property_do(struct mp_log *log, const struct m_property *prop_list,
assert(opt.type);
switch (action) {
case M_PROPERTY_FIXED_LEN_PRINT:
case M_PROPERTY_PRINT: {
if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, arg, ctx)) >= 0)
if ((r = do_action(prop_list, name, action, arg, ctx)) >= 0)
return r;
// Fallback to m_option
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
return r;
char *str = m_option_pretty_print(&opt, &val);
char *str = m_option_pretty_print(&opt, &val, action == M_PROPERTY_FIXED_LEN_PRINT);
m_option_free(&opt, &val);
*(char **)arg = str;
return str != NULL;
@ -258,11 +259,13 @@ static int expand_property(const struct m_property *prop_list, char **ret,
bool cond_no = !cond_yes && bstr_eatstart0(&prop, "!");
bool test = cond_yes || cond_no;
bool raw = bstr_eatstart0(&prop, "=");
bool fixed_len = !raw && bstr_eatstart0(&prop, ">");
bstr comp_with = {0};
bool comp = test && bstr_split_tok(prop, "==", &prop, &comp_with);
if (test && !comp)
raw = true;
int method = raw ? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
method = fixed_len ? M_PROPERTY_FIXED_LEN_PRINT : method;
char *s = NULL;
int r = m_property_do_bstr(prop_list, prop, method, &s, ctx);

View File

@ -48,6 +48,12 @@ enum mp_property_action {
// arg: char**
M_PROPERTY_PRINT,
// Get human readable fixed length string representing the current value.
// If unimplemented, the property wrapper uses the property type as
// fallback.
// arg: char**
M_PROPERTY_FIXED_LEN_PRINT,
// Like M_PROPERTY_GET_TYPE, but get a type that is compatible to the real
// type, but reflect practical limits, such as runtime-available values.
// This is mostly used for "UI" related things.

View File

@ -414,9 +414,9 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
if (action == M_PROPERTY_PRINT) {
double speed = mpctx->opts->playback_speed;
*(char **)arg = talloc_asprintf(NULL, "%.2f", speed);
if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
*(char **)arg = mp_format_double(NULL, mpctx->opts->playback_speed, 2,
false, false, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@ -434,8 +434,9 @@ static int mp_property_av_speed_correction(void *ctx, struct m_property *prop,
default: MP_ASSERT_UNREACHABLE();
}
if (action == M_PROPERTY_PRINT) {
*(char **)arg = talloc_asprintf(NULL, "%+.3g%%", (val - 1) * 100);
if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
*(char **)arg = mp_format_double(NULL, (val - 1) * 100, 2, true,
true, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
@ -667,13 +668,9 @@ static int mp_property_avsync(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (!mpctx->ao_chain || !mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_PRINT) {
// Truncate anything < 1e-4 to avoid switching to scientific notation
if (fabs(mpctx->last_av_difference) < 1e-4) {
*(char **)arg = talloc_strdup(NULL, "0");
} else {
*(char **)arg = talloc_asprintf(NULL, "%+.2g", mpctx->last_av_difference);
}
if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) {
*(char **)arg = mp_format_double(NULL, mpctx->last_av_difference, 4,
true, false, action != M_PROPERTY_FIXED_LEN_PRINT);
return M_PROPERTY_OK;
}
return m_property_double_ro(action, arg, mpctx->last_av_difference);
@ -3754,8 +3751,9 @@ static int do_op_udata(struct udata_ctx* ctx, int action, void *arg)
assert(node);
m_option_copy(&udata_type, arg, node);
return M_PROPERTY_OK;
case M_PROPERTY_FIXED_LEN_PRINT:
case M_PROPERTY_PRINT: {
char *str = m_option_pretty_print(&udata_type, node);
char *str = m_option_pretty_print(&udata_type, node, action == M_PROPERTY_FIXED_LEN_PRINT);
*(char **)arg = str;
return str != NULL;
}

99
test/format.c Normal file
View File

@ -0,0 +1,99 @@
#include "test_utils.h"
#include "common/common.h"
int main(void)
{
void *ta_ctx = talloc_new(NULL);
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, false, false, false), "123");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, false, false, true), "123");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, false, true, false), "123%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, false, true, true), "123%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, true, false, false), "+123");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, true, false, true), "+123");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, true, true, false), "+123%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 0, true, true, true), "+123%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, false, false, false), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, false, false, true), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, false, true, false), "-123%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, false, true, true), "-123%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, true, false, false), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, true, false, true), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, true, true, false), "-123%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 0, true, true, true), "-123%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, false, false, false), "123.46");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, false, false, true), "123.46");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, false, true, false), "123.46%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, false, true, true), "123.46%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, true, false, false), "+123.46");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, true, false, true), "+123.46");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, true, true, false), "+123.46%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 2, true, true, true), "+123.46%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, false, false, false), "-123.46");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, false, false, true), "-123.46");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, false, true, false), "-123.46%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, false, true, true), "-123.46%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, true, false, false), "-123.46");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, true, false, true), "-123.46");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, true, true, false), "-123.46%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 2, true, true, true), "-123.46%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, false, false, false), "123.456000");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, false, false, true), "123.456");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, false, true, false), "123.456000%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, false, true, true), "123.456%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, true, false, false), "+123.456000");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, true, false, true), "+123.456");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, true, true, false), "+123.456000%");
assert_string_equal(mp_format_double(ta_ctx, 123.456, 6, true, true, true), "+123.456%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, false, false, false), "-123.456000");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, false, false, true), "-123.456");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, false, true, false), "-123.456000%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, false, true, true), "-123.456%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, true, false, false), "-123.456000");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, true, false, true), "-123.456");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, true, true, false), "-123.456000%");
assert_string_equal(mp_format_double(ta_ctx, -123.456, 6, true, true, true), "-123.456%");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, false, false, false), "123.000000");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, false, false, true), "123");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, false, true, false), "123.000000%");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, false, true, true), "123%");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, true, false, false), "+123.000000");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, true, false, true), "+123");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, true, true, false), "+123.000000%");
assert_string_equal(mp_format_double(ta_ctx, 123, 6, true, true, true), "+123%");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, false, false, false), "-123.000000");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, false, false, true), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, false, true, false), "-123.000000%");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, false, true, true), "-123%");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, true, false, false), "-123.000000");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, true, false, true), "-123");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, true, true, false), "-123.000000%");
assert_string_equal(mp_format_double(ta_ctx, -123, 6, true, true, true), "-123%");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, false, false, false), "inf");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, false, false, true), "inf");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, false, true, false), "inf%");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, false, true, true), "inf%");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, true, false, false), "+inf");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, true, false, true), "+inf");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, true, true, false), "+inf%");
assert_string_equal(mp_format_double(ta_ctx, INFINITY, 6, true, true, true), "+inf%");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, false, false, false), "-inf");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, false, false, true), "-inf");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, false, true, false), "-inf%");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, false, true, true), "-inf%");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, true, false, false), "-inf");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, true, false, true), "-inf");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, true, true, false), "-inf%");
assert_string_equal(mp_format_double(ta_ctx, -INFINITY, 6, true, true, true), "-inf%");
talloc_free(ta_ctx);
}

View File

@ -106,6 +106,9 @@ test('linked-list', linked_list)
timer = executable('timer', files('timer.c'), include_directories: incdir, link_with: test_utils)
test('timer', timer)
format = executable('format', files('format.c'), include_directories: incdir, link_with: test_utils)
test('format', format)
paths_objects = libmpv.extract_objects('options/path.c', path_source)
paths = executable('paths', 'paths.c', include_directories: incdir,
objects: paths_objects, link_with: test_utils)