Skip to content

Instantly share code, notes, and snippets.

@johnnyasantoss
Last active September 15, 2023 18:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnnyasantoss/07d59d4fec1f6a4897c560108d8ad5f2 to your computer and use it in GitHub Desktop.
Save johnnyasantoss/07d59d4fec1f6a4897c560108d8ad5f2 to your computer and use it in GitHub Desktop.
C# Desktop Notifications on Linux

C# Desktop Notifications on Linux

Using Tmds.DBus. Should work with any Window manager that complies with freedesktop.org

Program.cs is only for demonstration purposes. Low quality code.

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RootNamespace>dbus_test</RootNamespace>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<DotNetCliToolReference Include="Tmds.DBus.Tool" Version="0.7.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Tmds.DBus" Version="0.7.0" />
</ItemGroup>
</Project>
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Tmds.DBus;
[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)]
namespace dbus_test
{
/// <seealso>http://www.galago-project.org/specs/notification/0.9/x408.html</seealso>
/// <summary>
/// Interface for notifications
/// </summary>
[DBusInterface("org.freedesktop.Notifications")]
interface IFreeDesktopNotifications : IDBusObject
{
Task<uint> NotifyAsync(string app_name, uint replaces_id, string app_icon, string summary, string body, string[] actions, IDictionary<string, object> hints, int expire_timeout);
Task CloseNotificationAsync(uint id);
Task<string[]> GetCapabilitiesAsync();
Task<(string name, string vendor, string version, string spec_version)> GetServerInformationAsync();
Task<IDisposable> WatchNotificationClosedAsync(Action<(uint id, uint reason)> handler, Action<Exception> onError = null);
Task<IDisposable> WatchActionInvokedAsync(Action<(uint id, string action_key)> handler, Action<Exception> onError = null);
}
public class FreeDesktopNotificationsInfo
{
public static readonly ObjectPath Path = new ObjectPath("/org/freedesktop/Notifications");
}
}
#!/bin/sh
# This uses ImageMagick (make sure you have it installed)
convert -background lightgreen -fill green \
-font Ubuntu Mono -pointsize 72 label:Test \
-resize 100x \
img.png
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tmds.DBus;
namespace dbus_test
{
class Program
{
private static uint LastId = 0;
public static async Task Main(string[] args)
{
using (var client = Connection.Session)
{
await client.ConnectAsync();
Console.WriteLine("Client connected");
var proxy = client.CreateProxy<IFreeDesktopNotifications>(
"org.freedesktop.Notifications",
FreeDesktopNotificationsInfo.Path
);
await ShowCaps(proxy);
await ShowServerInfo(proxy);
var watcher = await WatchNotificationsActionsCallbacks(proxy);
var waiter = await WatchClosedNotifications(proxy);
var notificationId = await ShowNotification(proxy, "Test");
await CloseNotificationDelayed(proxy, notificationId);
await Task.Delay(1000);
Console.WriteLine("Shooting a few notifications replacing each-other");
foreach (var i in Enumerable.Range(0, 10))
{
LastId = await ShowNotification(proxy, $"Test: {i}", LastId);
await Task.Delay(300);
}
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};
try
{
//5s
await Task.Delay(5 * 1000, cts.Token);
}
finally
{
watcher.Dispose();
waiter.Dispose();
Console.WriteLine("Ciao!");
}
}
}
private static async Task CloseNotificationDelayed(IFreeDesktopNotifications proxy, uint notificationId)
{
await Task.Run(async () =>
{
//3s
await Task.Delay(3 * 1000);
Console.WriteLine("Closing notification {0}", notificationId);
await proxy.CloseNotificationAsync(notificationId);
});
}
private static async Task<uint> ShowNotification(IFreeDesktopNotifications proxy, string title, uint replaceId = 0)
{
var notificationId = await proxy.NotifyAsync(
"dbus_test"
, replaceId
, $"{Directory.GetCurrentDirectory()}/img.png"
, title
, "<b>Awesome test</b><br/>!"
+ "We <i>also</i> have <a href=\"http://www.galago-project.org/specs/notification/0.9/x408.html\">links</a><br/>"
+ "<u>And</u> images: <br />"
+ $"<img alt=\"Altought the support is not garanteed\" src=\"{Directory.GetCurrentDirectory()}/img.png\" />"
, new string[] { "test", "Test" }
, new Dictionary<string, object>{
{ "urgency", (byte)0 }
, { "category", "transfer" }
, { "x", 100 }
, { "y", 100 }
}
, 10 * 1000
);
Console.WriteLine("Created notification {0}", notificationId);
return notificationId;
}
private static async Task<IDisposable> WatchNotificationsActionsCallbacks(IFreeDesktopNotifications proxy)
{
return await proxy.WatchActionInvokedAsync(e =>
{
Console.WriteLine("Action invoked signal: {0} {1}", e.id, e.action_key);
}, ex =>
{
Console.Error.WriteLine(ex.Message);
});
}
private static async Task<IDisposable> WatchClosedNotifications(IFreeDesktopNotifications proxy)
{
return await proxy.WatchNotificationClosedAsync(e =>
{
string reason;
switch (e.reason)
{
case 1:
reason = "The notification expired";
break;
case 2:
reason = "The notification was dismissed by the user";
break;
case 3:
reason = "The notification was closed by a call to CloseNotification";
break;
case 4:
reason = "Undefined/reserved reasons";
break;
default:
reason = "Unknown reason";
break;
}
Console.WriteLine("Notification closed signal: {0} {1}:{2}", e.id, e.reason, reason);
}, ex =>
{
Console.Error.WriteLine(ex.Message);
});
}
private static async Task ShowCaps(IFreeDesktopNotifications proxy)
{
Console.WriteLine("Capabilities:");
Console.WriteLine("[");
foreach (var cap in await proxy.GetCapabilitiesAsync())
{
Console.WriteLine(" {0},", cap);
}
Console.WriteLine("]");
Console.WriteLine("----");
}
private static async Task ShowServerInfo(IFreeDesktopNotifications proxy)
{
Console.WriteLine("Server Info:");
var (name, vendor, version, specVersion) = await proxy.GetServerInformationAsync();
Console.WriteLine("Name: {0}", name);
Console.WriteLine("Vendor: {0}", vendor);
Console.WriteLine("Version: {0}", version);
Console.WriteLine("Specifications Version: {0}", specVersion);
Console.WriteLine("----");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment