From 3066152052cedeebf34f07b62c9e23f93deb8131 Mon Sep 17 00:00:00 2001 From: erri120 Date: Thu, 9 Jan 2025 14:30:32 +0100 Subject: [PATCH] Use XDG Desktop Portal to open URIs and files Resolves #1676 and related to #2436. --- Directory.Packages.props | 1 + .../DesktopPortalConnectionManagerWrapper.cs | 30 +++++++++++++++++++ .../NexusMods.CrossPlatform.csproj | 1 + .../Process/AOSInterop.cs | 4 +-- .../Process/OSInteropLinux.cs | 25 ++++++++++++++++ src/NexusMods.CrossPlatform/Services.cs | 3 ++ 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/NexusMods.CrossPlatform/DesktopPortalConnectionManagerWrapper.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 7584d84122..d12283f122 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,6 +8,7 @@ + diff --git a/src/NexusMods.CrossPlatform/DesktopPortalConnectionManagerWrapper.cs b/src/NexusMods.CrossPlatform/DesktopPortalConnectionManagerWrapper.cs new file mode 100644 index 0000000000..e65222d2ba --- /dev/null +++ b/src/NexusMods.CrossPlatform/DesktopPortalConnectionManagerWrapper.cs @@ -0,0 +1,30 @@ +using LinuxDesktopUtils.XDGDesktopPortal; + +namespace NexusMods.CrossPlatform; + +internal sealed class DesktopPortalConnectionManagerWrapper : IAsyncDisposable +{ + private readonly SemaphoreSlim _semaphoreSlim = new(initialCount: 1, maxCount: 1); + private DesktopPortalConnectionManager? _instance; + + private async ValueTask InitAsync() + { + await _semaphoreSlim.WaitAsync(timeout: TimeSpan.FromSeconds(10)); + + var manager = await DesktopPortalConnectionManager.ConnectAsync(); + _instance = manager; + + return manager; + } + + public async ValueTask GetInstance() + { + if (_instance is not null) return _instance; + return await InitAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_instance is not null) await _instance.DisposeAsync(); + } +} diff --git a/src/NexusMods.CrossPlatform/NexusMods.CrossPlatform.csproj b/src/NexusMods.CrossPlatform/NexusMods.CrossPlatform.csproj index ce692ad4bf..066e61c6ba 100644 --- a/src/NexusMods.CrossPlatform/NexusMods.CrossPlatform.csproj +++ b/src/NexusMods.CrossPlatform/NexusMods.CrossPlatform.csproj @@ -4,6 +4,7 @@ + diff --git a/src/NexusMods.CrossPlatform/Process/AOSInterop.cs b/src/NexusMods.CrossPlatform/Process/AOSInterop.cs index 54f7beb129..794a5a3686 100644 --- a/src/NexusMods.CrossPlatform/Process/AOSInterop.cs +++ b/src/NexusMods.CrossPlatform/Process/AOSInterop.cs @@ -30,7 +30,7 @@ protected AOSInterop(ILoggerFactory loggerFactory, IProcessFactory processFactor protected abstract Command CreateCommand(Uri uri); /// - public async Task OpenUrl(Uri url, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) + public virtual async Task OpenUrl(Uri url, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) { var command = CreateCommand(url); @@ -55,7 +55,7 @@ public async Task OpenUrl(Uri url, bool logOutput = false, bool fireAndForget = } /// - public Task OpenFile(AbsolutePath filePath, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) + public virtual Task OpenFile(AbsolutePath filePath, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) { if (!filePath.FileExists) { diff --git a/src/NexusMods.CrossPlatform/Process/OSInteropLinux.cs b/src/NexusMods.CrossPlatform/Process/OSInteropLinux.cs index b7324064df..05b9fd6e1d 100644 --- a/src/NexusMods.CrossPlatform/Process/OSInteropLinux.cs +++ b/src/NexusMods.CrossPlatform/Process/OSInteropLinux.cs @@ -1,5 +1,6 @@ using System.Runtime.Versioning; using CliWrap; +using LinuxDesktopUtils.XDGDesktopPortal; using Microsoft.Extensions.Logging; using NexusMods.Paths; @@ -13,18 +14,42 @@ internal class OSInteropLinux : AOSInterop { private readonly IFileSystem _fileSystem; private readonly XDGOpenDependency _xdgOpenDependency; + private readonly DesktopPortalConnectionManagerWrapper _portalWrapper; + private readonly ILogger _logger; /// /// Constructor. /// public OSInteropLinux( ILoggerFactory loggerFactory, + DesktopPortalConnectionManagerWrapper portalWrapper, IProcessFactory processFactory, IFileSystem fileSystem, XDGOpenDependency xdgOpenDependency) : base(loggerFactory, processFactory) { _fileSystem = fileSystem; _xdgOpenDependency = xdgOpenDependency; + _portalWrapper = portalWrapper; + _logger = loggerFactory.CreateLogger(); + } + + /// + public override async Task OpenUrl(Uri url, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) + { + var connectionManager = await _portalWrapper.GetInstance(); + var portal = await connectionManager.GetOpenUriPortalAsync(); + await portal.OpenUriAsync(url, cancellationToken: cancellationToken); + } + + /// + public override async Task OpenFile(AbsolutePath filePath, bool logOutput = false, bool fireAndForget = false, CancellationToken cancellationToken = default) + { + var connectionManager = await _portalWrapper.GetInstance(); + var portal = await connectionManager.GetOpenUriPortalAsync(); + await portal.OpenFileInDirectoryAsync( + file: FilePath.From(filePath.ToNativeSeparators(_fileSystem.OS)), + cancellationToken: cancellationToken + ); } /// diff --git a/src/NexusMods.CrossPlatform/Services.cs b/src/NexusMods.CrossPlatform/Services.cs index 27b79b77d5..7aab2b7f47 100644 --- a/src/NexusMods.CrossPlatform/Services.cs +++ b/src/NexusMods.CrossPlatform/Services.cs @@ -1,3 +1,4 @@ +using LinuxDesktopUtils.XDGDesktopPortal; using Microsoft.Extensions.DependencyInjection; using NexusMods.CrossPlatform.Process; using NexusMods.CrossPlatform.ProtocolRegistration; @@ -41,6 +42,8 @@ public static IServiceCollection AddCrossPlatform(this IServiceCollection servic if (OSInformation.Shared.IsLinux) { + services.AddSingleton(); + // General Components services.AddSingleton(); services.AddSingleton();