diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index 7d5efae2f..01ef26e42 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -44,6 +44,9 @@ context.properties = { vm.overrides = { default.clock.min-quantum = 1024 } + + # keys checked below to disable module loading + module.x11.bell = true } context.spa-libs = { @@ -68,13 +71,16 @@ context.spa-libs = { context.modules = [ #{ name = - # [ args = { = ... } ] - # [ flags = [ [ ifexists ] [ nofail ] ] + # ( args = { = ... } ) + # ( flags = [ ( ifexists ) ( nofail ) ] ) + # ( condition = [ { = ... } ... ] ) #} # # Loads a module with the given parameters. # If ifexists is given, the module is ignored when it is not found. # If nofail is given, module initialization failures are ignored. + # If condition is given, the module is loaded only when the context + # properties all match the match rules. # # Uses realtime scheduling to boost the audio thread priorities. This uses @@ -167,17 +173,21 @@ context.modules = [ #x11.xauthority = null } flags = [ ifexists nofail ] + condition = [ { module.x11.bell = true } ] } ] context.objects = [ #{ factory = - # [ args = { = ... } ] - # [ flags = [ [ nofail ] ] + # ( args = { = ... } ) + # ( flags = [ ( nofail ) ] ) + # ( condition = [ { = ... } ... ] ) #} # # Creates an object from a PipeWire factory with the given parameters. # If nofail is given, errors are ignored (and no object is created). + # If condition is given, the object is created only when the context properties + # all match the match rules. # #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = [ nofail ] } @@ -256,9 +266,14 @@ context.objects = [ ] context.exec = [ - #{ path = [ args = "" ] } + #{ path = + # ( args = "" ) + # ( condition = [ { = ... } ... ] ) + #} # # Execute the given program with arguments. + # If condition is given, the program is executed only when the context + # properties all match the match rules. # # You can optionally start the session manager here, # but it is better to start it as a systemd service. diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index 700d8ee64..afc5edace 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -604,11 +604,71 @@ static int load_module(struct pw_context *context, const char *key, const char * return 0; } +/* + * { + * # all keys must match the value. ~ in value starts regex. + * = + * ... + * } + */ +static bool find_match(struct spa_json *arr, const struct spa_dict *props) +{ + struct spa_json it[1]; + + while (spa_json_enter_object(arr, &it[0]) > 0) { + char key[256], val[1024]; + const char *str, *value; + int match = 0, fail = 0; + int len; + + while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { + bool success = false; + + if ((len = spa_json_next(&it[0], &value)) <= 0) + break; + + str = spa_dict_lookup(props, key); + + if (spa_json_is_null(value, len)) { + success = str == NULL; + } else { + if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) + continue; + value = val; + len = strlen(val); + } + if (str != NULL) { + if (value[0] == '~') { + regex_t preg; + if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) { + if (regexec(&preg, str, 0, NULL, 0) == 0) + success = true; + regfree(&preg); + } + } else if (strncmp(str, value, len) == 0 && + strlen(str) == (size_t)len) { + success = true; + } + } + if (success) { + match++; + pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); + } + else + fail++; + } + if (match > 0 && fail == 0) + return true; + } + return false; +} + /* * context.modules = [ * { name = - * [ args = { = ... } ] - * [ flags = [ [ ifexists ] [ nofail ] ] + * ( args = { = ... } ) + * ( flags = [ ( ifexists ) ( nofail ) ] + * ( condition = [ { key = value, .. } .. ] ) * } * ] */ @@ -617,7 +677,7 @@ static int parse_modules(void *user_data, const char *location, { struct data *d = user_data; struct pw_context *context = d->context; - struct spa_json it[3]; + struct spa_json it[4]; char key[512], *s; int res = 0; @@ -631,6 +691,7 @@ static int parse_modules(void *user_data, const char *location, while (spa_json_enter_object(&it[1], &it[2]) > 0) { char *name = NULL, *args = NULL, *flags = NULL; + bool have_match = true; while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { const char *val; @@ -653,8 +714,16 @@ static int parse_modules(void *user_data, const char *location, len = spa_json_container_len(&it[2], val, len); flags = (char*)val; spa_json_parse_stringn(val, len, flags, len+1); + } else if (spa_streq(key, "condition")) { + if (!spa_json_is_array(val, len)) + break; + spa_json_enter(&it[2], &it[3]); + have_match = find_match(&it[3], &context->properties->dict); } } + if (!have_match) + continue; + if (name != NULL) res = load_module(context, name, args, flags); @@ -698,8 +767,9 @@ static int create_object(struct pw_context *context, const char *key, const char /* * context.objects = [ * { factory = - * [ args = { = ... } ] - * [ flags = [ [ nofail ] ] ] + * ( args = { = ... } ) + * ( flags = [ ( nofail ) ] ) + * ( condition = [ { key = value, .. } .. ] ) * } * ] */ @@ -708,7 +778,7 @@ static int parse_objects(void *user_data, const char *location, { struct data *d = user_data; struct pw_context *context = d->context; - struct spa_json it[3]; + struct spa_json it[4]; char key[512], *s; int res = 0; @@ -722,6 +792,7 @@ static int parse_objects(void *user_data, const char *location, while (spa_json_enter_object(&it[1], &it[2]) > 0) { char *factory = NULL, *args = NULL, *flags = NULL; + bool have_match = true; while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { const char *val; @@ -745,8 +816,16 @@ static int parse_objects(void *user_data, const char *location, flags = (char*)val; spa_json_parse_stringn(val, len, flags, len+1); + } else if (spa_streq(key, "condition")) { + if (!spa_json_is_array(val, len)) + break; + spa_json_enter(&it[2], &it[3]); + have_match = find_match(&it[3], &context->properties->dict); } } + if (!have_match) + continue; + if (factory != NULL) res = create_object(context, factory, args, flags); @@ -807,8 +886,9 @@ static int do_exec(struct pw_context *context, const char *key, const char *args /* * context.exec = [ - * { path = - * [ args = "" ] + * { path = + * ( args = "" ) + * ( condition = [ { key = value, .. } .. ] ) * } * ] */ @@ -817,7 +897,7 @@ static int parse_exec(void *user_data, const char *location, { struct data *d = user_data; struct pw_context *context = d->context; - struct spa_json it[3]; + struct spa_json it[4]; char key[512], *s; int res = 0; @@ -831,6 +911,7 @@ static int parse_exec(void *user_data, const char *location, while (spa_json_enter_object(&it[1], &it[2]) > 0) { char *path = NULL, *args = NULL; + bool have_match = true; while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) { const char *val; @@ -845,8 +926,16 @@ static int parse_exec(void *user_data, const char *location, } else if (spa_streq(key, "args")) { args = (char*)val; spa_json_parse_stringn(val, len, args, len+1); + } else if (spa_streq(key, "condition")) { + if (!spa_json_is_array(val, len)) + break; + spa_json_enter(&it[2], &it[3]); + have_match = find_match(&it[3], &context->properties->dict); } } + if (!have_match) + continue; + if (path != NULL) res = do_exec(context, path, args); @@ -1014,65 +1103,6 @@ int pw_context_conf_update_props(struct pw_context *context, } -/* - * { - * # all keys must match the value. ~ in value starts regex. - * = - * ... - * } - */ -static bool find_match(struct spa_json *arr, const struct spa_dict *props) -{ - struct spa_json it[1]; - - while (spa_json_enter_object(arr, &it[0]) > 0) { - char key[256], val[1024]; - const char *str, *value; - int match = 0, fail = 0; - int len; - - while (spa_json_get_string(&it[0], key, sizeof(key)) > 0) { - bool success = false; - - if ((len = spa_json_next(&it[0], &value)) <= 0) - break; - - str = spa_dict_lookup(props, key); - - if (spa_json_is_null(value, len)) { - success = str == NULL; - } else { - if (spa_json_parse_stringn(value, len, val, sizeof(val)) < 0) - continue; - value = val; - len = strlen(val); - } - if (str != NULL) { - if (value[0] == '~') { - regex_t preg; - if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) { - if (regexec(&preg, str, 0, NULL, 0) == 0) - success = true; - regfree(&preg); - } - } else if (strncmp(str, value, len) == 0 && - strlen(str) == (size_t)len) { - success = true; - } - } - if (success) { - match++; - pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value); - } - else - fail++; - } - if (match > 0 && fail == 0) - return true; - } - return false; -} - /** * [ * {