Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/pantheon.portal
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.pantheon
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.ScreenCast
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Notification;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.ScreenCast
UseIn=pantheon
20 changes: 20 additions & 0 deletions src/Notification/FdoInterface.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

[DBus (name = "org.freedesktop.Notifications")]
public interface Notification.FdoInterface : Object {
public enum CloseReason {
EXPIRED = 1,
DISMISSED = 2,
CLOSE_NOTIFICATION_CALL = 3,
UNDEFINED = 4
}

public signal void notification_closed (uint32 id, uint32 reason);
public signal void action_invoked (uint32 id, string action_key);

public abstract uint32 notify (string app_name, uint32 replaces_id, string app_icon, string summary, string body, string[] actions, HashTable<string, Variant> hints, int32 expire_timeout) throws Error;
public abstract void close_notification (uint32 id) throws Error;
}
102 changes: 102 additions & 0 deletions src/Notification/Portal.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/

// TEST CALL FOR DSPY:
// 'io.elementary.mail.desktop'
// 'new-mail'
// {'title': <'New mail from John Doe'>, 'body': <'You have a new mail from John Doe. Click to read it.'>, 'priority': <'high'>}

[DBus (name = "org.freedesktop.impl.portal.Notification")]
public class Notification.Portal : Object {
private const string ID_FORMAT = "%s:%s";

public signal void action_invoked (string app_id, string id, string action_name, Variant[] parameters);

public HashTable<string, Variant> supported_options { get; construct; }
public uint version { get; default = 2; }

[DBus (visible = false)]
public DBusConnection connection { private get; construct; }

private FdoInterface fdo_interface;
private HashTable<string, uint32> portal_id_to_fdo_id;

public Portal (DBusConnection connection) {
Object (connection: connection);
}

construct {
supported_options = new HashTable<string, Variant> (str_hash, str_equal);
portal_id_to_fdo_id = new HashTable<string, uint32> (str_hash, str_equal);

try {
fdo_interface = Bus.get_proxy_sync (SESSION, "org.freedesktop.Notifications", "/org/freedesktop/Notifications");
} catch (Error e) {
critical (e.message);
}
}

public void add_notification (string app_id, string id, HashTable<string, Variant> data) throws Error {
if (!("title" in data)) {
throw new DBusError.FAILED ("Can't show notification without title");
}

unowned var title = data["title"].get_string ();

unowned string body = "";
if ("body-markup" in data) {
body = data["body-markup"].get_string ();
} else if ("body" in data) {
body = data["body"].get_string ();
}

uint8 priority = 1;
if ("priority" in data) {
switch (data["priority"].get_string ()) {
case "low":
priority = 0;
break;

case "normal":
priority = 1;
break;
case "high":
priority = 2;
break;
case "urgent":
priority = 2;
break;
}
}

var hints = new HashTable<string, Variant> (str_hash, str_equal);
hints["urgency"] = priority;

try {
var fdo_id = fdo_interface.notify (app_id, 0, app_id, title, body, {}, hints, 0);
portal_id_to_fdo_id[ID_FORMAT.printf (app_id, id)] = fdo_id;
} catch (Error e) {
critical (e.message);
throw new DBusError.FAILED ("Failed to send notification: %s", e.message);
}
}

public void remove_notification (string app_id, string id) throws Error {
var formatted_id = ID_FORMAT.printf (app_id, id);

if (!(formatted_id in portal_id_to_fdo_id)) {
throw new DBusError.FAILED ("Provided id %s not found", id);
}

try {
fdo_interface.close_notification (portal_id_to_fdo_id[formatted_id]);
} catch (Error e) {
critical (e.message);
throw new DBusError.FAILED ("Failed to close notification: %s", e.message);
}
}
}
3 changes: 3 additions & 0 deletions src/XdgDesktopPortalPantheon.vala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ private void on_bus_acquired (DBusConnection connection, string name) {
connection.register_object ("/org/freedesktop/portal/desktop", new Background.Portal (connection));
debug ("Background Portal registered!");

connection.register_object ("/org/freedesktop/portal/desktop", new Notification.Portal (connection));
debug ("Notification Portal registered!");

connection.register_object ("/org/freedesktop/portal/desktop", new Screenshot.Portal (connection));
debug ("Screenshot Portal registered!");

Expand Down
2 changes: 2 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ executable(
'AppChooser/Portal.vala',
'Background/NotificationRequest.vala',
'Background/Portal.vala',
'Notification/FdoInterface.vala',
'Notification/Portal.vala',
'ScreenCast/MonitorTracker/Interface.vala',
'ScreenCast/MonitorTracker/Monitor.vala',
'ScreenCast/MonitorTracker/MonitorTracker.vala',
Expand Down