From 8887cce5b410d750395a1d810fc2be6439b64f47 Mon Sep 17 00:00:00 2001
From: Julian Bouzas <julian.bouzas@collabora.com>
Date: Thu, 19 Mar 2020 12:30:28 -0400
Subject: [PATCH] modules: add monitor reservation data API

---
 modules/meson.build                       |   1 +
 modules/module-monitor/reservation-data.c | 450 ++++++++++++++++++++++
 modules/module-monitor/reservation-data.h |  54 +++
 3 files changed, 505 insertions(+)
 create mode 100644 modules/module-monitor/reservation-data.c
 create mode 100644 modules/module-monitor/reservation-data.h

diff --git a/modules/meson.build b/modules/meson.build
index e12e01ba..3b3fabd3 100644
--- a/modules/meson.build
+++ b/modules/meson.build
@@ -24,6 +24,7 @@ shared_library(
   'wireplumber-module-monitor',
   [
     'module-monitor.c',
+    'module-monitor/reservation-data.c',
     'module-monitor/dbus-device-reservation.c',
     reserve_device_interface_src,
   ],
diff --git a/modules/module-monitor/reservation-data.c b/modules/module-monitor/reservation-data.c
new file mode 100644
index 00000000..26883909
--- /dev/null
+++ b/modules/module-monitor/reservation-data.c
@@ -0,0 +1,450 @@
+/* WirePlumber
+ *
+ * Copyright © 2020 Collabora Ltd.
+ *    @author Julian Bouzas <julian.bouzas@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "reservation-data.h"
+
+#include <spa/pod/builder.h>
+#include <pipewire/pipewire.h>
+
+struct _WpMonitorDeviceReservationData
+{
+  GObject parent;
+
+  /* Props */
+  GWeakRef device;
+  WpMonitorDbusDeviceReservation *reservation;
+
+  guint n_acquired;
+};
+
+enum {
+  DEVICE_PROP_0,
+  DEVICE_PROP_DEVICE,
+  DEVICE_PROP_RESERVATION,
+};
+
+G_DEFINE_TYPE (WpMonitorDeviceReservationData,
+    wp_monitor_device_reservation_data, G_TYPE_OBJECT)
+
+static void
+on_device_done (WpCore *core, GAsyncResult *res,
+    WpMonitorDeviceReservationData *self)
+{
+  if (self->reservation)
+    wp_monitor_dbus_device_reservation_complete_release (self->reservation, TRUE);
+}
+
+static void
+on_reservation_acquired (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+  WpMonitorDeviceReservationData *self = user_data;
+  WpMonitorDbusDeviceReservation *reserv =
+      WP_MONITOR_DBUS_DEVICE_RESERVATION (obj);
+  g_autoptr (GError) error = NULL;
+  g_autoptr (WpProxy) device = NULL;
+  char buf[1024];
+  struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof(buf));
+
+  /* Finish */
+  if (!wp_monitor_dbus_device_reservation_async_finish (reserv, res, &error)) {
+    g_warning ("%s", error->message);
+    return;
+  }
+
+  /* Get the device */
+  device = g_weak_ref_get (&self->device);
+  if (!device)
+    return;
+
+  /* Set profile 1 */
+  wp_proxy_set_param (device, SPA_PARAM_Profile, 0,
+      spa_pod_builder_add_object(&b,
+          SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
+          SPA_PARAM_PROFILE_index, SPA_POD_Int(1)));
+}
+
+static void
+on_reservation_release (WpMonitorDbusDeviceReservation *reservation, int forced,
+    WpMonitorDeviceReservationData *self)
+{
+  g_autoptr (WpProxy) device = NULL;
+  g_autoptr (WpCore) core = NULL;
+  char buf[1024];
+  struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof(buf));
+
+  /* Get the device and core */
+  device = g_weak_ref_get (&self->device);
+  if (!device)
+    return;
+  core = wp_proxy_get_core (device);
+  if (!core)
+    return;
+
+  /* Set profile 0 */
+  wp_proxy_set_param (device, SPA_PARAM_Profile, 0,
+      spa_pod_builder_add_object(&b,
+          SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
+          SPA_PARAM_PROFILE_index, SPA_POD_Int(0)));
+
+  /* Complete release on done */
+  wp_core_sync (core, NULL, (GAsyncReadyCallback)on_device_done, self);
+}
+
+static void
+on_device_destroyed (WpProxy *device, WpMonitorDeviceReservationData *self)
+{
+  wp_monitor_dbus_device_reservation_release (self->reservation);
+}
+
+static void
+wp_monitor_device_reservation_data_constructed (GObject * object)
+{
+  WpMonitorDeviceReservationData *self =
+      WP_MONITOR_DEVICE_RESERVATION_DATA (object);
+  g_autoptr (WpProxy) device = g_weak_ref_get (&self->device);
+
+  /* Make sure the device is released when the pw proxy device is destroyed */
+  g_return_if_fail (device);
+  g_signal_connect_object (device, "pw-proxy-destroyed",
+      (GCallback) on_device_destroyed, self, 0);
+
+  /* Handle the reservation signals */
+  g_return_if_fail (self->reservation);
+  g_signal_connect_object (self->reservation, "release",
+    (GCallback) on_reservation_release, self, 0);
+
+  /* Try to acquire the device */
+  wp_monitor_dbus_device_reservation_acquire (self->reservation, NULL,
+     on_reservation_acquired, self);
+
+  G_OBJECT_CLASS (wp_monitor_device_reservation_data_parent_class)->constructed (object);
+}
+
+static void
+wp_monitor_device_reservation_data_get_property (GObject * object,
+    guint property_id, GValue * value, GParamSpec * pspec)
+{
+  WpMonitorDeviceReservationData *self =
+      WP_MONITOR_DEVICE_RESERVATION_DATA (object);
+
+  switch (property_id) {
+  case DEVICE_PROP_DEVICE:
+    g_value_take_object (value, g_weak_ref_get (&self->device));
+    break;
+  case DEVICE_PROP_RESERVATION:
+    g_value_set_object (value, self->reservation);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+wp_monitor_device_reservation_data_set_property (GObject * object,
+    guint property_id, const GValue * value, GParamSpec * pspec)
+{
+  WpMonitorDeviceReservationData *self =
+      WP_MONITOR_DEVICE_RESERVATION_DATA (object);
+
+  switch (property_id) {
+  case DEVICE_PROP_DEVICE:
+    g_weak_ref_set (&self->device, g_value_get_object (value));
+    break;
+  case DEVICE_PROP_RESERVATION:
+    self->reservation = g_value_dup_object (value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+wp_monitor_device_reservation_data_finalize (GObject * object)
+{
+  WpMonitorDeviceReservationData *self =
+      WP_MONITOR_DEVICE_RESERVATION_DATA (object);
+
+  wp_monitor_dbus_device_reservation_release (self->reservation);
+
+  /* Props */
+  g_weak_ref_clear (&self->device);
+  g_clear_object (&self->reservation);
+
+  G_OBJECT_CLASS (wp_monitor_device_reservation_data_parent_class)->finalize (object);
+}
+
+static void
+wp_monitor_device_reservation_data_init (WpMonitorDeviceReservationData * self)
+{
+  /* Props */
+  g_weak_ref_init (&self->device, NULL);
+
+  self->n_acquired = 0;
+}
+
+static void
+wp_monitor_device_reservation_data_class_init (
+    WpMonitorDeviceReservationDataClass * klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->constructed = wp_monitor_device_reservation_data_constructed;
+  object_class->get_property = wp_monitor_device_reservation_data_get_property;
+  object_class->set_property = wp_monitor_device_reservation_data_set_property;
+  object_class->finalize = wp_monitor_device_reservation_data_finalize;
+
+  /* Props */
+  g_object_class_install_property (object_class, DEVICE_PROP_DEVICE,
+      g_param_spec_object ("device", "device", "The device", WP_TYPE_PROXY,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, DEVICE_PROP_RESERVATION,
+      g_param_spec_object ("reservation", "reservation",
+      "The dbus device reservation", WP_TYPE_MONITOR_DBUS_DEVICE_RESERVATION,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+WpMonitorDeviceReservationData *
+wp_monitor_device_reservation_data_new (WpProxy *device,
+    WpMonitorDbusDeviceReservation *reservation)
+{
+  return g_object_new (WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA,
+      "device", device,
+      "reservation", reservation,
+      NULL);
+}
+
+void
+wp_monitor_device_reservation_data_acquire (
+    WpMonitorDeviceReservationData *self)
+{
+  g_return_if_fail (WP_IS_MONITOR_DEVICE_RESERVATION_DATA (self));
+  g_return_if_fail (self->reservation);
+
+  if (self->n_acquired == 0)
+    wp_monitor_dbus_device_reservation_acquire (self->reservation, NULL,
+        on_reservation_acquired, self);
+
+  self->n_acquired++;
+}
+
+void
+wp_monitor_device_reservation_data_release (
+    WpMonitorDeviceReservationData *self)
+{
+  g_return_if_fail (WP_IS_MONITOR_DEVICE_RESERVATION_DATA (self));
+  g_return_if_fail (self->reservation);
+
+  if (self->n_acquired == 1)
+    wp_monitor_dbus_device_reservation_release (self->reservation);
+
+  self->n_acquired--;
+}
+
+
+struct _WpMonitorNodeReservationData
+{
+  GObject parent;
+
+  /* Props */
+  GWeakRef node;
+  WpMonitorDeviceReservationData *device_data;
+
+  gboolean acquired;
+  GSource *timeout_source;
+};
+
+enum {
+  NODE_PROP_0,
+  NODE_PROP_NODE,
+  NODE_PROP_DEVICE_DATA,
+};
+
+G_DEFINE_TYPE (WpMonitorNodeReservationData,
+    wp_monitor_node_reservation_data, G_TYPE_OBJECT)
+
+static void
+on_node_destroyed (WpProxy *node, WpMonitorNodeReservationData *self)
+{
+  if (self->acquired)
+    wp_monitor_device_reservation_data_release (self->device_data);
+}
+
+static void
+wp_monitor_node_reservation_data_constructed (GObject * object)
+{
+  WpMonitorNodeReservationData *self =
+      WP_MONITOR_NODE_RESERVATION_DATA (object);
+  WpProxy *node = g_weak_ref_get (&self->node);
+
+  g_return_if_fail (node);
+
+  /* Make sure the device is released when the pw proxy node is destroyed */
+  g_signal_connect_object (node, "pw-proxy-destroyed",
+      (GCallback) on_node_destroyed, self, 0);
+
+  G_OBJECT_CLASS (wp_monitor_node_reservation_data_parent_class)->constructed (object);
+}
+
+static void
+wp_monitor_node_reservation_data_get_property (GObject * object,
+    guint property_id, GValue * value, GParamSpec * pspec)
+{
+  WpMonitorNodeReservationData *self =
+      WP_MONITOR_NODE_RESERVATION_DATA (object);
+
+  switch (property_id) {
+  case NODE_PROP_NODE:
+    g_value_take_object (value, g_weak_ref_get (&self->node));
+    break;
+  case NODE_PROP_DEVICE_DATA:
+    g_value_set_object (value, self->device_data);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+wp_monitor_node_reservation_data_set_property (GObject * object,
+    guint property_id, const GValue * value, GParamSpec * pspec)
+{
+  WpMonitorNodeReservationData *self =
+      WP_MONITOR_NODE_RESERVATION_DATA (object);
+
+  switch (property_id) {
+  case NODE_PROP_NODE:
+    g_weak_ref_set (&self->node, g_value_get_object (value));
+    break;
+  case NODE_PROP_DEVICE_DATA:
+    self->device_data = g_value_dup_object (value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    break;
+  }
+}
+
+static void
+wp_monitor_node_reservation_data_finalize (GObject * object)
+{
+  WpMonitorNodeReservationData *self =
+      WP_MONITOR_NODE_RESERVATION_DATA (object);
+
+  /* Clear the current timeout release callback */
+  if (self->timeout_source)
+      g_source_destroy (self->timeout_source);
+  g_clear_pointer (&self->timeout_source, g_source_unref);
+
+  /* Release device if acquired */
+  if (self->acquired)
+    wp_monitor_device_reservation_data_release (self->device_data);
+
+  /* Props */
+  g_weak_ref_clear (&self->node);
+  g_clear_object (&self->device_data);
+
+  G_OBJECT_CLASS (wp_monitor_node_reservation_data_parent_class)->finalize (object);
+}
+
+static void
+wp_monitor_node_reservation_data_init (WpMonitorNodeReservationData * self)
+{
+  /* Props */
+  g_weak_ref_init (&self->node, NULL);
+
+  self->acquired = FALSE;
+  self->timeout_source = NULL;
+}
+
+static void
+wp_monitor_node_reservation_data_class_init (
+    WpMonitorNodeReservationDataClass * klass)
+{
+  GObjectClass *object_class = (GObjectClass *) klass;
+
+  object_class->constructed = wp_monitor_node_reservation_data_constructed;
+  object_class->get_property = wp_monitor_node_reservation_data_get_property;
+  object_class->set_property = wp_monitor_node_reservation_data_set_property;
+  object_class->finalize = wp_monitor_node_reservation_data_finalize;
+
+  /* Props */
+  g_object_class_install_property (object_class, NODE_PROP_NODE,
+      g_param_spec_object ("node", "node", "The node", WP_TYPE_PROXY,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, NODE_PROP_DEVICE_DATA,
+      g_param_spec_object ("device-data", "device-data",
+      "The monitor device reservation data", WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA,
+      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+WpMonitorNodeReservationData *
+wp_monitor_node_reservation_data_new (WpProxy *node,
+    WpMonitorDeviceReservationData *device_data)
+{
+  return g_object_new (WP_TYPE_MONITOR_NODE_RESERVATION_DATA,
+      "node", node,
+      "device-data", device_data,
+      NULL);
+}
+
+static gboolean
+timeout_release_callback (gpointer data)
+{
+  WpMonitorNodeReservationData *self = data;
+  g_return_val_if_fail (self, G_SOURCE_REMOVE);
+
+  wp_monitor_device_reservation_data_release (self->device_data);
+  self->acquired = FALSE;
+  return G_SOURCE_REMOVE;
+}
+
+void
+wp_monitor_node_reservation_data_timeout_release (
+    WpMonitorNodeReservationData *self, guint64 timeout_ms)
+{
+  g_autoptr (WpProxy) node = NULL;
+  g_autoptr (WpCore) core = NULL;
+  g_return_if_fail (WP_IS_MONITOR_NODE_RESERVATION_DATA (self));
+
+  node = g_weak_ref_get (&self->node);
+  g_return_if_fail (node);
+  core = wp_proxy_get_core (node);
+  g_return_if_fail (core);
+
+  /* Clear the current timeout release callback */
+  if (self->timeout_source)
+      g_source_destroy (self->timeout_source);
+  g_clear_pointer (&self->timeout_source, g_source_unref);
+
+  /* Add new timeout release callback */
+  wp_core_timeout_add (core, &self->timeout_source, timeout_ms,
+        timeout_release_callback, g_object_ref (self), g_object_unref);
+}
+
+void
+wp_monitor_node_reservation_data_acquire (WpMonitorNodeReservationData *self)
+{
+  g_return_if_fail (WP_IS_MONITOR_NODE_RESERVATION_DATA (self));
+
+  /* Clear the current timeout release callback */
+  if (self->timeout_source)
+      g_source_destroy (self->timeout_source);
+  g_clear_pointer (&self->timeout_source, g_source_unref);
+
+  /* Don't do anything if already acquired */
+  if (self->acquired)
+    return;
+
+  /* Acquire the device */
+  wp_monitor_device_reservation_data_acquire (self->device_data);
+  self->acquired = TRUE;
+}
+
diff --git a/modules/module-monitor/reservation-data.h b/modules/module-monitor/reservation-data.h
new file mode 100644
index 00000000..fc70e37f
--- /dev/null
+++ b/modules/module-monitor/reservation-data.h
@@ -0,0 +1,54 @@
+/* WirePlumber
+ *
+ * Copyright © 2020 Collabora Ltd.
+ *    @author Julian Bouzas <julian.bouzas@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef __WIREPLUMBER_MONITOR_RESERVATION_DATA_H__
+#define __WIREPLUMBER_MONITOR_RESERVATION_DATA_H__
+
+#include <wp/wp.h>
+
+#include "dbus-device-reservation.h"
+
+G_BEGIN_DECLS
+
+#define WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA (wp_monitor_device_reservation_data_get_type ())
+
+G_DECLARE_FINAL_TYPE (WpMonitorDeviceReservationData,
+    wp_monitor_device_reservation_data, WP, MONITOR_DEVICE_RESERVATION_DATA,
+    GObject)
+
+WpMonitorDeviceReservationData * wp_monitor_device_reservation_data_new (
+    WpProxy *device, WpMonitorDbusDeviceReservation *reservation);
+
+void
+wp_monitor_device_reservation_data_acquire (
+    WpMonitorDeviceReservationData *self);
+
+void
+wp_monitor_device_reservation_data_release (
+    WpMonitorDeviceReservationData *self);
+
+
+#define WP_TYPE_MONITOR_NODE_RESERVATION_DATA (wp_monitor_node_reservation_data_get_type ())
+
+G_DECLARE_FINAL_TYPE (WpMonitorNodeReservationData,
+    wp_monitor_node_reservation_data, WP, MONITOR_NODE_RESERVATION_DATA,
+    GObject)
+
+WpMonitorNodeReservationData * wp_monitor_node_reservation_data_new (
+    WpProxy *node, WpMonitorDeviceReservationData *device_data);
+
+void
+wp_monitor_node_reservation_data_timeout_release (
+    WpMonitorNodeReservationData *self, guint64 timeout_ms);
+
+void
+wp_monitor_node_reservation_data_acquire (WpMonitorNodeReservationData *self);
+
+G_END_DECLS
+
+#endif
-- 
GitLab