From e3ac60c0c08452619f712e6da8105ae1f217981c Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Thu, 12 Sep 2019 16:57:37 +0300
Subject: [PATCH] modules: implement a new module-monitor

This is a generic WpMonitor loader that sets up the WpMonitor
properties from the module arguments and applies some well-known
properties to the device & node objects
---
 modules/meson.build      |  11 ++
 modules/module-monitor.c | 250 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 261 insertions(+)
 create mode 100644 modules/module-monitor.c

diff --git a/modules/meson.build b/modules/meson.build
index 5edfb10a..79448d4d 100644
--- a/modules/meson.build
+++ b/modules/meson.build
@@ -25,6 +25,17 @@ shared_library(
   dependencies : [wp_dep],
 )
 
+shared_library(
+  'wireplumber-module-monitor',
+  [
+    'module-monitor.c',
+  ],
+  c_args : [common_c_args, '-DG_LOG_DOMAIN="m-monitor"'],
+  install : true,
+  install_dir : wireplumber_module_dir,
+  dependencies : [wp_dep, pipewire_dep],
+)
+
 shared_library(
   'wireplumber-module-pipewire',
   [
diff --git a/modules/module-monitor.c b/modules/module-monitor.c
new file mode 100644
index 00000000..d80bcf70
--- /dev/null
+++ b/modules/module-monitor.c
@@ -0,0 +1,250 @@
+/* WirePlumber
+ *
+ * Copyright © 2019 Wim Taymans
+ * Copyright © 2019 Collabora Ltd.
+ *    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <wp/wp.h>
+
+#include <spa/monitor/monitor.h>
+#include <pipewire/pipewire.h>
+#include <spa/utils/keys.h>
+
+static void
+setup_device_props (WpMonitor *self, WpProperties *p, WpModule *module)
+{
+  const gchar *s, *d, *api;
+
+  api = wp_properties_get (p, SPA_KEY_DEVICE_API);
+
+  /* set the device name if it's not already set */
+  if (!wp_properties_get (p, SPA_KEY_DEVICE_NAME)) {
+    if ((s = wp_properties_get (p, SPA_KEY_DEVICE_BUS_ID)) == NULL) {
+      if ((s = wp_properties_get (p, SPA_KEY_DEVICE_BUS_PATH)) == NULL) {
+        s = wp_properties_get (p, WP_MONITOR_KEY_OBJECT_ID);
+      }
+    }
+
+    if (!g_strcmp0 (api, "alsa")) {
+      /* what we call a "device" in pipewire, in alsa it's a "card",
+         so make it clear to avoid confusion */
+      wp_properties_setf (p, PW_KEY_DEVICE_NAME, "alsa_card.%s", s);
+    } else {
+      wp_properties_setf (p, PW_KEY_DEVICE_NAME, "%s_device.%s", api, s);
+    }
+  }
+
+  /* set the device description if it's not already set */
+  if (!wp_properties_get (p, PW_KEY_DEVICE_DESCRIPTION)) {
+    d = NULL;
+
+    if (!g_strcmp0 (api, "alsa")) {
+      if ((s = wp_properties_get (p, PW_KEY_DEVICE_FORM_FACTOR))
+            && !g_strcmp0 (s, "internal"))
+        d = "Built-in Audio";
+      else if ((s = wp_properties_get (p, PW_KEY_DEVICE_CLASS))
+            && !g_strcmp0 (s, "modem"))
+        d = "Modem";
+    }
+
+    if (!d)
+      d = wp_properties_get (p, PW_KEY_DEVICE_PRODUCT_NAME);
+    if (!d)
+      d = "Unknown device";
+
+    wp_properties_set (p, PW_KEY_DEVICE_DESCRIPTION, d);
+  }
+
+  /* set the icon name for ALSA - TODO for other APIs */
+  if (!wp_properties_get (p, PW_KEY_DEVICE_ICON_NAME)
+        && !g_strcmp0 (api, "alsa"))
+  {
+    d = NULL;
+
+    if ((s = wp_properties_get (p, PW_KEY_DEVICE_FORM_FACTOR))) {
+      if (!g_strcmp0 (s, "microphone"))
+        d = "audio-input-microphone";
+      else if (!g_strcmp0 (s, "webcam"))
+        d = "camera-web";
+      else if (!g_strcmp0 (s, "computer"))
+        d = "computer";
+      else if (!g_strcmp0 (s, "handset"))
+        d = "phone";
+      else if (!g_strcmp0 (s, "portable"))
+        d = "multimedia-player";
+      else if (!g_strcmp0 (s, "tv"))
+        d = "video-display";
+      else if (!g_strcmp0 (s, "headset"))
+        d = "audio-headset";
+      else if (!g_strcmp0 (s, "headphone"))
+        d = "audio-headphones";
+      else if (!g_strcmp0 (s, "speaker"))
+        d = "audio-speakers";
+      else if (!g_strcmp0 (s, "hands-free"))
+        d = "audio-handsfree";
+    }
+    if (!d)
+      if ((s = wp_properties_get (p, PW_KEY_DEVICE_CLASS))
+            && !g_strcmp0 (s, "modem"))
+          d = "modem";
+
+    if (!d)
+      d = "audio-card";
+
+    s = wp_properties_get (p, PW_KEY_DEVICE_BUS);
+
+    wp_properties_setf (p, PW_KEY_DEVICE_ICON_NAME,
+        "%s-analog%s%s", d, s ? "-" : "", s);
+  }
+}
+
+static void
+setup_node_props (WpMonitor *self, WpProperties *dev_props,
+    WpProperties *node_props, WpModule *module)
+{
+  const gchar *api, *name, *description, *factory;
+
+  /* Make the device properties directly available on the node */
+  wp_properties_copy_keys (dev_props, node_props,
+      SPA_KEY_DEVICE_API,
+      SPA_KEY_DEVICE_NAME,
+      SPA_KEY_DEVICE_ALIAS,
+      SPA_KEY_DEVICE_NICK,
+      SPA_KEY_DEVICE_DESCRIPTION,
+      SPA_KEY_DEVICE_ICON,
+      SPA_KEY_DEVICE_ICON_NAME,
+      SPA_KEY_DEVICE_PLUGGED_USEC,
+      SPA_KEY_DEVICE_BUS_ID,
+      SPA_KEY_DEVICE_BUS_PATH,
+      SPA_KEY_DEVICE_BUS,
+      SPA_KEY_DEVICE_SUBSYSTEM,
+      SPA_KEY_DEVICE_SYSFS_PATH,
+      SPA_KEY_DEVICE_VENDOR_ID,
+      SPA_KEY_DEVICE_VENDOR_NAME,
+      SPA_KEY_DEVICE_PRODUCT_ID,
+      SPA_KEY_DEVICE_PRODUCT_NAME,
+      SPA_KEY_DEVICE_SERIAL,
+      SPA_KEY_DEVICE_CLASS,
+      SPA_KEY_DEVICE_CAPABILITIES,
+      SPA_KEY_DEVICE_FORM_FACTOR,
+      PW_KEY_DEVICE_INTENDED_ROLES,
+      NULL);
+
+  /* get some strings that we are going to need below */
+  api = wp_properties_get (node_props, SPA_KEY_DEVICE_API);
+  factory = wp_properties_get (node_props, PW_KEY_FACTORY_NAME);
+
+  name = wp_properties_get (node_props, SPA_KEY_DEVICE_NAME);
+  if (G_UNLIKELY (!name))
+    name = wp_properties_get (node_props, SPA_KEY_DEVICE_NICK);
+  if (G_UNLIKELY (!name))
+    name = wp_properties_get (node_props, SPA_KEY_DEVICE_ALIAS);
+  if (G_UNLIKELY (!name))
+    name = "unknown-device";
+
+  description = wp_properties_get (node_props, SPA_KEY_DEVICE_DESCRIPTION);
+  if (!description)
+    description = name;
+
+  /* set ALSA specific properties */
+  if (!g_strcmp0 (api, "alsa")) {
+    const gchar *str;
+
+    /* compose the node name */
+    str = wp_properties_get (node_props, SPA_KEY_API_ALSA_PCM_ID);
+    wp_properties_setf (node_props, PW_KEY_NODE_NAME, "%s/%s/%s",
+        factory, name, str);
+
+    /* compose the node description */
+    str = wp_properties_get (node_props, SPA_KEY_API_ALSA_PCM_NAME);
+    wp_properties_setf (node_props, PW_KEY_NODE_DESCRIPTION, "%s: %s",
+        description, str);
+
+    wp_properties_copy_keys (dev_props, node_props,
+        SPA_KEY_API_ALSA_CARD,
+        SPA_KEY_API_ALSA_CARD_ID,
+        SPA_KEY_API_ALSA_CARD_COMPONENTS,
+        SPA_KEY_API_ALSA_CARD_DRIVER,
+        SPA_KEY_API_ALSA_CARD_NAME,
+        SPA_KEY_API_ALSA_CARD_LONGNAME,
+        SPA_KEY_API_ALSA_CARD_MIXERNAME,
+        NULL);
+
+  /* set BLUEZ 5 specific properties */
+  } else if (!g_strcmp0 (api, "bluez5")) {
+    const gchar *profile =
+        wp_properties_get (node_props, SPA_KEY_API_BLUEZ5_PROFILE);
+
+    /* compose the node name */
+    wp_properties_setf (node_props, PW_KEY_NODE_NAME, "%s/%s/%s",
+        factory, name, profile);
+
+    /* compose the node description */
+    wp_properties_setf (node_props, PW_KEY_NODE_DESCRIPTION, "%s (%s)",
+        description, profile);
+
+    wp_properties_copy_keys (dev_props, node_props,
+        SPA_KEY_API_BLUEZ5_PATH,
+        SPA_KEY_API_BLUEZ5_ADDRESS,
+        NULL);
+
+  /* set node properties for other APIs */
+  } else {
+    wp_properties_setf (node_props, PW_KEY_NODE_NAME, "%s/%s",
+        factory, name);
+    wp_properties_set (node_props, PW_KEY_NODE_DESCRIPTION, description);
+  }
+}
+
+static void
+start_monitor (WpMonitor *monitor)
+{
+  g_autoptr (GError) error = NULL;
+
+  if (!wp_monitor_start (monitor, &error)) {
+    g_message ("Failed to start monitor: %s", error->message);
+  }
+}
+
+void
+wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
+{
+  WpMonitor *monitor;
+  const gchar *factory = NULL;
+  WpMonitorFlags flags = 0;
+  GVariantIter *iter;
+
+  if (!g_variant_lookup (args, "factory", "&s", &factory)) {
+    g_message ("Failed to load monitor: no 'factory' key specified");
+    return;
+  }
+
+  if (g_variant_lookup (args, "flags", "as", &iter)) {
+    gchar *flag_str = NULL;
+    GFlagsValue *flag_val = NULL;
+    GFlagsClass *flag_class = g_type_class_ref (WP_TYPE_MONITOR_FLAGS);
+
+    while (g_variant_iter_loop (iter, "s", &flag_str)) {
+      flag_val = g_flags_get_value_by_nick (flag_class, flag_str);
+      if (flag_val)
+        flags |= flag_val->value;
+    }
+    g_variant_iter_free (iter);
+  }
+
+  monitor = wp_monitor_new (core, factory, flags);
+
+  g_signal_connect (monitor, "setup-device-props",
+      (GCallback) setup_device_props, module);
+  g_signal_connect (monitor, "setup-node-props",
+      (GCallback) setup_node_props, module);
+
+  wp_module_set_destroy_callback (module, g_object_unref, monitor);
+
+  /* Start the monitor when the connected callback is triggered */
+  g_signal_connect_object (core, "remote-state-changed::connected",
+      (GCallback) start_monitor, monitor, G_CONNECT_SWAPPED);
+}
-- 
GitLab