Skip to content

Instantly share code, notes, and snippets.

@lostmsu
Created December 11, 2024 22:22
Show Gist options
  • Save lostmsu/bad96cfeba5c72d5f520f3434e91e3e5 to your computer and use it in GitHub Desktop.
Save lostmsu/bad96cfeba5c72d5f520f3434e91e3e5 to your computer and use it in GitHub Desktop.
Using Tmds.DBus with org.freedesktop.portal
using Tmds.DBus.Protocol;
/* USE:
var response = await connection.Call(
() => screenCast.CreateSessionAsync(
new Dictionary<string, VariantValue> {
{ "handle_token", "s0" },
{ "session_handle_token", "s1" },
}), log).ConfigureAwait(false);
*/
class PortalResponse {
public required string RequestPath { get; init; }
public required Dictionary<string, VariantValue> Results { get; init; }
public static async Task<PortalResponse> WaitAsync(Connection connection,
Func<Task<ObjectPath>> request,
ILogger log,
CancellationToken cancel = default) {
ArgumentNullException.ThrowIfNull(connection);
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(log);
var results = new List<PortalResponse>();
var result = new TaskCompletionSource<PortalResponse>();
string? requestPath = null;
using var matcher = await connection.AddMatchAsync(
new() {
Type = MessageType.Signal,
Member = "Response",
Interface = "org.freedesktop.portal.Request",
},
reader: (message, state) => {
var reader = message.GetBodyReader();
uint arg0 = reader.ReadUInt32();
return new PortalResponse {
RequestPath = message.PathAsString,
Results = reader.ReadDictionaryOfStringToVariantValue(),
};
},
handler: (ex, response, _, __) => {
log.LogDebug("Got {Response}", response);
if (ex is null) {
lock (results) {
if (requestPath != null) {
if (response.RequestPath != requestPath)
return;
result.SetResult(response);
return;
}
results.Add(response);
}
} else {
result.SetException(ex);
}
},
ObserverFlags.NoSubscribe, emitOnCapturedContext: false).ConfigureAwait(false);
var path = await request().WaitAsync(cancel).ConfigureAwait(false);
lock (results) {
requestPath = path;
if (results.Find(r => r.RequestPath == path) is { } response)
return response;
}
return await result.Task.WaitAsync(cancel).ConfigureAwait(false);
}
public override string ToString() {
var result = new System.Text.StringBuilder();
result.Append(this.RequestPath.AfterLast('/'));
result.Append("{ ");
foreach (var (key, value) in this.Results) {
result.Append(key);
result.Append('=');
result.Append(value);
result.AppendLine(",");
}
result.Append('}');
return result.ToString();
}
}
static class ConnectionExtensions {
public static Task<PortalResponse> Call(this Connection connection,
Func<Task<ObjectPath>> request,
ILogger log,
CancellationToken cancel = default)
=> PortalResponse.WaitAsync(connection, request, log, cancel);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment