Merge branch 'fix/linux-file-picker' into 'master'

Linux: Fix file picker not launching from disabling core dumps

See merge request [ryubing/ryujinx!249](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/249)
This commit is contained in:
KeatonTheBot 2026-01-30 20:44:15 -06:00
commit 752b17e4c2
9 changed files with 124 additions and 2 deletions

View File

@ -22,10 +22,11 @@ namespace Ryujinx.Common.Utilities
} }
// "dumpable" attribute of the calling process // "dumpable" attribute of the calling process
private const int PR_GET_DUMPABLE = 3;
private const int PR_SET_DUMPABLE = 4; private const int PR_SET_DUMPABLE = 4;
[DllImport("libc", SetLastError = true)] [LibraryImport("libc", SetLastError = true)]
private static extern int prctl(int option, int arg2); private static partial int prctl(int option, int arg2);
public static void SetCoreDumpable(bool dumpable) public static void SetCoreDumpable(bool dumpable)
{ {
@ -36,5 +37,13 @@ namespace Ryujinx.Common.Utilities
Debug.Assert(result == 0); Debug.Assert(result == 0);
} }
} }
// Use the below line to display dumpable status in the console:
// Console.WriteLine($"{OsUtils.IsCoreDumpable()}");
public static bool IsCoreDumpable()
{
int result = prctl(PR_GET_DUMPABLE, 0);
return result == 1;
}
} }
} }

View File

@ -18,6 +18,7 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
@ -410,6 +411,8 @@ namespace Ryujinx.Ava.Common
public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName) public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
{ {
OsUtils.SetCoreDumpable(true);
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
@ -419,10 +422,17 @@ namespace Ryujinx.Ava.Common
return; return;
ExtractAoc(result.Value.Path.LocalPath, updateFilePath, updateName); ExtractAoc(result.Value.Path.LocalPath, updateFilePath, updateName);
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0) public static async Task ExtractSection(IStorageProvider storageProvider, NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
{ {
OsUtils.SetCoreDumpable(true);
Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions Gommon.Optional<IStorageFolder> result = await storageProvider.OpenSingleFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
@ -432,6 +442,11 @@ namespace Ryujinx.Ava.Common
return; return;
ExtractSection(result.Value.Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex); ExtractSection(result.Value.Path.LocalPath, ncaSectionType, titleFilePath, titleName, programIndex);
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token) public static (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath, CancellationToken token)

View File

@ -42,6 +42,7 @@ namespace Ryujinx.Ava
public static bool PreviewerDetached { get; private set; } public static bool PreviewerDetached { get; private set; }
public static bool UseHardwareAcceleration { get; private set; } public static bool UseHardwareAcceleration { get; private set; }
public static string BackendThreadingArg { get; private set; } public static string BackendThreadingArg { get; private set; }
public static bool CoreDumpArg { get; private set; }
private const uint MbIconwarning = 0x30; private const uint MbIconwarning = 0x30;
@ -81,6 +82,8 @@ namespace Ryujinx.Ava
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui"); bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps"); bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
CoreDumpArg = coreDumpArg;
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception. // TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
// This is undesirable and causes very odd behavior during development (the process stops responding, // This is undesirable and causes very odd behavior during development (the process stops responding,
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user. // the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.

View File

@ -8,6 +8,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
@ -128,6 +129,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add() public async void Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
@ -158,6 +161,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await ShowNewDlcAddedDialog(totalDlcAdded); await ShowNewDlcAddedDialog(totalDlcAdded);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private bool AddDownloadableContent(string path, out int numDlcAdded) private bool AddDownloadableContent(string path, out int numDlcAdded)

View File

@ -1266,6 +1266,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey,
LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle) LoadContentFromFolderDelegate onDirsSelected, LocaleKeys dirSelectDialogTitle)
{ {
OsUtils.SetCoreDumpable(true);
Optional<IReadOnlyList<IStorageFolder>> result = Optional<IReadOnlyList<IStorageFolder>> result =
await StorageProvider.OpenMultiFolderPickerAsync( await StorageProvider.OpenMultiFolderPickerAsync(
new FolderPickerOpenOptions { Title = LocaleManager.Instance[dirSelectDialogTitle] }); new FolderPickerOpenOptions { Title = LocaleManager.Instance[dirSelectDialogTitle] });
@ -1293,6 +1295,11 @@ namespace Ryujinx.Ava.UI.ViewModels
(int)Symbol.Checkmark); (int)Symbol.Checkmark);
}); });
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
#endregion #endregion
@ -1355,6 +1362,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task InstallFirmwareFromFile() public async Task InstallFirmwareFromFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
@ -1384,20 +1393,34 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await HandleFirmwareInstallation(result.Value.Path.LocalPath); await HandleFirmwareInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallFirmwareFromFolder() public async Task InstallFirmwareFromFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
if (result.HasValue) if (result.HasValue)
{ {
await HandleFirmwareInstallation(result.Value.Path.LocalPath); await HandleFirmwareInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallKeysFromFile() public async Task InstallKeysFromFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>
@ -1415,16 +1438,28 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await HandleKeysInstallation(result.Value.Path.LocalPath); await HandleKeysInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task InstallKeysFromFolder() public async Task InstallKeysFromFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(); Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync();
if (result.HasValue) if (result.HasValue)
{ {
await HandleKeysInstallation(result.Value.Path.LocalPath); await HandleKeysInstallation(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void OpenRyujinxFolder() public void OpenRyujinxFolder()
@ -1528,6 +1563,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenFile() public async Task OpenFile()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(new FilePickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle], Title = LocaleManager.Instance[LocaleKeys.LoadApplicationFromFileDialogTitle],
@ -1599,6 +1636,11 @@ namespace Ryujinx.Ava.UI.ViewModels
LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]); LocaleManager.Instance[LocaleKeys.MenuBarFileOpenFromFileError]);
} }
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public async Task LoadDlcFromFolder() public async Task LoadDlcFromFolder()
@ -1621,6 +1663,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenFolder() public async Task OpenFolder()
{ {
OsUtils.SetCoreDumpable(true);
Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync( Optional<IStorageFolder> result = await StorageProvider.OpenSingleFolderPickerAsync(
new FolderPickerOpenOptions new FolderPickerOpenOptions
{ {
@ -1636,6 +1680,11 @@ namespace Ryujinx.Ava.UI.ViewModels
await LoadApplication(applicationData); await LoadApplication(applicationData);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public static bool InitializeUserConfig(ApplicationData application) public static bool InitializeUserConfig(ApplicationData application)
@ -1843,6 +1892,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task OpenBinFile() public async Task OpenBinFile()
{ {
OsUtils.SetCoreDumpable(true);
if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning) if (AppHost.Device.System.SearchingForAmiibo(out _) && IsGameRunning)
{ {
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync( Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(
@ -1862,6 +1913,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath); AppHost.Device.System.ScanAmiiboFromBin(result.Value.Path.LocalPath);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
} }

View File

@ -288,6 +288,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add() public async void Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFolder> result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions IReadOnlyList<IStorageFolder> result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle],
@ -298,6 +300,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
AddMod(new DirectoryInfo(folder.Path.LocalPath)); AddMod(new DirectoryInfo(folder.Path.LocalPath));
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void DeleteAll() public void DeleteAll()

View File

@ -7,6 +7,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.AppLibrary; using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -148,6 +149,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task Add() public async Task Add()
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = true, AllowMultiple = true,
@ -177,6 +180,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
await ShowNewUpdatesAddedDialog(totalUpdatesAdded); await ShowNewUpdatesAddedDialog(totalUpdatesAdded);
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
public void Save() public void Save()

View File

@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Common.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -27,6 +28,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
private async Task AddDirButton(TextBox addDirBox, AvaloniaList<string> directories) private async Task AddDirButton(TextBox addDirBox, AvaloniaList<string> directories)
{ {
OsUtils.SetCoreDumpable(true);
string path = addDirBox.Text; string path = addDirBox.Text;
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path)) if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path))
@ -48,6 +51,11 @@ namespace Ryujinx.Ava.UI.Views.Settings
ViewModel.GameListNeedsRefresh = true; ViewModel.GameListNeedsRefresh = true;
} }
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e) private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e)

View File

@ -8,6 +8,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using SkiaSharp; using SkiaSharp;
using System.Collections.Generic; using System.Collections.Generic;
@ -62,6 +63,8 @@ namespace Ryujinx.Ava.UI.Views.User
private async void Import_OnClick(object sender, RoutedEventArgs e) private async void Import_OnClick(object sender, RoutedEventArgs e)
{ {
OsUtils.SetCoreDumpable(true);
IReadOnlyList<IStorageFile> result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions IReadOnlyList<IStorageFile> result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = false, AllowMultiple = false,
@ -81,6 +84,11 @@ namespace Ryujinx.Ava.UI.Views.User
_profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath)); _profile.Image = ProcessProfileImage(File.ReadAllBytes(result[0].Path.LocalPath));
_parent.GoBack(); _parent.GoBack();
} }
if (!Program.CoreDumpArg)
{
OsUtils.SetCoreDumpable(false);
}
} }
private void GoBack(object sender, RoutedEventArgs e) private void GoBack(object sender, RoutedEventArgs e)