Skip to content

Commit

Permalink
Use XDG Desktop Portal to open URIs and files
Browse files Browse the repository at this point in the history
Resolves Nexus-Mods#1676 and related to Nexus-Mods#2436.
  • Loading branch information
erri120 committed Jan 9, 2025
1 parent afe1167 commit 3066152
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 2 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageVersion Include="Bannerlord.ModuleManager" Version="6.0.246" />
<PackageVersion Include="BsDiff" Version="1.1.0" />
<PackageVersion Include="LinqGen" Version="0.3.1" />
<PackageVersion Include="LinuxDesktopUtils.XDGDesktopPortal" Version="1.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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<DesktopPortalConnectionManager> InitAsync()
{
await _semaphoreSlim.WaitAsync(timeout: TimeSpan.FromSeconds(10));

var manager = await DesktopPortalConnectionManager.ConnectAsync();
_instance = manager;

return manager;
}

public async ValueTask<DesktopPortalConnectionManager> GetInstance()
{
if (_instance is not null) return _instance;
return await InitAsync();
}

public async ValueTask DisposeAsync()
{
if (_instance is not null) await _instance.DisposeAsync();
}
}
1 change: 1 addition & 0 deletions src/NexusMods.CrossPlatform/NexusMods.CrossPlatform.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<ItemGroup>
<PackageReference Include="CliWrap" />
<PackageReference Include="LinuxDesktopUtils.XDGDesktopPortal" />
<PackageReference Include="NexusMods.Paths" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
Expand Down
4 changes: 2 additions & 2 deletions src/NexusMods.CrossPlatform/Process/AOSInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected AOSInterop(ILoggerFactory loggerFactory, IProcessFactory processFactor
protected abstract Command CreateCommand(Uri uri);

/// <inheritdoc/>
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);

Expand All @@ -55,7 +55,7 @@ public async Task OpenUrl(Uri url, bool logOutput = false, bool fireAndForget =
}

/// <inheritdoc />
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)
{
Expand Down
25 changes: 25 additions & 0 deletions src/NexusMods.CrossPlatform/Process/OSInteropLinux.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.Versioning;
using CliWrap;
using LinuxDesktopUtils.XDGDesktopPortal;
using Microsoft.Extensions.Logging;
using NexusMods.Paths;

Expand All @@ -13,18 +14,42 @@ internal class OSInteropLinux : AOSInterop
{
private readonly IFileSystem _fileSystem;
private readonly XDGOpenDependency _xdgOpenDependency;
private readonly DesktopPortalConnectionManagerWrapper _portalWrapper;
private readonly ILogger _logger;

/// <summary>
/// Constructor.
/// </summary>
public OSInteropLinux(
ILoggerFactory loggerFactory,
DesktopPortalConnectionManagerWrapper portalWrapper,
IProcessFactory processFactory,
IFileSystem fileSystem,
XDGOpenDependency xdgOpenDependency) : base(loggerFactory, processFactory)
{
_fileSystem = fileSystem;
_xdgOpenDependency = xdgOpenDependency;
_portalWrapper = portalWrapper;
_logger = loggerFactory.CreateLogger<OSInteropLinux>();
}

/// <inheritdoc/>
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);
}

/// <inheritdoc/>
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
);
}

/// <inheritdoc/>
Expand Down
3 changes: 3 additions & 0 deletions src/NexusMods.CrossPlatform/Services.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using LinuxDesktopUtils.XDGDesktopPortal;
using Microsoft.Extensions.DependencyInjection;
using NexusMods.CrossPlatform.Process;
using NexusMods.CrossPlatform.ProtocolRegistration;
Expand Down Expand Up @@ -41,6 +42,8 @@ public static IServiceCollection AddCrossPlatform(this IServiceCollection servic

if (OSInformation.Shared.IsLinux)
{
services.AddSingleton<DesktopPortalConnectionManagerWrapper>();

// General Components
services.AddSingleton<XDGOpenDependency>();
services.AddSingleton<IRuntimeDependency, XDGOpenDependency>();
Expand Down

0 comments on commit 3066152

Please sign in to comment.