Browse Source

Use source generator

pull/9810/head
affederaffe 3 years ago
parent
commit
2c2eed724e
  1. 3
      .gitmodules
  2. 1
      Avalonia.Desktop.slnf
  3. 7
      Avalonia.sln
  4. 173
      src/Avalonia.FreeDesktop/AppMenuRegistrar.DBus.cs
  5. 5
      src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
  6. 641
      src/Avalonia.FreeDesktop/DBus.DBus.cs
  7. 2
      src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs
  8. 627
      src/Avalonia.FreeDesktop/DBusIme/Fcitx/Fcitx.DBus.cs
  9. 10
      src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs
  10. 9
      src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs
  11. 513
      src/Avalonia.FreeDesktop/DBusIme/IBus/IBus.DBus.cs
  12. 12
      src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs
  13. 18
      src/Avalonia.FreeDesktop/DBusInterfaces.cs
  14. 500
      src/Avalonia.FreeDesktop/DBusMenu.DBus.cs
  15. 178
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  16. 18
      src/Avalonia.FreeDesktop/DBusSystemDialog.cs
  17. 190
      src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs
  18. 938
      src/Avalonia.FreeDesktop/FreeDesktopPortalDesktop.DBus.cs
  19. 350
      src/Avalonia.FreeDesktop/StatusNotifierWatcher.DBus.cs
  20. 1
      src/tools/Tmds.DBus.SourceGenerator

3
.gitmodules

@ -10,3 +10,6 @@
[submodule "Tmds.DBus"]
path = src/Linux/Tmds.DBus
url = https://github.com/affederaffe/Tmds.DBus
[submodule "src/tools/Tmds.DBus.SourceGenerator"]
path = src/tools/Tmds.DBus.SourceGenerator
url = https://github.com/affederaffe/Tmds.DBus.SourceGenerator

1
Avalonia.Desktop.slnf

@ -38,6 +38,7 @@
"src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj",
"src\\tools\\Tmds.DBus.SourceGenerator\\Tmds.DBus.SourceGenerator\\Tmds.DBus.SourceGenerator.csproj",
"src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
"src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
"src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",

7
Avalonia.sln

@ -233,6 +233,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus.Protocol", "src\Linux\Tmds.DBus\src\Tmds.DBus.Protocol\Tmds.DBus.Protocol.csproj", "{29E25263-3CC3-4D55-A042-00BA136867D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tmds.DBus.SourceGenerator", "src\tools\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator.csproj", "{5E9C0032-E460-4BC1-BCC7-6448F34DD679}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -548,6 +550,10 @@ Global
{29E25263-3CC3-4D55-A042-00BA136867D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29E25263-3CC3-4D55-A042-00BA136867D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29E25263-3CC3-4D55-A042-00BA136867D4}.Release|Any CPU.Build.0 = Release|Any CPU
{5E9C0032-E460-4BC1-BCC7-6448F34DD679}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E9C0032-E460-4BC1-BCC7-6448F34DD679}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E9C0032-E460-4BC1-BCC7-6448F34DD679}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E9C0032-E460-4BC1-BCC7-6448F34DD679}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -613,6 +619,7 @@ Global
{90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{29E25263-3CC3-4D55-A042-00BA136867D4} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{5E9C0032-E460-4BC1-BCC7-6448F34DD679} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

173
src/Avalonia.FreeDesktop/AppMenuRegistrar.DBus.cs

@ -1,173 +0,0 @@
using System;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop
{
internal class Registrar : AppMenuRegistrarObject
{
private const string Interface = "com.canonical.AppMenu.Registrar";
public Registrar(RegistrarService service, ObjectPath path) : base(service, path) { }
public Task RegisterWindowAsync(uint windowId, ObjectPath menuObjectPath)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uo",
member: "RegisterWindow",
flags: MessageFlags.NoReplyExpected);
writer.WriteUInt32(windowId);
writer.WriteObjectPath(menuObjectPath);
return writer.CreateMessage();
}
}
public Task UnregisterWindowAsync(uint windowId)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "u",
member: "UnregisterWindow");
writer.WriteUInt32(windowId);
return writer.CreateMessage();
}
}
public Task<(string Service, ObjectPath MenuObjectPath)> GetMenuForWindowAsync(uint windowId)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_so(m, (AppMenuRegistrarObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "u",
member: "GetMenuForWindow");
writer.WriteUInt32(windowId);
return writer.CreateMessage();
}
}
}
internal class RegistrarService
{
public RegistrarService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public Registrar CreateRegistrar(string path) => new(this, path);
}
internal class AppMenuRegistrarObject
{
protected AppMenuRegistrarObject(RegistrarService service, ObjectPath path)
=> (Service, Path) = (service, path);
protected RegistrarService Service { get; }
protected ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface,
MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs)
=> ((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes),
this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal,
MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs) =>
((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync<object>(rule, static (_, _)
=> null!, static (ex, _, _, hs)
=> ((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static (string, ObjectPath) ReadMessage_so(Message message, AppMenuRegistrarObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadString();
var arg1 = reader.ReadObjectPath();
return (arg0, arg1);
}
}
}

5
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@ -17,10 +17,15 @@
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\Linux\Tmds.DBus\src\Tmds.DBus.Protocol\Tmds.DBus.Protocol.csproj" />
<!--<PackageReference Include="Tmds.DBus.Protocol" Version="0.11.1-151-9f126c13809fceab8388328aff84b0d8e89b2aae" />-->
<ProjectReference Include="..\tools\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator\Tmds.DBus.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup>
<ItemGroup>
<Folder Include="obj" />
</ItemGroup>
</Project>

641
src/Avalonia.FreeDesktop/DBus.DBus.cs

@ -1,641 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop
{
internal record DBusProperties
{
public string[] Features { get; set; } = default!;
public string[] Interfaces { get; set; } = default!;
}
internal class DBus : DBusObject
{
private const string Interface = "org.freedesktop.DBus";
public DBus(DBusService service, ObjectPath path) : base(service, path) { }
public Task<string> HelloAsync()
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_s(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Hello");
return writer.CreateMessage();
}
}
public Task<uint> RequestNameAsync(string a0, uint a1)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_u(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "su",
member: "RequestName");
writer.WriteString(a0);
writer.WriteUInt32(a1);
return writer.CreateMessage();
}
}
public Task<uint> ReleaseNameAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_u(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "ReleaseName");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<uint> StartServiceByNameAsync(string a0, uint a1)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_u(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "su",
member: "StartServiceByName");
writer.WriteString(a0);
writer.WriteUInt32(a1);
return writer.CreateMessage();
}
}
public Task UpdateActivationEnvironmentAsync(Dictionary<string, string> a0)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "a{ss}",
member: "UpdateActivationEnvironment");
writer.WriteDictionary(a0);
return writer.CreateMessage();
}
}
public Task<bool> NameHasOwnerAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_b(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "NameHasOwner");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<string[]> ListNamesAsync()
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_as(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"ListNames");
return writer.CreateMessage();
}
}
public Task<string[]> ListActivatableNamesAsync()
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_as(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"ListActivatableNames");
return writer.CreateMessage();
}
}
public Task AddMatchAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "AddMatch");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task RemoveMatchAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "RemoveMatch");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<string> GetNameOwnerAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_s(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetNameOwner");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<string[]> ListQueuedOwnersAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_as(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "ListQueuedOwners");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<uint> GetConnectionUnixUserAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_u(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetConnectionUnixUser");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<uint> GetConnectionUnixProcessIDAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_u(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetConnectionUnixProcessID");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<byte[]> GetAdtAuditSessionDataAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_ay(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetAdtAuditSessionData");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task<byte[]> GetConnectionSELinuxSecurityContextAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_ay(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetConnectionSELinuxSecurityContext");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public Task ReloadConfigAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"ReloadConfig");
return writer.CreateMessage();
}
}
public Task<string> GetIdAsync()
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_s(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"GetId");
return writer.CreateMessage();
}
}
public Task<Dictionary<string, object>> GetConnectionCredentialsAsync(string a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_aesv(m, (DBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetConnectionCredentials");
writer.WriteString(a0);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchNameOwnerChangedAsync(Action<Exception?, (string A0, string A1, string A2)> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "NameOwnerChanged", static (m, s) =>
ReadMessage_sss(m, (DBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchNameLostAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "NameLost", static (m, s) =>
ReadMessage_s(m, (DBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchNameAcquiredAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "NameAcquired", static (m, s) =>
ReadMessage_s(m, (DBusObject)s!), handler, emitOnCapturedContext);
public Task SetFeaturesAsync(string[] value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("Features");
writer.WriteSignature("as");
writer.WriteArray(value);
return writer.CreateMessage();
}
}
public Task SetInterfacesAsync(string[] value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("Interfaces");
writer.WriteSignature("as");
writer.WriteArray(value);
return writer.CreateMessage();
}
}
public Task<string[]> GetFeaturesAsync() =>
Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "Features"), static (m, s) =>
ReadMessage_v_as(m, (DBusObject)s!), this);
public Task<string[]> GetInterfacesAsync() =>
Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "Interfaces"), static (m, s) =>
ReadMessage_v_as(m, (DBusObject)s!), this);
public Task<DBusProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _) =>
ReadMessage(m), this);
static DBusProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<DBusProperties>> handler, bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _) =>
ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<DBusProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<DBusProperties>(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "Features":
invalidated.Add("Features");
break;
case "Interfaces":
invalidated.Add("Interfaces");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static DBusProperties ReadProperties(ref Reader reader, List<string>? changedList = null)
{
var props = new DBusProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "Features":
reader.ReadSignature("as");
props.Features = reader.ReadArray<string>();
changedList?.Add("Features");
break;
case "Interfaces":
reader.ReadSignature("as");
props.Interfaces = reader.ReadArray<string>();
changedList?.Add("Interfaces");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal class DBusService
{
public DBusService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public DBus CreateDBus(string path) => new(this, path);
}
internal class DBusObject
{
protected DBusObject(DBusService service, ObjectPath path)
{
Service = service;
Path = path;
}
public DBusService Service { get; }
public ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface,
MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs) =>
((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes), this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal,
MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs) =>
((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync<object>(rule, static (_, _) => null!, static (ex, _, _, hs) =>
((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static string ReadMessage_s(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadString();
}
protected static uint ReadMessage_u(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadUInt32();
}
protected static bool ReadMessage_b(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadBool();
}
protected static string[] ReadMessage_as(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadArray<string>();
}
protected static byte[] ReadMessage_ay(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadArray<byte>();
}
protected static Dictionary<string, object> ReadMessage_aesv(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadDictionary<string, object>();
}
protected static (string, string, string) ReadMessage_sss(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadString();
var arg1 = reader.ReadString();
var arg2 = reader.ReadString();
return (arg0, arg1, arg2);
}
protected static string[] ReadMessage_v_as(Message message, DBusObject _)
{
var reader = message.GetBodyReader();
reader.ReadSignature("as");
return reader.ReadArray<string>();
}
}
}

2
src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs

@ -60,7 +60,7 @@ namespace Avalonia.FreeDesktop.DBusIme
{
foreach (var name in _knownNames)
{
var dbus = new DBusService(Connection, name).CreateDBus("/org/freedesktop/DBus");
var dbus = new OrgFreedesktopDBus(Connection, name, "/org/freedesktop/DBus");
_disposables.Add(await dbus.WatchNameOwnerChangedAsync(OnNameChange));
var nameOwner = await dbus.GetNameOwnerAsync(name);
OnNameChange(null, (name, null, nameOwner));

627
src/Avalonia.FreeDesktop/DBusIme/Fcitx/Fcitx.DBus.cs

@ -1,627 +0,0 @@
using System;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop.DBusIme.Fcitx
{
internal class InputContext : FcitxObject
{
private const string Interface = "org.fcitx.Fcitx.InputContext";
public InputContext(FcitxService service, ObjectPath path) : base(service, path) { }
public Task FocusInAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusIn");
return writer.CreateMessage();
}
}
public Task FocusOutAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusOut");
return writer.CreateMessage();
}
}
public Task ResetAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Reset");
return writer.CreateMessage();
}
}
public Task SetCursorRectAsync(int x, int y, int w, int h)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "iiii",
member: "SetCursorRect");
writer.WriteInt32(x);
writer.WriteInt32(y);
writer.WriteInt32(w);
writer.WriteInt32(h);
return writer.CreateMessage();
}
}
public Task SetCapacityAsync(uint caps)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "u",
member: "SetCapacity");
writer.WriteUInt32(caps);
return writer.CreateMessage();
}
}
public Task SetSurroundingTextAsync(string text, uint cursor, uint anchor)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "suu",
member: "SetSurroundingText");
writer.WriteString(text);
writer.WriteUInt32(cursor);
writer.WriteUInt32(anchor);
return writer.CreateMessage();
}
}
public Task SetSurroundingTextPositionAsync(uint cursor, uint anchor)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uu",
member: "SetSurroundingTextPosition");
writer.WriteUInt32(cursor);
writer.WriteUInt32(anchor);
return writer.CreateMessage();
}
}
public Task DestroyICAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"DestroyIC");
return writer.CreateMessage();
}
}
public Task<int> ProcessKeyEventAsync(uint keyval, uint keycode, uint state, int type, uint time)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_i(m, (FcitxObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uuuiu",
member: "ProcessKeyEvent");
writer.WriteUInt32(keyval);
writer.WriteUInt32(keycode);
writer.WriteUInt32(state);
writer.WriteInt32(type);
writer.WriteUInt32(time);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchCommitStringAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "CommitString", static (m, s) =>
ReadMessage_s(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchCurrentIMAsync(Action<Exception?, (string Name, string UniqueName, string LangCode)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "CurrentIM", static (m, s) =>
ReadMessage_sss(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdateFormattedPreeditAsync(Action<Exception?, ((string, int)[] Str, int Cursorpos)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdateFormattedPreedit", static (m, s) =>
ReadMessage_arsizi(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchForwardKeyAsync(Action<Exception?, (uint Keyval, uint State, int Type)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "ForwardKey", static (m, s) =>
ReadMessage_uui(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchDeleteSurroundingTextAsync(Action<Exception?, (int Offset, uint Nchar)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "DeleteSurroundingText", static (m, s) =>
ReadMessage_iu(m, (FcitxObject)s!), handler, emitOnCapturedContext);
}
internal class InputMethod : FcitxObject
{
private const string Interface = "org.fcitx.Fcitx.InputMethod";
public InputMethod(FcitxService service, ObjectPath path) : base(service, path) { }
public Task<(int Icid, bool Enable, uint Keyval1, uint State1, uint Keyval2, uint State2)> CreateICv3Async(string appname, int pid)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_ibuuuu(m, (FcitxObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "si",
member: "CreateICv3");
writer.WriteString(appname);
writer.WriteInt32(pid);
return writer.CreateMessage();
}
}
}
internal class InputContext1 : FcitxObject
{
private const string Interface = "org.fcitx.Fcitx.InputContext1";
public InputContext1(FcitxService service, ObjectPath path) : base(service, path) { }
public Task FocusInAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusIn");
return writer.CreateMessage();
}
}
public Task FocusOutAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusOut");
return writer.CreateMessage();
}
}
public Task ResetAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Reset");
return writer.CreateMessage();
}
}
public Task SetCursorRectAsync(int x, int y, int w, int h)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "iiii",
member: "SetCursorRect");
writer.WriteInt32(x);
writer.WriteInt32(y);
writer.WriteInt32(w);
writer.WriteInt32(h);
return writer.CreateMessage();
}
}
public Task SetCapabilityAsync(ulong caps)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "t",
member: "SetCapability");
writer.WriteUInt64(caps);
return writer.CreateMessage();
}
}
public Task SetSurroundingTextAsync(string text, uint cursor, uint anchor)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "suu",
member: "SetSurroundingText");
writer.WriteString(text);
writer.WriteUInt32(cursor);
writer.WriteUInt32(anchor);
return writer.CreateMessage();
}
}
public Task SetSurroundingTextPositionAsync(uint cursor, uint anchor)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uu",
member: "SetSurroundingTextPosition");
writer.WriteUInt32(cursor);
writer.WriteUInt32(anchor);
return writer.CreateMessage();
}
}
public Task DestroyICAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"DestroyIC");
return writer.CreateMessage();
}
}
public Task<bool> ProcessKeyEventAsync(uint keyval, uint keycode, uint state, bool type, uint time)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_b(m, (FcitxObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uuubu",
member: "ProcessKeyEvent");
writer.WriteUInt32(keyval);
writer.WriteUInt32(keycode);
writer.WriteUInt32(state);
writer.WriteBool(type);
writer.WriteUInt32(time);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchCommitStringAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "CommitString", static (m, s) =>
ReadMessage_s(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchCurrentIMAsync(Action<Exception?, (string Name, string UniqueName, string LangCode)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "CurrentIM", static (m, s) =>
ReadMessage_sss(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdateFormattedPreeditAsync(Action<Exception?, ((string, int)[] Str, int Cursorpos)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdateFormattedPreedit", static (m, s) =>
ReadMessage_arsizi(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchForwardKeyAsync(Action<Exception?, (uint Keyval, uint State, bool Type)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "ForwardKey", static (m, s) =>
ReadMessage_uub(m, (FcitxObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchDeleteSurroundingTextAsync(Action<Exception?, (int Offset, uint Nchar)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "DeleteSurroundingText", static (m, s) =>
ReadMessage_iu(m, (FcitxObject)s!), handler, emitOnCapturedContext);
}
internal class InputMethod1 : FcitxObject
{
private const string Interface = "org.fcitx.Fcitx.InputMethod1";
public InputMethod1(FcitxService service, ObjectPath path) : base(service, path) { }
public Task<(ObjectPath A0, byte[] A1)> CreateInputContextAsync((string, string)[] a0)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_oay(m, (FcitxObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "a(ss)",
member: "CreateInputContext");
writer.WriteArray(a0);
return writer.CreateMessage();
}
}
}
internal class FcitxService
{
public FcitxService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public InputContext CreateInputContext(string path) => new(this, path);
public InputMethod CreateInputMethod(string path) => new(this, path);
public InputContext1 CreateInputContext1(string path) => new(this, path);
public InputMethod1 CreateInputMethod1(string path) => new(this, path);
}
internal class FcitxObject
{
protected FcitxObject(FcitxService service, ObjectPath path)
=> (Service, Path) = (service, path);
public FcitxService Service { get; }
public ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface,
MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs) =>
((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes),
this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal,
MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs) =>
((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync<object>(rule, static (_, _) =>
null!, static (ex, _, _, hs) =>
((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static int ReadMessage_i(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
return reader.ReadInt32();
}
protected static string ReadMessage_s(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
return reader.ReadString();
}
protected static (string, string, string) ReadMessage_sss(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadString();
var arg1 = reader.ReadString();
var arg2 = reader.ReadString();
return (arg0, arg1, arg2);
}
protected static ((string, int)[], int) ReadMessage_arsizi(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadArray<(string, int)>();
var arg1 = reader.ReadInt32();
return (arg0, arg1);
}
protected static (uint, uint, int) ReadMessage_uui(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadUInt32();
var arg2 = reader.ReadInt32();
return (arg0, arg1, arg2);
}
protected static (int, uint) ReadMessage_iu(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadInt32();
var arg1 = reader.ReadUInt32();
return (arg0, arg1);
}
protected static (int, bool, uint, uint, uint, uint) ReadMessage_ibuuuu(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadInt32();
var arg1 = reader.ReadBool();
var arg2 = reader.ReadUInt32();
var arg3 = reader.ReadUInt32();
var arg4 = reader.ReadUInt32();
var arg5 = reader.ReadUInt32();
return (arg0, arg1, arg2, arg3, arg4, arg5);
}
protected static bool ReadMessage_b(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
return reader.ReadBool();
}
protected static (uint, uint, bool) ReadMessage_uub(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadUInt32();
var arg2 = reader.ReadBool();
return (arg0, arg1, arg2);
}
protected static (ObjectPath, byte[]) ReadMessage_oay(Message message, FcitxObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadObjectPath();
var arg1 = reader.ReadArray<byte>();
return (arg0, arg1);
}
}
}

10
src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxICWrapper.cs

@ -5,15 +5,15 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
{
internal class FcitxICWrapper
{
private readonly InputContext1? _modern;
private readonly InputContext? _old;
private readonly OrgFcitxFcitxInputContext1? _modern;
private readonly OrgFcitxFcitxInputContext? _old;
public FcitxICWrapper(InputContext old)
public FcitxICWrapper(OrgFcitxFcitxInputContext old)
{
_old = old;
}
public FcitxICWrapper(InputContext1 modern)
public FcitxICWrapper(OrgFcitxFcitxInputContext1 modern)
{
_modern = modern;
}
@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
public ValueTask<IDisposable?> WatchForwardKeyAsync(Action<Exception?, (uint keyval, uint state, int type)> handler) =>
_old?.WatchForwardKeyAsync(handler)
?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.Keyval, ev.State, ev.Type ? 1 : 0)))
?? _modern?.WatchForwardKeyAsync((e, ev) => handler.Invoke(e, (ev.keyval, ev.state, ev.type ? 1 : 0)))
?? new ValueTask<IDisposable?>(default(IDisposable?));
public Task SetCapacityAsync(uint flags) =>

9
src/Avalonia.FreeDesktop/DBusIme/Fcitx/FcitxX11TextInputMethod.cs

@ -17,21 +17,20 @@ namespace Avalonia.FreeDesktop.DBusIme.Fcitx
protected override async Task<bool> Connect(string name)
{
var service = new FcitxService(Connection, name);
if (name == "org.fcitx.Fcitx")
{
var method = service.CreateInputMethod("/inputmethod");
var method = new OrgFcitxFcitxInputMethod(Connection, name, "/inputmethod");
var resp = await method.CreateICv3Async(GetAppName(),
Process.GetCurrentProcess().Id);
var proxy = service.CreateInputContext($"/inputcontext_{resp.Icid}");
var proxy = new OrgFcitxFcitxInputContext(Connection, name, $"/inputcontext_{resp.icid}");
_context = new FcitxICWrapper(proxy);
}
else
{
var method = service.CreateInputMethod1("/inputmethod");
var method = new OrgFcitxFcitxInputMethod1(Connection, name, "/inputmethod");
var resp = await method.CreateInputContextAsync(new[] { ("appName", GetAppName()) });
var proxy = service.CreateInputContext1(resp.A0);
var proxy = new OrgFcitxFcitxInputContext1(Connection, name, resp.Item1);
_context = new FcitxICWrapper(proxy);
}

513
src/Avalonia.FreeDesktop/DBusIme/IBus/IBus.DBus.cs

@ -1,513 +0,0 @@
using System;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop.DBusIme.IBus
{
internal class Portal : IBusObject
{
private const string Interface = "org.freedesktop.IBus.Portal";
public Portal(IBusService service, ObjectPath path) : base(service, path) { }
public Task<ObjectPath> CreateInputContextAsync(string clientName)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (IBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "CreateInputContext");
writer.WriteString(clientName);
return writer.CreateMessage();
}
}
}
internal class InputContext : IBusObject
{
private const string Interface = "org.freedesktop.IBus.InputContext";
public InputContext(IBusService service, ObjectPath path) : base(service, path) { }
public Task<bool> ProcessKeyEventAsync(uint keyval, uint keycode, uint state)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_b(m, (IBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "uuu",
member: "ProcessKeyEvent");
writer.WriteUInt32(keyval);
writer.WriteUInt32(keycode);
writer.WriteUInt32(state);
return writer.CreateMessage();
}
}
public Task SetCursorLocationAsync(int x, int y, int w, int h)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "iiii",
member: "SetCursorLocation");
writer.WriteInt32(x);
writer.WriteInt32(y);
writer.WriteInt32(w);
writer.WriteInt32(h);
return writer.CreateMessage();
}
}
public Task SetCursorLocationRelativeAsync(int x, int y, int w, int h)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "iiii",
member: "SetCursorLocationRelative");
writer.WriteInt32(x);
writer.WriteInt32(y);
writer.WriteInt32(w);
writer.WriteInt32(h);
return writer.CreateMessage();
}
}
public Task ProcessHandWritingEventAsync(double[] coordinates)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ad",
member: "ProcessHandWritingEvent");
writer.WriteArray(coordinates);
return writer.CreateMessage();
}
}
public Task CancelHandWritingAsync(uint nStrokes)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "u",
member: "CancelHandWriting");
writer.WriteUInt32(nStrokes);
return writer.CreateMessage();
}
}
public Task FocusInAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusIn");
return writer.CreateMessage();
}
}
public Task FocusOutAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"FocusOut");
return writer.CreateMessage();
}
}
public Task ResetAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Reset");
return writer.CreateMessage();
}
}
public Task SetCapabilitiesAsync(uint caps)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "u",
member: "SetCapabilities");
writer.WriteUInt32(caps);
return writer.CreateMessage();
}
}
public Task PropertyActivateAsync(string name, uint state)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "su",
member: "PropertyActivate");
writer.WriteString(name);
writer.WriteUInt32(state);
return writer.CreateMessage();
}
}
public Task SetEngineAsync(string name)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "SetEngine");
writer.WriteString(name);
return writer.CreateMessage();
}
}
public Task<object> GetEngineAsync()
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_v(m, (IBusObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"GetEngine");
return writer.CreateMessage();
}
}
public Task SetSurroundingTextAsync(object text, uint cursorPos, uint anchorPos)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "vuu",
member: "SetSurroundingText");
writer.WriteVariant(text);
writer.WriteUInt32(cursorPos);
writer.WriteUInt32(anchorPos);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchCommitTextAsync(Action<Exception?, object> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "CommitText", static (m, s) =>
ReadMessage_v(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchForwardKeyEventAsync(Action<Exception?, (uint Keyval, uint Keycode, uint State)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "ForwardKeyEvent", static (m, s) =>
ReadMessage_uuu(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdatePreeditTextAsync(Action<Exception?, (object Text, uint CursorPos, bool Visible)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdatePreeditText", static (m, s) =>
ReadMessage_vub(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdatePreeditTextWithModeAsync(
Action<Exception?, (object Text, uint CursorPos, bool Visible, uint Mode)> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdatePreeditTextWithMode", static (m, s) =>
ReadMessage_vubu(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchShowPreeditTextAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ShowPreeditText", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchHidePreeditTextAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "HidePreeditText", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdateAuxiliaryTextAsync(Action<Exception?, (object Text, bool Visible)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdateAuxiliaryText", static (m, s) =>
ReadMessage_vb(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchShowAuxiliaryTextAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ShowAuxiliaryText", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchHideAuxiliaryTextAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "HideAuxiliaryText", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdateLookupTableAsync(Action<Exception?, (object Table, bool Visible)> handler,
bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "UpdateLookupTable", static (m, s) =>
ReadMessage_vb(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchShowLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ShowLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchHideLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "HideLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchPageUpLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "PageUpLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchPageDownLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "PageDownLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchCursorUpLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "CursorUpLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchCursorDownLookupTableAsync(Action<Exception?> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "CursorDownLookupTable", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchRegisterPropertiesAsync(Action<Exception?, object> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "RegisterProperties", static (m, s) => ReadMessage_v(m, (IBusObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchUpdatePropertyAsync(Action<Exception?, object> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "UpdateProperty", static (m, s) => ReadMessage_v(m, (IBusObject)s!), handler, emitOnCapturedContext);
}
internal class Service : IBusObject
{
private const string Interface = "org.freedesktop.IBus.Service";
public Service(IBusService service, ObjectPath path) : base(service, path) { }
public Task DestroyAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Destroy");
return writer.CreateMessage();
}
}
}
internal class IBusService
{
public IBusService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public Portal CreatePortal(string path) => new(this, path);
public InputContext CreateInputContext(string path) => new(this, path);
public Service CreateService(string path) => new(this, path);
}
internal class IBusObject
{
protected IBusObject(IBusService service, ObjectPath path)
=> (Service, Path) = (service, path);
public IBusService Service { get; }
public ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface,
MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs) =>
((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes),
this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal,
MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs) => ((Action<Exception?, TArg>)hs!).Invoke(ex, arg),
this, handler, emitOnCapturedContext);
}
public ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync<object>(rule, static (_, _) => null!, static (ex, _, _, hs) => ((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static ObjectPath ReadMessage_o(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadObjectPath();
}
protected static bool ReadMessage_b(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadBool();
}
protected static object ReadMessage_v(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
return reader.ReadVariant();
}
protected static (uint, uint, uint) ReadMessage_uuu(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadUInt32();
var arg2 = reader.ReadUInt32();
return (arg0, arg1, arg2);
}
protected static (object, uint, bool) ReadMessage_vub(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadVariant();
var arg1 = reader.ReadUInt32();
var arg2 = reader.ReadBool();
return (arg0, arg1, arg2);
}
protected static (object, uint, bool, uint) ReadMessage_vubu(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadVariant();
var arg1 = reader.ReadUInt32();
var arg2 = reader.ReadBool();
var arg3 = reader.ReadUInt32();
return (arg0, arg1, arg2, arg3);
}
protected static (object, bool) ReadMessage_vb(Message message, IBusObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadVariant();
var arg1 = reader.ReadBool();
return (arg0, arg1);
}
}
}

12
src/Avalonia.FreeDesktop/DBusIme/IBus/IBusX11TextInputMethod.cs

@ -9,17 +9,17 @@ namespace Avalonia.FreeDesktop.DBusIme.IBus
{
internal class IBusX11TextInputMethod : DBusTextInputMethodBase
{
private Service? _service;
private InputContext? _context;
private OrgFreedesktopIBusService? _service;
private OrgFreedesktopIBusInputContext? _context;
public IBusX11TextInputMethod(Connection connection) : base(connection, "org.freedesktop.portal.IBus") { }
protected override async Task<bool> Connect(string name)
{
var service = new IBusService(Connection, name);
var path = await service.CreatePortal("/org/freedesktop/IBus").CreateInputContextAsync(GetAppName());
_context = service.CreateInputContext(path);
_service = service.CreateService(path);
var portal = new OrgFreedesktopIBusPortal(Connection, name, "/org/freedesktop/IBus");
var path = await portal.CreateInputContextAsync(GetAppName());
_service = new OrgFreedesktopIBusService(Connection, name, path);
_context = new OrgFreedesktopIBusInputContext(Connection, name, path);
AddDisposable(await _context.WatchCommitTextAsync(OnCommitText));
AddDisposable(await _context.WatchForwardKeyEventAsync(OnForwardKey));
Enqueue(() => _context.SetCapabilitiesAsync((uint)IBusCapability.CapFocus));

18
src/Avalonia.FreeDesktop/DBusInterfaces.cs

@ -0,0 +1,18 @@
using Tmds.DBus.SourceGenerator;
namespace Avalonia.FreeDesktop
{
[DBusInterface("./DBusXml/DBus.xml")]
[DBusInterface("./DBusXml/StatusNotifierWatcher.xml")]
[DBusInterface("./DBusXml/com.canonical.AppMenu.Registrar.xml")]
[DBusInterface("./DBusXml/org.fcitx.Fcitx.InputContext.xml")]
[DBusInterface("./DBusXml/org.fcitx.Fcitx.InputMethod.xml")]
[DBusInterface("./DBusXml/org.fcitx.Fcitx.InputContext1.xml")]
[DBusInterface("./DBusXml/org.fcitx.Fcitx.InputMethod1.xml")]
[DBusInterface("./DBusXml/org.freedesktop.portal.FileChooser.xml")]
[DBusInterface("./DBusXml/org.freedesktop.portal.Request.xml")]
[DBusInterface("./DBusXml/org.freedesktop.IBus.Portal.xml")]
[DBusHandler("./DBusXml/DBusMenu.xml")]
[DBusHandler("./DBusXml/StatusNotifierItem.xml")]
internal class DBusInterfaces { }
}

500
src/Avalonia.FreeDesktop/DBusMenu.DBus.cs

@ -1,500 +0,0 @@
using System;
using Tmds.DBus.Protocol;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Avalonia.FreeDesktop
{
internal record DBusMenuProperties
{
public uint Version { get; set; }
public string TextDirection { get; set; } = default!;
public string Status { get; set; } = default!;
public string[] IconThemePath { get; set; } = default!;
}
internal class DBusMenu : DBusMenuObject
{
private const string Interface = "com.canonical.dbusmenu";
public DBusMenu(DBusMenuService service, ObjectPath path) : base(service, path) { }
public Task<(uint Revision, (int, Dictionary<string, object>, object[]) Layout)> GetLayoutAsync(int parentId, int recursionDepth, string[] propertyNames)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_uriaesvavz(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "iias",
member: "GetLayout");
writer.WriteInt32(parentId);
writer.WriteInt32(recursionDepth);
writer.WriteArray(propertyNames);
return writer.CreateMessage();
}
}
public Task<(int, Dictionary<string, object>)[]> GetGroupPropertiesAsync(int[] ids, string[] propertyNames)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_ariaesvz(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "aias",
member: "GetGroupProperties");
writer.WriteArray(ids);
writer.WriteArray(propertyNames);
return writer.CreateMessage();
}
}
public Task<object> GetPropertyAsync(int id, string name)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_v(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "is",
member: "GetProperty");
writer.WriteInt32(id);
writer.WriteString(name);
return writer.CreateMessage();
}
}
public Task EventAsync(int id, string eventId, object data, uint timestamp)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "isvu",
member: "Event");
writer.WriteInt32(id);
writer.WriteString(eventId);
writer.WriteVariant(data);
writer.WriteUInt32(timestamp);
return writer.CreateMessage();
}
}
public Task<int[]> EventGroupAsync((int, string, object, uint)[] events)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_ai(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "a(isvu)",
member: "EventGroup");
writer.WriteArray(events);
return writer.CreateMessage();
}
}
public Task<bool> AboutToShowAsync(int id)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_b(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "i",
member: "AboutToShow");
writer.WriteInt32(id);
return writer.CreateMessage();
}
}
public Task<(int[] UpdatesNeeded, int[] IdErrors)> AboutToShowGroupAsync(int[] ids)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, _) => ReadMessage_aiai(m), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ai",
member: "AboutToShowGroup");
writer.WriteArray(ids);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchItemsPropertiesUpdatedAsync(Action<Exception?, ((int, Dictionary<string, object>)[] UpdatedProps, (int, string[])[] RemovedProps)> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ItemsPropertiesUpdated", static (m, _) => ReadMessage_ariaesvzariasz(m), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchLayoutUpdatedAsync(Action<Exception?, (uint Revision, int Parent)> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "LayoutUpdated", static (m, _) => ReadMessage_ui(m), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchItemActivationRequestedAsync(Action<Exception?, (int Id, uint Timestamp)> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ItemActivationRequested", static (m, _) => ReadMessage_iu(m), handler, emitOnCapturedContext);
public Task SetVersionAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("Version");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task SetTextDirectionAsync(string value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("TextDirection");
writer.WriteSignature("s");
writer.WriteString(value);
return writer.CreateMessage();
}
}
public Task SetStatusAsync(string value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("Status");
writer.WriteSignature("s");
writer.WriteString(value);
return writer.CreateMessage();
}
}
public Task SetIconThemePathAsync(string[] value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("IconThemePath");
writer.WriteSignature("as");
writer.WriteArray(value);
return writer.CreateMessage();
}
}
public Task<uint> GetVersionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "Version"), static (m, _) => ReadMessage_v_u(m), this);
public Task<string> GetTextDirectionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "TextDirection"), static (m, _) => ReadMessage_v_s(m), this);
public Task<string> GetStatusAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "Status"), static (m, _) => ReadMessage_v_s(m), this);
public Task<string[]> GetIconThemePathAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "IconThemePath"), static (m, _) => ReadMessage_v_as(m), this);
public Task<DBusMenuProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _) => ReadMessage(m), this);
static DBusMenuProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<DBusMenuProperties>> handler, bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _) => ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<DBusMenuProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<DBusMenuProperties>(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "Version": invalidated.Add("Version"); break;
case "TextDirection": invalidated.Add("TextDirection"); break;
case "Status": invalidated.Add("Status"); break;
case "IconThemePath": invalidated.Add("IconThemePath"); break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static DBusMenuProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new DBusMenuProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "Version":
reader.ReadSignature("u");
props.Version = reader.ReadUInt32();
changedList?.Add("Version");
break;
case "TextDirection":
reader.ReadSignature("s");
props.TextDirection = reader.ReadString();
changedList?.Add("TextDirection");
break;
case "Status":
reader.ReadSignature("s");
props.Status = reader.ReadString();
changedList?.Add("Status");
break;
case "IconThemePath":
reader.ReadSignature("as");
props.IconThemePath = reader.ReadArray<string>();
changedList?.Add("IconThemePath");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal class DBusMenuService
{
public Connection Connection { get; }
public string Destination { get; }
public DBusMenuService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public DBusMenu CreateDbusmenu(string path) => new(this, path);
}
internal class DBusMenuObject
{
public DBusMenuService Service { get; }
public ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected DBusMenuObject(DBusMenuService service, ObjectPath path)
=> (Service, Path) = (service, path);
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface, MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs)
=> ((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes), this, handler, emitOnCapturedContext);
}
protected ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal, MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs)
=> ((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
protected ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync<object>(rule, static (_, _)
=> null!, static (ex, _, _, hs)
=> ((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static (uint, (int, Dictionary<string, object>, object[])) ReadMessage_uriaesvavz(Message message)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadStruct<int, Dictionary<string, object>, object[]>();
return (arg0, arg1);
}
protected static (int, Dictionary<string, object>)[] ReadMessage_ariaesvz(Message message)
{
var reader = message.GetBodyReader();
return reader.ReadArray<(int, Dictionary<string, object>)>();
}
protected static object ReadMessage_v(Message message)
{
var reader = message.GetBodyReader();
return reader.ReadVariant();
}
protected static int[] ReadMessage_ai(Message message)
{
var reader = message.GetBodyReader();
return reader.ReadArray<int>();
}
protected static bool ReadMessage_b(Message message)
{
var reader = message.GetBodyReader();
return reader.ReadBool();
}
protected static (int[], int[]) ReadMessage_aiai(Message message)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadArray<int>();
var arg1 = reader.ReadArray<int>();
return (arg0, arg1);
}
protected static ((int, Dictionary<string, object>)[], (int, string[])[]) ReadMessage_ariaesvzariasz(Message message)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadArray<(int, Dictionary<string, object>)>();
var arg1 = reader.ReadArray<(int, string[])>();
return (arg0, arg1);
}
protected static (uint, int) ReadMessage_ui(Message message)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadInt32();
return (arg0, arg1);
}
protected static (int, uint) ReadMessage_iu(Message message)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadInt32();
var arg1 = reader.ReadUInt32();
return (arg0, arg1);
}
protected static uint ReadMessage_v_u(Message message)
{
var reader = message.GetBodyReader();
reader.ReadSignature("u");
return reader.ReadUInt32();
}
protected static string ReadMessage_v_s(Message message)
{
var reader = message.GetBodyReader();
reader.ReadSignature("s");
return reader.ReadString();
}
protected static string[] ReadMessage_v_as(Message message)
{
var reader = message.GetBodyReader();
reader.ReadSignature("as");
return reader.ReadArray<string>();
}
}
}

178
src/Avalonia.FreeDesktop/DBusMenuExporter.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
@ -25,16 +24,15 @@ namespace Avalonia.FreeDesktop
public static string GenerateDBusMenuObjPath => $"/net/avaloniaui/dbusmenu/{Guid.NewGuid():N}";
private class DBusMenuExporterImpl : ITopLevelNativeMenuExporter, IMethodHandler, IDisposable
private class DBusMenuExporterImpl : ComCanonicalDbusmenu, ITopLevelNativeMenuExporter, IDisposable
{
private readonly Connection _connection;
private readonly DBusMenuProperties _dBusMenuProperties = new() { Version = 2, Status = "normal" };
private readonly Dictionary<int, NativeMenuItemBase> _idsToItems = new();
private readonly Dictionary<NativeMenuItemBase, int> _itemsToIds = new();
private readonly HashSet<NativeMenu> _menus = new();
private readonly uint _xid;
private readonly bool _appMenu = true;
private Registrar? _registrar;
private ComCanonicalAppMenuRegistrar? _registrar;
private NativeMenu? _menu;
private bool _disposed;
private uint _revision = 1;
@ -59,7 +57,40 @@ namespace Avalonia.FreeDesktop
Init();
}
public string Path { get; }
public override string Path { get; }
protected override (uint revision, (int, Dictionary<string, object>, object[]) layout) OnGetLayout(int parentId, int recursionDepth, string[] propertyNames)
{
var menu = GetMenu(parentId);
var layout = GetLayout(menu.item, menu.menu, recursionDepth, propertyNames);
if (!IsNativeMenuExported)
{
IsNativeMenuExported = true;
Dispatcher.UIThread.Post(() => OnIsNativeMenuExportedChanged?.Invoke(this, EventArgs.Empty));
}
return (_revision, layout);
}
protected override (int, Dictionary<string, object>)[] OnGetGroupProperties(int[] ids, string[] propertyNames) =>
ids.Select(id => (id, GetProperties(GetMenu(id), propertyNames))).ToArray();
protected override object OnGetProperty(int id, string name) => GetProperty(GetMenu(id), name) ?? 0;
protected override void OnEvent(int id, string eventId, object data, uint timestamp) =>
Dispatcher.UIThread.Post(() => HandleEvent(id, eventId, data, timestamp));
protected override int[] OnEventGroup((int, string, object, uint)[] events)
{
foreach (var e in events)
Dispatcher.UIThread.Post(() => HandleEvent(e.Item1, e.Item2, e.Item3, e.Item4));
return Array.Empty<int>();
}
protected override bool OnAboutToShow(int id) => false;
protected override (int[] updatesNeeded, int[] idErrors) OnAboutToShowGroup(int[] ids) =>
(Array.Empty<int>(), Array.Empty<int>());
private async void Init()
{
@ -69,8 +100,7 @@ namespace Avalonia.FreeDesktop
var services = await _connection.ListServicesAsync();
if (!services.Contains("com.canonical.AppMenu.Registrar"))
return;
_registrar = new RegistrarService(_connection, "com.canonical.AppMenu.Registrar")
.CreateRegistrar("/com/canonical/AppMenu/Registrar");
_registrar = new ComCanonicalAppMenuRegistrar(_connection, "com.canonical.AppMenu.Registrar", "/com/canonical/AppMenu/Registrar");
if (!_disposed)
await _registrar.RegisterWindowAsync(_xid, Path);
// It's not really important if this code succeeds,
@ -297,140 +327,6 @@ namespace Avalonia.FreeDesktop
}
}
public ValueTask HandleMethodAsync(MethodContext context)
{
switch (context.Request.InterfaceAsString)
{
case "com.canonical.dbusmenu":
switch (context.Request.MemberAsString, context.Request.SignatureAsString)
{
case ("GetLayout", "iias"):
{
using var writer = context.CreateReplyWriter("u(ia{sv}av)");
var reader = context.Request.GetBodyReader();
var parentId = reader.ReadInt32();
var recursionDepth = reader.ReadInt32();
var propertyNames = reader.ReadArray<string>();
var menu = GetMenu(parentId);
var layout = GetLayout(menu.item, menu.menu, recursionDepth, propertyNames);
writer.WriteUInt32(_revision);
writer.WriteStruct(layout);
if (!IsNativeMenuExported)
{
IsNativeMenuExported = true;
Dispatcher.UIThread.Post(() => OnIsNativeMenuExportedChanged?.Invoke(this, EventArgs.Empty));
}
context.Reply(writer.CreateMessage());
break;
}
case ("GetGroupProperties", "aias"):
{
using var writer = context.CreateReplyWriter("a(ia{sv})");
var reader = context.Request.GetBodyReader();
var ids = reader.ReadArray<int>();
var propertyNames = reader.ReadArray<string>();
var arrayStart = writer.WriteArrayStart(DBusType.Struct);
foreach (var id in ids)
{
var item = GetMenu(id);
var props = GetProperties(item, propertyNames);
writer.WriteStruct((id, props));
}
writer.WriteArrayEnd(arrayStart);
context.Reply(writer.CreateMessage());
break;
}
case ("GetProperty", "is"):
{
using var writer = context.CreateReplyWriter("v");
var reader = context.Request.GetBodyReader();
var id = reader.ReadInt32();
var name = reader.ReadString();
writer.WriteVariant(GetProperty(GetMenu(id), name) ?? 0);
context.Reply(writer.CreateMessage());
break;
}
case ("Event", "isvu"):
{
var reader = context.Request.GetBodyReader();
var id = reader.ReadInt32();
var eventId = reader.ReadString();
var data = reader.ReadVariant();
var timestamp = reader.ReadUInt32();
Dispatcher.UIThread.Post(() => HandleEvent(id, eventId, data, timestamp));
break;
}
case ("EventGroup", "a(isvu)"):
{
using var writer = context.CreateReplyWriter("ai");
var reader = context.Request.GetBodyReader();
var events = reader.ReadArray<(int Id, string EventId, object Data, uint Timestamp)>();
foreach (var e in events)
Dispatcher.UIThread.Post(() => HandleEvent(e.Id, e.EventId, e.Data, e.Timestamp));
writer.WriteArray(Array.Empty<int>());
context.Reply(writer.CreateMessage());
break;
}
case ("AboutToShow", "i"):
{
using var writer = context.CreateReplyWriter("b");
writer.WriteBool(false);
context.Reply(writer.CreateMessage());
break;
}
case ("AboutToShowGroup", "ai"):
{
using var writer = context.CreateReplyWriter("aiai");
writer.WriteStruct((Array.Empty<int>(), Array.Empty<int>()));
context.Reply(writer.CreateMessage());
break;
}
}
break;
case "org.freedesktop.DBus.Properties":
switch (context.Request.MemberAsString, context.Request.SignatureAsString)
{
case ("Version", "u"):
{
using var writer = context.CreateReplyWriter("u");
writer.WriteUInt32(_dBusMenuProperties.Version);
context.Reply(writer.CreateMessage());
break;
}
case ("TextDirection", "s"):
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_dBusMenuProperties.TextDirection);
context.Reply(writer.CreateMessage());
break;
}
case ("Status", "s"):
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_dBusMenuProperties.Status);
context.Reply(writer.CreateMessage());
break;
}
case ("IconThemePath", "as"):
{
using var writer = context.CreateReplyWriter("as");
writer.WriteArray(_dBusMenuProperties.IconThemePath);
context.Reply(writer.CreateMessage());
break;
}
}
break;
}
return default;
}
public bool RunMethodHandlerSynchronously(Message message) => true;
private void EmitUIntIntSignal(string member, uint arg0, int arg1)
{
using var writer = _connection.GetMessageWriter();

18
src/Avalonia.FreeDesktop/DBusSystemDialog.cs

@ -19,18 +19,18 @@ namespace Avalonia.FreeDesktop
return null;
var services = await DBusHelper.Connection.ListServicesAsync();
return services.Contains("org.freedesktop.portal.Desktop", StringComparer.Ordinal)
? new DBusSystemDialog(new DesktopService(DBusHelper.Connection, "org.freedesktop.portal.Desktop"), handle)
? new DBusSystemDialog(DBusHelper.Connection, handle)
: null;
}
private readonly DesktopService _desktopService;
private readonly FileChooser _fileChooser;
private readonly Connection _connection;
private readonly OrgFreedesktopPortalFileChooser _fileChooser;
private readonly IPlatformHandle _handle;
private DBusSystemDialog(DesktopService desktopService, IPlatformHandle handle)
private DBusSystemDialog(Connection connection, IPlatformHandle handle)
{
_desktopService = desktopService;
_fileChooser = desktopService.CreateFileChooser("/org/freedesktop/portal/desktop");
_connection = connection;
_fileChooser = new OrgFreedesktopPortalFileChooser(connection, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
_handle = handle;
}
@ -53,7 +53,7 @@ namespace Avalonia.FreeDesktop
objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = _desktopService.CreateRequest(objectPath);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
var tsc = new TaskCompletionSource<string[]?>();
using var disposable = await request.WatchResponseAsync((e, x) =>
{
@ -82,7 +82,7 @@ namespace Avalonia.FreeDesktop
chooserOptions.Add("current_folder", Encoding.UTF8.GetBytes(currentFolder.ToString()));
objectPath = await _fileChooser.SaveFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = _desktopService.CreateRequest(objectPath);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
var tsc = new TaskCompletionSource<string[]?>();
using var disposable = await request.WatchResponseAsync((e, x) =>
{
@ -113,7 +113,7 @@ namespace Avalonia.FreeDesktop
};
var objectPath = await _fileChooser.OpenFileAsync(parentWindow, options.Title ?? string.Empty, chooserOptions);
var request = _desktopService.CreateRequest(objectPath);
var request = new OrgFreedesktopPortalRequest(_connection, "org.freedesktop.portal.Desktop", objectPath);
var tsc = new TaskCompletionSource<string[]?>();
using var disposable = await request.WatchResponseAsync((e, x) =>
{

190
src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Logging;
using Avalonia.Platform;
@ -13,14 +11,15 @@ namespace Avalonia.FreeDesktop
internal class DBusTrayIconImpl : ITrayIconImpl
{
private static int s_trayIconInstanceId;
public static readonly (int, int, byte[]) EmptyPixmap = (1, 1, new byte[] { 255, 0, 0, 0 });
private readonly ObjectPath _dbusMenuPath;
private readonly Connection? _connection;
private readonly DBus? _dBus;
private readonly OrgFreedesktopDBus? _dBus;
private IDisposable? _serviceWatchDisposable;
private StatusNotifierItemDbusObj? _statusNotifierItemDbusObj;
private StatusNotifierWatcher? _statusNotifierWatcher;
private OrgKdeStatusNotifierWatcher? _statusNotifierWatcher;
private (int, int, byte[]) _icon;
private string? _sysTrayServiceName;
@ -48,7 +47,7 @@ namespace Avalonia.FreeDesktop
IsActive = true;
_dBus = new DBusService(_connection, "org.freedesktop.DBus").CreateDBus("/org/freedesktop/DBus");
_dBus = new OrgFreedesktopDBus(_connection, "org.freedesktop.DBus", "/org/freedesktop/DBus");
_dbusMenuPath = DBusMenuExporter.GenerateDBusMenuObjPath;
MenuExporter = DBusMenuExporter.TryCreateDetachedNativeMenu(_dbusMenuPath, _connection);
@ -61,8 +60,7 @@ namespace Avalonia.FreeDesktop
if (_connection is null || _isDisposed)
return;
_statusNotifierWatcher = new StatusNotifierWatcherService(_connection, "org.kde.StatusNotifierWatcher")
.CreateStatusNotifierWatcher("/StatusNotifierWatcher");
_statusNotifierWatcher = new OrgKdeStatusNotifierWatcher(_connection, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher");
_serviceConnected = true;
}
@ -72,7 +70,7 @@ namespace Avalonia.FreeDesktop
if (!services.Contains("org.kde.StatusNotifierWatcher", StringComparer.Ordinal))
return;
_serviceWatchDisposable = await _dBus!.WatchNameOwnerChangedAsync((_, x) => OnNameChange(x.A2) );
_serviceWatchDisposable = await _dBus!.WatchNameOwnerChangedAsync((_, x) => OnNameChange(x.Item2) );
var nameOwner = await _dBus.GetNameOwnerAsync("org.kde.StatusNotifierWatcher");
OnNameChange(nameOwner);
}
@ -147,7 +145,7 @@ namespace Avalonia.FreeDesktop
if (icon is null)
{
_statusNotifierItemDbusObj?.SetIcon(StatusNotifierItemDbusObj.StatusNotifierItemProperties.EmptyPixmap);
_statusNotifierItemDbusObj?.SetIcon(EmptyPixmap);
return;
}
@ -210,27 +208,37 @@ namespace Avalonia.FreeDesktop
/// <remarks>
/// Useful guide: https://web.archive.org/web/20210818173850/https://www.notmart.org/misc/statusnotifieritem/statusnotifieritem.html
/// </remarks>
internal class StatusNotifierItemDbusObj : IMethodHandler
internal class StatusNotifierItemDbusObj : OrgKdeStatusNotifierItem
{
private readonly Connection _connection;
private readonly StatusNotifierItemProperties _backingProperties;
public StatusNotifierItemDbusObj(Connection connection, ObjectPath dbusMenuPath)
{
_connection = connection;
_backingProperties = new StatusNotifierItemProperties
{
Menu = dbusMenuPath, // Needs a dbus menu somehow
ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), string.Empty, string.Empty)
};
BackingProperties.Menu = dbusMenuPath;
BackingProperties.ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), string.Empty, string.Empty);
BackingProperties.IconName = string.Empty;
BackingProperties.AttentionIconName = string.Empty;
BackingProperties.AttentionIconPixmap = new []{ DBusTrayIconImpl.EmptyPixmap };
BackingProperties.AttentionMovieName = string.Empty;
BackingProperties.IconThemePath = string.Empty;
BackingProperties.OverlayIconName = string.Empty;
BackingProperties.OverlayIconPixmap = new []{ DBusTrayIconImpl.EmptyPixmap };
InvalidateAll();
}
public string Path => "/StatusNotifierItem";
public override string Path => "/StatusNotifierItem";
public event Action? ActivationDelegate;
protected override void OnContextMenu(int x, int y) { }
protected override void OnActivate(int x, int y) => ActivationDelegate?.Invoke();
protected override void OnSecondaryActivate(int x, int y) { }
protected override void OnScroll(int delta, string orientation) { }
public void InvalidateAll()
{
EmitVoidSignal("NewTitle");
@ -238,12 +246,12 @@ namespace Avalonia.FreeDesktop
EmitVoidSignal("NewAttentionIcon");
EmitVoidSignal("NewOverlayIcon");
EmitVoidSignal("NewToolTip");
EmitStringSignal("NewStatus", _backingProperties.Status);
EmitStringSignal("NewStatus", BackingProperties.Status);
}
public void SetIcon((int, int, byte[]) dbusPixmap)
{
_backingProperties.IconPixmap = new[] { dbusPixmap };
BackingProperties.IconPixmap = new[] { dbusPixmap };
InvalidateAll();
}
@ -252,124 +260,14 @@ namespace Avalonia.FreeDesktop
if (text is null)
return;
_backingProperties.Id = text;
_backingProperties.Category = "ApplicationStatus";
_backingProperties.Status = text;
_backingProperties.Title = text;
_backingProperties.ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), text, string.Empty);
BackingProperties.Id = text;
BackingProperties.Category = "ApplicationStatus";
BackingProperties.Status = text;
BackingProperties.Title = text;
BackingProperties.ToolTip = (string.Empty, Array.Empty<(int, int, byte[])>(), text, string.Empty);
InvalidateAll();
}
public bool RunMethodHandlerSynchronously(Message message) => false;
public ValueTask HandleMethodAsync(MethodContext context)
{
switch (context.Request.InterfaceAsString)
{
case "org.kde.StatusNotifierItem":
switch (context.Request.MemberAsString, context.Request.SignatureAsString)
{
case ("ContextMenu", "ii"):
break;
case ("Activate", "ii"):
ActivationDelegate?.Invoke();
break;
case ("SecondaryActivate", "ii"):
break;
case ("Scroll", "is"):
break;
}
break;
case "org.freedesktop.DBus.Properties":
switch (context.Request.MemberAsString, context.Request.SignatureAsString)
{
case ("Get", "ss"):
{
var reader = context.Request.GetBodyReader();
_ = reader.ReadString();
var member = reader.ReadString();
switch (member)
{
case "Category":
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_backingProperties.Category);
context.Reply(writer.CreateMessage());
break;
}
case "Id":
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_backingProperties.Id);
context.Reply(writer.CreateMessage());
break;
}
case "Title":
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_backingProperties.Title);
context.Reply(writer.CreateMessage());
break;
}
case "Status":
{
using var writer = context.CreateReplyWriter("s");
writer.WriteString(_backingProperties.Status);
context.Reply(writer.CreateMessage());
break;
}
case "Menu":
{
using var writer = context.CreateReplyWriter("o");
writer.WriteObjectPath(_backingProperties.Menu);
context.Reply(writer.CreateMessage());
break;
}
case "IconPixmap":
{
using var writer = context.CreateReplyWriter("a(iiay)");
writer.WriteArray(_backingProperties.IconPixmap);
context.Reply(writer.CreateMessage());
break;
}
case "ToolTip":
{
using var writer = context.CreateReplyWriter("(sa(iiay)ss)");
writer.WriteStruct(_backingProperties.ToolTip);
context.Reply(writer.CreateMessage());
break;
}
}
break;
}
case ("GetAll", "s"):
{
var writer = context.CreateReplyWriter("a{sv}");
var dict = new Dictionary<string, object>
{
{ "Category", _backingProperties.Category },
{ "Id", _backingProperties.Id },
{ "Title", _backingProperties.Title },
{ "Status", _backingProperties.Status },
{ "Menu", _backingProperties.Menu },
{ "IconPixmap", _backingProperties.IconPixmap },
{ "ToolTip", _backingProperties.ToolTip }
};
writer.WriteDictionary(dict);
context.Reply(writer.CreateMessage());
break;
}
}
break;
}
return default;
}
private void EmitVoidSignal(string member)
{
using var writer = _connection.GetMessageWriter();
@ -384,27 +282,5 @@ namespace Avalonia.FreeDesktop
writer.WriteString(value);
_connection.TrySendMessage(writer.CreateMessage());
}
internal record StatusNotifierItemProperties
{
public string Category { get; set; } = string.Empty;
public string Id { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public int WindowId { get; set; }
public string? IconThemePath { get; set; }
public ObjectPath Menu { get; set; }
public bool ItemIsMenu { get; set; }
public string? IconName { get; set; }
public (int, int, byte[])[] IconPixmap { get; set; } = { EmptyPixmap };
public string? OverlayIconName { get; set; }
public (int, int, byte[])[]? OverlayIconPixmap { get; set; }
public string? AttentionIconName { get; set; }
public (int, int, byte[])[]? AttentionIconPixmap { get; set; }
public string? AttentionMovieName { get; set; }
public (string, (int, int, byte[])[], string, string) ToolTip { get; set; }
public static (int, int, byte[]) EmptyPixmap = (1, 1, new byte[] { 255, 0, 0, 0 });
}
}
}

938
src/Avalonia.FreeDesktop/FreeDesktopPortalDesktop.DBus.cs

@ -1,938 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop
{
internal record NotificationProperties
{
public uint Version { get; set; }
}
internal class Notification : DesktopObject
{
private const string Interface = "org.freedesktop.portal.Notification";
public Notification(DesktopService service, ObjectPath path) : base(service, path) { }
public Task AddNotificationAsync(string id, Dictionary<string, object> notification)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sa{sv}",
member: "AddNotification");
writer.WriteString(id);
writer.WriteDictionary(notification);
return writer.CreateMessage();
}
}
public Task RemoveNotificationAsync(string id)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "RemoveNotification");
writer.WriteString(id);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchActionInvokedAsync(Action<Exception?, (string Id, string Action, object[] Parameter)> handler,
bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "ActionInvoked", static (m, s)
=> ReadMessage_ssav(m, (DesktopObject)s!), handler, emitOnCapturedContext);
public Task SetVersionAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("version");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task<uint> GetVersionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "version"), static (m, s)
=> ReadMessage_v_u(m, (DesktopObject)s!), this);
public Task<NotificationProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _)
=> ReadMessage(m), this);
static NotificationProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<NotificationProperties>> handler,
bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _)
=> ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<NotificationProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<NotificationProperties>(ReadProperties(ref reader, changed), changed.ToArray(),
ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "version":
invalidated.Add("Version");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static NotificationProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new NotificationProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "version":
reader.ReadSignature("u");
props.Version = reader.ReadUInt32();
changedList?.Add("Version");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal record OpenURIProperties
{
public uint Version { get; set; }
}
internal class OpenURI : DesktopObject
{
private const string Interface = "org.freedesktop.portal.OpenURI";
public OpenURI(DesktopService service, ObjectPath path) : base(service, path) { }
public Task<ObjectPath> OpenUriAsync(string parentWindow, string uri, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ssa{sv}",
member: "OpenURI");
writer.WriteString(parentWindow);
writer.WriteString(uri);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<ObjectPath> OpenFileAsync(string parentWindow, SafeHandle fd, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sha{sv}",
member: "OpenFile");
writer.WriteString(parentWindow);
writer.WriteHandle(fd);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<ObjectPath> OpenDirectoryAsync(string parentWindow, SafeHandle fd, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sha{sv}",
member: "OpenDirectory");
writer.WriteString(parentWindow);
writer.WriteHandle(fd);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task SetVersionAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("version");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task<uint> GetVersionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "version"), static (m, s)
=> ReadMessage_v_u(m, (DesktopObject)s!), this);
public Task<OpenURIProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _)
=> ReadMessage(m), this);
static OpenURIProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<OpenURIProperties>> handler,
bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _)
=> ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<OpenURIProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<OpenURIProperties>(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "version":
invalidated.Add("Version");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static OpenURIProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new OpenURIProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "version":
reader.ReadSignature("u");
props.Version = reader.ReadUInt32();
changedList?.Add("Version");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal record DynamicLauncherProperties
{
public uint SupportedLauncherTypes { get; set; }
public uint Version { get; set; }
}
internal class DynamicLauncher : DesktopObject
{
private const string Interface = "org.freedesktop.portal.DynamicLauncher";
public DynamicLauncher(DesktopService service, ObjectPath path) : base(service, path) { }
public Task InstallAsync(string token, string desktopFileId, string desktopEntry, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sssa{sv}",
member: "Install");
writer.WriteString(token);
writer.WriteString(desktopFileId);
writer.WriteString(desktopEntry);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<ObjectPath> PrepareInstallAsync(string parentWindow, string name, object iconV, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ssva{sv}",
member: "PrepareInstall");
writer.WriteString(parentWindow);
writer.WriteString(name);
writer.WriteVariant(iconV);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<string> RequestInstallTokenAsync(string name, object iconV, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s)
=> ReadMessage_s(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sva{sv}",
member: "RequestInstallToken");
writer.WriteString(name);
writer.WriteVariant(iconV);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task UninstallAsync(string desktopFileId, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sa{sv}",
member: "Uninstall");
writer.WriteString(desktopFileId);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<string> GetDesktopEntryAsync(string desktopFileId)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s)
=> ReadMessage_s(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetDesktopEntry");
writer.WriteString(desktopFileId);
return writer.CreateMessage();
}
}
public Task<(object IconV, string IconFormat, uint IconSize)> GetIconAsync(string desktopFileId)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s)
=> ReadMessage_vsu(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "GetIcon");
writer.WriteString(desktopFileId);
return writer.CreateMessage();
}
}
public Task LaunchAsync(string desktopFileId, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "sa{sv}",
member: "Launch");
writer.WriteString(desktopFileId);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task SetSupportedLauncherTypesAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("SupportedLauncherTypes");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task SetVersionAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("version");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task<uint> GetSupportedLauncherTypesAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "SupportedLauncherTypes"), static (m, s)
=> ReadMessage_v_u(m, (DesktopObject)s!), this);
public Task<uint> GetVersionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "version"), static (m, s)
=> ReadMessage_v_u(m, (DesktopObject)s!), this);
public Task<DynamicLauncherProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _)
=> ReadMessage(m), this);
static DynamicLauncherProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<DynamicLauncherProperties>> handler,
bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _)
=> ReadMessage(m), handler,
emitOnCapturedContext);
static PropertyChanges<DynamicLauncherProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<DynamicLauncherProperties>(ReadProperties(ref reader, changed), changed.ToArray(),
ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "SupportedLauncherTypes":
invalidated.Add("SupportedLauncherTypes");
break;
case "version":
invalidated.Add("Version");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static DynamicLauncherProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new DynamicLauncherProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "SupportedLauncherTypes":
reader.ReadSignature("u");
props.SupportedLauncherTypes = reader.ReadUInt32();
changedList?.Add("SupportedLauncherTypes");
break;
case "version":
reader.ReadSignature("u");
props.Version = reader.ReadUInt32();
changedList?.Add("Version");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal record FileChooserProperties
{
public uint Version { get; set; }
}
internal class FileChooser : DesktopObject
{
private const string Interface = "org.freedesktop.portal.FileChooser";
public FileChooser(DesktopService service, ObjectPath path) : base(service, path) { }
public Task<ObjectPath> OpenFileAsync(string parentWindow, string title, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ssa{sv}",
member: "OpenFile");
writer.WriteString(parentWindow);
writer.WriteString(title);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<ObjectPath> SaveFileAsync(string parentWindow, string title, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ssa{sv}",
member: "SaveFile");
writer.WriteString(parentWindow);
writer.WriteString(title);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task<ObjectPath> SaveFilesAsync(string parentWindow, string title, Dictionary<string, object> options)
{
return Connection.CallMethodAsync(CreateMessage(), static (m, s) => ReadMessage_o(m, (DesktopObject)s!), this);
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "ssa{sv}",
member: "SaveFiles");
writer.WriteString(parentWindow);
writer.WriteString(title);
writer.WriteDictionary(options);
return writer.CreateMessage();
}
}
public Task SetVersionAsync(uint value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("version");
writer.WriteSignature("u");
writer.WriteUInt32(value);
return writer.CreateMessage();
}
}
public Task<uint> GetVersionAsync()
=> Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "version"), static (m, s)
=> ReadMessage_v_u(m, (DesktopObject)s!), this);
public Task<FileChooserProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _)
=> ReadMessage(m), this);
static FileChooserProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<FileChooserProperties>> handler,
bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _)
=> ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<FileChooserProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<FileChooserProperties>(ReadProperties(ref reader, changed), changed.ToArray(),
ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "version":
invalidated.Add("Version");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static FileChooserProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new FileChooserProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "version":
reader.ReadSignature("u");
props.Version = reader.ReadUInt32();
changedList?.Add("Version");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal class Request : DesktopObject
{
private const string Interface = "org.freedesktop.portal.Request";
public Request(DesktopService service, ObjectPath path) : base(service, path) { }
public Task CloseAsync()
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
"Close");
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchResponseAsync(Action<Exception?, (uint response, IDictionary<string, object> results)> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "Response",
static (m, s) => ReadMessage_uaesv(m, (DesktopObject)s!), handler, emitOnCapturedContext);
}
internal class DesktopService
{
public DesktopService(Connection connection, string destination)
=> (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public Notification CreateNotification(string path) => new(this, path);
public OpenURI CreateOpenUri(string path) => new(this, path);
public DynamicLauncher CreateDynamicLauncher(string path) => new(this, path);
public FileChooser CreateFileChooser(string path) => new(this, path);
public Request CreateRequest(string path) => new(this, path);
}
internal class DesktopObject
{
protected DesktopObject(DesktopService service, ObjectPath path)
=> (Service, Path) = (service, path);
protected DesktopService Service { get; }
protected ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface,
MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler,
bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs)
=> ((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes), this, handler, emitOnCapturedContext);
}
protected ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal,
MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs) =>
((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
protected static ObjectPath ReadMessage_o(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
return reader.ReadObjectPath();
}
protected static uint ReadMessage_v_u(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
reader.ReadSignature("u");
return reader.ReadUInt32();
}
protected static (string, string, object[]) ReadMessage_ssav(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadString();
var arg1 = reader.ReadString();
var arg2 = reader.ReadArray<object>();
return (arg0, arg1, arg2);
}
protected static (uint, Dictionary<string, object>) ReadMessage_uaesv(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadUInt32();
var arg1 = reader.ReadDictionary<string, object>();
return (arg0, arg1);
}
protected static string ReadMessage_s(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
return reader.ReadString();
}
protected static (object, string, uint) ReadMessage_vsu(Message message, DesktopObject _)
{
var reader = message.GetBodyReader();
var arg0 = reader.ReadVariant();
var arg1 = reader.ReadString();
var arg2 = reader.ReadUInt32();
return (arg0, arg1, arg2);
}
}
internal class PropertyChanges<TProperties>
{
public PropertyChanges(TProperties properties, string[] invalidated, string[] changed)
=> (Properties, Invalidated, Changed) = (properties, invalidated, changed);
public TProperties Properties { get; }
public string[] Invalidated { get; }
public string[] Changed { get; }
public bool HasChanged(string property) => Array.IndexOf(Changed, property) != -1;
public bool IsInvalidated(string property) => Array.IndexOf(Invalidated, property) != -1;
}
}

350
src/Avalonia.FreeDesktop/StatusNotifierWatcher.DBus.cs

@ -1,350 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Tmds.DBus.Protocol;
namespace Avalonia.FreeDesktop
{
internal record StatusNotifierWatcherProperties
{
public string[] RegisteredStatusNotifierItems { get; set; } = default!;
public bool IsStatusNotifierHostRegistered { get; set; }
public int ProtocolVersion { get; set; }
}
internal class StatusNotifierWatcher : StatusNotifierWatcherObject
{
private const string Interface = "org.kde.StatusNotifierWatcher";
public StatusNotifierWatcher(StatusNotifierWatcherService service, ObjectPath path) : base(service, path) { }
public Task RegisterStatusNotifierItemAsync(string service)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "RegisterStatusNotifierItem");
writer.WriteString(service);
return writer.CreateMessage();
}
}
public Task RegisterStatusNotifierHostAsync(string service)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
Interface,
signature: "s",
member: "RegisterStatusNotifierHost");
writer.WriteString(service);
return writer.CreateMessage();
}
}
public ValueTask<IDisposable> WatchStatusNotifierItemRegisteredAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "StatusNotifierItemRegistered", static (m, s) =>
ReadMessage_s(m, (StatusNotifierWatcherObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchStatusNotifierItemUnregisteredAsync(Action<Exception?, string> handler, bool emitOnCapturedContext = true)
=> WatchSignalAsync(Service.Destination, Interface, Path, "StatusNotifierItemUnregistered", static (m, s)
=> ReadMessage_s(m, (StatusNotifierWatcherObject)s!), handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchStatusNotifierHostRegisteredAsync(Action<Exception?> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "StatusNotifierHostRegistered", handler, emitOnCapturedContext);
public ValueTask<IDisposable> WatchStatusNotifierHostUnregisteredAsync(Action<Exception?> handler, bool emitOnCapturedContext = true) =>
WatchSignalAsync(Service.Destination, Interface, Path, "StatusNotifierHostUnregistered", handler, emitOnCapturedContext);
public Task SetRegisteredStatusNotifierItemsAsync(string[] value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("RegisteredStatusNotifierItems");
writer.WriteSignature("as");
writer.WriteArray(value);
return writer.CreateMessage();
}
}
public Task SetIsStatusNotifierHostRegisteredAsync(bool value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("IsStatusNotifierHostRegistered");
writer.WriteSignature("b");
writer.WriteBool(value);
return writer.CreateMessage();
}
}
public Task SetProtocolVersionAsync(int value)
{
return Connection.CallMethodAsync(CreateMessage());
MessageBuffer CreateMessage()
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ssv",
member: "Set");
writer.WriteString(Interface);
writer.WriteString("ProtocolVersion");
writer.WriteSignature("i");
writer.WriteInt32(value);
return writer.CreateMessage();
}
}
public Task<string[]> GetRegisteredStatusNotifierItemsAsync() =>
Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "RegisteredStatusNotifierItems"), static (m, s)
=> ReadMessage_v_as(m, (StatusNotifierWatcherObject)s!), this);
public Task<bool> GetIsStatusNotifierHostRegisteredAsync() =>
Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "IsStatusNotifierHostRegistered"), static (m, s) =>
ReadMessage_v_b(m, (StatusNotifierWatcherObject)s!), this);
public Task<int> GetProtocolVersionAsync() =>
Connection.CallMethodAsync(CreateGetPropertyMessage(Interface, "ProtocolVersion"), static (m, s)
=> ReadMessage_v_i(m, (StatusNotifierWatcherObject)s!), this);
public Task<StatusNotifierWatcherProperties> GetPropertiesAsync()
{
return Connection.CallMethodAsync(CreateGetAllPropertiesMessage(Interface), static (m, _)
=> ReadMessage(m), this);
static StatusNotifierWatcherProperties ReadMessage(Message message)
{
var reader = message.GetBodyReader();
return ReadProperties(ref reader);
}
}
public ValueTask<IDisposable> WatchPropertiesChangedAsync(Action<Exception?, PropertyChanges<StatusNotifierWatcherProperties>> handler, bool emitOnCapturedContext = true)
{
return base.WatchPropertiesChangedAsync(Interface, static (m, _) => ReadMessage(m), handler, emitOnCapturedContext);
static PropertyChanges<StatusNotifierWatcherProperties> ReadMessage(Message message)
{
var reader = message.GetBodyReader();
reader.ReadString(); // interface
List<string> changed = new();
return new PropertyChanges<StatusNotifierWatcherProperties>(ReadProperties(ref reader, changed), changed.ToArray(),
ReadInvalidated(ref reader));
}
static string[] ReadInvalidated(ref Reader reader)
{
List<string>? invalidated = null;
var headersEnd = reader.ReadArrayStart(DBusType.String);
while (reader.HasNext(headersEnd))
{
invalidated ??= new List<string>();
var property = reader.ReadString();
switch (property)
{
case "RegisteredStatusNotifierItems":
invalidated.Add("RegisteredStatusNotifierItems");
break;
case "IsStatusNotifierHostRegistered":
invalidated.Add("IsStatusNotifierHostRegistered");
break;
case "ProtocolVersion":
invalidated.Add("ProtocolVersion");
break;
}
}
return invalidated?.ToArray() ?? Array.Empty<string>();
}
}
private static StatusNotifierWatcherProperties ReadProperties(ref Reader reader, ICollection<string>? changedList = null)
{
var props = new StatusNotifierWatcherProperties();
var headersEnd = reader.ReadArrayStart(DBusType.Struct);
while (reader.HasNext(headersEnd))
{
var property = reader.ReadString();
switch (property)
{
case "RegisteredStatusNotifierItems":
reader.ReadSignature("as");
props.RegisteredStatusNotifierItems = reader.ReadArray<string>();
changedList?.Add("RegisteredStatusNotifierItems");
break;
case "IsStatusNotifierHostRegistered":
reader.ReadSignature("b");
props.IsStatusNotifierHostRegistered = reader.ReadBool();
changedList?.Add("IsStatusNotifierHostRegistered");
break;
case "ProtocolVersion":
reader.ReadSignature("i");
props.ProtocolVersion = reader.ReadInt32();
changedList?.Add("ProtocolVersion");
break;
default:
reader.ReadVariant();
break;
}
}
return props;
}
}
internal class StatusNotifierWatcherService
{
public StatusNotifierWatcherService(Connection connection, string destination) => (Connection, Destination) = (connection, destination);
public Connection Connection { get; }
public string Destination { get; }
public StatusNotifierWatcher CreateStatusNotifierWatcher(string path) => new(this, path);
}
internal class StatusNotifierWatcherObject
{
protected StatusNotifierWatcherObject(StatusNotifierWatcherService service, ObjectPath path)
{
(Service, Path) = (service, path);
}
protected StatusNotifierWatcherService Service { get; }
protected ObjectPath Path { get; }
protected Connection Connection => Service.Connection;
protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "ss",
member: "Get");
writer.WriteString(@interface);
writer.WriteString(property);
return writer.CreateMessage();
}
protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)
{
using var writer = Connection.GetMessageWriter();
writer.WriteMethodCallHeader(
Service.Destination,
Path,
"org.freedesktop.DBus.Properties",
signature: "s",
member: "GetAll");
writer.WriteString(@interface);
return writer.CreateMessage();
}
protected ValueTask<IDisposable> WatchPropertiesChangedAsync<TProperties>(string @interface, MessageValueReader<PropertyChanges<TProperties>> reader, Action<Exception?, PropertyChanges<TProperties>> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = Service.Destination,
Path = Path,
Interface = "org.freedesktop.DBus.Properties",
Member = "PropertiesChanged",
Arg0 = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, changes, _, hs) =>
((Action<Exception?, PropertyChanges<TProperties>>)hs!).Invoke(ex, changes), this, handler, emitOnCapturedContext);
}
protected ValueTask<IDisposable> WatchSignalAsync<TArg>(string sender, string @interface, ObjectPath path, string signal, MessageValueReader<TArg> reader, Action<Exception?, TArg> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, reader, static (ex, arg, _, hs)
=> ((Action<Exception?, TArg>)hs!).Invoke(ex, arg), this, handler, emitOnCapturedContext);
}
protected ValueTask<IDisposable> WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action<Exception?> handler, bool emitOnCapturedContext)
{
var rule = new MatchRule
{
Type = MessageType.Signal,
Sender = sender,
Path = path,
Member = signal,
Interface = @interface
};
return Connection.AddMatchAsync(rule, static (_, _)
=> null!, static (Exception? ex, object _, object? _, object? hs)
=> ((Action<Exception?>)hs!).Invoke(ex), this, handler, emitOnCapturedContext);
}
protected static string ReadMessage_s(Message message, StatusNotifierWatcherObject _)
{
var reader = message.GetBodyReader();
return reader.ReadString();
}
protected static string[] ReadMessage_v_as(Message message, StatusNotifierWatcherObject _)
{
var reader = message.GetBodyReader();
reader.ReadSignature("as");
return reader.ReadArray<string>();
}
protected static bool ReadMessage_v_b(Message message, StatusNotifierWatcherObject _)
{
var reader = message.GetBodyReader();
reader.ReadSignature("b");
return reader.ReadBool();
}
protected static int ReadMessage_v_i(Message message, StatusNotifierWatcherObject _)
{
var reader = message.GetBodyReader();
reader.ReadSignature("i");
return reader.ReadInt32();
}
}
}

1
src/tools/Tmds.DBus.SourceGenerator

@ -0,0 +1 @@
Subproject commit 1cf4faba30741f799a33313a2842cf70eeb6c67e
Loading…
Cancel
Save