Browse Source

more stuff

pull/6610/head
Jumar Macato 5 years ago
parent
commit
d32714559b
  1. 47
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  2. 109
      src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs
  3. 4
      src/Avalonia.X11/X11IconLoader.cs
  4. 82
      src/Avalonia.X11/X11TrayIconImpl.cs
  5. 2
      src/Avalonia.X11/X11Window.cs

47
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<NativeMenu> _menus = new HashSet<NativeMenu>();
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<IRegistrar>(
"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<IRegistrar>(
"com.canonical.AppMenu.Registrar",
"/com/canonical/AppMenu/Registrar");
if (!_disposed)
await _registar.RegisterWindowAsync(_xid, ObjectPath);
}
else
{
await _dbus.RegisterObjectAsync(this);
}
}
catch (Exception e)
{

109
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<PropertyChanges> 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<string[]> GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) =>
o.GetAsync<string[]>("RegisteredStatusNotifierItems");
@ -247,74 +255,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
o.GetAsync<int>("ProtocolVersion");
}
[DBusInterface("org.gtk.Actions")]
interface IActions : IDBusObject
{
Task<string[]> ListAsync();
Task<(bool description, Signature, object[])> DescribeAsync(string ActionName);
Task<IDictionary<string, (bool, Signature, object[])>> DescribeAllAsync();
Task ActivateAsync(string ActionName, object[] Parameter, IDictionary<string, object> PlatformData);
Task SetStateAsync(string ActionName, object Value, IDictionary<string, object> PlatformData);
Task<IDisposable> WatchChangedAsync(
Action<(string[] removals, IDictionary<string, bool> enableChanges, IDictionary<string, object> stateChanges
, IDictionary<string, (bool, Signature, object[])> additions)> handler,
Action<Exception> onError = null);
}
[DBusInterface("org.gtk.Application")]
interface IApplication : IDBusObject
{
Task ActivateAsync(IDictionary<string, object> PlatformData);
Task OpenAsync(string[] Uris, string Hint, IDictionary<string, object> PlatformData);
Task<int> CommandLineAsync(ObjectPath Path, byte[][] Arguments, IDictionary<string, object> PlatformData);
Task<T> GetAsync<T>(string prop);
Task<ApplicationProperties> GetAllAsync();
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}
[Dictionary]
class ApplicationProperties
{
public bool Busy;
}
static class ApplicationExtensions
{
public static Task<bool> GetBusyAsync(this IApplication o) => o.GetAsync<bool>("Busy");
}
[DBusInterface("org.freedesktop.Application")]
interface IApplication0 : IDBusObject
{
Task ActivateAsync(IDictionary<string, object> PlatformData);
Task OpenAsync(string[] Uris, IDictionary<string, object> PlatformData);
Task ActivateActionAsync(string ActionName, object[] Parameter, IDictionary<string, object> PlatformData);
}
[DBusInterface("org.gnome.Sysprof3.Profiler")]
interface IProfiler : IDBusObject
{
Task StartAsync(IDictionary<string, object> Options, CloseSafeHandle Fd);
Task StopAsync();
Task<T> GetAsync<T>(string prop);
Task<ProfilerProperties> GetAllAsync();
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}
[Dictionary]
class ProfilerProperties
{
public IDictionary<string, object> Capabilities;
}
static class ProfilerExtensions
{
public static Task<IDictionary<string, object>> GetCapabilitiesAsync(this IProfiler o) =>
o.GetAsync<IDictionary<string, object>>("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;

4
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)
{

82
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<ARGB32, byte>(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)
{
}

2
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(() =>
{

Loading…
Cancel
Save