mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-08 10:28:03 +02:00
lib: add new WpDbus API
This commit is contained in:
parent
2ac384711d
commit
9183787325
6 changed files with 534 additions and 0 deletions
361
lib/wp/dbus.c
Normal file
361
lib/wp/dbus.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2022 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "wp-dbus"
|
||||
|
||||
#include "private/registry.h"
|
||||
#include "log.h"
|
||||
#include "wpenums.h"
|
||||
#include "dbus.h"
|
||||
|
||||
enum {
|
||||
STEP_DBUS_ENABLE = WP_TRANSITION_STEP_CUSTOM_START,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_DBUS_0,
|
||||
PROP_DBUS_BUS_TYPE,
|
||||
PROP_DBUS_STATE,
|
||||
};
|
||||
|
||||
struct _WpDbus
|
||||
{
|
||||
WpObject parent;
|
||||
|
||||
/* Props */
|
||||
GBusType bus_type;
|
||||
WpDBusState state;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GDBusConnection *connection;
|
||||
};
|
||||
|
||||
static void on_connection_closed (GDBusConnection *connection, gboolean
|
||||
remote_peer_vanished, GError *error, gpointer data);
|
||||
|
||||
G_DEFINE_TYPE (WpDbus, wp_dbus, WP_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
wp_dbus_init (WpDbus * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_set_state (WpDbus *self, WpDBusState new_state)
|
||||
{
|
||||
if (self->state != new_state) {
|
||||
self->state = new_state;
|
||||
g_object_notify (G_OBJECT (self), "state");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_got_bus (GObject * obj, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
WpTransition *transition = WP_TRANSITION (data);
|
||||
WpDbus *self = wp_transition_get_source_object (transition);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
self->connection = g_dbus_connection_new_for_address_finish (res, &error);
|
||||
if (!self->connection) {
|
||||
g_prefix_error (&error, "Failed to connect to bus: ");
|
||||
wp_transition_return_error (transition, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
wp_debug_object (self, "Connected to bus");
|
||||
|
||||
/* set up connection */
|
||||
g_signal_connect_object (self->connection, "closed",
|
||||
G_CALLBACK (on_connection_closed), self, 0);
|
||||
g_dbus_connection_set_exit_on_close (self->connection, FALSE);
|
||||
|
||||
wp_dbus_set_state (self, WP_DBUS_STATE_CONNECTED);
|
||||
|
||||
wp_object_update_features (WP_OBJECT (self), WP_DBUS_FEATURE_ENABLED, 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_connect (WpDbus *self, GAsyncReadyCallback callback, gpointer data,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree gchar *address = NULL;
|
||||
|
||||
address = g_dbus_address_get_for_bus_sync (self->bus_type, NULL, error);
|
||||
if (!address) {
|
||||
g_prefix_error (error, "Error acquiring bus address: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wp_dbus_set_state (self, WP_DBUS_STATE_CONNECTING);
|
||||
|
||||
wp_debug_object (self, "Connecting to bus: %s", address);
|
||||
g_dbus_connection_new_for_address (address,
|
||||
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
||||
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
|
||||
NULL, self->cancellable, callback, data);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_sync_reconnect (WpCore * core, GAsyncResult * res, WpDbus * self)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_core_sync_finish (core, res, &error)) {
|
||||
wp_warning_object (self, "core sync error: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!do_connect (self, on_got_bus, self, &error))
|
||||
wp_info_object (self, "Cannot reconnect on sync: %s", error->message);
|
||||
}
|
||||
|
||||
static void
|
||||
on_connection_closed (GDBusConnection *connection,
|
||||
gboolean remote_peer_vanished, GError *error, gpointer data)
|
||||
{
|
||||
WpDbus *self = WP_DBUS (data);
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
|
||||
wp_info_object (self, "DBus connection closed: %s", error->message);
|
||||
|
||||
g_clear_object (&self->connection);
|
||||
wp_dbus_set_state (self, WP_DBUS_STATE_CLOSED);
|
||||
|
||||
/* try to reconnect on idle if connection was closed */
|
||||
core = wp_object_get_core (WP_OBJECT (self));
|
||||
if (core && wp_core_is_connected (core)) {
|
||||
wp_info_object (self, "Trying to reconnect on sync");
|
||||
wp_core_sync_closure (core, NULL, g_cclosure_new_object (
|
||||
G_CALLBACK (on_sync_reconnect), G_OBJECT (self)));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_enable (WpDbus *self, WpTransition *transition)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
if (!do_connect (self, on_got_bus, transition, &error)) {
|
||||
wp_transition_return_error (transition, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_disable (WpDbus *self)
|
||||
{
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
|
||||
g_clear_object (&self->connection);
|
||||
wp_dbus_set_state (self, WP_DBUS_STATE_CLOSED);
|
||||
|
||||
g_clear_object (&self->cancellable);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
wp_object_update_features (WP_OBJECT (self), 0, WP_DBUS_FEATURE_ENABLED);
|
||||
}
|
||||
|
||||
static WpObjectFeatures
|
||||
wp_dbus_get_supported_features (WpObject * self)
|
||||
{
|
||||
return WP_DBUS_FEATURE_ENABLED;
|
||||
}
|
||||
|
||||
static guint
|
||||
wp_dbus_activate_get_next_step (WpObject * object,
|
||||
WpFeatureActivationTransition * t, guint step, WpObjectFeatures missing)
|
||||
{
|
||||
switch (step) {
|
||||
case WP_TRANSITION_STEP_NONE:
|
||||
return STEP_DBUS_ENABLE;
|
||||
case STEP_DBUS_ENABLE:
|
||||
return WP_TRANSITION_STEP_NONE;
|
||||
default:
|
||||
return WP_TRANSITION_STEP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_activate_execute_step (WpObject * object,
|
||||
WpFeatureActivationTransition * t, guint step, WpObjectFeatures missing)
|
||||
{
|
||||
WpDbus *self = WP_DBUS (object);
|
||||
WpTransition *transition = WP_TRANSITION (t);
|
||||
|
||||
switch (step) {
|
||||
case STEP_DBUS_ENABLE:
|
||||
wp_dbus_enable (self, transition);
|
||||
break;
|
||||
|
||||
case WP_TRANSITION_STEP_ERROR:
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_deactivate (WpObject * object, WpObjectFeatures features)
|
||||
{
|
||||
WpDbus *self = WP_DBUS (object);
|
||||
guint current = wp_object_get_active_features (object);
|
||||
|
||||
if (features & current & WP_DBUS_FEATURE_ENABLED)
|
||||
wp_dbus_disable (self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpDbus *self = WP_DBUS (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_DBUS_BUS_TYPE:
|
||||
g_value_set_enum (value, self->bus_type);
|
||||
break;
|
||||
case PROP_DBUS_STATE:
|
||||
g_value_set_enum (value, self->state);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpDbus *self = WP_DBUS (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_DBUS_BUS_TYPE:
|
||||
self->bus_type = g_value_get_enum (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_dbus_class_init (WpDbusClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpObjectClass *wpobject_class = (WpObjectClass *) klass;
|
||||
|
||||
object_class->set_property = wp_dbus_set_property;
|
||||
object_class->get_property = wp_dbus_get_property;
|
||||
|
||||
wpobject_class->get_supported_features = wp_dbus_get_supported_features;
|
||||
wpobject_class->activate_get_next_step = wp_dbus_activate_get_next_step;
|
||||
wpobject_class->activate_execute_step = wp_dbus_activate_execute_step;
|
||||
wpobject_class->deactivate = wp_dbus_deactivate;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_DBUS_BUS_TYPE,
|
||||
g_param_spec_enum ("bus-type", "bus-type", "The bus type",
|
||||
G_TYPE_BUS_TYPE, G_BUS_TYPE_NONE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_DBUS_STATE,
|
||||
g_param_spec_enum ("state", "state", "The dbus connection state",
|
||||
WP_TYPE_DBUS_STATE, WP_DBUS_STATE_CLOSED,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_dbus_func (gpointer object, gpointer p)
|
||||
{
|
||||
GBusType *bus_type = p;
|
||||
|
||||
if (!WP_IS_DBUS (object) || !bus_type)
|
||||
return FALSE;
|
||||
|
||||
return wp_dbus_get_bus_type (WP_DBUS (object)) == *bus_type;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the dbus instance that is associated with the given core and
|
||||
* bus type.
|
||||
*
|
||||
* This method will also create the instance and register it with the core
|
||||
* if it had not been created before.
|
||||
*
|
||||
* \param core the core
|
||||
* \param bus_type the bus type
|
||||
* \return (transfer full): the dbus instance
|
||||
*/
|
||||
WpDbus *
|
||||
wp_dbus_get_instance (WpCore *core, GBusType bus_type)
|
||||
{
|
||||
WpRegistry *registry;
|
||||
WpDbus *dbus;
|
||||
|
||||
g_return_val_if_fail (core, NULL);
|
||||
g_return_val_if_fail (
|
||||
bus_type != G_BUS_TYPE_NONE || bus_type != G_BUS_TYPE_STARTER, NULL);
|
||||
|
||||
registry = wp_core_get_registry (core);
|
||||
dbus = wp_registry_find_object (registry, (GEqualFunc) find_dbus_func,
|
||||
&bus_type);
|
||||
if (G_UNLIKELY (!dbus)) {
|
||||
dbus = g_object_new (WP_TYPE_DBUS,
|
||||
"core", core,
|
||||
"bus-type", bus_type,
|
||||
NULL);
|
||||
wp_registry_register_object (registry, g_object_ref (dbus));
|
||||
}
|
||||
|
||||
return dbus;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the bus type of the dbus object.
|
||||
*
|
||||
* \param self the DBus object
|
||||
* \returns the bus type
|
||||
*/
|
||||
GBusType
|
||||
wp_dbus_get_bus_type (WpDbus *self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_DBUS (self), G_BUS_TYPE_NONE);
|
||||
|
||||
return self->bus_type;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the dbus connection state of the dbus object.
|
||||
*
|
||||
* \param self the DBus object
|
||||
* \returns the dbus connection state
|
||||
*/
|
||||
WpDBusState
|
||||
wp_dbus_get_state (WpDbus *self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_DBUS (self), WP_DBUS_STATE_CLOSED);
|
||||
|
||||
return self->state;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the DBus connection object of the dbus object.
|
||||
*
|
||||
* \param self the DBus object
|
||||
* \return (transfer full): the DBus connection object
|
||||
*/
|
||||
GDBusConnection *
|
||||
wp_dbus_get_connection (WpDbus *self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_DBUS (self), NULL);
|
||||
|
||||
return self->connection ? g_object_ref (self->connection) : NULL;
|
||||
}
|
||||
59
lib/wp/dbus.h
Normal file
59
lib/wp/dbus.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2022 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_DBUS_H__
|
||||
#define __WIREPLUMBER_DBUS_H__
|
||||
|
||||
#include "object.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* WpDbus */
|
||||
|
||||
/*!
|
||||
* \brief Flags to be used as WpObjectFeatures for WpDbus.
|
||||
* \ingroup wpdbus
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
/* main features */
|
||||
WP_DBUS_FEATURE_ENABLED = (1 << 0),
|
||||
} WpDbusFeatures;
|
||||
|
||||
/*!
|
||||
* \brief The state of the dbus connection
|
||||
* \ingroup wpdbus
|
||||
*/
|
||||
typedef enum {
|
||||
WP_DBUS_STATE_CLOSED = 0,
|
||||
WP_DBUS_STATE_CONNECTING,
|
||||
WP_DBUS_STATE_CONNECTED,
|
||||
} WpDBusState;
|
||||
|
||||
/*!
|
||||
* \brief The WpDbus GType
|
||||
* \ingroup wpdbus
|
||||
*/
|
||||
#define WP_TYPE_DBUS (wp_dbus_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpDbus, wp_dbus, WP, DBUS, WpObject)
|
||||
|
||||
WP_API
|
||||
WpDbus *wp_dbus_get_instance (WpCore *core, GBusType bus_type);
|
||||
|
||||
WP_API
|
||||
GBusType wp_dbus_get_bus_type (WpDbus *self);
|
||||
|
||||
WP_API
|
||||
WpDBusState wp_dbus_get_state (WpDbus *self);
|
||||
|
||||
WP_API
|
||||
GDBusConnection *wp_dbus_get_connection (WpDbus *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@ wp_lib_sources = files(
|
|||
'client.c',
|
||||
'component-loader.c',
|
||||
'core.c',
|
||||
'dbus.c',
|
||||
'device.c',
|
||||
'endpoint.c',
|
||||
'error.c',
|
||||
|
|
@ -40,6 +41,7 @@ wp_lib_headers = files(
|
|||
'client.h',
|
||||
'component-loader.h',
|
||||
'core.h',
|
||||
'dbus.h',
|
||||
'defs.h',
|
||||
'device.h',
|
||||
'endpoint.h',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "client.h"
|
||||
#include "component-loader.h"
|
||||
#include "core.h"
|
||||
#include "dbus.h"
|
||||
#include "device.h"
|
||||
#include "endpoint.h"
|
||||
#include "error.h"
|
||||
|
|
|
|||
103
tests/wp/dbus.c
Normal file
103
tests/wp/dbus.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2022 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include "../common/base-test-fixture.h"
|
||||
|
||||
typedef struct {
|
||||
WpBaseTestFixture base;
|
||||
GTestDBus *test_dbus;
|
||||
} TestFixture;
|
||||
|
||||
static void
|
||||
test_dbus_setup (TestFixture *self, gconstpointer user_data)
|
||||
{
|
||||
wp_base_test_fixture_setup (&self->base, 0);
|
||||
self->test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
|
||||
g_test_dbus_up (self->test_dbus);
|
||||
}
|
||||
|
||||
static void
|
||||
test_dbus_teardown (TestFixture *self, gconstpointer user_data)
|
||||
{
|
||||
g_test_dbus_down (self->test_dbus);
|
||||
g_clear_object (&self->test_dbus);
|
||||
wp_base_test_fixture_teardown (&self->base);
|
||||
}
|
||||
|
||||
static void
|
||||
test_dbus_basic (TestFixture *f, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpDbus) dbus = wp_dbus_get_instance (f->base.core,
|
||||
G_BUS_TYPE_SESSION);
|
||||
g_assert_nonnull (dbus);
|
||||
g_autoptr (WpDbus) dbus2 = wp_dbus_get_instance (f->base.core,
|
||||
G_BUS_TYPE_SESSION);
|
||||
g_assert_nonnull (dbus2);
|
||||
|
||||
GBusType bus_type = wp_dbus_get_bus_type (dbus);
|
||||
g_assert_true (bus_type == G_BUS_TYPE_SESSION);
|
||||
GBusType bus_type2 = wp_dbus_get_bus_type (dbus);
|
||||
g_assert_true (bus_type2 == G_BUS_TYPE_SESSION);
|
||||
g_assert_true (dbus == dbus2);
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_activated (WpObject * dbus, GAsyncResult * res, TestFixture * f)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
if (!wp_object_activate_finish (dbus, res, &error)) {
|
||||
wp_critical_object (dbus, "%s", error->message);
|
||||
g_main_loop_quit (f->base.loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_state_changed (GObject * obj, GParamSpec * spec, TestFixture *f)
|
||||
{
|
||||
WpDBusState state = wp_dbus_get_state (WP_DBUS (obj));
|
||||
if (state == WP_DBUS_STATE_CONNECTED)
|
||||
g_main_loop_quit (f->base.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
test_dbus_activation (TestFixture *f, gconstpointer user_data)
|
||||
{
|
||||
g_autoptr (WpDbus) dbus = wp_dbus_get_instance (
|
||||
f->base.core, G_BUS_TYPE_SESSION);
|
||||
g_assert_nonnull (dbus);
|
||||
|
||||
wp_object_activate (WP_OBJECT (dbus), WP_DBUS_FEATURE_ENABLED,
|
||||
NULL, (GAsyncReadyCallback) on_dbus_activated, f);
|
||||
|
||||
g_signal_connect (dbus, "notify::state", G_CALLBACK (on_dbus_state_changed),
|
||||
f);
|
||||
|
||||
g_main_loop_run (f->base.loop);
|
||||
g_assert_cmpint (wp_dbus_get_state (WP_DBUS (dbus)), ==,
|
||||
WP_DBUS_STATE_CONNECTED);
|
||||
|
||||
wp_object_deactivate (WP_OBJECT (dbus), WP_DBUS_FEATURE_ENABLED);
|
||||
g_assert_cmpint (wp_dbus_get_state (WP_DBUS (dbus)), ==,
|
||||
WP_DBUS_STATE_CLOSED);
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc, gchar *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
wp_init (WP_INIT_ALL);
|
||||
|
||||
g_test_add ("/wp/dbus/basic", TestFixture, NULL,
|
||||
test_dbus_setup, test_dbus_basic, test_dbus_teardown);
|
||||
g_test_add ("/wp/dbus/activation", TestFixture, NULL,
|
||||
test_dbus_setup, test_dbus_activation, test_dbus_teardown);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
|
@ -91,6 +91,14 @@ test(
|
|||
env: common_env,
|
||||
)
|
||||
|
||||
test(
|
||||
'test-dbus',
|
||||
executable('test-dbus', 'dbus.c',
|
||||
dependencies: common_deps, c_args: common_args),
|
||||
env: common_env,
|
||||
)
|
||||
|
||||
|
||||
test(
|
||||
'test-transition',
|
||||
executable('test-transition', 'transition.c',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue