From d21ff24ea1c22416f9a03275c15d4b1961cb2843 Mon Sep 17 00:00:00 2001 From: Julian Bouzas Date: Mon, 12 May 2025 10:29:11 -0400 Subject: [PATCH] modules: Add notifications-api module This allows sending Desktop notifications using D-Bus. --- modules/meson.build | 10 ++ modules/module-notifications-api.c | 152 +++++++++++++++++++++++++++++ src/config/wireplumber.conf | 8 ++ 3 files changed, 170 insertions(+) create mode 100644 modules/module-notifications-api.c diff --git a/modules/meson.build b/modules/meson.build index b78ace9c..7308aa4e 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -58,6 +58,16 @@ shared_library( dependencies : [wp_dep, giounix_dep], ) +shared_library( + 'wireplumber-module-notifications-api', + [ + 'module-notifications-api.c', + ], + install : true, + install_dir : wireplumber_module_dir, + dependencies : [wp_dep, giounix_dep], +) + shared_library( 'wireplumber-module-si-audio-adapter', [ diff --git a/modules/module-notifications-api.c b/modules/module-notifications-api.c new file mode 100644 index 00000000..29e7421e --- /dev/null +++ b/modules/module-notifications-api.c @@ -0,0 +1,152 @@ +/* WirePlumber + * + * Copyright © 2021 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#include +#include "dbus-connection-state.h" + +WP_DEFINE_LOCAL_LOG_TOPIC ("m-notification") + +#define DBUS_INTERFACE_NAME "org.freedesktop.Notifications" +#define DBUS_OBJECT_PATH "/org/freedesktop/Notifications" + +enum +{ + ACTION_GET_DBUS, + ACTION_SEND, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _WpNotificationsPlugin +{ + WpPlugin parent; + + WpPlugin *dbus; +}; + +G_DECLARE_FINAL_TYPE (WpNotificationsPlugin, + wp_notifications_plugin, WP, NOTIFICATIONS_PLUGIN, + WpPlugin) +G_DEFINE_TYPE (WpNotificationsPlugin, wp_notifications_plugin, + WP_TYPE_PLUGIN) + +static gpointer +wp_notifications_plugin_get_dbus (WpNotificationsPlugin *self) +{ + return self->dbus ? g_object_ref (self->dbus) : NULL; +} + +static void +wp_notifications_plugin_send (WpNotificationsPlugin *self, + const gchar *summary, const gchar *body_message) +{ + g_autoptr (GDBusConnection) conn = NULL; + g_autoptr (GError) error = NULL; + g_autoptr (GVariant) res = NULL; + GVariantBuilder hints; + + g_object_get (self->dbus, "connection", &conn, NULL); + g_return_if_fail (conn); + + /* Set urgency */ + g_variant_builder_init (&hints, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add (&hints, "{sv}", "urgency", g_variant_new_byte (0)); + + /* Notify */ + res = g_dbus_connection_call_sync (conn, DBUS_INTERFACE_NAME, + DBUS_OBJECT_PATH, DBUS_INTERFACE_NAME, "Notify", + g_variant_new("(susssasa{sv}i)", "wireplumber", 0, "", summary, + body_message, NULL, &hints, -1), NULL, G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); + if (error) { + g_autofree gchar *remote_error = g_dbus_error_get_remote_error (error); + g_dbus_error_strip_remote_error (error); + + wp_warning_object (self, "Notify: %s (%s)", error->message, remote_error); + return; + } +} + +static void +wp_notifications_plugin_init (WpNotificationsPlugin * self) +{ +} + +static void +wp_notifications_plugin_enable (WpPlugin * plugin, + WpTransition * transition) +{ + WpNotificationsPlugin *self = WP_NOTIFICATIONS_PLUGIN (plugin); + g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self)); + + self->dbus = wp_plugin_find (core, "dbus-connection"); + if (!self->dbus) { + wp_transition_return_error (transition, g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_INVARIANT, + "dbus-connection module must be loaded before notifications")); + return; + } + + wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0); +} + +static void +wp_notifications_plugin_disable (WpPlugin * plugin) +{ + WpNotificationsPlugin *self = WP_NOTIFICATIONS_PLUGIN (plugin); + + g_clear_object (&self->dbus); + + wp_object_update_features (WP_OBJECT (self), 0, WP_PLUGIN_FEATURE_ENABLED); +} + +static void +wp_notifications_plugin_class_init (WpNotificationsPluginClass * klass) +{ + WpPluginClass *plugin_class = (WpPluginClass *) klass; + + plugin_class->enable = wp_notifications_plugin_enable; + plugin_class->disable = wp_notifications_plugin_disable; + + /** + * WpNotificationsPlugin::get-dbus: + * + * Returns: (transfer full): the dbus object + */ + signals[ACTION_GET_DBUS] = g_signal_new_class_handler ( + "get-dbus", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + (GCallback) wp_notifications_plugin_get_dbus, + NULL, NULL, NULL, + G_TYPE_OBJECT, 0); + + /** + * WpNotificationsPlugin::send: + * + * @brief + * @em summary: the summary + * @em body_message: The body message + */ + signals[ACTION_SEND] = g_signal_new_class_handler ( + "send", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + (GCallback) wp_notifications_plugin_send, + NULL, NULL, NULL, G_TYPE_VARIANT, + 2, G_TYPE_STRING, G_TYPE_STRING); +} + +WP_PLUGIN_EXPORT GObject * +wireplumber__module_init (WpCore * core, WpSpaJson * args, GError ** error) +{ + return G_OBJECT (g_object_new ( + wp_notifications_plugin_get_type(), + "name", "notifications-api", + "core", core, + NULL)); +} diff --git a/src/config/wireplumber.conf b/src/config/wireplumber.conf index 038ef1ca..7afc584c 100644 --- a/src/config/wireplumber.conf +++ b/src/config/wireplumber.conf @@ -335,6 +335,14 @@ wireplumber.components = [ provides = api.file-monitor } + ## API to send D-Bus notifications + { + name = libwireplumber-module-notifications-api, type = module + provides = api.notifications + requires = [ support.dbus ] + } + + ## Provide the "default" pw_metadata { name = metadata.lua, type = script/lua