mpv/m_property.c

297 lines
9.2 KiB
C
Raw Normal View History

/*
* 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 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) {
*(const m_option_t **)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;
const m_option_t *opt = NULL;
r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx);
if (r <= 0)
return r;
assert(opt);
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: {
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;
bool wrap = opt->type == &m_option_type_choice ||
opt->type == &m_option_type_flag;
opt->type->add(opt, &val, *(double*)arg, 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;
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;
}