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.
This commit is contained in:
@@ -1,266 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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 <email@domain>, author2 <email@domain>
|
|
||||||
* @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);
|
|
||||||
}
|
|
@@ -1,129 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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
|
|
107
lib/wp/core.c
Normal file
107
lib/wp/core.c
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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)
|
43
lib/wp/core.h
Normal file
43
lib/wp/core.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WIREPLUMBER_CORE_H__
|
||||||
|
#define __WIREPLUMBER_CORE_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
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
|
439
lib/wp/endpoint.c
Normal file
439
lib/wp/endpoint.c
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
82
lib/wp/endpoint.h
Normal file
82
lib/wp/endpoint.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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
|
@@ -18,6 +18,8 @@ GQuark wp_domain_library_quark (void);
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WP_LIBRARY_ERROR_INVARIANT,
|
WP_LIBRARY_ERROR_INVARIANT,
|
||||||
|
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
|
||||||
|
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||||
} WpLibraryErrorEnum;
|
} WpLibraryErrorEnum;
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
48
lib/wp/factory.c
Normal file
48
lib/wp/factory.c
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
69
lib/wp/factory.h
Normal file
69
lib/wp/factory.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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
|
@@ -1,121 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
}
|
|
@@ -1,45 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_INTERFACE_IMPL_H__
|
|
||||||
#define __WP_INTERFACE_IMPL_H__
|
|
||||||
|
|
||||||
#include <glib-object.h>
|
|
||||||
|
|
||||||
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
|
|
@@ -1,21 +1,19 @@
|
|||||||
wp_lib_sources = [
|
wp_lib_sources = [
|
||||||
'core-interfaces.c',
|
'core.c',
|
||||||
|
'endpoint.c',
|
||||||
'error.c',
|
'error.c',
|
||||||
'interface-impl.c',
|
'factory.c',
|
||||||
'object.c',
|
'module.c',
|
||||||
'plugin.c',
|
'session-manager.c',
|
||||||
'proxy.c',
|
|
||||||
'session.c',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
wp_lib_headers = [
|
wp_lib_headers = [
|
||||||
'core-interfaces.h',
|
'core.h',
|
||||||
|
'endpoint.h',
|
||||||
'error.h',
|
'error.h',
|
||||||
'interface-impl.h',
|
'factory.h',
|
||||||
'object.h',
|
'module.h',
|
||||||
'plugin.h',
|
'session-manager.h',
|
||||||
'proxy.h',
|
|
||||||
'session.h',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
enums = gnome.mkenums_simple('wpenums', sources: wp_lib_headers)
|
enums = gnome.mkenums_simple('wpenums', sources: wp_lib_headers)
|
||||||
@@ -28,14 +26,15 @@ wp_lib = library('wireplumber-' + wireplumber_api_version,
|
|||||||
'-DG_LOG_DOMAIN="libwireplumber"'
|
'-DG_LOG_DOMAIN="libwireplumber"'
|
||||||
],
|
],
|
||||||
install: true,
|
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,
|
gnome.generate_gir(wp_lib,
|
||||||
namespace: 'Wp',
|
namespace: 'Wp',
|
||||||
nsversion: wireplumber_api_version,
|
nsversion: wireplumber_api_version,
|
||||||
sources: [wp_lib_sources, wp_lib_headers],
|
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,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
137
lib/wp/module.c
Normal file
137
lib/wp/module.c
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include <gmodule.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
29
lib/wp/module.h
Normal file
29
lib/wp/module.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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
|
225
lib/wp/object.c
225
lib/wp/object.c
@@ -1,225 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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);
|
|
||||||
}
|
|
@@ -1,54 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_OBJECT_H__
|
|
||||||
#define __WP_OBJECT_H__
|
|
||||||
|
|
||||||
#include <glib-object.h>
|
|
||||||
|
|
||||||
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
|
|
277
lib/wp/plugin.c
277
lib/wp/plugin.c
@@ -1,277 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "plugin.h"
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
190
lib/wp/plugin.h
190
lib/wp/plugin.h
@@ -1,190 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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 <email@domain>, author2 <email@domain>
|
|
||||||
* @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
|
|
611
lib/wp/proxy.c
611
lib/wp/proxy.c
@@ -1,611 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "proxy.h"
|
|
||||||
#include "error.h"
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
#include <spa/debug/types.h>
|
|
||||||
#include <spa/pod/builder.h>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_PROXY_H__
|
|
||||||
#define __WP_PROXY_H__
|
|
||||||
|
|
||||||
#include "object.h"
|
|
||||||
#include "core-interfaces.h"
|
|
||||||
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
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
|
|
120
lib/wp/session-manager.c
Normal file
120
lib/wp/session-manager.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
34
lib/wp/session-manager.h
Normal file
34
lib/wp/session-manager.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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
|
101
lib/wp/session.c
101
lib/wp/session.c
@@ -1,101 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
}
|
|
@@ -1,34 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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
|
|
@@ -6,6 +6,9 @@
|
|||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utils.h"
|
#include "core.h"
|
||||||
|
#include "endpoint.h"
|
||||||
G_DEFINE_QUARK (wireplumber-core, wp_domain_core);
|
#include "error.h"
|
||||||
|
#include "factory.h"
|
||||||
|
#include "module.h"
|
||||||
|
#include "session-manager.h"
|
@@ -4,9 +4,36 @@ common_c_args = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
shared_library(
|
shared_library(
|
||||||
'wireplumber-module-default-session',
|
'wireplumber-module-pipewire',
|
||||||
['module-default-session.c'],
|
[
|
||||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="wireplumber-module-default-session"'],
|
'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 : true,
|
||||||
#install_dir : modules_install_dir,
|
#install_dir : modules_install_dir,
|
||||||
dependencies : [wp_dep, pipewire_dep],
|
dependencies : [wp_dep, pipewire_dep],
|
||||||
|
@@ -1,338 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <wp/plugin.h>
|
|
||||||
#include <wp/session.h>
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
#include <spa/param/audio/format-utils.h>
|
|
||||||
|
|
||||||
#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 <george.kiagiadakis@collabora.com>",
|
|
||||||
.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));
|
|
||||||
}
|
|
68
modules/module-pipewire.c
Normal file
68
modules/module-pipewire.c
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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 <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
@@ -6,18 +6,12 @@
|
|||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration between the PipeWire main loop and GMainLoop
|
||||||
|
*/
|
||||||
|
|
||||||
#include "loop-source.h"
|
#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
|
static gboolean
|
||||||
wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
wp_loop_source_dispatch (GSource * s, GSourceFunc callback, gpointer user_data)
|
||||||
{
|
{
|
||||||
@@ -58,9 +52,3 @@ wp_loop_source_new (void)
|
|||||||
|
|
||||||
return (GSource *) s;
|
return (GSource *) s;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pw_loop *
|
|
||||||
wp_loop_source_get_loop (GSource *s)
|
|
||||||
{
|
|
||||||
return WP_LOOP_SOURCE(s)->loop;
|
|
||||||
}
|
|
21
modules/module-pipewire/loop-source.h
Normal file
21
modules/module-pipewire/loop-source.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#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);
|
71
modules/module-pipewire/simple-endpoint-link.c
Normal file
71
modules/module-pipewire/simple-endpoint-link.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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 <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
63
modules/module-pipewire/simple-endpoint.c
Normal file
63
modules/module-pipewire/simple-endpoint.c
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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 <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
20
modules/module-pw-alsa-udev.c
Normal file
20
modules/module-pw-alsa-udev.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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 <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
||||||
|
{
|
||||||
|
}
|
65
modules/module-pw-audio-softdsp-endpoint.c
Normal file
65
modules/module-pw-audio-softdsp-endpoint.c
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* WirePlumber
|
||||||
|
*
|
||||||
|
* Copyright © 2019 Collabora Ltd.
|
||||||
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
||||||
|
*
|
||||||
|
* 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 <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
358
src/core.c
358
src/core.c
@@ -1,358 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* 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 <wp/plugin.h>
|
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
#include <glib-unix.h>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
26
src/core.h
26
src/core.h
@@ -1,26 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WIREPLUMBER_CORE_H__
|
|
||||||
#define __WIREPLUMBER_CORE_H__
|
|
||||||
|
|
||||||
#include <wp/object.h>
|
|
||||||
|
|
||||||
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
|
|
@@ -1,28 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WIREPLUMBER_LOOP_SOURCE_H__
|
|
||||||
#define __WIREPLUMBER_LOOP_SOURCE_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
|
|
||||||
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
|
|
209
src/main.c
209
src/main.c
@@ -6,40 +6,221 @@
|
|||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "core.h"
|
#include <wp/wp.h>
|
||||||
#include "utils.h"
|
#include <gio/gio.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#define WIREPLUMBER_DEFAULT_CONFIG_FILE "wireplumber.conf"
|
||||||
|
|
||||||
static GOptionEntry entries[] =
|
static GOptionEntry entries[] =
|
||||||
{
|
{
|
||||||
{ NULL }
|
{ 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
|
gint
|
||||||
main (gint argc, gchar **argv)
|
main (gint argc, gchar **argv)
|
||||||
{
|
{
|
||||||
|
struct WpDaemonData data = {0};
|
||||||
g_autoptr (GOptionContext) context = NULL;
|
g_autoptr (GOptionContext) context = NULL;
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr (GError) error = NULL;
|
||||||
g_autoptr (WpCore) core = NULL;
|
g_autoptr (WpCore) core = NULL;
|
||||||
gint ret = 0;
|
g_autoptr (GMainLoop) loop = NULL;
|
||||||
|
|
||||||
context = g_option_context_new ("- PipeWire Session/Policy Manager");
|
context = g_option_context_new ("- PipeWire Session/Policy Manager");
|
||||||
g_option_context_add_main_entries (context, entries, NULL);
|
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;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
pw_init (NULL, NULL);
|
/* init wireplumber */
|
||||||
|
|
||||||
core = wp_core_get_instance ();
|
data.core = core = wp_core_new ();
|
||||||
wp_core_run (core, &error);
|
|
||||||
|
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:
|
out:
|
||||||
if (error) {
|
if (data.exit_message) {
|
||||||
ret = error->code;
|
g_message ("%s", data.exit_message);
|
||||||
if (error->domain != WP_DOMAIN_CORE)
|
if (data.free_message)
|
||||||
ret += 100;
|
data.free_message (data.exit_message);
|
||||||
g_message ("exit code %d; %s", ret, error->message);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return data.exit_code;
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,5 @@
|
|||||||
wp_sources = [
|
wp_sources = [
|
||||||
'core.c',
|
|
||||||
'loop-source.c',
|
|
||||||
'main.c',
|
'main.c',
|
||||||
'module-loader.c',
|
|
||||||
'plugin-registry-impl.c',
|
|
||||||
'proxy-registry-impl.c',
|
|
||||||
'session-registry-impl.c',
|
|
||||||
'utils.c',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('wireplumber',
|
executable('wireplumber',
|
||||||
@@ -17,5 +10,5 @@ executable('wireplumber',
|
|||||||
'-DG_LOG_DOMAIN="wireplumber"'
|
'-DG_LOG_DOMAIN="wireplumber"'
|
||||||
],
|
],
|
||||||
install: true,
|
install: true,
|
||||||
dependencies : [gobject_dep, gmodule_dep, gio_dep, pipewire_dep, wp_dep],
|
dependencies : [gobject_dep, gio_dep, wp_dep],
|
||||||
)
|
)
|
||||||
|
@@ -1,81 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "module-loader.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include <wp/plugin.h>
|
|
||||||
#include <gmodule.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WIREPLUMBER_MODULE_LOADER_H__
|
|
||||||
#define __WIREPLUMBER_MODULE_LOADER_H__
|
|
||||||
|
|
||||||
#include <glib-object.h>
|
|
||||||
#include <wp/core-interfaces.h>
|
|
||||||
|
|
||||||
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
|
|
@@ -1,164 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "plugin-registry-impl.h"
|
|
||||||
#include <wp/plugin.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_PLUGIN_REGISTRY_IMPL_H__
|
|
||||||
#define __WP_PLUGIN_REGISTRY_IMPL_H__
|
|
||||||
|
|
||||||
#include <wp/core-interfaces.h>
|
|
||||||
|
|
||||||
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
|
|
@@ -1,281 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "proxy-registry-impl.h"
|
|
||||||
#include "plugin-registry-impl.h"
|
|
||||||
|
|
||||||
#include <wp/proxy.h>
|
|
||||||
#include <wp/plugin.h>
|
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
|
||||||
#include <pipewire/map.h>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_PROXY_REGISTRY_IMPL_H__
|
|
||||||
#define __WP_PROXY_REGISTRY_IMPL_H__
|
|
||||||
|
|
||||||
#include <wp/core-interfaces.h>
|
|
||||||
|
|
||||||
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
|
|
@@ -1,196 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "session-registry-impl.h"
|
|
||||||
#include "plugin-registry-impl.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
#include <wp/session.h>
|
|
||||||
#include <wp/plugin.h>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WP_SESSION_REGISTRY_IMPL_H__
|
|
||||||
#define __WP_SESSION_REGISTRY_IMPL_H__
|
|
||||||
|
|
||||||
#include <wp/core-interfaces.h>
|
|
||||||
|
|
||||||
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
|
|
29
src/utils.h
29
src/utils.h
@@ -1,29 +0,0 @@
|
|||||||
/* WirePlumber
|
|
||||||
*
|
|
||||||
* Copyright © 2019 Collabora Ltd.
|
|
||||||
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __WIREPLUMBER_UTILS_H__
|
|
||||||
#define __WIREPLUMBER_UTILS_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
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
|
|
Reference in New Issue
Block a user