From bdce3b4de5de559d60b614be9178f85c335d6338 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis <george.kiagiadakis@collabora.com> Date: Thu, 22 Aug 2019 20:04:39 +0300 Subject: [PATCH] proxy: refactor the proxy class to hide pipewire API and make things easier --- lib/wp/proxy-link.c | 122 +------- lib/wp/proxy-link.h | 13 +- lib/wp/proxy-node.c | 122 +------- lib/wp/proxy-node.h | 13 +- lib/wp/proxy-port.c | 145 ++------- lib/wp/proxy-port.h | 16 +- lib/wp/proxy.c | 620 ++++++++++++++++++++++++++++++++++----- lib/wp/proxy.h | 64 +++- lib/wp/remote-pipewire.c | 131 +++++---- lib/wp/remote-pipewire.h | 10 +- 10 files changed, 770 insertions(+), 486 deletions(-) diff --git a/lib/wp/proxy-link.c b/lib/wp/proxy-link.c index c7b5adee..ff99fbc9 100644 --- a/lib/wp/proxy-link.c +++ b/lib/wp/proxy-link.c @@ -6,46 +6,29 @@ * SPDX-License-Identifier: MIT */ -#include "error.h" #include "proxy-link.h" + #include <pipewire/pipewire.h> struct _WpProxyLink { WpProxy parent; - /* The task to signal the proxy is initialized */ - GTask *init_task; - /* The link proxy listener */ struct spa_hook listener; - - /* The link info */ - struct pw_link_info *info; }; -static void wp_proxy_link_async_initable_init (gpointer iface, - gpointer iface_data); - -G_DEFINE_TYPE_WITH_CODE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, - wp_proxy_link_async_initable_init)) +G_DEFINE_TYPE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY) static void link_event_info(void *data, const struct pw_link_info *info) { - WpProxyLink *self = data; - - /* Make sure the task is valid */ - if (!self->init_task) - return; + WpProxy *proxy = WP_PROXY (data); - /* Update the link info */ - self->info = pw_link_info_update(self->info, info); - - /* Finish the creation of the proxy */ - g_task_return_boolean (self->init_task, TRUE); - g_clear_object (&self->init_task); + wp_proxy_update_native_info (proxy, info, + (WpProxyNativeInfoUpdate) pw_link_info_update, + (GDestroyNotify) pw_link_info_free); + wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); } static const struct pw_link_proxy_events link_events = { @@ -54,101 +37,22 @@ static const struct pw_link_proxy_events link_events = { }; static void -wp_proxy_link_finalize (GObject * object) -{ - WpProxyLink *self = WP_PROXY_LINK(object); - - /* Destroy the init task */ - g_clear_object (&self->init_task); - - /* Clear the info */ - if (self->info) { - pw_link_info_free(self->info); - self->info = NULL; - } - - G_OBJECT_CLASS (wp_proxy_link_parent_class)->finalize (object); -} - -static void -wp_proxy_link_destroy (WpProxy * proxy) -{ - WpProxyLink *self = WP_PROXY_LINK(proxy); - GError *error = NULL; - - /* Return error if the pipewire destruction happened while the async creation - * of this proxy link object has not finished */ - if (self->init_task) { - g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, - "pipewire link proxy destroyed before finishing"); - g_task_return_error (self->init_task, error); - g_clear_object (&self->init_task); - } -} - -static void -wp_proxy_link_init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) -{ - WpProxyLink *self = WP_PROXY_LINK(initable); - WpProxy *wp_proxy = WP_PROXY(initable); - struct pw_link_proxy *proxy = NULL; - - /* Create the async task */ - self->init_task = g_task_new (initable, cancellable, callback, data); - - /* Get the proxy from the base class */ - proxy = wp_proxy_get_pw_proxy(wp_proxy); - - /* Add the link proxy listener */ - pw_link_proxy_add_listener(proxy, &self->listener, &link_events, self); -} - -static void -wp_proxy_link_async_initable_init (gpointer iface, gpointer iface_data) +wp_proxy_link_init (WpProxyLink * self) { - GAsyncInitableIface *ai_iface = iface; - - /* Only set the init_async */ - ai_iface->init_async = wp_proxy_link_init_async; } static void -wp_proxy_link_init (WpProxyLink * self) +wp_proxy_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { + WpProxyLink *self = WP_PROXY_LINK (proxy); + pw_link_proxy_add_listener ((struct pw_link_proxy *) pw_proxy, + &self->listener, &link_events, self); } static void wp_proxy_link_class_init (WpProxyLinkClass * klass) { - GObjectClass *object_class = (GObjectClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass; - object_class->finalize = wp_proxy_link_finalize; - - proxy_class->destroy = wp_proxy_link_destroy; -} - -void -wp_proxy_link_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data) -{ - g_async_initable_new_async ( - WP_TYPE_PROXY_LINK, G_PRIORITY_DEFAULT, NULL, callback, user_data, - "global-id", global_id, - "pw-proxy", proxy, - NULL); -} - -WpProxyLink * -wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, GError **error) -{ - GAsyncInitable *ai = G_ASYNC_INITABLE(initable); - return WP_PROXY_LINK(g_async_initable_new_finish(ai, res, error)); -} - -const struct pw_link_info * -wp_proxy_link_get_info (WpProxyLink * self) -{ - return self->info; + proxy_class->pw_proxy_created = wp_proxy_link_pw_proxy_created; } diff --git a/lib/wp/proxy-link.h b/lib/wp/proxy-link.h index cd965f50..1a7521ba 100644 --- a/lib/wp/proxy-link.h +++ b/lib/wp/proxy-link.h @@ -9,7 +9,6 @@ #ifndef __WIREPLUMBER_PROXY_LINK_H__ #define __WIREPLUMBER_PROXY_LINK_H__ -#include "core.h" #include "proxy.h" G_BEGIN_DECLS @@ -17,12 +16,12 @@ G_BEGIN_DECLS #define WP_TYPE_PROXY_LINK (wp_proxy_link_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyLink, wp_proxy_link, WP, PROXY_LINK, WpProxy) -void wp_proxy_link_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data); -WpProxyLink *wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, - GError **error); - -const struct pw_link_info *wp_proxy_link_get_info (WpProxyLink * self); +static inline const struct pw_link_info * +wp_proxy_link_get_info (WpProxyLink * self) +{ + return (const struct pw_link_info *) + wp_proxy_get_native_info (WP_PROXY (self)); +} G_END_DECLS diff --git a/lib/wp/proxy-node.c b/lib/wp/proxy-node.c index 46569e31..86443327 100644 --- a/lib/wp/proxy-node.c +++ b/lib/wp/proxy-node.c @@ -6,46 +6,29 @@ * SPDX-License-Identifier: MIT */ -#include "error.h" #include "proxy-node.h" + #include <pipewire/pipewire.h> struct _WpProxyNode { WpProxy parent; - /* The task to signal the proxy is initialized */ - GTask *init_task; - /* The node proxy listener */ struct spa_hook listener; - - /* The node info */ - struct pw_node_info *info; }; -static void wp_proxy_node_async_initable_init (gpointer iface, - gpointer iface_data); - -G_DEFINE_TYPE_WITH_CODE (WpProxyNode, wp_proxy_node, WP_TYPE_PROXY, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, - wp_proxy_node_async_initable_init)) +G_DEFINE_TYPE (WpProxyNode, wp_proxy_node, WP_TYPE_PROXY) static void node_event_info(void *data, const struct pw_node_info *info) { - WpProxyNode *self = data; - - /* Make sure the task is valid */ - if (!self->init_task) - return; + WpProxy *proxy = WP_PROXY (data); - /* Update the node info */ - self->info = pw_node_info_update(self->info, info); - - /* Finish the creation of the proxy */ - g_task_return_boolean (self->init_task, TRUE); - g_clear_object (&self->init_task); + wp_proxy_update_native_info (proxy, info, + (WpProxyNativeInfoUpdate) pw_node_info_update, + (GDestroyNotify) pw_node_info_free); + wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); } static const struct pw_node_proxy_events node_events = { @@ -54,101 +37,22 @@ static const struct pw_node_proxy_events node_events = { }; static void -wp_proxy_node_finalize (GObject * object) -{ - WpProxyNode *self = WP_PROXY_NODE(object); - - /* Destroy the init task */ - g_clear_object (&self->init_task); - - /* Clear the info */ - if (self->info) { - pw_node_info_free(self->info); - self->info = NULL; - } - - G_OBJECT_CLASS (wp_proxy_node_parent_class)->finalize (object); -} - -static void -wp_proxy_node_destroy (WpProxy * proxy) -{ - WpProxyNode *self = WP_PROXY_NODE(proxy); - GError *error = NULL; - - /* Return error if the pipewire destruction happened while the async creation - * of this proxy node object has not finished */ - if (self->init_task) { - g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, - "pipewire node proxy destroyed before finishing"); - g_task_return_error (self->init_task, error); - g_clear_object (&self->init_task); - } -} - -static void -wp_proxy_node_init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) -{ - WpProxyNode *self = WP_PROXY_NODE(initable); - WpProxy *wp_proxy = WP_PROXY(initable); - struct pw_node_proxy *proxy = NULL; - - /* Create the async task */ - self->init_task = g_task_new (initable, cancellable, callback, data); - - /* Get the proxy from the base class */ - proxy = wp_proxy_get_pw_proxy(wp_proxy); - - /* Add the node proxy listener */ - pw_node_proxy_add_listener(proxy, &self->listener, &node_events, self); -} - -static void -wp_proxy_node_async_initable_init (gpointer iface, gpointer iface_data) +wp_proxy_node_init (WpProxyNode * self) { - GAsyncInitableIface *ai_iface = iface; - - /* Only set the init_async */ - ai_iface->init_async = wp_proxy_node_init_async; } static void -wp_proxy_node_init (WpProxyNode * self) +wp_proxy_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { + WpProxyNode *self = WP_PROXY_NODE (proxy); + pw_node_proxy_add_listener ((struct pw_node_proxy *) pw_proxy, + &self->listener, &node_events, self); } static void wp_proxy_node_class_init (WpProxyNodeClass * klass) { - GObjectClass *object_class = (GObjectClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass; - object_class->finalize = wp_proxy_node_finalize; - - proxy_class->destroy = wp_proxy_node_destroy; -} - -void -wp_proxy_node_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data) -{ - g_async_initable_new_async ( - WP_TYPE_PROXY_NODE, G_PRIORITY_DEFAULT, NULL, callback, user_data, - "global-id", global_id, - "pw-proxy", proxy, - NULL); -} - -WpProxyNode * -wp_proxy_node_new_finish(GObject *initable, GAsyncResult *res, GError **error) -{ - GAsyncInitable *ai = G_ASYNC_INITABLE(initable); - return WP_PROXY_NODE(g_async_initable_new_finish(ai, res, error)); -} - -const struct pw_node_info * -wp_proxy_node_get_info (WpProxyNode * self) -{ - return self->info; + proxy_class->pw_proxy_created = wp_proxy_node_pw_proxy_created; } diff --git a/lib/wp/proxy-node.h b/lib/wp/proxy-node.h index 12feba0b..7d6050dc 100644 --- a/lib/wp/proxy-node.h +++ b/lib/wp/proxy-node.h @@ -9,7 +9,6 @@ #ifndef __WIREPLUMBER_PROXY_NODE_H__ #define __WIREPLUMBER_PROXY_NODE_H__ -#include "core.h" #include "proxy.h" G_BEGIN_DECLS @@ -17,12 +16,12 @@ G_BEGIN_DECLS #define WP_TYPE_PROXY_NODE (wp_proxy_node_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyNode, wp_proxy_node, WP, PROXY_NODE, WpProxy) -void wp_proxy_node_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data); -WpProxyNode *wp_proxy_node_new_finish(GObject *initable, GAsyncResult *res, - GError **error); - -const struct pw_node_info *wp_proxy_node_get_info (WpProxyNode * self); +static inline const struct pw_node_info * +wp_proxy_node_get_info (WpProxyNode * self) +{ + return (const struct pw_node_info *) + wp_proxy_get_native_info (WP_PROXY (self)); +} G_END_DECLS diff --git a/lib/wp/proxy-port.c b/lib/wp/proxy-port.c index c796e5a2..5cc68ba2 100644 --- a/lib/wp/proxy-port.c +++ b/lib/wp/proxy-port.c @@ -6,8 +6,8 @@ * SPDX-License-Identifier: MIT */ -#include "error.h" #include "proxy-port.h" + #include <pipewire/pipewire.h> #include <spa/param/audio/format-utils.h> @@ -15,46 +15,33 @@ struct _WpProxyPort { WpProxy parent; - /* The task to signal the proxy is initialized */ - GTask *init_task; - /* The port proxy listener */ struct spa_hook listener; - /* The port info */ - struct pw_port_info *info; - /* The port format */ uint32_t media_type; uint32_t media_subtype; struct spa_audio_info_raw format; }; -static void wp_proxy_port_async_initable_init (gpointer iface, - gpointer iface_data); - -G_DEFINE_TYPE_WITH_CODE (WpProxyPort, wp_proxy_port, WP_TYPE_PROXY, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, - wp_proxy_port_async_initable_init)) +G_DEFINE_TYPE (WpProxyPort, wp_proxy_port, WP_TYPE_PROXY) static void port_event_info(void *data, const struct pw_port_info *info) { - WpProxyPort *self = data; + WpProxy *proxy = WP_PROXY (data); - /* Update the port info */ - self->info = pw_port_info_update(self->info, info); + wp_proxy_update_native_info (proxy, info, + (WpProxyNativeInfoUpdate) pw_port_info_update, + (GDestroyNotify) pw_port_info_free); + wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO); } static void port_event_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { - WpProxyPort *self = data; - - /* Make sure the task is valid */ - if (!self->init_task) - return; + WpProxyPort *self = WP_PROXY_PORT (data); /* Only handle EnumFormat */ if (id != SPA_PARAM_EnumFormat) @@ -64,17 +51,14 @@ port_event_param(void *data, int seq, uint32_t id, uint32_t index, spa_format_parse(param, &self->media_type, &self->media_subtype); /* Only handle raw audio formats for now */ - if (self->media_type != SPA_MEDIA_TYPE_audio || - self->media_subtype != SPA_MEDIA_SUBTYPE_raw) - return; - - /* Parse the raw audio format */ - spa_pod_fixate((struct spa_pod*)param); - spa_format_audio_raw_parse(param, &self->format); + if (self->media_type == SPA_MEDIA_TYPE_audio && + self->media_subtype == SPA_MEDIA_SUBTYPE_raw) { + /* Parse the raw audio format */ + spa_pod_fixate ((struct spa_pod *) param); + spa_format_audio_raw_parse (param, &self->format); + } - /* Finish the creation of the proxy */ - g_task_return_boolean (self->init_task, TRUE); - g_clear_object (&self->init_task); + wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_PORT_FEATURE_FORMAT); } static const struct pw_port_proxy_events port_events = { @@ -84,107 +68,40 @@ static const struct pw_port_proxy_events port_events = { }; static void -wp_proxy_port_finalize (GObject * object) -{ - WpProxyPort *self = WP_PROXY_PORT(object); - - /* Destroy the init task */ - g_clear_object (&self->init_task); - - /* Clear the indo */ - if (self->info) { - pw_port_info_free(self->info); - self->info = NULL; - } - - G_OBJECT_CLASS (wp_proxy_port_parent_class)->finalize (object); -} - -static void -wp_proxy_port_destroy (WpProxy * proxy) +wp_proxy_port_init (WpProxyPort * self) { - WpProxyPort *self = WP_PROXY_PORT(proxy); - GError *error = NULL; - - /* Return error if the pipewire destruction happened while the async creation - * of this proxy port object has not finished */ - if (self->init_task) { - g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, - "pipewire port proxy destroyed before finishing"); - g_task_return_error (self->init_task, error); - g_clear_object (&self->init_task); - } } static void -wp_proxy_port_init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +wp_proxy_port_augment (WpProxy * proxy, WpProxyFeatures features) { - WpProxyPort *self = WP_PROXY_PORT(initable); - WpProxy *wp_proxy = WP_PROXY(initable); - struct pw_port_proxy *proxy = NULL; + /* call the default implementation to ensure we have a proxy, if necessary */ + WP_PROXY_CLASS (wp_proxy_port_parent_class)->augment (proxy, features); - /* Create the async task */ - self->init_task = g_task_new (initable, cancellable, callback, data); + if (features & WP_PROXY_PORT_FEATURE_FORMAT) { + struct pw_proxy *pwp = wp_proxy_get_pw_proxy (proxy); + g_return_if_fail (pwp != NULL); - /* Get the proxy from the base class */ - proxy = wp_proxy_get_pw_proxy(wp_proxy); - - /* Add the port proxy listener */ - pw_port_proxy_add_listener(proxy, &self->listener, &port_events, self); - - /* Emit the EnumFormat param */ - pw_port_proxy_enum_params((struct pw_port_proxy*)proxy, 0, - SPA_PARAM_EnumFormat, 0, -1, NULL); -} - -static void -wp_proxy_port_async_initable_init (gpointer iface, gpointer iface_data) -{ - GAsyncInitableIface *ai_iface = iface; - - /* Only set the init_async */ - ai_iface->init_async = wp_proxy_port_init_async; + pw_port_proxy_enum_params ((struct pw_port_proxy *) pwp, 0, + SPA_PARAM_EnumFormat, 0, -1, NULL); + } } static void -wp_proxy_port_init (WpProxyPort * self) +wp_proxy_port_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) { + WpProxyPort *self = WP_PROXY_PORT (proxy); + pw_port_proxy_add_listener ((struct pw_port_proxy *) pw_proxy, + &self->listener, &port_events, self); } static void wp_proxy_port_class_init (WpProxyPortClass * klass) { - GObjectClass *object_class = (GObjectClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass; - object_class->finalize = wp_proxy_port_finalize; - - proxy_class->destroy = wp_proxy_port_destroy; -} - -void -wp_proxy_port_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data) -{ - g_async_initable_new_async ( - WP_TYPE_PROXY_PORT, G_PRIORITY_DEFAULT, NULL, callback, user_data, - "global-id", global_id, - "pw-proxy", proxy, - NULL); -} - -WpProxyPort * -wp_proxy_port_new_finish(GObject *initable, GAsyncResult *res, GError **error) -{ - GAsyncInitable *ai = G_ASYNC_INITABLE(initable); - return WP_PROXY_PORT(g_async_initable_new_finish(ai, res, error)); -} - -const struct pw_port_info * -wp_proxy_port_get_info (WpProxyPort * self) -{ - return self->info; + proxy_class->augment = wp_proxy_port_augment; + proxy_class->pw_proxy_created = wp_proxy_port_pw_proxy_created; } const struct spa_audio_info_raw * diff --git a/lib/wp/proxy-port.h b/lib/wp/proxy-port.h index 718c8da6..79261899 100644 --- a/lib/wp/proxy-port.h +++ b/lib/wp/proxy-port.h @@ -9,20 +9,24 @@ #ifndef __WIREPLUMBER_PROXY_PORT_H__ #define __WIREPLUMBER_PROXY_PORT_H__ -#include "core.h" #include "proxy.h" G_BEGIN_DECLS +typedef enum { /*< flags >*/ + WP_PROXY_PORT_FEATURE_FORMAT = (WP_PROXY_FEATURE_LAST << 0), +} WpProxyPortFeatures; + #define WP_TYPE_PROXY_PORT (wp_proxy_port_get_type ()) G_DECLARE_FINAL_TYPE (WpProxyPort, wp_proxy_port, WP, PROXY_PORT, WpProxy) -void wp_proxy_port_new (guint global_id, gpointer proxy, - GAsyncReadyCallback callback, gpointer user_data); -WpProxyPort *wp_proxy_port_new_finish(GObject *initable, GAsyncResult *res, - GError **error); +static inline const struct pw_port_info * +wp_proxy_port_get_info (WpProxyPort * self) +{ + return (const struct pw_port_info *) + wp_proxy_get_native_info (WP_PROXY (self)); +} -const struct pw_port_info *wp_proxy_port_get_info (WpProxyPort * self); const struct spa_audio_info_raw *wp_proxy_port_get_format (WpProxyPort * self); G_END_DECLS diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c index e3d612a4..79515792 100644 --- a/lib/wp/proxy.c +++ b/lib/wp/proxy.c @@ -2,46 +2,130 @@ * * Copyright © 2019 Collabora Ltd. * @author Julian Bouzas <julian.bouzas@collabora.com> + * @author George Kiagiadakis <george.kiagiadakis@collabora.com> * * SPDX-License-Identifier: MIT */ -#include <pipewire/pipewire.h> - #include "proxy.h" +#include "error.h" +#include "remote-pipewire.h" +#include "wpenums.h" + +#include "proxy-link.h" +#include "proxy-node.h" +#include "proxy-port.h" + +#include <pipewire/pipewire.h> +#include <spa/debug/types.h> typedef struct _WpProxyPrivate WpProxyPrivate; struct _WpProxyPrivate { - /* The global id */ - guint global_id; + /* properties */ + GWeakRef remote; + + guint32 global_id; + guint32 global_perm; + WpProperties *global_props; + + guint32 iface_type; + guint32 iface_version; - /* The proxy */ - struct pw_proxy *proxy; + struct pw_proxy *pw_proxy; + gpointer native_info; + GDestroyNotify native_info_destroy; /* The proxy listener */ struct spa_hook listener; + + /* augment state */ + WpProxyFeatures ft_ready; + WpProxyFeatures ft_wanted; + GTask *task; }; enum { PROP_0, + PROP_REMOTE, PROP_GLOBAL_ID, - PROP_PROXY, + PROP_GLOBAL_PERMISSIONS, + PROP_GLOBAL_PROPERTIES, + PROP_INTERFACE_TYPE, + PROP_INTERFACE_NAME, + PROP_INTERFACE_QUARK, + PROP_INTERFACE_VERSION, + PROP_PW_PROXY, + PROP_NATIVE_INFO, + PROP_FEATURES, }; enum { - SIGNAL_DONE, + SIGNAL_PW_PROXY_CREATED, + SIGNAL_PW_PROXY_DESTROYED, LAST_SIGNAL, }; static guint wp_proxy_signals[LAST_SIGNAL] = { 0 }; -static void wp_proxy_async_initable_init (gpointer iface, gpointer iface_data); +G_DEFINE_TYPE_WITH_PRIVATE (WpProxy, wp_proxy, G_TYPE_OBJECT) + +G_DEFINE_QUARK (core, wp_proxy_core) +G_DEFINE_QUARK (registry, wp_proxy_registry) +G_DEFINE_QUARK (node, wp_proxy_node) +G_DEFINE_QUARK (port, wp_proxy_port) +G_DEFINE_QUARK (factory, wp_proxy_factory) +G_DEFINE_QUARK (link, wp_proxy_link) +G_DEFINE_QUARK (client, wp_proxy_client) +G_DEFINE_QUARK (module, wp_proxy_module) +G_DEFINE_QUARK (device, wp_proxy_device) +G_DEFINE_QUARK (client-node, wp_proxy_client_node) + +static struct { + /* the pipewire interface type */ + guint32 pw_type; + /* the minimum interface version that the remote object must support */ + guint32 req_version; + /* the _get_type() function of the subclass */ + GType (*get_type) (void); + /* a function returning a quark that identifies the interface */ + GQuark (*get_quark) (void); +} types_assoc[] = { + { PW_TYPE_INTERFACE_Core, 0, wp_proxy_get_type, wp_proxy_core_quark }, + { PW_TYPE_INTERFACE_Registry, 0, wp_proxy_get_type, wp_proxy_registry_quark }, + { PW_TYPE_INTERFACE_Node, 0, wp_proxy_node_get_type, wp_proxy_node_quark }, + { PW_TYPE_INTERFACE_Port, 0, wp_proxy_port_get_type, wp_proxy_port_quark }, + { PW_TYPE_INTERFACE_Factory, 0, wp_proxy_get_type, wp_proxy_factory_quark }, + { PW_TYPE_INTERFACE_Link, 0, wp_proxy_link_get_type, wp_proxy_link_quark }, + { PW_TYPE_INTERFACE_Client, 0, wp_proxy_get_type, wp_proxy_client_quark }, + { PW_TYPE_INTERFACE_Module, 0, wp_proxy_get_type, wp_proxy_module_quark }, + { PW_TYPE_INTERFACE_Device, 0, wp_proxy_get_type, wp_proxy_device_quark }, + { PW_TYPE_INTERFACE_ClientNode, 0, wp_proxy_get_type, wp_proxy_client_node_quark }, +}; + +static inline GType +wp_proxy_find_instance_type (guint32 type, guint32 version) +{ + for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) { + if (types_assoc[i].pw_type == type && + types_assoc[i].req_version <= version) + return types_assoc[i].get_type (); + } -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpProxy, wp_proxy, G_TYPE_OBJECT, - G_ADD_PRIVATE (WpProxy) - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, wp_proxy_async_initable_init)) + return WP_TYPE_PROXY; +} + +static inline GQuark +wp_proxy_find_quark_for_type (guint32 type) +{ + for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) { + if (types_assoc[i].pw_type == type) + return types_assoc[i].get_quark (); + } + + return 0; +} static void proxy_event_destroy (void *data) @@ -49,34 +133,46 @@ proxy_event_destroy (void *data) WpProxy *self = WP_PROXY (data); WpProxyPrivate *priv = wp_proxy_get_instance_private (self); - /* Set the proxy to NULL */ - priv->proxy = NULL; + priv->pw_proxy = NULL; - /* Call the destroy method */ - if (WP_PROXY_GET_CLASS (self)->destroy) - WP_PROXY_GET_CLASS (self)->destroy (self); -} + g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED], 0); -static void -proxy_event_done (void *data, int seq) -{ - /* Emit the done signal */ - g_signal_emit (data, wp_proxy_signals[SIGNAL_DONE], 0); + /* Return error if the pw_proxy destruction happened while the async + * init or augment of this proxy object was in progress */ + if (priv->task) { + g_task_return_new_error (priv->task, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, + "pipewire node proxy destroyed before finishing"); + g_clear_object (&priv->task); + } } static const struct pw_proxy_events proxy_events = { PW_VERSION_PROXY_EVENTS, .destroy = proxy_event_destroy, - .done = proxy_event_done, }; +static void +wp_proxy_init (WpProxy * self) +{ + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + g_weak_ref_init (&priv->remote, NULL); +} + static void wp_proxy_constructed (GObject * object) { - WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); + WpProxy *self = WP_PROXY (object); + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); + + /* native proxy was passed in the constructor, declare it as ready */ + if (priv->pw_proxy) { + priv->ft_ready |= WP_PROXY_FEATURE_PW_PROXY; - /* Add the event listener */ - pw_proxy_add_listener (priv->proxy, &priv->listener, &proxy_events, object); + /* and inform the subclasses */ + g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_CREATED], 0, + priv->pw_proxy); + } } static void @@ -84,14 +180,14 @@ wp_proxy_finalize (GObject * object) { WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); - g_debug ("%s:%p destroyed (pw proxy %p)", G_OBJECT_TYPE_NAME (object), - object, priv->proxy); + g_debug ("%s:%p destroyed (global %u; pw_proxy %p)", G_OBJECT_TYPE_NAME (object), + object, priv->global_id, priv->pw_proxy); - /* Destroy the proxy */ - if (priv->proxy) { - pw_proxy_destroy (priv->proxy); - priv->proxy = NULL; - } + g_clear_object (&priv->task); + g_clear_pointer (&priv->global_props, wp_properties_unref); + g_clear_pointer (&priv->native_info, priv->native_info_destroy); + g_clear_pointer (&priv->pw_proxy, pw_proxy_destroy); + g_weak_ref_clear (&priv->remote); G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); } @@ -103,11 +199,29 @@ wp_proxy_set_property (GObject * object, guint property_id, WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); switch (property_id) { + case PROP_REMOTE: + g_weak_ref_set (&priv->remote, g_value_get_object (value)); + break; case PROP_GLOBAL_ID: priv->global_id = g_value_get_uint (value); break; - case PROP_PROXY: - priv->proxy = g_value_get_pointer (value); + case PROP_GLOBAL_PERMISSIONS: + priv->global_perm = g_value_get_uint (value); + break; + case PROP_GLOBAL_PROPERTIES: + priv->global_props = g_value_dup_boxed (value); + break; + case PROP_INTERFACE_TYPE: + priv->iface_type = g_value_get_uint (value); + break; + case PROP_INTERFACE_VERSION: + priv->iface_version = g_value_get_uint (value); + break; + case PROP_PW_PROXY: + priv->pw_proxy = g_value_get_pointer (value); + break; + case PROP_NATIVE_INFO: + priv->native_info = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -122,11 +236,39 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value, WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); switch (property_id) { + case PROP_REMOTE: + g_value_take_object (value, g_weak_ref_get (&priv->remote)); + break; case PROP_GLOBAL_ID: g_value_set_uint (value, priv->global_id); break; - case PROP_PROXY: - g_value_set_pointer (value, priv->proxy); + case PROP_GLOBAL_PERMISSIONS: + g_value_set_uint (value, priv->global_perm); + break; + case PROP_GLOBAL_PROPERTIES: + g_value_set_boxed (value, priv->global_props); + break; + case PROP_INTERFACE_TYPE: + g_value_set_uint (value, priv->iface_type); + break; + case PROP_INTERFACE_NAME: + g_value_set_static_string (value, + spa_debug_type_find_name (pw_type_info(), priv->iface_type)); + break; + case PROP_INTERFACE_QUARK: + g_value_set_uint (value, wp_proxy_find_quark_for_type (priv->iface_type)); + break; + case PROP_INTERFACE_VERSION: + g_value_set_uint (value, priv->iface_version); + break; + case PROP_PW_PROXY: + g_value_set_pointer (value, priv->pw_proxy); + break; + case PROP_NATIVE_INFO: + g_value_set_pointer (value, priv->native_info); + break; + case PROP_FEATURES: + g_value_set_flags (value, priv->ft_ready); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -134,27 +276,29 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value, } } -static gboolean -wp_proxy_init_finish (GAsyncInitable *initable, GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_task_is_valid (result, initable), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - static void -wp_proxy_async_initable_init (gpointer iface, gpointer iface_data) +wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features) { - GAsyncInitableIface *ai_iface = iface; - - /* The init_async must be implemented in the derived classes */ - ai_iface->init_finish = wp_proxy_init_finish; -} + WpProxyPrivate *priv = wp_proxy_get_instance_private (self); -static void -wp_proxy_init (WpProxy * self) -{ + /* ensure we have a pw_proxy, as we can't have + * any other feature without first having that */ + if (!priv->pw_proxy && features != 0) + features |= WP_PROXY_FEATURE_PW_PROXY; + + /* if we don't have a pw_proxy, we have to assume that this WpProxy + * represents a global object from the registry; we have no other way + * to get a pw_proxy */ + if (features & WP_PROXY_FEATURE_PW_PROXY) { + if (!wp_proxy_is_global (self)) { + wp_proxy_augment_error (self, g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "No global id specified; cannot bind pw_proxy")); + return; + } + + wp_proxy_bind_global (self); + } } static void @@ -167,22 +311,263 @@ wp_proxy_class_init (WpProxyClass * klass) object_class->get_property = wp_proxy_get_property; object_class->set_property = wp_proxy_set_property; + klass->augment = wp_proxy_default_augment; + /* Install the properties */ + + g_object_class_install_property (object_class, PROP_REMOTE, + g_param_spec_object ("remote", "remote", + "The pipewire connection WpRemote", WP_TYPE_REMOTE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_GLOBAL_ID, - g_param_spec_uint ("global-id", "global-id", "The pipewire global id", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_PROXY, - g_param_spec_pointer ("pw-proxy", "pw-proxy", "The pipewire proxy", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_param_spec_uint ("global-id", "global-id", + "The pipewire global id", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_GLOBAL_PERMISSIONS, + g_param_spec_uint ("global-permissions", "global-permissions", + "The pipewire global permissions", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_GLOBAL_PROPERTIES, + g_param_spec_boxed ("global-properties", "global-properties", + "The pipewire global properties", WP_TYPE_PROPERTIES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_INTERFACE_TYPE, + g_param_spec_uint ("interface-type", "interface-type", + "The pipewire interface type", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_INTERFACE_NAME, + g_param_spec_string ("interface-name", "interface-name", + "The name of the pipewire interface", NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_INTERFACE_QUARK, + g_param_spec_uint ("interface-quark", "interface-quark", + "A quark identifying the pipewire interface", 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_INTERFACE_VERSION, + g_param_spec_uint ("interface-version", "interface-version", + "The pipewire interface version", 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_PW_PROXY, + g_param_spec_pointer ("pw-proxy", "pw-proxy", "The struct pw_proxy *", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_NATIVE_INFO, + g_param_spec_pointer ("native-info", "native-info", + "The struct pw_XXXX_info *", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_FEATURES, + g_param_spec_flags ("features", "features", + "The ready WpProxyFeatures on this proxy", WP_TYPE_PROXY_FEATURES, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /* Signals */ - wp_proxy_signals[SIGNAL_DONE] = - g_signal_new ("done", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (WpProxyClass, done), NULL, NULL, NULL, G_TYPE_NONE, 0); + wp_proxy_signals[SIGNAL_PW_PROXY_CREATED] = g_signal_new ( + "pw-proxy-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (WpProxyClass, pw_proxy_created), NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED] = g_signal_new ( + "pw-proxy-destroyed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (WpProxyClass, pw_proxy_destroyed), NULL, NULL, NULL, + G_TYPE_NONE, 0); } -guint +WpProxy * +wp_proxy_new_global (WpRemote * remote, + guint32 id, guint32 permissions, WpProperties * properties, + guint32 type, guint32 version) +{ + GType gtype = wp_proxy_find_instance_type (type, version); + return g_object_new (gtype, + "remote", remote, + "global-id", id, + "global-permissions", permissions, + "global-properties", properties, + "interface-type", type, + "interface-version", version, + NULL); +} + +WpProxy * +wp_proxy_new_wrap (WpRemote * remote, + struct pw_proxy * proxy, guint32 type, guint32 version) +{ + GType gtype = wp_proxy_find_instance_type (type, version); + return g_object_new (gtype, + "remote", remote, + "pw-proxy", proxy, + "interface-type", type, + "interface-version", version, + NULL); +} + +void +wp_proxy_augment (WpProxy * self, + WpProxyFeatures ft_wanted, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + WpProxyPrivate *priv; + WpProxyFeatures missing = 0; + + g_return_if_fail (WP_IS_PROXY (self)); + g_return_if_fail (WP_PROXY_GET_CLASS (self)->augment); + + priv = wp_proxy_get_instance_private (self); + priv->task = g_task_new (self, cancellable, callback, user_data); + + /* we don't simply assign here, we keep all the previous wanted features; + * it is not allowed to remove features */ + priv->ft_wanted |= ft_wanted; + + /* find which features are wanted but missing from the "ready" set */ + missing = (priv->ft_ready ^ priv->ft_wanted) & priv->ft_wanted; + + /* if the features are not ready, call augment(), + * otherwise signal the callback directly */ + if (missing != 0) { + WP_PROXY_GET_CLASS (self)->augment (self, missing); + } else { + g_task_return_boolean (priv->task, TRUE); + g_clear_object (&priv->task); + } +} + +gboolean +wp_proxy_augment_finish (WpProxy * self, GAsyncResult * res, + GError ** error) +{ + g_return_val_if_fail (WP_IS_PROXY (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + + return g_task_propagate_boolean (G_TASK (res), error); +} + +void +wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature) +{ + WpProxyPrivate *priv; + + g_return_if_fail (WP_IS_PROXY (self)); + + priv = wp_proxy_get_instance_private (self); + + /* feature already marked as ready */ + if (priv->ft_ready & feature) + return; + + priv->ft_ready |= feature; + + g_object_notify (G_OBJECT (self), "features"); + + /* return from the task if all the wanted features are now ready */ + if (priv->task && + (priv->ft_ready & priv->ft_wanted) == priv->ft_wanted) { + g_task_return_boolean (priv->task, TRUE); + g_clear_object (&priv->task); + } +} + +/** + * wp_proxy_augment_error: + * @self: the proxy + * @error: (transfer full): the error + * + * Reports an error that occured during the augment process + */ +void +wp_proxy_augment_error (WpProxy * self, GError * error) +{ + WpProxyPrivate *priv; + + g_return_if_fail (WP_IS_PROXY (self)); + + priv = wp_proxy_get_instance_private (self); + + if (priv->task) + g_task_return_error (priv->task, error); + else + g_error_free (error); + + g_clear_object (&priv->task); +} + +gboolean +wp_proxy_bind_global (WpProxy * self) +{ + WpProxyPrivate *priv; + g_autoptr (WpRemote) remote = NULL; + + g_return_val_if_fail (wp_proxy_is_global (self), FALSE); + + priv = wp_proxy_get_instance_private (self); + + if (priv->pw_proxy) + return FALSE; + + remote = g_weak_ref_get (&priv->remote); + g_return_val_if_fail (remote != NULL, FALSE); + + /* bind */ + priv->pw_proxy = wp_remote_pipewire_proxy_bind ( + WP_REMOTE_PIPEWIRE (remote), + priv->global_id, priv->iface_type); + pw_proxy_add_listener (priv->pw_proxy, &priv->listener, &proxy_events, + self); + + /* inform subclasses and listeners */ + g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_CREATED], 0, + priv->pw_proxy); + + /* declare the feature as ready */ + wp_proxy_set_feature_ready (self, WP_PROXY_FEATURE_PW_PROXY); + + return TRUE; +} + +WpProxyFeatures +wp_proxy_get_features (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), 0); + + priv = wp_proxy_get_instance_private (self); + return priv->ft_ready; +} + +/** + * wp_proxy_get_remote: + * @self: the proxy + * + * Returns: (transfer full): the remote that created this proxy + */ +WpRemote * +wp_proxy_get_remote (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + priv = wp_proxy_get_instance_private (self); + return g_weak_ref_get (&priv->remote); +} + +gboolean +wp_proxy_is_global (WpProxy * self) +{ + return wp_proxy_get_global_id (self) != 0; +} + +guint32 wp_proxy_get_global_id (WpProxy * self) { WpProxyPrivate *priv; @@ -193,7 +578,78 @@ wp_proxy_get_global_id (WpProxy * self) return priv->global_id; } -gpointer +guint32 +wp_proxy_get_global_permissions (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), 0); + + priv = wp_proxy_get_instance_private (self); + return priv->global_perm; +} + +/** + * wp_proxy_get_global_properties: + * + * Returns: (transfer full): the global properties of the proxy + */ +WpProperties * +wp_proxy_get_global_properties (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + priv = wp_proxy_get_instance_private (self); + return priv->global_props ? wp_properties_ref (priv->global_props) : NULL; +} + +guint32 +wp_proxy_get_interface_type (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), 0); + + priv = wp_proxy_get_instance_private (self); + return priv->iface_type; +} + +const gchar * +wp_proxy_get_interface_name (WpProxy * self) +{ + const gchar *name = NULL; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + g_object_get (self, "interface-name", &name, NULL); + return name; +} + +GQuark +wp_proxy_get_interface_quark (WpProxy * self) +{ + GQuark q = 0; + + g_return_val_if_fail (WP_IS_PROXY (self), 0); + + g_object_get (self, "interface-quark", &q, NULL); + return q; +} + +guint32 +wp_proxy_get_interface_version (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), 0); + + priv = wp_proxy_get_instance_private (self); + return priv->iface_version; +} + +struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self) { WpProxyPrivate *priv; @@ -201,17 +657,39 @@ wp_proxy_get_pw_proxy (WpProxy * self) g_return_val_if_fail (WP_IS_PROXY (self), NULL); priv = wp_proxy_get_instance_private (self); - return priv->proxy; + return priv->pw_proxy; +} + +gconstpointer +wp_proxy_get_native_info (WpProxy * self) +{ + WpProxyPrivate *priv; + + g_return_val_if_fail (WP_IS_PROXY (self), NULL); + + priv = wp_proxy_get_instance_private (self); + return priv->native_info; } -void wp_proxy_sync (WpProxy * self) +void +wp_proxy_update_native_info (WpProxy * self, gconstpointer info, + WpProxyNativeInfoUpdate update, GDestroyNotify destroy) { WpProxyPrivate *priv; g_return_if_fail (WP_IS_PROXY (self)); + g_return_if_fail (destroy != NULL); priv = wp_proxy_get_instance_private (self); - /* Trigger the done callback */ - pw_proxy_sync(priv->proxy, 0); + if (update) { + priv->native_info = update (priv->native_info, info); + } else { + g_clear_pointer (&priv->native_info, priv->native_info_destroy); + priv->native_info = (gpointer) info; + } + + priv->native_info_destroy = destroy; + + g_object_notify (G_OBJECT (self), "native-info"); } diff --git a/lib/wp/proxy.h b/lib/wp/proxy.h index 52889874..9114858b 100644 --- a/lib/wp/proxy.h +++ b/lib/wp/proxy.h @@ -2,6 +2,7 @@ * * Copyright © 2019 Collabora Ltd. * @author Julian Bouzas <julian.bouzas@collabora.com> + * @author George Kiagiadakis <george.kiagiadakis@collabora.com> * * SPDX-License-Identifier: MIT */ @@ -11,10 +12,20 @@ #include <gio/gio.h> -#include "core.h" +#include "remote.h" +#include "properties.h" G_BEGIN_DECLS +struct pw_proxy; + +typedef enum { /*< flags >*/ + WP_PROXY_FEATURE_PW_PROXY = (1 << 0), + WP_PROXY_FEATURE_INFO = (1 << 1), + + WP_PROXY_FEATURE_LAST = (1 << 5), /*< skip >*/ +} WpProxyFeatures; + #define WP_TYPE_PROXY (wp_proxy_get_type ()) G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject) @@ -23,16 +34,53 @@ struct _WpProxyClass { GObjectClass parent_class; - /* Methods */ - void (*destroy) (WpProxy * self); + void (*augment) (WpProxy *self, WpProxyFeatures features); - /* Signals */ - void (*done)(WpProxy *wp_proxy, gpointer data); + void (*pw_proxy_created) (WpProxy * self, struct pw_proxy * proxy); + void (*pw_proxy_destroyed) (WpProxy * self); }; -guint wp_proxy_get_global_id (WpProxy * self); -gpointer wp_proxy_get_pw_proxy (WpProxy * self); -void wp_proxy_sync (WpProxy * self); +WpProxy * wp_proxy_new_global (WpRemote * remote, + guint32 id, guint32 permissions, WpProperties * properties, + guint32 type, guint32 version); +WpProxy * wp_proxy_new_wrap (WpRemote * remote, + struct pw_proxy * proxy, guint32 type, guint32 version); + +void wp_proxy_augment (WpProxy *self, + WpProxyFeatures wanted_features, GCancellable * cancellable, + GAsyncReadyCallback callback, gpointer user_data); +gboolean wp_proxy_augment_finish (WpProxy * self, GAsyncResult * res, + GError ** error); + +WpProxyFeatures wp_proxy_get_features (WpProxy * self); + +WpRemote * wp_proxy_get_remote (WpProxy * self); + +gboolean wp_proxy_is_global (WpProxy * self); +guint32 wp_proxy_get_global_id (WpProxy * self); +guint32 wp_proxy_get_global_permissions (WpProxy * self); +WpProperties * wp_proxy_get_global_properties (WpProxy * self); + +guint32 wp_proxy_get_interface_type (WpProxy * self); +const gchar * wp_proxy_get_interface_name (WpProxy * self); +GQuark wp_proxy_get_interface_quark (WpProxy * self); +guint32 wp_proxy_get_interface_version (WpProxy * self); + +struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self); +gconstpointer wp_proxy_get_native_info (WpProxy * self); + +/* for subclasses only */ + +typedef gpointer (*WpProxyNativeInfoUpdate) (gpointer old_info, + gconstpointer new_info); + +void wp_proxy_update_native_info (WpProxy * self, gconstpointer info, + WpProxyNativeInfoUpdate update, GDestroyNotify destroy); + +void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature); +void wp_proxy_augment_error (WpProxy * self, GError * error); + +gboolean wp_proxy_bind_global (WpProxy * self); G_END_DECLS diff --git a/lib/wp/remote-pipewire.c b/lib/wp/remote-pipewire.c index 64fcd3e5..45f0759f 100644 --- a/lib/wp/remote-pipewire.c +++ b/lib/wp/remote-pipewire.c @@ -7,6 +7,7 @@ */ #include "remote-pipewire.h" + #include <pipewire/pipewire.h> /* @@ -15,15 +16,6 @@ #define WP_LOOP_SOURCE(x) ((WpLoopSource *) x) -G_DEFINE_QUARK (node, signal_detail_node) -G_DEFINE_QUARK (port, signal_detail_port) -G_DEFINE_QUARK (factory, signal_detail_factory) -G_DEFINE_QUARK (link, signal_detail_link) -G_DEFINE_QUARK (client, signal_detail_client) -G_DEFINE_QUARK (module, signal_detail_module) -G_DEFINE_QUARK (device, signal_detail_device) -G_DEFINE_QUARK (endpoint, signal_detail_endpoint) - typedef struct _WpLoopSource WpLoopSource; struct _WpLoopSource { @@ -90,6 +82,10 @@ struct _WpRemotePipewire struct spa_hook registry_listener; GMainContext *context; + + GHashTable *proxies; + GHashTable *default_features; + GQueue created_obj_proxies; }; enum { @@ -113,45 +109,53 @@ static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (WpRemotePipewire, wp_remote_pipewire, WP_TYPE_REMOTE) static void -registry_global(void *data, uint32_t id, - uint32_t permissions, uint32_t type, uint32_t version, - const struct spa_dict *props) +on_proxy_ready (GObject * obj, GAsyncResult * res, gpointer data) { - GQuark detail = 0; + WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data); + WpProxy *proxy = WP_PROXY (obj); + g_autoptr (GError) error = NULL; - switch (type) { - case PW_TYPE_INTERFACE_Node: - detail = signal_detail_node_quark (); - break; - case PW_TYPE_INTERFACE_Port: - detail = signal_detail_port_quark (); - break; - case PW_TYPE_INTERFACE_Factory: - detail = signal_detail_factory_quark (); - break; - case PW_TYPE_INTERFACE_Link: - detail = signal_detail_link_quark (); - break; - case PW_TYPE_INTERFACE_Client: - detail = signal_detail_client_quark (); - break; - case PW_TYPE_INTERFACE_Module: - detail = signal_detail_module_quark (); - break; - case PW_TYPE_INTERFACE_Device: - detail = signal_detail_device_quark (); - break; - default: - break; + if (!wp_proxy_augment_finish (proxy, res, &error)) { + g_warning ("Failed to augment WpProxy (%p): %s", obj, error->message); } - g_signal_emit (data, signals[SIGNAL_GLOBAL_ADDED], detail, id, props); + g_signal_emit (self, signals[SIGNAL_GLOBAL_ADDED], + wp_proxy_get_interface_quark (proxy), proxy); +} + +static void +registry_global(void *data, uint32_t id, uint32_t permissions, + uint32_t type, uint32_t version, const struct spa_dict *props) +{ + WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data); + WpProxy *proxy; + WpProxyFeatures features; + g_autoptr (WpProperties) properties = wp_properties_new_copy_dict (props); + + /* construct & store WpProxy */ + proxy = wp_proxy_new_global (WP_REMOTE (self), id, permissions, properties, + type, version); + g_hash_table_insert (self->proxies, GUINT_TO_POINTER (id), proxy); + + g_debug ("registry global:%u perm:0x%x type:%u/%u -> WpProxy:%p", + id, permissions, type, version, proxy); + + /* augment */ + features = GPOINTER_TO_UINT (g_hash_table_lookup (self->default_features, + GUINT_TO_POINTER (G_TYPE_FROM_INSTANCE (proxy)))); + wp_proxy_augment (proxy, features, NULL, on_proxy_ready, self); } static void registry_global_remove (void *data, uint32_t id) { - g_signal_emit (data, signals[SIGNAL_GLOBAL_REMOVED], 0, id); + WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data); + g_autoptr (WpProxy) proxy = NULL; + + if (g_hash_table_steal_extended (self->proxies, GUINT_TO_POINTER (id), NULL, + (gpointer *) &proxy)) + g_signal_emit (data, signals[SIGNAL_GLOBAL_REMOVED], + wp_proxy_get_interface_quark (proxy), proxy); } static const struct pw_registry_proxy_events registry_proxy_events = { @@ -198,6 +202,9 @@ static const struct pw_remote_events remote_events = { static void wp_remote_pipewire_init (WpRemotePipewire *self) { + self->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + g_object_unref); + self->default_features = g_hash_table_new (g_direct_hash, g_direct_equal); } static void @@ -223,6 +230,8 @@ wp_remote_pipewire_finalize (GObject *object) { WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object); + g_clear_pointer (&self->proxies, g_hash_table_unref); + g_clear_pointer (&self->default_features, g_hash_table_unref); pw_remote_destroy (self->remote); pw_core_destroy (self->core); g_clear_pointer (&self->context, g_main_context_unref); @@ -336,10 +345,10 @@ wp_remote_pipewire_class_init (WpRemotePipewireClass *klass) /* Signals */ signals[SIGNAL_GLOBAL_ADDED] = g_signal_new ("global-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY); signals[SIGNAL_GLOBAL_REMOVED] = g_signal_new ("global-removed", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); + G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY); } WpRemote * @@ -359,6 +368,33 @@ wp_remote_pipewire_new (WpCore *core, GMainContext *context) return remote; } +void +wp_remote_pipewire_set_default_features (WpRemotePipewire * self, + GType proxy_type, WpProxyFeatures features) +{ + g_return_if_fail (WP_IS_REMOTE_PIPEWIRE (self)); + + g_hash_table_insert (self->default_features, GUINT_TO_POINTER (proxy_type), + GUINT_TO_POINTER (features)); +} + +WpProxy * +wp_remote_pipewire_create_object (WpRemotePipewire *self, + const gchar *factory_name, guint32 interface_type, + guint32 interface_version, WpProperties * properties) +{ + struct pw_proxy *pw_proxy; + + g_return_val_if_fail (WP_IS_REMOTE_PIPEWIRE (self), NULL); + g_return_val_if_fail (self->core_proxy, NULL); + + pw_proxy = pw_core_proxy_create_object (self->core_proxy, factory_name, + interface_type, interface_version, wp_properties_peek_dict (properties), + 0); + return wp_proxy_new_wrap (WP_REMOTE (self), pw_proxy, interface_type, + interface_version); +} + gpointer wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id, guint global_type) @@ -380,17 +416,6 @@ wp_remote_pipewire_find_factory (WpRemotePipewire *self, return pw_core_find_factory(self->core, factory_name); } -gpointer -wp_remote_pipewire_create_object (WpRemotePipewire *self, - const char *factory_name, guint global_type, gconstpointer props) -{ - g_return_val_if_fail (WP_IS_REMOTE_PIPEWIRE(self), NULL); - g_return_val_if_fail (self->core_proxy, NULL); - - return pw_core_proxy_create_object (self->core_proxy, factory_name, - global_type, 0, props, 0); -} - void wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self, const char *factory_regexp, const char *lib) diff --git a/lib/wp/remote-pipewire.h b/lib/wp/remote-pipewire.h index 42d430ae..31fe4591 100644 --- a/lib/wp/remote-pipewire.h +++ b/lib/wp/remote-pipewire.h @@ -10,6 +10,7 @@ #define __WIREPLUMBER_REMOTE_PIPEWIRE_H__ #include "remote.h" +#include "proxy.h" G_BEGIN_DECLS @@ -19,12 +20,17 @@ G_DECLARE_FINAL_TYPE (WpRemotePipewire, wp_remote_pipewire, WpRemote *wp_remote_pipewire_new (WpCore *core, GMainContext *context); +void wp_remote_pipewire_set_default_features ( + WpRemotePipewire * self, GType proxy_type, WpProxyFeatures features); + +WpProxy * wp_remote_pipewire_create_object (WpRemotePipewire *self, + const gchar *factory_name, guint32 interface_type, + guint32 interface_version, WpProperties * properties); + gpointer wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id, guint global_type); gpointer wp_remote_pipewire_find_factory (WpRemotePipewire *self, const char *factory_name); -gpointer wp_remote_pipewire_create_object (WpRemotePipewire *self, - const char *factory_name, guint global_type, gconstpointer props); void wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self, const char *factory_regexp, const char *lib); gpointer wp_remote_pipewire_load_spa_handle(WpRemotePipewire *self, -- GitLab