From 447c9688464883a5bf6f124fd24052ede005debd Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Fri, 17 May 2019 13:08:45 +0300 Subject: [PATCH] Refactor everything! After discussing things at the AGL May 2019 F2F meeting and reflecting on the initial design of WirePlumber, it became clear that it needed a fresh start. --- lib/wp/core-interfaces.c | 266 -------- lib/wp/core-interfaces.h | 129 ---- lib/wp/core.c | 107 +++ lib/wp/core.h | 43 ++ lib/wp/endpoint.c | 439 +++++++++++++ lib/wp/endpoint.h | 82 +++ lib/wp/error.h | 2 + lib/wp/factory.c | 48 ++ lib/wp/factory.h | 69 ++ lib/wp/interface-impl.c | 121 ---- lib/wp/interface-impl.h | 45 -- lib/wp/meson.build | 27 +- lib/wp/module.c | 137 ++++ lib/wp/module.h | 29 + lib/wp/object.c | 225 ------- lib/wp/object.h | 54 -- lib/wp/plugin.c | 277 -------- lib/wp/plugin.h | 190 ------ lib/wp/proxy.c | 611 ------------------ lib/wp/proxy.h | 43 -- lib/wp/session-manager.c | 120 ++++ lib/wp/session-manager.h | 34 + lib/wp/session.c | 101 --- lib/wp/session.h | 34 - src/utils.c => lib/wp/wp.h | 9 +- modules/meson.build | 33 +- modules/module-default-session.c | 338 ---------- modules/module-pipewire.c | 68 ++ .../module-pipewire}/loop-source.c | 20 +- modules/module-pipewire/loop-source.h | 21 + .../module-pipewire/simple-endpoint-link.c | 71 ++ modules/module-pipewire/simple-endpoint.c | 63 ++ modules/module-pw-alsa-udev.c | 20 + modules/module-pw-audio-softdsp-endpoint.c | 65 ++ src/core.c | 358 ---------- src/core.h | 26 - src/loop-source.h | 28 - src/main.c | 209 +++++- src/meson.build | 9 +- src/module-loader.c | 81 --- src/module-loader.h | 27 - src/plugin-registry-impl.c | 164 ----- src/plugin-registry-impl.h | 37 -- src/proxy-registry-impl.c | 281 -------- src/proxy-registry-impl.h | 27 - src/session-registry-impl.c | 196 ------ src/session-registry-impl.h | 23 - src/utils.h | 29 - 48 files changed, 1667 insertions(+), 3769 deletions(-) delete mode 100644 lib/wp/core-interfaces.c delete mode 100644 lib/wp/core-interfaces.h create mode 100644 lib/wp/core.c create mode 100644 lib/wp/core.h create mode 100644 lib/wp/endpoint.c create mode 100644 lib/wp/endpoint.h create mode 100644 lib/wp/factory.c create mode 100644 lib/wp/factory.h delete mode 100644 lib/wp/interface-impl.c delete mode 100644 lib/wp/interface-impl.h create mode 100644 lib/wp/module.c create mode 100644 lib/wp/module.h delete mode 100644 lib/wp/object.c delete mode 100644 lib/wp/object.h delete mode 100644 lib/wp/plugin.c delete mode 100644 lib/wp/plugin.h delete mode 100644 lib/wp/proxy.c delete mode 100644 lib/wp/proxy.h create mode 100644 lib/wp/session-manager.c create mode 100644 lib/wp/session-manager.h delete mode 100644 lib/wp/session.c delete mode 100644 lib/wp/session.h rename src/utils.c => lib/wp/wp.h (57%) delete mode 100644 modules/module-default-session.c create mode 100644 modules/module-pipewire.c rename {src => modules/module-pipewire}/loop-source.c (81%) create mode 100644 modules/module-pipewire/loop-source.h create mode 100644 modules/module-pipewire/simple-endpoint-link.c create mode 100644 modules/module-pipewire/simple-endpoint.c create mode 100644 modules/module-pw-alsa-udev.c create mode 100644 modules/module-pw-audio-softdsp-endpoint.c delete mode 100644 src/core.c delete mode 100644 src/core.h delete mode 100644 src/loop-source.h delete mode 100644 src/module-loader.c delete mode 100644 src/module-loader.h delete mode 100644 src/plugin-registry-impl.c delete mode 100644 src/plugin-registry-impl.h delete mode 100644 src/proxy-registry-impl.c delete mode 100644 src/proxy-registry-impl.h delete mode 100644 src/session-registry-impl.c delete mode 100644 src/session-registry-impl.h delete mode 100644 src/utils.h diff --git a/lib/wp/core-interfaces.c b/lib/wp/core-interfaces.c deleted file mode 100644 index ba137162..00000000 --- a/lib/wp/core-interfaces.c +++ /dev/null @@ -1,266 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "core-interfaces.h" -#include "plugin.h" -#include "session.h" - -/* WpPipewireObjects */ - -G_DEFINE_INTERFACE (WpPipewireObjects, wp_pipewire_objects, G_TYPE_OBJECT) - -static void -wp_pipewire_objects_default_init (WpPipewireObjectsInterface * iface) -{ -} - -struct pw_core * wp_pipewire_objects_get_pw_core (WpPipewireObjects * self) -{ - WpPipewireObjectsInterface *iface = WP_PIPEWIRE_OBJECTS_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PIPEWIRE_OBJECTS (self), NULL); - g_return_val_if_fail (iface->get_pw_core, NULL); - - return iface->get_pw_core (self); -} - -struct pw_remote * wp_pipewire_objects_get_pw_remote (WpPipewireObjects * self) -{ - WpPipewireObjectsInterface *iface = WP_PIPEWIRE_OBJECTS_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PIPEWIRE_OBJECTS (self), NULL); - g_return_val_if_fail (iface->get_pw_remote, NULL); - - return iface->get_pw_remote (self); -} - -/* WpPluginRegistry */ - -G_DEFINE_INTERFACE (WpPluginRegistry, wp_plugin_registry, WP_TYPE_INTERFACE_IMPL) - -static void -wp_plugin_registry_default_init (WpPluginRegistryInterface * iface) -{ -} - -/** - * wp_plugin_registry_register_static: (skip) - * @plugin_type: the #GType of the #WpPlugin subclass - * @metadata: the metadata - * @metadata_size: the sizeof (@metadata), to allow ABI-compatible future - * expansion of the structure - * - * Registers a plugin in the registry. - * This method is used internally by WP_PLUGIN_REGISTER(). - * Avoid using it directly. - */ -void -wp_plugin_registry_register_static (WpPluginRegistry * self, - GType plugin_type, - const WpPluginMetadata * metadata, - gsize metadata_size) -{ - WpPluginRegistryInterface *iface = WP_PLUGIN_REGISTRY_GET_IFACE (self); - - g_return_if_fail (WP_IS_PLUGIN_REGISTRY (self)); - g_return_if_fail (iface->register_plugin != NULL); - g_return_if_fail (g_type_is_a (plugin_type, WP_TYPE_PLUGIN)); - g_return_if_fail (metadata->name != NULL); - g_return_if_fail (metadata->description != NULL); - g_return_if_fail (metadata->author != NULL); - g_return_if_fail (metadata->license != NULL); - g_return_if_fail (metadata->version != NULL); - g_return_if_fail (metadata->origin != NULL); - - WP_PLUGIN_REGISTRY_GET_IFACE (self)->register_plugin (self, plugin_type, - metadata, metadata_size, TRUE); -} - -/** - * wp_plugin_registry_register: (method) - * @plugin_type: the #GType of the #WpPlugin subclass - * @rank: the rank of the plugin - * @name: the name of the plugin - * @description: plugin description - * @author: author , author2 - * @license: a SPDX license ID or "Proprietary" - * @version: the version of the plugin - * @origin: URL or short reference of where this plugin came from - * - * Registers a plugin in the registry. - * This method creates a dynamically allocated #WpPluginMetadata and is meant - * to be used by bindings that have no way of representing #WpPluginMetadata. - * In C/C++, you should use WP_PLUGIN_REGISTER() - */ -void -wp_plugin_registry_register (WpPluginRegistry * self, - GType plugin_type, - guint16 rank, - const gchar *name, - const gchar *description, - const gchar *author, - const gchar *license, - const gchar *version, - const gchar *origin) -{ - WpPluginRegistryInterface *iface = WP_PLUGIN_REGISTRY_GET_IFACE (self); - WpPluginMetadata metadata = {0}; - - g_return_if_fail (WP_IS_PLUGIN_REGISTRY (self)); - g_return_if_fail (iface->register_plugin != NULL); - g_return_if_fail (g_type_is_a (plugin_type, WP_TYPE_PLUGIN)); - g_return_if_fail (name != NULL); - g_return_if_fail (description != NULL); - g_return_if_fail (author != NULL); - g_return_if_fail (license != NULL); - g_return_if_fail (version != NULL); - g_return_if_fail (origin != NULL); - - metadata.rank = rank; - metadata.name = name; - metadata.description = description; - metadata.author = author; - metadata.license = license; - metadata.version = version; - metadata.origin = origin; - - WP_PLUGIN_REGISTRY_GET_IFACE (self)->register_plugin (self, plugin_type, - &metadata, sizeof (metadata), FALSE); -} - -/* WpProxyRegistry */ - -G_DEFINE_INTERFACE (WpProxyRegistry, wp_proxy_registry, WP_TYPE_INTERFACE_IMPL) - -static void -wp_proxy_registry_default_init (WpProxyRegistryInterface * iface) -{ -} - -/** - * wp_proxy_registry_get_proxy: (method) - * @self: the registry - * @global_id: the ID of the pw_global that is represented by the proxy - * - * Returns: (transfer full): the #WpProxy that represents the global with - * @global_id - */ -WpProxy * -wp_proxy_registry_get_proxy (WpProxyRegistry * self, guint32 global_id) -{ - WpProxyRegistryInterface * iface = WP_PROXY_REGISTRY_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PROXY_REGISTRY (self), NULL); - g_return_val_if_fail (iface->get_proxy != NULL, NULL); - - return iface->get_proxy (self, global_id); -} - -/** - * wp_proxy_registry_get_pw_registry_proxy: (skip) - * @self: the registry - * - * Returns: the underlying `pw_registry_proxy` - */ -struct pw_registry_proxy * -wp_proxy_registry_get_pw_registry_proxy (WpProxyRegistry * self) -{ - WpProxyRegistryInterface * iface = WP_PROXY_REGISTRY_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PROXY_REGISTRY (self), NULL); - g_return_val_if_fail (iface->get_pw_registry_proxy != NULL, NULL); - - return iface->get_pw_registry_proxy (self); -} - -/* WpSessionRegistry */ - -G_DEFINE_INTERFACE (WpSessionRegistry, wp_session_registry, G_TYPE_OBJECT) - -enum { - SIG_SESSION_REGISTERED, - SIG_SESSION_UNREGISTERED, - N_SESSION_REGISTRY_SIGNALS -}; - -static guint session_registry_signals[N_SESSION_REGISTRY_SIGNALS]; - -static void -wp_session_registry_default_init (WpSessionRegistryInterface * iface) -{ - session_registry_signals[SIG_SESSION_REGISTERED] = g_signal_new ( - "session-registered", G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, - WP_TYPE_SESSION); - - session_registry_signals[SIG_SESSION_UNREGISTERED] = g_signal_new ( - "session-unregistered", G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); -} - -guint32 -wp_session_registry_register_session (WpSessionRegistry * self, - WpSession * session, - GError ** error) -{ - WpSessionRegistryInterface *iface = WP_SESSION_REGISTRY_GET_IFACE (self); - guint32 id; - - g_return_val_if_fail (WP_IS_SESSION_REGISTRY (self), -1); - g_return_val_if_fail (session != NULL, -1); - g_return_val_if_fail (iface->register_session, -1); - - id = iface->register_session (self, session, error); - if (id != -1) { - g_signal_emit (self, session_registry_signals[SIG_SESSION_REGISTERED], 0, - id, session); - } - return id; -} - -gboolean -wp_session_registry_unregister_session (WpSessionRegistry * self, - guint32 session_id) -{ - WpSessionRegistryInterface *iface = WP_SESSION_REGISTRY_GET_IFACE (self); - gboolean ret; - - g_return_val_if_fail (WP_IS_SESSION_REGISTRY (self), FALSE); - g_return_val_if_fail (iface->unregister_session, FALSE); - - ret = iface->unregister_session (self, session_id); - if (ret) { - g_signal_emit (self, session_registry_signals[SIG_SESSION_UNREGISTERED], 0, - session_id); - } - return ret; -} - -GArray * -wp_session_registry_list_sessions (WpSessionRegistry * self, - const gchar * media_class) -{ - WpSessionRegistryInterface *iface = WP_SESSION_REGISTRY_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_SESSION_REGISTRY (self), NULL); - g_return_val_if_fail (iface->list_sessions, NULL); - - return iface->list_sessions (self, media_class); -} - -WpSession * -wp_session_registry_get_session (WpSessionRegistry * self, - guint32 session_id) -{ - WpSessionRegistryInterface *iface = WP_SESSION_REGISTRY_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_SESSION_REGISTRY (self), NULL); - g_return_val_if_fail (iface->get_session, NULL); - - return iface->get_session (self, session_id); -} diff --git a/lib/wp/core-interfaces.h b/lib/wp/core-interfaces.h deleted file mode 100644 index d2cc02ab..00000000 --- a/lib/wp/core-interfaces.h +++ /dev/null @@ -1,129 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_CORE_INTERFACES_H__ -#define __WP_CORE_INTERFACES_H__ - -#include "interface-impl.h" - -G_BEGIN_DECLS - -/* WpPipewireObjects */ - -struct pw_core; -struct pw_remote; - -#define WP_TYPE_PIPEWIRE_OBJECTS (wp_pipewire_objects_get_type ()) -G_DECLARE_INTERFACE (WpPipewireObjects, wp_pipewire_objects, WP, PIPEWIRE_OBJECTS, GObject) - -struct _WpPipewireObjectsInterface -{ - GTypeInterface parent; - - struct pw_core * (*get_pw_core) (WpPipewireObjects * self); - struct pw_remote * (*get_pw_remote) (WpPipewireObjects * self); -}; - -struct pw_core * wp_pipewire_objects_get_pw_core (WpPipewireObjects * self); -struct pw_remote * wp_pipewire_objects_get_pw_remote (WpPipewireObjects * self); - -/* WpPluginRegistry */ - -#define WP_TYPE_PLUGIN_REGISTRY (wp_plugin_registry_get_type ()) -G_DECLARE_INTERFACE (WpPluginRegistry, wp_plugin_registry, WP, PLUGIN_REGISTRY, WpInterfaceImpl) - -typedef struct _WpPluginMetadata WpPluginMetadata; - -struct _WpPluginRegistryInterface -{ - GTypeInterface parent; - - void (*register_plugin) (WpPluginRegistry * self, - GType plugin_type, - const WpPluginMetadata * metadata, - gsize metadata_size, - gboolean static_data); -}; - -void wp_plugin_registry_register_static (WpPluginRegistry * self, - GType plugin_type, - const WpPluginMetadata * metadata, - gsize metadata_size); - -void wp_plugin_registry_register (WpPluginRegistry * self, - GType plugin_type, - guint16 rank, - const gchar *name, - const gchar *description, - const gchar *author, - const gchar *license, - const gchar *version, - const gchar *origin); - -/* WpProxyRegistry */ - -struct pw_registry_proxy; -typedef struct _WpProxy WpProxy; - -#define WP_TYPE_PROXY_REGISTRY (wp_proxy_registry_get_type ()) -G_DECLARE_INTERFACE (WpProxyRegistry, wp_proxy_registry, WP, PROXY_REGISTRY, WpInterfaceImpl) - -struct _WpProxyRegistryInterface -{ - GTypeInterface parent; - - WpProxy * (*get_proxy) (WpProxyRegistry * self, guint32 global_id); - struct pw_registry_proxy * (*get_pw_registry_proxy) (WpProxyRegistry * self); -}; - -WpProxy * wp_proxy_registry_get_proxy (WpProxyRegistry * self, - guint32 global_id); - -struct pw_registry_proxy * wp_proxy_registry_get_pw_registry_proxy ( - WpProxyRegistry * self); - -/* WpSessionRegistry */ - -#define WP_TYPE_SESSION_REGISTRY (wp_session_registry_get_type ()) -G_DECLARE_INTERFACE (WpSessionRegistry, wp_session_registry, WP, SESSION_REGISTRY, GObject) - -typedef struct _WpSession WpSession; - -struct _WpSessionRegistryInterface -{ - GTypeInterface parent; - - guint32 (*register_session) (WpSessionRegistry * self, - WpSession * session, - GError ** error); - - gboolean (*unregister_session) (WpSessionRegistry * self, guint32 session_id); - - GArray * (*list_sessions) (WpSessionRegistry * self, - const gchar * media_class); - - WpSession * (*get_session) (WpSessionRegistry * self, - guint32 session_id); -}; - -guint32 wp_session_registry_register_session (WpSessionRegistry * self, - WpSession * session_object, - GError ** error); - -gboolean wp_session_registry_unregister_session (WpSessionRegistry * self, - guint32 session_id); - -GArray * wp_session_registry_list_sessions (WpSessionRegistry * self, - const gchar * media_class); - -WpSession * wp_session_registry_get_session (WpSessionRegistry * self, - guint32 session_id); - -G_END_DECLS - -#endif diff --git a/lib/wp/core.c b/lib/wp/core.c new file mode 100644 index 00000000..14fcd634 --- /dev/null +++ b/lib/wp/core.c @@ -0,0 +1,107 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "core.h" + +struct _WpCore +{ + GObject parent; + GData *global_objects; +}; + +G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT) + +static void +wp_core_init (WpCore * self) +{ + g_datalist_init (&self->global_objects); +} + +static void +wp_core_finalize (GObject * obj) +{ + WpCore *self = WP_CORE (obj); + + g_datalist_clear (&self->global_objects); + + G_OBJECT_CLASS (wp_core_parent_class)->finalize (obj); +} + +static void +wp_core_class_init (WpCoreClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + object_class->finalize = wp_core_finalize; +} + +WpCore * +wp_core_new (void) +{ + return g_object_new (WP_TYPE_CORE, NULL); +} + +/** + * wp_core_get_global: (method) + * @self: the core + * @key: the key of the global + * + * Returns: (type GObject*) (nullable) (transfer none): the global object + * implementing @type + */ +gpointer +wp_core_get_global (WpCore * self, GQuark key) +{ + g_return_val_if_fail (WP_IS_CORE (self), NULL); + + return g_datalist_id_get_data (&self->global_objects, key); +} + +/** + * wp_core_register_global: (method) + * @self: the core + * @key: the key for this global + * @obj: (transfer full): the global object to attach + * @destroy_obj: the destroy function for @obj + * + * Returns: TRUE one success, FALSE if the global already exists + */ +gboolean +wp_core_register_global (WpCore * self, GQuark key, gpointer obj, + GDestroyNotify destroy_obj) +{ + gpointer other = NULL; + + g_return_val_if_fail (WP_IS_CORE(self), FALSE); + + if ((other = g_datalist_id_get_data (&self->global_objects, key)) != NULL) { + g_warning ("cannot register global '%s': it already exists", + g_quark_to_string (key)); + return FALSE; + } + + g_datalist_id_set_data_full (&self->global_objects, key, obj, destroy_obj); + return TRUE; +} + +/** + * wp_core_remove_global: (method) + * @self: the core + * @key: the key for this global + * + * Detaches and unrefs the specified global from this core + */ +void +wp_core_remove_global (WpCore * self, GQuark key) +{ + g_return_if_fail (WP_IS_CORE (self)); + + g_datalist_id_remove_data (&self->global_objects, key); +} + +G_DEFINE_QUARK (WP_GLOBAL_PW_CORE, wp_global_pw_core) +G_DEFINE_QUARK (WP_GLIBAL_PW_REMOTE, wp_global_pw_remote) diff --git a/lib/wp/core.h b/lib/wp/core.h new file mode 100644 index 00000000..6853b075 --- /dev/null +++ b/lib/wp/core.h @@ -0,0 +1,43 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WIREPLUMBER_CORE_H__ +#define __WIREPLUMBER_CORE_H__ + +#include + +G_BEGIN_DECLS + +#define WP_TYPE_CORE (wp_core_get_type ()) +G_DECLARE_FINAL_TYPE (WpCore, wp_core, WP, CORE, GObject) + +WpCore * wp_core_new (void); + +gpointer wp_core_get_global (WpCore * self, GQuark key); + +gboolean wp_core_register_global (WpCore * self, GQuark key, gpointer obj, + GDestroyNotify destroy_obj); +void wp_core_remove_global (WpCore * self, GQuark key); + +/** + * WP_GLOBAL_PW_CORE: + * The key to access the pw_core global object + */ +#define WP_GLOBAL_PW_CORE (wp_global_pw_core_quark ()) +GQuark wp_global_pw_core_quark (void); + +/** + * WP_GLOBAL_PW_REMOTE: + * The key to access the pw_remote global object + */ +#define WP_GLOBAL_PW_REMOTE (wp_global_pw_remote_quark ()) +GQuark wp_global_pw_remote_quark (void); + +G_END_DECLS + +#endif diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c new file mode 100644 index 00000000..8e9d9571 --- /dev/null +++ b/lib/wp/endpoint.c @@ -0,0 +1,439 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "endpoint.h" +#include "error.h" +#include "factory.h" + +typedef struct _WpEndpointPrivate WpEndpointPrivate; +struct _WpEndpointPrivate +{ + gchar *name; + gchar media_class[40]; + guint32 active_profile; + GPtrArray *links; +}; + +enum { + PROP_0, + PROP_NAME, + PROP_MEDIA_CLASS, +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEndpoint, wp_endpoint, G_TYPE_OBJECT) + +static void +wp_endpoint_init (WpEndpoint * self) +{ + WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self); + + priv->links = g_ptr_array_new_with_free_func (g_object_unref); +} + +static void +wp_endpoint_dispose (GObject * object) +{ + WpEndpointPrivate *priv = + wp_endpoint_get_instance_private (WP_ENDPOINT (object)); + gint i; + + /* wp_endpoint_link_destroy removes elements from the array, + * so traversing in reverse order is faster and less complicated */ + for (i = priv->links->len - 1; i >= 0; i--) { + wp_endpoint_link_destroy (g_ptr_array_index (priv->links, i)); + } + + G_OBJECT_CLASS (wp_endpoint_parent_class)->dispose (object); +} + +static void +wp_endpoint_finalize (GObject * object) +{ + WpEndpointPrivate *priv = + wp_endpoint_get_instance_private (WP_ENDPOINT (object)); + + g_ptr_array_unref (priv->links); + + G_OBJECT_CLASS (wp_endpoint_parent_class)->finalize (object); +} + +static void +wp_endpoint_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + WpEndpointPrivate *priv = + wp_endpoint_get_instance_private (WP_ENDPOINT (object)); + + switch (property_id) { + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + case PROP_MEDIA_CLASS: + strncpy (priv->media_class, g_value_get_string (value), + sizeof (priv->media_class) - 1); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_endpoint_get_property (GObject * object, guint property_id, GValue * value, + GParamSpec * pspec) +{ + WpEndpointPrivate *priv = + wp_endpoint_get_instance_private (WP_ENDPOINT (object)); + + switch (property_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_MEDIA_CLASS: + g_value_set_string (value, priv->media_class); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static guint32 +default_get_streams_or_profiles_count (WpEndpoint * self) +{ + return 1; +} + +static const gchar * +default_get_stream_or_profile_name (WpEndpoint * self, guint32 id) +{ + return (id == 0) ? "default" : NULL; +} + +static gboolean +default_activate_profile (WpEndpoint * self, guint32 profile_id, + GError ** error) +{ + WpEndpointPrivate *priv = wp_endpoint_get_instance_private (self); + + if (profile_id >= wp_endpoint_get_profiles_count (self)) { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "profile id %d does not exist", profile_id); + return FALSE; + } + + priv->active_profile = profile_id; + return TRUE; +} + +static void +wp_endpoint_class_init (WpEndpointClass * klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + + object_class->dispose = wp_endpoint_dispose; + object_class->finalize = wp_endpoint_finalize; + object_class->get_property = wp_endpoint_get_property; + object_class->set_property = wp_endpoint_set_property; + + klass->get_streams_count = default_get_streams_or_profiles_count; + klass->get_stream_name = default_get_stream_or_profile_name; + klass->get_profiles_count = default_get_streams_or_profiles_count; + klass->get_profile_name = default_get_stream_or_profile_name; + klass->activate_profile = default_activate_profile; + + g_object_class_install_property (object_class, PROP_NAME, + g_param_spec_string ("name", "name", "The name of the endpoint", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_MEDIA_CLASS, + g_param_spec_string ("media-class", "media-class", + "The media class of the endpoint", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +const gchar * +wp_endpoint_get_name (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL); + + priv = wp_endpoint_get_instance_private (self); + return priv->name; +} + +const gchar * +wp_endpoint_get_media_class (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL); + + priv = wp_endpoint_get_instance_private (self); + return priv->media_class; +} + +guint32 +wp_endpoint_get_streams_count (WpEndpoint * self) +{ + g_return_val_if_fail (WP_IS_ENDPOINT (self), 0); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (self)->get_streams_count, 0); + + return WP_ENDPOINT_GET_CLASS (self)->get_streams_count (self); +} + +const gchar * +wp_endpoint_get_stream_name (WpEndpoint * self, guint32 stream_id) +{ + g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (self)->get_stream_name, NULL); + + return WP_ENDPOINT_GET_CLASS (self)->get_stream_name (self, stream_id); +} + +guint32 +wp_endpoint_get_profiles_count (WpEndpoint * self) +{ + g_return_val_if_fail (WP_IS_ENDPOINT (self), 0); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (self)->get_profiles_count, 0); + + return WP_ENDPOINT_GET_CLASS (self)->get_profiles_count (self); +} + +const gchar * +wp_endpoint_get_profile_name (WpEndpoint * self, guint32 profile_id) +{ + g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (self)->get_profile_name, NULL); + + return WP_ENDPOINT_GET_CLASS (self)->get_profile_name (self, profile_id); +} + +guint32 +wp_endpoint_get_active_profile (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (self), 0); + + priv = wp_endpoint_get_instance_private (self); + return priv->active_profile; +} + +gboolean +wp_endpoint_activate_profile (WpEndpoint * self, guint32 profile_id, + GError ** error) +{ + g_return_val_if_fail (WP_IS_ENDPOINT (self), FALSE); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (self)->activate_profile, FALSE); + + return WP_ENDPOINT_GET_CLASS (self)->activate_profile (self, profile_id, + error); +} + +gboolean +wp_endpoint_is_linked (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (self), FALSE); + + priv = wp_endpoint_get_instance_private (self); + return (priv->links->len > 0); +} + +GPtrArray * +wp_endpoint_get_links (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (self), NULL); + + priv = wp_endpoint_get_instance_private (self); + return priv->links; +} + + +typedef struct _WpEndpointLinkPrivate WpEndpointLinkPrivate; +struct _WpEndpointLinkPrivate +{ + WpEndpoint *src; + guint32 src_stream; + WpEndpoint *sink; + guint32 sink_stream; +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEndpointLink, wp_endpoint_link, G_TYPE_OBJECT) + +static void +wp_endpoint_link_init (WpEndpointLink * self) +{ +} + +static void +wp_endpoint_link_class_init (WpEndpointLinkClass * klass) +{ +} + +void +wp_endpoint_link_set_endpoints (WpEndpointLink * self, WpEndpoint * src, + guint32 src_stream, WpEndpoint * sink, guint32 sink_stream) +{ + WpEndpointLinkPrivate *priv; + + g_return_if_fail (WP_IS_ENDPOINT_LINK (self)); + g_return_if_fail (WP_IS_ENDPOINT (src)); + g_return_if_fail (WP_IS_ENDPOINT (sink)); + + priv = wp_endpoint_link_get_instance_private (self); + priv->src = src; + priv->src_stream = src_stream; + priv->sink = sink; + priv->sink_stream = sink_stream; +} + +WpEndpoint * +wp_endpoint_link_get_source_endpoint (WpEndpointLink * self) +{ + WpEndpointLinkPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), NULL); + + priv = wp_endpoint_link_get_instance_private (self); + return priv->src; +} + +guint32 +wp_endpoint_link_get_source_stream (WpEndpointLink * self) +{ + WpEndpointLinkPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), 0); + + priv = wp_endpoint_link_get_instance_private (self); + return priv->src_stream; +} + +WpEndpoint * +wp_endpoint_link_get_sink_endpoint (WpEndpointLink * self) +{ + WpEndpointLinkPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), NULL); + + priv = wp_endpoint_link_get_instance_private (self); + return priv->sink; +} + +guint32 +wp_endpoint_link_get_sink_stream (WpEndpointLink * self) +{ + WpEndpointLinkPrivate *priv; + + g_return_val_if_fail (WP_IS_ENDPOINT_LINK (self), 0); + + priv = wp_endpoint_link_get_instance_private (self); + return priv->sink_stream; +} + +WpEndpointLink * wp_endpoint_link_new (WpCore * core, WpEndpoint * src, + guint32 src_stream, WpEndpoint * sink, guint32 sink_stream, GError ** error) +{ + g_autoptr (WpEndpointLink) link = NULL; + g_autoptr (GVariant) src_props = NULL; + g_autoptr (GVariant) sink_props = NULL; + const gchar *src_factory = NULL, *sink_factory = NULL; + WpEndpointPrivate *endpoint_priv; + + g_return_val_if_fail (WP_IS_ENDPOINT (src), NULL); + g_return_val_if_fail (WP_IS_ENDPOINT (sink), NULL); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (src)->prepare_link, NULL); + g_return_val_if_fail (WP_ENDPOINT_GET_CLASS (sink)->prepare_link, NULL); + + /* find the factory */ + + if (WP_ENDPOINT_GET_CLASS (src)->get_endpoint_link_factory) + src_factory = WP_ENDPOINT_GET_CLASS (src)->get_endpoint_link_factory (src); + if (WP_ENDPOINT_GET_CLASS (sink)->get_endpoint_link_factory) + sink_factory = WP_ENDPOINT_GET_CLASS (sink)->get_endpoint_link_factory (sink); + + if (src_factory || sink_factory) { + if (src_factory && sink_factory && strcmp (src_factory, sink_factory) != 0) { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "It is not possible to link endpoints that both specify different " + "custom link factories"); + return NULL; + } else if (sink_factory) + src_factory = sink_factory; + } else { + src_factory = "pipewire-simple-endpoint-link"; + } + + /* create link object */ + + link = wp_core_make_from_factory (core, src_factory, WP_TYPE_ENDPOINT_LINK, + NULL); + if (!link) { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, + "Failed to create link object from factory %s", src_factory); + return NULL; + } + g_return_val_if_fail (WP_ENDPOINT_LINK_GET_CLASS (link)->create, NULL); + + /* prepare the link */ + + wp_endpoint_link_set_endpoints (link, src, src_stream, sink, sink_stream); + + if (!WP_ENDPOINT_GET_CLASS (src)->prepare_link (src, src_stream, link, + &src_props, error)) + return NULL; + if (!WP_ENDPOINT_GET_CLASS (src)->prepare_link (sink, sink_stream, link, + &sink_props, error)) + return NULL; + + /* create the link */ + + if (!WP_ENDPOINT_LINK_GET_CLASS (link)->create (link, src_props, sink_props, + error)) + return NULL; + + /* register the link on the endpoints */ + + endpoint_priv = wp_endpoint_get_instance_private (src); + g_ptr_array_add (endpoint_priv->links, g_object_ref (link)); + + endpoint_priv = wp_endpoint_get_instance_private (sink); + g_ptr_array_add (endpoint_priv->links, g_object_ref (link)); + + return link; +} + +void +wp_endpoint_link_destroy (WpEndpointLink * self) +{ + WpEndpointLinkPrivate *priv; + WpEndpointPrivate *endpoint_priv; + + g_return_if_fail (WP_IS_ENDPOINT_LINK (self)); + g_return_if_fail (WP_ENDPOINT_LINK_GET_CLASS (self)->destroy); + + priv = wp_endpoint_link_get_instance_private (self); + + WP_ENDPOINT_LINK_GET_CLASS (self)->destroy (self); + if (WP_ENDPOINT_GET_CLASS (priv->src)->release_link) + WP_ENDPOINT_GET_CLASS (priv->src)->release_link (priv->src, self); + if (WP_ENDPOINT_GET_CLASS (priv->sink)->release_link) + WP_ENDPOINT_GET_CLASS (priv->sink)->release_link (priv->sink, self); + + endpoint_priv = wp_endpoint_get_instance_private (priv->src); + g_ptr_array_remove_fast (endpoint_priv->links, self); + + endpoint_priv = wp_endpoint_get_instance_private (priv->sink); + g_ptr_array_remove_fast (endpoint_priv->links, self); +} diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h new file mode 100644 index 00000000..3456a0bc --- /dev/null +++ b/lib/wp/endpoint.h @@ -0,0 +1,82 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WIREPLUMBER_ENDPOINT_H__ +#define __WIREPLUMBER_ENDPOINT_H__ + +#include "core.h" + +G_BEGIN_DECLS + +#define WP_TYPE_ENDPOINT (wp_endpoint_get_type ()) +G_DECLARE_DERIVABLE_TYPE (WpEndpoint, wp_endpoint, WP, ENDPOINT, GObject) + +#define WP_TYPE_ENDPOINT_LINK (wp_endpoint_link_get_type ()) +G_DECLARE_DERIVABLE_TYPE (WpEndpointLink, wp_endpoint_link, WP, ENDPOINT_LINK, GObject) + +struct _WpEndpointClass +{ + GObjectClass parent_class; + + guint32 (*get_streams_count) (WpEndpoint * self); + const gchar * (*get_stream_name) (WpEndpoint * self, guint32 stream_id); + + guint32 (*get_profiles_count) (WpEndpoint * self); + const gchar * (*get_profile_name) (WpEndpoint * self, guint32 profile_id); + gboolean (*activate_profile) (WpEndpoint * self, guint32 profile_id, + GError ** error); + + gboolean (*prepare_link) (WpEndpoint * self, guint32 stream_id, + WpEndpointLink * link, GVariant ** properties, GError ** error); + void (*release_link) (WpEndpoint * self, WpEndpointLink * link); + + const gchar * (*get_endpoint_link_factory) (WpEndpoint * self); +}; + +const gchar * wp_endpoint_get_name (WpEndpoint * self); +const gchar * wp_endpoint_get_media_class (WpEndpoint * self); + +guint32 wp_endpoint_get_streams_count (WpEndpoint * self); +const gchar * wp_endpoint_get_stream_name (WpEndpoint * self, + guint32 stream_id); + +guint32 wp_endpoint_get_profiles_count (WpEndpoint * self); +const gchar * wp_endpoint_get_profile_name (WpEndpoint * self, + guint32 profile_id); +guint32 wp_endpoint_get_active_profile (WpEndpoint * self); +gboolean wp_endpoint_activate_profile (WpEndpoint * self, guint32 profile_id, + GError ** error); + +gboolean wp_endpoint_is_linked (WpEndpoint * self); +GPtrArray * wp_endpoint_get_links (WpEndpoint * self); + +struct _WpEndpointLinkClass +{ + GObjectClass parent_class; + + gboolean (*create) (WpEndpointLink * self, GVariant * src_data, + GVariant * sink_data, GError ** error); + void (*destroy) (WpEndpointLink * self); +}; + +void wp_endpoint_link_set_endpoints (WpEndpointLink * self, WpEndpoint * src, + guint32 src_stream, WpEndpoint * sink, guint32 sink_stream); + +WpEndpoint * wp_endpoint_link_get_source_endpoint (WpEndpointLink * self); +guint32 wp_endpoint_link_get_source_stream (WpEndpointLink * self); +WpEndpoint * wp_endpoint_link_get_sink_endpoint (WpEndpointLink * self); +guint32 wp_endpoint_link_get_sink_stream (WpEndpointLink * self); + +WpEndpointLink * wp_endpoint_link_new (WpCore * core, WpEndpoint * src, + guint32 src_stream, WpEndpoint * sink, guint32 sink_stream, + GError ** error); +void wp_endpoint_link_destroy (WpEndpointLink * self); + +G_END_DECLS + +#endif diff --git a/lib/wp/error.h b/lib/wp/error.h index 3d229d00..fb773bd0 100644 --- a/lib/wp/error.h +++ b/lib/wp/error.h @@ -18,6 +18,8 @@ GQuark wp_domain_library_quark (void); typedef enum { WP_LIBRARY_ERROR_INVARIANT, + WP_LIBRARY_ERROR_INVALID_ARGUMENT, + WP_LIBRARY_ERROR_OPERATION_FAILED, } WpLibraryErrorEnum; G_END_DECLS diff --git a/lib/wp/factory.c b/lib/wp/factory.c new file mode 100644 index 00000000..a49921eb --- /dev/null +++ b/lib/wp/factory.c @@ -0,0 +1,48 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "factory.h" + +G_DEFINE_TYPE (WpFactory, wp_factory, G_TYPE_OBJECT) + +static void +wp_factory_init (WpFactory * self) +{ +} + +static void +wp_factory_finalize (GObject * obj) +{ + WpFactory * self = WP_FACTORY (obj); + + g_free (self->name); + + G_OBJECT_CLASS (wp_factory_parent_class)->finalize (obj); +} + +static void +wp_factory_class_init (WpFactoryClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + object_class->finalize = wp_factory_finalize; +} + +WpFactory * +wp_factory_new (const gchar * name, WpFactoryFunc func) +{ + WpFactory *f; + + g_return_val_if_fail (name != NULL && *name != '\0', NULL); + g_return_val_if_fail (func != NULL, NULL); + + f = g_object_new (WP_TYPE_FACTORY, NULL); + f->name = g_strdup (name); + f->name_quark = g_quark_from_string (f->name); + f->create_object = func; + return f; +} diff --git a/lib/wp/factory.h b/lib/wp/factory.h new file mode 100644 index 00000000..9e993068 --- /dev/null +++ b/lib/wp/factory.h @@ -0,0 +1,69 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WIREPLUMBER_FACTORY_H__ +#define __WIREPLUMBER_FACTORY_H__ + +#include "core.h" + +G_BEGIN_DECLS + +#define WP_TYPE_FACTORY (wp_factory_get_type ()) +G_DECLARE_FINAL_TYPE (WpFactory, wp_factory, WP, FACTORY, GObject) + +typedef gpointer (*WpFactoryFunc) (WpFactory * self, GType type, + GVariant * properties); + +struct _WpFactory +{ + GObject parent; + + gchar *name; + GQuark name_quark; + WpFactoryFunc create_object; +}; + +WpFactory * wp_factory_new (const gchar * name, WpFactoryFunc func); + +static inline const gchar * wp_factory_get_name (WpFactory * factory) +{ + return factory->name; +} + +static inline gpointer wp_factory_create_object (WpFactory * factory, + GType type, GVariant * properties) +{ + return factory->create_object (factory, type, properties); +} + + +static inline gboolean +wp_core_register_factory (WpCore * core, WpFactory * factory) +{ + return wp_core_register_global (core, factory->name_quark, factory, + g_object_unref); +} + +static inline WpFactory * +wp_core_find_factory (WpCore * core, const gchar * name) +{ + return wp_core_get_global (core, g_quark_from_string (name)); +} + +static inline gpointer +wp_core_make_from_factory (WpCore * core, const gchar * name, GType type, + GVariant * properties) +{ + WpFactory *f = wp_core_find_factory (core, name); + if (!f) return NULL; + return wp_factory_create_object (f, type, properties); +} + +G_END_DECLS + +#endif diff --git a/lib/wp/interface-impl.c b/lib/wp/interface-impl.c deleted file mode 100644 index ec2ce88d..00000000 --- a/lib/wp/interface-impl.c +++ /dev/null @@ -1,121 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "interface-impl.h" -#include "object.h" - -typedef struct { - WpObject *object; -} WpInterfaceImplPrivate; - -G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpInterfaceImpl, wp_interface_impl, G_TYPE_OBJECT); - -static void -wp_interface_impl_init (WpInterfaceImpl * self) -{ -} - -static void -wp_interface_impl_finalize (GObject * obj) -{ - WpInterfaceImpl *self = WP_INTERFACE_IMPL (obj); - WpInterfaceImplPrivate *priv = wp_interface_impl_get_instance_private (self); - - g_clear_weak_pointer (&priv->object); - - G_OBJECT_CLASS (wp_interface_impl_parent_class)->finalize (obj); -} - -static void -wp_interface_impl_class_init (WpInterfaceImplClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - object_class->finalize = wp_interface_impl_finalize; -} - -/** - * wp_interface_impl_set_object: (skip) - * @self: the interface implementation instance - * @object: (nullable) (transfer none): the implementor - */ -void -wp_interface_impl_set_object (WpInterfaceImpl * self, WpObject * object) -{ - WpInterfaceImplPrivate *priv = wp_interface_impl_get_instance_private (self); - - g_return_if_fail (WP_IS_INTERFACE_IMPL (self)); - g_return_if_fail (WP_IS_OBJECT (object)); - - g_set_weak_pointer (&priv->object, object); -} - -/** - * wp_interface_impl_get_object: (method) - * @self: the interface implementation instance - * - * Returns: (nullable) (transfer none): the object implementing this interface - */ -WpObject * -wp_interface_impl_get_object (WpInterfaceImpl * self) -{ - WpInterfaceImplPrivate *priv = wp_interface_impl_get_instance_private (self); - - g_return_val_if_fail (WP_IS_INTERFACE_IMPL (self), NULL); - - return priv->object; -} - -/** - * wp_interface_impl_get_sibling: (method) - * @self: the interface implementation instance - * @interface: an interface type - * - * Returns: (type GObject*) (nullable) (transfer none): the object - * implementing @interface - */ -gpointer -wp_interface_impl_get_sibling (WpInterfaceImpl * self, GType interface) -{ - WpInterfaceImplPrivate *priv = wp_interface_impl_get_instance_private (self); - gpointer iface = NULL; - - g_return_val_if_fail (WP_IS_INTERFACE_IMPL (self), NULL); - g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); - - if (g_type_is_a (G_TYPE_FROM_INSTANCE (self), interface)) { - iface = self; - } else if (priv->object) { - iface = wp_object_get_interface (priv->object, interface); - } - - return iface; -} - -/** - * wp_interface_impl_get_prerequisites: (virtual get_prerequisites) - * @self: the interface implementation instance - * @n_prerequisites: (out): the number of elements in the returned array - * - * Returns: (array length=n_prerequisites) (transfer none): the types that are - * required by this interface implementation - */ -GType * -wp_interface_impl_get_prerequisites (WpInterfaceImpl * self, - guint * n_prerequisites) -{ - WpInterfaceImplClass * klass = WP_INTERFACE_IMPL_GET_CLASS (self); - - g_return_val_if_fail (WP_IS_INTERFACE_IMPL (self), NULL); - g_return_val_if_fail (n_prerequisites != NULL, NULL); - - if (klass->get_prerequisites) - return klass->get_prerequisites (self, n_prerequisites); - - *n_prerequisites = 0; - return NULL; -} diff --git a/lib/wp/interface-impl.h b/lib/wp/interface-impl.h deleted file mode 100644 index 068b9fe3..00000000 --- a/lib/wp/interface-impl.h +++ /dev/null @@ -1,45 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_INTERFACE_IMPL_H__ -#define __WP_INTERFACE_IMPL_H__ - -#include - -G_BEGIN_DECLS - -typedef struct _WpObject WpObject; - -#define WP_TYPE_INTERFACE_IMPL (wp_interface_impl_get_type ()) -G_DECLARE_DERIVABLE_TYPE (WpInterfaceImpl, wp_interface_impl, WP, INTERFACE_IMPL, GObject) - -struct _WpInterfaceImplClass -{ - GObjectClass parent_class; - - /** - * get_prerequisites: - * @self: the interface implementation instance - * @n_prerequisites: (out): the number of elements in the returned array - * - * Returns: (array length=n_prerequisites) (transfer none): the types that are - * required by this interface implementation - */ - GType *(*get_prerequisites) (WpInterfaceImpl * self, guint * n_prerequisites); -}; - -void wp_interface_impl_set_object (WpInterfaceImpl * self, WpObject * object); -WpObject * wp_interface_impl_get_object (WpInterfaceImpl * self); -gpointer wp_interface_impl_get_sibling (WpInterfaceImpl * self, - GType interface); -GType * wp_interface_impl_get_prerequisites (WpInterfaceImpl * self, - guint * n_prerequisites); - -G_END_DECLS - -#endif diff --git a/lib/wp/meson.build b/lib/wp/meson.build index e710d2ec..6ac37095 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -1,21 +1,19 @@ wp_lib_sources = [ - 'core-interfaces.c', + 'core.c', + 'endpoint.c', 'error.c', - 'interface-impl.c', - 'object.c', - 'plugin.c', - 'proxy.c', - 'session.c', + 'factory.c', + 'module.c', + 'session-manager.c', ] wp_lib_headers = [ - 'core-interfaces.h', + 'core.h', + 'endpoint.h', 'error.h', - 'interface-impl.h', - 'object.h', - 'plugin.h', - 'proxy.h', - 'session.h', + 'factory.h', + 'module.h', + 'session-manager.h', ] enums = gnome.mkenums_simple('wpenums', sources: wp_lib_headers) @@ -28,14 +26,15 @@ wp_lib = library('wireplumber-' + wireplumber_api_version, '-DG_LOG_DOMAIN="libwireplumber"' ], install: true, - dependencies : [gobject_dep, pipewire_dep, gio_dep], + include_directories: wp_lib_include_dir, + dependencies : [gobject_dep, gmodule_dep], ) gnome.generate_gir(wp_lib, namespace: 'Wp', nsversion: wireplumber_api_version, sources: [wp_lib_sources, wp_lib_headers], - includes: ['GLib-2.0', 'GObject-2.0', 'Gio-2.0'], + includes: ['GLib-2.0', 'GObject-2.0'], install: true, ) diff --git a/lib/wp/module.c b/lib/wp/module.c new file mode 100644 index 00000000..6f4f734a --- /dev/null +++ b/lib/wp/module.c @@ -0,0 +1,137 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "module.h" +#include "error.h" +#include + +#define WP_MODULE_INIT_SYMBOL "wireplumber__module_init" + +typedef void (*WpModuleInitFunc) (WpModule *, WpCore *, GVariant *); + +struct _WpModule +{ + GObject parent; + GVariant *properties; + GDestroyNotify destroy; + gpointer destroy_data; +}; + +G_DEFINE_TYPE (WpModule, wp_module, G_TYPE_OBJECT) + +static void +wp_module_init (WpModule * self) +{ +} + +static void +wp_module_finalize (GObject * object) +{ + WpModule *self = WP_MODULE (object); + + if (self->destroy) + self->destroy (self->destroy_data); + g_clear_pointer (&self->properties, g_variant_unref); + + G_OBJECT_CLASS (wp_module_parent_class)->finalize (object); +} + +static void +wp_module_class_init (WpModuleClass * klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + object_class->finalize = wp_module_finalize; +} + +static const gchar * +get_module_dir (void) +{ + const gchar *module_dir = NULL; + if (!module_dir) + module_dir = g_getenv ("WIREPLUMBER_MODULE_DIR"); + return module_dir; +} + +static gboolean +wp_module_load_c (WpModule * self, WpCore * core, + const gchar * module_name, GVariant * args, GError ** error) +{ + g_autofree gchar *module_path = NULL; + GModule *gmodule; + gpointer module_init; + GVariantDict properties; + + module_path = g_module_build_path (get_module_dir (), module_name); + gmodule = g_module_open (module_path, G_MODULE_BIND_LOCAL); + if (!gmodule) { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, + "Failed to open module %s: %s", module_path, g_module_error ()); + return FALSE; + } + + if (!g_module_symbol (gmodule, WP_MODULE_INIT_SYMBOL, &module_init)) { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED, + "Failed to locate symbol " WP_MODULE_INIT_SYMBOL " in %s", + module_path); + g_module_close (gmodule); + return FALSE; + } + + g_variant_dict_init (&properties, NULL); + g_variant_dict_insert (&properties, "module.name", "s", module_name); + g_variant_dict_insert (&properties, "module.abi", "s", "C"); + g_variant_dict_insert (&properties, "module.path", "s", module_path); + if (args) { + g_variant_take_ref (args); + g_variant_dict_insert_value (&properties, "module.args", args); + } + self->properties = g_variant_ref_sink (g_variant_dict_end (&properties)); + + ((WpModuleInitFunc) module_init) (self, core, args); + + if (args) + g_variant_unref (args); + + return TRUE; +} + +WpModule * +wp_module_load (WpCore * core, const gchar * abi, + const gchar * module_name, GVariant * args, GError ** error) +{ + g_autoptr (WpModule) module = NULL; + + if (!g_strcmp0 (abi, "C")) { + module = g_object_new (WP_TYPE_MODULE, NULL); + if (!wp_module_load_c (module, core, module_name, args, error)) + return NULL; + } else { + g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "unknown module ABI %s", abi); + return NULL; + } + + wp_core_register_global (core, g_quark_from_string (module_name), + g_object_ref (module), g_object_unref); + return module; +} + +GVariant * +wp_module_get_properties (WpModule * self) +{ + return self->properties; +} + +void +wp_module_set_destroy_callback (WpModule * self, GDestroyNotify callback, + gpointer data) +{ + g_return_if_fail (self->destroy == NULL); + self->destroy = callback; + self->destroy_data = data; +} diff --git a/lib/wp/module.h b/lib/wp/module.h new file mode 100644 index 00000000..c157a306 --- /dev/null +++ b/lib/wp/module.h @@ -0,0 +1,29 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WIREPLUMBER_MODULE_H__ +#define __WIREPLUMBER_MODULE_H__ + +#include "core.h" + +G_BEGIN_DECLS + +#define WP_TYPE_MODULE (wp_module_get_type ()) +G_DECLARE_FINAL_TYPE (WpModule, wp_module, WP, MODULE, GObject) + +WpModule * wp_module_load (WpCore * core, const gchar * abi, + const gchar * module_name, GVariant * args, GError ** error); + +GVariant * wp_module_get_properties (WpModule * module); + +void wp_module_set_destroy_callback (WpModule * module, GDestroyNotify callback, + gpointer data); + +G_END_DECLS + +#endif diff --git a/lib/wp/object.c b/lib/wp/object.c deleted file mode 100644 index 0c5a0adc..00000000 --- a/lib/wp/object.c +++ /dev/null @@ -1,225 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "object.h" -#include "interface-impl.h" -#include "error.h" - -typedef struct { - GArray *iface_objects; - GArray *iface_types; -} WpObjectPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (WpObject, wp_object, G_TYPE_OBJECT); - -static void -wp_object_init (WpObject * self) -{ - WpObjectPrivate *priv = wp_object_get_instance_private (self); - - priv->iface_objects = g_array_new (FALSE, FALSE, sizeof (gpointer)); - priv->iface_types = g_array_new (FALSE, FALSE, sizeof (GType)); -} - -static void -wp_object_finalize (GObject * obj) -{ - WpObject *self = WP_OBJECT (obj); - WpObjectPrivate *priv = wp_object_get_instance_private (self); - guint i; - - for (i = 0; i < priv->iface_objects->len; i++) { - GObject *obj = g_array_index (priv->iface_objects, GObject*, i); - wp_interface_impl_set_object (WP_INTERFACE_IMPL (obj), NULL); - g_object_unref (obj); - } - - g_array_free (priv->iface_objects, TRUE); - g_array_free (priv->iface_types, TRUE); - - G_OBJECT_CLASS (wp_object_parent_class)->finalize (obj); -} - -static void -wp_object_class_init (WpObjectClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - object_class->finalize = wp_object_finalize; -} - -/** - * wp_object_implements_interface: (method) - * @self: the object - * @interface: an interface type - * - * Returns: whether the interface is implemented in this object or not - */ -gboolean -wp_object_implements_interface (WpObject * self, GType interface) -{ - WpObjectPrivate *priv = wp_object_get_instance_private (self); - guint i; - - g_return_val_if_fail (WP_IS_OBJECT (self), FALSE); - - for (i = 0; i < priv->iface_types->len; i++) { - GType t = g_array_index (priv->iface_types, GType, i); - if (t == interface) - return TRUE; - } - - return FALSE; -} - -/** - * wp_object_get_interface: (method) - * @self: the object - * @interface: an interface type - * - * Returns: (type GObject*) (nullable) (transfer none): the object - * implementing @interface - */ -gpointer -wp_object_get_interface (WpObject * self, GType interface) -{ - WpObjectPrivate *priv = wp_object_get_instance_private (self); - guint i; - - g_return_val_if_fail (WP_IS_OBJECT (self), FALSE); - - for (i = 0; i < priv->iface_objects->len; i++) { - GObject *obj = g_array_index (priv->iface_objects, GObject*, i); - if (g_type_is_a (G_TYPE_FROM_INSTANCE (obj), interface)) - return obj; - } - - return NULL; -} - -/** - * wp_object_list_interfaces: (method) - * @self: the object - * @n_interfaces: (out): the number of elements in the returned array - * - * Returns: (array length=n_interfaces) (transfer none): the interface types - * that are implemented in this object - */ -GType * -wp_object_list_interfaces (WpObject * self, guint * n_interfaces) -{ - WpObjectPrivate *priv = wp_object_get_instance_private (self); - - g_return_val_if_fail (WP_IS_OBJECT (self), NULL); - - *n_interfaces = priv->iface_types->len; - return (GType *) priv->iface_types->data; -} - -/** - * wp_object_attach_interface_impl: (method) - * @self: the object - * @impl: (type WpInterfaceImpl*) (transfer none): the interface implementation - * @error: (out): a GError to return on failure - * - * Returns: TRUE one success, FALSE on error - */ -gboolean -wp_object_attach_interface_impl (WpObject * self, gpointer impl, - GError ** error) -{ - WpObjectPrivate *priv = wp_object_get_instance_private (self); - GType *new_ifaces; - GType *prerequisites; - guint n_new_ifaces; - guint n_prerequisites, n_satisfied = 0; - guint i, j; - - g_return_val_if_fail (WP_IS_OBJECT (self), FALSE); - g_return_val_if_fail (WP_IS_INTERFACE_IMPL (impl), FALSE); - - new_ifaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (impl), - &n_new_ifaces); - prerequisites = wp_interface_impl_get_prerequisites (impl, &n_prerequisites); - - for (i = 0; i < priv->iface_types->len; i++) { - GType t = g_array_index (priv->iface_types, GType, i); - - for (j = 0; j < n_prerequisites; j++) { - if (prerequisites[j] == t) - n_satisfied++; - } - - for (j = 0; j < n_new_ifaces; j++) { - if (new_ifaces[j] == t) { - g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, - "Interface %s is already provided on object %p", - g_type_name (t), self); - return FALSE; - } - } - } - - if (n_satisfied != n_prerequisites) { - g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVARIANT, - "Interface implementation %p has unsatisfied requirements", impl); - return FALSE; - } - - g_object_ref (impl); - g_array_append_val (priv->iface_objects, impl); - g_array_append_vals (priv->iface_types, new_ifaces, n_new_ifaces); - wp_interface_impl_set_object (WP_INTERFACE_IMPL (impl), self); - return TRUE; -} - -/* WpPipewireProperties */ - -G_DEFINE_INTERFACE (WpPipewireProperties, wp_pipewire_properties, G_TYPE_OBJECT) - -static void -wp_pipewire_properties_default_init (WpPipewirePropertiesInterface * iface) -{ -} - -/** - * wp_pipewire_properties_get: (virtual get) - * @self: the interface - * @key: the name of the property to lookup - * - * Return: (transfer none): The value of the underlying PipeWire object's - * property with this @key, or %NULL. - */ -const gchar * -wp_pipewire_properties_get (WpPipewireProperties * self, const gchar * key) -{ - WpPipewirePropertiesInterface *iface = WP_PIPEWIRE_PROPERTIES_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PIPEWIRE_PROPERTIES (self), NULL); - g_return_val_if_fail (key != NULL, NULL); - g_return_val_if_fail (iface->get, NULL); - - return iface->get (self, key); -} - -/** - * wp_pipewire_properties_get_as_spa_dict: (virtual get_as_spa_dict) - * @self: the interface - * - * Return: (transfer none): The underlying `struct spa_dict` that holds - * the properties - */ -const struct spa_dict * -wp_pipewire_properties_get_as_spa_dict (WpPipewireProperties * self) -{ - WpPipewirePropertiesInterface *iface = WP_PIPEWIRE_PROPERTIES_GET_IFACE (self); - - g_return_val_if_fail (WP_IS_PIPEWIRE_PROPERTIES (self), NULL); - g_return_val_if_fail (iface->get_as_spa_dict, NULL); - - return iface->get_as_spa_dict (self); -} diff --git a/lib/wp/object.h b/lib/wp/object.h deleted file mode 100644 index 0d6bee7b..00000000 --- a/lib/wp/object.h +++ /dev/null @@ -1,54 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_OBJECT_H__ -#define __WP_OBJECT_H__ - -#include - -G_BEGIN_DECLS - -#define WP_TYPE_OBJECT (wp_object_get_type ()) -G_DECLARE_DERIVABLE_TYPE (WpObject, wp_object, WP, OBJECT, GObject) - -struct _WpObjectClass -{ - GObjectClass parent_class; -}; - -gboolean wp_object_implements_interface (WpObject * self, GType interface); -gpointer wp_object_get_interface (WpObject * self, GType interface); -GType * wp_object_list_interfaces (WpObject * self, guint * n_interfaces); - -gboolean wp_object_attach_interface_impl (WpObject * self, gpointer impl, - GError ** error); - -/* WpPipewireProperties */ - -#define WP_TYPE_PIPEWIRE_PROPERTIES (wp_pipewire_properties_get_type ()) -G_DECLARE_INTERFACE (WpPipewireProperties, wp_pipewire_properties, - WP, PIPEWIRE_PROPERTIES, GObject) - -struct spa_dict; - -struct _WpPipewirePropertiesInterface -{ - GTypeInterface parent; - - const gchar * (*get) (WpPipewireProperties * self, const gchar * key); - const struct spa_dict * (*get_as_spa_dict) (WpPipewireProperties * self); -}; - -const gchar * wp_pipewire_properties_get (WpPipewireProperties * self, - const gchar * key); -const struct spa_dict * wp_pipewire_properties_get_as_spa_dict ( - WpPipewireProperties * self); - -G_END_DECLS - -#endif diff --git a/lib/wp/plugin.c b/lib/wp/plugin.c deleted file mode 100644 index caf6cb99..00000000 --- a/lib/wp/plugin.c +++ /dev/null @@ -1,277 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "plugin.h" -#include - -enum { - PROP_0, - PROP_RANK, - PROP_NAME, - PROP_DESCRIPTION, - PROP_AUTHOR, - PROP_LICENSE, - PROP_VERSION, - PROP_ORIGIN, - PROP_CORE, - PROP_METADATA, -}; - -typedef struct { - WpObject *core; - const WpPluginMetadata *metadata; -} WpPluginPrivate; - -G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpPlugin, wp_plugin, G_TYPE_OBJECT); - -static void -wp_plugin_init (WpPlugin * self) -{ -} - -static void -wp_plugin_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - WpPlugin *plugin = WP_PLUGIN (object); - WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin); - - switch (property_id) { - case PROP_CORE: - priv->core = g_value_get_object (value); - break; - case PROP_METADATA: - priv->metadata = g_value_get_pointer (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_plugin_get_property (GObject * object, guint property_id, GValue * value, - GParamSpec * pspec) -{ - WpPlugin *plugin = WP_PLUGIN (object); - WpPluginPrivate *priv = wp_plugin_get_instance_private (plugin); - - switch (property_id) { - case PROP_RANK: - g_value_set_uint (value, priv->metadata->rank); - break; - case PROP_NAME: - g_value_set_string (value, priv->metadata->name); - break; - case PROP_DESCRIPTION: - g_value_set_string (value, priv->metadata->description); - break; - case PROP_AUTHOR: - g_value_set_string (value, priv->metadata->author); - break; - case PROP_LICENSE: - g_value_set_string (value, priv->metadata->license); - break; - case PROP_VERSION: - g_value_set_string (value, priv->metadata->version); - break; - case PROP_ORIGIN: - g_value_set_string (value, priv->metadata->origin); - break; - case PROP_CORE: - g_value_set_object (value, priv->core); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static gboolean -default_handle_pw_proxy (WpPlugin * self, WpProxy * proxy) -{ - WpPluginPrivate *priv = wp_plugin_get_instance_private (self); - - switch (wp_proxy_get_spa_type (proxy)) { - case PW_TYPE_INTERFACE_Device: - return wp_plugin_handle_pw_device (self, proxy); - - case PW_TYPE_INTERFACE_Client: - return wp_plugin_handle_pw_client (self, proxy); - - case PW_TYPE_INTERFACE_Node: - { - WpProxyRegistry *reg; - g_autoptr (WpProxy) parent; - - reg = wp_object_get_interface (priv->core, WP_TYPE_PROXY_REGISTRY); - parent = wp_proxy_registry_get_proxy (reg, wp_proxy_get_parent_id (proxy)); - - switch (wp_proxy_get_spa_type (parent)) { - case PW_TYPE_INTERFACE_Device: - return wp_plugin_handle_pw_device_node (self, proxy); - - case PW_TYPE_INTERFACE_Client: - return wp_plugin_handle_pw_client_node (self, proxy); - } - } - - default: - return FALSE; - } -} - -static void -wp_plugin_class_init (WpPluginClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - - klass->handle_pw_proxy = default_handle_pw_proxy; - - object_class->get_property = wp_plugin_get_property; - object_class->set_property = wp_plugin_set_property; - - g_object_class_install_property (object_class, PROP_RANK, - g_param_spec_uint ("rank", "Rank", "The plugin rank", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_NAME, - g_param_spec_string ("name", "Name", "The plugin's name", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_DESCRIPTION, - g_param_spec_string ("description", "Description", - "The plugin's description", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_AUTHOR, - g_param_spec_string ("author", "Author", "The plugin's author", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_LICENSE, - g_param_spec_string ("license", "License", "The plugin's license", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_VERSION, - g_param_spec_string ("version", "Version", "The plugin's version", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_ORIGIN, - g_param_spec_string ("origin", "Origin", "The plugin's origin", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_CORE, - g_param_spec_object ("core", "Core", "The WpCore that owns this plugin", - WP_TYPE_OBJECT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_METADATA, - g_param_spec_pointer ("metadata", "metadata", "metadata", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | - G_PARAM_PRIVATE)); -} - -/** - * wp_plugin_handle_pw_proxy: (virtual handle_pw_proxy) - */ -gboolean -wp_plugin_handle_pw_proxy (WpPlugin * self, WpProxy * proxy) -{ - if (WP_PLUGIN_GET_CLASS (self)->handle_pw_proxy) - return WP_PLUGIN_GET_CLASS (self)->handle_pw_proxy (self, proxy); - else - return FALSE; -} - -/** - * wp_plugin_handle_pw_device: (virtual handle_pw_device) - */ -gboolean -wp_plugin_handle_pw_device (WpPlugin * self, WpProxy * proxy) -{ - if (WP_PLUGIN_GET_CLASS (self)->handle_pw_device) - return WP_PLUGIN_GET_CLASS (self)->handle_pw_device (self, proxy); - else - return FALSE; -} - -/** - * wp_plugin_handle_pw_device_node: (virtual handle_pw_device_node) - */ -gboolean -wp_plugin_handle_pw_device_node (WpPlugin * self, WpProxy * proxy) -{ - if (WP_PLUGIN_GET_CLASS (self)->handle_pw_device_node) - return WP_PLUGIN_GET_CLASS (self)->handle_pw_device_node (self, proxy); - else - return FALSE; -} - -/** - * wp_plugin_handle_pw_client: (virtual handle_pw_client) - */ -gboolean -wp_plugin_handle_pw_client (WpPlugin * self, WpProxy * proxy) -{ - if (WP_PLUGIN_GET_CLASS (self)->handle_pw_client) - return WP_PLUGIN_GET_CLASS (self)->handle_pw_client (self, proxy); - else - return FALSE; -} - -/** - * wp_plugin_handle_pw_client_node: (virtual handle_pw_client_node) - */ -gboolean -wp_plugin_handle_pw_client_node (WpPlugin * self, WpProxy * proxy) -{ - if (WP_PLUGIN_GET_CLASS (self)->handle_pw_client_node) - return WP_PLUGIN_GET_CLASS (self)->handle_pw_client_node (self, proxy); - else - return FALSE; -} - -/** - * wp_plugin_provide_interfaces: (virtual provide_interfaces) - */ -gboolean -wp_plugin_provide_interfaces (WpPlugin * self, WpObject * object) -{ - if (WP_PLUGIN_GET_CLASS (self)->provide_interfaces) - return WP_PLUGIN_GET_CLASS (self)->provide_interfaces (self, object); - else - return FALSE; -} - -/** - * wp_plugin_get_core: (method) - * @self: the plugin - * - * Returns: (transfer full): the core where this plugin is registered - */ -WpObject * -wp_plugin_get_core (WpPlugin * self) -{ - WpPluginPrivate *priv = wp_plugin_get_instance_private (self); - return g_object_ref (priv->core); -} - -/** - * wp_plugin_get_metadata: (skip) - * @self: the plugin - * - * This is intended for C/C++ only. Use the #WpPlugin properties in bindings. - * - * Returns: the metadata structure associated with this plugin - */ -const WpPluginMetadata * -wp_plugin_get_metadata (WpPlugin * self) -{ - WpPluginPrivate *priv = wp_plugin_get_instance_private (self); - return priv->metadata; -} diff --git a/lib/wp/plugin.h b/lib/wp/plugin.h deleted file mode 100644 index d688ede9..00000000 --- a/lib/wp/plugin.h +++ /dev/null @@ -1,190 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_PLUGIN_H__ -#define __WP_PLUGIN_H__ - -#include "object.h" -#include "proxy.h" -#include "core-interfaces.h" - -G_BEGIN_DECLS - -/** - * WpPluginRank: - * @WP_PLUGIN_RANK_UPSTREAM: should only be used inside WirePlumber - * @WP_PLUGIN_RANK_PLATFORM_OVERRIDE: plugins provided by the platform, - * possibly to provide a platform-specific policy - * @WP_PLUGIN_RANK_VENDOR_OVERRIDE: plugins provided by hardware vendors - * to provide hardware-specific device handling and/or policies - * - * The rank of a plugin is an unsigned integer that can take an arbitrary - * value. On invocation, plugins ranked with a higher number are tried first, - * which is how one can implement overrides. This enum provides default - * values for certain kinds of plugins. Feel free to add/substract numbers - * to these constants in order to make a hierarchy, if you are implementing - * multiple different plugins that need to be tried in a certain order. - */ -typedef enum { - WP_PLUGIN_RANK_UPSTREAM = 0, - WP_PLUGIN_RANK_PLATFORM_OVERRIDE = 128, - WP_PLUGIN_RANK_VENDOR_OVERRIDE = 256, -} WpPluginRank; - -/** - * WpPluginMetadata: (skip) - * @rank: the rank of the plugin - * @name: the name of the plugin - * @description: plugin description - * @author: author , author2 - * @license: a SPDX license ID or "Proprietary" - * @version: the version of the plugin - * @origin: URL or short reference of where this plugin came from - * - * Metadata for registering a plugin (for the C API). - * You should normally never need to use this directly. - * Use WP_PLUGIN_DEFINE() instead. - */ -struct _WpPluginMetadata -{ - union { - guint rank; - gpointer _unused_for_alignment[2]; - }; - const gchar *name; - const gchar *description; - const gchar *author; - const gchar *license; - const gchar *version; - const gchar *origin; -}; - -#define WP_TYPE_PLUGIN (wp_plugin_get_type ()) -G_DECLARE_DERIVABLE_TYPE (WpPlugin, wp_plugin, WP, PLUGIN, GObject) - -struct _WpPluginClass -{ - GObjectClass parent_class; - - /** - * handle_pw_proxy: - * @self: the plugin - * @proxy: (transfer none): the proxy - * - * This method is called for every new proxy that appears in PipeWire. - * The default implementation will inspect the proxy type and will dispatch - * the call to one of the specialized methods available below. - * Override only for very special cases. - */ - gboolean (*handle_pw_proxy) (WpPlugin * self, WpProxy * proxy); - - /** - * handle_pw_device: - * @self: the plugin - * @proxy: (transfer none): the device proxy - * - * This method is called for every new PipeWire proxy of type - * `PipeWire:Interface:Device`. The implementation is expected to create - * a new #WpDevice and register it with the #WpDeviceManager. - * - * The default implementation returns FALSE. - * Override if you are implementing custom device management. - * - * Returns: TRUE if the device was handled, FALSE otherwise. - */ - gboolean (*handle_pw_device) (WpPlugin * self, WpProxy * proxy); - - /** - * handle_pw_device_node: - * @self: the plugin - * @proxy: (transfer none): the node proxy - * - * This method is called for every new PipeWire proxy of type - * `PipeWire:Interface:Node` whose parent proxy is a - * `PipeWire:Interface:Device`. - * - * The default implementation returns FALSE. - * Override if you are implementing custom device management. - * - * Returns: TRUE if the node was handled, FALSE otherwise. - */ - gboolean (*handle_pw_device_node) (WpPlugin * self, WpProxy * proxy); - - /** - * handle_pw_client: - * @self: the plugin - * @proxy: (transfer none): the client proxy - * - * This method is called for every new PipeWire proxy of type - * `PipeWire:Interface:Client`. The implementation is expected to update - * the client's permissions, if necessary. - * - * The default implementation returns FALSE. - * Override if you are implementing custom policy management. - * - * Returns: TRUE if the client was handled, FALSE otherwise. - */ - gboolean (*handle_pw_client) (WpPlugin * self, WpProxy * proxy); - - /** - * handle_pw_client_node: - * @self: the plugin - * @proxy: (transfer none): the node proxy - * - * This method is called for every new PipeWire proxy of type - * `PipeWire:Interface:Node` whose parent proxy is a - * `PipeWire:Interface:Client`. The implementation is expected to create - * a new #WpStream in some #WpSession. - * - * The default implementation returns FALSE. - * Override if you are implementing custom policy management. - * - * Returns: TRUE if the node was handled, FALSE otherwise. - */ - gboolean (*handle_pw_client_node) (WpPlugin * self, WpProxy * proxy); - - /** - * provide_interfaces: - * @self: the plugin - * @object: (transfer none): a #WpObject - * - * This method is called for every new #WpObject created in WirePlumber. - * The implementation is expected to attach any interface implementations - * that it can provide for this kind of object, if necessary, only if - * these interfaces have not already been attached on the @object. - * - * The default implementation returns FALSE. - * Override if you are providing custom interface implementations for objects. - * - * Returns: TRUE if the node was handled, FALSE otherwise. - */ - gboolean (*provide_interfaces) (WpPlugin * self, WpObject * object); -}; - -gboolean wp_plugin_handle_pw_proxy (WpPlugin * self, WpProxy * proxy); -gboolean wp_plugin_handle_pw_device (WpPlugin * self, WpProxy * proxy); -gboolean wp_plugin_handle_pw_device_node (WpPlugin * self, WpProxy * proxy); -gboolean wp_plugin_handle_pw_client (WpPlugin * self, WpProxy * proxy); -gboolean wp_plugin_handle_pw_client_node (WpPlugin * self, WpProxy * proxy); -gboolean wp_plugin_provide_interfaces (WpPlugin * self, WpObject * object); - -WpObject * wp_plugin_get_core (WpPlugin * self); -const WpPluginMetadata * wp_plugin_get_metadata (WpPlugin * self); - - -/** - * WP_MODULE_INIT_SYMBOL: (skip) - * - * The linker symbol that serves as an entry point in modules - */ -#define WP_MODULE_INIT_SYMBOL wireplumber__module_init - - -G_END_DECLS - -#endif diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c deleted file mode 100644 index 3e99cd4d..00000000 --- a/lib/wp/proxy.c +++ /dev/null @@ -1,611 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "proxy.h" -#include "error.h" -#include -#include -#include - -struct _WpProxy -{ - GObject parent; - - WpObject *core; - - struct pw_proxy *proxy; - guint32 id; - guint32 parent_id; - guint32 type; - const gchar *type_string; - - struct spa_hook proxy_listener; - struct spa_hook proxy_proxy_listener; - - union { - const struct spa_dict *initial_properties; - struct pw_properties *properties; - }; - - union { - gpointer info; - struct pw_node_info *node_info; - struct pw_port_info *port_info; - struct pw_factory_info *factory_info; - struct pw_link_info *link_info; - struct pw_client_info *client_info; - struct pw_module_info *module_info; - struct pw_device_info *device_info; - }; - - GList *tasks; -}; - -enum { - PROP_0, - PROP_ID, - PROP_PARENT_ID, - PROP_SPA_TYPE, - PROP_SPA_TYPE_STRING, - PROP_INITIAL_PROPERTIES, - PROP_CORE, - PROP_PROXY, -}; - -enum { - SIGNAL_DESTROYED, - SIGNAL_CHANGED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS]; -static int global_seq = 0; - -static void wp_proxy_pw_properties_init (WpPipewirePropertiesInterface * iface); - -G_DEFINE_TYPE_WITH_CODE (WpProxy, wp_proxy, WP_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_PROPERTIES, wp_proxy_pw_properties_init); -) - -struct task_data { - guint32 seq; - GPtrArray *result; -}; - -/* Updates the info structure while copying the properties into self->properties - * and avoiding making a second copy of the properties dict in info->props */ -#define PROXY_INFO_FUNC(TYPE, type) \ - static void \ - type##_event_info (void *object, const struct pw_##type##_info *info) \ - { \ - WpProxy *self = WP_PROXY (object); \ - \ - if (info->change_mask & PW_##TYPE##_CHANGE_MASK_PROPS) { \ - g_clear_pointer (&self->properties, pw_properties_free); \ - self->properties = pw_properties_new_dict (info->props); \ - } \ - \ - { \ - uint64_t change_mask = info->change_mask; \ - ((struct pw_##type##_info *)info)->change_mask &= ~PW_##TYPE##_CHANGE_MASK_PROPS; \ - self->type##_info = pw_##type##_info_update (self->type##_info, info); \ - self->type##_info->props = &self->properties->dict; \ - ((struct pw_##type##_info *)info)->change_mask = change_mask; \ - } \ - \ - g_signal_emit (self, signals[SIGNAL_CHANGED], 0); \ - } - -#define PROXY_PARAM_FUNC(type) \ - static void \ - type##_event_param(void *object, int seq, uint32_t id, uint32_t index, \ - uint32_t next, const struct spa_pod *param) \ - { \ - WpProxy *self = WP_PROXY (object); \ - GList *l = self->tasks; \ - struct task_data *data = NULL; \ - \ - /* FIXME: seq is not propagated properly \ - while (l) { \ - data = g_task_get_task_data (G_TASK (l->data)); \ - if (data && data->seq == seq) \ - break; \ - l = g_list_next (l); \ - } \ - */ \ - g_return_if_fail (l != NULL); \ - data = g_task_get_task_data (G_TASK (l->data)); \ - g_ptr_array_add (data->result, spa_pod_copy (param)); \ - } - -PROXY_INFO_FUNC (NODE, node) -PROXY_PARAM_FUNC(node) - -static const struct pw_node_proxy_events node_events = { - PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info, - .param = node_event_param, -}; - -PROXY_INFO_FUNC (PORT, port) -PROXY_PARAM_FUNC(port) - -static const struct pw_port_proxy_events port_events = { - PW_VERSION_PORT_PROXY_EVENTS, - .info = port_event_info, - .param = port_event_param, -}; - -PROXY_INFO_FUNC (FACTORY, factory) - -static const struct pw_factory_proxy_events factory_events = { - PW_VERSION_FACTORY_PROXY_EVENTS, - .info = factory_event_info -}; - -PROXY_INFO_FUNC (LINK, link) - -static const struct pw_link_proxy_events link_events = { - PW_VERSION_LINK_PROXY_EVENTS, - .info = link_event_info -}; - -PROXY_INFO_FUNC (CLIENT, client) - -static const struct pw_client_proxy_events client_events = { - PW_VERSION_CLIENT_PROXY_EVENTS, - .info = client_event_info -}; - -PROXY_INFO_FUNC (MODULE, module) - -static const struct pw_module_proxy_events module_events = { - PW_VERSION_MODULE_PROXY_EVENTS, - .info = module_event_info -}; - -PROXY_INFO_FUNC (DEVICE, device) -PROXY_PARAM_FUNC (device) - -static const struct pw_device_proxy_events device_events = { - PW_VERSION_DEVICE_PROXY_EVENTS, - .info = device_event_info, - .param = device_event_param, -}; - -static void -proxy_event_destroy (void *object) -{ - WpProxy *self = WP_PROXY (object); - - g_debug ("proxy %u destroyed", self->id); - - self->proxy = NULL; - g_signal_emit (self, signals[SIGNAL_DESTROYED], 0); - g_object_unref (self); -} - -static void -proxy_event_done (void *object, int seq) -{ - WpProxy *self = WP_PROXY (object); - GList *l = self->tasks; - struct task_data *data = NULL; - - /* FIXME: seq is not propagated properly */ -#if 0 - while (l) { - data = g_task_get_task_data (G_TASK (l->data)); - if (data && data->seq == seq) - break; - l = g_list_next (l); - } -#endif - g_return_if_fail (l != NULL); - data = g_task_get_task_data (G_TASK (l->data)); - g_task_return_pointer (G_TASK (l->data), g_ptr_array_ref (data->result), - (GDestroyNotify) g_ptr_array_unref); - g_object_unref (l->data); - self->tasks = g_list_remove_link (self->tasks, l); -} - -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) -{ -} - -static void -wp_proxy_constructed (GObject * object) -{ - WpProxy *self = WP_PROXY (object); - WpProxyRegistry *pr = NULL; - struct pw_registry_proxy *reg_proxy; - const void *events = NULL; - uint32_t ver = 0; - - self->type_string = spa_debug_type_find_name (pw_type_info (), self->type); - - g_debug ("added id %u, parent %u, type %s", self->id, self->parent_id, - self->type_string); - - switch (self->type) { - case PW_TYPE_INTERFACE_Node: - events = &node_events; - ver = PW_VERSION_NODE; - break; - case PW_TYPE_INTERFACE_Port: - events = &port_events; - ver = PW_VERSION_PORT; - break; - case PW_TYPE_INTERFACE_Factory: - events = &factory_events; - ver = PW_VERSION_FACTORY; - break; - case PW_TYPE_INTERFACE_Link: - events = &link_events; - ver = PW_VERSION_LINK; - break; - case PW_TYPE_INTERFACE_Client: - events = &client_events; - ver = PW_VERSION_CLIENT; - break; - case PW_TYPE_INTERFACE_Module: - events = &module_events; - ver = PW_VERSION_MODULE; - break; - case PW_TYPE_INTERFACE_Device: - events = &device_events; - ver = PW_VERSION_DEVICE; - break; - default: - break; - } - - pr = wp_object_get_interface (self->core, WP_TYPE_PROXY_REGISTRY); - reg_proxy = wp_proxy_registry_get_pw_registry_proxy (pr); - g_warn_if_fail (reg_proxy != NULL); - - self->proxy = pw_registry_proxy_bind (reg_proxy, self->id, self->type, ver, 0); - pw_proxy_add_listener (self->proxy, &self->proxy_listener, &proxy_events, - self); - - /* this reference is held by the pw_proxy, released in proxy_event_destroy() */ - g_object_ref (self); - - if (events) - pw_proxy_add_proxy_listener(self->proxy, &self->proxy_proxy_listener, - events, self); - - /* - * initial_properties is a stack-allocated const spa_dict * - * that is not safe to access beyond the scope of the g_object_new() - * call, so we replace it with a pw_properties - */ - if (self->initial_properties) - self->properties = pw_properties_new_dict (self->initial_properties); - else - self->properties = pw_properties_new (NULL); - - G_OBJECT_CLASS (wp_proxy_parent_class)->constructed (object); -} - -static void -wp_proxy_finalize (GObject * object) -{ - WpProxy *self = WP_PROXY (object); - - g_clear_pointer (&self->properties, pw_properties_free); - - G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); -} - -static void -wp_proxy_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - WpProxy *self = WP_PROXY (object); - - switch (property_id) { - case PROP_ID: - self->id = g_value_get_uint (value); - break; - case PROP_PARENT_ID: - self->parent_id = g_value_get_uint (value); - break; - case PROP_SPA_TYPE: - self->type = g_value_get_uint (value); - break; - case PROP_INITIAL_PROPERTIES: - self->initial_properties = g_value_get_pointer (value); - break; - case PROP_CORE: - self->core = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_proxy_get_property (GObject * object, guint property_id, GValue * value, - GParamSpec * pspec) -{ - WpProxy *self = WP_PROXY (object); - - switch (property_id) { - case PROP_ID: - g_value_set_uint (value, self->id); - break; - case PROP_PARENT_ID: - g_value_set_uint (value, self->parent_id); - break; - case PROP_SPA_TYPE: - g_value_set_uint (value, self->type); - break; - case PROP_SPA_TYPE_STRING: - g_value_set_string (value, self->type_string); - break; - case PROP_CORE: - g_value_set_object (value, self->core); - break; - case PROP_PROXY: - g_value_set_pointer (value, self->proxy); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_proxy_class_init (WpProxyClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - - object_class->constructed = wp_proxy_constructed; - object_class->finalize = wp_proxy_finalize; - object_class->get_property = wp_proxy_get_property; - object_class->set_property = wp_proxy_set_property; - - g_object_class_install_property (object_class, PROP_ID, - g_param_spec_uint ("id", "id", - "The global ID of the object", 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_PARENT_ID, - g_param_spec_uint ("parent-id", "parent-id", - "The global ID of the parent object", 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_SPA_TYPE, - g_param_spec_uint ("spa-type", "spa-type", - "The SPA type of the object", 0, G_MAXUINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_SPA_TYPE_STRING, - g_param_spec_string ("spa-type-string", "spa-type-string", - "The string representation of the SPA type of the object", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_INITIAL_PROPERTIES, - g_param_spec_pointer ("initial-properties", "initial-properties", - "The initial set of properties of the proxy", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_CORE, - g_param_spec_object ("core", "core", "The core that owns this proxy", - WP_TYPE_OBJECT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_PROXY, - g_param_spec_pointer ("proxy", "proxy", - "The underlying struct pw_proxy *", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - signals[SIGNAL_DESTROYED] = g_signal_new ("destroyed", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - signals[SIGNAL_CHANGED] = g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); -} - -static const gchar * -wp_proxy_pw_properties_get (WpPipewireProperties * p, const gchar * property) -{ - WpProxy * self = WP_PROXY (p); - return pw_properties_get (self->properties, property); -} - -static const struct spa_dict * -wp_proxy_pw_properties_get_as_spa_dict (WpPipewireProperties * p) -{ - WpProxy * self = WP_PROXY (p); - return &self->properties->dict; -} - -static void -wp_proxy_pw_properties_init (WpPipewirePropertiesInterface * iface) -{ - iface->get = wp_proxy_pw_properties_get; - iface->get_as_spa_dict = wp_proxy_pw_properties_get_as_spa_dict; -} - -/** - * wp_proxy_get_id: (method) - * @self: the proxy - * - * Returns: the global ID of the remote object - */ -guint32 -wp_proxy_get_id (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -1); - return self->id; -} - -/** - * wp_proxy_get_parent_id: (method) - * @self: the proxy - * - * Returns: the global ID of the parent remote object - */ -guint32 -wp_proxy_get_parent_id (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -1); - return self->parent_id; -} - -/** - * wp_proxy_get_spa_type: (method) - * @self: the proxy - * - * Returns: the SPA type of the remote object - */ -guint32 -wp_proxy_get_spa_type (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), -1); - return self->type; -} - -/** - * wp_proxy_get_spa_type_string: (method) - * @self: the proxy - * - * Returns: (transfer none): the string that describes the SPA type of - * the remote object - */ -const gchar * -wp_proxy_get_spa_type_string (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), NULL); - return self->type_string; -} - -/** - * wp_proxy_get_core: (method) - * @self: the proxy - * - * Returns: (transfer full): the core #WpObject - */ -WpObject * -wp_proxy_get_core (WpProxy *self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), NULL); - return g_object_ref (self->core); -} - -/** - * wp_proxy_is_destroyed: (method) - * @self: the proxy - * - * Returns: TRUE if the proxy has been destroyed - */ -gboolean -wp_proxy_is_destroyed (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), TRUE); - return (self->proxy == NULL); -} - -/** - * wp_proxy_get_pw_proxy: (skip) - * @self: the proxy - * - * Returns: the pw_proxy pointer or %NULL if the proxy is destroyed - */ -struct pw_proxy * -wp_proxy_get_pw_proxy (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), NULL); - return self->proxy; -} - -gconstpointer -wp_proxy_get_info_native (WpProxy * self) -{ - g_return_val_if_fail (WP_IS_PROXY (self), NULL); - return self->info; -} - -static void -task_data_free (struct task_data * data) -{ - g_ptr_array_unref (data->result); - g_slice_free (struct task_data, data); -} - -void -wp_proxy_enum_params (WpProxy * self, guint32 id, - GAsyncReadyCallback callback, gpointer user_data) -{ - g_autoptr (GTask) task = NULL; - int seq; - struct task_data *data; - - g_return_if_fail (WP_IS_PROXY (self)); - g_return_if_fail (callback != NULL); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_source_tag (task, wp_proxy_enum_params); - - data = g_slice_new0 (struct task_data); - data->seq = seq = global_seq++; - data->result = g_ptr_array_new_with_free_func (free); - g_task_set_task_data (task, data, (GDestroyNotify) task_data_free); - - self->tasks = g_list_append (self->tasks, g_object_ref (task)); - - switch (self->type) { - case PW_TYPE_INTERFACE_Node: - pw_node_proxy_enum_params ((struct pw_node_proxy *) self->proxy, - seq, id, 0, -1, NULL); - pw_proxy_sync (self->proxy, seq); - break; - case PW_TYPE_INTERFACE_Port: - pw_port_proxy_enum_params ((struct pw_port_proxy *) self->proxy, - seq, id, 0, -1, NULL); - pw_proxy_sync (self->proxy, seq); - break; - case PW_TYPE_INTERFACE_Device: - pw_device_proxy_enum_params ((struct pw_device_proxy *) self->proxy, - seq, id, 0, -1, NULL); - pw_proxy_sync (self->proxy, seq); - break; - default: - g_task_return_new_error (task, WP_DOMAIN_LIBRARY, - WP_LIBRARY_ERROR_INVARIANT, - "Proxy interface does not have an enum_params method"); - break; - } -} - -/** - * wp_proxy_enum_params_finish: - * - * Returns: (transfer full) (element-type spa_pod*): the params - */ -GPtrArray * -wp_proxy_enum_params_finish (WpProxy * self, - GAsyncResult * res, GError ** err) -{ - g_return_val_if_fail (g_task_is_valid (res, self), NULL); - g_return_val_if_fail (g_async_result_is_tagged (res, wp_proxy_enum_params), - NULL); - - return g_task_propagate_pointer (G_TASK (res), err); -} diff --git a/lib/wp/proxy.h b/lib/wp/proxy.h deleted file mode 100644 index ee799f55..00000000 --- a/lib/wp/proxy.h +++ /dev/null @@ -1,43 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_PROXY_H__ -#define __WP_PROXY_H__ - -#include "object.h" -#include "core-interfaces.h" - -#include - -G_BEGIN_DECLS - -struct pw_proxy; - -#define WP_TYPE_PROXY (wp_proxy_get_type ()) -G_DECLARE_FINAL_TYPE (WpProxy, wp_proxy, WP, PROXY, WpObject) - -guint32 wp_proxy_get_id (WpProxy * self); -guint32 wp_proxy_get_parent_id (WpProxy * self); -guint32 wp_proxy_get_spa_type (WpProxy * self); -const gchar * wp_proxy_get_spa_type_string (WpProxy * self); - -WpObject * wp_proxy_get_core (WpProxy *self); - -gboolean wp_proxy_is_destroyed (WpProxy * self); -struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self); - -gconstpointer wp_proxy_get_info_native (WpProxy * self); - -void wp_proxy_enum_params (WpProxy * self, guint32 id, - GAsyncReadyCallback callback, gpointer data); -GPtrArray * wp_proxy_enum_params_finish (WpProxy * self, - GAsyncResult * res, GError ** err); - -G_END_DECLS - -#endif diff --git a/lib/wp/session-manager.c b/lib/wp/session-manager.c new file mode 100644 index 00000000..65249064 --- /dev/null +++ b/lib/wp/session-manager.c @@ -0,0 +1,120 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "session-manager.h" + +struct _WpSessionManager +{ + GObject parent; + GPtrArray *endpoints; +}; + +G_DEFINE_TYPE (WpSessionManager, wp_session_manager, G_TYPE_OBJECT) +G_DEFINE_QUARK (WP_GLOBAL_SESSION_MANAGER, wp_global_session_manager) + +static void +wp_session_manager_init (WpSessionManager * self) +{ + self->endpoints = g_ptr_array_new_with_free_func (g_object_unref); +} + +static void +wp_session_manager_finalize (GObject * obj) +{ + WpSessionManager * self = WP_SESSION_MANAGER (obj); + + g_ptr_array_unref (self->endpoints); + + G_OBJECT_CLASS (wp_session_manager_parent_class)->finalize (obj); +} + +static void +wp_session_manager_class_init (WpSessionManagerClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + object_class->finalize = wp_session_manager_finalize; +} + +WpSessionManager * +wp_session_manager_new (void) +{ + return g_object_new (WP_TYPE_SESSION_MANAGER, NULL); +} + +void +wp_session_manager_register_endpoint (WpSessionManager * self, + WpEndpoint * ep) +{ + g_ptr_array_add (self->endpoints, g_object_ref (ep)); +} + +void +wp_session_manager_remove_endpoint (WpSessionManager * self, + WpEndpoint * ep) +{ + g_ptr_array_remove_fast (self->endpoints, ep); +} + +struct endpoints_foreach_data +{ + GPtrArray *result; + const gchar *lookup; +}; + +static inline gboolean +media_class_matches (const gchar * media_class, const gchar * lookup) +{ + const gchar *c1 = media_class, *c2 = lookup; + + /* empty lookup matches all classes */ + if (!lookup) + return TRUE; + + /* compare until we reach the end of the lookup string */ + for (; *c2 != '\0'; c1++, c2++) { + if (*c1 != *c2) + return FALSE; + } + + /* the lookup may not end in a slash, however it must match up + * to the end of a submedia_class. i.e.: + * match: media_class: Audio/Source/Virtual + * lookup: Audio/Source + * + * NO match: media_class: Audio/Source/Virtual + * lookup: Audio/Sou + * + * if *c1 is not /, also check the previous char, because the lookup + * may actually end in a slash: + * + * match: media_class: Audio/Source/Virtual + * lookup: Audio/Source/ + */ + if (!(*c1 == '/' || *c1 == '\0' || *(c1 - 1) == '/')) + return FALSE; + + return TRUE; +} + +static void +find_endpoints (WpEndpoint * endpoint, struct endpoints_foreach_data * data) +{ + if (media_class_matches (wp_endpoint_get_media_class (endpoint), data->lookup)) + g_ptr_array_add (data->result, g_object_ref (endpoint)); +} + +GPtrArray * +wp_session_manager_find_endpoints (WpSessionManager * self, + const gchar * media_class_lookup) +{ + struct endpoints_foreach_data data; + data.result = g_ptr_array_new_with_free_func (g_object_unref); + data.lookup = media_class_lookup; + g_ptr_array_foreach (self->endpoints, (GFunc) find_endpoints, &data); + return data.result; +} diff --git a/lib/wp/session-manager.h b/lib/wp/session-manager.h new file mode 100644 index 00000000..7bc432e7 --- /dev/null +++ b/lib/wp/session-manager.h @@ -0,0 +1,34 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __WIREPLUMBER_SESSION_MANAGER_H__ +#define __WIREPLUMBER_SESSION_MANAGER_H__ + +#include "endpoint.h" + +G_BEGIN_DECLS + +#define WP_TYPE_SESSION_MANAGER (wp_session_manager_get_type ()) +G_DECLARE_FINAL_TYPE (WpSessionManager, wp_session_manager, WP, SESSION_MANAGER, GObject) + +#define WP_GLOBAL_SESSION_MANAGER (wp_global_session_manager_quark ()) +GQuark wp_global_session_manager_quark (void); + +WpSessionManager * wp_session_manager_new (void); + +void wp_session_manager_register_endpoint (WpSessionManager * self, + WpEndpoint * ep); +void wp_session_manager_remove_endpoint (WpSessionManager * self, + WpEndpoint * ep); + +GPtrArray * wp_session_manager_find_endpoints (WpSessionManager * self, + const gchar * media_class_lookup); + +G_END_DECLS + +#endif diff --git a/lib/wp/session.c b/lib/wp/session.c deleted file mode 100644 index 4c9d9f72..00000000 --- a/lib/wp/session.c +++ /dev/null @@ -1,101 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "session.h" -#include "wpenums.h" - -enum { - PROP_0, - PROP_DIRECTION, - PROP_MEDIA_CLASS, -}; - -typedef struct -{ - WpSessionDirection direction; - gchar media_class[41]; -} WpSessionPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (WpSession, wp_session, WP_TYPE_OBJECT) - -static void -wp_session_init (WpSession * self) -{ -} - -static void -wp_session_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (object)); - - switch (property_id) { - case PROP_DIRECTION: - priv->direction = g_value_get_enum (value); - break; - case PROP_MEDIA_CLASS: - strncpy (priv->media_class, g_value_get_string (value), 40); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_session_get_property (GObject * object, guint property_id, GValue * value, - GParamSpec * pspec) -{ - WpSessionPrivate *priv = wp_session_get_instance_private (WP_SESSION (object)); - - switch (property_id) { - case PROP_DIRECTION: - g_value_set_enum (value, priv->direction); - break; - case PROP_MEDIA_CLASS: - g_value_set_string (value, priv->media_class); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_session_class_init (WpSessionClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - - object_class->set_property = wp_session_set_property; - object_class->get_property = wp_session_get_property; - - g_object_class_install_property (object_class, PROP_DIRECTION, - g_param_spec_enum ("direction", "direction", - "The media flow direction of the session", - WP_TYPE_SESSION_DIRECTION, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_MEDIA_CLASS, - g_param_spec_string ("media-class", "media-class", - "The media class of the session", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -} - -WpSessionDirection -wp_session_get_direction (WpSession * self) -{ - WpSessionPrivate *priv = wp_session_get_instance_private (self); - return priv->direction; -} - -const gchar * -wp_session_get_media_class (WpSession * self) -{ - WpSessionPrivate *priv = wp_session_get_instance_private (self); - return priv->media_class; -} diff --git a/lib/wp/session.h b/lib/wp/session.h deleted file mode 100644 index 8c0fc642..00000000 --- a/lib/wp/session.h +++ /dev/null @@ -1,34 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_SESSION_H__ -#define __WP_SESSION_H__ - -#include "object.h" - -G_BEGIN_DECLS - -#define WP_TYPE_SESSION (wp_session_get_type ()) -G_DECLARE_DERIVABLE_TYPE (WpSession, wp_session, WP, SESSION, WpObject) - -typedef enum { - WP_SESSION_DIRECTION_INPUT, - WP_SESSION_DIRECTION_OUTPUT -} WpSessionDirection; - -struct _WpSessionClass -{ - WpObjectClass parent_class; -}; - -WpSessionDirection wp_session_get_direction (WpSession * session); -const gchar *wp_session_get_media_class (WpSession * session); - -G_END_DECLS - -#endif diff --git a/src/utils.c b/lib/wp/wp.h similarity index 57% rename from src/utils.c rename to lib/wp/wp.h index f6bc0bb9..8f0a6c1b 100644 --- a/src/utils.c +++ b/lib/wp/wp.h @@ -6,6 +6,9 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "utils.h" - -G_DEFINE_QUARK (wireplumber-core, wp_domain_core); +#include "core.h" +#include "endpoint.h" +#include "error.h" +#include "factory.h" +#include "module.h" +#include "session-manager.h" diff --git a/modules/meson.build b/modules/meson.build index 39db6467..7d9d4dfc 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -4,9 +4,36 @@ common_c_args = [ ] shared_library( - 'wireplumber-module-default-session', - ['module-default-session.c'], - c_args : [common_c_args, '-DG_LOG_DOMAIN="wireplumber-module-default-session"'], + 'wireplumber-module-pipewire', + [ + 'module-pipewire.c', + 'module-pipewire/loop-source.c', + 'module-pipewire/simple-endpoint-link.c', + 'module-pipewire/simple-endpoint.c', + ], + c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pipewire"'], + install : true, + #install_dir : modules_install_dir, + dependencies : [wp_dep, pipewire_dep], +) + +shared_library( + 'wireplumber-module-pw-alsa-udev', + [ + 'module-pw-alsa-udev.c', + ], + c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-alsa-udev"'], + install : true, + #install_dir : modules_install_dir, + dependencies : [wp_dep, pipewire_dep], +) + +shared_library( + 'wireplumber-module-pw-audio-softdsp-endpoint', + [ + 'module-pw-audio-softdsp-endpoint.c', + ], + c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-audio-softdsp-endpoint"'], install : true, #install_dir : modules_install_dir, dependencies : [wp_dep, pipewire_dep], diff --git a/modules/module-default-session.c b/modules/module-default-session.c deleted file mode 100644 index 0c24e8bb..00000000 --- a/modules/module-default-session.c +++ /dev/null @@ -1,338 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include -#include -#include -#include - -#define MIN_QUANTUM_SIZE 64 -#define MAX_QUANTUM_SIZE 1024 - -G_DECLARE_FINAL_TYPE (DefaultSession, session, DEFAULT, SESSION, WpSession) -G_DECLARE_FINAL_TYPE (DefaultSessionPlugin, plugin, DEFAULT, SESSION_PLUGIN, WpPlugin) - -/* DefaultSession */ - -struct _DefaultSession -{ - WpSession parent; - - WpProxy *device_node; - struct pw_node_proxy *dsp_proxy; - - struct spa_audio_info_raw format; - guint32 media_type; - guint32 session_id; -}; - -G_DEFINE_TYPE (DefaultSession, session, WP_TYPE_SESSION) - -static void -session_init (DefaultSession * self) -{ -} - -static void -session_finalize (GObject * obj) -{ - DefaultSession *self = DEFAULT_SESSION (obj); - - G_OBJECT_CLASS (session_parent_class)->finalize (obj); -} - -static void -session_class_init (DefaultSessionClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - object_class->finalize = session_finalize; -} - -static DefaultSession * -session_new (WpProxy * device_node, guint32 type, WpSessionDirection dir, - const gchar * media_class) -{ - DefaultSession *sess = g_object_new (session_get_type (), - "direction", dir, - "media-class", media_class, - NULL); - - sess->device_node = device_node; - sess->media_type = type; - - return sess; -} - -/* DefaultSessionPlugin */ - -struct _DefaultSessionPlugin -{ - WpPlugin parent; -}; - -G_DEFINE_TYPE (DefaultSessionPlugin, plugin, WP_TYPE_PLUGIN) - -static void -device_node_destroyed (WpProxy * device_node, DefaultSession * session) -{ - g_autoptr (WpObject) core = NULL; - WpSessionRegistry *sr = NULL; - - g_info ("Proxy %u destroyed - unregistering session %u", - wp_proxy_get_id (device_node), session->session_id); - - core = wp_proxy_get_core (device_node); - sr = wp_object_get_interface (core, WP_TYPE_SESSION_REGISTRY); - g_return_if_fail (sr != NULL); - - wp_session_registry_unregister_session (sr, session->session_id); -} - -static gboolean -handle_node (WpPlugin * self, WpProxy * proxy) -{ - g_autoptr (WpObject) core = NULL; - g_autoptr (DefaultSession) session = NULL; - g_autoptr (GError) error = NULL; - WpSessionRegistry *sr = NULL; - const gchar *media_class, *ptr; - WpSessionDirection direction; - guint32 media_type; - guint32 sess_id; - - ptr = media_class = wp_pipewire_properties_get ( - WP_PIPEWIRE_PROPERTIES (proxy), "media.class"); - - if (g_str_has_prefix (ptr, "Audio/")) { - ptr += strlen ("Audio/"); - media_type = SPA_MEDIA_TYPE_audio; - } else if (g_str_has_prefix (ptr, "Video/")) { - ptr += strlen ("Video/"); - media_type = SPA_MEDIA_TYPE_video; - } else { - goto out; - } - - if (strcmp (ptr, "Sink") == 0) - direction = WP_SESSION_DIRECTION_OUTPUT; - else if (strcmp (ptr, "Source") == 0) - direction = WP_SESSION_DIRECTION_INPUT; - else - goto out; - - g_info ("Creating session for node %u (%s), media.class = '%s'", - wp_proxy_get_id (proxy), wp_proxy_get_spa_type_string (proxy), - media_class); - - session = session_new (proxy, media_type, direction, media_class); - - core = wp_plugin_get_core (self); - sr = wp_object_get_interface (core, WP_TYPE_SESSION_REGISTRY); - g_return_val_if_fail (sr != NULL, FALSE); - - if ((sess_id = wp_session_registry_register_session (sr, - WP_SESSION (session), &error)) == -1) { - g_warning ("Error registering session: %s", error->message); - return FALSE; - } - - session->session_id = sess_id; - g_object_set_data_full (G_OBJECT (proxy), "module-default-session.session", - g_object_ref (session), g_object_unref); - g_signal_connect_object (proxy, "destroyed", - (GCallback) device_node_destroyed, session, 0); - - return TRUE; - -out: - g_message ("Unrecognized media.class '%s' - not handling proxy %u (%s)", - media_class, wp_proxy_get_id (proxy), - wp_proxy_get_spa_type_string (proxy)); - return FALSE; -} - -static gboolean -plug_dsp (WpProxy * node) -{ - DefaultSession *session; - g_autoptr (WpObject) core = NULL; - WpPipewireObjects *pw_objects = NULL; - struct pw_core_proxy *core_proxy; - WpPipewireProperties *pw_props = NULL; - struct pw_properties *props; - const char *name; - enum pw_direction reverse_direction; - uint8_t buf[1024]; - struct spa_pod_builder b = { 0, }; - struct spa_pod *param; - - session = g_object_get_data (G_OBJECT (node), "module-default-session.session"); - - g_return_val_if_fail (session->media_type == SPA_MEDIA_TYPE_audio, - G_SOURCE_REMOVE); - - g_info ("making audio dsp for session %u", session->session_id); - - core = wp_proxy_get_core (node); - pw_objects = WP_PIPEWIRE_OBJECTS (core); - core_proxy = pw_remote_get_core_proxy (wp_pipewire_objects_get_pw_remote (pw_objects)); - - pw_props = WP_PIPEWIRE_PROPERTIES (node); - props = pw_properties_new_dict ( - wp_pipewire_properties_get_as_spa_dict (pw_props)); - if ((name = pw_properties_get (props, "device.nick")) == NULL) - name = "unnamed"; - pw_properties_set (props, "audio-dsp.name", name); - pw_properties_setf (props, "audio-dsp.direction", "%d", - wp_session_get_direction (WP_SESSION (session))); - pw_properties_setf (props, "audio-dsp.maxbuffer", "%ld", - MAX_QUANTUM_SIZE * sizeof (float)); - - session->dsp_proxy = pw_core_proxy_create_object (core_proxy, - "audio-dsp", - PW_TYPE_INTERFACE_Node, - PW_VERSION_NODE, - &props->dict, - 0); - pw_properties_free (props); - - reverse_direction = - (wp_session_get_direction (WP_SESSION (session)) == WP_SESSION_DIRECTION_INPUT) ? - PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; - - spa_pod_builder_init (&b, buf, sizeof (buf)); - param = spa_format_audio_raw_build (&b, SPA_PARAM_Format, &session->format); - param = spa_pod_builder_add_object (&b, - SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_direction, SPA_POD_Id (reverse_direction), - SPA_PARAM_PROFILE_format, SPA_POD_Pod (param)); - - pw_node_proxy_set_param ((struct pw_node_proxy *) session->dsp_proxy, - SPA_PARAM_Profile, 0, param); - - return G_SOURCE_REMOVE; -} - -static void -audio_port_enum_params_done (GObject * port, GAsyncResult * res, gpointer data) -{ - g_autoptr (GError) error = NULL; - g_autoptr (GPtrArray) params = NULL; - WpProxy *node; - DefaultSession *session; - struct spa_audio_info_raw info = { 0, }; - guint32 media_type, media_subtype; - guint i; - - g_debug ("done enumerating port %u params", - wp_proxy_get_id (WP_PROXY (port))); - - params = wp_proxy_enum_params_finish (WP_PROXY (port), res, &error); - if (!params) { - g_warning ("%s", error->message); - return; - } - - node = WP_PROXY (data); - session = g_object_get_data (G_OBJECT (node), "module-default-session.session"); - - for (i = 0; i < params->len; i++) { - struct spa_pod *param = g_ptr_array_index (params, i); - - if (spa_format_parse(param, &media_type, &media_subtype) < 0) - return; - - if (media_type != SPA_MEDIA_TYPE_audio || - media_subtype != SPA_MEDIA_SUBTYPE_raw) - return; - - spa_pod_fixate (param); - - if (spa_format_audio_raw_parse (param, &info) < 0) - return; - - if (info.channels > session->format.channels) - session->format = info; - } - - g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) plug_dsp, - g_object_ref (node), g_object_unref); -} - -static gboolean -handle_audio_port (WpPlugin * self, WpProxy * port, WpProxy * node) -{ - wp_proxy_enum_params (port, SPA_PARAM_EnumFormat, - audio_port_enum_params_done, node); - return TRUE; -} - -static gboolean -handle_pw_proxy (WpPlugin * self, WpProxy * proxy) -{ - g_autoptr (WpObject) core = NULL; - g_autoptr (WpProxy) parent = NULL; - WpProxyRegistry *reg = NULL; - DefaultSession *session; - - if (wp_proxy_get_spa_type (proxy) != PW_TYPE_INTERFACE_Port && - wp_proxy_get_spa_type (proxy) != PW_TYPE_INTERFACE_Node) - return FALSE; - - core = wp_plugin_get_core (self); - reg = wp_object_get_interface (core, WP_TYPE_PROXY_REGISTRY); - parent = wp_proxy_registry_get_proxy (reg, wp_proxy_get_parent_id (proxy)); - - if (wp_proxy_get_spa_type (parent) == PW_TYPE_INTERFACE_Device && - wp_proxy_get_spa_type (proxy) == PW_TYPE_INTERFACE_Node) - { - g_debug ("handling node %u (parent device %u)", wp_proxy_get_id (proxy), - wp_proxy_get_id (parent)); - return handle_node (self, proxy); - } - else if (wp_proxy_get_spa_type (parent) == PW_TYPE_INTERFACE_Node && - wp_proxy_get_spa_type (proxy) == PW_TYPE_INTERFACE_Port && - (session = g_object_get_data (G_OBJECT (parent), "module-default-session.session")) && - session->media_type == SPA_MEDIA_TYPE_audio) - { - g_debug ("handling audio port %u (parent node %u)", wp_proxy_get_id (proxy), - wp_proxy_get_id (parent)); - return handle_audio_port (self, proxy, parent); - } - - return FALSE; -} - -static void -plugin_init (DefaultSessionPlugin * self) -{ -} - -static void -plugin_class_init (DefaultSessionPluginClass * klass) -{ - WpPluginClass *plugin_class = WP_PLUGIN_CLASS (klass); - plugin_class->handle_pw_proxy = handle_pw_proxy; -} - -static const WpPluginMetadata plugin_metadata = { - .rank = WP_PLUGIN_RANK_UPSTREAM, - .name = "default-session", - .description = "Provides the default WpSession implementation", - .author = "George Kiagiadakis ", - .license = "LGPL-2.1-or-later", - .version = "0.1", - .origin = "https://gitlab.freedesktop.org/gkiagia/wireplumber" -}; - -void -WP_MODULE_INIT_SYMBOL (WpPluginRegistry * registry) -{ - wp_plugin_registry_register_static (registry, plugin_get_type (), - &plugin_metadata, sizeof (plugin_metadata)); -} diff --git a/modules/module-pipewire.c b/modules/module-pipewire.c new file mode 100644 index 00000000..5040723e --- /dev/null +++ b/modules/module-pipewire.c @@ -0,0 +1,68 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/** + * module-pipewire provides basic integration between wireplumber and pipewire. + * It provides the pipewire core and remote, connects to pipewire and provides + * the most primitive implementations of WpEndpoint and WpEndpointLink + */ + +#include +#include + +#include "module-pipewire/loop-source.h" + +gpointer simple_endpoint_factory (WpFactory * factory, GType type, + GVariant * properties); +gpointer simple_endpoint_link_factory (WpFactory * factory, GType type, + GVariant * properties); + +static gboolean +connect_in_idle (struct pw_remote *remote) +{ + pw_remote_connect (remote); + return G_SOURCE_REMOVE; +} + +static void +module_destroy (gpointer r) +{ + struct pw_remote *remote = r; + struct pw_core *core = pw_remote_get_core (remote); + + pw_remote_destroy (remote); + pw_core_destroy (core); +} + +void +wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) +{ + GSource *source; + struct pw_core *pw_core; + struct pw_remote *pw_remote; + + pw_init (NULL, NULL); + + source = wp_loop_source_new (); + g_source_attach (source, NULL); + + pw_core = pw_core_new (WP_LOOP_SOURCE (source)->loop, NULL, 0); + wp_core_register_global (core, WP_GLOBAL_PW_CORE, pw_core, NULL); + + pw_remote = pw_remote_new (pw_core, NULL, 0); + wp_core_register_global (core, WP_GLOBAL_PW_REMOTE, pw_remote, NULL); + + wp_module_set_destroy_callback (module, module_destroy, pw_remote); + + wp_core_register_factory (core, wp_factory_new ( + "pipewire-simple-endpoint", simple_endpoint_factory)); + wp_core_register_factory (core, wp_factory_new ( + "pipewire-simple-endpoint-link", simple_endpoint_link_factory)); + + g_idle_add ((GSourceFunc) connect_in_idle, pw_remote); +} diff --git a/src/loop-source.c b/modules/module-pipewire/loop-source.c similarity index 81% rename from src/loop-source.c rename to modules/module-pipewire/loop-source.c index b8f43a0b..5ab7b2fb 100644 --- a/src/loop-source.c +++ b/modules/module-pipewire/loop-source.c @@ -6,18 +6,12 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ +/** + * Integration between the PipeWire main loop and GMainLoop + */ + #include "loop-source.h" -#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x) - -typedef struct _WpLoopSource WpLoopSource; - -struct _WpLoopSource -{ - GSource parent; - struct pw_loop *loop; -}; - static gboolean wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data) { @@ -58,9 +52,3 @@ wp_loop_source_new (void) return (GSource *) s; } - -struct pw_loop * -wp_loop_source_get_loop (GSource *s) -{ - return WP_LOOP_SOURCE(s)->loop; -} diff --git a/modules/module-pipewire/loop-source.h b/modules/module-pipewire/loop-source.h new file mode 100644 index 00000000..a4fca6e4 --- /dev/null +++ b/modules/module-pipewire/loop-source.h @@ -0,0 +1,21 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include +#include + +#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x) + +typedef struct _WpLoopSource WpLoopSource; +struct _WpLoopSource +{ + GSource parent; + struct pw_loop *loop; +}; + +GSource * wp_loop_source_new (void); diff --git a/modules/module-pipewire/simple-endpoint-link.c b/modules/module-pipewire/simple-endpoint-link.c new file mode 100644 index 00000000..93dc160a --- /dev/null +++ b/modules/module-pipewire/simple-endpoint-link.c @@ -0,0 +1,71 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/** + * The simple endpoint link is an implementation of WpEndpointLink that + * expects the two linked endpoints to have nodes in the pipewire graph. + * When asked to create a link, it creates pw_link objects that will link + * the ports of the source node to the ports of the sink node. + * + * The GVariant data that is passed in create must be of type (uau), + * which means a tuple with the following fields: + * - u: a uint32 that is the ID of a node + * - au: an array of uint32 that are the IDs of the ports on this node + * + * Linking endpoints with multiple nodes is not supported by this implementation. + */ + +#include +#include + +struct _WpPipewireSimpleEndpointLink +{ + WpEndpointLink parent; +}; + +G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpointLink, + simple_endpoint_link, WP_PIPEWIRE, SIMPLE_ENDPOINT_LINK, WpEndpointLink) + +G_DEFINE_TYPE (WpPipewireSimpleEndpointLink, + simple_endpoint_link, WP_TYPE_ENDPOINT_LINK) + +static void +simple_endpoint_link_init (WpPipewireSimpleEndpointLink * self) +{ +} + +static gboolean +simple_endpoint_link_create (WpEndpointLink * self, GVariant * src_data, + GVariant * sink_data, GError ** error) +{ + /* TODO create pw_links based on the nodes & ports described in src/sink_data */ +} + +static void +simple_endpoint_link_destroy (WpEndpointLink * self) +{ + /* TODO destroy pw_links */ +} + +static void +simple_endpoint_link_class_init (WpPipewireSimpleEndpointLinkClass * klass) +{ + WpEndpointLinkClass *link_class = (WpEndpointLinkClass *) klass; + + link_class->create = simple_endpoint_link_create; + link_class->destroy = simple_endpoint_link_destroy; +} + +gpointer +simple_endpoint_link_factory (WpFactory * factory, GType type, + GVariant * properties) +{ + if (type != WP_TYPE_ENDPOINT_LINK) + return NULL; + return g_object_new (simple_endpoint_link_get_type (), NULL); +} diff --git a/modules/module-pipewire/simple-endpoint.c b/modules/module-pipewire/simple-endpoint.c new file mode 100644 index 00000000..5c0dcac1 --- /dev/null +++ b/modules/module-pipewire/simple-endpoint.c @@ -0,0 +1,63 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/** + * The simple endpoint is a WpEndpoint implementation that represents + * all ports of a single direction of a single pipewire node. + * It can be used to create an Endpoint for a client node or for any + * other arbitrary node that does not need any kind of internal management. + */ + +#include +#include + +struct _WpPipewireSimpleEndpoint +{ + WpEndpoint parent; +}; + +G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpoint, + simple_endpoint, WP_PIPEWIRE, SIMPLE_ENDPOINT, WpEndpoint) + +G_DEFINE_TYPE (WpPipewireSimpleEndpoint, simple_endpoint, WP_TYPE_ENDPOINT) + +static void +simple_endpoint_init (WpPipewireSimpleEndpoint * self) +{ +} + +static gboolean +simple_endpoint_prepare_link (WpEndpoint * self, guint32 stream_id, + WpEndpointLink * link, GVariant ** properties, GError ** error) +{ + /* TODO: verify that the remote end supports the same media type */ + /* TODO: fill @properties with (node id, array(port ids)) */ +} + +static void +simple_endpoint_class_init (WpPipewireSimpleEndpointClass * klass) +{ + WpEndpointClass *endpoint_class = (WpEndpointClass *) klass; + + endpoint_class->prepare_link = simple_endpoint_prepare_link; +} + +gpointer +simple_endpoint_factory (WpFactory * factory, GType type, + GVariant * properties) +{ + if (type != WP_TYPE_ENDPOINT) + return NULL; + + /* TODO: retrieve pw_node* from @properties and keep it + * TODO: populate media_class and name on the endpoint + * TODO: potentially choose between subclasses of SimpleEndpoint + * in order to add interfaces (volume, color balance, etc) + */ + return g_object_new (simple_endpoint_get_type (), NULL); +} diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c new file mode 100644 index 00000000..156882d5 --- /dev/null +++ b/modules/module-pw-alsa-udev.c @@ -0,0 +1,20 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/** + * module-pw-alsa-udev provides alsa device detection through pipewire + * and automatically creates endpoints for all alsa device nodes that appear + */ + +#include +#include + +void +wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) +{ +} diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c new file mode 100644 index 00000000..a590dadc --- /dev/null +++ b/modules/module-pw-audio-softdsp-endpoint.c @@ -0,0 +1,65 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author George Kiagiadakis + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/** + * module-pw-audio-softdsp-endpoint provides a WpEndpoint implementation + * that wraps an audio device node in pipewire and plugs a DSP node, as well + * as optional merger+volume nodes that are used as entry points for the + * various streams that this endpoint may have + */ + +#include +#include + +struct _WpPwAudioSoftdspEndpoint +{ + WpEndpoint parent; +}; + +G_DECLARE_FINAL_TYPE (WpPwAudioSoftdspEndpoint, endpoint, + WP_PW, AUDIO_SOFTDSP_ENDPOINT, WpEndpoint) + +G_DEFINE_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT) + +static void +endpoint_init (WpPwAudioSoftdspEndpoint * self) +{ +} + +static gboolean +endpoint_prepare_link (WpEndpoint * self, guint32 stream_id, + WpEndpointLink * link, GVariant ** properties, GError ** error) +{ +} + +static void +endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass) +{ + WpEndpointClass *endpoint_class = (WpEndpointClass *) klass; + + endpoint_class->prepare_link = endpoint_prepare_link; +} + +static gpointer +endpoint_factory (WpFactory * factory, GType type, GVariant * properties) +{ + if (type != WP_TYPE_ENDPOINT) + return NULL; + + /* TODO: retrieve pw_node* from @properties and keep it + * TODO: populate media_class and name on the endpoint + */ + return g_object_new (endpoint_get_type (), NULL); +} + +void +wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) +{ + wp_core_register_factory (core, wp_factory_new ( + "pw-audio-softdsp-endpoint", endpoint_factory)); +} diff --git a/src/core.c b/src/core.c deleted file mode 100644 index f52c0419..00000000 --- a/src/core.c +++ /dev/null @@ -1,358 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "core.h" -#include "loop-source.h" -#include "module-loader.h" -#include "plugin-registry-impl.h" -#include "proxy-registry-impl.h" -#include "session-registry-impl.h" -#include "utils.h" - -#include - -#include -#include -#include - -#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf" - -struct _WpCore -{ - GObject parent; - - GMainLoop *loop; - GSource *source; - - struct pw_core *core; - struct pw_remote *remote; - struct spa_hook remote_listener; - - WpModuleLoader *module_loader; - - GError *exit_error; -}; - -static void wp_core_pw_objects_init (WpPipewireObjectsInterface * iface); - -G_DEFINE_TYPE_WITH_CODE (WpCore, wp_core, WP_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (WP_TYPE_PIPEWIRE_OBJECTS, wp_core_pw_objects_init); -) - -static gboolean -signal_handler (gpointer data) -{ - WpCore *self = WP_CORE (data); - wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_INTERRUPTED, - "interrupted by signal"); - return G_SOURCE_CONTINUE; -} - -static void -remote_state_changed (void * data, enum pw_remote_state old_state, - enum pw_remote_state new_state, const char * error) -{ - WpCore *self = WP_CORE (data); - - g_debug ("remote state changed, old:%s new:%s", - pw_remote_state_as_string (old_state), - pw_remote_state_as_string (new_state)); - - switch (new_state) { - case PW_REMOTE_STATE_UNCONNECTED: - wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_DISCONNECTED, "disconnected"); - break; - - case PW_REMOTE_STATE_ERROR: - wp_core_exit (self, WP_DOMAIN_CORE, WP_CODE_REMOTE_ERROR, - "pipewire remote error: %s", error); - break; - - default: - break; - } -} - -static const struct pw_remote_events remote_events = { - PW_VERSION_REMOTE_EVENTS, - .state_changed = remote_state_changed, -}; - -static gboolean -wp_core_parse_commands_file (WpCore * self, GInputStream * stream, - GError ** error) -{ - WpPluginRegistry *plugin_registry = NULL; - gchar buffer[4096]; - gssize bytes_read; - gchar *cur, *linestart, *saveptr; - gchar *cmd, *abi, *module; - gint lineno = 1; - gboolean eof = FALSE; - - plugin_registry = wp_object_get_interface (WP_OBJECT (self), - WP_TYPE_PLUGIN_REGISTRY); - - linestart = cur = buffer; - - do { - bytes_read = g_input_stream_read (stream, cur, sizeof (buffer), NULL, error); - if (bytes_read < 0) - return FALSE; - else if (bytes_read == 0) { - eof = TRUE; - /* terminate the remaining data, so that we consume it all */ - if (cur != linestart) { - *cur = '\n'; - } - } - - bytes_read += (cur - linestart); - - while (cur - buffer < bytes_read) { - while (cur - buffer < bytes_read && *cur != '\n') - cur++; - - if (*cur == '\n') { - /* found the end of a line */ - *cur = '\0'; - - /* tokenize and execute */ - cmd = strtok_r (linestart, " ", &saveptr); - - if (!g_strcmp0 (cmd, "load-module")) { - abi = strtok_r (NULL, " ", &saveptr); - module = strtok_r (NULL, " ", &saveptr); - - if (!abi || !module) { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT, - "expected ABI and MODULE at line %i", lineno); - return FALSE; - } else if (!wp_module_loader_load (self->module_loader, - plugin_registry, abi, module, error)) { - return FALSE; - } - } else { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT, - "unknown command '%s' at line %i", cmd, lineno); - return FALSE; - } - - /* continue with the next line */ - linestart = ++cur; - lineno++; - } - } - - /* reached the end of the data that was read */ - - if (cur - linestart >= sizeof (buffer)) { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED, - "line %i exceeds the maximum allowed line size (%d bytes)", - lineno, (gint) sizeof (buffer)); - return FALSE; - } else if (cur - linestart > 0) { - /* we have unparsed data, move it to the - * beginning of the buffer and continue */ - strncpy (buffer, linestart, cur - linestart); - linestart = buffer; - cur = buffer + (cur - linestart); - } - } while (!eof); - - return TRUE; -} - -static gboolean -wp_core_load_commands_file (WpCore * self) -{ - g_autoptr (GFile) file = NULL; - g_autoptr (GError) error = NULL; - g_autoptr (GFileInputStream) istream = NULL; - const gchar *filename; - - filename = g_getenv ("WIREPLUMBER_CONFIG_FILE"); - if (!filename) - filename = WIREPLUMBER_DEFAULT_CONFIG_FILE; - - file = g_file_new_for_path (filename); - istream = g_file_read (file, NULL, &error); - if (!istream) { - g_propagate_error (&self->exit_error, error); - error = NULL; - g_main_loop_quit (self->loop); - return FALSE; - } - - if (!wp_core_parse_commands_file (self, G_INPUT_STREAM (istream), &error)) { - g_propagate_prefixed_error (&self->exit_error, error, "Failed to read %s: ", - filename); - error = NULL; - g_main_loop_quit (self->loop); - return FALSE; - } - - return TRUE; -} - -static void -wp_core_handle_proxy (WpProxyRegistry *pr, WpProxy * proxy, WpCore * self) -{ - WpPluginRegistry *plugin_registry = wp_object_get_interface (WP_OBJECT (self), - WP_TYPE_PLUGIN_REGISTRY); - g_return_if_fail (plugin_registry != NULL); - wp_plugin_registry_impl_invoke (plugin_registry, wp_plugin_handle_pw_proxy, - proxy); -} - -static void -wp_core_init (WpCore * self) -{ - WpPluginRegistryImpl *plugin_registry; - WpProxyRegistryImpl *proxy_registry; - WpSessionRegistryImpl *session_registry; - - self->loop = g_main_loop_new (NULL, FALSE); - self->source = wp_loop_source_new (); - g_source_attach (self->source, NULL); - - self->core = pw_core_new (wp_loop_source_get_loop (self->source), NULL, 0); - self->remote = pw_remote_new (self->core, NULL, 0); - - pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events, - self); - - self->module_loader = wp_module_loader_new (); - - proxy_registry = wp_proxy_registry_impl_new (self->remote); - wp_object_attach_interface_impl (WP_OBJECT (self), proxy_registry, NULL); - - plugin_registry = wp_plugin_registry_impl_new (); - wp_object_attach_interface_impl (WP_OBJECT (self), plugin_registry, NULL); - - session_registry = wp_session_registry_impl_new (); - wp_object_attach_interface_impl (WP_OBJECT (self), session_registry, NULL); - - g_signal_connect (proxy_registry, "new-proxy-available", - (GCallback) wp_core_handle_proxy, self); -} - -static void -wp_core_dispose (GObject * obj) -{ - WpCore *self = WP_CORE (obj); - WpPluginRegistry *plugin_registry = NULL; - WpProxyRegistry *proxy_registry = NULL; - - /* ensure all proxies and plugins are unrefed, - * so that the registries can be disposed */ - - plugin_registry = wp_object_get_interface (WP_OBJECT (self), - WP_TYPE_PLUGIN_REGISTRY); - wp_plugin_registry_impl_unload (WP_PLUGIN_REGISTRY_IMPL (plugin_registry)); - - proxy_registry = wp_object_get_interface (WP_OBJECT (self), - WP_TYPE_PROXY_REGISTRY); - wp_proxy_registry_impl_unload (WP_PROXY_REGISTRY_IMPL (proxy_registry)); -} - -static void -wp_core_finalize (GObject * obj) -{ - WpCore *self = WP_CORE (obj); - - g_clear_object (&self->module_loader); - - spa_hook_remove (&self->remote_listener); - - pw_remote_destroy (self->remote); - pw_core_destroy (self->core); - - g_source_destroy (self->source); - g_source_unref (self->source); - g_main_loop_unref (self->loop); - - g_warn_if_fail (self->exit_error == NULL); - g_clear_error (&self->exit_error); -} - -static void -wp_core_class_init (WpCoreClass * klass) -{ - GObjectClass * object_class = (GObjectClass *) klass; - - object_class->dispose = wp_core_dispose; - object_class->finalize = wp_core_finalize; -} - -static struct pw_core * -wp_core_get_pw_core (WpPipewireObjects *pwobj) -{ - return WP_CORE (pwobj)->core; -} - -static struct pw_remote * -wp_core_get_pw_remote (WpPipewireObjects *pwobj) -{ - return WP_CORE (pwobj)->remote; -} - -static void -wp_core_pw_objects_init (WpPipewireObjectsInterface * iface) -{ - iface->get_pw_core = wp_core_get_pw_core; - iface->get_pw_remote = wp_core_get_pw_remote; -} - -WpCore * -wp_core_get_instance (void) -{ - static WpCore *instance = NULL; - if (G_UNLIKELY (!instance)) { - instance = g_object_new (wp_core_get_type (), NULL); - } - return instance; -} - -static gboolean -wp_core_run_in_idle (WpCore * self) -{ - if (!wp_core_load_commands_file (self)) goto out; - if (pw_remote_connect (self->remote) < 0) goto out; - -out: - return G_SOURCE_REMOVE; -} - -void -wp_core_run (WpCore * self, GError ** error) -{ - g_unix_signal_add (SIGINT, signal_handler, self); - g_unix_signal_add (SIGTERM, signal_handler, self); - g_unix_signal_add (SIGHUP, signal_handler, self); - - g_idle_add ((GSourceFunc) wp_core_run_in_idle, self); - - g_main_loop_run (self->loop); - - if (self->exit_error) { - g_propagate_error (error, self->exit_error); - self->exit_error = NULL; - } -} - -void -wp_core_exit (WpCore * self, GQuark domain, gint code, - const gchar *format, ...) -{ - va_list args; - va_start (args, format); - self->exit_error = g_error_new_valist (domain, code, format, args); - va_end (args); - g_main_loop_quit (self->loop); -} diff --git a/src/core.h b/src/core.h deleted file mode 100644 index f4cb3e45..00000000 --- a/src/core.h +++ /dev/null @@ -1,26 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WIREPLUMBER_CORE_H__ -#define __WIREPLUMBER_CORE_H__ - -#include - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE (WpCore, wp_core, WP, CORE, WpObject); - -WpCore * wp_core_get_instance (void); -void wp_core_run (WpCore * self, GError ** error); - -void wp_core_exit (WpCore * self, GQuark domain, gint code, - const gchar *format, ...); - -G_END_DECLS - -#endif diff --git a/src/loop-source.h b/src/loop-source.h deleted file mode 100644 index c52acee7..00000000 --- a/src/loop-source.h +++ /dev/null @@ -1,28 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WIREPLUMBER_LOOP_SOURCE_H__ -#define __WIREPLUMBER_LOOP_SOURCE_H__ - -#include -#include - -G_BEGIN_DECLS - -/* - * A GSource that integrates a pw_loop with GMainLoop. - * Use g_source_ref/unref to manage lifetime. - * The pw_loop is owned by the GSource. - */ - -GSource * wp_loop_source_new (void); -struct pw_loop * wp_loop_source_get_loop (GSource * s); - -G_END_DECLS - -#endif diff --git a/src/main.c b/src/main.c index e021c48c..bce2d7f4 100644 --- a/src/main.c +++ b/src/main.c @@ -6,40 +6,221 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "core.h" -#include "utils.h" +#include +#include +#include -#include +#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf" static GOptionEntry entries[] = { { NULL } }; +#define WP_DOMAIN_DAEMON (wp_domain_daemon_quark ()) +static G_DEFINE_QUARK (wireplumber-daemon, wp_domain_daemon); + +enum WpExitCode +{ + WP_CODE_DISCONNECTED = 0, + WP_CODE_INTERRUPTED, + WP_CODE_OPERATION_FAILED, + WP_CODE_INVALID_ARGUMENT, +}; + +struct WpDaemonData +{ + WpCore *core; + GMainLoop *loop; + + gint exit_code; + gchar *exit_message; + GDestroyNotify free_message; +}; + +static void +daemon_exit (struct WpDaemonData * d, gint code, const gchar *format, ...) +{ + va_list args; + va_start (args, format); + d->exit_code = code; + d->exit_message = g_strdup_vprintf (format, args); + d->free_message = g_free; + va_end (args); + g_main_loop_quit (d->loop); +} + +static void +daemon_exit_static_str (struct WpDaemonData * d, gint code, const gchar *str) +{ + d->exit_code = code; + d->exit_message = (gchar *) str; + d->free_message = NULL; + g_main_loop_quit (d->loop); +} + +static gboolean +signal_handler (gpointer data) +{ + struct WpDaemonData *d = data; + daemon_exit_static_str (d, WP_CODE_INTERRUPTED, "interrupted by signal"); + return G_SOURCE_CONTINUE; +} + +static gboolean +parse_commands_file (struct WpDaemonData *d, GInputStream * stream, + GError ** error) +{ + gchar buffer[4096]; + gssize bytes_read; + gchar *cur, *linestart, *saveptr; + gchar *cmd, *abi, *module; + gint lineno = 1; + gboolean eof = FALSE; + + linestart = cur = buffer; + + do { + bytes_read = g_input_stream_read (stream, cur, sizeof (buffer), NULL, error); + if (bytes_read < 0) + return FALSE; + else if (bytes_read == 0) { + eof = TRUE; + /* terminate the remaining data, so that we consume it all */ + if (cur != linestart) { + *cur = '\n'; + } + } + + bytes_read += (cur - linestart); + + while (cur - buffer < bytes_read) { + while (cur - buffer < bytes_read && *cur != '\n') + cur++; + + if (*cur == '\n') { + /* found the end of a line */ + *cur = '\0'; + + /* tokenize and execute */ + cmd = strtok_r (linestart, " ", &saveptr); + + if (!g_strcmp0 (cmd, "load-module")) { + abi = strtok_r (NULL, " ", &saveptr); + module = strtok_r (NULL, " ", &saveptr); + + if (!abi || !module) { + g_set_error (error, WP_DOMAIN_DAEMON, WP_CODE_INVALID_ARGUMENT, + "expected ABI and MODULE at line %i", lineno); + return FALSE; + } + + if (!wp_module_load (d->core, abi, module, NULL, error)) { + return FALSE; + } + } else { + g_set_error (error, WP_DOMAIN_DAEMON, WP_CODE_INVALID_ARGUMENT, + "unknown command '%s' at line %i", cmd, lineno); + return FALSE; + } + + /* continue with the next line */ + linestart = ++cur; + lineno++; + } + } + + /* reached the end of the data that was read */ + + if (cur - linestart >= sizeof (buffer)) { + g_set_error (error, WP_DOMAIN_DAEMON, WP_CODE_OPERATION_FAILED, + "line %i exceeds the maximum allowed line size (%d bytes)", + lineno, (gint) sizeof (buffer)); + return FALSE; + } else if (cur - linestart > 0) { + /* we have unparsed data, move it to the + * beginning of the buffer and continue */ + strncpy (buffer, linestart, cur - linestart); + linestart = buffer; + cur = buffer + (cur - linestart); + } + } while (!eof); + + return TRUE; +} + +static gboolean +load_commands_file (struct WpDaemonData *d) +{ + g_autoptr (GFile) file = NULL; + g_autoptr (GError) error = NULL; + g_autoptr (GFileInputStream) istream = NULL; + const gchar *filename; + + filename = g_getenv ("WIREPLUMBER_CONFIG_FILE"); + if (!filename) + filename = WIREPLUMBER_DEFAULT_CONFIG_FILE; + + file = g_file_new_for_path (filename); + istream = g_file_read (file, NULL, &error); + if (!istream) { + daemon_exit (d, WP_CODE_INVALID_ARGUMENT, "%s", error->message); + return G_SOURCE_REMOVE; + } + + if (!parse_commands_file (d, G_INPUT_STREAM (istream), &error)) { + daemon_exit (d, error->code, "Failed to read '%s': %s", filename, + error->message); + return G_SOURCE_REMOVE; + } + + return G_SOURCE_REMOVE; +} + gint main (gint argc, gchar **argv) { + struct WpDaemonData data = {0}; g_autoptr (GOptionContext) context = NULL; g_autoptr (GError) error = NULL; g_autoptr (WpCore) core = NULL; - gint ret = 0; + g_autoptr (GMainLoop) loop = NULL; context = g_option_context_new ("- PipeWire Session/Policy Manager"); g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) + if (!g_option_context_parse (context, &argc, &argv, &error)) { + data.exit_message = error->message; + data.exit_code = WP_CODE_INVALID_ARGUMENT; goto out; + } - pw_init (NULL, NULL); + /* init wireplumber */ - core = wp_core_get_instance (); - wp_core_run (core, &error); + data.core = core = wp_core_new (); + + wp_core_register_global (core, WP_GLOBAL_SESSION_MANAGER, + wp_session_manager_new (), g_object_unref); + + /* init main loop */ + + data.loop = loop = g_main_loop_new (NULL, FALSE); + + /* watch for exit signals */ + + g_unix_signal_add (SIGINT, signal_handler, &data); + g_unix_signal_add (SIGTERM, signal_handler, &data); + g_unix_signal_add (SIGHUP, signal_handler, &data); + + /* run */ + + g_idle_add ((GSourceFunc) load_commands_file, &data); + g_main_loop_run (data.loop); out: - if (error) { - ret = error->code; - if (error->domain != WP_DOMAIN_CORE) - ret += 100; - g_message ("exit code %d; %s", ret, error->message); + if (data.exit_message) { + g_message ("%s", data.exit_message); + if (data.free_message) + data.free_message (data.exit_message); } - return ret; + return data.exit_code; } diff --git a/src/meson.build b/src/meson.build index 6649784d..866daa7b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,12 +1,5 @@ wp_sources = [ - 'core.c', - 'loop-source.c', 'main.c', - 'module-loader.c', - 'plugin-registry-impl.c', - 'proxy-registry-impl.c', - 'session-registry-impl.c', - 'utils.c', ] executable('wireplumber', @@ -17,5 +10,5 @@ executable('wireplumber', '-DG_LOG_DOMAIN="wireplumber"' ], install: true, - dependencies : [gobject_dep, gmodule_dep, gio_dep, pipewire_dep, wp_dep], + dependencies : [gobject_dep, gio_dep, wp_dep], ) diff --git a/src/module-loader.c b/src/module-loader.c deleted file mode 100644 index 64434b16..00000000 --- a/src/module-loader.c +++ /dev/null @@ -1,81 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "module-loader.h" -#include "utils.h" - -#include -#include - -struct _WpModuleLoader -{ - GObject parent; - const gchar *module_dir; -}; - -G_DEFINE_TYPE (WpModuleLoader, wp_module_loader, G_TYPE_OBJECT); - -static void -wp_module_loader_init (WpModuleLoader * self) -{ - self->module_dir = g_getenv ("WIREPLUMBER_MODULE_DIR"); -} - -static void -wp_module_loader_class_init (WpModuleLoaderClass * klass) -{ -} - -WpModuleLoader * -wp_module_loader_new (void) -{ - return g_object_new (wp_module_loader_get_type (), NULL); -} - -static gboolean -wp_module_loader_load_c (WpModuleLoader * self, WpPluginRegistry * registry, - const gchar * module_name, GError ** error) -{ - g_autofree gchar *module_path = NULL; - GModule *module; - gpointer module_init; - typedef void (*WpModuleInitFunc)(WpPluginRegistry *); - - module_path = g_module_build_path (self->module_dir, module_name); - module = g_module_open (module_path, G_MODULE_BIND_LOCAL); - if (!module) { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED, - "Failed to open module %s: %s", module_path, g_module_error ()); - return FALSE; - } - - if (!g_module_symbol (module, G_STRINGIFY (WP_MODULE_INIT_SYMBOL), - &module_init)) { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_OPERATION_FAILED, - "Failed to locate symbol " G_STRINGIFY (WP_MODULE_INIT_SYMBOL) " in %s", - module_path); - g_module_close (module); - return FALSE; - } - - ((WpModuleInitFunc) module_init) (registry); - return TRUE; -} - -gboolean -wp_module_loader_load (WpModuleLoader * self, WpPluginRegistry * registry, - const gchar * abi, const gchar * module_name, GError ** error) -{ - if (!g_strcmp0 (abi, "C")) { - return wp_module_loader_load_c (self, registry, module_name, error); - } else { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT, - "unknown module ABI %s", abi); - return FALSE; - } -} diff --git a/src/module-loader.h b/src/module-loader.h deleted file mode 100644 index 553b8e57..00000000 --- a/src/module-loader.h +++ /dev/null @@ -1,27 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WIREPLUMBER_MODULE_LOADER_H__ -#define __WIREPLUMBER_MODULE_LOADER_H__ - -#include -#include - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE (WpModuleLoader, wp_module_loader, WP, MODULE_LOADER, GObject) - -WpModuleLoader * wp_module_loader_new (void); - -gboolean wp_module_loader_load (WpModuleLoader * self, - WpPluginRegistry * registry, const gchar * abi, const gchar * module_name, - GError ** error); - -G_END_DECLS - -#endif diff --git a/src/plugin-registry-impl.c b/src/plugin-registry-impl.c deleted file mode 100644 index 2e713882..00000000 --- a/src/plugin-registry-impl.c +++ /dev/null @@ -1,164 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "plugin-registry-impl.h" -#include - -typedef struct { - gsize block_size; - GType gtype; - const WpPluginMetadata *metadata; - WpPlugin *instance; -} PluginData; - -struct _WpPluginRegistryImpl -{ - WpInterfaceImpl parent; - - GList *plugins; - GStringChunk *metadata_strings; -}; - -static void wp_plugin_registry_impl_iface_init (WpPluginRegistryInterface * iface); - -G_DEFINE_TYPE_WITH_CODE (WpPluginRegistryImpl, wp_plugin_registry_impl, WP_TYPE_INTERFACE_IMPL, - G_IMPLEMENT_INTERFACE (WP_TYPE_PLUGIN_REGISTRY, wp_plugin_registry_impl_iface_init);) - -static void -wp_plugin_registry_impl_init (WpPluginRegistryImpl * self) -{ - self->metadata_strings = g_string_chunk_new (200); -} - -static void -plugin_data_free (PluginData *data) -{ - g_slice_free1 (data->block_size, data); -} - -static void -wp_plugin_registry_impl_finalize (GObject * object) -{ - WpPluginRegistryImpl *self = WP_PLUGIN_REGISTRY_IMPL (object); - - g_list_free_full (self->plugins, (GDestroyNotify) plugin_data_free); - g_string_chunk_free (self->metadata_strings); - - G_OBJECT_CLASS (wp_plugin_registry_impl_parent_class)->finalize (object); -} - -static void -wp_plugin_registry_impl_class_init (WpPluginRegistryImplClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - object_class->finalize = wp_plugin_registry_impl_finalize; -} - -static gint -compare_ranks (const WpPluginMetadata * a, const WpPluginMetadata * b) -{ - return (gint) b->rank - (gint) a->rank; -} - -static void -wp_plugin_registry_impl_register_plugin (WpPluginRegistry * r, - GType plugin_type, - const WpPluginMetadata * metadata, - gsize metadata_size, - gboolean static_data) -{ - WpPluginRegistryImpl *self = WP_PLUGIN_REGISTRY_IMPL (r); - PluginData *data; - - g_return_if_fail (metadata_size == sizeof (WpPluginMetadata)); - - if (static_data) { - data = g_slice_alloc (sizeof (PluginData)); - data->block_size = sizeof (PluginData); - } else { - data = g_slice_alloc (sizeof (PluginData) + sizeof (WpPluginMetadata)); - data->block_size = sizeof (PluginData) + sizeof (WpPluginMetadata); - } - - data->gtype = plugin_type; - data->instance = NULL; - - if (!static_data) { - WpPluginMetadata *m; - m = (WpPluginMetadata *) ((guint8 *) data) + sizeof (PluginData); - m->rank = metadata->rank; - m->name = g_string_chunk_insert (self->metadata_strings, metadata->name); - m->description = g_string_chunk_insert (self->metadata_strings, - metadata->description); - m->author = g_string_chunk_insert (self->metadata_strings, - metadata->author); - m->license = g_string_chunk_insert (self->metadata_strings, - metadata->license); - m->version = g_string_chunk_insert (self->metadata_strings, - metadata->version); - m->origin = g_string_chunk_insert (self->metadata_strings, - metadata->origin); - data->metadata = m; - } else { - data->metadata = metadata; - } - - self->plugins = g_list_insert_sorted (self->plugins, data, - (GCompareFunc) compare_ranks); -} - -static void -wp_plugin_registry_impl_iface_init (WpPluginRegistryInterface * iface) -{ - iface->register_plugin = wp_plugin_registry_impl_register_plugin; -} - -WpPluginRegistryImpl * -wp_plugin_registry_impl_new (void) -{ - return g_object_new (wp_plugin_registry_impl_get_type (), NULL); -} - -void -wp_plugin_registry_impl_unload (WpPluginRegistryImpl * self) -{ - GList *list; - PluginData *plugin_data; - - for (list = self->plugins; list != NULL; list = g_list_next (list)) { - plugin_data = list->data; - g_clear_object (&plugin_data->instance); - } -} - -static inline void -make_plugin (WpPluginRegistryImpl * self, PluginData * plugin_data) -{ - WpObject *core = wp_interface_impl_get_object (WP_INTERFACE_IMPL (self)); - plugin_data->instance = g_object_new (plugin_data->gtype, - "core", core, "metadata", plugin_data->metadata, NULL); -} - -gboolean -wp_plugin_registry_impl_invoke_internal (WpPluginRegistryImpl * self, - WpPluginFunc func, gpointer data) -{ - GList *list; - PluginData *plugin_data; - - for (list = self->plugins; list != NULL; list = g_list_next (list)) { - plugin_data = list->data; - if (!plugin_data->instance) - make_plugin (self, plugin_data); - - if (func (plugin_data->instance, data)) - return TRUE; - } - - return FALSE; -} diff --git a/src/plugin-registry-impl.h b/src/plugin-registry-impl.h deleted file mode 100644 index 578b1b28..00000000 --- a/src/plugin-registry-impl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_PLUGIN_REGISTRY_IMPL_H__ -#define __WP_PLUGIN_REGISTRY_IMPL_H__ - -#include - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE (WpPluginRegistryImpl, wp_plugin_registry_impl, - WP, PLUGIN_REGISTRY_IMPL, WpInterfaceImpl) - -WpPluginRegistryImpl * wp_plugin_registry_impl_new (void); - -void wp_plugin_registry_impl_unload (WpPluginRegistryImpl * self); - -typedef gboolean (*WpPluginFunc) (gpointer plugin, gpointer data); -gboolean wp_plugin_registry_impl_invoke_internal (WpPluginRegistryImpl * self, - WpPluginFunc func, gpointer data); - -#define wp_plugin_registry_impl_invoke(r, func, data) \ - G_STMT_START { \ - (0 ? func ((WpPlugin *) NULL, data) : \ - wp_plugin_registry_impl_invoke_internal ( \ - WP_PLUGIN_REGISTRY_IMPL (r), (WpPluginFunc) func, \ - (gpointer) data)); \ - } G_STMT_END - -G_END_DECLS - -#endif diff --git a/src/proxy-registry-impl.c b/src/proxy-registry-impl.c deleted file mode 100644 index 3ad0770a..00000000 --- a/src/proxy-registry-impl.c +++ /dev/null @@ -1,281 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "proxy-registry-impl.h" -#include "plugin-registry-impl.h" - -#include -#include - -#include -#include - -struct _WpProxyRegistryImpl -{ - WpInterfaceImpl parent; - - struct pw_remote *remote; - struct spa_hook remote_listener; - - struct pw_registry_proxy *reg_proxy; - struct spa_hook reg_proxy_listener; - - struct pw_map globals; - GArray *new_globals; -}; - -enum { - PROP_0, - PROP_REMOTE, -}; - -enum { - SIGNAL_NEW_PROXY_AVAILABLE, - N_SIGNALS -}; - -static guint signals[N_SIGNALS]; - -static void wp_proxy_registry_impl_iface_init (WpProxyRegistryInterface * iface); - -G_DEFINE_TYPE_WITH_CODE (WpProxyRegistryImpl, wp_proxy_registry_impl, WP_TYPE_INTERFACE_IMPL, - G_IMPLEMENT_INTERFACE (WP_TYPE_PROXY_REGISTRY, wp_proxy_registry_impl_iface_init);) - -static gint -guint32_compare (const guint32 *a, const guint32 *b) -{ - return (gint) ((gint64)*a - (gint64)*b); -} - -static gboolean -idle_notify_new_globals (gpointer data) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (data); - guint i; - guint32 id; - - // TODO verify these globals still exist - - g_array_sort (self->new_globals, (GCompareFunc) guint32_compare); - for (i = 0; i < self->new_globals->len; i++) { - id = g_array_index (self->new_globals, guint32, i); - g_signal_emit (self, signals[SIGNAL_NEW_PROXY_AVAILABLE], 0, - pw_map_lookup (&self->globals, id)); - } - g_array_remove_range (self->new_globals, 0, self->new_globals->len); - - return G_SOURCE_REMOVE; -} - -static inline void -map_insert (struct pw_map *map, guint32 id, gpointer obj) -{ - size_t size = pw_map_get_size (map); - while (id > size) - pw_map_insert_at (map, size++, NULL); - pw_map_insert_at (map, id, obj); -} - -static void -registry_global (void * data, uint32_t id, uint32_t parent_id, - uint32_t permissions, uint32_t type, uint32_t version, - const struct spa_dict * props) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (data); - WpProxy *proxy; - WpPluginRegistry *plugin_registry = NULL; - WpObject *core = wp_interface_impl_get_object (WP_INTERFACE_IMPL (self)); - - proxy = g_object_new (WP_TYPE_PROXY, - "id", id, - "parent-id", parent_id, - "spa-type", type, - "initial-properties", props, - "core", core, - NULL); - map_insert (&self->globals, id, proxy); - - plugin_registry = wp_interface_impl_get_sibling (WP_INTERFACE_IMPL (self), - WP_TYPE_PLUGIN_REGISTRY); - wp_plugin_registry_impl_invoke (plugin_registry, - wp_plugin_provide_interfaces, WP_OBJECT (proxy)); - - /* - * defer notifications until we return to the main loop; - * this allows the pipewire event loop to finish emitting - * all new available globals before we use them - */ - if (self->new_globals->len == 0) - g_idle_add (idle_notify_new_globals, self); - g_array_append_val (self->new_globals, id); -} - -static void -registry_global_remove (void * data, uint32_t id) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (data); - GObject *p = pw_map_lookup (&self->globals, id); - g_object_unref (p); - pw_map_insert_at (&self->globals, id, NULL); -} - -static const struct pw_registry_proxy_events registry_events = { - PW_VERSION_REGISTRY_PROXY_EVENTS, - .global = registry_global, - .global_remove = registry_global_remove, -}; - -static void -remote_state_changed (void * data, enum pw_remote_state old_state, - enum pw_remote_state new_state, const char * error) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (data); - - switch (new_state) { - case PW_REMOTE_STATE_CONNECTED: - self->reg_proxy = pw_core_proxy_get_registry ( - pw_remote_get_core_proxy (self->remote), - PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0); - pw_registry_proxy_add_listener (self->reg_proxy, - &self->reg_proxy_listener, ®istry_events, self); - break; - - case PW_REMOTE_STATE_UNCONNECTED: - self->reg_proxy = NULL; - break; - - default: - break; - } -} - -static const struct pw_remote_events remote_events = { - PW_VERSION_REMOTE_EVENTS, - .state_changed = remote_state_changed, -}; - -static void -wp_proxy_registry_impl_init (WpProxyRegistryImpl * self) -{ - pw_map_init (&self->globals, 64, 64); - self->new_globals = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 64); -} - -static void -wp_proxy_registry_impl_constructed (GObject * obj) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (obj); - - pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events, - self); - - G_OBJECT_CLASS (wp_proxy_registry_impl_parent_class)->constructed (obj); -} - -static void -wp_proxy_registry_impl_finalize (GObject * obj) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (obj); - - pw_map_clear (&self->globals); - g_array_unref (self->new_globals); - - G_OBJECT_CLASS (wp_proxy_registry_impl_parent_class)->finalize (obj); -} - -static void -wp_proxy_registry_impl_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (object); - - switch (property_id) { - case PROP_REMOTE: - self->remote = g_value_get_pointer (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_proxy_registry_impl_get_property (GObject * object, guint property_id, - GValue * value, GParamSpec * pspec) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (object); - - switch (property_id) { - case PROP_REMOTE: - g_value_set_pointer (value, self->remote); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -wp_proxy_registry_impl_class_init (WpProxyRegistryImplClass * klass) -{ - GObjectClass * object_class = (GObjectClass *) klass; - - object_class->constructed = wp_proxy_registry_impl_constructed; - object_class->finalize = wp_proxy_registry_impl_finalize; - object_class->get_property = wp_proxy_registry_impl_get_property; - object_class->set_property = wp_proxy_registry_impl_set_property; - - g_object_class_install_property (object_class, PROP_REMOTE, - g_param_spec_pointer ("remote", "remote", - "The underlying struct pw_remote *", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - signals[SIGNAL_NEW_PROXY_AVAILABLE] = g_signal_new ("new-proxy-available", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 1, WP_TYPE_PROXY); -} - -static WpProxy * -wp_proxy_registry_impl_get_proxy (WpProxyRegistry * r, guint32 global_id) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (r); - WpProxy *p = pw_map_lookup (&self->globals, global_id); - if (p) - g_object_ref (p); - return p; -} - -static struct pw_registry_proxy * -wp_proxy_registry_impl_get_pw_registry_proxy (WpProxyRegistry * r) -{ - WpProxyRegistryImpl *self = WP_PROXY_REGISTRY_IMPL (r); - return self->reg_proxy; -} - -static void -wp_proxy_registry_impl_iface_init (WpProxyRegistryInterface * iface) -{ - iface->get_proxy = wp_proxy_registry_impl_get_proxy; - iface->get_pw_registry_proxy = wp_proxy_registry_impl_get_pw_registry_proxy; -} - -WpProxyRegistryImpl * -wp_proxy_registry_impl_new (struct pw_remote * remote) -{ - return g_object_new (wp_proxy_registry_impl_get_type (), "remote", remote, - NULL); -} - -void -wp_proxy_registry_impl_unload (WpProxyRegistryImpl * self) -{ - size_t i, size = pw_map_get_size (&self->globals); - for (i = 0; i < size; i++) { - g_clear_object (&pw_map_get_item (&self->globals, i)->data); - } -} diff --git a/src/proxy-registry-impl.h b/src/proxy-registry-impl.h deleted file mode 100644 index a0e4944a..00000000 --- a/src/proxy-registry-impl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_PROXY_REGISTRY_IMPL_H__ -#define __WP_PROXY_REGISTRY_IMPL_H__ - -#include - -G_BEGIN_DECLS - -struct pw_remote; - -G_DECLARE_FINAL_TYPE (WpProxyRegistryImpl, wp_proxy_registry_impl, - WP, PROXY_REGISTRY_IMPL, WpInterfaceImpl) - -WpProxyRegistryImpl * wp_proxy_registry_impl_new (struct pw_remote * remote); - -void wp_proxy_registry_impl_unload (WpProxyRegistryImpl * self); - -G_END_DECLS - -#endif diff --git a/src/session-registry-impl.c b/src/session-registry-impl.c deleted file mode 100644 index 22fb42fb..00000000 --- a/src/session-registry-impl.c +++ /dev/null @@ -1,196 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "session-registry-impl.h" -#include "plugin-registry-impl.h" -#include "utils.h" - -#include -#include - -typedef struct -{ - guint32 id; - gchar *media_class; - WpSession *session; -} SessionData; - -struct _WpSessionRegistryImpl -{ - WpInterfaceImpl parent; - - guint32 next_id; - GArray *sessions; -}; - -static void wp_session_registry_impl_iface_init (WpSessionRegistryInterface * iface); - -G_DEFINE_TYPE_WITH_CODE (WpSessionRegistryImpl, wp_session_registry_impl, WP_TYPE_INTERFACE_IMPL, - G_IMPLEMENT_INTERFACE (WP_TYPE_SESSION_REGISTRY, wp_session_registry_impl_iface_init);) - -static void -wp_session_registry_impl_init (WpSessionRegistryImpl * self) -{ - self->sessions = g_array_new (FALSE, FALSE, sizeof (SessionData)); -} - -static void -wp_session_registry_impl_finalize (GObject * obj) -{ - WpSessionRegistryImpl * self = WP_SESSION_REGISTRY_IMPL (obj); - - g_array_unref (self->sessions); - - G_OBJECT_CLASS (wp_session_registry_impl_parent_class)->finalize (obj); -} - -static void -wp_session_registry_impl_class_init (WpSessionRegistryImplClass * klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - object_class->finalize = wp_session_registry_impl_finalize; -} - -static gboolean -media_class_matches (const gchar * media_class, const gchar * lookup) -{ - const gchar *c1 = media_class, *c2 = lookup; - - /* empty lookup matches all classes */ - if (!lookup) - return TRUE; - - /* compare until we reach the end of the lookup string */ - for (; *c2 != '\0'; c1++, c2++) { - if (*c1 != *c2) - return FALSE; - } - - /* the lookup may not end in a slash, however it must match up - * to the end of a submedia_class. i.e.: - * OK: media_class: Audio/Source/Virtual/ - * lookup: Audio/Source - * - * Not OK: media_class: Audio/Source/Virtual/ - * lookup: Audio/Sou - * - * if *c1 is not /, also check the previous char, because the lookup - * may actually end in a slash. - */ - if (*c1 != '/' && *(c1 - 1) != '/') - return FALSE; - - return TRUE; -} - -static gchar * -sanitize_media_class (const gchar *media_class) -{ - gsize len = strlen (media_class); - if (media_class[len-1] != '/') - return g_strdup_printf ("%s/", media_class); - else - return g_strdup (media_class); -} - -static guint32 -register_session (WpSessionRegistry * sr, - WpSession * session, - GError ** error) -{ - WpSessionRegistryImpl * self = WP_SESSION_REGISTRY_IMPL (sr); - WpPluginRegistry *plugin_registry = NULL; - const gchar *media_class = NULL; - SessionData data; - - plugin_registry = wp_interface_impl_get_sibling (WP_INTERFACE_IMPL (self), - WP_TYPE_PLUGIN_REGISTRY); - wp_plugin_registry_impl_invoke (plugin_registry, - wp_plugin_provide_interfaces, WP_OBJECT (session)); - - media_class = wp_session_get_media_class (session); - if (!media_class) { - g_set_error (error, WP_DOMAIN_CORE, WP_CODE_INVALID_ARGUMENT, - "session media_class is NULL"); - return -1; - } - - data.id = self->next_id++; - data.media_class = sanitize_media_class (media_class); - data.session = g_object_ref (session); - g_array_append_val (self->sessions, data); - - return data.id; -} - -static gboolean -unregister_session (WpSessionRegistry * sr, guint32 session_id) -{ - WpSessionRegistryImpl * self = WP_SESSION_REGISTRY_IMPL (sr); - guint i; - - for (i = 0; i < self->sessions->len; i++) { - SessionData *d = &g_array_index (self->sessions, SessionData, i); - if (session_id == d->id) { - g_free (d->media_class); - g_object_unref (d->session); - g_array_remove_index_fast (self->sessions, i); - return TRUE; - } - } - - return FALSE; -} - -static WpSession * -get_session (WpSessionRegistry * sr, guint32 session_id) -{ - WpSessionRegistryImpl * self = WP_SESSION_REGISTRY_IMPL (sr); - guint i; - - for (i = 0; i < self->sessions->len; i++) { - SessionData *d = &g_array_index (self->sessions, SessionData, i); - if (session_id == d->id) - return g_object_ref (d->session); - } - - return NULL; -} - -static GArray * -list_sessions (WpSessionRegistry * sr, const gchar * media_class) -{ - WpSessionRegistryImpl * self = WP_SESSION_REGISTRY_IMPL (sr); - guint i; - GArray *ret; - - ret = g_array_new (FALSE, FALSE, sizeof (guint32)); - - for (i = 0; i < self->sessions->len; i++) { - SessionData *d = &g_array_index (self->sessions, SessionData, i); - if (media_class_matches (d->media_class, media_class)) - g_array_append_val (ret, d->id); - } - - return ret; -} - -static void -wp_session_registry_impl_iface_init (WpSessionRegistryInterface * iface) -{ - iface->register_session = register_session; - iface->unregister_session = unregister_session; - iface->get_session = get_session; - iface->list_sessions = list_sessions; -} - -WpSessionRegistryImpl * -wp_session_registry_impl_new (void) -{ - return g_object_new (wp_session_registry_impl_get_type (), NULL); -} diff --git a/src/session-registry-impl.h b/src/session-registry-impl.h deleted file mode 100644 index 66d6d55d..00000000 --- a/src/session-registry-impl.h +++ /dev/null @@ -1,23 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WP_SESSION_REGISTRY_IMPL_H__ -#define __WP_SESSION_REGISTRY_IMPL_H__ - -#include - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE (WpSessionRegistryImpl, wp_session_registry_impl, - WP, SESSION_REGISTRY_IMPL, WpInterfaceImpl) - -WpSessionRegistryImpl * wp_session_registry_impl_new (void); - -G_END_DECLS - -#endif diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 9a2d99cf..00000000 --- a/src/utils.h +++ /dev/null @@ -1,29 +0,0 @@ -/* WirePlumber - * - * Copyright © 2019 Collabora Ltd. - * @author George Kiagiadakis - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __WIREPLUMBER_UTILS_H__ -#define __WIREPLUMBER_UTILS_H__ - -#include - -G_BEGIN_DECLS - -GQuark wp_domain_core_quark (void); -#define WP_DOMAIN_CORE (wp_domain_core_quark ()) - -enum WpCoreCode { - WP_CODE_DISCONNECTED = 0, - WP_CODE_INTERRUPTED, - WP_CODE_OPERATION_FAILED, - WP_CODE_INVALID_ARGUMENT, - WP_CODE_REMOTE_ERROR, -}; - -G_END_DECLS - -#endif