From cc33dd6b50ef63048829ea5bc63863e7c47e97c1 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 25 Jan 2025 18:15:14 +0300 Subject: [PATCH] Introduce NotificationsManager (#2237) --- src/DBus.vala | 4 +- src/DBusAccelerator.vala | 18 +++++- src/MediaFeedback.vala | 107 ---------------------------------- src/NotificationsManager.vala | 79 +++++++++++++++++++++++++ src/WindowManager.vala | 6 +- src/meson.build | 2 +- 6 files changed, 102 insertions(+), 114 deletions(-) delete mode 100644 src/MediaFeedback.vala create mode 100644 src/NotificationsManager.vala diff --git a/src/DBus.vala b/src/DBus.vala index f2982d438..9b9492785 100644 --- a/src/DBus.vala +++ b/src/DBus.vala @@ -11,7 +11,7 @@ public class Gala.DBus { private static WindowManagerGala wm; [DBus (visible = false)] - public static void init (WindowManagerGala _wm, ScreenshotManager screenshot_manager) { + public static void init (WindowManagerGala _wm, NotificationsManager notifications_manager, ScreenshotManager screenshot_manager) { wm = _wm; Bus.own_name (BusType.SESSION, "org.pantheon.gala", BusNameOwnerFlags.NONE, @@ -33,7 +33,7 @@ public class Gala.DBus { Bus.own_name (BusType.SESSION, "org.gnome.Shell", BusNameOwnerFlags.NONE, (connection) => { try { - connection.register_object ("/org/gnome/Shell", new DBusAccelerator (wm.get_display ())); + connection.register_object ("/org/gnome/Shell", new DBusAccelerator (wm.get_display (), notifications_manager)); connection.register_object ("/org/gnome/Shell/Screenshot", screenshot_manager); } catch (Error e) { warning (e.message); } }, diff --git a/src/DBusAccelerator.vala b/src/DBusAccelerator.vala index 9e33d0111..5c496d9ab 100644 --- a/src/DBusAccelerator.vala +++ b/src/DBusAccelerator.vala @@ -73,13 +73,17 @@ namespace Gala { [DBus (name="org.gnome.Shell")] public class DBusAccelerator { + private const string NOTIFICATION_COMPONENT_NAME = "DBusAccelerator"; + public signal void accelerator_activated (uint action, GLib.HashTable parameters); private Meta.Display display; + private NotificationsManager notifications_manager; private GLib.HashTable grabbed_accelerators; - public DBusAccelerator (Meta.Display _display) { + public DBusAccelerator (Meta.Display _display, NotificationsManager _notifications_manager) { display = _display; + notifications_manager = _notifications_manager; grabbed_accelerators = new HashTable (str_hash, str_equal); display.accelerator_activated.connect (on_accelerator_activated); } @@ -166,7 +170,17 @@ namespace Gala { level = (int)(double_level * 100); } - MediaFeedback.send (icon, level); + var hints = new GLib.HashTable (null, null); + hints.set ("x-canonical-private-synchronous", new Variant.string ("gala-feedback")); + hints.set ("value", new Variant.int32 (level)); + + notifications_manager.send.begin ( + NOTIFICATION_COMPONENT_NAME, + icon, + label, + "", + hints + ); } } } diff --git a/src/MediaFeedback.vala b/src/MediaFeedback.vala deleted file mode 100644 index b3ec11216..000000000 --- a/src/MediaFeedback.vala +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright (C) 2016 Rico Tzschichholz -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -namespace Gala { - [DBus (name = "org.freedesktop.Notifications")] - interface DBusNotifications : GLib.Object { - public abstract uint32 notify (string app_name, uint32 replaces_id, string app_icon, string summary, - string body, string[] actions, HashTable hints, int32 expire_timeout) throws DBusError, IOError; - } - - public class MediaFeedback : GLib.Object { - [Compact] - class Feedback { - public string icon; - public int32 level; - - public Feedback (string _icon, int32 _level) { - icon = _icon; - level = _level; - } - } - - private static MediaFeedback? instance = null; - private static ThreadPool? pool = null; - - public static void init () { - if (instance == null) - instance = new MediaFeedback (); - } - - public static void send (string icon, int val) - requires (instance != null && pool != null) { - try { - pool.add (new Feedback (icon, val)); - } catch (ThreadError e) { - } - } - - private DBusNotifications? notifications = null; - private uint32 notification_id = 0; - - private MediaFeedback () { - Object (); - } - - construct { - try { - pool = new ThreadPool.with_owned_data (send_feedback, 1, false); - } catch (ThreadError e) { - critical ("%s", e.message); - pool = null; - } - - try { - Bus.watch_name (BusType.SESSION, "org.freedesktop.Notifications", BusNameWatcherFlags.NONE, on_watch, on_unwatch); - } catch (IOError e) { - debug (e.message); - } - } - - private void on_watch (DBusConnection conn) { - conn.get_proxy.begin ("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", DBusProxyFlags.NONE, null, (obj, res) => { - try { - notifications = ((DBusConnection) obj).get_proxy.end (res); - } catch (Error e) { - debug (e.message); - notifications = null; - } - }); - } - - private void on_unwatch (DBusConnection conn) { - notifications = null; - } - - private void send_feedback (owned Feedback feedback) { - if (notifications == null) { - return; - } - - var hints = new GLib.HashTable (null, null); - hints.set ("x-canonical-private-synchronous", new Variant.string ("gala-feedback")); - hints.set ("value", new Variant.int32 (feedback.level)); - - try { - notification_id = notifications.notify ("gala-feedback", notification_id, feedback.icon, "", "", {}, hints, 2000); - } catch (Error e) { - critical ("%s", e.message); - } - } - } -} diff --git a/src/NotificationsManager.vala b/src/NotificationsManager.vala new file mode 100644 index 000000000..8898fc4c1 --- /dev/null +++ b/src/NotificationsManager.vala @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2016 Rico Tzschichholz + * 2025 elementary, Inc. (https://elementary.io) + */ + +/** + * Gala.NotificationsManager can be used to send notifications to org.freedesktop.Notifications + */ +public class Gala.NotificationsManager : GLib.Object { + [DBus (name = "org.freedesktop.Notifications")] + private interface DBusNotifications : GLib.Object { + public abstract async uint32 notify (string app_name, uint32 replaces_id, string app_icon, string summary, + string body, string[] actions, HashTable hints, int32 expire_timeout) throws DBusError, IOError; + } + + private const int EXPIRE_TIMEOUT = 2000; + + private DBusNotifications? notifications = null; + private GLib.HashTable replaces_id_table = new GLib.HashTable (str_hash, str_equal); + + construct { + Bus.watch_name (BusType.SESSION, "org.freedesktop.Notifications", BusNameWatcherFlags.NONE, on_watch, on_unwatch); + } + + private void on_watch (DBusConnection connection) { + connection.get_proxy.begin ( + "org.freedesktop.Notifications", "/org/freedesktop/Notifications", DBusProxyFlags.NONE, null, + (obj, res) => { + try { + notifications = ((DBusConnection) obj).get_proxy.end (res); + } catch (Error e) { + warning ("NotificationsManager: Couldn't connect to notifications server: %s", e.message); + notifications = null; + } + } + ); + } + + private void on_unwatch (DBusConnection conn) { + warning ("NotificationsManager: Lost connection to notifications server"); + notifications = null; + } + + public async void send ( + string component_name, + string icon, + string summary, + string body, + GLib.HashTable hints + ) { + if (notifications == null) { + warning ("NotificationsManager: Unable to send notification. No connection to notification server"); + return; + } + + uint32? replaces_id = replaces_id_table.get (component_name); + if (replaces_id == null) { + replaces_id = 0; + } + + try { + var notification_id = yield notifications.notify ( + "gala-feedback", + replaces_id, + icon, + summary, + body, + {}, + hints, + EXPIRE_TIMEOUT + ); + + replaces_id_table.insert (component_name, notification_id); + } catch (Error e) { + critical ("NotificationsManager: There was an error sending a notification: %s", e.message); + } + } +} diff --git a/src/WindowManager.vala b/src/WindowManager.vala index f277fed3a..d74d3c308 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -72,6 +72,8 @@ namespace Gala { public WindowTracker? window_tracker { get; private set; } + private NotificationsManager notifications_manager; + private ScreenshotManager screenshot_manager; /** @@ -190,9 +192,9 @@ namespace Gala { private void show_stage () { unowned Meta.Display display = get_display (); + notifications_manager = new NotificationsManager (); screenshot_manager = new ScreenshotManager (this); - DBus.init (this, screenshot_manager); - MediaFeedback.init (); + DBus.init (this, notifications_manager, screenshot_manager); WindowListener.init (display); KeyboardManager.init (display); diff --git a/src/meson.build b/src/meson.build index 9660dd2b8..974b561f5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,7 +7,7 @@ gala_bin_sources = files( 'InternalUtils.vala', 'KeyboardManager.vala', 'Main.vala', - 'MediaFeedback.vala', + 'NotificationsManager.vala', 'NotificationStack.vala', 'PantheonShell.vala', 'PluginManager.vala',