From b216b1afbe79d2028880abcea4005c059ce9d2b5 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Fri, 17 Sep 2021 20:03:43 +0800 Subject: [PATCH] some progress.... --- .../DBusSystemTray/SNIDBus.cs | 358 +++++++++++++----- src/Avalonia.X11/X11Platform.cs | 2 +- src/Avalonia.X11/X11TrayIconImpl.cs | 11 +- 3 files changed, 278 insertions(+), 93 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs b/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs index f3d1073e29..fc62d24a7d 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs @@ -1,55 +1,229 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Reactive.Disposables; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Tmds.DBus; [assembly: InternalsVisibleTo(Tmds.DBus.Connection.DynamicAssemblyName)] + namespace Avalonia.FreeDesktop.DBusSystemTray { public class SNIDBus { public SNIDBus() { - } - public async void Initialize() + private static int trayinstanceID = 0; + + private static int GetTID() + { + trayinstanceID = (int)new Random().Next(0, 100); + return trayinstanceID; + } + + public async void Initialize() { + var x = Process.GetCurrentProcess().Id; + var y = GetTID(); - var serviceName = $"AvaloniaSNITest_{Guid.NewGuid()}"; + var sysTraySrvName = $"org.kde.StatusNotifierItem-{x}-{y}"; + var tx = new StatusNotifierItem(); - var path = - Connection.Session.CreateProxy("org.kde.StatusNotifierWatcher", + await DBusHelper.Connection.RegisterObjectAsync(tx); + + await DBusHelper.Connection.RegisterServiceAsync(sysTraySrvName, () => + { + }); + + while (!await DBusHelper.Connection.IsServiceActiveAsync(sysTraySrvName)) + { + await Task.Delay(1000); + } + + var yx = DBusHelper.Connection.CreateProxy(sysTraySrvName, tx.ObjectPath); + + var snw = + DBusHelper.Connection.CreateProxy("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher"); - - await path.RegisterStatusNotifierHostAsync(serviceName); - await path.WatchPropertiesAsync(x => - { + while (!await DBusHelper.Connection.IsServiceActiveAsync("org.kde.StatusNotifierWatcher")) + { + await Task.Delay(1000); + } + + await snw.RegisterStatusNotifierItemAsync(sysTraySrvName); + + tx.Ready(); + + await yx.ActivateAsync(1, 1); + } + } + + internal class StatusNotifierItem : IStatusNotifierItem + { + private event Action OnPropertyChange; + + public event Action OnTitleChanged; + public event Action OnIconChanged; + public event Action OnAttentionIconChanged; + public event Action OnOverlayIconChanged; - }); - await path.WatchStatusNotifierHostRegisteredAsync(() => - { + public Action NewToolTipAsync; + public ObjectPath ObjectPath { get; } - }, z => - { + readonly StatusNotifierItemProperties props; + + public StatusNotifierItem() + { + var ID = Guid.NewGuid().ToString().Replace("-", ""); + ObjectPath = new ObjectPath($"/StatusNotifierItem"); + props = new StatusNotifierItemProperties(); + + props.Title = "Avalonia Test Tray"; + props.Id = ID; +// + // + // public string Category { get; set; } = default; + // + // public string Id { get; set; } = default; + // + // public string Title { get; set; } = default; + // + // public string Status { get; set; } = default; + // + // public int WindowId { get; set; } = default; + } - }); - - + // static class StatusNotifierItemExtensions + // { + // public static Task GetCategoryAsync(this IStatusNotifierItem o) => o.GetAsync("Category"); + // public static Task GetIdAsync(this IStatusNotifierItem o) => o.GetAsync("Id"); + // public static Task GetTitleAsync(this IStatusNotifierItem o) => o.GetAsync("Title"); + // public static Task GetStatusAsync(this IStatusNotifierItem o) => o.GetAsync("Status"); + // public static Task GetWindowIdAsync(this IStatusNotifierItem o) => o.GetAsync("WindowId"); + // + // public static Task GetIconThemePathAsync(this IStatusNotifierItem o) => + // o.GetAsync("IconThemePath"); + // + // public static Task GetMenuAsync(this IStatusNotifierItem o) => o.GetAsync("Menu"); + // public static Task GetItemIsMenuAsync(this IStatusNotifierItem o) => o.GetAsync("ItemIsMenu"); + // public static Task GetIconNameAsync(this IStatusNotifierItem o) => o.GetAsync("IconName"); + // + // public static Task<(int, int, byte[])[]> GetIconPixmapAsync(this IStatusNotifierItem o) => + // o.GetAsync<(int, int, byte[])[]>("IconPixmap"); + // + // public static Task GetOverlayIconNameAsync(this IStatusNotifierItem o) => + // o.GetAsync("OverlayIconName"); + // + // public static Task<(int, int, byte[])[]> GetOverlayIconPixmapAsync(this IStatusNotifierItem o) => + // o.GetAsync<(int, int, byte[])[]>("OverlayIconPixmap"); + // + // public static Task GetAttentionIconNameAsync(this IStatusNotifierItem o) => + // o.GetAsync("AttentionIconName"); + // + // public static Task<(int, int, byte[])[]> GetAttentionIconPixmapAsync(this IStatusNotifierItem o) => + // o.GetAsync<(int, int, byte[])[]>("AttentionIconPixmap"); + // + // public static Task GetAttentionMovieNameAsync(this IStatusNotifierItem o) => + // o.GetAsync("AttentionMovieName"); + // + // public static Task<(string, (int, int, byte[])[], string, string)> + // GetToolTipAsync(this IStatusNotifierItem o) => + // o.GetAsync<(string, (int, int, byte[])[], string, string)>("ToolTip"); + // } + + public async Task ContextMenuAsync(int X, int Y) + { + } + + public async Task ActivateAsync(int X, int Y) + { + // OnPropertyChange?.Invoke(new PropertyChanges()); + } + + public async Task SecondaryActivateAsync(int X, int Y) + { + throw new NotImplementedException(); + } + + public async Task ScrollAsync(int Delta, string Orientation) + { + } + + public async Task WatchNewTitleAsync(Action handler, Action onError = null) + { + OnTitleChanged += handler; + return Disposable.Create(() => OnTitleChanged -= handler); + } + + + public async Task WatchNewIconAsync(Action handler, Action onError = null) + { + OnIconChanged += handler; + return Disposable.Create(() => OnIconChanged -= handler); + } + + public async Task WatchNewAttentionIconAsync(Action handler, Action onError = null) + { + OnAttentionIconChanged += handler; + return Disposable.Create(() => OnAttentionIconChanged -= handler); + } + + + public async Task WatchNewOverlayIconAsync(Action handler, Action onError = null) + { + OnOverlayIconChanged += handler; + return Disposable.Create(() => OnOverlayIconChanged -= handler); + } + + public async Task WatchNewToolTipAsync(Action handler, Action onError = null) + { + NewToolTipAsync += handler; + return Disposable.Create(() => NewToolTipAsync -= handler); + } + + public async Task WatchNewStatusAsync(Action handler, Action onError = null) + { + NewStatusAsync += handler; + return Disposable.Create(() => NewStatusAsync -= handler); + } + + public Action NewStatusAsync { get; set; } + + public async Task GetAllAsync() + { + return props; + } + + public async Task WatchPropertiesAsync(Action handler) + { + OnPropertyChange += handler; + return Disposable.Create(() => OnPropertyChange -= handler); + } + + public void Ready() + { + OnTitleChanged?.Invoke(); } } - + [DBusInterface("org.kde.StatusNotifierWatcher")] interface IStatusNotifierWatcher : IDBusObject { Task RegisterStatusNotifierItemAsync(string Service); Task RegisterStatusNotifierHostAsync(string Service); - Task WatchStatusNotifierItemRegisteredAsync(Action handler, Action onError = null); - Task WatchStatusNotifierItemUnregisteredAsync(Action handler, Action onError = null); + + Task WatchStatusNotifierItemRegisteredAsync(Action handler, + Action onError = null); + + Task WatchStatusNotifierItemUnregisteredAsync(Action handler, + Action onError = null); + Task WatchStatusNotifierHostRegisteredAsync(Action handler, Action onError = null); Task GetAsync(string prop); Task GetAllAsync(); @@ -60,54 +234,23 @@ namespace Avalonia.FreeDesktop.DBusSystemTray [Dictionary] class StatusNotifierWatcherProperties { - private string[] _RegisteredStatusNotifierItems = default(string[]); - public string[] RegisteredStatusNotifierItems - { - get - { - return _RegisteredStatusNotifierItems; - } + public string[] RegisteredStatusNotifierItems { get; set; } = default; - set - { - _RegisteredStatusNotifierItems = (value); - } - } - - private bool _IsStatusNotifierHostRegistered = default(bool); - public bool IsStatusNotifierHostRegistered - { - get - { - return _IsStatusNotifierHostRegistered; - } + public bool IsStatusNotifierHostRegistered { get; set; } = default; - set - { - _IsStatusNotifierHostRegistered = (value); - } - } - - private int _ProtocolVersion = default(int); - public int ProtocolVersion - { - get - { - return _ProtocolVersion; - } - - set - { - _ProtocolVersion = (value); - } - } + public int ProtocolVersion { get; set; } = default; } static class StatusNotifierWatcherExtensions { - public static Task GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) => o.GetAsync("RegisteredStatusNotifierItems"); - public static Task GetIsStatusNotifierHostRegisteredAsync(this IStatusNotifierWatcher o) => o.GetAsync("IsStatusNotifierHostRegistered"); - public static Task GetProtocolVersionAsync(this IStatusNotifierWatcher o) => o.GetAsync("ProtocolVersion"); + public static Task GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) => + o.GetAsync("RegisteredStatusNotifierItems"); + + public static Task GetIsStatusNotifierHostRegisteredAsync(this IStatusNotifierWatcher o) => + o.GetAsync("IsStatusNotifierHostRegistered"); + + public static Task GetProtocolVersionAsync(this IStatusNotifierWatcher o) => + o.GetAsync("ProtocolVersion"); } [DBusInterface("org.gtk.Actions")] @@ -118,7 +261,11 @@ namespace Avalonia.FreeDesktop.DBusSystemTray Task> DescribeAllAsync(); Task ActivateAsync(string ActionName, object[] Parameter, IDictionary PlatformData); Task SetStateAsync(string ActionName, object Value, IDictionary PlatformData); - Task WatchChangedAsync(Action<(string[] removals, IDictionary enableChanges, IDictionary stateChanges, IDictionary additions)> handler, Action onError = null); + + Task WatchChangedAsync( + Action<(string[] removals, IDictionary enableChanges, IDictionary stateChanges + , IDictionary additions)> handler, + Action onError = null); } [DBusInterface("org.gtk.Application")] @@ -136,19 +283,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray [Dictionary] class ApplicationProperties { - private bool _Busy = default(bool); - public bool Busy - { - get - { - return _Busy; - } - - set - { - _Busy = (value); - } - } + public bool Busy { get; set; } = default; } static class ApplicationExtensions @@ -178,23 +313,66 @@ namespace Avalonia.FreeDesktop.DBusSystemTray [Dictionary] class ProfilerProperties { - private IDictionary _Capabilities = default(IDictionary); - public IDictionary Capabilities - { - get - { - return _Capabilities; - } - - set - { - _Capabilities = (value); - } - } + public IDictionary Capabilities { get; set; } = default; } static class ProfilerExtensions { - public static Task> GetCapabilitiesAsync(this IProfiler o) => o.GetAsync>("Capabilities"); + public static Task> GetCapabilitiesAsync(this IProfiler o) => + o.GetAsync>("Capabilities"); + } + + [DBusInterface("org.kde.StatusNotifierItem")] + interface IStatusNotifierItem : IDBusObject + { + Task ContextMenuAsync(int X, int Y); + Task ActivateAsync(int X, int Y); + Task SecondaryActivateAsync(int X, int Y); + Task ScrollAsync(int Delta, string Orientation); + Task WatchNewTitleAsync(Action handler, Action onError = null); + Task WatchNewIconAsync(Action handler, Action onError = null); + Task WatchNewAttentionIconAsync(Action handler, Action onError = null); + Task WatchNewOverlayIconAsync(Action handler, Action onError = null); + Task WatchNewToolTipAsync(Action handler, Action onError = null); + Task WatchNewStatusAsync(Action handler, Action onError = null); + Task GetAllAsync(); + Task WatchPropertiesAsync(Action handler); + } + + + [Dictionary] + class StatusNotifierItemProperties + { + public string Category { get; set; } = default; + + public string Id { get; set; } = default; + + public string Title { get; set; } = default; + + public string Status { get; set; } = default; + + public int WindowId { get; set; } = default; + + public string IconThemePath { get; set; } = default; + + public ObjectPath Menu { get; set; } = default; + + public bool ItemIsMenu { get; set; } = default; + + public string IconName { get; set; } = default; + + public (int, int, byte[])[] IconPixmap { get; set; } = default; + + public string OverlayIconName { get; set; } = default; + + public (int, int, byte[])[] OverlayIconPixmap { get; set; } = default; + + public string AttentionIconName { get; set; } = default; + + public (int, int, byte[])[] AttentionIconPixmap { get; set; } = default; + + public string AttentionMovieName { get; set; } = default; + + public (string, (int, int, byte[])[], string, string) ToolTip { get; set; } = default; } } diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 5d80c860a7..eb805c0f0b 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -103,7 +103,7 @@ namespace Avalonia.X11 public ITrayIconImpl CreateTrayIcon () { - return new X11TrayIconImpl(); + return new X11TrayIconImpl(this); } public IWindowImpl CreateWindow() diff --git a/src/Avalonia.X11/X11TrayIconImpl.cs b/src/Avalonia.X11/X11TrayIconImpl.cs index 0ab34434fb..0f42a15cae 100644 --- a/src/Avalonia.X11/X11TrayIconImpl.cs +++ b/src/Avalonia.X11/X11TrayIconImpl.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Avalonia.Controls.Platform; using Avalonia.FreeDesktop.DBusSystemTray; using Avalonia.Platform; @@ -7,11 +8,17 @@ namespace Avalonia.X11 { class X11TrayIconImpl : ITrayIconImpl { + private readonly AvaloniaX11Platform _avaloniaX11Platform; public INativeMenuExporter MenuExporter => null; public Action OnClicked { get; set; } private SNIDBus sni = new SNIDBus(); - + + public X11TrayIconImpl(AvaloniaX11Platform avaloniaX11Platform) + { + _avaloniaX11Platform = avaloniaX11Platform; + } + public void Dispose() { @@ -30,7 +37,7 @@ namespace Avalonia.X11 public void SetToolTipText(string text) { - sni.Initialize(); + sni.Initialize(); } } }