From 471b52383911e39385cddfa79ea32aa5688db7f0 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Thu, 14 Mar 2024 10:25:22 +0100 Subject: [PATCH] Tool: ProtocolGenerator: use trimming/aot-safe methods only. (#271) --- docs/protocol.md | 2 +- samples/MediaPlayerRemote/Mpris.DBus.cs | 1504 ++++++++++------- samples/MediaPlayerRemote/Program.cs | 4 +- .../MessageWriter.Dictionary.cs | 12 +- src/Tmds.DBus.Protocol/Reader.Array.cs | 2 +- src/Tmds.DBus.Tool/ProtocolGenerator.cs | 678 ++++++-- 6 files changed, 1477 insertions(+), 725 deletions(-) diff --git a/docs/protocol.md b/docs/protocol.md index 1a0b7074..3fdbf618 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -180,7 +180,7 @@ When there is no such method, the item type can be read using a `while` loop as ```cs List arrayOfByteArrays = new(); -ArrayEnd arrayEnd = reader.ReadArrayStart(); +ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Array); while (reader.HasNext(arrayEnd)) { arrayOfByteArrays.Add(reader.ReadArrayOfByte()); diff --git a/samples/MediaPlayerRemote/Mpris.DBus.cs b/samples/MediaPlayerRemote/Mpris.DBus.cs index e71e878b..9ec74dd9 100644 --- a/samples/MediaPlayerRemote/Mpris.DBus.cs +++ b/samples/MediaPlayerRemote/Mpris.DBus.cs @@ -5,57 +5,170 @@ namespace Mpris.DBus using SafeHandle = System.Runtime.InteropServices.SafeHandle; using System.Collections.Generic; using System.Threading.Tasks; - record MediaPlayer2Properties + record PlayerProperties { - public string Identity { get; set; } = default!; - public string DesktopEntry { get; set; } = default!; - public string[] SupportedMimeTypes { get; set; } = default!; - public string[] SupportedUriSchemes { get; set; } = default!; - public bool HasTrackList { get; set; } = default!; - public bool CanQuit { get; set; } = default!; - public bool CanSetFullscreen { get; set; } = default!; - public bool Fullscreen { get; set; } = default!; - public bool CanRaise { get; set; } = default!; + public string PlaybackStatus { get; set; } = default!; + public string LoopStatus { get; set; } = default!; + public double Rate { get; set; } = default!; + public bool Shuffle { get; set; } = default!; + public Dictionary Metadata { get; set; } = default!; + public double Volume { get; set; } = default!; + public long Position { get; set; } = default!; + public double MinimumRate { get; set; } = default!; + public double MaximumRate { get; set; } = default!; + public bool CanGoNext { get; set; } = default!; + public bool CanGoPrevious { get; set; } = default!; + public bool CanPlay { get; set; } = default!; + public bool CanPause { get; set; } = default!; + public bool CanSeek { get; set; } = default!; + public bool CanControl { get; set; } = default!; } - partial class MediaPlayer2 : MprisObject + partial class Player : MprisObject { - private const string __Interface = "org.mpris.MediaPlayer2"; - public MediaPlayer2(MprisService service, ObjectPath path) : base(service, path) + private const string __Interface = "org.mpris.MediaPlayer2.Player"; + public Player(MprisService service, ObjectPath path) : base(service, path) { } - public Task QuitAsync() + public Task NextAsync() { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @interface: __Interface, - member: "Quit"); + member: "Next"); return writer.CreateMessage(); } } - public Task RaiseAsync() + public Task PreviousAsync() { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @interface: __Interface, - member: "Raise"); + member: "Previous"); return writer.CreateMessage(); } } - public Task SetIdentityAsync(string value) + public Task PauseAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "Pause"); + return writer.CreateMessage(); + } + } + public Task PlayPauseAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "PlayPause"); + return writer.CreateMessage(); + } + } + public Task StopAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "Stop"); + return writer.CreateMessage(); + } + } + public Task PlayAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "Play"); + return writer.CreateMessage(); + } + } + public Task SeekAsync(long offset) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + signature: "x", + member: "Seek"); + writer.WriteInt64(offset); + return writer.CreateMessage(); + } + } + public Task SetPositionAsync(ObjectPath trackId, long position) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + signature: "ox", + member: "SetPosition"); + writer.WriteObjectPath(trackId); + writer.WriteInt64(position); + return writer.CreateMessage(); + } + } + public Task OpenUriAsync(string uri) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + signature: "s", + member: "OpenUri"); + writer.WriteString(uri); + return writer.CreateMessage(); + } + } + public ValueTask WatchSeekedAsync(Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "Seeked", (Message m, object? s) => ReadMessage_x(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public Task SetPlaybackStatusAsync(string value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -63,18 +176,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("Identity"); + writer.WriteString("PlaybackStatus"); writer.WriteSignature("s"); writer.WriteString(value); return writer.CreateMessage(); } } - public Task SetDesktopEntryAsync(string value) + public Task SetLoopStatusAsync(string value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -82,18 +195,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("DesktopEntry"); + writer.WriteString("LoopStatus"); writer.WriteSignature("s"); writer.WriteString(value); return writer.CreateMessage(); } } - public Task SetSupportedMimeTypesAsync(string[] value) + public Task SetRateAsync(double value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -101,18 +214,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("SupportedMimeTypes"); - writer.WriteSignature("as"); - writer.WriteArray(value); + writer.WriteString("Rate"); + writer.WriteSignature("d"); + writer.WriteDouble(value); return writer.CreateMessage(); } } - public Task SetSupportedUriSchemesAsync(string[] value) + public Task SetShuffleAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -120,18 +233,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("SupportedUriSchemes"); - writer.WriteSignature("as"); - writer.WriteArray(value); + writer.WriteString("Shuffle"); + writer.WriteSignature("b"); + writer.WriteBool(value); return writer.CreateMessage(); } } - public Task SetHasTrackListAsync(bool value) + public Task SetMetadataAsync(Dictionary value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -139,18 +252,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("HasTrackList"); - writer.WriteSignature("b"); - writer.WriteBool(value); + writer.WriteString("Metadata"); + writer.WriteSignature("a{sv}"); + writer.WriteDictionary(value); return writer.CreateMessage(); } } - public Task SetCanQuitAsync(bool value) + public Task SetVolumeAsync(double value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -158,18 +271,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanQuit"); - writer.WriteSignature("b"); - writer.WriteBool(value); + writer.WriteString("Volume"); + writer.WriteSignature("d"); + writer.WriteDouble(value); return writer.CreateMessage(); } } - public Task SetCanSetFullscreenAsync(bool value) + public Task SetPositionAsync(long value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -177,18 +290,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanSetFullscreen"); - writer.WriteSignature("b"); - writer.WriteBool(value); + writer.WriteString("Position"); + writer.WriteSignature("x"); + writer.WriteInt64(value); return writer.CreateMessage(); } } - public Task SetFullscreenAsync(bool value) + public Task SetMinimumRateAsync(double value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -196,18 +309,56 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("Fullscreen"); + writer.WriteString("MinimumRate"); + writer.WriteSignature("d"); + writer.WriteDouble(value); + return writer.CreateMessage(); + } + } + public Task SetMaximumRateAsync(double value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("MaximumRate"); + writer.WriteSignature("d"); + writer.WriteDouble(value); + return writer.CreateMessage(); + } + } + public Task SetCanGoNextAsync(bool value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanGoNext"); writer.WriteSignature("b"); writer.WriteBool(value); return writer.CreateMessage(); } } - public Task SetCanRaiseAsync(bool value) + public Task SetCanGoPreviousAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -215,126 +366,250 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanRaise"); + writer.WriteString("CanGoPrevious"); writer.WriteSignature("b"); writer.WriteBool(value); return writer.CreateMessage(); } } - public Task GetIdentityAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Identity"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); - public Task GetDesktopEntryAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "DesktopEntry"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); - public Task GetSupportedMimeTypesAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "SupportedMimeTypes"), (Message m, object? s) => ReadMessage_v_as(m, (MprisObject)s!), this); - public Task GetSupportedUriSchemesAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "SupportedUriSchemes"), (Message m, object? s) => ReadMessage_v_as(m, (MprisObject)s!), this); - public Task GetHasTrackListAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "HasTrackList"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanQuitAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanQuit"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanSetFullscreenAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanSetFullscreen"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetFullscreenAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Fullscreen"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanRaiseAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanRaise"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetPropertiesAsync() + public Task SetCanPlayAsync(bool value) { - return this.Connection.CallMethodAsync(CreateGetAllPropertiesMessage(__Interface), (Message m, object? s) => ReadMessage(m, (MprisObject)s!), this); - static MediaPlayer2Properties ReadMessage(Message message, MprisObject _) + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() { - var reader = message.GetBodyReader(); - return ReadProperties(ref reader); + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanPlay"); + writer.WriteSignature("b"); + writer.WriteBool(value); + return writer.CreateMessage(); } } - public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true) + public Task SetCanPauseAsync(bool value) { - return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext); - static PropertyChanges ReadMessage(Message message, MprisObject _) + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() { - var reader = message.GetBodyReader(); - reader.ReadString(); // interface - List changed = new(), invalidated = new(); - return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanPause"); + writer.WriteSignature("b"); + writer.WriteBool(value); + return writer.CreateMessage(); + } + } + public Task SetCanSeekAsync(bool value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanSeek"); + writer.WriteSignature("b"); + writer.WriteBool(value); + return writer.CreateMessage(); + } + } + public Task SetCanControlAsync(bool value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanControl"); + writer.WriteSignature("b"); + writer.WriteBool(value); + return writer.CreateMessage(); + } + } + public Task GetPlaybackStatusAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "PlaybackStatus"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); + public Task GetLoopStatusAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "LoopStatus"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); + public Task GetRateAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Rate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); + public Task GetShuffleAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Shuffle"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task> GetMetadataAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Metadata"), (Message m, object? s) => ReadMessage_v_aesv(m, (MprisObject)s!), this); + public Task GetVolumeAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Volume"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); + public Task GetPositionAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Position"), (Message m, object? s) => ReadMessage_v_x(m, (MprisObject)s!), this); + public Task GetMinimumRateAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "MinimumRate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); + public Task GetMaximumRateAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "MaximumRate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); + public Task GetCanGoNextAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanGoNext"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanGoPreviousAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanGoPrevious"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanPlayAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanPlay"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanPauseAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanPause"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanSeekAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanSeek"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanControlAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanControl"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetPropertiesAsync() + { + return this.Connection.CallMethodAsync(CreateGetAllPropertiesMessage(__Interface), (Message m, object? s) => ReadMessage(m, (MprisObject)s!), this); + static PlayerProperties ReadMessage(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + return ReadProperties(ref reader); + } + } + public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + { + return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + static PropertyChanges ReadMessage(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + reader.ReadString(); // interface + List changed = new(), invalidated = new(); + return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); } static string[] ReadInvalidated(ref Reader reader) { List? invalidated = null; - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.String); - while (reader.HasNext(headersEnd)) + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.String); + while (reader.HasNext(arrayEnd)) { invalidated ??= new(); var property = reader.ReadString(); switch (property) { - case "Identity": invalidated.Add("Identity"); break; - case "DesktopEntry": invalidated.Add("DesktopEntry"); break; - case "SupportedMimeTypes": invalidated.Add("SupportedMimeTypes"); break; - case "SupportedUriSchemes": invalidated.Add("SupportedUriSchemes"); break; - case "HasTrackList": invalidated.Add("HasTrackList"); break; - case "CanQuit": invalidated.Add("CanQuit"); break; - case "CanSetFullscreen": invalidated.Add("CanSetFullscreen"); break; - case "Fullscreen": invalidated.Add("Fullscreen"); break; - case "CanRaise": invalidated.Add("CanRaise"); break; + case "PlaybackStatus": invalidated.Add("PlaybackStatus"); break; + case "LoopStatus": invalidated.Add("LoopStatus"); break; + case "Rate": invalidated.Add("Rate"); break; + case "Shuffle": invalidated.Add("Shuffle"); break; + case "Metadata": invalidated.Add("Metadata"); break; + case "Volume": invalidated.Add("Volume"); break; + case "Position": invalidated.Add("Position"); break; + case "MinimumRate": invalidated.Add("MinimumRate"); break; + case "MaximumRate": invalidated.Add("MaximumRate"); break; + case "CanGoNext": invalidated.Add("CanGoNext"); break; + case "CanGoPrevious": invalidated.Add("CanGoPrevious"); break; + case "CanPlay": invalidated.Add("CanPlay"); break; + case "CanPause": invalidated.Add("CanPause"); break; + case "CanSeek": invalidated.Add("CanSeek"); break; + case "CanControl": invalidated.Add("CanControl"); break; } } return invalidated?.ToArray() ?? Array.Empty(); } } - private static MediaPlayer2Properties ReadProperties(ref Reader reader, List? changedList = null) + private static PlayerProperties ReadProperties(ref Reader reader, List? changedList = null) { - var props = new MediaPlayer2Properties(); - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.Struct); - while (reader.HasNext(headersEnd)) + var props = new PlayerProperties(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Struct); + while (reader.HasNext(arrayEnd)) { var property = reader.ReadString(); switch (property) { - case "Identity": + case "PlaybackStatus": reader.ReadSignature("s"); - props.Identity = reader.ReadString(); - changedList?.Add("Identity"); + props.PlaybackStatus = reader.ReadString(); + changedList?.Add("PlaybackStatus"); break; - case "DesktopEntry": + case "LoopStatus": reader.ReadSignature("s"); - props.DesktopEntry = reader.ReadString(); - changedList?.Add("DesktopEntry"); + props.LoopStatus = reader.ReadString(); + changedList?.Add("LoopStatus"); break; - case "SupportedMimeTypes": - reader.ReadSignature("as"); - props.SupportedMimeTypes = reader.ReadArrayOfString(); - changedList?.Add("SupportedMimeTypes"); + case "Rate": + reader.ReadSignature("d"); + props.Rate = reader.ReadDouble(); + changedList?.Add("Rate"); break; - case "SupportedUriSchemes": - reader.ReadSignature("as"); - props.SupportedUriSchemes = reader.ReadArrayOfString(); - changedList?.Add("SupportedUriSchemes"); + case "Shuffle": + reader.ReadSignature("b"); + props.Shuffle = reader.ReadBool(); + changedList?.Add("Shuffle"); break; - case "HasTrackList": + case "Metadata": + reader.ReadSignature("a{sv}"); + props.Metadata = reader.ReadDictionaryOfStringToVariantValue(); + changedList?.Add("Metadata"); + break; + case "Volume": + reader.ReadSignature("d"); + props.Volume = reader.ReadDouble(); + changedList?.Add("Volume"); + break; + case "Position": + reader.ReadSignature("x"); + props.Position = reader.ReadInt64(); + changedList?.Add("Position"); + break; + case "MinimumRate": + reader.ReadSignature("d"); + props.MinimumRate = reader.ReadDouble(); + changedList?.Add("MinimumRate"); + break; + case "MaximumRate": + reader.ReadSignature("d"); + props.MaximumRate = reader.ReadDouble(); + changedList?.Add("MaximumRate"); + break; + case "CanGoNext": reader.ReadSignature("b"); - props.HasTrackList = reader.ReadBool(); - changedList?.Add("HasTrackList"); + props.CanGoNext = reader.ReadBool(); + changedList?.Add("CanGoNext"); break; - case "CanQuit": + case "CanGoPrevious": reader.ReadSignature("b"); - props.CanQuit = reader.ReadBool(); - changedList?.Add("CanQuit"); + props.CanGoPrevious = reader.ReadBool(); + changedList?.Add("CanGoPrevious"); break; - case "CanSetFullscreen": + case "CanPlay": reader.ReadSignature("b"); - props.CanSetFullscreen = reader.ReadBool(); - changedList?.Add("CanSetFullscreen"); + props.CanPlay = reader.ReadBool(); + changedList?.Add("CanPlay"); break; - case "Fullscreen": + case "CanPause": reader.ReadSignature("b"); - props.Fullscreen = reader.ReadBool(); - changedList?.Add("Fullscreen"); + props.CanPause = reader.ReadBool(); + changedList?.Add("CanPause"); break; - case "CanRaise": + case "CanSeek": reader.ReadSignature("b"); - props.CanRaise = reader.ReadBool(); - changedList?.Add("CanRaise"); + props.CanSeek = reader.ReadBool(); + changedList?.Add("CanSeek"); + break; + case "CanControl": + reader.ReadSignature("b"); + props.CanControl = reader.ReadBool(); + changedList?.Add("CanControl"); break; default: reader.ReadVariantValue(); @@ -344,166 +619,60 @@ private static MediaPlayer2Properties ReadProperties(ref Reader reader, List Metadata { get; set; } = default!; - public string PlaybackStatus { get; set; } = default!; - public string LoopStatus { get; set; } = default!; - public double Volume { get; set; } = default!; - public bool Shuffle { get; set; } = default!; - public int Position { get; set; } = default!; - public double Rate { get; set; } = default!; - public double MinimumRate { get; set; } = default!; - public double MaximumRate { get; set; } = default!; - public bool CanControl { get; set; } = default!; - public bool CanPlay { get; set; } = default!; - public bool CanPause { get; set; } = default!; - public bool CanSeek { get; set; } = default!; + public uint PlaylistCount { get; set; } = default!; + public string[] Orderings { get; set; } = default!; + public (bool, (ObjectPath, string, string)) ActivePlaylist { get; set; } = default!; } - partial class Player : MprisObject + partial class Playlists : MprisObject { - private const string __Interface = "org.mpris.MediaPlayer2.Player"; - public Player(MprisService service, ObjectPath path) : base(service, path) + private const string __Interface = "org.mpris.MediaPlayer2.Playlists"; + public Playlists(MprisService service, ObjectPath path) : base(service, path) { } - public Task PreviousAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "Previous"); - return writer.CreateMessage(); - } - } - public Task NextAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "Next"); - return writer.CreateMessage(); - } - } - public Task StopAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "Stop"); - return writer.CreateMessage(); - } - } - public Task PlayAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "Play"); - return writer.CreateMessage(); - } - } - public Task PauseAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "Pause"); - return writer.CreateMessage(); - } - } - public Task PlayPauseAsync() - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - member: "PlayPause"); - return writer.CreateMessage(); - } - } - public Task SeekAsync(long a0) - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() - { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: __Interface, - signature: "x", - member: "Seek"); - writer.WriteInt64(a0); - return writer.CreateMessage(); - } - } - public Task OpenUriAsync(string a0) + public Task ActivatePlaylistAsync(ObjectPath playlistId) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @interface: __Interface, - signature: "s", - member: "OpenUri"); - writer.WriteString(a0); + signature: "o", + member: "ActivatePlaylist"); + writer.WriteObjectPath(playlistId); return writer.CreateMessage(); } } - public Task SetPositionAsync(ObjectPath a0, long a1) + public Task<(ObjectPath, string, string)[]> GetPlaylistsAsync(uint index, uint maxCount, string order, bool reverseOrder) { - return this.Connection.CallMethodAsync(CreateMessage()); + return this.Connection.CallMethodAsync(CreateMessage(), (Message m, object? s) => ReadMessage_arossz(m, (MprisObject)s!), this); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @interface: __Interface, - signature: "ox", - member: "SetPosition"); - writer.WriteObjectPath(a0); - writer.WriteInt64(a1); + signature: "uusb", + member: "GetPlaylists"); + writer.WriteUInt32(index); + writer.WriteUInt32(maxCount); + writer.WriteString(order); + writer.WriteBool(reverseOrder); return writer.CreateMessage(); } } - public Task SetMetadataAsync(Dictionary value) + public ValueTask WatchPlaylistChangedAsync(Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "PlaylistChanged", (Message m, object? s) => ReadMessage_rossz(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public Task SetPlaylistCountAsync(uint value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -511,18 +680,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("Metadata"); - writer.WriteSignature("a{sv}"); - writer.WriteDictionary(value); + writer.WriteString("PlaylistCount"); + writer.WriteSignature("u"); + writer.WriteUInt32(value); return writer.CreateMessage(); } } - public Task SetPlaybackStatusAsync(string value) + public Task SetOrderingsAsync(string[] value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -530,18 +699,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("PlaybackStatus"); - writer.WriteSignature("s"); - writer.WriteString(value); + writer.WriteString("Orderings"); + writer.WriteSignature("as"); + writer.WriteArray(value); return writer.CreateMessage(); } } - public Task SetLoopStatusAsync(string value) + public Task SetActivePlaylistAsync((bool, (ObjectPath, string, string)) value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -549,170 +718,177 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("LoopStatus"); - writer.WriteSignature("s"); - writer.WriteString(value); + writer.WriteString("ActivePlaylist"); + writer.WriteSignature("(b(oss))"); + WriteType_rbrosszz(ref writer, value); return writer.CreateMessage(); } } - public Task SetVolumeAsync(double value) + public Task GetPlaylistCountAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "PlaylistCount"), (Message m, object? s) => ReadMessage_v_u(m, (MprisObject)s!), this); + public Task GetOrderingsAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Orderings"), (Message m, object? s) => ReadMessage_v_as(m, (MprisObject)s!), this); + public Task<(bool, (ObjectPath, string, string))> GetActivePlaylistAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "ActivePlaylist"), (Message m, object? s) => ReadMessage_v_rbrosszz(m, (MprisObject)s!), this); + public Task GetPropertiesAsync() { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() + return this.Connection.CallMethodAsync(CreateGetAllPropertiesMessage(__Interface), (Message m, object? s) => ReadMessage(m, (MprisObject)s!), this); + static PlaylistsProperties ReadMessage(Message message, MprisObject _) { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("Volume"); - writer.WriteSignature("d"); - writer.WriteDouble(value); - return writer.CreateMessage(); + var reader = message.GetBodyReader(); + return ReadProperties(ref reader); } } - public Task SetShuffleAsync(double value) + public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() + return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + static PropertyChanges ReadMessage(Message message, MprisObject _) { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("Shuffle"); - writer.WriteSignature("d"); - writer.WriteDouble(value); - return writer.CreateMessage(); + var reader = message.GetBodyReader(); + reader.ReadString(); // interface + List changed = new(), invalidated = new(); + return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); } - } - public Task SetPositionAsync(int value) - { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() + static string[] ReadInvalidated(ref Reader reader) { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("Position"); - writer.WriteSignature("i"); - writer.WriteInt32(value); - return writer.CreateMessage(); + List? invalidated = null; + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.String); + while (reader.HasNext(arrayEnd)) + { + invalidated ??= new(); + var property = reader.ReadString(); + switch (property) + { + case "PlaylistCount": invalidated.Add("PlaylistCount"); break; + case "Orderings": invalidated.Add("Orderings"); break; + case "ActivePlaylist": invalidated.Add("ActivePlaylist"); break; + } + } + return invalidated?.ToArray() ?? Array.Empty(); } } - public Task SetRateAsync(double value) + private static PlaylistsProperties ReadProperties(ref Reader reader, List? changedList = null) { - return this.Connection.CallMethodAsync(CreateMessage()); - MessageBuffer CreateMessage() + var props = new PlaylistsProperties(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Struct); + while (reader.HasNext(arrayEnd)) { - using var writer = this.Connection.GetMessageWriter(); - writer.WriteMethodCallHeader( - destination: Service.Destination, - path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("Rate"); - writer.WriteSignature("d"); - writer.WriteDouble(value); - return writer.CreateMessage(); + var property = reader.ReadString(); + switch (property) + { + case "PlaylistCount": + reader.ReadSignature("u"); + props.PlaylistCount = reader.ReadUInt32(); + changedList?.Add("PlaylistCount"); + break; + case "Orderings": + reader.ReadSignature("as"); + props.Orderings = reader.ReadArrayOfString(); + changedList?.Add("Orderings"); + break; + case "ActivePlaylist": + reader.ReadSignature("(b(oss))"); + props.ActivePlaylist = ReadType_rbrosszz(ref reader); + changedList?.Add("ActivePlaylist"); + break; + default: + reader.ReadVariantValue(); + break; + } } + return props; } - public Task SetMinimumRateAsync(double value) + } + record TrackListProperties + { + public ObjectPath[] Tracks { get; set; } = default!; + public bool CanEditTracks { get; set; } = default!; + } + partial class TrackList : MprisObject + { + private const string __Interface = "org.mpris.MediaPlayer2.TrackList"; + public TrackList(MprisService service, ObjectPath path) : base(service, path) + { } + public Task[]> GetTracksMetadataAsync(ObjectPath[] trackIds) { - return this.Connection.CallMethodAsync(CreateMessage()); + return this.Connection.CallMethodAsync(CreateMessage(), (Message m, object? s) => ReadMessage_aaesv(m, (MprisObject)s!), this); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("MinimumRate"); - writer.WriteSignature("d"); - writer.WriteDouble(value); + @interface: __Interface, + signature: "ao", + member: "GetTracksMetadata"); + writer.WriteArray(trackIds); return writer.CreateMessage(); } } - public Task SetMaximumRateAsync(double value) + public Task AddTrackAsync(string uri, ObjectPath afterTrack, bool setAsCurrent) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("MaximumRate"); - writer.WriteSignature("d"); - writer.WriteDouble(value); + @interface: __Interface, + signature: "sob", + member: "AddTrack"); + writer.WriteString(uri); + writer.WriteObjectPath(afterTrack); + writer.WriteBool(setAsCurrent); return writer.CreateMessage(); } } - public Task SetCanControlAsync(bool value) + public Task RemoveTrackAsync(ObjectPath trackId) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("CanControl"); - writer.WriteSignature("b"); - writer.WriteBool(value); + @interface: __Interface, + signature: "o", + member: "RemoveTrack"); + writer.WriteObjectPath(trackId); return writer.CreateMessage(); } } - public Task SetCanPlayAsync(bool value) + public Task GoToAsync(ObjectPath trackId) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: "org.freedesktop.DBus.Properties", - signature: "ssv", - member: "Set"); - writer.WriteString(__Interface); - writer.WriteString("CanPlay"); - writer.WriteSignature("b"); - writer.WriteBool(value); + @interface: __Interface, + signature: "o", + member: "GoTo"); + writer.WriteObjectPath(trackId); return writer.CreateMessage(); } } - public Task SetCanPauseAsync(bool value) + public ValueTask WatchTrackListReplacedAsync(Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackListReplaced", (Message m, object? s) => ReadMessage_aoo(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public ValueTask WatchTrackAddedAsync(Action Metadata, ObjectPath AfterTrack)> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackAdded", (Message m, object? s) => ReadMessage_aesvo(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public ValueTask WatchTrackRemovedAsync(Action handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackRemoved", (Message m, object? s) => ReadMessage_o(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public ValueTask WatchTrackMetadataChangedAsync(Action Metadata)> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) + => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackMetadataChanged", (Message m, object? s) => ReadMessage_oaesv(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + public Task SetTracksAsync(ObjectPath[] value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -720,18 +896,18 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanPause"); - writer.WriteSignature("b"); - writer.WriteBool(value); + writer.WriteString("Tracks"); + writer.WriteSignature("ao"); + writer.WriteArray(value); return writer.CreateMessage(); } } - public Task SetCanSeekAsync(bool value) + public Task SetCanEditTracksAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -739,257 +915,225 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanSeek"); + writer.WriteString("CanEditTracks"); writer.WriteSignature("b"); writer.WriteBool(value); return writer.CreateMessage(); } } - public Task> GetMetadataAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Metadata"), (Message m, object? s) => ReadMessage_v_aesv(m, (MprisObject)s!), this); - public Task GetPlaybackStatusAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "PlaybackStatus"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); - public Task GetLoopStatusAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "LoopStatus"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); - public Task GetVolumeAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Volume"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); - public Task GetShuffleAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Shuffle"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); - public Task GetPositionAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Position"), (Message m, object? s) => ReadMessage_v_i(m, (MprisObject)s!), this); - public Task GetRateAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Rate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); - public Task GetMinimumRateAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "MinimumRate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); - public Task GetMaximumRateAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "MaximumRate"), (Message m, object? s) => ReadMessage_v_d(m, (MprisObject)s!), this); - public Task GetCanControlAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanControl"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanPlayAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanPlay"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanPauseAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanPause"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetCanSeekAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanSeek"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetPropertiesAsync() + public Task GetTracksAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Tracks"), (Message m, object? s) => ReadMessage_v_ao(m, (MprisObject)s!), this); + public Task GetCanEditTracksAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanEditTracks"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetPropertiesAsync() { return this.Connection.CallMethodAsync(CreateGetAllPropertiesMessage(__Interface), (Message m, object? s) => ReadMessage(m, (MprisObject)s!), this); - static PlayerProperties ReadMessage(Message message, MprisObject _) + static TrackListProperties ReadMessage(Message message, MprisObject _) { var reader = message.GetBodyReader(); return ReadProperties(ref reader); } } - public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true) + public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) { - return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext); - static PropertyChanges ReadMessage(Message message, MprisObject _) + return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + static PropertyChanges ReadMessage(Message message, MprisObject _) { var reader = message.GetBodyReader(); reader.ReadString(); // interface List changed = new(), invalidated = new(); - return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); + return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); } static string[] ReadInvalidated(ref Reader reader) { List? invalidated = null; - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.String); - while (reader.HasNext(headersEnd)) + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.String); + while (reader.HasNext(arrayEnd)) { invalidated ??= new(); var property = reader.ReadString(); switch (property) { - case "Metadata": invalidated.Add("Metadata"); break; - case "PlaybackStatus": invalidated.Add("PlaybackStatus"); break; - case "LoopStatus": invalidated.Add("LoopStatus"); break; - case "Volume": invalidated.Add("Volume"); break; - case "Shuffle": invalidated.Add("Shuffle"); break; - case "Position": invalidated.Add("Position"); break; - case "Rate": invalidated.Add("Rate"); break; - case "MinimumRate": invalidated.Add("MinimumRate"); break; - case "MaximumRate": invalidated.Add("MaximumRate"); break; - case "CanControl": invalidated.Add("CanControl"); break; - case "CanPlay": invalidated.Add("CanPlay"); break; - case "CanPause": invalidated.Add("CanPause"); break; - case "CanSeek": invalidated.Add("CanSeek"); break; + case "Tracks": invalidated.Add("Tracks"); break; + case "CanEditTracks": invalidated.Add("CanEditTracks"); break; } } return invalidated?.ToArray() ?? Array.Empty(); } } - private static PlayerProperties ReadProperties(ref Reader reader, List? changedList = null) + private static TrackListProperties ReadProperties(ref Reader reader, List? changedList = null) { - var props = new PlayerProperties(); - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.Struct); - while (reader.HasNext(headersEnd)) + var props = new TrackListProperties(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Struct); + while (reader.HasNext(arrayEnd)) { var property = reader.ReadString(); switch (property) { - case "Metadata": - reader.ReadSignature("a{sv}"); - props.Metadata = reader.ReadDictionaryOfStringToVariantValue(); - changedList?.Add("Metadata"); - break; - case "PlaybackStatus": - reader.ReadSignature("s"); - props.PlaybackStatus = reader.ReadString(); - changedList?.Add("PlaybackStatus"); - break; - case "LoopStatus": - reader.ReadSignature("s"); - props.LoopStatus = reader.ReadString(); - changedList?.Add("LoopStatus"); - break; - case "Volume": - reader.ReadSignature("d"); - props.Volume = reader.ReadDouble(); - changedList?.Add("Volume"); - break; - case "Shuffle": - reader.ReadSignature("b"); - props.Shuffle = reader.ReadBool(); - changedList?.Add("Shuffle"); - break; - case "Position": - reader.ReadSignature("i"); - props.Position = reader.ReadInt32(); - changedList?.Add("Position"); - break; - case "Rate": - reader.ReadSignature("d"); - props.Rate = reader.ReadDouble(); - changedList?.Add("Rate"); - break; - case "MinimumRate": - reader.ReadSignature("d"); - props.MinimumRate = reader.ReadDouble(); - changedList?.Add("MinimumRate"); - break; - case "MaximumRate": - reader.ReadSignature("d"); - props.MaximumRate = reader.ReadDouble(); - changedList?.Add("MaximumRate"); - break; - case "CanControl": - reader.ReadSignature("b"); - props.CanControl = reader.ReadBool(); - changedList?.Add("CanControl"); - break; - case "CanPlay": - reader.ReadSignature("b"); - props.CanPlay = reader.ReadBool(); - changedList?.Add("CanPlay"); - break; - case "CanPause": - reader.ReadSignature("b"); - props.CanPause = reader.ReadBool(); - changedList?.Add("CanPause"); + case "Tracks": + reader.ReadSignature("ao"); + props.Tracks = reader.ReadArrayOfObjectPath(); + changedList?.Add("Tracks"); break; - case "CanSeek": + case "CanEditTracks": reader.ReadSignature("b"); - props.CanSeek = reader.ReadBool(); - changedList?.Add("CanSeek"); + props.CanEditTracks = reader.ReadBool(); + changedList?.Add("CanEditTracks"); break; default: reader.ReadVariantValue(); break; } } - return props; + return props; + } + } + record MediaPlayer2Properties + { + public bool CanQuit { get; set; } = default!; + public bool Fullscreen { get; set; } = default!; + public bool CanSetFullscreen { get; set; } = default!; + public bool CanRaise { get; set; } = default!; + public bool HasTrackList { get; set; } = default!; + public string Identity { get; set; } = default!; + public string DesktopEntry { get; set; } = default!; + public string[] SupportedUriSchemes { get; set; } = default!; + public string[] SupportedMimeTypes { get; set; } = default!; + } + partial class MediaPlayer2 : MprisObject + { + private const string __Interface = "org.mpris.MediaPlayer2"; + public MediaPlayer2(MprisService service, ObjectPath path) : base(service, path) + { } + public Task RaiseAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "Raise"); + return writer.CreateMessage(); + } + } + public Task QuitAsync() + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: __Interface, + member: "Quit"); + return writer.CreateMessage(); + } + } + public Task SetCanQuitAsync(bool value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanQuit"); + writer.WriteSignature("b"); + writer.WriteBool(value); + return writer.CreateMessage(); + } } - } - record TrackListProperties - { - public ObjectPath[] Tracks { get; set; } = default!; - public bool CanEditTracks { get; set; } = default!; - } - partial class TrackList : MprisObject - { - private const string __Interface = "org.mpris.MediaPlayer2.TrackList"; - public TrackList(MprisService service, ObjectPath path) : base(service, path) - { } - public Task[]> GetTracksMetadataAsync(ObjectPath[] a0) + public Task SetFullscreenAsync(bool value) { - return this.Connection.CallMethodAsync(CreateMessage(), (Message m, object? s) => ReadMessage_aaesv(m, (MprisObject)s!), this); + return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: __Interface, - signature: "ao", - member: "GetTracksMetadata"); - writer.WriteArray(a0); + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("Fullscreen"); + writer.WriteSignature("b"); + writer.WriteBool(value); return writer.CreateMessage(); } } - public Task AddTrackAsync(string a0, ObjectPath a1, bool a2) + public Task SetCanSetFullscreenAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: __Interface, - signature: "sob", - member: "AddTrack"); - writer.WriteString(a0); - writer.WriteObjectPath(a1); - writer.WriteBool(a2); + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanSetFullscreen"); + writer.WriteSignature("b"); + writer.WriteBool(value); return writer.CreateMessage(); } } - public Task RemoveTrackAsync(ObjectPath a0) + public Task SetCanRaiseAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: __Interface, - signature: "o", - member: "RemoveTrack"); - writer.WriteObjectPath(a0); + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("CanRaise"); + writer.WriteSignature("b"); + writer.WriteBool(value); return writer.CreateMessage(); } } - public Task GoToAsync(ObjectPath a0) + public Task SetHasTrackListAsync(bool value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, - @interface: __Interface, - signature: "o", - member: "GoTo"); - writer.WriteObjectPath(a0); + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("HasTrackList"); + writer.WriteSignature("b"); + writer.WriteBool(value); return writer.CreateMessage(); } } - public ValueTask WatchTrackListReplacedAsync(Action handler, bool emitOnCapturedContext = true) - => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackListReplaced", (Message m, object? s) => ReadMessage_aoo(m, (MprisObject)s!), handler, emitOnCapturedContext); - public ValueTask WatchTrackAddedAsync(Action A0, ObjectPath A1)> handler, bool emitOnCapturedContext = true) - => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackAdded", (Message m, object? s) => ReadMessage_aesvo(m, (MprisObject)s!), handler, emitOnCapturedContext); - public ValueTask WatchTrackRemovedAsync(Action handler, bool emitOnCapturedContext = true) - => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackRemoved", (Message m, object? s) => ReadMessage_o(m, (MprisObject)s!), handler, emitOnCapturedContext); - public ValueTask WatchTrackMetadataChangedAsync(Action A1)> handler, bool emitOnCapturedContext = true) - => base.WatchSignalAsync(Service.Destination, __Interface, Path, "TrackMetadataChanged", (Message m, object? s) => ReadMessage_oaesv(m, (MprisObject)s!), handler, emitOnCapturedContext); - public Task SetTracksAsync(ObjectPath[] value) + public Task SetIdentityAsync(string value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -997,18 +1141,56 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("Tracks"); - writer.WriteSignature("ao"); + writer.WriteString("Identity"); + writer.WriteSignature("s"); + writer.WriteString(value); + return writer.CreateMessage(); + } + } + public Task SetDesktopEntryAsync(string value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("DesktopEntry"); + writer.WriteSignature("s"); + writer.WriteString(value); + return writer.CreateMessage(); + } + } + public Task SetSupportedUriSchemesAsync(string[] value) + { + return this.Connection.CallMethodAsync(CreateMessage()); + MessageBuffer CreateMessage() + { + var writer = this.Connection.GetMessageWriter(); + writer.WriteMethodCallHeader( + destination: Service.Destination, + path: Path, + @interface: "org.freedesktop.DBus.Properties", + signature: "ssv", + member: "Set"); + writer.WriteString(__Interface); + writer.WriteString("SupportedUriSchemes"); + writer.WriteSignature("as"); writer.WriteArray(value); return writer.CreateMessage(); } } - public Task SetCanEditTracksAsync(bool value) + public Task SetSupportedMimeTypesAsync(string[] value) { return this.Connection.CallMethodAsync(CreateMessage()); MessageBuffer CreateMessage() { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -1016,70 +1198,126 @@ MessageBuffer CreateMessage() signature: "ssv", member: "Set"); writer.WriteString(__Interface); - writer.WriteString("CanEditTracks"); - writer.WriteSignature("b"); - writer.WriteBool(value); + writer.WriteString("SupportedMimeTypes"); + writer.WriteSignature("as"); + writer.WriteArray(value); return writer.CreateMessage(); } } - public Task GetTracksAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Tracks"), (Message m, object? s) => ReadMessage_v_ao(m, (MprisObject)s!), this); - public Task GetCanEditTracksAsync() - => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanEditTracks"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); - public Task GetPropertiesAsync() + public Task GetCanQuitAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanQuit"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetFullscreenAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Fullscreen"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanSetFullscreenAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanSetFullscreen"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetCanRaiseAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "CanRaise"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetHasTrackListAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "HasTrackList"), (Message m, object? s) => ReadMessage_v_b(m, (MprisObject)s!), this); + public Task GetIdentityAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "Identity"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); + public Task GetDesktopEntryAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "DesktopEntry"), (Message m, object? s) => ReadMessage_v_s(m, (MprisObject)s!), this); + public Task GetSupportedUriSchemesAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "SupportedUriSchemes"), (Message m, object? s) => ReadMessage_v_as(m, (MprisObject)s!), this); + public Task GetSupportedMimeTypesAsync() + => this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, "SupportedMimeTypes"), (Message m, object? s) => ReadMessage_v_as(m, (MprisObject)s!), this); + public Task GetPropertiesAsync() { return this.Connection.CallMethodAsync(CreateGetAllPropertiesMessage(__Interface), (Message m, object? s) => ReadMessage(m, (MprisObject)s!), this); - static TrackListProperties ReadMessage(Message message, MprisObject _) + static MediaPlayer2Properties ReadMessage(Message message, MprisObject _) { var reader = message.GetBodyReader(); return ReadProperties(ref reader); } } - public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true) + public ValueTask WatchPropertiesChangedAsync(Action> handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None) { - return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext); - static PropertyChanges ReadMessage(Message message, MprisObject _) + return base.WatchPropertiesChangedAsync(__Interface, (Message m, object? s) => ReadMessage(m, (MprisObject)s!), handler, emitOnCapturedContext, flags); + static PropertyChanges ReadMessage(Message message, MprisObject _) { var reader = message.GetBodyReader(); reader.ReadString(); // interface List changed = new(), invalidated = new(); - return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); + return new PropertyChanges(ReadProperties(ref reader, changed), changed.ToArray(), ReadInvalidated(ref reader)); } static string[] ReadInvalidated(ref Reader reader) { List? invalidated = null; - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.String); - while (reader.HasNext(headersEnd)) + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.String); + while (reader.HasNext(arrayEnd)) { invalidated ??= new(); var property = reader.ReadString(); switch (property) { - case "Tracks": invalidated.Add("Tracks"); break; - case "CanEditTracks": invalidated.Add("CanEditTracks"); break; + case "CanQuit": invalidated.Add("CanQuit"); break; + case "Fullscreen": invalidated.Add("Fullscreen"); break; + case "CanSetFullscreen": invalidated.Add("CanSetFullscreen"); break; + case "CanRaise": invalidated.Add("CanRaise"); break; + case "HasTrackList": invalidated.Add("HasTrackList"); break; + case "Identity": invalidated.Add("Identity"); break; + case "DesktopEntry": invalidated.Add("DesktopEntry"); break; + case "SupportedUriSchemes": invalidated.Add("SupportedUriSchemes"); break; + case "SupportedMimeTypes": invalidated.Add("SupportedMimeTypes"); break; } } return invalidated?.ToArray() ?? Array.Empty(); } } - private static TrackListProperties ReadProperties(ref Reader reader, List? changedList = null) + private static MediaPlayer2Properties ReadProperties(ref Reader reader, List? changedList = null) { - var props = new TrackListProperties(); - ArrayEnd headersEnd = reader.ReadArrayStart(DBusType.Struct); - while (reader.HasNext(headersEnd)) + var props = new MediaPlayer2Properties(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Struct); + while (reader.HasNext(arrayEnd)) { var property = reader.ReadString(); switch (property) { - case "Tracks": - reader.ReadSignature("ao"); - props.Tracks = reader.ReadArrayOfObjectPath(); - changedList?.Add("Tracks"); + case "CanQuit": + reader.ReadSignature("b"); + props.CanQuit = reader.ReadBool(); + changedList?.Add("CanQuit"); break; - case "CanEditTracks": + case "Fullscreen": reader.ReadSignature("b"); - props.CanEditTracks = reader.ReadBool(); - changedList?.Add("CanEditTracks"); + props.Fullscreen = reader.ReadBool(); + changedList?.Add("Fullscreen"); + break; + case "CanSetFullscreen": + reader.ReadSignature("b"); + props.CanSetFullscreen = reader.ReadBool(); + changedList?.Add("CanSetFullscreen"); + break; + case "CanRaise": + reader.ReadSignature("b"); + props.CanRaise = reader.ReadBool(); + changedList?.Add("CanRaise"); + break; + case "HasTrackList": + reader.ReadSignature("b"); + props.HasTrackList = reader.ReadBool(); + changedList?.Add("HasTrackList"); + break; + case "Identity": + reader.ReadSignature("s"); + props.Identity = reader.ReadString(); + changedList?.Add("Identity"); + break; + case "DesktopEntry": + reader.ReadSignature("s"); + props.DesktopEntry = reader.ReadString(); + changedList?.Add("DesktopEntry"); + break; + case "SupportedUriSchemes": + reader.ReadSignature("as"); + props.SupportedUriSchemes = reader.ReadArrayOfString(); + changedList?.Add("SupportedUriSchemes"); + break; + case "SupportedMimeTypes": + reader.ReadSignature("as"); + props.SupportedMimeTypes = reader.ReadArrayOfString(); + changedList?.Add("SupportedMimeTypes"); break; default: reader.ReadVariantValue(); @@ -1095,9 +1333,10 @@ partial class MprisService public string Destination { get; } public MprisService(Tmds.DBus.Protocol.Connection connection, string destination) => (Connection, Destination) = (connection, destination); - public MediaPlayer2 CreateMediaPlayer2(string path) => new MediaPlayer2(this, path); public Player CreatePlayer(string path) => new Player(this, path); + public Playlists CreatePlaylists(string path) => new Playlists(this, path); public TrackList CreateTrackList(string path) => new TrackList(this, path); + public MediaPlayer2 CreateMediaPlayer2(string path) => new MediaPlayer2(this, path); } class MprisObject { @@ -1108,7 +1347,7 @@ protected MprisObject(MprisService service, ObjectPath path) => (Service, Path) = (service, path); protected MessageBuffer CreateGetPropertyMessage(string @interface, string property) { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -1121,7 +1360,7 @@ protected MessageBuffer CreateGetPropertyMessage(string @interface, string prope } protected MessageBuffer CreateGetAllPropertiesMessage(string @interface) { - using var writer = this.Connection.GetMessageWriter(); + var writer = this.Connection.GetMessageWriter(); writer.WriteMethodCallHeader( destination: Service.Destination, path: Path, @@ -1131,7 +1370,7 @@ protected MessageBuffer CreateGetAllPropertiesMessage(string @interface) writer.WriteString(@interface); return writer.CreateMessage(); } - protected ValueTask WatchPropertiesChangedAsync(string @interface, MessageValueReader> reader, Action> handler, bool emitOnCapturedContext) + protected ValueTask WatchPropertiesChangedAsync(string @interface, MessageValueReader> reader, Action> handler, bool emitOnCapturedContext, ObserverFlags flags) { var rule = new MatchRule { @@ -1144,10 +1383,9 @@ protected ValueTask WatchPropertiesChangedAsync(string }; return this.Connection.AddMatchAsync(rule, reader, (Exception? ex, PropertyChanges changes, object? rs, object? hs) => ((Action>)hs!).Invoke(ex, changes), - ObserverFlags.None, - this, handler, emitOnCapturedContext); + this, handler, emitOnCapturedContext, flags); } - public ValueTask WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, MessageValueReader reader, Action handler, bool emitOnCapturedContext) + public ValueTask WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, MessageValueReader reader, Action handler, bool emitOnCapturedContext, ObserverFlags flags) { var rule = new MatchRule { @@ -1159,10 +1397,9 @@ public ValueTask WatchSignalAsync(string sender, string @inte }; return this.Connection.AddMatchAsync(rule, reader, (Exception? ex, TArg arg, object? rs, object? hs) => ((Action)hs!).Invoke(ex, arg), - ObserverFlags.None, - this, handler, emitOnCapturedContext); + this, handler, emitOnCapturedContext, flags); } - public ValueTask WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action handler, bool emitOnCapturedContext) + public ValueTask WatchSignalAsync(string sender, string @interface, ObjectPath path, string signal, Action handler, bool emitOnCapturedContext, ObserverFlags flags) { var rule = new MatchRule { @@ -1173,8 +1410,12 @@ public ValueTask WatchSignalAsync(string sender, string @interface, Interface = @interface }; return this.Connection.AddMatchAsync(rule, (Message message, object? state) => null!, - (Exception? ex, object v, object? rs, object? hs) => ((Action)hs!).Invoke(ex), - ObserverFlags.None, this, handler, emitOnCapturedContext); + (Exception? ex, object v, object? rs, object? hs) => ((Action)hs!).Invoke(ex), this, handler, emitOnCapturedContext, flags); + } + protected static long ReadMessage_x(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + return reader.ReadInt64(); } protected static string ReadMessage_v_s(Message message, MprisObject _) { @@ -1182,11 +1423,11 @@ protected static string ReadMessage_v_s(Message message, MprisObject _) reader.ReadSignature("s"); return reader.ReadString(); } - protected static string[] ReadMessage_v_as(Message message, MprisObject _) + protected static double ReadMessage_v_d(Message message, MprisObject _) { var reader = message.GetBodyReader(); - reader.ReadSignature("as"); - return reader.ReadArrayOfString(); + reader.ReadSignature("d"); + return reader.ReadDouble(); } protected static bool ReadMessage_v_b(Message message, MprisObject _) { @@ -1200,28 +1441,44 @@ protected static Dictionary ReadMessage_v_aesv(Message mes reader.ReadSignature("a{sv}"); return reader.ReadDictionaryOfStringToVariantValue(); } - protected static double ReadMessage_v_d(Message message, MprisObject _) + protected static long ReadMessage_v_x(Message message, MprisObject _) { var reader = message.GetBodyReader(); - reader.ReadSignature("d"); - return reader.ReadDouble(); + reader.ReadSignature("x"); + return reader.ReadInt64(); + } + protected static (ObjectPath, string, string)[] ReadMessage_arossz(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + return ReadType_arossz(ref reader); + } + protected static (ObjectPath, string, string) ReadMessage_rossz(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + return ReadType_rossz(ref reader); + } + protected static uint ReadMessage_v_u(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + reader.ReadSignature("u"); + return reader.ReadUInt32(); + } + protected static string[] ReadMessage_v_as(Message message, MprisObject _) + { + var reader = message.GetBodyReader(); + reader.ReadSignature("as"); + return reader.ReadArrayOfString(); } - protected static int ReadMessage_v_i(Message message, MprisObject _) + protected static (bool, (ObjectPath, string, string)) ReadMessage_v_rbrosszz(Message message, MprisObject _) { var reader = message.GetBodyReader(); - reader.ReadSignature("i"); - return reader.ReadInt32(); + reader.ReadSignature("(b(oss))"); + return ReadType_rbrosszz(ref reader); } protected static Dictionary[] ReadMessage_aaesv(Message message, MprisObject _) { var reader = message.GetBodyReader(); - List> list = new(); - var arrayStart = reader.ReadArrayStart(DBusType.DictEntry); - while (reader.HasNext(arrayStart)) - { - list.Add(reader.ReadDictionaryOfStringToVariantValue()); - } - return list.ToArray(); + return ReadType_aaesv(ref reader); } protected static (ObjectPath[], ObjectPath) ReadMessage_aoo(Message message, MprisObject _) { @@ -1255,6 +1512,47 @@ protected static ObjectPath[] ReadMessage_v_ao(Message message, MprisObject _) reader.ReadSignature("ao"); return reader.ReadArrayOfObjectPath(); } + protected static (bool, (ObjectPath, string, string)) ReadType_rbrosszz(ref Reader reader) + { + return (reader.ReadBool(), ReadType_rossz(ref reader)); + } + protected static (ObjectPath, string, string) ReadType_rossz(ref Reader reader) + { + return (reader.ReadObjectPath(), reader.ReadString(), reader.ReadString()); + } + protected static (ObjectPath, string, string)[] ReadType_arossz(ref Reader reader) + { + List<(ObjectPath, string, string)> list = new(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Struct); + while (reader.HasNext(arrayEnd)) + { + list.Add(ReadType_rossz(ref reader)); + } + return list.ToArray(); + } + protected static Dictionary[] ReadType_aaesv(ref Reader reader) + { + List> list = new(); + ArrayEnd arrayEnd = reader.ReadArrayStart(DBusType.Array); + while (reader.HasNext(arrayEnd)) + { + list.Add(reader.ReadDictionaryOfStringToVariantValue()); + } + return list.ToArray(); + } + protected static void WriteType_rbrosszz(ref MessageWriter writer, (bool, (ObjectPath, string, string)) value) + { + writer.WriteStructureStart(); + writer.WriteBool(value.Item1); + WriteType_rossz(ref writer, value.Item2); + } + protected static void WriteType_rossz(ref MessageWriter writer, (ObjectPath, string, string) value) + { + writer.WriteStructureStart(); + writer.WriteObjectPath(value.Item1); + writer.WriteString(value.Item2); + writer.WriteString(value.Item3); + } } class PropertyChanges { diff --git a/samples/MediaPlayerRemote/Program.cs b/samples/MediaPlayerRemote/Program.cs index 72e6bafa..2751e6f0 100644 --- a/samples/MediaPlayerRemote/Program.cs +++ b/samples/MediaPlayerRemote/Program.cs @@ -31,7 +31,9 @@ string firstPlayer = availablePlayers.First(); Console.WriteLine($"Using: {firstPlayer}"); -Player player = new Player(new MprisService(connection,firstPlayer)); +var mpris = new MprisService(connection,firstPlayer); + +Player player = new Player(mpris); foreach (var track in await player.GetTracksAsync()) { Console.WriteLine($"* {track}"); diff --git a/src/Tmds.DBus.Protocol/MessageWriter.Dictionary.cs b/src/Tmds.DBus.Protocol/MessageWriter.Dictionary.cs index 74d071ca..34ca420c 100644 --- a/src/Tmds.DBus.Protocol/MessageWriter.Dictionary.cs +++ b/src/Tmds.DBus.Protocol/MessageWriter.Dictionary.cs @@ -16,18 +16,18 @@ public void WriteDictionaryEntryStart() // Write method for the common 'a{sv}' type. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026")] // It's safe to call WriteDictionary with these types. - public void WriteDictionary(IEnumerable> value) - => WriteDictionary(value); + public void WriteDictionary(IEnumerable> value) + => WriteDictionary(value); // Write method for the common 'a{sv}' type. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026")] // It's safe to call WriteDictionary with these types. - public void WriteDictionary(KeyValuePair[] value) - => WriteDictionary(value); + public void WriteDictionary(KeyValuePair[] value) + => WriteDictionary(value); // Write method for the common 'a{sv}' type. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026")] // It's safe to call WriteDictionary with these types. - public void WriteDictionary(Dictionary value) - => WriteDictionary(value); + public void WriteDictionary(Dictionary value) + => WriteDictionary(value); [RequiresUnreferencedCode(Strings.UseNonGenericWriteDictionary)] [Obsolete(Strings.UseNonGenericWriteDictionaryObsolete)] diff --git a/src/Tmds.DBus.Protocol/Reader.Array.cs b/src/Tmds.DBus.Protocol/Reader.Array.cs index 5c7373b1..f1fffe3b 100644 --- a/src/Tmds.DBus.Protocol/Reader.Array.cs +++ b/src/Tmds.DBus.Protocol/Reader.Array.cs @@ -41,7 +41,7 @@ public Signature[] ReadArrayOfSignature() public VariantValue[] ReadArrayOfVariantValue() => ReadArrayOfT(); - public T[] ReadArrayOfUnixFd<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() where T : SafeHandle + public T[] ReadArrayOfHandle<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() where T : SafeHandle => ReadArrayOfT(); [RequiresUnreferencedCode(Strings.UseNonGenericReadArray)] diff --git a/src/Tmds.DBus.Tool/ProtocolGenerator.cs b/src/Tmds.DBus.Tool/ProtocolGenerator.cs index d19331f9..60d54246 100644 --- a/src/Tmds.DBus.Tool/ProtocolGenerator.cs +++ b/src/Tmds.DBus.Tool/ProtocolGenerator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Xml.Linq; using Microsoft.CodeAnalysis; @@ -18,8 +19,10 @@ class ProtocolGeneratorSettings class ProtocolGenerator : IGenerator { private readonly ProtocolGeneratorSettings _settings; - private readonly StringBuilder _sb; - private readonly Dictionary _messageReadMethods; + private readonly StringBuilder _sb = new(); + private readonly Dictionary _messageReadMethods = new(); + private readonly Dictionary _typeReadMethods = new(); + private readonly Dictionary _typeWriteMethods = new(); private readonly string _objectName; private readonly string _serviceClassName; private int _indentation = 0; @@ -27,8 +30,6 @@ class ProtocolGenerator : IGenerator public ProtocolGenerator(ProtocolGeneratorSettings settings) { _settings = settings; - _sb = new StringBuilder(); - _messageReadMethods = new Dictionary(); _objectName = $"{settings.ServiceName}Object"; _serviceClassName = $"{settings.ServiceName}Service"; } @@ -146,7 +147,7 @@ private void AppendObjectClass() AppendLine(""); AppendLine("protected MessageBuffer CreateGetPropertyMessage(string @interface, string property)"); StartBlock(); - AppendLine("using var writer = this.Connection.GetMessageWriter();"); + AppendLine("var writer = this.Connection.GetMessageWriter();"); AppendLine(""); AppendLine("writer.WriteMethodCallHeader("); AppendLine(" destination: Service.Destination,"); @@ -164,7 +165,7 @@ private void AppendObjectClass() AppendLine(""); AppendLine("protected MessageBuffer CreateGetAllPropertiesMessage(string @interface)"); StartBlock(); - AppendLine("using var writer = this.Connection.GetMessageWriter();"); + AppendLine("var writer = this.Connection.GetMessageWriter();"); AppendLine(""); AppendLine("writer.WriteMethodCallHeader("); AppendLine(" destination: Service.Destination,"); @@ -231,6 +232,171 @@ private void AppendObjectClass() AppendReadMessageMethod(readMethod.Key, readMethod.Value.Item1, readMethod.Value.Item2); } + foreach (var readMethod in _typeReadMethods) + { + AppendReadTypeMethod(readMethod.Key, readMethod.Value); + } + + foreach (var writeMethod in _typeWriteMethods) + { + AppendWriteTypeMethod(writeMethod.Key, writeMethod.Value); + } + + EndBlock(); + } + + private void AppendReadTypeMethod(string method, string signature) + { + string dotnetReturnType = GetDotnetReadType(signature); + AppendLine($"protected static {dotnetReturnType} {method}(ref Reader reader)"); + StartBlock(); + SignatureReader reader = new SignatureReader(Encoding.UTF8.GetBytes(signature)); + if (reader.TryRead(out DBusType type, out ReadOnlySpan innerSignature)) + { + reader = new SignatureReader(innerSignature); + if (type == DBusType.Array) + { + if (!reader.TryRead(out DBusType itemType, out ReadOnlySpan itemInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (itemType == DBusType.DictEntry) + { + reader = new SignatureReader(itemInnerSignature); + if (!reader.TryRead(out DBusType keyType, out ReadOnlySpan keyInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (!reader.TryRead(out DBusType valueType, out ReadOnlySpan valueInnerSignature)) + { + ThrowInvalidSignature(signature); + } + + string dotnetKeyType = GetDotnetReadType(keyType, keyInnerSignature); + string dotnetValueType = GetDotnetReadType(valueType, valueInnerSignature); + string keyTypeSignature = GetSignature(keyType, keyInnerSignature); + string valueTypeSignature = GetSignature(valueType, valueInnerSignature); + + AppendLine($"Dictionary<{dotnetKeyType}, {dotnetValueType}> dictionary = new();"); + AppendLine($"ArrayEnd dictEnd = reader.ReadDictionaryStart();"); + + AppendLine($"while (reader.HasNext(dictEnd))"); + StartBlock(); + AppendLine($"var key = {CallReadArgumentType(keyTypeSignature)};"); + AppendLine($"var value = {CallReadArgumentType(valueTypeSignature)};"); + AppendLine($"dictionary[key] = value;"); + EndBlock(); + + AppendLine($"return dictionary;"); + } + else + { + string dotnetItemType = GetDotnetReadType(itemType, itemInnerSignature); + + AppendLine($"List<{dotnetItemType}> list = new();"); + AppendLine($"ArrayEnd arrayEnd = reader.ReadArrayStart({GetDBusTypeEnumValue(itemType)});"); + + AppendLine($"while (reader.HasNext(arrayEnd))"); + StartBlock(); + AppendLine($"list.Add({CallReadArgumentType(Encoding.UTF8.GetString(innerSignature))});"); + EndBlock(); + + AppendLine($"return list.ToArray();"); + } + } + else if (type == DBusType.Struct) + { + StringBuilder sb = new(); + sb.Append("return ("); + bool first = true; + while (reader.TryRead(out DBusType fieldType, out ReadOnlySpan fieldInnerSignature)) + { + if (!first) + { + sb.Append(", "); + } + first = false; + sb.Append(CallReadArgumentType(GetSignature(fieldType, fieldInnerSignature))); + } + sb.Append(");"); + AppendLine(sb.ToString()); + } + else + { + ThrowInvalidSignature(signature); + } + } + EndBlock(); + } + + private void AppendWriteTypeMethod(string method, string signature) + { + string dotnetArgType = GetDotnetWriteType(signature); + AppendLine($"protected static void {method}(ref MessageWriter writer, {dotnetArgType} value)"); + StartBlock(); + SignatureReader reader = new SignatureReader(Encoding.UTF8.GetBytes(signature)); + if (reader.TryRead(out DBusType type, out ReadOnlySpan innerSignature)) + { + reader = new SignatureReader(innerSignature); + if (type == DBusType.Array) + { + if (!reader.TryRead(out DBusType itemType, out ReadOnlySpan itemInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (itemType == DBusType.DictEntry) + { + reader = new SignatureReader(itemInnerSignature); + if (!reader.TryRead(out DBusType keyType, out ReadOnlySpan keyInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (!reader.TryRead(out DBusType valueType, out ReadOnlySpan valueInnerSignature)) + { + ThrowInvalidSignature(signature); + } + + string keyTypeSignature = GetSignature(keyType, keyInnerSignature); + string valueTypeSignature = GetSignature(valueType, valueInnerSignature); + + AppendLine($"ArrayStart arrayStart = writer.WriteDictionaryStart();"); + AppendLine($"foreach (var item in value)"); + StartBlock(); + AppendLine($"writer.WriteDictionaryEntryStart();"); + AppendLine($"{CallWriteArgumentType(keyTypeSignature, "item.Key")};"); + AppendLine($"{CallWriteArgumentType(valueTypeSignature, "item.Value")};"); + EndBlock(); + AppendLine($"writer.WriteDictionaryEnd(arrayStart);"); + } + else + { + string dotnetItemSignature = GetSignature(itemType, itemInnerSignature); + + AppendLine($"ArrayStart arrayStart = writer.WriteArrayStart({GetDBusTypeEnumValue(itemType)});"); + AppendLine($"foreach (var item in value)"); + StartBlock(); + AppendLine($"{CallWriteArgumentType(dotnetItemSignature, "item")};"); + EndBlock(); + AppendLine($"writer.WriteArrayEnd(arrayStart);"); + } + } + else if (type == DBusType.Struct) + { + AppendLine($"writer.WriteStructureStart();"); + int i = 1; + while (reader.TryRead(out DBusType fieldType, out ReadOnlySpan fieldInnerSignature)) + { + string fieldSignature = GetSignature(fieldType, fieldInnerSignature); + string parameterName = i < 8 ? $"value.Item{i}" : $"value.Rest.Item{1 + (i % 8)}"; + AppendLine($"{CallWriteArgumentType(fieldSignature, parameterName)};"); + i++; + } + } + else + { + ThrowInvalidSignature(signature); + } + } EndBlock(); } @@ -248,7 +414,7 @@ private void AppendInterface(string name, XElement interfaceXml) StartBlock(); foreach (var property in readableProperties) { - AppendLine($"public {property.DotnetType} {property.NameUpper} {{ get; set; }} = default!;"); + AppendLine($"public {property.DotnetReadType} {property.NameUpper} {{ get; set; }} = default!;"); } EndBlock(); } @@ -284,7 +450,7 @@ private void AppendInterface(string name, XElement interfaceXml) { foreach (var property in readableProperties) { - AppendLine($"public Task<{property.DotnetType}> Get{property.NameUpper}Async()"); + AppendLine($"public Task<{property.DotnetReadType}> Get{property.NameUpper}Async()"); _indentation++; string readMessageName = GetReadMessageMethodName(new[] { property }, variant: true); AppendLine($"=> this.Connection.CallMethodAsync(CreateGetPropertyMessage(__Interface, \"{property.Name}\"), (Message m, object? s) => {readMessageName}(m, ({_objectName})s!), this);"); @@ -352,8 +518,8 @@ private void AppendInterface(string name, XElement interfaceXml) { AppendLine($"case \"{property.Name}\":"); _indentation++; - AppendLine($"reader.ReadSignature(\"{property.Type}\");"); - AppendLine($"props.{property.NameUpper} = reader.{GetArgumentReadMethodName(property)}();"); + AppendLine($"reader.ReadSignature(\"{property.Signature}\");"); + AppendLine($"props.{property.NameUpper} = {CallReadArgumentType(property.Signature)};"); AppendLine($"changedList?.Add(\"{property.NameUpper}\");"); AppendLine("break;"); _indentation--; @@ -378,13 +544,13 @@ private void AppendInterface(string name, XElement interfaceXml) private void AppendPropertySetMethod(Argument property) { string methodName = $"Set{property.NameUpper}Async"; - AppendLine($"public Task {methodName}({property.DotnetType} value)"); + AppendLine($"public Task {methodName}({property.DotnetWriteType} value)"); StartBlock(); AppendLine($"return this.Connection.CallMethodAsync(CreateMessage());"); AppendLine(""); AppendLine("MessageBuffer CreateMessage()"); StartBlock(); - AppendLine("using var writer = this.Connection.GetMessageWriter();"); + AppendLine("var writer = this.Connection.GetMessageWriter();"); AppendLine(""); AppendLine("writer.WriteMethodCallHeader("); AppendLine(" destination: Service.Destination,"); @@ -395,8 +561,8 @@ private void AppendPropertySetMethod(Argument property) AppendLine(""); AppendLine("writer.WriteString(__Interface);"); AppendLine($"writer.WriteString(\"{property.Name}\");"); - AppendLine($"writer.WriteSignature(\"{property.Type}\");"); - AppendLine($"writer.{GetArgumentWriteMethodName(property)}(value);"); + AppendLine($"writer.WriteSignature(\"{property.Signature}\");"); + AppendLine($"{CallWriteArgumentType(property.Signature, "value")};"); AppendLine(""); AppendLine("return writer.CreateMessage();"); EndBlock(); @@ -417,7 +583,7 @@ private void AppendSignal(string className, XElement signalXml) string dbusSignalName = (string)signalXml.Attribute("name"); var args = signalXml.Elements("arg").Select(ToArgument).ToArray(); - string watchType = args.Length == 0 ? null : args.Length == 1 ? args[0].DotnetType : TupleOf(args.Select(arg => $"{arg.DotnetType} {arg.NameUpper}")); + string watchType = args.Length == 0 ? null : args.Length == 1 ? args[0].DotnetReadType : TupleOf(args.Select(arg => $"{arg.DotnetReadType} {arg.NameUpper}")); string methodArg = watchType == null ? $"Action" : $"Action"; string dotnetMethodName = "Watch" + Prettify(dbusSignalName) + "Async"; AppendLine($"public ValueTask {dotnetMethodName}({methodArg} handler, bool emitOnCapturedContext = true, ObserverFlags flags = ObserverFlags.None)"); @@ -434,7 +600,7 @@ private void AppendSignal(string className, XElement signalXml) private string GetReadMessageMethodName(Argument[] args, bool variant) { - string mangle = string.Join("", args.Select(arg => arg.Type)).Replace('{', 'e').Replace('(', 'r').Replace("}", "").Replace(")", "z"); + string mangle = MangleSignatureForMethodName(string.Join("", args.Select(arg => arg.Signature))); if (variant) { mangle = "v_" + mangle; @@ -447,6 +613,84 @@ private string GetReadMessageMethodName(Argument[] args, bool variant) return methodName; } + private string GetReadTypeMethodName(string signature) + { + string mangle = MangleSignatureForMethodName(signature); + string methodName = "ReadType_" + mangle; + if (!_typeReadMethods.ContainsKey(methodName)) + { + _typeReadMethods.Add(methodName, signature); + + // Ensure inner types are readable. + CallForInnerSignatures(signature, sig => CallReadArgumentType(sig)); + } + return methodName; + } + + private void CallForInnerSignatures(string signature, Action action) + { + SignatureReader reader = new SignatureReader(Encoding.UTF8.GetBytes(signature)); + if (reader.TryRead(out DBusType type, out ReadOnlySpan innerSignature) && innerSignature.Length > 0) + { + reader = new SignatureReader(innerSignature); + if (type == DBusType.Array) + { + if (!reader.TryRead(out DBusType itemType, out ReadOnlySpan itemInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (itemType == DBusType.DictEntry) + { + reader = new SignatureReader(itemInnerSignature); + if (!reader.TryRead(out DBusType keyType, out ReadOnlySpan keyInnerSignature)) + { + ThrowInvalidSignature(signature); + } + if (!reader.TryRead(out DBusType valueType, out ReadOnlySpan valueInnerSignature)) + { + ThrowInvalidSignature(signature); + } + action(GetSignature(keyType, keyInnerSignature)); + action(GetSignature(valueType, valueInnerSignature)); + } + else + { + action(Encoding.UTF8.GetString(innerSignature)); + } + } + else if (type == DBusType.Struct) + { + while (reader.TryRead(out DBusType fieldType, out ReadOnlySpan fieldInnerSignature)) + { + action(GetSignature(fieldType, fieldInnerSignature)); + } + } + else + { + ThrowInvalidSignature(signature); + } + } + } + + private string GetWriteTypeMethodName(string signature) + { + string mangle = MangleSignatureForMethodName(signature); + string methodName = "WriteType_" + mangle; + if (!_typeWriteMethods.ContainsKey(methodName)) + { + _typeWriteMethods.Add(methodName, signature); + + // Ensure inner types are writable. + CallForInnerSignatures(signature, sig => CallWriteArgumentType(sig, "dummy")); + } + return methodName; + } + + private static string MangleSignatureForMethodName(string signature) + { + return signature.Replace('{', 'e').Replace('(', 'r').Replace("}", "").Replace(")", "z"); + } + private static string TupleOf(IEnumerable elements) => $"({string.Join(", ", elements)})"; @@ -456,10 +700,10 @@ private void AppendMethod(XElement methodXml) string dbusMethodName = (string)methodXml.Attribute("name"); var inArgs = methodXml.Elements("arg").Where(arg => (arg.Attribute("direction")?.Value ?? "in") == "in").Select(ToArgument).ToArray(); var outArgs = methodXml.Elements("arg").Where(arg => arg.Attribute("direction")?.Value == "out").Select(ToArgument).ToArray(); - string dotnetReturnType = outArgs.Length == 0 ? null : outArgs.Length == 1 ? outArgs[0].DotnetType : TupleOf(outArgs.Select(arg => $"{arg.DotnetType} {arg.NameUpper}")); + string dotnetReturnType = outArgs.Length == 0 ? null : outArgs.Length == 1 ? outArgs[0].DotnetReadType : TupleOf(outArgs.Select(arg => $"{arg.DotnetReadType} {arg.NameUpper}")); string retType = dotnetReturnType == null ? "Task" : $"Task<{dotnetReturnType}>"; - string args = TupleOf(inArgs.Select(arg => $"{arg.DotnetType} {arg.NameLower}")); + string args = TupleOf(inArgs.Select(arg => $"{arg.DotnetWriteType} {arg.NameLower}")); string dotnetMethodName = Prettify(dbusMethodName) + "Async"; AppendLine($"public {retType} {dotnetMethodName}{args}"); @@ -477,7 +721,7 @@ private void AppendMethod(XElement methodXml) AppendLine("MessageBuffer CreateMessage()"); StartBlock(); - AppendLine("using var writer = this.Connection.GetMessageWriter();"); + AppendLine("var writer = this.Connection.GetMessageWriter();"); AppendLine(""); AppendLine("writer.WriteMethodCallHeader("); AppendLine($" destination: Service.Destination,"); @@ -485,7 +729,7 @@ private void AppendMethod(XElement methodXml) AppendLine($" @interface: __Interface,"); if (inArgs.Length > 0) { - string signature = string.Join("", inArgs.Select(a => a.Type)); + string signature = string.Join("", inArgs.Select(a => a.Signature)); AppendLine($" signature: \"{signature}\","); } AppendLine($" member: \"{dbusMethodName}\");"); @@ -495,8 +739,7 @@ private void AppendMethod(XElement methodXml) } foreach (var inArg in inArgs) { - string writeMethod = GetArgumentWriteMethodName(inArg); - AppendLine($"writer.{writeMethod}({inArg.NameLower});"); + AppendLine($"{CallWriteArgumentType(inArg.Signature, inArg.NameLower)};"); } AppendLine(""); AppendLine("return writer.CreateMessage();"); @@ -505,37 +748,12 @@ private void AppendMethod(XElement methodXml) EndBlock(); } - private static string GetArgumentWriteMethodName(Argument inArg) - { - return inArg.DBusType switch - { - DBusType.Byte => "WriteByte", - DBusType.Bool => "WriteBool", - DBusType.Int16 => "WriteInt16", - DBusType.UInt16 => "WriteUInt16", - DBusType.Int32 => "WriteInt32", - DBusType.UInt32 => "WriteUInt32", - DBusType.Int64 => "WriteInt64", - DBusType.UInt64 => "WriteUInt64", - DBusType.Double => "WriteDouble", - DBusType.String => "WriteString", - DBusType.ObjectPath => "WriteObjectPath", - DBusType.Signature => "WriteSignature", - DBusType.Array => "WriteArray", - DBusType.Struct => "WriteStruct", - DBusType.Variant => "WriteVariant", - DBusType.DictEntry => "WriteDictionary", - DBusType.UnixFd => "WriteHandle", - _ => throw new IndexOutOfRangeException("Unknown type") - }; - } - private void AppendReadMessageMethod(string name, bool variant, Argument[] args) { - string dotnetReturnType = args.Length == 0 ? null : args.Length == 1 ? args[0].DotnetType : TupleOf(args.Select(arg => arg.DotnetType)); + string dotnetReturnType = args.Length == 0 ? null : args.Length == 1 ? args[0].DotnetReadType : TupleOf(args.Select(arg => arg.DotnetReadType)); AppendLine($"protected static {dotnetReturnType} {name}(Message message, {_objectName} _)"); StartBlock(); - string signature = string.Join("", args.Select(a => a.Type)); + string signature = string.Join("", args.Select(a => a.Signature)); AppendLine("var reader = message.GetBodyReader();"); if (variant) { @@ -543,46 +761,311 @@ private void AppendReadMessageMethod(string name, bool variant, Argument[] args) } if (args.Length == 1) { - AppendLine($"return reader.{GetArgumentReadMethodName(args[0])}();"); + AppendLine($"return {CallReadArgumentType(args[0].Signature)};"); } else { for (int i = 0; i < args.Length; i++) { - Argument arg = args[i]; - string readMethod = GetArgumentReadMethodName(arg); - AppendLine($"var arg{i} = reader.{GetArgumentReadMethodName(args[i])}();"); + AppendLine($"var arg{i} = {CallReadArgumentType(args[i].Signature)};"); } AppendLine($"return {TupleOf(args.Select((a, i) => $"arg{i}"))};"); } EndBlock(); } - private static string GetArgumentReadMethodName(Argument arg) + private static string GetDBusTypeEnumValue(DBusType type) { - return arg.DBusType switch - { - DBusType.Byte => "ReadByte", - DBusType.Bool => "ReadBool", - DBusType.Int16 => "ReadInt16", - DBusType.UInt16 => "ReadUInt15", - DBusType.Int32 => "ReadInt32", - DBusType.UInt32 => "ReadUInt32", - DBusType.Int64 => "ReadInt64", - DBusType.UInt64 => "ReadUInt64", - DBusType.Double => "ReadDouble", - DBusType.String => "ReadString", - DBusType.ObjectPath => "ReadObjectPath", - DBusType.Signature => "ReadSignature", - DBusType.Array => $"ReadArray<{arg.DotnetInnerTypes[0]}>", - DBusType.Struct => $"ReadStruct<{string.Join(", ", arg.DotnetInnerTypes)}>", - DBusType.Variant => "ReadVariant", - DBusType.DictEntry => $"ReadDictionary<{arg.DotnetInnerTypes[0]}, {arg.DotnetInnerTypes[1]}>", - DBusType.UnixFd => "ReadHandle", - _ => throw new IndexOutOfRangeException("Unknown type") + return type switch + { + DBusType.Byte => $"{nameof(DBusType)}.{nameof(DBusType.Byte)}", + DBusType.Bool => $"{nameof(DBusType)}.{nameof(DBusType.Bool)}", + DBusType.Int16 => $"{nameof(DBusType)}.{nameof(DBusType.Int16)}", + DBusType.UInt16 => $"{nameof(DBusType)}.{nameof(DBusType.UInt16)}", + DBusType.Int32 => $"{nameof(DBusType)}.{nameof(DBusType.Int32)}", + DBusType.UInt32 => $"{nameof(DBusType)}.{nameof(DBusType.UInt32)}", + DBusType.Int64 => $"{nameof(DBusType)}.{nameof(DBusType.Int64)}", + DBusType.UInt64 => $"{nameof(DBusType)}.{nameof(DBusType.UInt64)}", + DBusType.Double => $"{nameof(DBusType)}.{nameof(DBusType.Double)}", + DBusType.String => $"{nameof(DBusType)}.{nameof(DBusType.String)}", + DBusType.ObjectPath => $"{nameof(DBusType)}.{nameof(DBusType.ObjectPath)}", + DBusType.Signature => $"{nameof(DBusType)}.{nameof(DBusType.Signature)}", + DBusType.Array => $"{nameof(DBusType)}.{nameof(DBusType.Array)}", + DBusType.Struct => $"{nameof(DBusType)}.{nameof(DBusType.Struct)}", + DBusType.Variant => $"{nameof(DBusType)}.{nameof(DBusType.Variant)}", + DBusType.DictEntry => $"{nameof(DBusType)}.{nameof(DBusType.DictEntry)}", + DBusType.UnixFd => $"{nameof(DBusType)}.{nameof(DBusType.UnixFd)}", + _ => throw new ArgumentOutOfRangeException(type.ToString()) }; } + private string CallWriteArgumentType(string signature, string parameterName) + { + switch (signature) + { + case "y": + return $"writer.{nameof(MessageWriter.WriteByte)}({parameterName})"; + case "b": + return $"writer.{nameof(MessageWriter.WriteBool)}({parameterName})"; + case "n": + return $"writer.{nameof(MessageWriter.WriteInt16)}({parameterName})"; + case "q": + return $"writer.{nameof(MessageWriter.WriteUInt16)}({parameterName})"; + case "i": + return $"writer.{nameof(MessageWriter.WriteInt32)}({parameterName})"; + case "u": + return $"writer.{nameof(MessageWriter.WriteUInt32)}({parameterName})"; + case "x": + return $"writer.{nameof(MessageWriter.WriteInt64)}({parameterName})"; + case "t": + return $"writer.{nameof(MessageWriter.WriteUInt64)}({parameterName})"; + case "d": + return $"writer.{nameof(MessageWriter.WriteDouble)}({parameterName})"; + case "s": + return $"writer.{nameof(MessageWriter.WriteString)}({parameterName})"; + case "o": + return $"writer.{nameof(MessageWriter.WriteObjectPath)}({parameterName})"; + case "g": + return $"writer.{nameof(MessageWriter.WriteSignature)}({parameterName})"; + case "v": + return $"writer.{nameof(MessageWriter.WriteVariant)}({parameterName})"; + case "h": + return $"writer.{nameof(MessageWriter.WriteHandle)}({parameterName})"; + + case "ay": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ab": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "an": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "aq": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ai": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "au": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ax": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "at": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ad": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "as": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ao": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ag": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "av": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + case "ah": + return $"writer.{nameof(MessageWriter.WriteArray)}({parameterName})"; + + case "a{sv}": + return $"writer.{nameof(MessageWriter.WriteDictionary)}({parameterName})"; + } + + return $"{GetWriteTypeMethodName(signature)}(ref writer, {parameterName})"; + } + + private string CallReadArgumentType(string signature) + { + switch (signature) + { + case "y": + return $"reader.{nameof(Reader.ReadByte)}()"; + case "b": + return $"reader.{nameof(Reader.ReadBool)}()"; + case "n": + return $"reader.{nameof(Reader.ReadInt16)}()"; + case "q": + return $"reader.{nameof(Reader.ReadUInt16)}()"; + case "i": + return $"reader.{nameof(Reader.ReadInt32)}()"; + case "u": + return $"reader.{nameof(Reader.ReadUInt32)}()"; + case "x": + return $"reader.{nameof(Reader.ReadInt64)}()"; + case "t": + return $"reader.{nameof(Reader.ReadUInt64)}()"; + case "d": + return $"reader.{nameof(Reader.ReadDouble)}()"; + case "s": + return $"reader.{nameof(Reader.ReadString)}()"; + case "o": + return $"reader.{nameof(Reader.ReadObjectPath)}()"; + case "g": + return $"reader.{nameof(Reader.ReadSignature)}()"; + case "v": + return $"reader.{nameof(Reader.ReadVariantValue)}()"; + case "h": + return $"reader.{nameof(Reader.ReadHandle)}<{typeof(SafeHandle).FullName}>()"; + + case "ay": + return $"reader.{nameof(Reader.ReadArrayOfByte)}()"; + case "ab": + return $"reader.{nameof(Reader.ReadArrayOfBool)}()"; + case "an": + return $"reader.{nameof(Reader.ReadArrayOfInt16)}()"; + case "aq": + return $"reader.{nameof(Reader.ReadArrayOfUInt16)}()"; + case "ai": + return $"reader.{nameof(Reader.ReadArrayOfInt32)}()"; + case "au": + return $"reader.{nameof(Reader.ReadArrayOfUInt32)}()"; + case "ax": + return $"reader.{nameof(Reader.ReadArrayOfInt64)}()"; + case "at": + return $"reader.{nameof(Reader.ReadArrayOfUInt64)}()"; + case "ad": + return $"reader.{nameof(Reader.ReadArrayOfDouble)}()"; + case "as": + return $"reader.{nameof(Reader.ReadArrayOfString)}()"; + case "ao": + return $"reader.{nameof(Reader.ReadArrayOfObjectPath)}()"; + case "ag": + return $"reader.{nameof(Reader.ReadArrayOfSignature)}()"; + case "av": + return $"reader.{nameof(Reader.ReadArrayOfVariantValue)}()"; + case "ah": + return $"reader.{nameof(Reader.ReadArrayOfHandle)}<{typeof(SafeHandle).FullName}>()"; + + case "a{sv}": + return $"reader.{nameof(Reader.ReadDictionaryOfStringToVariantValue)}()"; + + } + + return $"{GetReadTypeMethodName(signature)}(ref reader)"; + } + + private static string GetDotnetReadType(string signature) + => GetDotnetType(signature, true); + + private static string GetDotnetWriteType(string signature) + => GetDotnetType(signature, false); + + private static string GetDotnetType(string signature, bool readNotWrite) + { + SignatureReader reader = new SignatureReader(Encoding.UTF8.GetBytes(signature)); + if (!reader.TryRead(out DBusType type, out ReadOnlySpan innerSignature)) + { + ThrowInvalidSignature(signature); + } + return GetDotnetType(type, innerSignature, readNotWrite); + } + + private static string GetDotnetReadType(DBusType type, ReadOnlySpan innerSignature) + => GetDotnetType(type, innerSignature, true); + + private static string GetSignature(DBusType type, ReadOnlySpan innerSignature) + { + if (innerSignature.Length == 0) + { + return $"{(char)type}"; + } + else if (type == DBusType.Array) + { + return $"a{Encoding.UTF8.GetString(innerSignature)}"; + } + else if (type == DBusType.Struct) + { + return $"({Encoding.UTF8.GetString(innerSignature)})"; + } + else if (type == DBusType.DictEntry) + { + return "{" + Encoding.UTF8.GetString(innerSignature) + "}"; + } + else + { + throw new InvalidOperationException($"Cannot create signature for {type} and {Encoding.UTF8.GetString(innerSignature)}."); + } + } + + private static string GetDotnetType(DBusType type, ReadOnlySpan innerSignature, bool readNotWrite) + { + switch (type) + { + case DBusType.Byte: + return "byte"; + case DBusType.Bool: + return "bool"; + case DBusType.Int16: + return "short"; + case DBusType.UInt16: + return "ushort"; + case DBusType.Int32: + return "int"; + case DBusType.UInt32: + return "uint"; + case DBusType.Int64: + return "long"; + case DBusType.UInt64: + return "ulong"; + case DBusType.Double: + return "double"; + case DBusType.String: + return "string"; + case DBusType.ObjectPath: + return "ObjectPath"; + case DBusType.Signature: + return "Signature"; + case DBusType.Variant: + return readNotWrite ? "VariantValue" : "Variant"; + case DBusType.UnixFd: + return "SafeHandle"; + + case DBusType.Array: + { + SignatureReader reader = new SignatureReader(innerSignature); + if (!reader.TryRead(out DBusType itemtype, out ReadOnlySpan itemInnerSignature)) + { + ThrowInvalidSignature(GetSignature(type, innerSignature)); + } + bool isDictionary = itemtype == DBusType.DictEntry; + if (isDictionary) + { + reader = new SignatureReader(itemInnerSignature); + if (!reader.TryRead(out DBusType keyType, out ReadOnlySpan keyInnerSignature)) + { + ThrowInvalidSignature(GetSignature(type, innerSignature)); + } + if (!reader.TryRead(out DBusType valueType, out ReadOnlySpan valueInnerSignature)) + { + ThrowInvalidSignature(GetSignature(type, innerSignature)); + } + string dotnetKeyType = GetDotnetType(keyType, keyInnerSignature, readNotWrite); + string dotnetValueType = GetDotnetType(valueType, valueInnerSignature, readNotWrite); + return $"Dictionary<{dotnetKeyType}, {dotnetValueType}>"; + } + else + { + string itemType = GetDotnetType(itemtype, itemInnerSignature, readNotWrite); + return $"{itemType}[]"; + } + } + case DBusType.Struct: + { + SignatureReader reader = new SignatureReader(innerSignature); + StringBuilder sb = new(); + sb.Append("("); + bool first = true; + while (reader.TryRead(out DBusType fieldType, out ReadOnlySpan fieldInnerSignature)) + { + if (!first) + { + sb.Append(", "); + } + first = false; + sb.Append(GetDotnetType(fieldType, fieldInnerSignature, readNotWrite)); + } + sb.Append(")"); + return sb.ToString(); + } + } + + throw new InvalidOperationException($"Cannot determine .NET type for {type} and {Encoding.UTF8.GetString(innerSignature)}."); + } + private Argument ToArgument(XElement argXml, int i) { return new Argument(i, argXml); @@ -593,51 +1076,20 @@ class Argument public Argument(int i, XElement argXml) { Name = (string)argXml.Attribute("name") ?? $"a{i}"; - Type = (string)argXml.Attribute("type"); - (DotnetType, DotnetInnerTypes, DBusType) = DetermineType(Type); + Signature = (string)argXml.Attribute("type"); } public string Name { get; } public string NameUpper => Prettify(Name, startWithUpper: true); public string NameLower => Prettify(Name, startWithUpper: false); - public string Type { get; } - public string DotnetType { get; } - public string[] DotnetInnerTypes { get; } - public DBusType DBusType { get; } - - private (string, string[], DBusType) DetermineType(string signature) - { - DBusType dbusType = (DBusType)signature[0]; - - Func map = (dbusType, inner) => - { - string[] innerTypes = inner.Select(s => s.Item1).ToArray(); - switch (dbusType) - { - case DBusType.Byte: return ("byte", innerTypes, dbusType); - case DBusType.Bool: return ("bool", innerTypes, dbusType); - case DBusType.Int16: return ("short", innerTypes, dbusType); - case DBusType.UInt16: return ("ushort", innerTypes, dbusType); - case DBusType.Int32: return ("int", innerTypes, dbusType); - case DBusType.UInt32: return ("uint", innerTypes, dbusType); - case DBusType.Int64: return ("long", innerTypes, dbusType); - case DBusType.UInt64: return ("ulong", innerTypes, dbusType); - case DBusType.Double: return ("double", innerTypes, dbusType); - case DBusType.String: return ("string", innerTypes, dbusType); - case DBusType.ObjectPath: return ("ObjectPath", innerTypes, dbusType); - case DBusType.Signature: return ("Signature", innerTypes, dbusType); - case DBusType.Variant: return ("object", innerTypes, dbusType); - case DBusType.UnixFd: return ("SafeHandle", innerTypes, dbusType); - case DBusType.Array: return ($"{innerTypes[0]}[]", innerTypes, dbusType); - case DBusType.DictEntry: return ($"Dictionary<{innerTypes[0]}, {innerTypes[1]}>", innerTypes, dbusType); - case DBusType.Struct: return ($"({string.Join(", ", innerTypes)})", innerTypes, dbusType); - } - throw new IndexOutOfRangeException($"Invalid type {dbusType}"); - }; - (string dotnetType, string[] dotnetInnerTypes, DBusType dbusType2) = Tmds.DBus.Protocol.SignatureReader.Transform(Encoding.ASCII.GetBytes(signature), map); + public string Signature { get; } + public string DotnetReadType => GetDotnetReadType(Signature); + public string DotnetWriteType => GetDotnetWriteType(Signature); + } - return (dotnetType, dotnetInnerTypes, dbusType2); - } + private static void ThrowInvalidSignature(string signature) + { + throw new InvalidOperationException($"Invalid signature: {signature}"); } private static string Prettify(string name, bool startWithUpper = true)