mirror of
https://github.com/mpv-player/mpv
synced 2025-04-16 20:31:06 +00:00
Now it depends on the command whether a property wraps around, or stops at min/max valid property value. For practically all properties, it's quite unambiguous what the "switch" command should have done, and there's technically no need to replace it with these new commands. More over, most properties that cycle are boolean anyway. But it seems more orthogonal to make the difference explicit, rather than hardcoding it. Having different commands also makes it more explicit to the user what these commands do, both just due to the naming, and what wrapping policy is used. The code is simpler too.
296 lines
9.1 KiB
C
296 lines
9.1 KiB
C
/*
|
|
* This file is part of MPlayer.
|
|
*
|
|
* MPlayer is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* MPlayer is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/// \file
|
|
/// \ingroup Properties
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <assert.h>
|
|
|
|
#include "talloc.h"
|
|
#include "m_option.h"
|
|
#include "m_property.h"
|
|
#include "mp_msg.h"
|
|
#include "mpcommon.h"
|
|
|
|
static int do_action(const m_option_t *prop_list, const char *name,
|
|
int action, void *arg, void *ctx)
|
|
{
|
|
const char *sep;
|
|
const m_option_t *prop;
|
|
if ((sep = strchr(name, '/')) && sep[1]) {
|
|
int len = sep - name;
|
|
char base[len + 1];
|
|
memcpy(base, name, len);
|
|
base[len] = 0;
|
|
prop = m_option_list_find(prop_list, base);
|
|
struct m_property_action_arg ka = {
|
|
.key = sep + 1,
|
|
.action = action,
|
|
.arg = arg,
|
|
};
|
|
action = M_PROPERTY_KEY_ACTION;
|
|
arg = &ka;
|
|
} else
|
|
prop = m_option_list_find(prop_list, name);
|
|
if (!prop)
|
|
return M_PROPERTY_UNKNOWN;
|
|
int (*control)(const m_option_t*, int, void*, void*) = prop->p;
|
|
int r = control(prop, action, arg, ctx);
|
|
if (action == M_PROPERTY_GET_TYPE && r < 0) {
|
|
*(struct m_option *)arg = *prop;
|
|
return M_PROPERTY_OK;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int m_property_do(const m_option_t *prop_list, const char *name,
|
|
int action, void *arg, void *ctx)
|
|
{
|
|
union m_option_value val = {0};
|
|
int r;
|
|
|
|
struct m_option opt = {0};
|
|
r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx);
|
|
if (r <= 0)
|
|
return r;
|
|
assert(opt.type);
|
|
|
|
switch (action) {
|
|
case M_PROPERTY_PRINT: {
|
|
if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, 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);
|
|
m_option_free(&opt, &val);
|
|
*(char **)arg = str;
|
|
return str != NULL;
|
|
}
|
|
case M_PROPERTY_TO_STRING: {
|
|
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
|
return r;
|
|
char *str = m_option_print(&opt, &val);
|
|
m_option_free(&opt, &val);
|
|
*(char **)arg = str;
|
|
return str != NULL;
|
|
}
|
|
case M_PROPERTY_PARSE: {
|
|
// (reject 0 return value: success, but empty string with flag)
|
|
if (m_option_parse(&opt, bstr0(opt->name), bstr0(arg), &val) <= 0)
|
|
return M_PROPERTY_ERROR;
|
|
r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
|
|
m_option_free(&opt, &val);
|
|
return r;
|
|
}
|
|
case M_PROPERTY_SWITCH: {
|
|
struct m_property_switch_arg *sarg = arg;
|
|
if ((r = do_action(prop_list, name, M_PROPERTY_SWITCH, arg, ctx)) !=
|
|
M_PROPERTY_NOT_IMPLEMENTED)
|
|
return r;
|
|
// Fallback to m_option
|
|
if (!opt.type->add)
|
|
return M_PROPERTY_NOT_IMPLEMENTED;
|
|
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
|
return r;
|
|
opt.type->add(&opt, &val, sarg->inc, sarg->wrap);
|
|
r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
|
|
m_option_free(&opt, &val);
|
|
return r;
|
|
}
|
|
case M_PROPERTY_SET: {
|
|
if (!opt.type->clamp) {
|
|
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Property '%s' without clamp().\n",
|
|
name);
|
|
} else {
|
|
m_option_copy(&opt, &val, arg);
|
|
r = opt.type->clamp(&opt, arg);
|
|
m_option_free(&opt, &val);
|
|
if (r != 0) {
|
|
mp_msg(MSGT_CPLAYER, MSGL_ERR,
|
|
"Property '%s': invalid value.\n", name);
|
|
return M_PROPERTY_ERROR;
|
|
}
|
|
}
|
|
return do_action(prop_list, name, M_PROPERTY_SET, arg, ctx);
|
|
}
|
|
default:
|
|
return do_action(prop_list, name, action, arg, ctx);
|
|
}
|
|
}
|
|
|
|
char *m_properties_expand_string(const m_option_t *prop_list, char *str,
|
|
void *ctx)
|
|
{
|
|
int l, fr = 0, pos = 0, size = strlen(str) + 512;
|
|
char *p = NULL, *e, *ret = malloc(size), num_val;
|
|
int skip = 0, lvl = 0, skip_lvl = 0;
|
|
|
|
while (str[0]) {
|
|
if (str[0] == '\\') {
|
|
int sl = 1;
|
|
switch (str[1]) {
|
|
case 'e':
|
|
p = "\x1b", l = 1; break;
|
|
case 'n':
|
|
p = "\n", l = 1; break;
|
|
case 'r':
|
|
p = "\r", l = 1; break;
|
|
case 't':
|
|
p = "\t", l = 1; break;
|
|
case 'x':
|
|
if (str[2]) {
|
|
char num[3] = { str[2], str[3], 0 };
|
|
char *end = num;
|
|
num_val = strtol(num, &end, 16);
|
|
sl = end - num + 1;
|
|
l = 1;
|
|
p = &num_val;
|
|
} else
|
|
l = 0;
|
|
break;
|
|
default:
|
|
p = str + 1, l = 1;
|
|
}
|
|
str += 1 + sl;
|
|
} else if (lvl > 0 && str[0] == ')') {
|
|
if (skip && lvl <= skip_lvl)
|
|
skip = 0;
|
|
lvl--, str++, l = 0;
|
|
} else if (str[0] == '$' && str[1] == '{'
|
|
&& (e = strchr(str + 2, '}'))) {
|
|
str += 2;
|
|
int method = M_PROPERTY_PRINT;
|
|
if (str[0] == '=') {
|
|
str += 1;
|
|
method = M_PROPERTY_TO_STRING;
|
|
}
|
|
int pl = e - str;
|
|
char pname[pl + 1];
|
|
memcpy(pname, str, pl);
|
|
pname[pl] = 0;
|
|
if (m_property_do(prop_list, pname, method, &p, ctx) >= 0 && p)
|
|
l = strlen(p), fr = 1;
|
|
else
|
|
l = 0;
|
|
str = e + 1;
|
|
} else if (str[0] == '?' && str[1] == '('
|
|
&& (e = strchr(str + 2, ':'))) {
|
|
lvl++;
|
|
if (!skip) {
|
|
int is_not = str[2] == '!';
|
|
int pl = e - str - (is_not ? 3 : 2);
|
|
char pname[pl + 1];
|
|
memcpy(pname, str + (is_not ? 3 : 2), pl);
|
|
pname[pl] = 0;
|
|
struct m_option opt = {0};
|
|
union m_option_value val = {0};
|
|
if (m_property_do(prop_list, pname, M_PROPERTY_GET_TYPE, &opt, ctx) <= 0 &&
|
|
m_property_do(prop_list, pname, M_PROPERTY_GET, &val, ctx) <= 0)
|
|
{
|
|
if (!is_not)
|
|
skip = 1, skip_lvl = lvl;
|
|
m_option_free(&opt, &val);
|
|
} else if (is_not)
|
|
skip = 1, skip_lvl = lvl;
|
|
}
|
|
str = e + 1, l = 0;
|
|
} else
|
|
p = str, l = 1, str++;
|
|
|
|
if (skip || l <= 0)
|
|
continue;
|
|
|
|
if (pos + l + 1 > size) {
|
|
size = pos + l + 512;
|
|
ret = realloc(ret, size);
|
|
}
|
|
memcpy(ret + pos, p, l);
|
|
pos += l;
|
|
if (fr)
|
|
talloc_free(p), fr = 0;
|
|
}
|
|
|
|
ret[pos] = 0;
|
|
return ret;
|
|
}
|
|
|
|
void m_properties_print_help_list(const m_option_t *list)
|
|
{
|
|
char min[50], max[50];
|
|
int i, count = 0;
|
|
|
|
mp_tmsg(MSGT_CFGPARSER, MSGL_INFO,
|
|
"\n Name Type Min Max\n\n");
|
|
for (i = 0; list[i].name; i++) {
|
|
const m_option_t *opt = &list[i];
|
|
if (opt->flags & M_OPT_MIN)
|
|
sprintf(min, "%-8.0f", opt->min);
|
|
else
|
|
strcpy(min, "No");
|
|
if (opt->flags & M_OPT_MAX)
|
|
sprintf(max, "%-8.0f", opt->max);
|
|
else
|
|
strcpy(max, "No");
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO,
|
|
" %-20.20s %-15.15s %-10.10s %-10.10s\n",
|
|
opt->name,
|
|
opt->type->name,
|
|
min,
|
|
max);
|
|
count++;
|
|
}
|
|
mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d properties\n", count);
|
|
}
|
|
|
|
int m_property_int_ro(const m_option_t *prop, int action,
|
|
void *arg, int var)
|
|
{
|
|
if (action == M_PROPERTY_GET) {
|
|
*(int *)arg = var;
|
|
return M_PROPERTY_OK;
|
|
}
|
|
return M_PROPERTY_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
int m_property_float_ro(const m_option_t *prop, int action,
|
|
void *arg, float var)
|
|
{
|
|
if (action == M_PROPERTY_GET) {
|
|
*(float *)arg = var;
|
|
return M_PROPERTY_OK;
|
|
}
|
|
return M_PROPERTY_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
int m_property_double_ro(const m_option_t *prop, int action,
|
|
void *arg, double var)
|
|
{
|
|
if (action == M_PROPERTY_GET) {
|
|
*(double *)arg = var;
|
|
return M_PROPERTY_OK;
|
|
}
|
|
return M_PROPERTY_NOT_IMPLEMENTED;
|
|
}
|