From f724319e8aac5edacd94edcdc40f68c2cde23f1c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 12 Dec 2019 16:34:01 +0100 Subject: [PATCH] context: move core implementation to impl-core.h Move the core implementation to a separate file. Make a default core object in the context. Pass impl-core to server. We now tie the server to a core. Also keep track of the core that a client connected to. Fill the properties that we usually use to connect in the context and copy them when a connection is made. Use "internal" as the remote name to connect to the default internal core. --- src/modules/module-protocol-native.c | 23 +- src/pipewire/context.c | 509 +++------------------- src/pipewire/context.h | 8 - src/pipewire/core.c | 24 +- src/pipewire/impl-client.c | 16 +- src/pipewire/impl-client.h | 2 +- src/pipewire/impl-core.c | 624 +++++++++++++++++++++++++++ src/pipewire/impl-core.h | 94 ++++ src/pipewire/impl.h | 1 + src/pipewire/meson.build | 2 + src/pipewire/pipewire.c | 57 --- src/pipewire/pipewire.h | 6 - src/pipewire/private.h | 35 +- src/pipewire/protocol.h | 4 + src/tests/test-context.c | 37 +- src/tools/pipewire-cli.c | 10 +- 16 files changed, 839 insertions(+), 613 deletions(-) create mode 100644 src/pipewire/impl-core.c create mode 100644 src/pipewire/impl-core.h diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index 16df0c369..22ef217d1 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -301,11 +301,10 @@ static void on_start(void *data, uint32_t version) { struct client_data *this = data; struct pw_impl_client *client = this->client; - struct pw_context *context = client->context; pw_log_debug("version %d", version); - if (pw_global_bind(pw_context_get_global(context), client, + if (pw_global_bind(pw_impl_core_get_global(client->core), client, PW_PERM_RWX, version, 0) < 0) return; @@ -357,7 +356,7 @@ static struct client_data *client_new(struct server *s, int fd) pw_properties_setf(props, PW_KEY_MODULE_ID, "%d", d->module->global->id); - client = pw_context_create_client(protocol->context, + client = pw_context_create_client(s->this.core, protocol, props, sizeof(struct client_data)); if (client == NULL) goto exit; @@ -809,6 +808,7 @@ error: static struct pw_protocol_client * impl_new_client(struct pw_protocol *protocol, + struct pw_core *core, const struct pw_properties *properties) { struct client *impl; @@ -823,6 +823,7 @@ impl_new_client(struct pw_protocol *protocol, this = &impl->this; this->protocol = protocol; + this->core = core; impl->context = protocol->context; impl->connection = pw_protocol_native_connection_new(protocol->context, -1); @@ -834,8 +835,8 @@ impl_new_client(struct pw_protocol *protocol, if (properties) { str = pw_properties_get(properties, PW_KEY_REMOTE_INTENTION); if (str == NULL && - (str = pw_properties_get(properties, PW_KEY_REMOTE_NAME)) != NULL && - strcmp(str, impl->context->info.name) == 0) + (str = pw_properties_get(properties, PW_KEY_REMOTE_NAME)) != NULL && + strcmp(str, "internal") == 0) str = "internal"; } if (str == NULL) @@ -933,6 +934,7 @@ get_name(const struct pw_properties *properties) static struct server * create_server(struct pw_protocol *protocol, + struct pw_impl_core *core, const struct pw_properties *properties) { struct pw_protocol_server *this; @@ -946,6 +948,7 @@ create_server(struct pw_protocol *protocol, this = &s->this; this->protocol = protocol; + this->core = core; spa_list_init(&this->client_list); this->destroy = destroy_server; @@ -960,6 +963,7 @@ create_server(struct pw_protocol *protocol, static struct pw_protocol_server * impl_add_server(struct pw_protocol *protocol, + struct pw_impl_core *core, const struct pw_properties *properties) { struct pw_protocol_server *this; @@ -967,7 +971,7 @@ impl_add_server(struct pw_protocol *protocol, const char *name; int res; - if ((s = create_server(protocol, properties)) == NULL) + if ((s = create_server(protocol, core, properties)) == NULL) return NULL; this = &s->this; @@ -1083,9 +1087,9 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) { struct pw_context *context = pw_impl_module_get_context(module); struct pw_protocol *this; - const char *val; struct protocol_data *d; const struct pw_properties *props; + const char *val; int res; if (pw_context_find_protocol(context, PW_TYPE_INFO_PROTOCOL_Native) != NULL) @@ -1109,15 +1113,14 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) d->protocol = this; d->module = module; - d->local = create_server(this, props); - props = pw_context_get_properties(context); + d->local = create_server(this, context->core, props); val = getenv("PIPEWIRE_DAEMON"); if (val == NULL) val = pw_properties_get(props, PW_KEY_CORE_DAEMON); if (val && pw_properties_parse_bool(val)) { - if (impl_add_server(this, props) == NULL) { + if (impl_add_server(this, context->core, props) == NULL) { res = -errno; goto error_cleanup; } diff --git a/src/pipewire/context.c b/src/pipewire/context.c index 4722f651b..b1828063a 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -39,6 +39,8 @@ #include #include +#include + #define NAME "context" /** \cond */ @@ -58,399 +60,6 @@ struct factory_entry { char *lib; }; -/** \endcond */ -static void * registry_bind(void *object, uint32_t id, - uint32_t type, uint32_t version, size_t user_data_size) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_context *context = resource->context; - struct pw_global *global; - uint32_t permissions, new_id = user_data_size; - - if ((global = pw_context_find_global(context, id)) == NULL) - goto error_no_id; - - permissions = pw_global_get_permissions(global, client); - - if (!PW_PERM_IS_R(permissions)) - goto error_no_id; - - if (global->type != type) - goto error_wrong_interface; - - pw_log_debug("global %p: bind global id %d, iface %s/%d to %d", global, id, - spa_debug_type_find_name(pw_type_info(), type), version, new_id); - - if (pw_global_bind(global, client, permissions, version, new_id) < 0) - goto error_exit_clean; - - return NULL; - -error_no_id: - pw_log_debug("registry %p: no global with id %u to bind to %u", resource, id, new_id); - pw_resource_errorf(resource, -ENOENT, "no such global %u", id); - goto error_exit_clean; -error_wrong_interface: - pw_log_debug("registry %p: global with id %u has no interface %u", resource, id, type); - pw_resource_errorf(resource, -ENOENT, "no such interface %u", type); - goto error_exit_clean; -error_exit_clean: - /* unmark the new_id the map, the client does not yet know about the failed - * bind and will choose the next id, which we would refuse when we don't mark - * new_id as 'used and freed' */ - pw_map_insert_at(&client->objects, new_id, NULL); - pw_core_resource_remove_id(client->core_resource, new_id); - return NULL; -} - -static int registry_destroy(void *object, uint32_t id) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_context *context = resource->context; - struct pw_global *global; - uint32_t permissions; - int res; - - if ((global = pw_context_find_global(context, id)) == NULL) - goto error_no_id; - - permissions = pw_global_get_permissions(global, client); - - if (!PW_PERM_IS_R(permissions)) - goto error_no_id; - - if (!PW_PERM_IS_X(permissions)) - goto error_not_allowed; - - pw_log_debug("global %p: destroy global id %d", global, id); - - pw_global_destroy(global); - return 0; - -error_no_id: - pw_log_debug("registry %p: no global with id %u to destroy", resource, id); - res = -ENOENT; - goto error_exit; -error_not_allowed: - pw_log_debug("registry %p: destroy of id %u not allowed", resource, id); - res = -EPERM; - goto error_exit; -error_exit: - return res; -} - -static const struct pw_registry_methods registry_methods = { - PW_VERSION_REGISTRY_METHODS, - .bind = registry_bind, - .destroy = registry_destroy -}; - -static void destroy_registry_resource(void *object) -{ - struct pw_resource *resource = object; - spa_list_remove(&resource->link); -} - -static const struct pw_resource_events resource_events = { - PW_VERSION_RESOURCE_EVENTS, - .destroy = destroy_registry_resource -}; - -static int destroy_resource(void *object, void *data) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - - if (resource && - resource != client->core_resource) { - pw_resource_remove(resource); - } - return 0; -} - -static int core_hello(void *object, uint32_t version) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_context *this = resource->context; - int res; - - pw_log_debug(NAME" %p: hello %d from resource %p", this, version, resource); - pw_map_for_each(&client->objects, destroy_resource, client); - - this->info.change_mask = PW_CORE_CHANGE_MASK_ALL; - pw_core_resource_info(resource, &this->info); - - if (version >= 3) { - if ((res = pw_global_bind(client->global, client, - PW_PERM_RWX, PW_VERSION_CLIENT, 1)) < 0) - return res; - } - return 0; -} - -static int core_sync(void *object, uint32_t id, int seq) -{ - struct pw_resource *resource = object; - pw_log_trace(NAME" %p: sync %d for resource %d", resource->context, seq, id); - pw_core_resource_done(resource, id, seq); - return 0; -} - -static int core_pong(void *object, uint32_t id, int seq) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_resource *r; - - pw_log_debug(NAME" %p: pong %d for resource %d", resource->context, seq, id); - - if ((r = pw_impl_client_find_resource(client, id)) == NULL) - return -EINVAL; - - pw_resource_emit_pong(r, seq); - return 0; -} - -static int core_error(void *object, uint32_t id, int seq, int res, const char *message) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_resource *r; - - pw_log_debug(NAME" %p: error %d for resource %d: %s", resource->context, res, id, message); - - if ((r = pw_impl_client_find_resource(client, id)) == NULL) - return -EINVAL; - - pw_resource_emit_error(r, seq, res, message); - return 0; -} - -static struct pw_registry * core_get_registry(void *object, uint32_t version, size_t user_data_size) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_context *this = resource->context; - struct pw_global *global; - struct pw_resource *registry_resource; - struct resource_data *data; - uint32_t new_id = user_data_size; - int res; - - registry_resource = pw_resource_new(client, - new_id, - PW_PERM_RWX, - PW_TYPE_INTERFACE_Registry, - version, - sizeof(*data)); - if (registry_resource == NULL) { - res = -errno; - goto error_resource; - } - - data = pw_resource_get_user_data(registry_resource); - pw_resource_add_listener(registry_resource, - &data->resource_listener, - &resource_events, - registry_resource); - pw_resource_add_object_listener(registry_resource, - &data->object_listener, - ®istry_methods, - registry_resource); - - spa_list_append(&this->registry_resource_list, ®istry_resource->link); - - spa_list_for_each(global, &this->global_list, link) { - uint32_t permissions = pw_global_get_permissions(global, client); - if (PW_PERM_IS_R(permissions)) { - pw_registry_resource_global(registry_resource, - global->id, - permissions, - global->type, - global->version, - &global->properties->dict); - } - } - - return (struct pw_registry *)registry_resource; - -error_resource: - pw_log_error(NAME" %p: can't create registry resource: %m", this); - pw_core_resource_errorf(client->core_resource, new_id, - client->recv_seq, res, - "can't create registry resource: %s", spa_strerror(res)); - pw_map_insert_at(&client->objects, new_id, NULL); - pw_core_resource_remove_id(client->core_resource, new_id); - errno = -res; - return NULL; -} - -static void * -core_create_object(void *object, - const char *factory_name, - uint32_t type, - uint32_t version, - const struct spa_dict *props, - size_t user_data_size) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_impl_factory *factory; - void *obj; - struct pw_properties *properties; - struct pw_context *this = client->context; - uint32_t new_id = user_data_size; - int res; - - factory = pw_context_find_factory(this, factory_name); - if (factory == NULL || factory->global == NULL) - goto error_no_factory; - - if (!PW_PERM_IS_R(pw_global_get_permissions(factory->global, client))) - goto error_no_factory; - - if (factory->info.type != type) - goto error_type; - - if (factory->info.version < version) - goto error_version; - - if (props) { - properties = pw_properties_new_dict(props); - if (properties == NULL) - goto error_properties; - } else - properties = NULL; - - /* error will be posted */ - obj = pw_impl_factory_create_object(factory, resource, type, version, properties, new_id); - if (obj == NULL) - goto error_create_failed; - - return 0; - -error_no_factory: - res = -ENOENT; - pw_log_error(NAME" %p: can't find factory '%s'", this, factory_name); - pw_resource_errorf(resource, res, "unknown factory name %s", factory_name); - goto error_exit; -error_version: -error_type: - res = -EPROTO; - pw_log_error(NAME" %p: invalid resource type/version", this); - pw_resource_errorf(resource, res, "wrong resource type/version"); - goto error_exit; -error_properties: - res = -errno; - pw_log_error(NAME" %p: can't create properties: %m", this); - pw_resource_errorf(resource, res, "can't create properties: %s", spa_strerror(res)); - goto error_exit; -error_create_failed: - res = -errno; - goto error_exit; -error_exit: - pw_map_insert_at(&client->objects, new_id, NULL); - pw_core_resource_remove_id(client->core_resource, new_id); - errno = -res; - return NULL; -} - -static int core_destroy(void *object, void *proxy) -{ - struct pw_resource *resource = object; - struct pw_impl_client *client = resource->client; - struct pw_resource *r = proxy; - pw_log_debug(NAME" %p: destroy resource %p from client %p", resource->context, r, client); - pw_resource_destroy(r); - return 0; -} - -static const struct pw_core_methods core_methods = { - PW_VERSION_CORE_METHODS, - .hello = core_hello, - .sync = core_sync, - .pong = core_pong, - .error = core_error, - .get_registry = core_get_registry, - .create_object = core_create_object, - .destroy = core_destroy, -}; - -static void core_unbind_func(void *data) -{ - struct pw_resource *resource = data; - if (resource->id == 0) - resource->client->core_resource = NULL; - spa_list_remove(&resource->link); -} - -static const struct pw_resource_events core_resource_events = { - PW_VERSION_RESOURCE_EVENTS, - .destroy = core_unbind_func, -}; - -static int -global_bind(void *_data, - struct pw_impl_client *client, - uint32_t permissions, - uint32_t version, - uint32_t id) -{ - struct pw_context *this = _data; - struct pw_global *global = this->global; - struct pw_resource *resource; - struct resource_data *data; - int res; - - resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); - if (resource == NULL) { - res = -errno; - goto error; - } - - data = pw_resource_get_user_data(resource); - - pw_resource_add_listener(resource, - &data->resource_listener, - &core_resource_events, - resource); - pw_resource_add_object_listener(resource, - &data->object_listener, - &core_methods, resource); - - spa_list_append(&global->resource_list, &resource->link); - pw_resource_set_bound_id(resource, global->id); - - if (resource->id == 0) - client->core_resource = resource; - else - pw_core_resource_info(resource, &this->info); - - pw_log_debug(NAME" %p: bound to %d", this, resource->id); - - return 0; - -error: - pw_log_error(NAME" %p: can't create resource: %m", this); - return res; -} - -static void global_destroy(void *object) -{ - struct pw_context *context = object; - spa_hook_remove(&context->global_listener); - context->global = NULL; - pw_context_destroy(context); -} - -static const struct pw_global_events global_events = { - PW_VERSION_GLOBAL_EVENTS, - .destroy = global_destroy, -}; - static int load_module_profile(struct pw_context *this, const char *profile) { pw_log_debug(NAME" %p: module profile %s", this, profile); @@ -466,6 +75,41 @@ static int load_module_profile(struct pw_context *this, const char *profile) return 0; } +static void fill_properties(struct pw_context *context) +{ + struct pw_properties *properties = context->properties; + + if (!pw_properties_get(properties, PW_KEY_APP_NAME)) + pw_properties_set(properties, PW_KEY_APP_NAME, pw_get_client_name()); + + if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_BINARY)) + pw_properties_set(properties, PW_KEY_APP_PROCESS_BINARY, pw_get_prgname()); + + if (!pw_properties_get(properties, PW_KEY_APP_LANGUAGE)) { + pw_properties_set(properties, PW_KEY_APP_LANGUAGE, getenv("LANG")); + } + if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_ID)) { + pw_properties_setf(properties, PW_KEY_APP_PROCESS_ID, "%zd", (size_t) getpid()); + } + if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_USER)) + pw_properties_set(properties, PW_KEY_APP_PROCESS_USER, pw_get_user_name()); + + if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_HOST)) + pw_properties_set(properties, PW_KEY_APP_PROCESS_HOST, pw_get_host_name()); + + if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_SESSION_ID)) { + pw_properties_set(properties, PW_KEY_APP_PROCESS_SESSION_ID, + getenv("XDG_SESSION_ID")); + } + if (!pw_properties_get(properties, PW_KEY_WINDOW_X11_DISPLAY)) { + pw_properties_set(properties, PW_KEY_WINDOW_X11_DISPLAY, + getenv("DISPLAY")); + } + pw_properties_set(properties, PW_KEY_CORE_VERSION, context->core->info.version); + pw_properties_set(properties, PW_KEY_CORE_NAME, context->core->info.name); +} + + /** Create a new context object * * \param main_loop the main loop to use @@ -481,7 +125,7 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, { struct impl *impl; struct pw_context *this; - const char *name, *lib, *str; + const char *lib, *str; void *dbus_iface = NULL; uint32_t n_support; struct pw_properties *pr; @@ -560,6 +204,7 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, pw_array_init(&this->factory_lib, 32); pw_map_init(&this->globals, 128, 32); + spa_list_init(&this->core_impl_list); spa_list_init(&this->protocol_list); spa_list_init(&this->core_list); spa_list_init(&this->registry_resource_list); @@ -576,48 +221,20 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, spa_list_init(&this->driver_list); spa_hook_list_init(&this->listener_list); - if ((name = pw_properties_get(properties, PW_KEY_CORE_NAME)) == NULL) { - pw_properties_setf(properties, - PW_KEY_CORE_NAME, "pipewire-%s-%d", - pw_get_user_name(), getpid()); - name = pw_properties_get(properties, PW_KEY_CORE_NAME); + this->core = pw_context_create_core(this, pw_properties_copy(properties), 0); + if (this->core == NULL) { + res = -errno; + goto error_free_loop; } + pw_impl_core_register(this->core, NULL); + + fill_properties(this); if ((res = pw_data_loop_start(this->data_loop_impl)) < 0) goto error_free_loop; - this->info.change_mask = 0; - this->info.user_name = pw_get_user_name(); - this->info.host_name = pw_get_host_name(); - this->info.version = pw_get_library_version(); - srandom(time(NULL)); - this->info.cookie = random(); - this->info.name = name; - this->sc_pagesize = sysconf(_SC_PAGESIZE); - this->global = pw_global_new(this, - PW_TYPE_INTERFACE_Core, - PW_VERSION_CORE, - pw_properties_new( - PW_KEY_USER_NAME, this->info.user_name, - PW_KEY_HOST_NAME, this->info.host_name, - PW_KEY_CORE_NAME, this->info.name, - PW_KEY_CORE_VERSION, this->info.version, - NULL), - global_bind, - this); - if (this->global == NULL) { - res = -errno; - goto error_free_loop; - } - this->info.id = this->global->id; - pw_properties_setf(this->properties, PW_KEY_OBJECT_ID, "%d", this->info.id); - this->info.props = &this->properties->dict; - - pw_global_add_listener(this->global, &this->global_listener, &global_events, this); - pw_global_register(this->global); - if ((str = pw_properties_get(properties, PW_KEY_CONTEXT_PROFILE_MODULES)) == NULL) str = "default"; @@ -655,12 +272,11 @@ void pw_context_destroy(struct pw_context *context) struct pw_resource *resource; struct pw_impl_node *node; struct factory_entry *entry; + struct pw_impl_core *core_impl; pw_log_debug(NAME" %p: destroy", context); pw_context_emit_destroy(context); - spa_hook_remove(&context->global_listener); - spa_list_consume(core, &context->core_list, link) pw_core_disconnect(core); @@ -679,6 +295,9 @@ void pw_context_destroy(struct pw_context *context) spa_list_consume(global, &context->global_list, link) pw_global_destroy(global); + spa_list_consume(core_impl, &context->core_impl_list, link) + pw_impl_core_destroy(core_impl); + pw_log_debug(NAME" %p: free", context); pw_context_emit_free(context); @@ -708,18 +327,6 @@ void *pw_context_get_user_data(struct pw_context *context) return context->user_data; } -SPA_EXPORT -const struct pw_core_info *pw_context_get_info(struct pw_context *context) -{ - return &context->info; -} - -SPA_EXPORT -struct pw_global *pw_context_get_global(struct pw_context *context) -{ - return context->global; -} - SPA_EXPORT void pw_context_add_listener(struct pw_context *context, struct spa_hook *listener, @@ -760,27 +367,11 @@ const struct pw_properties *pw_context_get_properties(struct pw_context *context SPA_EXPORT int pw_context_update_properties(struct pw_context *context, const struct spa_dict *dict) { - struct pw_resource *resource; int changed; changed = pw_properties_update(context->properties, dict); - context->info.props = &context->properties->dict; - pw_log_debug(NAME" %p: updated %d properties", context, changed); - if (!changed) - return 0; - - context->info.change_mask = PW_CORE_CHANGE_MASK_PROPS; - - pw_context_emit_info_changed(context, &context->info); - - if (context->global) - spa_list_for_each(resource, &context->global->resource_list, link) - pw_core_resource_info(resource, &context->info); - - context->info.change_mask = 0; - return changed; } diff --git a/src/pipewire/context.h b/src/pipewire/context.h index 8f5373770..b9210a8ee 100644 --- a/src/pipewire/context.h +++ b/src/pipewire/context.h @@ -85,8 +85,6 @@ struct pw_context_events { void (*destroy) (void *data); /** The context is being freed */ void (*free) (void *data); - /** The context info changed, use \ref pw_context_get_info() to get the updated info */ - void (*info_changed) (void *data, const struct pw_core_info *info); /** a new client object is added */ void (*check_access) (void *data, struct pw_impl_client *client); /** a new global object was added */ @@ -112,12 +110,6 @@ void pw_context_add_listener(struct pw_context *context, const struct pw_context_events *events, void *data); -/** Get the context info object */ -const struct pw_core_info *pw_context_get_info(struct pw_context *context); - -/** Get the context global object */ -struct pw_global *pw_context_get_global(struct pw_context *context); - /** Get the context properties */ const struct pw_properties *pw_context_get_properties(struct pw_context *context); diff --git a/src/pipewire/core.c b/src/pipewire/core.c index ed742968f..84a15facf 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -276,7 +276,7 @@ static struct pw_core *core_new(struct pw_context *context, struct pw_properties *properties, size_t user_data_size) { struct pw_core *p; - struct pw_protocol *protocol = NULL; + struct pw_protocol *protocol; const char *protocol_name; int res; @@ -292,7 +292,7 @@ static struct pw_core *core_new(struct pw_context *context, if (properties == NULL) goto error_properties; - pw_fill_connect_properties(context, properties); + pw_properties_add(properties, &context->properties->dict); p->proxy.core = p; p->context = context; @@ -307,25 +307,20 @@ static struct pw_core *core_new(struct pw_context *context, spa_list_init(&p->stream_list); spa_list_init(&p->filter_list); - if ((protocol_name = pw_properties_get(properties, PW_KEY_PROTOCOL)) == NULL) { - if ((protocol_name = pw_properties_get(context->properties, PW_KEY_PROTOCOL)) == NULL) { - protocol_name = PW_TYPE_INFO_PROTOCOL_Native; - } - } + if ((protocol_name = pw_properties_get(properties, PW_KEY_PROTOCOL)) == NULL && + (protocol_name = pw_properties_get(context->properties, PW_KEY_PROTOCOL)) == NULL) + protocol_name = PW_TYPE_INFO_PROTOCOL_Native; - if (protocol == NULL) - protocol = pw_context_find_protocol(context, protocol_name); + protocol = pw_context_find_protocol(context, protocol_name); if (protocol == NULL) { res = -ENOTSUP; goto error_protocol; } - p->conn = pw_protocol_new_client(protocol, properties); + p->conn = pw_protocol_new_client(protocol, p, properties); if (p->conn == NULL) goto error_connection; - p->conn->core = p; - if ((res = pw_proxy_init(&p->proxy, PW_TYPE_INTERFACE_Core, PW_VERSION_CORE)) < 0) goto error_proxy; @@ -423,15 +418,12 @@ struct pw_core * pw_context_connect_self(struct pw_context *context, struct pw_properties *properties, size_t user_data_size) { - const struct pw_core_info *info; - if (properties == NULL) properties = pw_properties_new(NULL, NULL); if (properties == NULL) return NULL; - info = pw_context_get_info(context); - pw_properties_set(properties, PW_KEY_REMOTE_NAME, info->name); + pw_properties_set(properties, PW_KEY_REMOTE_NAME, "internal"); return pw_context_connect(context, properties, user_data_size); } diff --git a/src/pipewire/impl-client.c b/src/pipewire/impl-client.c index 3d8293fec..18d1bebe3 100644 --- a/src/pipewire/impl-client.c +++ b/src/pipewire/impl-client.c @@ -280,7 +280,7 @@ static const struct pw_context_events context_events = { * \memberof pw_impl_client */ SPA_EXPORT -struct pw_impl_client *pw_context_create_client(struct pw_context *context, +struct pw_impl_client *pw_context_create_client(struct pw_impl_core *core, struct pw_protocol *protocol, struct pw_properties *properties, size_t user_data_size) @@ -299,7 +299,8 @@ struct pw_impl_client *pw_context_create_client(struct pw_context *context, this = &impl->this; pw_log_debug(NAME" %p: new", this); - this->context = context; + this->context = core->context; + this->core = core; this->protocol = protocol; if (properties == NULL) @@ -336,11 +337,11 @@ struct pw_impl_client *pw_context_create_client(struct pw_context *context, pw_map_init(&this->objects, 0, 32); - pw_context_add_listener(context, &impl->context_listener, &context_events, impl); + pw_context_add_listener(this->context, &impl->context_listener, &context_events, impl); this->info.props = &this->properties->dict; - pw_context_emit_check_access(context, this); + pw_context_emit_check_access(this->context, this); return this; @@ -427,7 +428,7 @@ error_existed: SPA_EXPORT struct pw_context *pw_impl_client_get_context(struct pw_impl_client *client) { - return client->context; + return client->core->context; } SPA_EXPORT @@ -569,7 +570,8 @@ SPA_EXPORT int pw_impl_client_update_permissions(struct pw_impl_client *client, uint32_t n_permissions, const struct pw_permission *permissions) { - struct pw_context *context = client->context; + struct pw_impl_core *core = client->core; + struct pw_context *context = core->context; struct pw_permission *def; uint32_t i; @@ -603,7 +605,7 @@ int pw_impl_client_update_permissions(struct pw_impl_client *client, else { struct pw_global *global; - global = pw_context_find_global(client->context, permissions[i].id); + global = pw_context_find_global(context, permissions[i].id); if (global == NULL || global->id != permissions[i].id) { pw_log_warn(NAME" %p: invalid global %d", client, permissions[i].id); continue; diff --git a/src/pipewire/impl-client.h b/src/pipewire/impl-client.h index ca6e37cc6..fa0c35071 100644 --- a/src/pipewire/impl-client.h +++ b/src/pipewire/impl-client.h @@ -110,7 +110,7 @@ struct pw_impl_client_events { /** Create a new client. This is mainly used by protocols. */ struct pw_impl_client * -pw_context_create_client(struct pw_context *context, /**< the context object */ +pw_context_create_client(struct pw_impl_core *core, /**< the core object */ struct pw_protocol *prototol, /**< the client protocol */ struct pw_properties *properties, /**< client properties */ size_t user_data_size /**< extra user data size */); diff --git a/src/pipewire/impl-core.c b/src/pipewire/impl-core.c new file mode 100644 index 000000000..10634224e --- /dev/null +++ b/src/pipewire/impl-core.c @@ -0,0 +1,624 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include + +#include "pipewire/impl.h" +#include "pipewire/private.h" + +#include "extensions/protocol-native.h" + +#define NAME "impl-core" + +struct resource_data { + struct spa_hook resource_listener; + struct spa_hook object_listener; +}; + +static void * registry_bind(void *object, uint32_t id, + uint32_t type, uint32_t version, size_t user_data_size) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_context *context = resource->context; + struct pw_global *global; + uint32_t permissions, new_id = user_data_size; + + if ((global = pw_context_find_global(context, id)) == NULL) + goto error_no_id; + + permissions = pw_global_get_permissions(global, client); + + if (!PW_PERM_IS_R(permissions)) + goto error_no_id; + + if (global->type != type) + goto error_wrong_interface; + + pw_log_debug("global %p: bind global id %d, iface %s/%d to %d", global, id, + spa_debug_type_find_name(pw_type_info(), type), version, new_id); + + if (pw_global_bind(global, client, permissions, version, new_id) < 0) + goto error_exit_clean; + + return NULL; + +error_no_id: + pw_log_debug("registry %p: no global with id %u to bind to %u", resource, id, new_id); + pw_resource_errorf(resource, -ENOENT, "no such global %u", id); + goto error_exit_clean; +error_wrong_interface: + pw_log_debug("registry %p: global with id %u has no interface %u", resource, id, type); + pw_resource_errorf(resource, -ENOENT, "no such interface %u", type); + goto error_exit_clean; +error_exit_clean: + /* unmark the new_id the map, the client does not yet know about the failed + * bind and will choose the next id, which we would refuse when we don't mark + * new_id as 'used and freed' */ + pw_map_insert_at(&client->objects, new_id, NULL); + pw_core_resource_remove_id(client->core_resource, new_id); + return NULL; +} + +static int registry_destroy(void *object, uint32_t id) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_context *context = resource->context; + struct pw_global *global; + uint32_t permissions; + int res; + + if ((global = pw_context_find_global(context, id)) == NULL) + goto error_no_id; + + permissions = pw_global_get_permissions(global, client); + + if (!PW_PERM_IS_R(permissions)) + goto error_no_id; + + if (!PW_PERM_IS_X(permissions)) + goto error_not_allowed; + + pw_log_debug("global %p: destroy global id %d", global, id); + + pw_global_destroy(global); + return 0; + +error_no_id: + pw_log_debug("registry %p: no global with id %u to destroy", resource, id); + res = -ENOENT; + goto error_exit; +error_not_allowed: + pw_log_debug("registry %p: destroy of id %u not allowed", resource, id); + res = -EPERM; + goto error_exit; +error_exit: + return res; +} + +static const struct pw_registry_methods registry_methods = { + PW_VERSION_REGISTRY_METHODS, + .bind = registry_bind, + .destroy = registry_destroy +}; + +static void destroy_registry_resource(void *object) +{ + struct pw_resource *resource = object; + spa_list_remove(&resource->link); +} + +static const struct pw_resource_events resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = destroy_registry_resource +}; + +static int destroy_resource(void *object, void *data) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + + if (resource && + resource != client->core_resource) { + pw_resource_remove(resource); + } + return 0; +} + +static int core_hello(void *object, uint32_t version) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_context *context = client->context; + struct pw_impl_core *this = client->core; + int res; + + pw_log_debug(NAME" %p: hello %d from resource %p", context, version, resource); + pw_map_for_each(&client->objects, destroy_resource, client); + + this->info.change_mask = PW_CORE_CHANGE_MASK_ALL; + pw_core_resource_info(resource, &this->info); + + if (version >= 3) { + if ((res = pw_global_bind(client->global, client, + PW_PERM_RWX, PW_VERSION_CLIENT, 1)) < 0) + return res; + } + return 0; +} + +static int core_sync(void *object, uint32_t id, int seq) +{ + struct pw_resource *resource = object; + pw_log_trace(NAME" %p: sync %d for resource %d", resource->context, seq, id); + pw_core_resource_done(resource, id, seq); + return 0; +} + +static int core_pong(void *object, uint32_t id, int seq) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_resource *r; + + pw_log_debug(NAME" %p: pong %d for resource %d", resource->context, seq, id); + + if ((r = pw_impl_client_find_resource(client, id)) == NULL) + return -EINVAL; + + pw_resource_emit_pong(r, seq); + return 0; +} + +static int core_error(void *object, uint32_t id, int seq, int res, const char *message) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_resource *r; + + pw_log_debug(NAME" %p: error %d for resource %d: %s", resource->context, res, id, message); + + if ((r = pw_impl_client_find_resource(client, id)) == NULL) + return -EINVAL; + + pw_resource_emit_error(r, seq, res, message); + return 0; +} + +static struct pw_registry * core_get_registry(void *object, uint32_t version, size_t user_data_size) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_context *context = client->context; + struct pw_global *global; + struct pw_resource *registry_resource; + struct resource_data *data; + uint32_t new_id = user_data_size; + int res; + + registry_resource = pw_resource_new(client, + new_id, + PW_PERM_RWX, + PW_TYPE_INTERFACE_Registry, + version, + sizeof(*data)); + if (registry_resource == NULL) { + res = -errno; + goto error_resource; + } + + data = pw_resource_get_user_data(registry_resource); + pw_resource_add_listener(registry_resource, + &data->resource_listener, + &resource_events, + registry_resource); + pw_resource_add_object_listener(registry_resource, + &data->object_listener, + ®istry_methods, + registry_resource); + + spa_list_append(&context->registry_resource_list, ®istry_resource->link); + + spa_list_for_each(global, &context->global_list, link) { + uint32_t permissions = pw_global_get_permissions(global, client); + if (PW_PERM_IS_R(permissions)) { + pw_registry_resource_global(registry_resource, + global->id, + permissions, + global->type, + global->version, + &global->properties->dict); + } + } + + return (struct pw_registry *)registry_resource; + +error_resource: + pw_log_error(NAME" %p: can't create registry resource: %m", context); + pw_core_resource_errorf(client->core_resource, new_id, + client->recv_seq, res, + "can't create registry resource: %s", spa_strerror(res)); + pw_map_insert_at(&client->objects, new_id, NULL); + pw_core_resource_remove_id(client->core_resource, new_id); + errno = -res; + return NULL; +} + +static void * +core_create_object(void *object, + const char *factory_name, + uint32_t type, + uint32_t version, + const struct spa_dict *props, + size_t user_data_size) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_impl_factory *factory; + void *obj; + struct pw_properties *properties; + struct pw_context *context = client->context; + uint32_t new_id = user_data_size; + int res; + + factory = pw_context_find_factory(context, factory_name); + if (factory == NULL || factory->global == NULL) + goto error_no_factory; + + if (!PW_PERM_IS_R(pw_global_get_permissions(factory->global, client))) + goto error_no_factory; + + if (factory->info.type != type) + goto error_type; + + if (factory->info.version < version) + goto error_version; + + if (props) { + properties = pw_properties_new_dict(props); + if (properties == NULL) + goto error_properties; + } else + properties = NULL; + + /* error will be posted */ + obj = pw_impl_factory_create_object(factory, resource, type, version, properties, new_id); + if (obj == NULL) + goto error_create_failed; + + return 0; + +error_no_factory: + res = -ENOENT; + pw_log_error(NAME" %p: can't find factory '%s'", context, factory_name); + pw_resource_errorf(resource, res, "unknown factory name %s", factory_name); + goto error_exit; +error_version: +error_type: + res = -EPROTO; + pw_log_error(NAME" %p: invalid resource type/version", context); + pw_resource_errorf(resource, res, "wrong resource type/version"); + goto error_exit; +error_properties: + res = -errno; + pw_log_error(NAME" %p: can't create properties: %m", context); + pw_resource_errorf(resource, res, "can't create properties: %s", spa_strerror(res)); + goto error_exit; +error_create_failed: + res = -errno; + goto error_exit; +error_exit: + pw_map_insert_at(&client->objects, new_id, NULL); + pw_core_resource_remove_id(client->core_resource, new_id); + errno = -res; + return NULL; +} + +static int core_destroy(void *object, void *proxy) +{ + struct pw_resource *resource = object; + struct pw_impl_client *client = resource->client; + struct pw_impl_core *this = client->core; + struct pw_resource *r = proxy; + pw_log_debug(NAME" %p: destroy resource %p from client %p", this, r, client); + pw_resource_destroy(r); + return 0; +} + +static const struct pw_core_methods core_methods = { + PW_VERSION_CORE_METHODS, + .hello = core_hello, + .sync = core_sync, + .pong = core_pong, + .error = core_error, + .get_registry = core_get_registry, + .create_object = core_create_object, + .destroy = core_destroy, +}; + +SPA_EXPORT +struct pw_impl_core *pw_context_create_core(struct pw_context *context, + struct pw_properties *properties, + size_t user_data_size) +{ + struct pw_impl_core *this; + const char *name; + int res; + + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + return NULL; + + this = calloc(1, sizeof(*this) + user_data_size); + if (this == NULL) { + res = -errno; + goto error_exit; + }; + + this->context = context; + this->properties = properties; + + if ((name = pw_properties_get(properties, PW_KEY_CORE_NAME)) == NULL) { + pw_properties_setf(properties, + PW_KEY_CORE_NAME, "pipewire-%s-%d", + pw_get_user_name(), getpid()); + name = pw_properties_get(properties, PW_KEY_CORE_NAME); + } + + this->info.user_name = pw_get_user_name(); + this->info.host_name = pw_get_host_name(); + this->info.version = pw_get_library_version(); + srandom(time(NULL)); + this->info.cookie = random(); + this->info.name = name; + spa_hook_list_init(&this->listener_list); + + if (user_data_size > 0) + this->user_data = SPA_MEMBER(this, sizeof(*this), void); + + pw_log_debug(NAME" %p: new %s", this, name); + + return this; + +error_exit: + if (properties) + pw_properties_free(properties); + errno = -res; + return NULL; +} + +SPA_EXPORT +struct pw_impl_core *pw_context_get_default_core(struct pw_context *context) +{ + return context->core; +} + +SPA_EXPORT +void pw_impl_core_destroy(struct pw_impl_core *core) +{ + pw_log_debug(NAME" %p: destroy", core); + pw_impl_core_emit_destroy(core); + + if (core->registered) + spa_list_remove(&core->link); + + if (core->global) { + spa_hook_remove(&core->global_listener); + pw_global_destroy(core->global); + } + + pw_impl_core_emit_free(core); + pw_log_debug(NAME" %p: free", core); + + pw_properties_free(core->properties); + + free(core); +} + +static void core_unbind_func(void *data) +{ + struct pw_resource *resource = data; + if (resource->id == 0) + resource->client->core_resource = NULL; + spa_list_remove(&resource->link); +} + +static const struct pw_resource_events core_resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = core_unbind_func, +}; + +static int +global_bind(void *_data, + struct pw_impl_client *client, + uint32_t permissions, + uint32_t version, + uint32_t id) +{ + struct pw_impl_core *this = _data; + struct pw_global *global = this->global; + struct pw_resource *resource; + struct resource_data *data; + int res; + + resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); + if (resource == NULL) { + res = -errno; + goto error; + } + + data = pw_resource_get_user_data(resource); + + pw_resource_add_listener(resource, + &data->resource_listener, + &core_resource_events, + resource); + pw_resource_add_object_listener(resource, + &data->object_listener, + &core_methods, resource); + + spa_list_append(&global->resource_list, &resource->link); + pw_resource_set_bound_id(resource, global->id); + + if (resource->id == 0) { + client->core_resource = resource; + } + else { + this->info.change_mask = PW_CORE_CHANGE_MASK_ALL; + pw_core_resource_info(resource, &this->info); + this->info.change_mask = 0; + } + + pw_log_debug(NAME" %p: bound to %d", this, resource->id); + + return 0; + +error: + pw_log_error(NAME" %p: can't create resource: %m", this); + return res; +} + +static void global_destroy(void *object) +{ + struct pw_impl_core *core = object; + spa_hook_remove(&core->global_listener); + core->global = NULL; + pw_impl_core_destroy(core); +} + +static const struct pw_global_events global_events = { + PW_VERSION_GLOBAL_EVENTS, + .destroy = global_destroy, +}; + +SPA_EXPORT +const struct pw_properties *pw_impl_core_get_properties(struct pw_impl_core *core) +{ + return core->properties; +} + +SPA_EXPORT +int pw_impl_core_update_properties(struct pw_impl_core *core, const struct spa_dict *dict) +{ + struct pw_resource *resource; + int changed; + + changed = pw_properties_update(core->properties, dict); + core->info.props = &core->properties->dict; + + pw_log_debug(NAME" %p: updated %d properties", core, changed); + + if (!changed) + return 0; + + core->info.change_mask |= PW_CORE_CHANGE_MASK_PROPS; + if (core->global) + spa_list_for_each(resource, &core->global->resource_list, link) + pw_core_resource_info(resource, &core->info); + core->info.change_mask = 0; + + return changed; +} + +SPA_EXPORT +int pw_impl_core_register(struct pw_impl_core *core, + struct pw_properties *properties) +{ + struct pw_context *context = core->context; + int res; + const char *keys[] = { + PW_KEY_USER_NAME, + PW_KEY_HOST_NAME, + PW_KEY_CORE_NAME, + PW_KEY_CORE_VERSION, + NULL + }; + + if (core->registered) + goto error_existed; + + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + return -errno; + + pw_properties_update_keys(properties, &core->properties->dict, keys); + + core->global = pw_global_new(context, + PW_TYPE_INTERFACE_Core, + PW_VERSION_CORE, + properties, + global_bind, + core); + if (core->global == NULL) + return -errno; + + spa_list_append(&context->core_impl_list, &core->link); + core->registered = true; + + core->info.id = core->global->id; + pw_properties_setf(core->properties, PW_KEY_OBJECT_ID, "%d", core->info.id); + core->info.props = &core->properties->dict; + + pw_impl_core_emit_initialized(core); + + pw_global_add_listener(core->global, &core->global_listener, &global_events, core); + pw_global_register(core->global); + + return 0; +error_existed: + res = -EEXIST; + goto error_exit; + +error_exit: + if (properties) + pw_properties_free(properties); + return res; +} + +SPA_EXPORT +void *pw_impl_core_get_user_data(struct pw_impl_core *core) +{ + return core->user_data; +} + +SPA_EXPORT +struct pw_global *pw_impl_core_get_global(struct pw_impl_core *core) +{ + return core->global; +} + +SPA_EXPORT +void pw_impl_core_add_listener(struct pw_impl_core *core, + struct spa_hook *listener, + const struct pw_impl_core_events *events, + void *data) +{ + spa_hook_list_append(&core->listener_list, listener, events, data); +} diff --git a/src/pipewire/impl-core.h b/src/pipewire/impl-core.h new file mode 100644 index 000000000..7e9b8e85b --- /dev/null +++ b/src/pipewire/impl-core.h @@ -0,0 +1,94 @@ +/* PipeWire + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIPEWIRE_IMPL_CORE_H +#define PIPEWIRE_IMPL_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \class pw_impl_core + * + * \brief PipeWire core interface. + * + * The core is used to make objects on demand. + */ +struct pw_impl_core; + +#include +#include +#include +#include + +/** Factory events, listen to them with \ref pw_impl_core_add_listener */ +struct pw_impl_core_events { +#define PW_VERSION_IMPL_CORE_EVENTS 0 + uint32_t version; + + /** the core is destroyed */ + void (*destroy) (void *data); + /** the core is freed */ + void (*free) (void *data); + /** the core is initialized */ + void (*initialized) (void *data); +}; + +struct pw_impl_core *pw_context_create_core(struct pw_context *context, + struct pw_properties *properties, + size_t user_data_size); + +/* get the default core in a context */ +struct pw_impl_core *pw_context_get_default_core(struct pw_context *context); + +/** Get the core properties */ +const struct pw_properties *pw_impl_core_get_properties(struct pw_impl_core *core); + +/** Get the core information */ +const struct pw_core_info *pw_impl_core_get_info(struct pw_impl_core *core); + +/** Update the core properties */ +int pw_impl_core_update_properties(struct pw_impl_core *core, const struct spa_dict *dict); + +int pw_impl_core_register(struct pw_impl_core *core, + struct pw_properties *properties); + +void pw_impl_core_destroy(struct pw_impl_core *core); + +void *pw_impl_core_get_user_data(struct pw_impl_core *core); + +/** Get the global of this core */ +struct pw_global *pw_impl_core_get_global(struct pw_impl_core *core); + +/** Add an event listener */ +void pw_impl_core_add_listener(struct pw_impl_core *core, + struct spa_hook *listener, + const struct pw_impl_core_events *events, + void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_IMPL_CORE_H */ diff --git a/src/pipewire/impl.h b/src/pipewire/impl.h index fce1cac73..f500a36a8 100644 --- a/src/pipewire/impl.h +++ b/src/pipewire/impl.h @@ -38,6 +38,7 @@ struct pw_resource; #include #include +#include #include #include #include diff --git a/src/pipewire/meson.build b/src/pipewire/meson.build index 0d4552d05..c65e283bf 100644 --- a/src/pipewire/meson.build +++ b/src/pipewire/meson.build @@ -1,6 +1,7 @@ pipewire_headers = [ 'array.h', 'buffers.h', + 'impl-core.h', 'impl-client.h', 'client.h', 'context.h', @@ -42,6 +43,7 @@ pipewire_headers = [ pipewire_sources = [ 'buffers.c', + 'impl-core.c', 'impl-client.c', 'context.c', 'control.c', diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c index d6d6a95e0..db808a4b4 100644 --- a/src/pipewire/pipewire.c +++ b/src/pipewire/pipewire.c @@ -496,63 +496,6 @@ const char *pw_get_client_name(void) } } -/** Fill connectremote properties - * \param properties a \ref pw_properties - * - * Fill \a properties with a set of default properties for connecting to a PipeWire instance. - * - * \memberof pw_pipewire - */ -SPA_EXPORT -void pw_fill_connect_properties(struct pw_context *context, struct pw_properties *properties) -{ - const char *val; - - if (!pw_properties_get(properties, PW_KEY_APP_NAME)) - pw_properties_set(properties, PW_KEY_APP_NAME, pw_get_client_name()); - - if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_BINARY)) - pw_properties_set(properties, PW_KEY_APP_PROCESS_BINARY, pw_get_prgname()); - - if (!pw_properties_get(properties, PW_KEY_APP_LANGUAGE)) { - pw_properties_set(properties, PW_KEY_APP_LANGUAGE, getenv("LANG")); - } - if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_ID)) { - pw_properties_setf(properties, PW_KEY_APP_PROCESS_ID, "%zd", (size_t) getpid()); - } - if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_USER)) - pw_properties_set(properties, PW_KEY_APP_PROCESS_USER, pw_get_user_name()); - - if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_HOST)) - pw_properties_set(properties, PW_KEY_APP_PROCESS_HOST, pw_get_host_name()); - - if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_SESSION_ID)) { - pw_properties_set(properties, PW_KEY_APP_PROCESS_SESSION_ID, - getenv("XDG_SESSION_ID")); - } - if (!pw_properties_get(properties, PW_KEY_WINDOW_X11_DISPLAY)) { - pw_properties_set(properties, PW_KEY_WINDOW_X11_DISPLAY, - getenv("DISPLAY")); - } - pw_properties_set(properties, PW_KEY_CORE_VERSION, context->info.version); - pw_properties_set(properties, PW_KEY_CORE_NAME, context->info.name); - - if ((val = pw_properties_get(context->properties, PW_KEY_CORE_DAEMON))) - pw_properties_set(properties, PW_KEY_CORE_DAEMON, val); -} - -/** Fill stream properties - * \param properties a \ref pw_properties - * - * Fill \a properties with a set of default stream properties. - * - * \memberof pw_pipewire - */ -SPA_EXPORT -void pw_fill_stream_properties(struct pw_context *context, struct pw_properties *properties) -{ -} - /** Reverse the direction \memberof pw_pipewire */ SPA_EXPORT enum pw_direction pw_direction_reverse(enum pw_direction direction) diff --git a/src/pipewire/pipewire.h b/src/pipewire/pipewire.h index 1fd265819..7a7fd017d 100644 --- a/src/pipewire/pipewire.h +++ b/src/pipewire/pipewire.h @@ -133,12 +133,6 @@ pw_get_host_name(void); const char * pw_get_client_name(void); -void -pw_fill_connect_properties(struct pw_context *context, struct pw_properties *properties); - -void -pw_fill_stream_properties(struct pw_context *context, struct pw_properties *properties); - enum pw_direction pw_direction_reverse(enum pw_direction direction); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index e853eecc8..63c48d8e9 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -95,10 +95,33 @@ struct protocol_compat_v2 { struct pw_map types; }; +#define pw_impl_core_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, struct pw_impl_core_events, m, v, ##__VA_ARGS__) + +#define pw_impl_core_emit_destroy(s) pw_impl_core_emit(s, destroy, 0) +#define pw_impl_core_emit_free(s) pw_impl_core_emit(s, free, 0) +#define pw_impl_core_emit_initialized(s) pw_impl_core_emit(s, initialized, 0) + +struct pw_impl_core { + struct pw_context *context; + struct spa_list link; /**< link in context object core_impl list */ + struct pw_global *global; /**< global object created for this core */ + struct spa_hook global_listener; + + struct pw_properties *properties; /**< core properties */ + struct pw_core_info info; /**< core info */ + + struct spa_hook_list listener_list; + void *user_data; /**< extra user data */ + + unsigned int registered:1; +}; + struct pw_impl_client { + struct pw_impl_core *core; /**< core object */ struct pw_context *context; /**< context object */ - struct spa_list link; /**< link in context object client list */ - struct pw_global *global; /**< global object created for this client */ + + struct spa_list link; /**< link in context object client list */ + struct pw_global *global; /**< global object created for this client */ struct spa_hook global_listener; pw_permission_func_t permission_func; /**< get permissions of an object */ @@ -201,10 +224,7 @@ pw_core_resource_errorf(struct pw_resource *resource, uint32_t id, int seq, struct pw_context { - struct pw_global *global; /**< the global of the context */ - struct spa_hook global_listener; - - struct pw_core_info info; /**< info about the core */ + struct pw_impl_core *core; /**< core object */ struct pw_properties *properties; /**< properties of the context */ @@ -212,6 +232,7 @@ struct pw_context { struct pw_map globals; /**< map of globals */ + struct spa_list core_impl_list; /**< list of core_imp */ struct spa_list protocol_list; /**< list of protocols */ struct spa_list core_list; /**< list of core connections */ struct spa_list registry_resource_list; /**< list of registry resources */ @@ -675,7 +696,7 @@ struct pw_impl_link { struct pw_resource { struct spa_interface impl; /**< object implementation */ - struct pw_context *context; /**< the context object */ + struct pw_context *context; /**< the context object */ struct spa_list link; /**< link in global resource_list */ struct pw_impl_client *client; /**< owner client */ diff --git a/src/pipewire/protocol.h b/src/pipewire/protocol.h index fa202499d..371a963f7 100644 --- a/src/pipewire/protocol.h +++ b/src/pipewire/protocol.h @@ -66,6 +66,8 @@ struct pw_protocol_server { struct spa_list link; /**< link in protocol server_list */ struct pw_protocol *protocol; /**< the owner protocol */ + struct pw_impl_core *core; + struct spa_list client_list; /**< list of clients of this protocol */ void (*destroy) (struct pw_protocol_server *listen); @@ -91,8 +93,10 @@ struct pw_protocol_implementaton { uint32_t version; struct pw_protocol_client * (*new_client) (struct pw_protocol *protocol, + struct pw_core *core, const struct pw_properties *properties); struct pw_protocol_server * (*add_server) (struct pw_protocol *protocol, + struct pw_impl_core *core, const struct pw_properties *properties); }; diff --git a/src/tests/test-context.c b/src/tests/test-context.c index 357bc5296..9fa38cf64 100644 --- a/src/tests/test-context.c +++ b/src/tests/test-context.c @@ -38,7 +38,6 @@ static void test_abi(void) uint32_t version; void (*destroy) (void *data); void (*free) (void *data); - void (*info_changed) (void *data, const struct pw_core_info *info); void (*check_access) (void *data, struct pw_impl_client *client); void (*global_added) (void *data, struct pw_global *global); void (*global_removed) (void *data, struct pw_global *global); @@ -46,7 +45,6 @@ static void test_abi(void) TEST_FUNC(ev, test, destroy); TEST_FUNC(ev, test, free); - TEST_FUNC(ev, test, info_changed); TEST_FUNC(ev, test, check_access); TEST_FUNC(ev, test, global_added); TEST_FUNC(ev, test, global_removed); @@ -63,10 +61,6 @@ static void context_free_error(void *data) { spa_assert_not_reached(); } -static void context_info_changed_error(void *data, const struct pw_core_info *info) -{ - spa_assert_not_reached(); -} static void context_check_access_error(void *data, struct pw_impl_client *client) { spa_assert_not_reached(); @@ -85,7 +79,6 @@ static const struct pw_context_events context_events_error = PW_VERSION_CONTEXT_EVENTS, .destroy = context_destroy_error, .free = context_free_error, - .info_changed = context_info_changed_error, .check_access = context_check_access_error, .global_added = context_global_added_error, .global_removed = context_global_removed_error, @@ -123,7 +116,6 @@ static void test_create(void) struct pw_context *context; struct spa_hook listener = { NULL, }; struct pw_context_events context_events = context_events_error; - struct pw_global *global; int res; loop = pw_main_loop_new(NULL); @@ -131,7 +123,7 @@ static void test_create(void) context = pw_context_new(pw_main_loop_get_loop(loop), pw_properties_new( - PW_KEY_CORE_PROFILE_MODULES, "none", + PW_KEY_CONTEXT_PROFILE_MODULES, "none", NULL), 12); spa_assert(context != NULL); pw_context_add_listener(context, &listener, &context_events, context); @@ -141,19 +133,6 @@ static void test_create(void) /* check user data */ spa_assert(pw_context_get_user_data(context) != NULL); - /* check info */ - spa_assert(pw_context_get_info(context) != NULL); - - /* check global */ - global = pw_context_get_global(context); - spa_assert(global != NULL); - spa_assert(pw_context_find_global(context, 0) == global); - spa_assert(pw_global_get_context(global) == context); - spa_assert(pw_global_get_type(global) == PW_TYPE_INTERFACE_Core); - spa_assert(pw_global_get_version(global) == PW_VERSION_CORE); - spa_assert(pw_global_get_id(global) == 0); - spa_assert(pw_global_get_object(global) == (void*)context); - /* iterate globals */ spa_assert(context_foreach_count == 0); res = pw_context_for_each_global(context, context_foreach, context); @@ -178,15 +157,6 @@ static void test_create(void) pw_main_loop_destroy(loop); } -static int info_changed_count = 0; -static void context_info_changed_count(void *data, const struct pw_core_info *info) -{ - spa_assert(spa_dict_lookup(info->props, "foo") == NULL); - spa_assert(!strcmp(spa_dict_lookup(info->props, "biz"), "buzz")); - spa_assert(!strcmp(spa_dict_lookup(info->props, "buzz"), "frizz")); - info_changed_count++; -} - static void test_properties(void) { struct pw_main_loop *loop; @@ -206,9 +176,6 @@ static void test_properties(void) spa_assert(pw_context_get_user_data(context) == NULL); pw_context_add_listener(context, &listener, &context_events, context); - context_events.info_changed = context_info_changed_count; - spa_assert(info_changed_count == 0); - props = pw_context_get_properties(context); spa_assert(props != NULL); spa_assert(!strcmp(pw_properties_get(props, "foo"), "bar")); @@ -223,8 +190,6 @@ static void test_properties(void) items[2] = SPA_DICT_ITEM_INIT("buzz", "frizz"); pw_context_update_properties(context, &SPA_DICT_INIT(items, 3)); - spa_assert(info_changed_count == 1); - spa_assert(props == pw_context_get_properties(context)); spa_assert(pw_properties_get(props, "foo") == NULL); spa_assert(!strcmp(pw_properties_get(props, "biz"), "buzz")); diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c index 7d148b9b3..c299fe8f0 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c @@ -1632,8 +1632,7 @@ int main(int argc, char *argv[]) { struct data data = { 0 }; struct pw_loop *l; - const struct pw_core_info *info; - char *error, args[128]; + char *error; pw_init(&argc, &argv); @@ -1646,16 +1645,15 @@ int main(int argc, char *argv[]) pw_map_init(&data.vars, 64, 16); data.context = pw_context_new(l, pw_properties_new(PW_KEY_CORE_DAEMON, "1", NULL), 0); - info = pw_context_get_info(data.context); pw_context_load_module(data.context, "libpipewire-module-link-factory", NULL, NULL); pw_loop_add_io(l, STDIN_FILENO, SPA_IO_IN|SPA_IO_HUP, false, do_input, &data); - fprintf(stdout, "Welcome to PipeWire \"%s\" version %s. Type 'help' for usage.\n", info->name, info->version); + fprintf(stdout, "Welcome to PipeWire version %s. Type 'help' for usage.\n", + pw_get_library_version()); - snprintf(args, sizeof(args), "%s", info->name); - do_connect(&data, "connect", args, &error); + do_connect(&data, "connect", "internal", &error); pw_main_loop_run(data.loop);