proxy: refactor the proxy class to hide pipewire API and make things easier

This commit is contained in:
George Kiagiadakis
2019-08-22 20:04:39 +03:00
parent 199241894e
commit bdce3b4de5
10 changed files with 784 additions and 500 deletions

View File

@@ -6,46 +6,29 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include "error.h"
#include "proxy-link.h" #include "proxy-link.h"
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
struct _WpProxyLink struct _WpProxyLink
{ {
WpProxy parent; WpProxy parent;
/* The task to signal the proxy is initialized */
GTask *init_task;
/* The link proxy listener */ /* The link proxy listener */
struct spa_hook listener; struct spa_hook listener;
/* The link info */
struct pw_link_info *info;
}; };
static void wp_proxy_link_async_initable_init (gpointer iface, G_DEFINE_TYPE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY)
gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
wp_proxy_link_async_initable_init))
static void static void
link_event_info(void *data, const struct pw_link_info *info) link_event_info(void *data, const struct pw_link_info *info)
{ {
WpProxyLink *self = data; WpProxy *proxy = WP_PROXY (data);
/* Make sure the task is valid */ wp_proxy_update_native_info (proxy, info,
if (!self->init_task) (WpProxyNativeInfoUpdate) pw_link_info_update,
return; (GDestroyNotify) pw_link_info_free);
wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO);
/* Update the link info */
self->info = pw_link_info_update(self->info, info);
/* Finish the creation of the proxy */
g_task_return_boolean (self->init_task, TRUE);
g_clear_object (&self->init_task);
} }
static const struct pw_link_proxy_events link_events = { static const struct pw_link_proxy_events link_events = {
@@ -53,102 +36,23 @@ static const struct pw_link_proxy_events link_events = {
.info = link_event_info, .info = link_event_info,
}; };
static void
wp_proxy_link_finalize (GObject * object)
{
WpProxyLink *self = WP_PROXY_LINK(object);
/* Destroy the init task */
g_clear_object (&self->init_task);
/* Clear the info */
if (self->info) {
pw_link_info_free(self->info);
self->info = NULL;
}
G_OBJECT_CLASS (wp_proxy_link_parent_class)->finalize (object);
}
static void
wp_proxy_link_destroy (WpProxy * proxy)
{
WpProxyLink *self = WP_PROXY_LINK(proxy);
GError *error = NULL;
/* Return error if the pipewire destruction happened while the async creation
* of this proxy link object has not finished */
if (self->init_task) {
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"pipewire link proxy destroyed before finishing");
g_task_return_error (self->init_task, error);
g_clear_object (&self->init_task);
}
}
static void
wp_proxy_link_init_async (GAsyncInitable *initable, int io_priority,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
{
WpProxyLink *self = WP_PROXY_LINK(initable);
WpProxy *wp_proxy = WP_PROXY(initable);
struct pw_link_proxy *proxy = NULL;
/* Create the async task */
self->init_task = g_task_new (initable, cancellable, callback, data);
/* Get the proxy from the base class */
proxy = wp_proxy_get_pw_proxy(wp_proxy);
/* Add the link proxy listener */
pw_link_proxy_add_listener(proxy, &self->listener, &link_events, self);
}
static void
wp_proxy_link_async_initable_init (gpointer iface, gpointer iface_data)
{
GAsyncInitableIface *ai_iface = iface;
/* Only set the init_async */
ai_iface->init_async = wp_proxy_link_init_async;
}
static void static void
wp_proxy_link_init (WpProxyLink * self) wp_proxy_link_init (WpProxyLink * self)
{ {
} }
static void
wp_proxy_link_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
{
WpProxyLink *self = WP_PROXY_LINK (proxy);
pw_link_proxy_add_listener ((struct pw_link_proxy *) pw_proxy,
&self->listener, &link_events, self);
}
static void static void
wp_proxy_link_class_init (WpProxyLinkClass * klass) wp_proxy_link_class_init (WpProxyLinkClass * klass)
{ {
GObjectClass *object_class = (GObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->finalize = wp_proxy_link_finalize; proxy_class->pw_proxy_created = wp_proxy_link_pw_proxy_created;
proxy_class->destroy = wp_proxy_link_destroy;
}
void
wp_proxy_link_new (guint global_id, gpointer proxy,
GAsyncReadyCallback callback, gpointer user_data)
{
g_async_initable_new_async (
WP_TYPE_PROXY_LINK, G_PRIORITY_DEFAULT, NULL, callback, user_data,
"global-id", global_id,
"pw-proxy", proxy,
NULL);
}
WpProxyLink *
wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, GError **error)
{
GAsyncInitable *ai = G_ASYNC_INITABLE(initable);
return WP_PROXY_LINK(g_async_initable_new_finish(ai, res, error));
}
const struct pw_link_info *
wp_proxy_link_get_info (WpProxyLink * self)
{
return self->info;
} }

View File

@@ -9,7 +9,6 @@
#ifndef __WIREPLUMBER_PROXY_LINK_H__ #ifndef __WIREPLUMBER_PROXY_LINK_H__
#define __WIREPLUMBER_PROXY_LINK_H__ #define __WIREPLUMBER_PROXY_LINK_H__
#include "core.h"
#include "proxy.h" #include "proxy.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@@ -17,12 +16,12 @@ G_BEGIN_DECLS
#define WP_TYPE_PROXY_LINK (wp_proxy_link_get_type ()) #define WP_TYPE_PROXY_LINK (wp_proxy_link_get_type ())
G_DECLARE_FINAL_TYPE (WpProxyLink, wp_proxy_link, WP, PROXY_LINK, WpProxy) G_DECLARE_FINAL_TYPE (WpProxyLink, wp_proxy_link, WP, PROXY_LINK, WpProxy)
void wp_proxy_link_new (guint global_id, gpointer proxy, static inline const struct pw_link_info *
GAsyncReadyCallback callback, gpointer user_data); wp_proxy_link_get_info (WpProxyLink * self)
WpProxyLink *wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, {
GError **error); return (const struct pw_link_info *)
wp_proxy_get_native_info (WP_PROXY (self));
const struct pw_link_info *wp_proxy_link_get_info (WpProxyLink * self); }
G_END_DECLS G_END_DECLS

View File

@@ -6,46 +6,29 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include "error.h"
#include "proxy-node.h" #include "proxy-node.h"
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
struct _WpProxyNode struct _WpProxyNode
{ {
WpProxy parent; WpProxy parent;
/* The task to signal the proxy is initialized */
GTask *init_task;
/* The node proxy listener */ /* The node proxy listener */
struct spa_hook listener; struct spa_hook listener;
/* The node info */
struct pw_node_info *info;
}; };
static void wp_proxy_node_async_initable_init (gpointer iface, G_DEFINE_TYPE (WpProxyNode, wp_proxy_node, WP_TYPE_PROXY)
gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (WpProxyNode, wp_proxy_node, WP_TYPE_PROXY,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
wp_proxy_node_async_initable_init))
static void static void
node_event_info(void *data, const struct pw_node_info *info) node_event_info(void *data, const struct pw_node_info *info)
{ {
WpProxyNode *self = data; WpProxy *proxy = WP_PROXY (data);
/* Make sure the task is valid */ wp_proxy_update_native_info (proxy, info,
if (!self->init_task) (WpProxyNativeInfoUpdate) pw_node_info_update,
return; (GDestroyNotify) pw_node_info_free);
wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO);
/* Update the node info */
self->info = pw_node_info_update(self->info, info);
/* Finish the creation of the proxy */
g_task_return_boolean (self->init_task, TRUE);
g_clear_object (&self->init_task);
} }
static const struct pw_node_proxy_events node_events = { static const struct pw_node_proxy_events node_events = {
@@ -53,102 +36,23 @@ static const struct pw_node_proxy_events node_events = {
.info = node_event_info, .info = node_event_info,
}; };
static void
wp_proxy_node_finalize (GObject * object)
{
WpProxyNode *self = WP_PROXY_NODE(object);
/* Destroy the init task */
g_clear_object (&self->init_task);
/* Clear the info */
if (self->info) {
pw_node_info_free(self->info);
self->info = NULL;
}
G_OBJECT_CLASS (wp_proxy_node_parent_class)->finalize (object);
}
static void
wp_proxy_node_destroy (WpProxy * proxy)
{
WpProxyNode *self = WP_PROXY_NODE(proxy);
GError *error = NULL;
/* Return error if the pipewire destruction happened while the async creation
* of this proxy node object has not finished */
if (self->init_task) {
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"pipewire node proxy destroyed before finishing");
g_task_return_error (self->init_task, error);
g_clear_object (&self->init_task);
}
}
static void
wp_proxy_node_init_async (GAsyncInitable *initable, int io_priority,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
{
WpProxyNode *self = WP_PROXY_NODE(initable);
WpProxy *wp_proxy = WP_PROXY(initable);
struct pw_node_proxy *proxy = NULL;
/* Create the async task */
self->init_task = g_task_new (initable, cancellable, callback, data);
/* Get the proxy from the base class */
proxy = wp_proxy_get_pw_proxy(wp_proxy);
/* Add the node proxy listener */
pw_node_proxy_add_listener(proxy, &self->listener, &node_events, self);
}
static void
wp_proxy_node_async_initable_init (gpointer iface, gpointer iface_data)
{
GAsyncInitableIface *ai_iface = iface;
/* Only set the init_async */
ai_iface->init_async = wp_proxy_node_init_async;
}
static void static void
wp_proxy_node_init (WpProxyNode * self) wp_proxy_node_init (WpProxyNode * self)
{ {
} }
static void
wp_proxy_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
{
WpProxyNode *self = WP_PROXY_NODE (proxy);
pw_node_proxy_add_listener ((struct pw_node_proxy *) pw_proxy,
&self->listener, &node_events, self);
}
static void static void
wp_proxy_node_class_init (WpProxyNodeClass * klass) wp_proxy_node_class_init (WpProxyNodeClass * klass)
{ {
GObjectClass *object_class = (GObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->finalize = wp_proxy_node_finalize; proxy_class->pw_proxy_created = wp_proxy_node_pw_proxy_created;
proxy_class->destroy = wp_proxy_node_destroy;
}
void
wp_proxy_node_new (guint global_id, gpointer proxy,
GAsyncReadyCallback callback, gpointer user_data)
{
g_async_initable_new_async (
WP_TYPE_PROXY_NODE, G_PRIORITY_DEFAULT, NULL, callback, user_data,
"global-id", global_id,
"pw-proxy", proxy,
NULL);
}
WpProxyNode *
wp_proxy_node_new_finish(GObject *initable, GAsyncResult *res, GError **error)
{
GAsyncInitable *ai = G_ASYNC_INITABLE(initable);
return WP_PROXY_NODE(g_async_initable_new_finish(ai, res, error));
}
const struct pw_node_info *
wp_proxy_node_get_info (WpProxyNode * self)
{
return self->info;
} }

View File

@@ -9,7 +9,6 @@
#ifndef __WIREPLUMBER_PROXY_NODE_H__ #ifndef __WIREPLUMBER_PROXY_NODE_H__
#define __WIREPLUMBER_PROXY_NODE_H__ #define __WIREPLUMBER_PROXY_NODE_H__
#include "core.h"
#include "proxy.h" #include "proxy.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@@ -17,12 +16,12 @@ G_BEGIN_DECLS
#define WP_TYPE_PROXY_NODE (wp_proxy_node_get_type ()) #define WP_TYPE_PROXY_NODE (wp_proxy_node_get_type ())
G_DECLARE_FINAL_TYPE (WpProxyNode, wp_proxy_node, WP, PROXY_NODE, WpProxy) G_DECLARE_FINAL_TYPE (WpProxyNode, wp_proxy_node, WP, PROXY_NODE, WpProxy)
void wp_proxy_node_new (guint global_id, gpointer proxy, static inline const struct pw_node_info *
GAsyncReadyCallback callback, gpointer user_data); wp_proxy_node_get_info (WpProxyNode * self)
WpProxyNode *wp_proxy_node_new_finish(GObject *initable, GAsyncResult *res, {
GError **error); return (const struct pw_node_info *)
wp_proxy_get_native_info (WP_PROXY (self));
const struct pw_node_info *wp_proxy_node_get_info (WpProxyNode * self); }
G_END_DECLS G_END_DECLS

View File

@@ -6,8 +6,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include "error.h"
#include "proxy-port.h" #include "proxy-port.h"
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <spa/param/audio/format-utils.h> #include <spa/param/audio/format-utils.h>
@@ -15,46 +15,33 @@ struct _WpProxyPort
{ {
WpProxy parent; WpProxy parent;
/* The task to signal the proxy is initialized */
GTask *init_task;
/* The port proxy listener */ /* The port proxy listener */
struct spa_hook listener; struct spa_hook listener;
/* The port info */
struct pw_port_info *info;
/* The port format */ /* The port format */
uint32_t media_type; uint32_t media_type;
uint32_t media_subtype; uint32_t media_subtype;
struct spa_audio_info_raw format; struct spa_audio_info_raw format;
}; };
static void wp_proxy_port_async_initable_init (gpointer iface, G_DEFINE_TYPE (WpProxyPort, wp_proxy_port, WP_TYPE_PROXY)
gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (WpProxyPort, wp_proxy_port, WP_TYPE_PROXY,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
wp_proxy_port_async_initable_init))
static void static void
port_event_info(void *data, const struct pw_port_info *info) port_event_info(void *data, const struct pw_port_info *info)
{ {
WpProxyPort *self = data; WpProxy *proxy = WP_PROXY (data);
/* Update the port info */ wp_proxy_update_native_info (proxy, info,
self->info = pw_port_info_update(self->info, info); (WpProxyNativeInfoUpdate) pw_port_info_update,
(GDestroyNotify) pw_port_info_free);
wp_proxy_set_feature_ready (proxy, WP_PROXY_FEATURE_INFO);
} }
static void static void
port_event_param(void *data, int seq, uint32_t id, uint32_t index, port_event_param(void *data, int seq, uint32_t id, uint32_t index,
uint32_t next, const struct spa_pod *param) uint32_t next, const struct spa_pod *param)
{ {
WpProxyPort *self = data; WpProxyPort *self = WP_PROXY_PORT (data);
/* Make sure the task is valid */
if (!self->init_task)
return;
/* Only handle EnumFormat */ /* Only handle EnumFormat */
if (id != SPA_PARAM_EnumFormat) if (id != SPA_PARAM_EnumFormat)
@@ -64,17 +51,14 @@ port_event_param(void *data, int seq, uint32_t id, uint32_t index,
spa_format_parse(param, &self->media_type, &self->media_subtype); spa_format_parse(param, &self->media_type, &self->media_subtype);
/* Only handle raw audio formats for now */ /* Only handle raw audio formats for now */
if (self->media_type != SPA_MEDIA_TYPE_audio || if (self->media_type == SPA_MEDIA_TYPE_audio &&
self->media_subtype != SPA_MEDIA_SUBTYPE_raw) self->media_subtype == SPA_MEDIA_SUBTYPE_raw) {
return;
/* Parse the raw audio format */ /* Parse the raw audio format */
spa_pod_fixate ((struct spa_pod *) param); spa_pod_fixate ((struct spa_pod *) param);
spa_format_audio_raw_parse (param, &self->format); spa_format_audio_raw_parse (param, &self->format);
}
/* Finish the creation of the proxy */ wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_PORT_FEATURE_FORMAT);
g_task_return_boolean (self->init_task, TRUE);
g_clear_object (&self->init_task);
} }
static const struct pw_port_proxy_events port_events = { static const struct pw_port_proxy_events port_events = {
@@ -83,108 +67,41 @@ static const struct pw_port_proxy_events port_events = {
.param = port_event_param, .param = port_event_param,
}; };
static void
wp_proxy_port_finalize (GObject * object)
{
WpProxyPort *self = WP_PROXY_PORT(object);
/* Destroy the init task */
g_clear_object (&self->init_task);
/* Clear the indo */
if (self->info) {
pw_port_info_free(self->info);
self->info = NULL;
}
G_OBJECT_CLASS (wp_proxy_port_parent_class)->finalize (object);
}
static void
wp_proxy_port_destroy (WpProxy * proxy)
{
WpProxyPort *self = WP_PROXY_PORT(proxy);
GError *error = NULL;
/* Return error if the pipewire destruction happened while the async creation
* of this proxy port object has not finished */
if (self->init_task) {
g_set_error (&error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
"pipewire port proxy destroyed before finishing");
g_task_return_error (self->init_task, error);
g_clear_object (&self->init_task);
}
}
static void
wp_proxy_port_init_async (GAsyncInitable *initable, int io_priority,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
{
WpProxyPort *self = WP_PROXY_PORT(initable);
WpProxy *wp_proxy = WP_PROXY(initable);
struct pw_port_proxy *proxy = NULL;
/* Create the async task */
self->init_task = g_task_new (initable, cancellable, callback, data);
/* Get the proxy from the base class */
proxy = wp_proxy_get_pw_proxy(wp_proxy);
/* Add the port proxy listener */
pw_port_proxy_add_listener(proxy, &self->listener, &port_events, self);
/* Emit the EnumFormat param */
pw_port_proxy_enum_params((struct pw_port_proxy*)proxy, 0,
SPA_PARAM_EnumFormat, 0, -1, NULL);
}
static void
wp_proxy_port_async_initable_init (gpointer iface, gpointer iface_data)
{
GAsyncInitableIface *ai_iface = iface;
/* Only set the init_async */
ai_iface->init_async = wp_proxy_port_init_async;
}
static void static void
wp_proxy_port_init (WpProxyPort * self) wp_proxy_port_init (WpProxyPort * self)
{ {
} }
static void
wp_proxy_port_augment (WpProxy * proxy, WpProxyFeatures features)
{
/* call the default implementation to ensure we have a proxy, if necessary */
WP_PROXY_CLASS (wp_proxy_port_parent_class)->augment (proxy, features);
if (features & WP_PROXY_PORT_FEATURE_FORMAT) {
struct pw_proxy *pwp = wp_proxy_get_pw_proxy (proxy);
g_return_if_fail (pwp != NULL);
pw_port_proxy_enum_params ((struct pw_port_proxy *) pwp, 0,
SPA_PARAM_EnumFormat, 0, -1, NULL);
}
}
static void
wp_proxy_port_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
{
WpProxyPort *self = WP_PROXY_PORT (proxy);
pw_port_proxy_add_listener ((struct pw_port_proxy *) pw_proxy,
&self->listener, &port_events, self);
}
static void static void
wp_proxy_port_class_init (WpProxyPortClass * klass) wp_proxy_port_class_init (WpProxyPortClass * klass)
{ {
GObjectClass *object_class = (GObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass; WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->finalize = wp_proxy_port_finalize; proxy_class->augment = wp_proxy_port_augment;
proxy_class->pw_proxy_created = wp_proxy_port_pw_proxy_created;
proxy_class->destroy = wp_proxy_port_destroy;
}
void
wp_proxy_port_new (guint global_id, gpointer proxy,
GAsyncReadyCallback callback, gpointer user_data)
{
g_async_initable_new_async (
WP_TYPE_PROXY_PORT, G_PRIORITY_DEFAULT, NULL, callback, user_data,
"global-id", global_id,
"pw-proxy", proxy,
NULL);
}
WpProxyPort *
wp_proxy_port_new_finish(GObject *initable, GAsyncResult *res, GError **error)
{
GAsyncInitable *ai = G_ASYNC_INITABLE(initable);
return WP_PROXY_PORT(g_async_initable_new_finish(ai, res, error));
}
const struct pw_port_info *
wp_proxy_port_get_info (WpProxyPort * self)
{
return self->info;
} }
const struct spa_audio_info_raw * const struct spa_audio_info_raw *

View File

@@ -9,20 +9,24 @@
#ifndef __WIREPLUMBER_PROXY_PORT_H__ #ifndef __WIREPLUMBER_PROXY_PORT_H__
#define __WIREPLUMBER_PROXY_PORT_H__ #define __WIREPLUMBER_PROXY_PORT_H__
#include "core.h"
#include "proxy.h" #include "proxy.h"
G_BEGIN_DECLS G_BEGIN_DECLS
typedef enum { /*< flags >*/
WP_PROXY_PORT_FEATURE_FORMAT = (WP_PROXY_FEATURE_LAST << 0),
} WpProxyPortFeatures;
#define WP_TYPE_PROXY_PORT (wp_proxy_port_get_type ()) #define WP_TYPE_PROXY_PORT (wp_proxy_port_get_type ())
G_DECLARE_FINAL_TYPE (WpProxyPort, wp_proxy_port, WP, PROXY_PORT, WpProxy) G_DECLARE_FINAL_TYPE (WpProxyPort, wp_proxy_port, WP, PROXY_PORT, WpProxy)
void wp_proxy_port_new (guint global_id, gpointer proxy, static inline const struct pw_port_info *
GAsyncReadyCallback callback, gpointer user_data); wp_proxy_port_get_info (WpProxyPort * self)
WpProxyPort *wp_proxy_port_new_finish(GObject *initable, GAsyncResult *res, {
GError **error); return (const struct pw_port_info *)
wp_proxy_get_native_info (WP_PROXY (self));
}
const struct pw_port_info *wp_proxy_port_get_info (WpProxyPort * self);
const struct spa_audio_info_raw *wp_proxy_port_get_format (WpProxyPort * self); const struct spa_audio_info_raw *wp_proxy_port_get_format (WpProxyPort * self);
G_END_DECLS G_END_DECLS

View File

@@ -2,46 +2,130 @@
* *
* Copyright © 2019 Collabora Ltd. * Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com> * @author Julian Bouzas <julian.bouzas@collabora.com>
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <pipewire/pipewire.h>
#include "proxy.h" #include "proxy.h"
#include "error.h"
#include "remote-pipewire.h"
#include "wpenums.h"
#include "proxy-link.h"
#include "proxy-node.h"
#include "proxy-port.h"
#include <pipewire/pipewire.h>
#include <spa/debug/types.h>
typedef struct _WpProxyPrivate WpProxyPrivate; typedef struct _WpProxyPrivate WpProxyPrivate;
struct _WpProxyPrivate struct _WpProxyPrivate
{ {
/* The global id */ /* properties */
guint global_id; GWeakRef remote;
/* The proxy */ guint32 global_id;
struct pw_proxy *proxy; guint32 global_perm;
WpProperties *global_props;
guint32 iface_type;
guint32 iface_version;
struct pw_proxy *pw_proxy;
gpointer native_info;
GDestroyNotify native_info_destroy;
/* The proxy listener */ /* The proxy listener */
struct spa_hook listener; struct spa_hook listener;
/* augment state */
WpProxyFeatures ft_ready;
WpProxyFeatures ft_wanted;
GTask *task;
}; };
enum { enum {
PROP_0, PROP_0,
PROP_REMOTE,
PROP_GLOBAL_ID, PROP_GLOBAL_ID,
PROP_PROXY, PROP_GLOBAL_PERMISSIONS,
PROP_GLOBAL_PROPERTIES,
PROP_INTERFACE_TYPE,
PROP_INTERFACE_NAME,
PROP_INTERFACE_QUARK,
PROP_INTERFACE_VERSION,
PROP_PW_PROXY,
PROP_NATIVE_INFO,
PROP_FEATURES,
}; };
enum enum
{ {
SIGNAL_DONE, SIGNAL_PW_PROXY_CREATED,
SIGNAL_PW_PROXY_DESTROYED,
LAST_SIGNAL, LAST_SIGNAL,
}; };
static guint wp_proxy_signals[LAST_SIGNAL] = { 0 }; static guint wp_proxy_signals[LAST_SIGNAL] = { 0 };
static void wp_proxy_async_initable_init (gpointer iface, gpointer iface_data); G_DEFINE_TYPE_WITH_PRIVATE (WpProxy, wp_proxy, G_TYPE_OBJECT)
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpProxy, wp_proxy, G_TYPE_OBJECT, G_DEFINE_QUARK (core, wp_proxy_core)
G_ADD_PRIVATE (WpProxy) G_DEFINE_QUARK (registry, wp_proxy_registry)
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, wp_proxy_async_initable_init)) G_DEFINE_QUARK (node, wp_proxy_node)
G_DEFINE_QUARK (port, wp_proxy_port)
G_DEFINE_QUARK (factory, wp_proxy_factory)
G_DEFINE_QUARK (link, wp_proxy_link)
G_DEFINE_QUARK (client, wp_proxy_client)
G_DEFINE_QUARK (module, wp_proxy_module)
G_DEFINE_QUARK (device, wp_proxy_device)
G_DEFINE_QUARK (client-node, wp_proxy_client_node)
static struct {
/* the pipewire interface type */
guint32 pw_type;
/* the minimum interface version that the remote object must support */
guint32 req_version;
/* the _get_type() function of the subclass */
GType (*get_type) (void);
/* a function returning a quark that identifies the interface */
GQuark (*get_quark) (void);
} types_assoc[] = {
{ PW_TYPE_INTERFACE_Core, 0, wp_proxy_get_type, wp_proxy_core_quark },
{ PW_TYPE_INTERFACE_Registry, 0, wp_proxy_get_type, wp_proxy_registry_quark },
{ PW_TYPE_INTERFACE_Node, 0, wp_proxy_node_get_type, wp_proxy_node_quark },
{ PW_TYPE_INTERFACE_Port, 0, wp_proxy_port_get_type, wp_proxy_port_quark },
{ PW_TYPE_INTERFACE_Factory, 0, wp_proxy_get_type, wp_proxy_factory_quark },
{ PW_TYPE_INTERFACE_Link, 0, wp_proxy_link_get_type, wp_proxy_link_quark },
{ PW_TYPE_INTERFACE_Client, 0, wp_proxy_get_type, wp_proxy_client_quark },
{ PW_TYPE_INTERFACE_Module, 0, wp_proxy_get_type, wp_proxy_module_quark },
{ PW_TYPE_INTERFACE_Device, 0, wp_proxy_get_type, wp_proxy_device_quark },
{ PW_TYPE_INTERFACE_ClientNode, 0, wp_proxy_get_type, wp_proxy_client_node_quark },
};
static inline GType
wp_proxy_find_instance_type (guint32 type, guint32 version)
{
for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) {
if (types_assoc[i].pw_type == type &&
types_assoc[i].req_version <= version)
return types_assoc[i].get_type ();
}
return WP_TYPE_PROXY;
}
static inline GQuark
wp_proxy_find_quark_for_type (guint32 type)
{
for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) {
if (types_assoc[i].pw_type == type)
return types_assoc[i].get_quark ();
}
return 0;
}
static void static void
proxy_event_destroy (void *data) proxy_event_destroy (void *data)
@@ -49,34 +133,46 @@ proxy_event_destroy (void *data)
WpProxy *self = WP_PROXY (data); WpProxy *self = WP_PROXY (data);
WpProxyPrivate *priv = wp_proxy_get_instance_private (self); WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
/* Set the proxy to NULL */ priv->pw_proxy = NULL;
priv->proxy = NULL;
/* Call the destroy method */ g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED], 0);
if (WP_PROXY_GET_CLASS (self)->destroy)
WP_PROXY_GET_CLASS (self)->destroy (self); /* Return error if the pw_proxy destruction happened while the async
* init or augment of this proxy object was in progress */
if (priv->task) {
g_task_return_new_error (priv->task, WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_OPERATION_FAILED,
"pipewire node proxy destroyed before finishing");
g_clear_object (&priv->task);
} }
static void
proxy_event_done (void *data, int seq)
{
/* Emit the done signal */
g_signal_emit (data, wp_proxy_signals[SIGNAL_DONE], 0);
} }
static const struct pw_proxy_events proxy_events = { static const struct pw_proxy_events proxy_events = {
PW_VERSION_PROXY_EVENTS, PW_VERSION_PROXY_EVENTS,
.destroy = proxy_event_destroy, .destroy = proxy_event_destroy,
.done = proxy_event_done,
}; };
static void
wp_proxy_init (WpProxy * self)
{
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
g_weak_ref_init (&priv->remote, NULL);
}
static void static void
wp_proxy_constructed (GObject * object) wp_proxy_constructed (GObject * object)
{ {
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); WpProxy *self = WP_PROXY (object);
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
/* Add the event listener */ /* native proxy was passed in the constructor, declare it as ready */
pw_proxy_add_listener (priv->proxy, &priv->listener, &proxy_events, object); if (priv->pw_proxy) {
priv->ft_ready |= WP_PROXY_FEATURE_PW_PROXY;
/* and inform the subclasses */
g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_CREATED], 0,
priv->pw_proxy);
}
} }
static void static void
@@ -84,14 +180,14 @@ wp_proxy_finalize (GObject * object)
{ {
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
g_debug ("%s:%p destroyed (pw proxy %p)", G_OBJECT_TYPE_NAME (object), g_debug ("%s:%p destroyed (global %u; pw_proxy %p)", G_OBJECT_TYPE_NAME (object),
object, priv->proxy); object, priv->global_id, priv->pw_proxy);
/* Destroy the proxy */ g_clear_object (&priv->task);
if (priv->proxy) { g_clear_pointer (&priv->global_props, wp_properties_unref);
pw_proxy_destroy (priv->proxy); g_clear_pointer (&priv->native_info, priv->native_info_destroy);
priv->proxy = NULL; g_clear_pointer (&priv->pw_proxy, pw_proxy_destroy);
} g_weak_ref_clear (&priv->remote);
G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object); G_OBJECT_CLASS (wp_proxy_parent_class)->finalize (object);
} }
@@ -103,11 +199,29 @@ wp_proxy_set_property (GObject * object, guint property_id,
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
switch (property_id) { switch (property_id) {
case PROP_REMOTE:
g_weak_ref_set (&priv->remote, g_value_get_object (value));
break;
case PROP_GLOBAL_ID: case PROP_GLOBAL_ID:
priv->global_id = g_value_get_uint (value); priv->global_id = g_value_get_uint (value);
break; break;
case PROP_PROXY: case PROP_GLOBAL_PERMISSIONS:
priv->proxy = g_value_get_pointer (value); priv->global_perm = g_value_get_uint (value);
break;
case PROP_GLOBAL_PROPERTIES:
priv->global_props = g_value_dup_boxed (value);
break;
case PROP_INTERFACE_TYPE:
priv->iface_type = g_value_get_uint (value);
break;
case PROP_INTERFACE_VERSION:
priv->iface_version = g_value_get_uint (value);
break;
case PROP_PW_PROXY:
priv->pw_proxy = g_value_get_pointer (value);
break;
case PROP_NATIVE_INFO:
priv->native_info = g_value_get_pointer (value);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -122,11 +236,39 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value,
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object)); WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
switch (property_id) { switch (property_id) {
case PROP_REMOTE:
g_value_take_object (value, g_weak_ref_get (&priv->remote));
break;
case PROP_GLOBAL_ID: case PROP_GLOBAL_ID:
g_value_set_uint (value, priv->global_id); g_value_set_uint (value, priv->global_id);
break; break;
case PROP_PROXY: case PROP_GLOBAL_PERMISSIONS:
g_value_set_pointer (value, priv->proxy); g_value_set_uint (value, priv->global_perm);
break;
case PROP_GLOBAL_PROPERTIES:
g_value_set_boxed (value, priv->global_props);
break;
case PROP_INTERFACE_TYPE:
g_value_set_uint (value, priv->iface_type);
break;
case PROP_INTERFACE_NAME:
g_value_set_static_string (value,
spa_debug_type_find_name (pw_type_info(), priv->iface_type));
break;
case PROP_INTERFACE_QUARK:
g_value_set_uint (value, wp_proxy_find_quark_for_type (priv->iface_type));
break;
case PROP_INTERFACE_VERSION:
g_value_set_uint (value, priv->iface_version);
break;
case PROP_PW_PROXY:
g_value_set_pointer (value, priv->pw_proxy);
break;
case PROP_NATIVE_INFO:
g_value_set_pointer (value, priv->native_info);
break;
case PROP_FEATURES:
g_value_set_flags (value, priv->ft_ready);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -134,27 +276,29 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value,
} }
} }
static gboolean static void
wp_proxy_init_finish (GAsyncInitable *initable, GAsyncResult *result, wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features)
GError **error)
{ {
g_return_val_if_fail (g_task_is_valid (result, initable), FALSE); WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
return g_task_propagate_boolean (G_TASK (result), error); /* ensure we have a pw_proxy, as we can't have
* any other feature without first having that */
if (!priv->pw_proxy && features != 0)
features |= WP_PROXY_FEATURE_PW_PROXY;
/* if we don't have a pw_proxy, we have to assume that this WpProxy
* represents a global object from the registry; we have no other way
* to get a pw_proxy */
if (features & WP_PROXY_FEATURE_PW_PROXY) {
if (!wp_proxy_is_global (self)) {
wp_proxy_augment_error (self, g_error_new (WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_INVALID_ARGUMENT,
"No global id specified; cannot bind pw_proxy"));
return;
} }
static void wp_proxy_bind_global (self);
wp_proxy_async_initable_init (gpointer iface, gpointer iface_data)
{
GAsyncInitableIface *ai_iface = iface;
/* The init_async must be implemented in the derived classes */
ai_iface->init_finish = wp_proxy_init_finish;
} }
static void
wp_proxy_init (WpProxy * self)
{
} }
static void static void
@@ -167,22 +311,263 @@ wp_proxy_class_init (WpProxyClass * klass)
object_class->get_property = wp_proxy_get_property; object_class->get_property = wp_proxy_get_property;
object_class->set_property = wp_proxy_set_property; object_class->set_property = wp_proxy_set_property;
klass->augment = wp_proxy_default_augment;
/* Install the properties */ /* Install the properties */
g_object_class_install_property (object_class, PROP_REMOTE,
g_param_spec_object ("remote", "remote",
"The pipewire connection WpRemote", WP_TYPE_REMOTE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_GLOBAL_ID, g_object_class_install_property (object_class, PROP_GLOBAL_ID,
g_param_spec_uint ("global-id", "global-id", "The pipewire global id", g_param_spec_uint ("global-id", "global-id",
0, G_MAXUINT, 0, "The pipewire global id", 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_GLOBAL_PERMISSIONS,
g_param_spec_uint ("global-permissions", "global-permissions",
"The pipewire global permissions", 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_GLOBAL_PROPERTIES,
g_param_spec_boxed ("global-properties", "global-properties",
"The pipewire global properties", WP_TYPE_PROPERTIES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_INTERFACE_TYPE,
g_param_spec_uint ("interface-type", "interface-type",
"The pipewire interface type", 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PROXY,
g_param_spec_pointer ("pw-proxy", "pw-proxy", "The pipewire proxy", g_object_class_install_property (object_class, PROP_INTERFACE_NAME,
g_param_spec_string ("interface-name", "interface-name",
"The name of the pipewire interface", NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_INTERFACE_QUARK,
g_param_spec_uint ("interface-quark", "interface-quark",
"A quark identifying the pipewire interface", 0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_INTERFACE_VERSION,
g_param_spec_uint ("interface-version", "interface-version",
"The pipewire interface version", 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PW_PROXY,
g_param_spec_pointer ("pw-proxy", "pw-proxy", "The struct pw_proxy *",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_NATIVE_INFO,
g_param_spec_pointer ("native-info", "native-info",
"The struct pw_XXXX_info *",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FEATURES,
g_param_spec_flags ("features", "features",
"The ready WpProxyFeatures on this proxy", WP_TYPE_PROXY_FEATURES, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/* Signals */ /* Signals */
wp_proxy_signals[SIGNAL_DONE] = wp_proxy_signals[SIGNAL_PW_PROXY_CREATED] = g_signal_new (
g_signal_new ("done", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, "pw-proxy-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (WpProxyClass, done), NULL, NULL, NULL, G_TYPE_NONE, 0); G_STRUCT_OFFSET (WpProxyClass, pw_proxy_created), NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_POINTER);
wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED] = g_signal_new (
"pw-proxy-destroyed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (WpProxyClass, pw_proxy_destroyed), NULL, NULL, NULL,
G_TYPE_NONE, 0);
} }
guint WpProxy *
wp_proxy_new_global (WpRemote * remote,
guint32 id, guint32 permissions, WpProperties * properties,
guint32 type, guint32 version)
{
GType gtype = wp_proxy_find_instance_type (type, version);
return g_object_new (gtype,
"remote", remote,
"global-id", id,
"global-permissions", permissions,
"global-properties", properties,
"interface-type", type,
"interface-version", version,
NULL);
}
WpProxy *
wp_proxy_new_wrap (WpRemote * remote,
struct pw_proxy * proxy, guint32 type, guint32 version)
{
GType gtype = wp_proxy_find_instance_type (type, version);
return g_object_new (gtype,
"remote", remote,
"pw-proxy", proxy,
"interface-type", type,
"interface-version", version,
NULL);
}
void
wp_proxy_augment (WpProxy * self,
WpProxyFeatures ft_wanted, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer user_data)
{
WpProxyPrivate *priv;
WpProxyFeatures missing = 0;
g_return_if_fail (WP_IS_PROXY (self));
g_return_if_fail (WP_PROXY_GET_CLASS (self)->augment);
priv = wp_proxy_get_instance_private (self);
priv->task = g_task_new (self, cancellable, callback, user_data);
/* we don't simply assign here, we keep all the previous wanted features;
* it is not allowed to remove features */
priv->ft_wanted |= ft_wanted;
/* find which features are wanted but missing from the "ready" set */
missing = (priv->ft_ready ^ priv->ft_wanted) & priv->ft_wanted;
/* if the features are not ready, call augment(),
* otherwise signal the callback directly */
if (missing != 0) {
WP_PROXY_GET_CLASS (self)->augment (self, missing);
} else {
g_task_return_boolean (priv->task, TRUE);
g_clear_object (&priv->task);
}
}
gboolean
wp_proxy_augment_finish (WpProxy * self, GAsyncResult * res,
GError ** error)
{
g_return_val_if_fail (WP_IS_PROXY (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
void
wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature)
{
WpProxyPrivate *priv;
g_return_if_fail (WP_IS_PROXY (self));
priv = wp_proxy_get_instance_private (self);
/* feature already marked as ready */
if (priv->ft_ready & feature)
return;
priv->ft_ready |= feature;
g_object_notify (G_OBJECT (self), "features");
/* return from the task if all the wanted features are now ready */
if (priv->task &&
(priv->ft_ready & priv->ft_wanted) == priv->ft_wanted) {
g_task_return_boolean (priv->task, TRUE);
g_clear_object (&priv->task);
}
}
/**
* wp_proxy_augment_error:
* @self: the proxy
* @error: (transfer full): the error
*
* Reports an error that occured during the augment process
*/
void
wp_proxy_augment_error (WpProxy * self, GError * error)
{
WpProxyPrivate *priv;
g_return_if_fail (WP_IS_PROXY (self));
priv = wp_proxy_get_instance_private (self);
if (priv->task)
g_task_return_error (priv->task, error);
else
g_error_free (error);
g_clear_object (&priv->task);
}
gboolean
wp_proxy_bind_global (WpProxy * self)
{
WpProxyPrivate *priv;
g_autoptr (WpRemote) remote = NULL;
g_return_val_if_fail (wp_proxy_is_global (self), FALSE);
priv = wp_proxy_get_instance_private (self);
if (priv->pw_proxy)
return FALSE;
remote = g_weak_ref_get (&priv->remote);
g_return_val_if_fail (remote != NULL, FALSE);
/* bind */
priv->pw_proxy = wp_remote_pipewire_proxy_bind (
WP_REMOTE_PIPEWIRE (remote),
priv->global_id, priv->iface_type);
pw_proxy_add_listener (priv->pw_proxy, &priv->listener, &proxy_events,
self);
/* inform subclasses and listeners */
g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_CREATED], 0,
priv->pw_proxy);
/* declare the feature as ready */
wp_proxy_set_feature_ready (self, WP_PROXY_FEATURE_PW_PROXY);
return TRUE;
}
WpProxyFeatures
wp_proxy_get_features (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), 0);
priv = wp_proxy_get_instance_private (self);
return priv->ft_ready;
}
/**
* wp_proxy_get_remote:
* @self: the proxy
*
* Returns: (transfer full): the remote that created this proxy
*/
WpRemote *
wp_proxy_get_remote (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), NULL);
priv = wp_proxy_get_instance_private (self);
return g_weak_ref_get (&priv->remote);
}
gboolean
wp_proxy_is_global (WpProxy * self)
{
return wp_proxy_get_global_id (self) != 0;
}
guint32
wp_proxy_get_global_id (WpProxy * self) wp_proxy_get_global_id (WpProxy * self)
{ {
WpProxyPrivate *priv; WpProxyPrivate *priv;
@@ -193,7 +578,78 @@ wp_proxy_get_global_id (WpProxy * self)
return priv->global_id; return priv->global_id;
} }
gpointer guint32
wp_proxy_get_global_permissions (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), 0);
priv = wp_proxy_get_instance_private (self);
return priv->global_perm;
}
/**
* wp_proxy_get_global_properties:
*
* Returns: (transfer full): the global properties of the proxy
*/
WpProperties *
wp_proxy_get_global_properties (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), NULL);
priv = wp_proxy_get_instance_private (self);
return priv->global_props ? wp_properties_ref (priv->global_props) : NULL;
}
guint32
wp_proxy_get_interface_type (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), 0);
priv = wp_proxy_get_instance_private (self);
return priv->iface_type;
}
const gchar *
wp_proxy_get_interface_name (WpProxy * self)
{
const gchar *name = NULL;
g_return_val_if_fail (WP_IS_PROXY (self), NULL);
g_object_get (self, "interface-name", &name, NULL);
return name;
}
GQuark
wp_proxy_get_interface_quark (WpProxy * self)
{
GQuark q = 0;
g_return_val_if_fail (WP_IS_PROXY (self), 0);
g_object_get (self, "interface-quark", &q, NULL);
return q;
}
guint32
wp_proxy_get_interface_version (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), 0);
priv = wp_proxy_get_instance_private (self);
return priv->iface_version;
}
struct pw_proxy *
wp_proxy_get_pw_proxy (WpProxy * self) wp_proxy_get_pw_proxy (WpProxy * self)
{ {
WpProxyPrivate *priv; WpProxyPrivate *priv;
@@ -201,17 +657,39 @@ wp_proxy_get_pw_proxy (WpProxy * self)
g_return_val_if_fail (WP_IS_PROXY (self), NULL); g_return_val_if_fail (WP_IS_PROXY (self), NULL);
priv = wp_proxy_get_instance_private (self); priv = wp_proxy_get_instance_private (self);
return priv->proxy; return priv->pw_proxy;
} }
void wp_proxy_sync (WpProxy * self) gconstpointer
wp_proxy_get_native_info (WpProxy * self)
{
WpProxyPrivate *priv;
g_return_val_if_fail (WP_IS_PROXY (self), NULL);
priv = wp_proxy_get_instance_private (self);
return priv->native_info;
}
void
wp_proxy_update_native_info (WpProxy * self, gconstpointer info,
WpProxyNativeInfoUpdate update, GDestroyNotify destroy)
{ {
WpProxyPrivate *priv; WpProxyPrivate *priv;
g_return_if_fail (WP_IS_PROXY (self)); g_return_if_fail (WP_IS_PROXY (self));
g_return_if_fail (destroy != NULL);
priv = wp_proxy_get_instance_private (self); priv = wp_proxy_get_instance_private (self);
/* Trigger the done callback */ if (update) {
pw_proxy_sync(priv->proxy, 0); priv->native_info = update (priv->native_info, info);
} else {
g_clear_pointer (&priv->native_info, priv->native_info_destroy);
priv->native_info = (gpointer) info;
}
priv->native_info_destroy = destroy;
g_object_notify (G_OBJECT (self), "native-info");
} }

View File

@@ -2,6 +2,7 @@
* *
* Copyright © 2019 Collabora Ltd. * Copyright © 2019 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com> * @author Julian Bouzas <julian.bouzas@collabora.com>
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -11,10 +12,20 @@
#include <gio/gio.h> #include <gio/gio.h>
#include "core.h" #include "remote.h"
#include "properties.h"
G_BEGIN_DECLS G_BEGIN_DECLS
struct pw_proxy;
typedef enum { /*< flags >*/
WP_PROXY_FEATURE_PW_PROXY = (1 << 0),
WP_PROXY_FEATURE_INFO = (1 << 1),
WP_PROXY_FEATURE_LAST = (1 << 5), /*< skip >*/
} WpProxyFeatures;
#define WP_TYPE_PROXY (wp_proxy_get_type ()) #define WP_TYPE_PROXY (wp_proxy_get_type ())
G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject) G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject)
@@ -23,16 +34,53 @@ struct _WpProxyClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
/* Methods */ void (*augment) (WpProxy *self, WpProxyFeatures features);
void (*destroy) (WpProxy * self);
/* Signals */ void (*pw_proxy_created) (WpProxy * self, struct pw_proxy * proxy);
void (*done)(WpProxy *wp_proxy, gpointer data); void (*pw_proxy_destroyed) (WpProxy * self);
}; };
guint wp_proxy_get_global_id (WpProxy * self); WpProxy * wp_proxy_new_global (WpRemote * remote,
gpointer wp_proxy_get_pw_proxy (WpProxy * self); guint32 id, guint32 permissions, WpProperties * properties,
void wp_proxy_sync (WpProxy * self); guint32 type, guint32 version);
WpProxy * wp_proxy_new_wrap (WpRemote * remote,
struct pw_proxy * proxy, guint32 type, guint32 version);
void wp_proxy_augment (WpProxy *self,
WpProxyFeatures wanted_features, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer user_data);
gboolean wp_proxy_augment_finish (WpProxy * self, GAsyncResult * res,
GError ** error);
WpProxyFeatures wp_proxy_get_features (WpProxy * self);
WpRemote * wp_proxy_get_remote (WpProxy * self);
gboolean wp_proxy_is_global (WpProxy * self);
guint32 wp_proxy_get_global_id (WpProxy * self);
guint32 wp_proxy_get_global_permissions (WpProxy * self);
WpProperties * wp_proxy_get_global_properties (WpProxy * self);
guint32 wp_proxy_get_interface_type (WpProxy * self);
const gchar * wp_proxy_get_interface_name (WpProxy * self);
GQuark wp_proxy_get_interface_quark (WpProxy * self);
guint32 wp_proxy_get_interface_version (WpProxy * self);
struct pw_proxy * wp_proxy_get_pw_proxy (WpProxy * self);
gconstpointer wp_proxy_get_native_info (WpProxy * self);
/* for subclasses only */
typedef gpointer (*WpProxyNativeInfoUpdate) (gpointer old_info,
gconstpointer new_info);
void wp_proxy_update_native_info (WpProxy * self, gconstpointer info,
WpProxyNativeInfoUpdate update, GDestroyNotify destroy);
void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature);
void wp_proxy_augment_error (WpProxy * self, GError * error);
gboolean wp_proxy_bind_global (WpProxy * self);
G_END_DECLS G_END_DECLS

View File

@@ -7,6 +7,7 @@
*/ */
#include "remote-pipewire.h" #include "remote-pipewire.h"
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
/* /*
@@ -15,15 +16,6 @@
#define WP_LOOP_SOURCE(x) ((WpLoopSource *) x) #define WP_LOOP_SOURCE(x) ((WpLoopSource *) x)
G_DEFINE_QUARK (node, signal_detail_node)
G_DEFINE_QUARK (port, signal_detail_port)
G_DEFINE_QUARK (factory, signal_detail_factory)
G_DEFINE_QUARK (link, signal_detail_link)
G_DEFINE_QUARK (client, signal_detail_client)
G_DEFINE_QUARK (module, signal_detail_module)
G_DEFINE_QUARK (device, signal_detail_device)
G_DEFINE_QUARK (endpoint, signal_detail_endpoint)
typedef struct _WpLoopSource WpLoopSource; typedef struct _WpLoopSource WpLoopSource;
struct _WpLoopSource struct _WpLoopSource
{ {
@@ -90,6 +82,10 @@ struct _WpRemotePipewire
struct spa_hook registry_listener; struct spa_hook registry_listener;
GMainContext *context; GMainContext *context;
GHashTable *proxies;
GHashTable *default_features;
GQueue created_obj_proxies;
}; };
enum { enum {
@@ -113,45 +109,53 @@ static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (WpRemotePipewire, wp_remote_pipewire, WP_TYPE_REMOTE) G_DEFINE_TYPE (WpRemotePipewire, wp_remote_pipewire, WP_TYPE_REMOTE)
static void static void
registry_global(void *data, uint32_t id, on_proxy_ready (GObject * obj, GAsyncResult * res, gpointer data)
uint32_t permissions, uint32_t type, uint32_t version,
const struct spa_dict *props)
{ {
GQuark detail = 0; WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data);
WpProxy *proxy = WP_PROXY (obj);
g_autoptr (GError) error = NULL;
switch (type) { if (!wp_proxy_augment_finish (proxy, res, &error)) {
case PW_TYPE_INTERFACE_Node: g_warning ("Failed to augment WpProxy (%p): %s", obj, error->message);
detail = signal_detail_node_quark ();
break;
case PW_TYPE_INTERFACE_Port:
detail = signal_detail_port_quark ();
break;
case PW_TYPE_INTERFACE_Factory:
detail = signal_detail_factory_quark ();
break;
case PW_TYPE_INTERFACE_Link:
detail = signal_detail_link_quark ();
break;
case PW_TYPE_INTERFACE_Client:
detail = signal_detail_client_quark ();
break;
case PW_TYPE_INTERFACE_Module:
detail = signal_detail_module_quark ();
break;
case PW_TYPE_INTERFACE_Device:
detail = signal_detail_device_quark ();
break;
default:
break;
} }
g_signal_emit (data, signals[SIGNAL_GLOBAL_ADDED], detail, id, props); g_signal_emit (self, signals[SIGNAL_GLOBAL_ADDED],
wp_proxy_get_interface_quark (proxy), proxy);
}
static void
registry_global(void *data, uint32_t id, uint32_t permissions,
uint32_t type, uint32_t version, const struct spa_dict *props)
{
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data);
WpProxy *proxy;
WpProxyFeatures features;
g_autoptr (WpProperties) properties = wp_properties_new_copy_dict (props);
/* construct & store WpProxy */
proxy = wp_proxy_new_global (WP_REMOTE (self), id, permissions, properties,
type, version);
g_hash_table_insert (self->proxies, GUINT_TO_POINTER (id), proxy);
g_debug ("registry global:%u perm:0x%x type:%u/%u -> WpProxy:%p",
id, permissions, type, version, proxy);
/* augment */
features = GPOINTER_TO_UINT (g_hash_table_lookup (self->default_features,
GUINT_TO_POINTER (G_TYPE_FROM_INSTANCE (proxy))));
wp_proxy_augment (proxy, features, NULL, on_proxy_ready, self);
} }
static void static void
registry_global_remove (void *data, uint32_t id) registry_global_remove (void *data, uint32_t id)
{ {
g_signal_emit (data, signals[SIGNAL_GLOBAL_REMOVED], 0, id); WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (data);
g_autoptr (WpProxy) proxy = NULL;
if (g_hash_table_steal_extended (self->proxies, GUINT_TO_POINTER (id), NULL,
(gpointer *) &proxy))
g_signal_emit (data, signals[SIGNAL_GLOBAL_REMOVED],
wp_proxy_get_interface_quark (proxy), proxy);
} }
static const struct pw_registry_proxy_events registry_proxy_events = { static const struct pw_registry_proxy_events registry_proxy_events = {
@@ -198,6 +202,9 @@ static const struct pw_remote_events remote_events = {
static void static void
wp_remote_pipewire_init (WpRemotePipewire *self) wp_remote_pipewire_init (WpRemotePipewire *self)
{ {
self->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
g_object_unref);
self->default_features = g_hash_table_new (g_direct_hash, g_direct_equal);
} }
static void static void
@@ -223,6 +230,8 @@ wp_remote_pipewire_finalize (GObject *object)
{ {
WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object); WpRemotePipewire *self = WP_REMOTE_PIPEWIRE (object);
g_clear_pointer (&self->proxies, g_hash_table_unref);
g_clear_pointer (&self->default_features, g_hash_table_unref);
pw_remote_destroy (self->remote); pw_remote_destroy (self->remote);
pw_core_destroy (self->core); pw_core_destroy (self->core);
g_clear_pointer (&self->context, g_main_context_unref); g_clear_pointer (&self->context, g_main_context_unref);
@@ -336,10 +345,10 @@ wp_remote_pipewire_class_init (WpRemotePipewireClass *klass)
/* Signals */ /* Signals */
signals[SIGNAL_GLOBAL_ADDED] = g_signal_new ("global-added", signals[SIGNAL_GLOBAL_ADDED] = g_signal_new ("global-added",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST, G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); 0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY);
signals[SIGNAL_GLOBAL_REMOVED] = g_signal_new ("global-removed", signals[SIGNAL_GLOBAL_REMOVED] = g_signal_new ("global-removed",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); 0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY);
} }
WpRemote * WpRemote *
@@ -359,6 +368,33 @@ wp_remote_pipewire_new (WpCore *core, GMainContext *context)
return remote; return remote;
} }
void
wp_remote_pipewire_set_default_features (WpRemotePipewire * self,
GType proxy_type, WpProxyFeatures features)
{
g_return_if_fail (WP_IS_REMOTE_PIPEWIRE (self));
g_hash_table_insert (self->default_features, GUINT_TO_POINTER (proxy_type),
GUINT_TO_POINTER (features));
}
WpProxy *
wp_remote_pipewire_create_object (WpRemotePipewire *self,
const gchar *factory_name, guint32 interface_type,
guint32 interface_version, WpProperties * properties)
{
struct pw_proxy *pw_proxy;
g_return_val_if_fail (WP_IS_REMOTE_PIPEWIRE (self), NULL);
g_return_val_if_fail (self->core_proxy, NULL);
pw_proxy = pw_core_proxy_create_object (self->core_proxy, factory_name,
interface_type, interface_version, wp_properties_peek_dict (properties),
0);
return wp_proxy_new_wrap (WP_REMOTE (self), pw_proxy, interface_type,
interface_version);
}
gpointer gpointer
wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id, wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id,
guint global_type) guint global_type)
@@ -380,17 +416,6 @@ wp_remote_pipewire_find_factory (WpRemotePipewire *self,
return pw_core_find_factory(self->core, factory_name); return pw_core_find_factory(self->core, factory_name);
} }
gpointer
wp_remote_pipewire_create_object (WpRemotePipewire *self,
const char *factory_name, guint global_type, gconstpointer props)
{
g_return_val_if_fail (WP_IS_REMOTE_PIPEWIRE(self), NULL);
g_return_val_if_fail (self->core_proxy, NULL);
return pw_core_proxy_create_object (self->core_proxy, factory_name,
global_type, 0, props, 0);
}
void void
wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self, wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self,
const char *factory_regexp, const char *lib) const char *factory_regexp, const char *lib)

View File

@@ -10,6 +10,7 @@
#define __WIREPLUMBER_REMOTE_PIPEWIRE_H__ #define __WIREPLUMBER_REMOTE_PIPEWIRE_H__
#include "remote.h" #include "remote.h"
#include "proxy.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@@ -19,12 +20,17 @@ G_DECLARE_FINAL_TYPE (WpRemotePipewire, wp_remote_pipewire,
WpRemote *wp_remote_pipewire_new (WpCore *core, GMainContext *context); WpRemote *wp_remote_pipewire_new (WpCore *core, GMainContext *context);
void wp_remote_pipewire_set_default_features (
WpRemotePipewire * self, GType proxy_type, WpProxyFeatures features);
WpProxy * wp_remote_pipewire_create_object (WpRemotePipewire *self,
const gchar *factory_name, guint32 interface_type,
guint32 interface_version, WpProperties * properties);
gpointer wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id, gpointer wp_remote_pipewire_proxy_bind (WpRemotePipewire *self, guint global_id,
guint global_type); guint global_type);
gpointer wp_remote_pipewire_find_factory (WpRemotePipewire *self, gpointer wp_remote_pipewire_find_factory (WpRemotePipewire *self,
const char *factory_name); const char *factory_name);
gpointer wp_remote_pipewire_create_object (WpRemotePipewire *self,
const char *factory_name, guint global_type, gconstpointer props);
void wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self, void wp_remote_pipewire_add_spa_lib (WpRemotePipewire *self,
const char *factory_regexp, const char *lib); const char *factory_regexp, const char *lib);
gpointer wp_remote_pipewire_load_spa_handle(WpRemotePipewire *self, gpointer wp_remote_pipewire_load_spa_handle(WpRemotePipewire *self,