522 lines
16 KiB
C
522 lines
16 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2020 Collabora Ltd.
|
|
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/* Generated with gdbus-codegen */
|
|
#include "reserve-device-interface.h"
|
|
|
|
#include <pipewire/pipewire.h>
|
|
|
|
#include "dbus-device-reservation.h"
|
|
|
|
#define DEVICE_RESERVATION_SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
|
|
#define DEVICE_RESERVATION_OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
|
|
|
|
struct _WpMonitorDbusDeviceReservation
|
|
{
|
|
GObject parent;
|
|
|
|
/* Props */
|
|
gint card_id;
|
|
char *application_name;
|
|
gint priority;
|
|
char *app_dev_name;
|
|
|
|
char *service_name;
|
|
char *object_path;
|
|
GDBusConnection *connection;
|
|
guint owner_id;
|
|
guint registered_id;
|
|
GDBusMethodInvocation *pending_release;
|
|
|
|
GTask *pending_task;
|
|
char *pending_property_name;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CARD_ID,
|
|
PROP_APPLICATION_NAME,
|
|
PROP_PRIORITY,
|
|
PROP_APP_DEV_NAME,
|
|
};
|
|
|
|
enum
|
|
{
|
|
SIGNAL_RELEASE,
|
|
SIGNAL_LAST,
|
|
};
|
|
|
|
static guint device_reservation_signals[SIGNAL_LAST] = { 0 };
|
|
|
|
G_DEFINE_TYPE (WpMonitorDbusDeviceReservation,
|
|
wp_monitor_dbus_device_reservation, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
handle_method_call (GDBusConnection *connection, const char *sender,
|
|
const char *object_path, const char *interface_name,
|
|
const char *method_name, GVariant *parameters,
|
|
GDBusMethodInvocation *invocation, gpointer data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = data;
|
|
|
|
if (g_strcmp0 (method_name, "RequestRelease") == 0) {
|
|
gint priority;
|
|
g_variant_get (parameters, "(i)", &priority);
|
|
|
|
if (priority > self->priority) {
|
|
if (self->pending_release)
|
|
wp_monitor_dbus_device_reservation_complete_release (self, FALSE);
|
|
self->pending_release = g_object_ref (invocation);
|
|
g_signal_emit (self, device_reservation_signals[SIGNAL_RELEASE], 0, 0);
|
|
} else {
|
|
wp_monitor_dbus_device_reservation_complete_release (self, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GVariant *
|
|
handle_get_property (GDBusConnection *connection, const char *sender,
|
|
const char *object_path, const char *interface_name,
|
|
const char *property, GError **error, gpointer data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = data;
|
|
GVariant *ret = NULL;
|
|
|
|
if (g_strcmp0 (property, "ApplicationName") == 0)
|
|
ret = g_variant_new_string (self->application_name ? self->application_name : "");
|
|
else if (g_strcmp0 (property, "ApplicationDeviceName") == 0)
|
|
ret = g_variant_new_string (self->app_dev_name ? self->app_dev_name : "");
|
|
else if (g_strcmp0 (property, "Priority") == 0)
|
|
ret = g_variant_new_int32 (self->priority);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
on_bus_acquired (GDBusConnection *connection, const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = user_data;
|
|
g_autoptr (GError) error = NULL;
|
|
static const GDBusInterfaceVTable interface_vtable = {
|
|
handle_method_call,
|
|
handle_get_property,
|
|
NULL, /* Don't allow setting a property */
|
|
};
|
|
|
|
g_debug ("WpMonitorDbusDeviceReservation:%p bus acquired", self);
|
|
|
|
self->registered_id = g_dbus_connection_register_object (connection,
|
|
self->object_path,
|
|
wp_monitor_org_freedesktop_reserve_device1_interface_info (),
|
|
&interface_vtable,
|
|
g_object_ref (self),
|
|
g_object_unref,
|
|
&error);
|
|
g_return_if_fail (!error);
|
|
g_return_if_fail (self->registered_id > 0);
|
|
}
|
|
|
|
static void
|
|
on_name_acquired (GDBusConnection *connection, const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = user_data;
|
|
g_debug ("WpMonitorDbusDeviceReservation:%p name acquired", self);
|
|
|
|
self->connection = connection;
|
|
|
|
/* Trigger the acquired task */
|
|
if (self->pending_task) {
|
|
g_task_return_pointer (self->pending_task, GUINT_TO_POINTER (TRUE), NULL);
|
|
g_clear_object (&self->pending_task);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_unregister_object (
|
|
WpMonitorDbusDeviceReservation *self)
|
|
{
|
|
if (self->connection && self->registered_id > 0) {
|
|
g_dbus_connection_unregister_object (self->connection, self->registered_id);
|
|
self->registered_id = 0;
|
|
self->connection = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_name_lost (GDBusConnection *connection, const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = user_data;
|
|
g_debug ("WpMonitorDbusDeviceReservation:%p name lost\n", self);
|
|
|
|
self->connection = connection;
|
|
|
|
/* Unregister object */
|
|
wp_monitor_dbus_device_reservation_unregister_object (self);
|
|
|
|
/* Trigger the acquired task */
|
|
if (self->pending_task) {
|
|
GError *error = g_error_new (WP_DOMAIN_LIBRARY,
|
|
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
|
"dbus name lost before acquiring (connection=%p)", connection);
|
|
g_task_return_error (self->pending_task, error);
|
|
g_clear_object (&self->pending_task);
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_constructed (GObject * object)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self =
|
|
WP_MONITOR_DBUS_DEVICE_RESERVATION (object);
|
|
|
|
/* Set service name and object path */
|
|
self->service_name = g_strdup_printf (DEVICE_RESERVATION_SERVICE_PREFIX
|
|
"Audio%d", self->card_id);
|
|
self->object_path = g_strdup_printf (DEVICE_RESERVATION_OBJECT_PREFIX
|
|
"Audio%d", self->card_id);
|
|
|
|
G_OBJECT_CLASS (
|
|
wp_monitor_dbus_device_reservation_parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_get_property (GObject * object,
|
|
guint property_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self =
|
|
WP_MONITOR_DBUS_DEVICE_RESERVATION (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_CARD_ID:
|
|
g_value_set_int (value, self->card_id);
|
|
break;
|
|
case PROP_APPLICATION_NAME:
|
|
g_value_set_string (value, self->application_name);
|
|
break;
|
|
case PROP_PRIORITY:
|
|
g_value_set_int (value, self->priority);
|
|
break;
|
|
case PROP_APP_DEV_NAME:
|
|
g_value_set_string (value, self->app_dev_name);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_set_property (GObject * object,
|
|
guint property_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self =
|
|
WP_MONITOR_DBUS_DEVICE_RESERVATION (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_CARD_ID:
|
|
self->card_id = g_value_get_int (value);
|
|
break;
|
|
case PROP_APPLICATION_NAME:
|
|
g_clear_pointer (&self->application_name, g_free);
|
|
self->application_name = g_value_dup_string (value);
|
|
break;
|
|
case PROP_PRIORITY:
|
|
self->priority = g_value_get_int(value);
|
|
break;
|
|
case PROP_APP_DEV_NAME:
|
|
g_clear_pointer (&self->app_dev_name, g_free);
|
|
self->app_dev_name = g_value_dup_string (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_finalize (GObject * object)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self =
|
|
WP_MONITOR_DBUS_DEVICE_RESERVATION (object);
|
|
|
|
/* Finish pending task */
|
|
if (self->pending_task) {
|
|
GError *error = g_error_new (WP_DOMAIN_LIBRARY,
|
|
WP_LIBRARY_ERROR_OPERATION_FAILED, "finishing before task is done");
|
|
g_task_return_error (self->pending_task, error);
|
|
}
|
|
g_clear_object (&self->pending_task);
|
|
g_clear_pointer (&self->pending_property_name, g_free);
|
|
|
|
/* Unregister and release */
|
|
wp_monitor_dbus_device_reservation_unregister_object (self);
|
|
wp_monitor_dbus_device_reservation_release (self);
|
|
|
|
g_clear_object (&self->pending_release);
|
|
g_clear_pointer (&self->service_name, g_free);
|
|
g_clear_pointer (&self->object_path, g_free);
|
|
|
|
/* Props */
|
|
g_clear_pointer (&self->application_name, g_free);
|
|
g_clear_pointer (&self->app_dev_name, g_free);
|
|
|
|
G_OBJECT_CLASS (
|
|
wp_monitor_dbus_device_reservation_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_init (WpMonitorDbusDeviceReservation * self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
wp_monitor_dbus_device_reservation_class_init (
|
|
WpMonitorDbusDeviceReservationClass * klass)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
|
|
object_class->constructed = wp_monitor_dbus_device_reservation_constructed;
|
|
object_class->get_property = wp_monitor_dbus_device_reservation_get_property;
|
|
object_class->set_property = wp_monitor_dbus_device_reservation_set_property;
|
|
object_class->finalize = wp_monitor_dbus_device_reservation_finalize;
|
|
|
|
/* Properties */
|
|
g_object_class_install_property (object_class, PROP_CARD_ID,
|
|
g_param_spec_int ("card-id", "card-id",
|
|
"The card Id", G_MININT, G_MAXINT, -1,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class, PROP_APPLICATION_NAME,
|
|
g_param_spec_string ("application-name", "application-name",
|
|
"The application name", NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class, PROP_PRIORITY,
|
|
g_param_spec_int ("priority", "priority",
|
|
"The priority", G_MININT, G_MAXINT, 0,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class, PROP_APP_DEV_NAME,
|
|
g_param_spec_string ("app-dev-name", "app-dev-name",
|
|
"The application device name", NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
|
|
/* Signals */
|
|
device_reservation_signals[SIGNAL_RELEASE] = g_signal_new (
|
|
"release", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
|
|
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
|
|
}
|
|
|
|
WpMonitorDbusDeviceReservation *
|
|
wp_monitor_dbus_device_reservation_new (gint card_id,
|
|
const char *application_name, gint priority, const char *app_dev_name)
|
|
{
|
|
return g_object_new (WP_TYPE_MONITOR_DBUS_DEVICE_RESERVATION,
|
|
"card-id", card_id,
|
|
"application-name", application_name,
|
|
"priority", priority,
|
|
"app-dev-name", app_dev_name,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
wp_monitor_dbus_device_reservation_release (
|
|
WpMonitorDbusDeviceReservation *self)
|
|
{
|
|
g_return_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self));
|
|
if (self->owner_id == 0)
|
|
return;
|
|
|
|
/* Release */
|
|
g_bus_unown_name (self->owner_id);
|
|
self->owner_id = 0;
|
|
}
|
|
|
|
void
|
|
wp_monitor_dbus_device_reservation_complete_release (
|
|
WpMonitorDbusDeviceReservation *self, gboolean res)
|
|
{
|
|
g_return_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self));
|
|
|
|
if (!self->pending_release)
|
|
return;
|
|
|
|
g_dbus_method_invocation_return_value (self->pending_release,
|
|
g_variant_new ("(b)", g_variant_new_boolean (res)));
|
|
g_clear_object (&self->pending_release);
|
|
}
|
|
|
|
static void
|
|
on_unowned (gpointer user_data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = user_data;
|
|
wp_monitor_dbus_device_reservation_unregister_object (self);
|
|
g_object_unref (self);
|
|
}
|
|
|
|
gboolean
|
|
wp_monitor_dbus_device_reservation_acquire (
|
|
WpMonitorDbusDeviceReservation *self, GCancellable *cancellable,
|
|
GAsyncReadyCallback callback, gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE);
|
|
g_return_val_if_fail (!self->pending_task, FALSE);
|
|
if (self->owner_id > 0)
|
|
return FALSE;
|
|
|
|
/* Set the new task */
|
|
self->pending_task = g_task_new (self, cancellable, callback, user_data);
|
|
|
|
/* Aquire */
|
|
self->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, self->service_name,
|
|
self->priority < INT32_MAX ?
|
|
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT : G_BUS_NAME_OWNER_FLAGS_NONE,
|
|
on_bus_acquired, on_name_acquired, on_name_lost, g_object_ref (self),
|
|
on_unowned);
|
|
g_return_val_if_fail (self->owner_id > 0, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_request_release_done (GObject *proxy, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = user_data;
|
|
g_autoptr (GError) error = NULL;
|
|
gboolean ret;
|
|
|
|
/* Finish */
|
|
wp_monitor_org_freedesktop_reserve_device1_call_request_release_finish (
|
|
WP_MONITOR_ORG_FREEDESKTOP_RESERVE_DEVICE1 (proxy),
|
|
&ret, res, &error);
|
|
|
|
/* Return */
|
|
g_return_if_fail (self->pending_task);
|
|
if (error)
|
|
g_task_return_error (self->pending_task, g_steal_pointer (&error));
|
|
else
|
|
g_task_return_pointer (self->pending_task, GUINT_TO_POINTER (ret), NULL);
|
|
g_clear_object (&self->pending_task);
|
|
}
|
|
|
|
static void
|
|
on_proxy_done_request_release (GObject *obj, GAsyncResult *res, gpointer data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = data;
|
|
g_autoptr (WpMonitorOrgFreedesktopReserveDevice1) proxy = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
/* Finish */
|
|
proxy = wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus_finish (
|
|
res, &error);
|
|
|
|
/* Check for errors */
|
|
g_return_if_fail (self->pending_task);
|
|
if (error) {
|
|
g_task_return_error (self->pending_task, g_steal_pointer (&error));
|
|
g_clear_object (&self->pending_task);
|
|
return;
|
|
}
|
|
|
|
/* Request release */
|
|
g_return_if_fail (proxy);
|
|
wp_monitor_org_freedesktop_reserve_device1_call_request_release (proxy,
|
|
self->priority, NULL, on_request_release_done, self);
|
|
}
|
|
|
|
gboolean
|
|
wp_monitor_dbus_device_reservation_request_release (
|
|
WpMonitorDbusDeviceReservation *self, GCancellable *cancellable,
|
|
GAsyncReadyCallback callback, gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE);
|
|
g_return_val_if_fail (!self->pending_task, FALSE);
|
|
|
|
/* Set the new task */
|
|
self->pending_task = g_task_new (self, cancellable, callback, user_data);
|
|
|
|
/* Get the proxy */
|
|
wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus (
|
|
G_BUS_TYPE_SESSION, G_BUS_NAME_OWNER_FLAGS_NONE, self->service_name,
|
|
self->object_path, NULL, on_proxy_done_request_release, self);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_proxy_done_request_property (GObject *obj, GAsyncResult *res, gpointer data)
|
|
{
|
|
WpMonitorDbusDeviceReservation *self = data;
|
|
g_autoptr (WpMonitorOrgFreedesktopReserveDevice1) proxy = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
/* Finish */
|
|
proxy = wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus_finish (
|
|
res, &error);
|
|
|
|
/* Check for errors */
|
|
g_return_if_fail (self->pending_task);
|
|
if (error) {
|
|
g_task_return_error (self->pending_task, g_steal_pointer (&error));
|
|
g_clear_object (&self->pending_task);
|
|
return;
|
|
}
|
|
|
|
/* Request the property */
|
|
g_return_if_fail (proxy);
|
|
g_return_if_fail (self->pending_property_name);
|
|
|
|
if (g_strcmp0 (self->pending_property_name, "ApplicationName") == 0) {
|
|
char *v = wp_monitor_org_freedesktop_reserve_device1_dup_application_name (proxy);
|
|
g_task_return_pointer (self->pending_task, v, g_free);
|
|
}
|
|
else if (g_strcmp0 (self->pending_property_name, "ApplicationDeviceName") == 0) {
|
|
char *v = wp_monitor_org_freedesktop_reserve_device1_dup_application_device_name (proxy);
|
|
g_task_return_pointer (self->pending_task, v, g_free);
|
|
}
|
|
else if (g_strcmp0 (self->pending_property_name, "Priority") == 0) {
|
|
gint v = wp_monitor_org_freedesktop_reserve_device1_get_priority (proxy);
|
|
g_task_return_pointer (self->pending_task, GINT_TO_POINTER (v), NULL);
|
|
}
|
|
else {
|
|
GError *error = g_error_new (WP_DOMAIN_LIBRARY,
|
|
WP_LIBRARY_ERROR_OPERATION_FAILED, "invalid property '%s' on proxy",
|
|
self->pending_property_name);
|
|
g_task_return_error (self->pending_task, error);
|
|
}
|
|
g_clear_object (&self->pending_task);
|
|
}
|
|
|
|
gboolean
|
|
wp_monitor_dbus_device_reservation_request_property (
|
|
WpMonitorDbusDeviceReservation *self, const char *name,
|
|
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE);
|
|
g_return_val_if_fail (!self->pending_task, FALSE);
|
|
|
|
/* Set the new task and property name */
|
|
self->pending_task = g_task_new (self, cancellable, callback, user_data);
|
|
g_clear_pointer (&self->pending_property_name, g_free);
|
|
self->pending_property_name = g_strdup (name);
|
|
|
|
/* Get the proxy */
|
|
wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus (
|
|
G_BUS_TYPE_SESSION, G_BUS_NAME_OWNER_FLAGS_NONE, self->service_name,
|
|
self->object_path, NULL, on_proxy_done_request_property, self);
|
|
return TRUE;
|
|
}
|
|
|
|
gpointer
|
|
wp_monitor_dbus_device_reservation_async_finish (
|
|
WpMonitorDbusDeviceReservation *self, GAsyncResult * res, GError ** error)
|
|
{
|
|
g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE);
|
|
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
|
|
|
|
return g_task_propagate_pointer (G_TASK (res), error);
|
|
}
|