Skip to content
Snippets Groups Projects
Commit 753e7085 authored by George Kiagiadakis's avatar George Kiagiadakis
Browse files

object-manager: refactor to be able to track locally created proxies

There are 3 kinds of WpProxy objects:
 * the ones that are created as a result of binding a global
   from the registry
 * the ones that are created as a result of calling into a remote
   factory (wp_node_new_from_factory, etc...)
 * the ones that are a local implementation of an object
   (WpImplNode, etc...) and are exported

Previously the object manager was only able to track the first kind.
With these changes we can now also have globals associated with
WpProxies that were created earlier (and caused the creation of the global).
This saves some resources and reduces round-trips (in case client
code wants to change properties of an object that is locally
implemented, it shouldn't need to do a round-trip through the server)
parent a92e1a64
No related branches found
No related tags found
No related merge requests found
......@@ -11,7 +11,6 @@
#include "private.h"
#include <pipewire/pipewire.h>
#include <pipewire/impl.h>
#include <spa/utils/result.h>
#include <spa/debug/types.h>
......@@ -74,32 +73,6 @@ wp_loop_source_new (void)
* WpCore
*/
struct _WpCore
{
GObject parent;
/* main loop integration */
GMainContext *context;
/* extra properties */
WpProperties *properties;
/* pipewire main objects */
struct pw_context *pw_context;
struct pw_core *pw_core;
struct pw_registry *pw_registry;
/* pipewire main listeners */
struct spa_hook core_listener;
struct spa_hook proxy_core_listener;
struct spa_hook registry_listener;
GPtrArray *globals; // elementy-type: WpGlobal*
GPtrArray *objects; // element-type: GObject*
GPtrArray *object_managers; // element-type: WpObjectManager*
GHashTable *async_tasks; // <int seq, GTask*>
};
enum {
PROP_0,
PROP_CONTEXT,
......@@ -119,87 +92,6 @@ static guint32 signals[NUM_SIGNALS];
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT)
/* find the subclass of WpProxy that can handle
the given pipewire interface type of the given version */
static inline GType
find_proxy_instance_type (const char * type, guint32 version)
{
g_autofree GType *children;
guint n_children;
children = g_type_children (WP_TYPE_PROXY, &n_children);
for (gint i = 0; i < n_children; i++) {
WpProxyClass *klass = (WpProxyClass *) g_type_class_ref (children[i]);
if (g_strcmp0 (klass->pw_iface_type, type) == 0 &&
klass->pw_iface_version == version) {
g_type_class_unref (klass);
return children[i];
}
g_type_class_unref (klass);
}
return WP_TYPE_PROXY;
}
static void
registry_global (void *data, uint32_t id, uint32_t permissions,
const char *type, uint32_t version, const struct spa_dict *props)
{
WpCore *self = WP_CORE (data);
WpGlobal *global = NULL;
guint i;
g_return_if_fail (self->globals->len <= id ||
g_ptr_array_index (self->globals, id) == NULL);
g_debug ("registry global:%u perm:0x%x type:%s version:%u",
id, permissions, type, version);
/* construct & store the global */
global = wp_global_new ();
global->id = id;
global->type = find_proxy_instance_type (type, version);
global->permissions = permissions;
global->properties = wp_properties_new_copy_dict (props);
if (self->globals->len <= id)
g_ptr_array_set_size (self->globals, id + 1);
g_ptr_array_index (self->globals, id) = global;
/* notify object managers */
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_add_global (om, global);
}
}
static void
registry_global_remove (void *data, uint32_t id)
{
WpCore *self = WP_CORE (data);
g_autoptr (WpGlobal) global = NULL;
guint i;
global = g_steal_pointer (&g_ptr_array_index (self->globals, id));
g_debug ("registry global removed:%u type:%s", id, g_type_name (global->type));
/* notify object managers */
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_rm_global (om, id);
}
}
static const struct pw_registry_events registry_events = {
PW_VERSION_REGISTRY_EVENTS,
.global = registry_global,
.global_remove = registry_global_remove,
};
static void
core_done (void *data, uint32_t id, int seq)
{
......@@ -232,21 +124,10 @@ static const struct pw_proxy_events proxy_core_events = {
.destroy = proxy_core_destroy,
};
/* wrapper around wp_global_unref because
the WpGlobal pointers in self->globals can be NULL */
static inline void
free_global (WpGlobal * g)
{
if (g)
wp_global_unref (g);
}
static void
wp_core_init (WpCore * self)
{
self->globals = g_ptr_array_new_with_free_func ((GDestroyNotify) free_global);
self->objects = g_ptr_array_new_with_free_func (g_object_unref);
self->object_managers = g_ptr_array_new ();
wp_registry_init (&self->registry);
self->async_tasks = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref);
}
......@@ -269,63 +150,12 @@ wp_core_constructed (GObject *object)
G_OBJECT_CLASS (wp_core_parent_class)->constructed (object);
}
static void object_manager_destroyed (gpointer data, GObject * om);
static void
wp_core_dispose (GObject * obj)
{
WpCore *self = WP_CORE (obj);
/* remove pipewire globals */
{
g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->globals);
while (objlist->len > 0) {
guint i;
g_autoptr (WpGlobal) global = g_ptr_array_steal_index_fast (objlist,
objlist->len - 1);
if (!global)
continue;
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_rm_global (om, global->id);
}
}
}
/* remove all the registered objects
this will normally also destroy the object managers, eventually, since
they are normally ref'ed by modules, which are registered objects */
{
g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->objects);
while (objlist->len > 0) {
guint i;
g_autoptr (GObject) object = g_ptr_array_steal_index_fast (objlist,
objlist->len - 1);
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_rm_object (om, object);
}
}
}
/* in case there are any object managers left,
remove the weak ref on them and let them be... */
{
g_autoptr (GPtrArray) object_mgrs;
GObject *om;
object_mgrs = g_steal_pointer (&self->object_managers);
while (object_mgrs->len > 0) {
om = g_ptr_array_steal_index_fast (object_mgrs, object_mgrs->len - 1);
g_object_weak_unref (om, object_manager_destroyed, self);
}
}
wp_registry_clear (&self->registry);
G_OBJECT_CLASS (wp_core_parent_class)->dispose (obj);
}
......@@ -336,7 +166,6 @@ wp_core_finalize (GObject * obj)
WpCore *self = WP_CORE (obj);
wp_core_disconnect (self);
g_clear_pointer (&self->pw_context, pw_context_destroy);
g_clear_pointer (&self->properties, wp_properties_unref);
......@@ -472,13 +301,6 @@ wp_core_get_pw_core (WpCore * self)
return self->pw_core;
}
struct pw_registry *
wp_core_get_pw_registry (WpCore * self)
{
g_return_val_if_fail (WP_IS_CORE (self), NULL);
return self->pw_registry;
}
gboolean
wp_core_connect (WpCore *self)
{
......@@ -490,8 +312,6 @@ wp_core_connect (WpCore *self)
if (self->pw_core)
return TRUE;
g_return_val_if_fail (!self->pw_registry, FALSE);
/* Connect */
p = self->properties ? wp_properties_to_pw_properties (self->properties) : NULL;
self->pw_core = pw_context_connect (self->pw_context, p, 0);
......@@ -504,10 +324,7 @@ wp_core_connect (WpCore *self)
&self->proxy_core_listener, &proxy_core_events, self);
/* Add the registry listener */
self->pw_registry = pw_core_get_registry (self->pw_core,
PW_VERSION_REGISTRY, 0);
pw_registry_add_listener(self->pw_registry, &self->registry_listener,
&registry_events, self);
wp_registry_attach (&self->registry, self->pw_core);
/* Emit the connected signal */
g_signal_emit (self, signals[SIGNAL_CONNECTED], 0);
......@@ -518,11 +335,7 @@ wp_core_connect (WpCore *self)
void
wp_core_disconnect (WpCore *self)
{
if (self->pw_registry) {
pw_proxy_destroy ((struct pw_proxy *)self->pw_registry);
self->pw_registry = NULL;
}
wp_registry_detach (&self->registry);
g_clear_pointer (&self->pw_core, pw_core_disconnect);
/* Emit the disconnected signal */
......@@ -589,139 +402,3 @@ wp_core_sync_finish (WpCore * self, GAsyncResult * res, GError ** error)
return g_task_propagate_boolean (G_TASK (res), error);
}
/**
* wp_core_find_object: (skip)
* @self: the core
* @func: (scope call): a function that takes the object being searched
* as the first argument and @data as the second. it should return TRUE if
* the object is found or FALSE otherwise
* @data: the second argument to @func
*
* Finds a registered object
*
* Returns: (transfer full) (type GObject *) (nullable): the registered object
* or NULL if not found
*/
gpointer
wp_core_find_object (WpCore * self, GEqualFunc func, gconstpointer data)
{
GObject *object;
guint i;
g_return_val_if_fail (WP_IS_CORE (self), NULL);
/* prevent bad things when called from within _dispose() */
if (G_UNLIKELY (!self->objects))
return NULL;
for (i = 0; i < self->objects->len; i++) {
object = g_ptr_array_index (self->objects, i);
if (func (object, data))
return g_object_ref (object);
}
return NULL;
}
/**
* wp_core_register_object: (skip)
* @self: the core
* @obj: (transfer full) (type GObject*): the object to register
*
* Registers @obj with the core, making it appear on #WpObjectManager
* instances as well. The core will also maintain a ref to that object
* until it is removed.
*/
void
wp_core_register_object (WpCore * self, gpointer obj)
{
guint i;
g_return_if_fail (WP_IS_CORE (self));
g_return_if_fail (G_IS_OBJECT (obj));
/* prevent bad things when called from within _dispose() */
if (G_UNLIKELY (!self->objects)) {
g_object_unref (obj);
return;
}
g_ptr_array_add (self->objects, obj);
/* notify object managers */
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_add_object (om, obj);
}
}
/**
* wp_core_remove_object: (skip)
* @self: the core
* @obj: (transfer none) (type GObject*): a pointer to the object to remove
*
* Detaches and unrefs the specified object from this core
*/
void
wp_core_remove_object (WpCore * self, gpointer obj)
{
guint i;
g_return_if_fail (WP_IS_CORE (self));
g_return_if_fail (G_IS_OBJECT (obj));
/* prevent bad things when called from within _dispose() */
if (G_UNLIKELY (!self->objects))
return;
/* notify object managers */
for (i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_rm_object (om, obj);
}
g_ptr_array_remove_fast (self->objects, obj);
}
static void
object_manager_destroyed (gpointer data, GObject * om)
{
WpCore *self = WP_CORE (data);
g_ptr_array_remove_fast (self->object_managers, om);
}
/**
* wp_core_install_object_manager: (method)
* @self: the core
* @om: (transfer none): a #WpObjectManager
*
* Installs the object manager on this core, activating its internal management
* engine. This will immediately emit signals about objects added on @om
* if objects that the @om is interested in were in existence already.
*/
void
wp_core_install_object_manager (WpCore * self, WpObjectManager * om)
{
guint i;
g_return_if_fail (WP_IS_CORE (self));
g_return_if_fail (WP_IS_OBJECT_MANAGER (om));
g_object_weak_ref (G_OBJECT (om), object_manager_destroyed, self);
g_ptr_array_add (self->object_managers, om);
g_object_set (om, "core", self, NULL);
/* add pre-existing objects to the object manager,
in case it's interested in them */
for (i = 0; i < self->globals->len; i++) {
WpGlobal *g = g_ptr_array_index (self->globals, i);
/* check if null because the globals array can have gaps */
if (g)
wp_object_manager_add_global (om, g);
}
for (i = 0; i < self->objects->len; i++) {
GObject *o = g_ptr_array_index (self->objects, i);
wp_object_manager_add_object (om, o);
}
}
......@@ -8,12 +8,462 @@
#include "object-manager.h"
#include "private.h"
#include <pipewire/array.h>
#include <pipewire/pipewire.h>
static void wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global);
static void wp_object_manager_add_object (WpObjectManager * self, gpointer object);
static void wp_object_manager_rm_object (WpObjectManager * self, gpointer object);
/*
* WpRegistry
*
* The registry keeps track of registered objects on the wireplumber core.
* There are 3 kinds of registered objects:
*
* 1) PipeWire global objects, which live in another process.
*
* These objects are represented by a WpGlobal with the
* WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY flag set. They appear when
* the registry_global() event is fired and are removed by
* registry_global_remove(). These objects do not have an associated
* WpProxy, unless there is at least one WpObjectManager that is interested
* in them. In this case, a WpProxy is constructed and it is owned by the
* WpGlobal until the global is removed by the registry_global_remove() event.
*
* 2) PipeWire global objects, which were constructed by this process, either
* by calling into a remove factory (see wp_node_new_from_factory()) or
* by exporting a local object (WpImplNode etc...).
*
* These objects are also represented by a WpGlobal, which is however
* constructed before they appear on the registry. The associated WpProxy
* calls into wp_registry_new_global_for_proxy() at the time it receives
* the 'bound' event and creates a global that has the
* WP_GLOBAL_FLAG_OWNED_BY_PROXY flag enabled. As the flag name suggests,
* these globals are "owned" by the WpProxy and the WpGlobal has no ref
* on the WpProxy itself. This allows destroying the proxy in client code
* by dropping its last reference.
*
* Normally, these global objects also appear on the pipewire registry. When
* this happens, the WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY flag is also added
* and that keeps an additional reference on the global (both flags must
* be dropped before the WpGlobal is destroyed).
*
* 3) WirePlumber global objects (WpModule, WpFactory).
*
* These are local objects that have nothing to do with PipeWire. They do not
* have a global id and they are also not subclasses of WpProxy. The registry
* always owns a reference on them, so that they are kept alive for as long
* as the WpCore is alive.
*/
static void
wp_registry_notify_add_object (WpRegistry *self, gpointer object)
{
for (guint i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_add_object (om, object);
}
}
static void
wp_registry_notify_rm_object (WpRegistry *self, gpointer object)
{
for (guint i = 0; i < self->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
wp_object_manager_rm_object (om, object);
}
}
static void
object_manager_destroyed (gpointer data, GObject * om)
{
WpRegistry *self = data;
g_ptr_array_remove_fast (self->object_managers, om);
}
/* find the subclass of WpProxy that can handle
the given pipewire interface type of the given version */
static inline GType
find_proxy_instance_type (const char * type, guint32 version)
{
g_autofree GType *children;
guint n_children;
children = g_type_children (WP_TYPE_PROXY, &n_children);
for (gint i = 0; i < n_children; i++) {
WpProxyClass *klass = (WpProxyClass *) g_type_class_ref (children[i]);
if (g_strcmp0 (klass->pw_iface_type, type) == 0 &&
klass->pw_iface_version == version) {
g_type_class_unref (klass);
return children[i];
}
g_type_class_unref (klass);
}
return WP_TYPE_PROXY;
}
/* called by the registry when a global appears */
static void
registry_global (void *data, uint32_t id, uint32_t permissions,
const char *type, uint32_t version, const struct spa_dict *props)
{
WpRegistry *self = data;
WpGlobal *global = NULL;
if (id < self->globals->len)
global = g_ptr_array_index (self->globals, id);
g_debug ("registry global:%u perm:0x%x type:%s version:%u proxy:%p(%s)",
id, permissions, type, version, global ? global->proxy : NULL,
(global && global->proxy) ? G_OBJECT_TYPE_NAME (global->proxy) : NULL);
if (!global) {
/* global did not exist; object was just advertised on the registry */
global = wp_global_new (self, id, permissions,
find_proxy_instance_type (type, version),
props ? wp_properties_new_copy_dict (props) : wp_properties_new_empty (),
NULL, WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);
/* we do not need this ref; the globals array also has one */
wp_global_unref (global);
} else {
/* global existed; proxy was created as a result of local action */
global->flags |= WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY;
global->permissions = permissions;
global->type = find_proxy_instance_type (type, version);
if (props)
wp_properties_update_from_dict (global->properties, props);
}
}
/* called by the registry when a global is removed */
static void
registry_global_remove (void *data, uint32_t id)
{
WpRegistry *self = data;
WpGlobal *global = NULL;
global = g_ptr_array_index (self->globals, id);
g_return_if_fail (global &&
global->flags & WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);
g_debug ("registry global removed:%u type:%s", id, g_type_name (global->type));
wp_global_rm_flag (global, WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);
}
static const struct pw_registry_events registry_events = {
PW_VERSION_REGISTRY_EVENTS,
.global = registry_global,
.global_remove = registry_global_remove,
};
void
wp_registry_init (WpRegistry *self)
{
self->globals = g_ptr_array_new ();
self->objects = g_ptr_array_new_with_free_func (g_object_unref);
self->object_managers = g_ptr_array_new ();
}
void
wp_registry_clear (WpRegistry *self)
{
wp_registry_detach (self);
g_clear_pointer (&self->globals, g_ptr_array_unref);
/* remove all the registered objects
this will normally also destroy the object managers, eventually, since
they are normally ref'ed by modules, which are registered objects */
{
g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->objects);
while (objlist->len > 0) {
g_autoptr (GObject) object = g_ptr_array_steal_index_fast (objlist,
objlist->len - 1);
wp_registry_notify_rm_object (self, object);
}
}
/* in case there are any object managers left,
remove the weak ref on them and let them be... */
{
g_autoptr (GPtrArray) object_mgrs;
GObject *om;
object_mgrs = g_steal_pointer (&self->object_managers);
while (object_mgrs->len > 0) {
om = g_ptr_array_steal_index_fast (object_mgrs, object_mgrs->len - 1);
g_object_weak_unref (om, object_manager_destroyed, self);
}
}
}
void
wp_registry_attach (WpRegistry *self, struct pw_core *pw_core)
{
self->pw_registry = pw_core_get_registry (pw_core,
PW_VERSION_REGISTRY, 0);
pw_registry_add_listener (self->pw_registry, &self->listener,
&registry_events, self);
}
void
wp_registry_detach (WpRegistry *self)
{
if (self->pw_registry) {
spa_hook_remove (&self->listener);
pw_proxy_destroy ((struct pw_proxy *) self->pw_registry);
self->pw_registry = NULL;
}
/* remove pipewire globals */
GPtrArray *objlist = self->globals;
if (!objlist)
return;
while (objlist->len > 0) {
g_autoptr (WpGlobal) global = g_ptr_array_steal_index_fast (objlist,
objlist->len - 1);
if (!global)
continue;
if (global->proxy)
wp_registry_notify_rm_object (self, global->proxy);
/* remove the APPEARS_ON_REGISTRY flag to unref the proxy if it is owned
by the registry; set registry to NULL to avoid further interference */
global->registry = NULL;
wp_global_rm_flag (global, WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY);
/* the registry's ref on global is dropped here; it may still live if
there is a proxy that owns a ref on it, but global->registry is set
to NULL, so there is no further interference */
}
}
/*
* wp_core_find_object:
* @core: the core
* @func: (scope call): a function that takes the object being searched
* as the first argument and @data as the second. it should return TRUE if
* the object is found or FALSE otherwise
* @data: the second argument to @func
*
* Finds a registered object
*
* Returns: (transfer full) (type GObject *) (nullable): the registered object
* or NULL if not found
*/
gpointer
wp_core_find_object (WpCore * core, GEqualFunc func, gconstpointer data)
{
WpRegistry *reg;
GObject *object;
guint i;
g_return_val_if_fail (WP_IS_CORE (core), NULL);
reg = &core->registry;
/* prevent bad things when called from within wp_registry_clear() */
if (G_UNLIKELY (!reg->objects))
return NULL;
for (i = 0; i < reg->objects->len; i++) {
object = g_ptr_array_index (reg->objects, i);
if (func (object, data))
return g_object_ref (object);
}
return NULL;
}
/*
* wp_core_register_object:
* @core: the core
* @obj: (transfer full) (type GObject*): the object to register
*
* Registers @obj with the core, making it appear on #WpObjectManager
* instances as well. The core will also maintain a ref to that object
* until it is removed.
*/
void
wp_core_register_object (WpCore * core, gpointer obj)
{
WpRegistry *reg;
g_return_if_fail (WP_IS_CORE (core));
g_return_if_fail (G_IS_OBJECT (obj));
reg = &core->registry;
/* prevent bad things when called from within wp_registry_clear() */
if (G_UNLIKELY (!reg->objects)) {
g_object_unref (obj);
return;
}
g_ptr_array_add (reg->objects, obj);
/* notify object managers */
wp_registry_notify_add_object (reg, obj);
}
/*
* wp_core_remove_object:
* @core: the core
* @obj: (transfer none) (type GObject*): a pointer to the object to remove
*
* Detaches and unrefs the specified object from this core
*/
void
wp_core_remove_object (WpCore * core, gpointer obj)
{
WpRegistry *reg;
g_return_if_fail (WP_IS_CORE (core));
g_return_if_fail (G_IS_OBJECT (obj));
reg = &core->registry;
/* prevent bad things when called from within wp_registry_clear() */
if (G_UNLIKELY (!reg->objects))
return;
/* notify object managers */
wp_registry_notify_rm_object (reg, obj);
g_ptr_array_remove_fast (reg->objects, obj);
}
/**
* wp_core_install_object_manager: (method)
* @core: the core
* @om: (transfer none): a #WpObjectManager
*
* Installs the object manager on this core, activating its internal management
* engine. This will immediately emit signals about objects added on @om
* if objects that the @om is interested in were in existence already.
*/
void
wp_core_install_object_manager (WpCore * core, WpObjectManager * om)
{
WpRegistry *reg;
guint i;
g_return_if_fail (WP_IS_CORE (core));
g_return_if_fail (WP_IS_OBJECT_MANAGER (om));
reg = &core->registry;
g_object_weak_ref (G_OBJECT (om), object_manager_destroyed, reg);
g_ptr_array_add (reg->object_managers, om);
g_object_set (om, "core", core, NULL);
/* add pre-existing objects to the object manager,
in case it's interested in them */
for (i = 0; i < reg->globals->len; i++) {
WpGlobal *g = g_ptr_array_index (reg->globals, i);
/* check if null because the globals array can have gaps */
if (g)
wp_object_manager_add_global (om, g);
}
for (i = 0; i < reg->objects->len; i++) {
GObject *o = g_ptr_array_index (reg->objects, i);
wp_object_manager_add_object (om, o);
}
}
/* WpGlobal */
G_DEFINE_BOXED_TYPE (WpGlobal, wp_global, wp_global_ref, wp_global_unref)
WpGlobal *
wp_global_new (WpRegistry * reg, guint32 id, guint32 permissions,
GType type, WpProperties * properties, WpProxy * proxy, guint32 flags)
{
g_return_val_if_fail (flags != 0, NULL);
WpGlobal *global = g_rc_box_new0 (WpGlobal);
global->flags = flags;
global->id = id;
global->type = type;
global->permissions = permissions;
global->properties = properties;
global->proxy = proxy;
global->registry = reg;
if (reg->globals->len <= id)
g_ptr_array_set_size (reg->globals, id + 1);
g_ptr_array_index (reg->globals, id) = wp_global_ref (global);
/* notify object managers */
for (guint i = 0; i < reg->object_managers->len; i++) {
WpObjectManager *om = g_ptr_array_index (reg->object_managers, i);
wp_object_manager_add_global (om, global);
}
return global;
}
void
wp_global_rm_flag (WpGlobal *global, guint rm_flag)
{
WpRegistry *reg = global->registry;
/* no flag to remove */
if (!(global->flags & rm_flag))
return;
/* global was owned by the proxy; by removing the flag, we clear out
also the proxy pointer, which is presumably no longer valid and we
notify all listeners that the proxy is gone */
if (rm_flag == WP_GLOBAL_FLAG_OWNED_BY_PROXY) {
global->flags &= ~WP_GLOBAL_FLAG_OWNED_BY_PROXY;
if (reg)
wp_registry_notify_rm_object (reg, global->proxy);
global->proxy = NULL;
}
/* registry removed the global */
else if (rm_flag == WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY) {
global->flags &= ~WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY;
/* if there is a proxy and it's not owning the global, destroy it */
if (global->flags == 0 && global->proxy) {
if (reg)
wp_registry_notify_rm_object (reg, global->proxy);
g_clear_object (&global->proxy);
}
}
/* drop the registry's ref on global when it has no flags anymore */
if (global->flags == 0 && reg) {
g_clear_pointer (&g_ptr_array_index (reg->globals, global->id), wp_global_unref);
}
}
struct pw_proxy *
wp_global_bind (WpGlobal * global)
{
g_return_val_if_fail (global->proxy, NULL);
g_return_val_if_fail (global->registry, NULL);
WpProxyClass *klass = WP_PROXY_GET_CLASS (global->proxy);
return pw_registry_bind (global->registry->pw_registry, global->id,
klass->pw_iface_type, klass->pw_iface_version, 0);
}
/* WpObjectManager */
struct interest
{
GType g_type;
gboolean for_proxy;
WpProxyFeatures wanted_features;
GVariant *constraints; // aa{sv}
};
......@@ -28,7 +478,7 @@ struct _WpObjectManager
pw_array has a better API for our use case than GArray */
struct pw_array interests;
/* objects that we are interested in, with a strong ref */
/* objects that we are interested in, without a ref */
GPtrArray *objects;
gboolean pending_objchanged;
......@@ -55,7 +505,7 @@ wp_object_manager_init (WpObjectManager * self)
{
g_weak_ref_init (&self->core, NULL);
pw_array_init (&self->interests, sizeof (struct interest));
self->objects = g_ptr_array_new_with_free_func (g_object_unref);
self->objects = g_ptr_array_new ();
self->pending_objchanged = FALSE;
}
......@@ -144,44 +594,23 @@ wp_object_manager_new (void)
}
void
wp_object_manager_add_proxy_interest (WpObjectManager *self,
wp_object_manager_add_interest (WpObjectManager *self,
GType gtype, GVariant * constraints,
WpProxyFeatures wanted_features)
{
struct interest *i;
g_return_if_fail (WP_IS_OBJECT_MANAGER (self));
g_return_if_fail (g_type_is_a (gtype, WP_TYPE_PROXY));
g_return_if_fail (constraints == NULL ||
g_variant_is_of_type (constraints, G_VARIANT_TYPE ("aa{sv}")));
/* grow the array by 1 struct interest and fill it in */
i = pw_array_add (&self->interests, sizeof (struct interest));
i->g_type = gtype;
i->for_proxy = TRUE;
i->wanted_features = wanted_features;
i->constraints = constraints ? g_variant_ref_sink (constraints) : NULL;
}
void
wp_object_manager_add_object_interest (WpObjectManager *self,
GType gtype, GVariant * constraints)
{
struct interest *i;
g_return_if_fail (WP_IS_OBJECT_MANAGER (self));
g_return_if_fail (G_TYPE_IS_OBJECT (gtype));
g_return_if_fail (constraints == NULL ||
g_variant_is_of_type (constraints, G_VARIANT_TYPE ("aa{sv}")));
/* grow the array by 1 struct interest and fill it in */
i = pw_array_add (&self->interests, sizeof (struct interest));
i->g_type = gtype;
i->for_proxy = FALSE;
i->wanted_features = 0;
i->constraints = constraints ? g_variant_ref_sink (constraints) : NULL;
}
/**
* wp_object_manager_get_objects:
* @self: the object manager
......@@ -329,8 +758,7 @@ wp_object_manager_is_interested_in_object (WpObjectManager * self,
struct interest *i;
pw_array_for_each (i, &self->interests) {
if (!i->for_proxy
&& g_type_is_a (G_OBJECT_TYPE (object), i->g_type)
if (g_type_is_a (G_OBJECT_TYPE (object), i->g_type)
&& (!i->constraints ||
check_constraints (i->constraints, NULL, object)))
{
......@@ -348,8 +776,7 @@ wp_object_manager_is_interested_in_global (WpObjectManager * self,
struct interest *i;
pw_array_for_each (i, &self->interests) {
if (i->for_proxy
&& g_type_is_a (global->type, i->g_type)
if (g_type_is_a (global->type, i->g_type)
&& (!i->constraints ||
check_constraints (i->constraints, global->properties, NULL)))
{
......@@ -389,56 +816,42 @@ on_proxy_ready (GObject * proxy, GAsyncResult * res, gpointer data)
{
g_autoptr (WpObjectManager) self = WP_OBJECT_MANAGER (data);
g_ptr_array_add (self->objects, g_object_ref (proxy));
g_ptr_array_add (self->objects, proxy);
g_signal_emit (self, signals[SIGNAL_OBJECT_ADDED], 0, proxy);
schedule_emit_objects_changed (self);
}
void
static void
wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global)
{
WpProxyFeatures features = 0;
if (wp_object_manager_is_interested_in_global (self, global, &features)) {
g_autoptr (WpProxy) proxy = g_weak_ref_get (&global->proxy);
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
if (!proxy) {
proxy = wp_proxy_new_global (core, global);
g_weak_ref_set (&global->proxy, proxy);
}
wp_proxy_augment (proxy, features, NULL, on_proxy_ready,
g_object_ref (self));
}
}
if (!global->proxy)
global->proxy = g_object_new (global->type,
"core", core,
"global", global,
NULL);
void
wp_object_manager_rm_global (WpObjectManager * self, guint32 id)
{
guint i;
for (i = 0; i < self->objects->len; i++) {
gpointer obj = g_ptr_array_index (self->objects, i);
if (WP_IS_PROXY (obj) && id == wp_proxy_get_bound_id (WP_PROXY (obj))) {
g_signal_emit (self, signals[SIGNAL_OBJECT_REMOVED], 0, obj);
g_ptr_array_remove_index_fast (self->objects, i);
schedule_emit_objects_changed (self);
return;
}
wp_proxy_augment (global->proxy, features, NULL, on_proxy_ready,
g_object_ref (self));
}
}
void
wp_object_manager_add_object (WpObjectManager * self, GObject * object)
static void
wp_object_manager_add_object (WpObjectManager * self, gpointer object)
{
if (wp_object_manager_is_interested_in_object (self, object)) {
g_ptr_array_add (self->objects, g_object_ref (object));
g_ptr_array_add (self->objects, object);
g_signal_emit (self, signals[SIGNAL_OBJECT_ADDED], 0, object);
schedule_emit_objects_changed (self);
}
}
void
wp_object_manager_rm_object (WpObjectManager * self, GObject * object)
static void
wp_object_manager_rm_object (WpObjectManager * self, gpointer object)
{
guint index;
if (g_ptr_array_find (self->objects, object, &index)) {
......
......@@ -28,13 +28,9 @@ WP_API
WpObjectManager * wp_object_manager_new (void);
WP_API
void wp_object_manager_add_proxy_interest (WpObjectManager *self,
void wp_object_manager_add_interest (WpObjectManager *self,
GType gtype, GVariant * constraints, WpProxyFeatures wanted_features);
WP_API
void wp_object_manager_add_object_interest (WpObjectManager *self,
GType gtype, GVariant * constraints);
WP_API
GPtrArray * wp_object_manager_get_objects (WpObjectManager *self,
GType type_filter);
......
......@@ -113,8 +113,8 @@ wp_policy_manager_get_instance (WpCore *core)
mgr = g_object_new (WP_TYPE_POLICY_MANAGER, NULL);
/* install the object manager to listen to added/removed endpoints */
wp_object_manager_add_object_interest (mgr->endpoints_om,
WP_TYPE_BASE_ENDPOINT, NULL);
wp_object_manager_add_interest (mgr->endpoints_om,
WP_TYPE_BASE_ENDPOINT, NULL, 0);
g_signal_connect_object (mgr->endpoints_om, "object-added",
(GCallback) policy_mgr_endpoint_added, mgr, 0);
g_signal_connect_object (mgr->endpoints_om, "object-removed",
......@@ -122,8 +122,9 @@ wp_policy_manager_get_instance (WpCore *core)
wp_core_install_object_manager (core, mgr->endpoints_om);
/* install the object manager to listen to changed sessions */
wp_object_manager_add_object_interest (mgr->sessions_om,
WP_TYPE_IMPL_SESSION, NULL);
wp_object_manager_add_interest (mgr->sessions_om,
WP_TYPE_IMPL_SESSION, NULL,
WP_PROXY_FEATURES_STANDARD | WP_SESSION_FEATURE_DEFAULT_ENDPOINT);
wp_core_install_object_manager (core, mgr->sessions_om);
wp_core_register_object (core, g_object_ref (mgr));
......
......@@ -14,14 +14,51 @@
#include "proxy.h"
#include <stdint.h>
#include <pipewire/pipewire.h>
G_BEGIN_DECLS
/* registry */
typedef struct _WpRegistry WpRegistry;
struct _WpRegistry
{
struct pw_registry *pw_registry;
struct spa_hook listener;
GPtrArray *globals; // elementy-type: WpGlobal*
GPtrArray *objects; // element-type: GObject*
GPtrArray *object_managers; // element-type: WpObjectManager*
};
void wp_registry_init (WpRegistry *self);
void wp_registry_clear (WpRegistry *self);
void wp_registry_attach (WpRegistry *self, struct pw_core *pw_core);
void wp_registry_detach (WpRegistry *self);
/* core */
struct pw_registry;
struct _WpCore
{
GObject parent;
/* main loop integration */
GMainContext *context;
/* extra properties */
WpProperties *properties;
/* pipewire main objects */
struct pw_context *pw_context;
struct pw_core *pw_core;
struct pw_registry * wp_core_get_pw_registry (WpCore * self);
/* pipewire main listeners */
struct spa_hook core_listener;
struct spa_hook proxy_core_listener;
WpRegistry registry;
GHashTable *async_tasks; // <int seq, GTask*>
};
gpointer wp_core_find_object (WpCore * self, GEqualFunc func,
gconstpointer data);
......@@ -30,29 +67,30 @@ void wp_core_remove_object (WpCore * self, gpointer obj);
/* global */
typedef enum {
WP_GLOBAL_FLAG_APPEARS_ON_REGISTRY = 0x1,
WP_GLOBAL_FLAG_OWNED_BY_PROXY = 0x2,
} WpGlobalFlags;
typedef struct _WpGlobal WpGlobal;
struct _WpGlobal
{
guint32 flags;
guint32 id;
GType type;
guint32 permissions;
WpProperties *properties;
GWeakRef proxy;
WpProxy *proxy;
WpRegistry *registry;
};
static inline WpGlobal *
wp_global_new (void)
{
WpGlobal *self = g_rc_box_new0 (WpGlobal);
g_weak_ref_init (&self->proxy, NULL);
return self;
}
#define WP_TYPE_GLOBAL (wp_global_get_type ())
GType wp_global_get_type (void);
static inline void
wp_global_clear (WpGlobal * self)
{
g_clear_pointer (&self->properties, wp_properties_unref);
g_weak_ref_clear (&self->proxy);
}
static inline WpGlobal *
......@@ -67,20 +105,16 @@ wp_global_unref (WpGlobal * self)
g_rc_box_release_full (self, (GDestroyNotify) wp_global_clear);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpGlobal, wp_global_unref)
/* object manager */
WpGlobal * wp_global_new (WpRegistry * reg, guint32 id, guint32 permissions,
GType type, WpProperties * properties, WpProxy * proxy, guint32 flags);
void wp_global_rm_flag (WpGlobal *global, guint rm_flag);
void wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global);
void wp_object_manager_rm_global (WpObjectManager * self, guint32 id);
struct pw_proxy * wp_global_bind (WpGlobal * global);
void wp_object_manager_add_object (WpObjectManager * self, GObject * object);
void wp_object_manager_rm_object (WpObjectManager * self, GObject * object);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpGlobal, wp_global_unref)
/* proxy */
WpProxy * wp_proxy_new_global (WpCore * core, WpGlobal * global);
void wp_proxy_set_pw_proxy (WpProxy * self, struct pw_proxy * proxy);
void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature);
......
......@@ -64,7 +64,6 @@ enum
static guint wp_proxy_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_BOXED_TYPE (WpGlobal, wp_global, wp_global_ref, wp_global_unref)
G_DEFINE_TYPE_WITH_PRIVATE (WpProxy, wp_proxy, G_TYPE_OBJECT)
static void
......@@ -116,6 +115,14 @@ proxy_event_bound (void *data, uint32_t global_id)
g_warn_if_fail (!priv->global || priv->global->id == global_id);
wp_proxy_set_feature_ready (self, WP_PROXY_FEATURE_BOUND);
/* construct a WpGlobal if it was not already there */
if (!priv->global) {
g_autoptr (WpCore) core = g_weak_ref_get (&priv->core);
priv->global = wp_global_new (&core->registry, global_id, PW_PERM_RWX,
G_TYPE_FROM_INSTANCE (self), wp_properties_new_empty (), self,
WP_GLOBAL_FLAG_OWNED_BY_PROXY);
}
}
static const struct pw_proxy_events proxy_events = {
......@@ -164,8 +171,10 @@ wp_proxy_dispose (GObject * object)
g_debug ("%s:%p dispose (global %u; pw_proxy %p)",
G_OBJECT_TYPE_NAME (object), object,
priv->global ? priv->global->id : 0,
priv->pw_proxy);
priv->global ? priv->global->id : 0, priv->pw_proxy);
if (priv->global)
wp_global_rm_flag (priv->global, WP_GLOBAL_FLAG_OWNED_BY_PROXY);
/* this will trigger proxy_event_destroy() if the pw_proxy exists */
if (priv->pw_proxy)
......@@ -253,8 +262,6 @@ static void
wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features)
{
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
WpProxyClass *klass = WP_PROXY_GET_CLASS (self);
g_autoptr (WpCore) core = NULL;
/* ensure we have a pw_proxy, as we can't have
* any other feature without first having that */
......@@ -272,13 +279,8 @@ wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features)
return;
}
core = g_weak_ref_get (&priv->core);
g_return_if_fail (core);
/* bind */
wp_proxy_set_pw_proxy (self, pw_registry_bind (
wp_core_get_pw_registry (core), priv->global->id,
klass->pw_iface_type, klass->pw_iface_version, 0));
wp_proxy_set_pw_proxy (self, wp_global_bind (priv->global));
}
}
......@@ -355,15 +357,6 @@ wp_proxy_class_init (WpProxyClass * klass)
G_TYPE_INT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_POINTER);
}
WpProxy *
wp_proxy_new_global (WpCore * core, WpGlobal * global)
{
return g_object_new (global->type,
"core", core,
"global", global,
NULL);
}
void
wp_proxy_augment (WpProxy * self,
WpProxyFeatures ft_wanted, GCancellable * cancellable,
......
......@@ -33,7 +33,7 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
WpObjectManager *om;
om = wp_object_manager_new ();
wp_object_manager_add_proxy_interest (om, WP_TYPE_CLIENT, NULL,
wp_object_manager_add_interest (om, WP_TYPE_CLIENT, NULL,
WP_PROXY_FEATURES_STANDARD);
g_signal_connect (om, "object-added", (GCallback) client_added, NULL);
......
......@@ -256,7 +256,7 @@ wp_config_endpoint_context_init (WpConfigEndpointContext *self)
g_direct_equal, NULL, (GDestroyNotify) g_object_unref);
/* Only handle augmented nodes with info set */
wp_object_manager_add_proxy_interest (self->om, WP_TYPE_NODE, NULL,
wp_object_manager_add_interest (self->om, WP_TYPE_NODE, NULL,
WP_PROXY_FEATURES_STANDARD);
/* Register the global added/removed callbacks */
......
......@@ -205,7 +205,7 @@ wp_config_static_nodes_context_init (WpConfigStaticNodesContext *self)
self->devices_om = wp_object_manager_new ();
/* Only handle devices */
wp_object_manager_add_proxy_interest (self->devices_om,
wp_object_manager_add_interest (self->devices_om,
WP_TYPE_DEVICE, NULL, WP_PROXY_FEATURE_INFO);
g_signal_connect (self->devices_om, "object-added",
(GCallback) on_device_added, self);
......
......@@ -159,7 +159,7 @@ on_node_proxy_augmented (WpProxy * proxy, GAsyncResult * res,
g_variant_builder_close (&b);
/* declare interest on ports with this constraint */
wp_object_manager_add_proxy_interest (priv->ports_om, WP_TYPE_PORT,
wp_object_manager_add_interest (priv->ports_om, WP_TYPE_PORT,
g_variant_builder_end (&b),
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO);
......
......@@ -133,7 +133,8 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
(GCallback) on_endpoint_added, data);
g_signal_connect (data->om, "object-removed",
(GCallback) on_endpoint_removed, data);
wp_object_manager_add_object_interest (data->om,
WP_TYPE_IMPL_ENDPOINT, NULL);
wp_object_manager_add_interest (data->om,
WP_TYPE_IMPL_ENDPOINT, NULL,
WP_PROXY_FEATURES_STANDARD | WP_ENDPOINT_FEATURE_CONTROLS);
wp_core_install_object_manager (core, data->om);
}
......@@ -222,8 +222,9 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
(GCallback) test_endpoint_basic_impl_object_added, fixture);
g_signal_connect (fixture->export_om, "object-removed",
(GCallback) test_endpoint_basic_impl_object_removed, fixture);
wp_object_manager_add_object_interest (fixture->export_om,
WP_TYPE_IMPL_ENDPOINT, NULL);
wp_object_manager_add_interest (fixture->export_om,
WP_TYPE_IMPL_ENDPOINT, NULL,
WP_PROXY_FEATURES_STANDARD | WP_ENDPOINT_FEATURE_CONTROLS);
wp_core_install_object_manager (fixture->export_core, fixture->export_om);
g_assert_true (wp_core_connect (fixture->export_core));
......@@ -233,7 +234,7 @@ test_endpoint_basic (TestEndpointFixture *fixture, gconstpointer data)
(GCallback) test_endpoint_basic_proxy_object_added, fixture);
g_signal_connect (fixture->proxy_om, "object-removed",
(GCallback) test_endpoint_basic_proxy_object_removed, fixture);
wp_object_manager_add_proxy_interest (fixture->proxy_om,
wp_object_manager_add_interest (fixture->proxy_om,
WP_TYPE_ENDPOINT, NULL,
WP_PROXY_FEATURES_STANDARD | WP_ENDPOINT_FEATURE_CONTROLS);
wp_core_install_object_manager (fixture->proxy_core, fixture->proxy_om);
......
......@@ -138,7 +138,7 @@ test_proxy_basic (TestProxyFixture *fixture, gconstpointer data)
g_signal_connect (fixture->om, "object-added",
(GCallback) test_proxy_basic_object_added, fixture);
wp_object_manager_add_proxy_interest (fixture->om, WP_TYPE_CLIENT, NULL, 0);
wp_object_manager_add_interest (fixture->om, WP_TYPE_CLIENT, NULL, 0);
wp_core_install_object_manager (fixture->core, fixture->om);
g_assert_true (wp_core_connect (fixture->core));
......@@ -239,7 +239,7 @@ test_node (TestProxyFixture *fixture, gconstpointer data)
/* declare interest and set default features to be ready
when the signal is fired */
wp_object_manager_add_proxy_interest (fixture->om,
wp_object_manager_add_interest (fixture->om,
WP_TYPE_NODE, NULL, WP_PROXY_FEATURES_STANDARD);
wp_core_install_object_manager (fixture->core, fixture->om);
......
......@@ -220,8 +220,9 @@ test_session_basic (TestSessionFixture *fixture, gconstpointer data)
(GCallback) test_session_basic_exported_object_added, fixture);
g_signal_connect (fixture->export_om, "object-removed",
(GCallback) test_session_basic_exported_object_removed, fixture);
wp_object_manager_add_object_interest (fixture->export_om,
WP_TYPE_IMPL_SESSION, NULL);
wp_object_manager_add_interest (fixture->export_om,
WP_TYPE_IMPL_SESSION, NULL,
WP_PROXY_FEATURES_STANDARD | WP_SESSION_FEATURE_DEFAULT_ENDPOINT);
wp_core_install_object_manager (fixture->export_core, fixture->export_om);
g_assert_true (wp_core_connect (fixture->export_core));
......@@ -231,7 +232,7 @@ test_session_basic (TestSessionFixture *fixture, gconstpointer data)
(GCallback) test_session_basic_proxy_object_added, fixture);
g_signal_connect (fixture->proxy_om, "object-removed",
(GCallback) test_session_basic_proxy_object_removed, fixture);
wp_object_manager_add_proxy_interest (fixture->proxy_om,
wp_object_manager_add_interest (fixture->proxy_om,
WP_TYPE_SESSION, NULL,
WP_PROXY_FEATURES_STANDARD | WP_SESSION_FEATURE_DEFAULT_ENDPOINT);
wp_core_install_object_manager (fixture->proxy_core, fixture->proxy_om);
......
......@@ -258,10 +258,10 @@ main (gint argc, gchar **argv)
om = wp_object_manager_new ();
if (argc == 2 && !g_strcmp0 (argv[1], "ls-endpoints")) {
wp_object_manager_add_proxy_interest (om, WP_TYPE_ENDPOINT,
wp_object_manager_add_interest (om, WP_TYPE_ENDPOINT,
NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND |
WP_ENDPOINT_FEATURE_CONTROLS);
wp_object_manager_add_proxy_interest (om, WP_TYPE_SESSION,
wp_object_manager_add_interest (om, WP_TYPE_SESSION,
NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND |
WP_SESSION_FEATURE_DEFAULT_ENDPOINT);
g_signal_connect (om, "objects-changed", (GCallback) list_endpoints, &data);
......@@ -274,9 +274,9 @@ main (gint argc, gchar **argv)
return 1;
}
wp_object_manager_add_proxy_interest (om, WP_TYPE_ENDPOINT,
wp_object_manager_add_interest (om, WP_TYPE_ENDPOINT,
NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND);
wp_object_manager_add_proxy_interest (om, WP_TYPE_SESSION,
wp_object_manager_add_interest (om, WP_TYPE_SESSION,
NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND |
WP_SESSION_FEATURE_DEFAULT_ENDPOINT);
......@@ -292,7 +292,7 @@ main (gint argc, gchar **argv)
return 1;
}
wp_object_manager_add_proxy_interest (om, WP_TYPE_ENDPOINT,
wp_object_manager_add_interest (om, WP_TYPE_ENDPOINT,
NULL, WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND |
WP_ENDPOINT_FEATURE_CONTROLS);
......@@ -302,7 +302,7 @@ main (gint argc, gchar **argv)
}
else if (argc == 2 && !g_strcmp0 (argv[1], "device-node-props")) {
wp_object_manager_add_proxy_interest (om, WP_TYPE_NODE, NULL,
wp_object_manager_add_interest (om, WP_TYPE_NODE, NULL,
WP_PROXY_FEATURE_INFO);
g_signal_connect (om, "objects-changed", (GCallback) device_node_props,
&data);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment