pw-cli: add pattern matching lookups

ex:

pw-cli e alsa_card.usb-BEHRINGER_UMC404HD_192k-00 Profile
pw-cli ls alsa_input*
This commit is contained in:
Wim Taymans 2022-01-26 16:32:35 +01:00
parent b7e0b5437b
commit 420d65fb38

View file

@ -32,6 +32,7 @@
#include <alloca.h>
#endif
#include <getopt.h>
#include <fnmatch.h>
#include <readline/readline.h>
#include <readline/history.h>
@ -53,6 +54,18 @@ static const char WHITESPACE[] = " \t";
static char prompt[64];
struct remote_data;
struct proxy_data;
typedef void (*info_func_t) (struct proxy_data *pd);
struct class {
const char *type;
uint32_t version;
const void *events;
pw_destroy_t destroy;
info_func_t info;
const char *name_key;
};
struct data {
struct pw_main_loop *loop;
@ -73,6 +86,7 @@ struct global {
uint32_t permissions;
uint32_t version;
char *type;
const struct class *class;
struct pw_proxy *proxy;
bool info_pending;
struct pw_properties *properties;
@ -96,19 +110,15 @@ struct remote_data {
struct pw_map globals;
};
struct proxy_data;
typedef void (*info_func_t) (struct proxy_data *pd);
struct proxy_data {
struct remote_data *rd;
struct global *global;
struct pw_proxy *proxy;
void *info;
info_func_t info_func;
pw_destroy_t destroy;
struct spa_hook proxy_listener;
struct spa_hook object_listener;
void *info;
const struct class *class;
struct spa_hook proxy_listener;
struct spa_hook object_listener;
};
struct command {
@ -303,6 +313,29 @@ static void on_core_done(void *_data, uint32_t id, int seq)
}
}
static bool global_matches(struct global *g, const char *pattern)
{
const char *str;
if (g->properties == NULL)
return false;
if (strstr(g->type, pattern) != NULL)
return true;
if ((str = pw_properties_get(g->properties, PW_KEY_OBJECT_PATH)) != NULL &&
fnmatch(pattern, str, FNM_EXTMATCH) == 0)
return true;
if ((str = pw_properties_get(g->properties, PW_KEY_OBJECT_SERIAL)) != NULL &&
spa_streq(pattern, str))
return true;
if (g->class != NULL && g->class->name_key != NULL &&
(str = pw_properties_get(g->properties, g->class->name_key)) != NULL &&
fnmatch(pattern, str, FNM_EXTMATCH) == 0)
return true;
return false;
}
static int print_global(void *obj, void *data)
{
struct global *global = obj;
@ -311,7 +344,7 @@ static int print_global(void *obj, void *data)
if (global == NULL)
return 0;
if (filter && !strstr(global->type, filter))
if (filter && !global_matches(global, filter))
return 0;
fprintf(stdout, "\tid %d, type %s/%d\n", global->id,
@ -403,61 +436,19 @@ static const struct pw_registry_events registry_events = {
.global_remove = registry_event_global_remove,
};
static bool global_is_named(struct global *g, const char *name)
{
const char *str;
if (g->properties == NULL)
return false;
if ((str = pw_properties_get(g->properties, PW_KEY_OBJECT_PATH)) != NULL &&
spa_streq(name, str))
return true;
if ((str = pw_properties_get(g->properties, PW_KEY_OBJECT_SERIAL)) != NULL &&
spa_streq(name, str))
return true;
if (spa_streq(g->type, PW_TYPE_INTERFACE_Node)) {
if ((str = pw_properties_get(g->properties, PW_KEY_NODE_NAME)) != NULL &&
spa_streq(name, str))
return true;
}
else if (spa_streq(g->type, PW_TYPE_INTERFACE_Port)) {
if ((str = pw_properties_get(g->properties, PW_KEY_PORT_NAME)) != NULL &&
spa_streq(name, str))
return true;
}
else if (spa_streq(g->type, PW_TYPE_INTERFACE_Device)) {
if ((str = pw_properties_get(g->properties, PW_KEY_DEVICE_NAME)) != NULL &&
spa_streq(name, str))
return true;
}
else if (spa_streq(g->type, PW_TYPE_INTERFACE_Factory)) {
if ((str = pw_properties_get(g->properties, PW_KEY_FACTORY_NAME)) != NULL &&
spa_streq(name, str))
return true;
}
else if (spa_streq(g->type, PW_TYPE_INTERFACE_Module)) {
if ((str = pw_properties_get(g->properties, PW_KEY_MODULE_NAME)) != NULL &&
spa_streq(name, str))
return true;
}
return false;
}
static struct global *find_global(struct remote_data *rd, const char *name)
static struct global *find_global(struct remote_data *rd, const char *pattern)
{
uint32_t id;
union pw_map_item *item;
if (spa_atou32(name, &id, 0))
if (spa_atou32(pattern, &id, 0))
return pw_map_lookup(&rd->globals, id);
pw_array_for_each(item, &rd->globals.items) {
struct global *g = item->data;
if (pw_map_item_is_free(item) || g == NULL)
continue;
if (global_is_named(g, name))
if (global_matches(g, pattern))
return g;
}
return NULL;
@ -1166,14 +1157,14 @@ destroy_proxy (void *data)
spa_hook_remove(&pd->proxy_listener);
spa_hook_remove(&pd->object_listener);
if (pd->info == NULL)
return;
if (pd->global)
pd->global->proxy = NULL;
if (pd->destroy)
pd->destroy(pd->info);
if (pd->info == NULL)
return;
if (pd->class->destroy)
pd->class->destroy(pd->info);
pd->info = NULL;
}
@ -1190,88 +1181,150 @@ static bool do_list_objects(struct data *data, const char *cmd, char *args, char
return true;
}
static const struct class core_class = {
.type = PW_TYPE_INTERFACE_Core,
.version = PW_VERSION_CORE,
.events = &core_events,
.destroy = (pw_destroy_t) pw_core_info_free,
.info = info_core,
.name_key = PW_KEY_CORE_NAME,
};
static const struct class module_class = {
.type = PW_TYPE_INTERFACE_Module,
.version = PW_VERSION_MODULE,
.events = &module_events,
.destroy = (pw_destroy_t) pw_module_info_free,
.info = info_module,
.name_key = PW_KEY_MODULE_NAME,
};
static const struct class factory_class = {
.type = PW_TYPE_INTERFACE_Factory,
.version = PW_VERSION_FACTORY,
.events = &factory_events,
.destroy = (pw_destroy_t) pw_factory_info_free,
.info = info_factory,
.name_key = PW_KEY_FACTORY_NAME,
};
static const struct class client_class = {
.type = PW_TYPE_INTERFACE_Client,
.version = PW_VERSION_CLIENT,
.events = &client_events,
.destroy = (pw_destroy_t) pw_client_info_free,
.info = info_client,
.name_key = PW_KEY_APP_NAME,
};
static const struct class device_class = {
.type = PW_TYPE_INTERFACE_Device,
.version = PW_VERSION_DEVICE,
.events = &device_events,
.destroy = (pw_destroy_t) pw_device_info_free,
.info = info_device,
.name_key = PW_KEY_DEVICE_NAME,
};
static const struct class node_class = {
.type = PW_TYPE_INTERFACE_Node,
.version = PW_VERSION_NODE,
.events = &node_events,
.destroy = (pw_destroy_t) pw_node_info_free,
.info = info_node,
.name_key = PW_KEY_NODE_NAME,
};
static const struct class port_class = {
.type = PW_TYPE_INTERFACE_Port,
.version = PW_VERSION_PORT,
.events = &port_events,
.destroy = (pw_destroy_t) pw_port_info_free,
.info = info_port,
.name_key = PW_KEY_PORT_NAME,
};
static const struct class link_class = {
.type = PW_TYPE_INTERFACE_Link,
.version = PW_VERSION_LINK,
.events = &link_events,
.destroy = (pw_destroy_t) pw_link_info_free,
.info = info_link,
};
static const struct class session_class = {
.type = PW_TYPE_INTERFACE_Session,
.version = PW_VERSION_SESSION,
.events = &session_events,
.destroy = (pw_destroy_t) session_info_free,
.info = info_session,
};
static const struct class endpoint_class = {
.type = PW_TYPE_INTERFACE_Endpoint,
.version = PW_VERSION_ENDPOINT,
.events = &endpoint_events,
.destroy = (pw_destroy_t) endpoint_info_free,
.info = info_endpoint,
};
static const struct class endpoint_stream_class = {
.type = PW_TYPE_INTERFACE_EndpointStream,
.version = PW_VERSION_ENDPOINT_STREAM,
.events = &endpoint_stream_events,
.destroy = (pw_destroy_t) endpoint_stream_info_free,
.info = info_endpoint_stream,
};
static const struct class metadata_class = {
.type = PW_TYPE_INTERFACE_Metadata,
.version = PW_VERSION_METADATA,
.name_key = PW_KEY_METADATA_NAME,
};
static const struct class *classes[] =
{
&core_class,
&module_class,
&factory_class,
&client_class,
&device_class,
&node_class,
&port_class,
&link_class,
&session_class,
&endpoint_class,
&endpoint_stream_class,
&metadata_class,
};
static const struct class *find_class(const char *type, uint32_t version)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(classes); i++) {
if (spa_streq(classes[i]->type, type) &&
classes[i]->version <= version)
return classes[i];
}
return NULL;
}
static bool bind_global(struct remote_data *rd, struct global *global, char **error)
{
const void *events;
uint32_t client_version;
info_func_t info_func;
pw_destroy_t destroy;
const struct class *class;
struct proxy_data *pd;
struct pw_proxy *proxy;
if (spa_streq(global->type, PW_TYPE_INTERFACE_Core)) {
events = &core_events;
client_version = PW_VERSION_CORE;
destroy = (pw_destroy_t) pw_core_info_free;
info_func = info_core;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Module)) {
events = &module_events;
client_version = PW_VERSION_MODULE;
destroy = (pw_destroy_t) pw_module_info_free;
info_func = info_module;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Device)) {
events = &device_events;
client_version = PW_VERSION_DEVICE;
destroy = (pw_destroy_t) pw_device_info_free;
info_func = info_device;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Node)) {
events = &node_events;
client_version = PW_VERSION_NODE;
destroy = (pw_destroy_t) pw_node_info_free;
info_func = info_node;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Port)) {
events = &port_events;
client_version = PW_VERSION_PORT;
destroy = (pw_destroy_t) pw_port_info_free;
info_func = info_port;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Factory)) {
events = &factory_events;
client_version = PW_VERSION_FACTORY;
destroy = (pw_destroy_t) pw_factory_info_free;
info_func = info_factory;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Client)) {
events = &client_events;
client_version = PW_VERSION_CLIENT;
destroy = (pw_destroy_t) pw_client_info_free;
info_func = info_client;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Link)) {
events = &link_events;
client_version = PW_VERSION_LINK;
destroy = (pw_destroy_t) pw_link_info_free;
info_func = info_link;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Session)) {
events = &session_events;
client_version = PW_VERSION_SESSION;
destroy = (pw_destroy_t) session_info_free;
info_func = info_session;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_Endpoint)) {
events = &endpoint_events;
client_version = PW_VERSION_ENDPOINT;
destroy = (pw_destroy_t) endpoint_info_free;
info_func = info_endpoint;
} else if (spa_streq(global->type, PW_TYPE_INTERFACE_EndpointStream)) {
events = &endpoint_stream_events;
client_version = PW_VERSION_ENDPOINT_STREAM;
destroy = (pw_destroy_t) endpoint_stream_info_free;
info_func = info_endpoint_stream;
} else {
class = find_class(global->type, global->version);
if (class == NULL) {
*error = spa_aprintf("unsupported type %s", global->type);
return false;
}
global->class = class;
proxy = pw_registry_bind(rd->registry,
global->id,
global->type,
client_version,
class->version,
sizeof(struct proxy_data));
pd = pw_proxy_get_user_data(proxy);
pd->rd = rd;
pd->global = global;
pd->proxy = proxy;
pd->info_func = info_func;
pd->destroy = destroy;
pw_proxy_add_object_listener(proxy, &pd->object_listener, events, pd);
pd->class = class;
pw_proxy_add_object_listener(proxy, &pd->object_listener, class->events, pd);
pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);
global->proxy = proxy;
@ -1292,8 +1345,8 @@ static bool do_global_info(struct global *global, char **error)
global->info_pending = true;
} else {
pd = pw_proxy_get_user_data(global->proxy);
if (pd->info_func)
pd->info_func(pd);
if (pd->class->info)
pd->class->info(pd);
}
return true;
}
@ -1367,7 +1420,7 @@ static bool do_create_device(struct data *data, const char *cmd, char *args, cha
pd = pw_proxy_get_user_data(proxy);
pd->rd = rd;
pd->proxy = proxy;
pd->destroy = (pw_destroy_t) pw_device_info_free;
pd->class = &device_class;
pw_proxy_add_object_listener(proxy, &pd->object_listener, &device_events, pd);
pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);
@ -1406,7 +1459,7 @@ static bool do_create_node(struct data *data, const char *cmd, char *args, char
pd = pw_proxy_get_user_data(proxy);
pd->rd = rd;
pd->proxy = proxy;
pd->destroy = (pw_destroy_t) pw_node_info_free;
pd->class = &node_class;
pw_proxy_add_object_listener(proxy, &pd->object_listener, &node_events, pd);
pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);
@ -1479,7 +1532,7 @@ static bool do_create_link(struct data *data, const char *cmd, char *args, char
pd = pw_proxy_get_user_data(proxy);
pd->rd = rd;
pd->proxy = proxy;
pd->destroy = (pw_destroy_t) pw_link_info_free;
pd->class = &link_class;
pw_proxy_add_object_listener(proxy, &pd->object_listener, &link_events, pd);
pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd);