mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
match: Support for both merging and replacing proplist updates.
This patch adds a new update mode specifier that can be optionally given in match rules after the regexp. Property list updates triggered by the rule will honour the given mode. The two allowed modes are 'merge' and 'replace', corresponding to PA_UPDATE_MERGE and PA_UPDATE_REPLACE respectively. If omitted, the mode defaults to PA_UPDATE_MERGE, ie. to the original behavior. For example, to force 'media.role' to be overwritten with 'bar' for streams matching foo you can use an entry like this: foo replace "bar" This will really overwrite media.role to bar even if it has already been set to something else by the application. Thanks to Krisztian Litkey for the original patch and the description above. In addition to implementing the new feature, this patch fixes a number of bugs in the parsing code.
This commit is contained in:
parent
495c1ed236
commit
2ee398fd94
1 changed files with 58 additions and 35 deletions
|
|
@ -60,6 +60,9 @@ PA_MODULE_USAGE("table=<filename> "
|
||||||
#define DEFAULT_MATCH_TABLE_FILE PA_DEFAULT_CONFIG_DIR"/match.table"
|
#define DEFAULT_MATCH_TABLE_FILE PA_DEFAULT_CONFIG_DIR"/match.table"
|
||||||
#define DEFAULT_MATCH_TABLE_FILE_USER "match.table"
|
#define DEFAULT_MATCH_TABLE_FILE_USER "match.table"
|
||||||
|
|
||||||
|
#define UPDATE_REPLACE "replace"
|
||||||
|
#define UPDATE_MERGE "merge"
|
||||||
|
|
||||||
static const char* const valid_modargs[] = {
|
static const char* const valid_modargs[] = {
|
||||||
"table",
|
"table",
|
||||||
"key",
|
"key",
|
||||||
|
|
@ -70,6 +73,7 @@ struct rule {
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
pa_volume_t volume;
|
pa_volume_t volume;
|
||||||
pa_proplist *proplist;
|
pa_proplist *proplist;
|
||||||
|
pa_update_mode_t mode;
|
||||||
struct rule *next;
|
struct rule *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -101,13 +105,14 @@ static int load_rules(struct userdata *u, const char *filename) {
|
||||||
pa_lock_fd(fileno(f), 1);
|
pa_lock_fd(fileno(f), 1);
|
||||||
|
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
char *d, *v;
|
char *token_end, *value_str;
|
||||||
pa_volume_t volume = PA_VOLUME_NORM;
|
pa_volume_t volume = PA_VOLUME_NORM;
|
||||||
uint32_t k;
|
uint32_t k;
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
char ln[256];
|
char ln[256];
|
||||||
struct rule *rule;
|
struct rule *rule;
|
||||||
pa_proplist *proplist = NULL;
|
pa_proplist *proplist = NULL;
|
||||||
|
pa_update_mode_t mode = (pa_update_mode_t) -1;
|
||||||
|
|
||||||
if (!fgets(ln, sizeof(ln), f))
|
if (!fgets(ln, sizeof(ln), f))
|
||||||
break;
|
break;
|
||||||
|
|
@ -119,51 +124,72 @@ static int load_rules(struct userdata *u, const char *filename) {
|
||||||
if (ln[0] == '#' || !*ln )
|
if (ln[0] == '#' || !*ln )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
d = ln+strcspn(ln, WHITESPACE);
|
token_end = ln + strcspn(ln, WHITESPACE);
|
||||||
v = d+strspn(d, WHITESPACE);
|
value_str = token_end + strspn(token_end, WHITESPACE);
|
||||||
|
*token_end = 0;
|
||||||
|
|
||||||
|
if (!*ln) {
|
||||||
if (!*v) {
|
pa_log("[%s:%u] failed to parse line - missing regexp", fn, n);
|
||||||
pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n);
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
*d = 0;
|
if (!*value_str) {
|
||||||
if (pa_atou(v, &k) >= 0) {
|
pa_log("[%s:%u] failed to parse line - too few words", fn, n);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa_atou(value_str, &k) >= 0)
|
||||||
volume = (pa_volume_t) PA_CLAMP_VOLUME(k);
|
volume = (pa_volume_t) PA_CLAMP_VOLUME(k);
|
||||||
} else if (*v == '"') {
|
else {
|
||||||
char *e;
|
size_t len;
|
||||||
|
|
||||||
e = strchr(v+1, '"');
|
token_end = value_str + strcspn(value_str, WHITESPACE);
|
||||||
if (!e) {
|
|
||||||
pa_log(__FILE__ ": [%s:%u] failed to parse line - missing role closing quote", filename, n);
|
len = token_end - value_str;
|
||||||
|
if (len == (sizeof(UPDATE_REPLACE) - 1) && !strncmp(value_str, UPDATE_REPLACE, len))
|
||||||
|
mode = PA_UPDATE_REPLACE;
|
||||||
|
else if (len == (sizeof(UPDATE_MERGE) - 1) && !strncmp(value_str, UPDATE_MERGE, len))
|
||||||
|
mode = PA_UPDATE_MERGE;
|
||||||
|
|
||||||
|
if (mode != (pa_update_mode_t) -1) {
|
||||||
|
value_str = token_end + strspn(token_end, WHITESPACE);
|
||||||
|
|
||||||
|
if (!*value_str) {
|
||||||
|
pa_log("[%s:%u] failed to parse line - too few words", fn, n);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
mode = PA_UPDATE_MERGE;
|
||||||
|
|
||||||
*e = '\0';
|
if (*value_str == '"') {
|
||||||
e = pa_sprintf_malloc("media.role=\"%s\"", v+1);
|
value_str++;
|
||||||
proplist = pa_proplist_from_string(e);
|
|
||||||
pa_xfree(e);
|
|
||||||
} else {
|
|
||||||
char *e;
|
|
||||||
|
|
||||||
e = v+strspn(v, WHITESPACE);
|
token_end = strchr(value_str, '"');
|
||||||
if (!*e) {
|
if (!token_end) {
|
||||||
pa_log(__FILE__ ": [%s:%u] failed to parse line - missing end of property list", filename, n);
|
pa_log("[%s:%u] failed to parse line - missing role closing quote", fn, n);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
*e = '\0';
|
} else
|
||||||
proplist = pa_proplist_from_string(v);
|
token_end = value_str + strcspn(value_str, WHITESPACE);
|
||||||
|
|
||||||
|
*token_end = 0;
|
||||||
|
|
||||||
|
value_str = pa_sprintf_malloc("media.role=\"%s\"", value_str);
|
||||||
|
proplist = pa_proplist_from_string(value_str);
|
||||||
|
pa_xfree(value_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (regcomp(®ex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
|
if (regcomp(®ex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
|
||||||
pa_log("[%s:%u] invalid regular expression", filename, n);
|
pa_log("[%s:%u] invalid regular expression", fn, n);
|
||||||
|
if (proplist)
|
||||||
|
pa_proplist_free(proplist);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
rule = pa_xnew(struct rule, 1);
|
rule = pa_xnew(struct rule, 1);
|
||||||
rule->regex = regex;
|
rule->regex = regex;
|
||||||
rule->proplist = proplist;
|
rule->proplist = proplist;
|
||||||
|
rule->mode = mode;
|
||||||
rule->volume = volume;
|
rule->volume = volume;
|
||||||
rule->next = NULL;
|
rule->next = NULL;
|
||||||
|
|
||||||
|
|
@ -172,8 +198,6 @@ static int load_rules(struct userdata *u, const char *filename) {
|
||||||
else
|
else
|
||||||
u->rules = rule;
|
u->rules = rule;
|
||||||
end = rule;
|
end = rule;
|
||||||
|
|
||||||
*d = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
@ -184,7 +208,6 @@ finish:
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fn)
|
|
||||||
pa_xfree(fn);
|
pa_xfree(fn);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -206,7 +229,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
||||||
if (!regexec(&r->regex, n, 0, NULL, 0)) {
|
if (!regexec(&r->regex, n, 0, NULL, 0)) {
|
||||||
if (r->proplist) {
|
if (r->proplist) {
|
||||||
pa_log_debug("updating proplist of sink input '%s'", n);
|
pa_log_debug("updating proplist of sink input '%s'", n);
|
||||||
pa_proplist_update(si->proplist, PA_UPDATE_MERGE, r->proplist);
|
pa_proplist_update(si->proplist, r->mode, r->proplist);
|
||||||
} else if (si->volume_writable) {
|
} else if (si->volume_writable) {
|
||||||
pa_cvolume cv;
|
pa_cvolume cv;
|
||||||
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
|
pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
|
||||||
|
|
@ -231,7 +254,7 @@ int pa__init(pa_module*m) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
u = pa_xnew(struct userdata, 1);
|
u = pa_xnew0(struct userdata, 1);
|
||||||
u->rules = NULL;
|
u->rules = NULL;
|
||||||
m->userdata = u;
|
m->userdata = u;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue