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 class DBusMenuExporter
{ {
public static ITopLevelNativeMenuExporter TryCreate(IntPtr xid) public static ITopLevelNativeMenuExporter TryCreateTopLevelNativeMenu(IntPtr xid)
{ {
return null;
if (DBusHelper.Connection == null) if (DBusHelper.Connection == null)
return null; return null;
return new DBusMenuExporterImpl(DBusHelper.Connection, xid); 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 class DBusMenuExporterImpl : ITopLevelNativeMenuExporter, IDBusMenu, IDisposable
{ {
private readonly string _targetServiceName;
private readonly Connection _dbus; private readonly Connection _dbus;
private readonly uint _xid; private readonly uint _xid;
private IRegistrar _registar; private IRegistrar _registar;
@ -37,26 +51,41 @@ namespace Avalonia.FreeDesktop
private readonly HashSet<NativeMenu> _menus = new HashSet<NativeMenu>(); private readonly HashSet<NativeMenu> _menus = new HashSet<NativeMenu>();
private bool _resetQueued; private bool _resetQueued;
private int _nextId = 1; private int _nextId = 1;
private bool AppMenu = true;
public DBusMenuExporterImpl(Connection dbus, IntPtr xid) public DBusMenuExporterImpl(Connection dbus, IntPtr xid)
{ {
_dbus = dbus; _dbus = dbus;
_xid = (uint)xid.ToInt32(); _xid = (uint)xid.ToInt32();
ObjectPath = new ObjectPath("/net/avaloniaui/dbusmenu/" ObjectPath = GenerateDBusMenuObjPath;
+ Guid.NewGuid().ToString().Replace("-", ""));
SetNativeMenu(new NativeMenu()); SetNativeMenu(new NativeMenu());
Init(); Init();
} }
public DBusMenuExporterImpl(Connection dbus, ObjectPath path)
{
_dbus = dbus;
AppMenu = false;
ObjectPath = path;
SetNativeMenu(new NativeMenu());
Init();
}
async void Init() async void Init()
{ {
try try
{ {
await _dbus.RegisterObjectAsync(this); if (AppMenu)
_registar = DBusHelper.Connection.CreateProxy<IRegistrar>( {
"com.canonical.AppMenu.Registrar", await _dbus.RegisterObjectAsync(this);
"/com/canonical/AppMenu/Registrar"); _registar = DBusHelper.Connection.CreateProxy<IRegistrar>(
if (!_disposed) "com.canonical.AppMenu.Registrar",
await _registar.RegisterWindowAsync(_xid, ObjectPath); "/com/canonical/AppMenu/Registrar");
if (!_disposed)
await _registar.RegisterWindowAsync(_xid, ObjectPath);
}
else
{
await _dbus.RegisterObjectAsync(this);
}
} }
catch (Exception e) catch (Exception e)
{ {

109
src/Avalonia.FreeDesktop/DBusSystemTray/SNIDBus.cs

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Tmds.DBus; using Tmds.DBus;
[assembly: InternalsVisibleTo(Tmds.DBus.Connection.DynamicAssemblyName)] [assembly: InternalsVisibleTo(Tmds.DBus.Connection.DynamicAssemblyName)]
@ -20,11 +21,11 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
private IStatusNotifierWatcher _snw; private IStatusNotifierWatcher _snw;
private string _sysTraySrvName; private string _sysTraySrvName;
private StatusNotifierItem _statusNotifierItem; private StatusNotifierItem _statusNotifierItem;
public INativeMenuExporter NativeMenuExporter;
private static int GetTID() private static int GetTID()
{ {
trayinstanceID = 4; return trayinstanceID = new Random().Next(0, 100);
return trayinstanceID;
} }
public async void Initialize() public async void Initialize()
@ -42,11 +43,12 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
await con.RegisterObjectAsync(_statusNotifierItem); await con.RegisterObjectAsync(_statusNotifierItem);
await con.RegisterServiceAsync(_sysTraySrvName, () => await con.RegisterServiceAsync(_sysTraySrvName);
{
}); await _snw.RegisterStatusNotifierItemAsync(_sysTraySrvName);
await _snw.RegisterStatusNotifierItemAsync(_sysTraySrvName); NativeMenuExporter = _statusNotifierItem.NativeMenuExporter;
} }
public async void Dispose() public async void Dispose()
@ -68,7 +70,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
internal class StatusNotifierItem : IStatusNotifierItem internal class StatusNotifierItem : IStatusNotifierItem
{ {
private event Action<PropertyChanges> OnPropertyChange; private event Action<PropertyChanges> OnPropertyChange;
public event Action OnTitleChanged; public event Action OnTitleChanged;
public event Action OnIconChanged; public event Action OnIconChanged;
public event Action OnAttentionIconChanged; public event Action OnAttentionIconChanged;
@ -84,19 +85,26 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
{ {
var ID = Guid.NewGuid().ToString().Replace("-", ""); var ID = Guid.NewGuid().ToString().Replace("-", "");
ObjectPath = new ObjectPath($"/StatusNotifierItem"); 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 props = new StatusNotifierItemProperties
{ {
Menu = "/MenuBar", // Needs a dbus menu somehow
ItemIsMenu = false,
ToolTip = new ToolTip("", blankPixmaps, "Avalonia Test Tray", ""),
Category = "",
Title = "Avalonia Test Tray", Title = "Avalonia Test Tray",
Status = "Avalonia Test Tray", Status = "Avalonia Test Tray",
Id = "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) public async Task ContextMenuAsync(int X, int Y)
{ {
} }
@ -207,7 +215,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
} }
[DBusInterface("org.kde.StatusNotifierWatcher")] [DBusInterface("org.kde.StatusNotifierWatcher")]
interface IStatusNotifierWatcher : IDBusObject internal interface IStatusNotifierWatcher : IDBusObject
{ {
Task RegisterStatusNotifierItemAsync(string Service); Task RegisterStatusNotifierItemAsync(string Service);
Task RegisterStatusNotifierHostAsync(string Service); Task RegisterStatusNotifierHostAsync(string Service);
@ -226,7 +234,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
} }
[Dictionary] [Dictionary]
class StatusNotifierWatcherProperties internal class StatusNotifierWatcherProperties
{ {
public string[] RegisteredStatusNotifierItems; public string[] RegisteredStatusNotifierItems;
@ -235,7 +243,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
public int ProtocolVersion; public int ProtocolVersion;
} }
static class StatusNotifierWatcherExtensions internal static class StatusNotifierWatcherExtensions
{ {
public static Task<string[]> GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) => public static Task<string[]> GetRegisteredStatusNotifierItemsAsync(this IStatusNotifierWatcher o) =>
o.GetAsync<string[]>("RegisteredStatusNotifierItems"); o.GetAsync<string[]>("RegisteredStatusNotifierItems");
@ -247,74 +255,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
o.GetAsync<int>("ProtocolVersion"); 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")] [DBusInterface("org.kde.StatusNotifierItem")]
interface IStatusNotifierItem : IDBusObject interface IStatusNotifierItem : IDBusObject
@ -337,7 +277,7 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
[Dictionary] [Dictionary]
class StatusNotifierItemProperties internal class StatusNotifierItemProperties
{ {
public string Category; public string Category;
@ -388,7 +328,6 @@ namespace Avalonia.FreeDesktop.DBusSystemTray
} }
} }
public readonly struct Pixmap public readonly struct Pixmap
{ {
public readonly int Width; public readonly int Width;

4
src/Avalonia.X11/X11IconLoader.cs

@ -48,8 +48,8 @@ namespace Avalonia.X11
public X11IconData(Bitmap bitmap) public X11IconData(Bitmap bitmap)
{ {
_width = Math.Min(bitmap.PixelSize.Width, 128); _width = Math.Min(bitmap.PixelSize.Width, 256);
_height = Math.Min(bitmap.PixelSize.Height, 128); _height = Math.Min(bitmap.PixelSize.Height, 256);
_bdata = new uint[_width * _height]; _bdata = new uint[_width * _height];
fixed (void* ptr = _bdata) fixed (void* ptr = _bdata)
{ {

82
src/Avalonia.X11/X11TrayIconImpl.cs

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -11,7 +12,7 @@ namespace Avalonia.X11
class X11TrayIconImpl : ITrayIconImpl class X11TrayIconImpl : ITrayIconImpl
{ {
private readonly AvaloniaX11Platform _avaloniaX11Platform; private readonly AvaloniaX11Platform _avaloniaX11Platform;
public INativeMenuExporter MenuExporter => null; public INativeMenuExporter MenuExporter { get; }
public Action OnClicked { get; set; } public Action OnClicked { get; set; }
private SNIDBus sni = new SNIDBus(); private SNIDBus sni = new SNIDBus();
@ -20,6 +21,7 @@ namespace Avalonia.X11
{ {
_avaloniaX11Platform = avaloniaX11Platform; _avaloniaX11Platform = avaloniaX11Platform;
sni.Initialize(); sni.Initialize();
MenuExporter = sni.NativeMenuExporter;
} }
public void Dispose() public void Dispose()
@ -37,6 +39,35 @@ namespace Avalonia.X11
[FieldOffset(1)] public readonly byte G; [FieldOffset(1)] public readonly byte G;
[FieldOffset(0)] public readonly byte B; [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 static unsafe class X11IconToPixmap
@ -47,38 +78,37 @@ namespace Avalonia.X11
{ {
if (icon is X11IconData x11icon) 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(); var offset = y * w;
for (var x = 0; x < w; x++)
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)
{ {
byteBuf[byteCount++] = curPix.A; pixelBuf[offset + x] = (ix % 2 == 1) ? gold : blue;
byteBuf[byteCount++] = curPix.R; ix++;
byteBuf[byteCount++] = curPix.G;
byteBuf[byteCount++] = curPix.B;
} }
ix++;
sni.SetIcon(new Pixmap(w, h, byteBuf));
} }
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) public void SetIsVisible(bool visible)
{ {
} }

2
src/Avalonia.X11/X11Window.cs

@ -190,7 +190,7 @@ namespace Avalonia.X11
if(_popup) if(_popup)
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize));
if (platform.Options.UseDBusMenu) if (platform.Options.UseDBusMenu)
NativeMenuExporter = DBusMenuExporter.TryCreate(_handle); NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle);
NativeControlHost = new X11NativeControlHost(_platform, this); NativeControlHost = new X11NativeControlHost(_platform, this);
DispatcherTimer.Run(() => DispatcherTimer.Run(() =>
{ {

Loading…
Cancel
Save