From d32714559bb5cc0b56f262159d1b7b4127ce10a7 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Sat, 18 Sep 2021 12:05:16 +0800 Subject: [PATCH] more stuff --- src/Avalonia.FreeDesktop/DBusMenuExporter.cs | 47 ++++++-- .../DBusSystemTray/SNIDBus.cs | 109 ++++-------------- src/Avalonia.X11/X11IconLoader.cs | 4 +- src/Avalonia.X11/X11TrayIconImpl.cs | 82 ++++++++----- src/Avalonia.X11/X11Window.cs | 2 +- 5 files changed, 121 insertions(+), 123 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs index 1c58ff3ecf..95aea86dfe 100644 --- a/src/Avalonia.FreeDesktop/DBusMenuExporter.cs +++ b/src/Avalonia.FreeDesktop/DBusMenuExporter.cs @@ -16,16 +16,30 @@ namespace Avalonia.FreeDesktop { public class DBusMenuExporter { - public static ITopLevelNativeMenuExporter TryCreate(IntPtr xid) + public static ITopLevelNativeMenuExporter TryCreateTopLevelNativeMenu(IntPtr xid) { + return null; + if (DBusHelper.Connection == null) return null; return new DBusMenuExporterImpl(DBusHelper.Connection, xid); } + + public static INativeMenuExporter TryCreateDetachedNativeMenu(ObjectPath path) + { + if (DBusHelper.Connection == null) + return null; + + return new DBusMenuExporterImpl(DBusHelper.Connection, path); + } + + public static ObjectPath GenerateDBusMenuObjPath = "/net/avaloniaui/dbusmenu/" + + Guid.NewGuid().ToString().Replace("-", ""); class DBusMenuExporterImpl : ITopLevelNativeMenuExporter, IDBusMenu, IDisposable { + private readonly string _targetServiceName; private readonly Connection _dbus; private readonly uint _xid; private IRegistrar _registar; @@ -37,26 +51,41 @@ namespace Avalonia.FreeDesktop private readonly HashSet _menus = new HashSet(); private bool _resetQueued; private int _nextId = 1; + private bool AppMenu = true; public DBusMenuExporterImpl(Connection dbus, IntPtr xid) { _dbus = dbus; _xid = (uint)xid.ToInt32(); - ObjectPath = new ObjectPath("/net/avaloniaui/dbusmenu/" - + Guid.NewGuid().ToString().Replace("-", "")); + ObjectPath = GenerateDBusMenuObjPath; SetNativeMenu(new NativeMenu()); Init(); } + public DBusMenuExporterImpl(Connection dbus, ObjectPath path) + { + _dbus = dbus; + AppMenu = false; + ObjectPath = path; + SetNativeMenu(new NativeMenu()); + Init(); + } async void Init() { try { - await _dbus.RegisterObjectAsync(this); - _registar = DBusHelper.Connection.CreateProxy( - "com.canonical.AppMenu.Registrar", - "/com/canonical/AppMenu/Registrar"); - if (!_disposed) - await _registar.RegisterWindowAsync(_xid, ObjectPath); + if (AppMenu) + { + await _dbus.RegisterObjectAsync(this); + _registar = DBusHelper.Connection.CreateProxy( + "com.canonical.AppMenu.Registrar", + "/com/canonical/AppMenu/Registrar"); + if (!_disposed) + await _registar.RegisterWindowAsync(_xid, ObjectPath); + } + else + { + await _dbus.RegisterObjectAsync(this); + } } catch (Exception e) { diff --git a/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs b/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs index 210f9e3159..398411a81a 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Reactive.Disposables; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using Avalonia.Controls.Platform; using Tmds.DBus; [assembly: InternalsVisibleTo(Tmds.DBus.Connection.DynamicAssemblyName)] @@ -20,11 +21,11 @@ namespace Avalonia.FreeDesktop.DBusSystemTray private IStatusNotifierWatcher _snw; private string _sysTraySrvName; private StatusNotifierItem _statusNotifierItem; + public INativeMenuExporter NativeMenuExporter; private static int GetTID() { - trayinstanceID = 4; - return trayinstanceID; + return trayinstanceID = new Random().Next(0, 100); } public async void Initialize() @@ -42,11 +43,12 @@ namespace Avalonia.FreeDesktop.DBusSystemTray await con.RegisterObjectAsync(_statusNotifierItem); - await con.RegisterServiceAsync(_sysTraySrvName, () => - { - }); - - await _snw.RegisterStatusNotifierItemAsync(_sysTraySrvName); + await con.RegisterServiceAsync(_sysTraySrvName); + + await _snw.RegisterStatusNotifierItemAsync(_sysTraySrvName); + + NativeMenuExporter = _statusNotifierItem.NativeMenuExporter; + } public async void Dispose() @@ -68,7 +70,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray internal class StatusNotifierItem : IStatusNotifierItem { private event Action OnPropertyChange; - public event Action OnTitleChanged; public event Action OnIconChanged; public event Action OnAttentionIconChanged; @@ -84,19 +85,26 @@ namespace Avalonia.FreeDesktop.DBusSystemTray { var ID = Guid.NewGuid().ToString().Replace("-", ""); ObjectPath = new ObjectPath($"/StatusNotifierItem"); + var blankPixmaps = new[] { new Pixmap(0, 0, new byte[] { }), new Pixmap(0, 0, new byte[] { }) }; + + var dbusmenuPath = DBusMenuExporter.GenerateDBusMenuObjPath; + + NativeMenuExporter = DBusMenuExporter.TryCreateDetachedNativeMenu(dbusmenuPath); props = new StatusNotifierItemProperties { + Menu = "/MenuBar", // Needs a dbus menu somehow + ItemIsMenu = false, + ToolTip = new ToolTip("", blankPixmaps, "Avalonia Test Tray", ""), + Category = "", Title = "Avalonia Test Tray", Status = "Avalonia Test Tray", Id = "Avalonia Test Tray", - AttentionIconPixmap = new[] { new Pixmap(0, 0, new byte[] { }), new Pixmap(0, 0, new byte[] { }) }, - // IconPixmap = new[] { new Pixmap(1, 1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }) } }; - - InvalidateAll(); } + public INativeMenuExporter NativeMenuExporter; + public async Task ContextMenuAsync(int X, int Y) { } @@ -207,7 +215,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray } [DBusInterface("org.kde.StatusNotifierWatcher")] - interface IStatusNotifierWatcher : IDBusObject + internal interface IStatusNotifierWatcher : IDBusObject { Task RegisterStatusNotifierItemAsync(string Service); Task RegisterStatusNotifierHostAsync(string Service); @@ -226,7 +234,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray } [Dictionary] - class StatusNotifierWatcherProperties + internal class StatusNotifierWatcherProperties { public string[] RegisteredStatusNotifierItems; @@ -235,7 +243,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray public int ProtocolVersion; } - static class StatusNotifierWatcherExtensions + internal static class StatusNotifierWatcherExtensions { public static Task GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) => o.GetAsync("RegisteredStatusNotifierItems"); @@ -247,74 +255,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray o.GetAsync("ProtocolVersion"); } - [DBusInterface("org.gtk.Actions")] - interface IActions : IDBusObject - { - Task ListAsync(); - Task<(bool description, Signature, object[])> DescribeAsync(string ActionName); - 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); - } - - [DBusInterface("org.gtk.Application")] - interface IApplication : IDBusObject - { - Task ActivateAsync(IDictionary PlatformData); - Task OpenAsync(string[] Uris, string Hint, IDictionary PlatformData); - Task CommandLineAsync(ObjectPath Path, byte[][] Arguments, IDictionary PlatformData); - Task GetAsync(string prop); - Task GetAllAsync(); - Task SetAsync(string prop, object val); - Task WatchPropertiesAsync(Action handler); - } - - [Dictionary] - class ApplicationProperties - { - public bool Busy; - } - - static class ApplicationExtensions - { - public static Task GetBusyAsync(this IApplication o) => o.GetAsync("Busy"); - } - - [DBusInterface("org.freedesktop.Application")] - interface IApplication0 : IDBusObject - { - Task ActivateAsync(IDictionary PlatformData); - Task OpenAsync(string[] Uris, IDictionary PlatformData); - Task ActivateActionAsync(string ActionName, object[] Parameter, IDictionary PlatformData); - } - - [DBusInterface("org.gnome.Sysprof3.Profiler")] - interface IProfiler : IDBusObject - { - Task StartAsync(IDictionary Options, CloseSafeHandle Fd); - Task StopAsync(); - Task GetAsync(string prop); - Task GetAllAsync(); - Task SetAsync(string prop, object val); - Task WatchPropertiesAsync(Action handler); - } - - [Dictionary] - class ProfilerProperties - { - public IDictionary Capabilities; - } - - static class ProfilerExtensions - { - public static Task> GetCapabilitiesAsync(this IProfiler o) => - o.GetAsync>("Capabilities"); - } [DBusInterface("org.kde.StatusNotifierItem")] interface IStatusNotifierItem : IDBusObject @@ -337,7 +277,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray [Dictionary] - class StatusNotifierItemProperties + internal class StatusNotifierItemProperties { public string Category; @@ -388,7 +328,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray } } - public readonly struct Pixmap { public readonly int Width; diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index 093f2b12c1..27843fd354 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -48,8 +48,8 @@ namespace Avalonia.X11 public X11IconData(Bitmap bitmap) { - _width = Math.Min(bitmap.PixelSize.Width, 128); - _height = Math.Min(bitmap.PixelSize.Height, 128); + _width = Math.Min(bitmap.PixelSize.Width, 256); + _height = Math.Min(bitmap.PixelSize.Height, 256); _bdata = new uint[_width * _height]; fixed (void* ptr = _bdata) { diff --git a/src/Avalonia.X11/X11TrayIconImpl.cs b/src/Avalonia.X11/X11TrayIconImpl.cs index 6006107d21..352ac2d833 100644 --- a/src/Avalonia.X11/X11TrayIconImpl.cs +++ b/src/Avalonia.X11/X11TrayIconImpl.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -11,7 +12,7 @@ namespace Avalonia.X11 class X11TrayIconImpl : ITrayIconImpl { private readonly AvaloniaX11Platform _avaloniaX11Platform; - public INativeMenuExporter MenuExporter => null; + public INativeMenuExporter MenuExporter { get; } public Action OnClicked { get; set; } private SNIDBus sni = new SNIDBus(); @@ -20,6 +21,7 @@ namespace Avalonia.X11 { _avaloniaX11Platform = avaloniaX11Platform; sni.Initialize(); + MenuExporter = sni.NativeMenuExporter; } public void Dispose() @@ -37,6 +39,35 @@ namespace Avalonia.X11 [FieldOffset(1)] public readonly byte G; [FieldOffset(0)] public readonly byte B; + + public BGRA32(byte a, byte r, byte g, byte b) + { + A = a; + R = r; + G = g; + B = b; + } + } + + + [StructLayout(LayoutKind.Explicit)] + readonly struct ARGB32 + { + [FieldOffset(0)] public readonly byte A; + + [FieldOffset(1)] public readonly byte R; + + [FieldOffset(2)] public readonly byte G; + + [FieldOffset(3)] public readonly byte B; + + public ARGB32(byte a, byte r, byte g, byte b) + { + A = a; + R = r; + G = g; + B = b; + } } static unsafe class X11IconToPixmap @@ -47,38 +78,37 @@ namespace Avalonia.X11 { if (icon is X11IconData x11icon) { - unsafe + var w = 6; + var h = 6; + var rb = 4; + var pixelBuf = new ARGB32[w * h]; + + + var gold = new ARGB32(255, 212, 175, 55); + var red = new ARGB32(255, 255, 0, 0); + var blue = new ARGB32(255, 0, 0, 255); + + var ix = 0; + for (var y = 0; y < h; y++) { - using var l = x11icon.Lock(); - - if (l.Format != PixelFormat.Bgra8888) return; - var h = l.Size.Height; - var w = l.Size.Width; - - var totalPixels = w * h; - var totalBytes = totalPixels * 4; - var _bgraBuf = new BGRA32[totalPixels]; - var byteBuf = new byte[totalBytes]; - - fixed (void* src = &x11icon.Data[0]) - fixed (void* dst = &_bgraBuf[0]) - Buffer.MemoryCopy(src, dst, (uint)totalBytes, (uint)totalBytes); - - var byteCount = 0; - - foreach (var curPix in _bgraBuf) + var offset = y * w; + for (var x = 0; x < w; x++) { - byteBuf[byteCount++] = curPix.A; - byteBuf[byteCount++] = curPix.R; - byteBuf[byteCount++] = curPix.G; - byteBuf[byteCount++] = curPix.B; + pixelBuf[offset + x] = (ix % 2 == 1) ? gold : blue; + ix++; } - - sni.SetIcon(new Pixmap(w, h, byteBuf)); + ix++; } + + var pixmapBytes = MemoryMarshal.Cast(pixelBuf.AsSpan()).ToArray(); + + sni.SetIcon(new Pixmap(w, h, pixmapBytes)); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int PixCoord(int x, int y, int w) => x + (y *w); + public void SetIsVisible(bool visible) { } diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index f0d2d5ca8a..e39be6fc04 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -190,7 +190,7 @@ namespace Avalonia.X11 if(_popup) PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize)); if (platform.Options.UseDBusMenu) - NativeMenuExporter = DBusMenuExporter.TryCreate(_handle); + NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle); NativeControlHost = new X11NativeControlHost(_platform, this); DispatcherTimer.Run(() => {