Compare commits

..

1 Commits

Author SHA1 Message Date
Keaton
7664f8cde9 update FFmpeg to 6.1.2 & Windows on ARM support (#702)
As stated in the title, win-arm64 (Windows 11 ARM) has been added to the
workflows, so these builds should automatically compile for this PR and
all other releases going forward.

Also updated the FFmpeg runtimes from 5.0.3 to 6.1.2. macOS (x64/arm64)
is _currently_ excluded from the update until a proper cross-compiling
environment can be set up for these architectures.

Windows 11 ARM users, please test the win-arm64 build for any issues.

---------

Co-authored-by: Evan Husted <greem@greemdev.net>
2025-03-13 18:36:57 -05:00
20 changed files with 108 additions and 522 deletions

View File

@@ -19,6 +19,7 @@ jobs:
configuration: [Debug, Release]
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }

View File

@@ -62,6 +62,7 @@ jobs:
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
@@ -79,6 +80,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:

View File

@@ -66,6 +66,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -117,6 +118,7 @@ jobs:
if: matrix.platform.os == 'ubuntu-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
popd

View File

@@ -39,7 +39,7 @@
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
@@ -53,8 +53,8 @@
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
<PackageVersion Include="System.Management" Version="9.0.0" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64</RuntimeIdentifiers>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup>
@@ -11,15 +11,15 @@
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dylib</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.so</TargetPath>
</ContentWithTargetPath>

View File

@@ -21,11 +21,6 @@ namespace Ryujinx.Common.Configuration.Hid
/// </summary>
public string Id { get; set; }
/// <summary>
/// Controller name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Controller's Type
/// </summary>

View File

@@ -12,8 +12,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new()
{
{ AvCodecLibraryName, (58, 59) },
{ AvUtilLibraryName, (56, 57) },
{ AvCodecLibraryName, (58, 61) },
{ AvUtilLibraryName, (56, 59) },
};
private static string FormatLibraryNameForCurrentOs(string libraryName, int version)

View File

@@ -49,6 +49,7 @@
<TextBlock
Classes="globalConfigMarker"/>
</StackPanel>
</Border>
</Design.PreviewWith>
<Style Selector="DropDownButton">

View File

@@ -6922,31 +6922,6 @@
"zh_TW": "輸入裝置"
}
},
{
"ID": "ControllerSettingsWaitingConnectDevice",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Configuration found:\n\nName:\t{0}\nGUID:\t{1}\n\n Waiting for controller connection...",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "ControllerSettingsRefresh",
"Translations": {
@@ -7247,81 +7222,6 @@
"zh_TW": "新增"
}
},
{
"ID": "ControllerSettingsModifiedNotification",
"Translations": {
"ar_SA": "(تم التعديل!)",
"de_DE": "(modifiziert!)",
"el_GR": "(τροποποιημένο!)",
"en_US": "(Modified!)",
"es_ES": "(modificado!)",
"fr_FR": "(modifié!)",
"he_IL": "(שונה!)",
"it_IT": "(modificato!)",
"ja_JP": "(変更済み!)",
"ko_KR": "(수정됨!)",
"no_NO": "(modifisert!)",
"pl_PL": "(zmodyfikowane!)",
"pt_BR": "(modificado!)",
"ru_RU": "(изменено!)",
"sv_SE": "(ändrad!)",
"th_TH": "(แก้ไขแล้ว!)",
"tr_TR": "(değiştirildi!)",
"uk_UA": "(модифіковано!)",
"zh_CN": "(已修改!)",
"zh_TW": "(已修改!)"
}
},
{
"ID": "ControllerSettingsDisableDeviceForSaving",
"Translations": {
"ar_SA": "تم إعداد التحكم.\n\nفي انتظار اتصال وحدة التحكم...",
"de_DE": "Steuerung konfiguriert.\n\nWarten auf die Verbindung des Controllers...",
"el_GR": "Η διαχείριση έχει ρυθμιστεί.\n\nΑναμένεται σύνδεση του χειριστηρίου...",
"en_US": "Control configured.\n\nWaiting for controller connection...",
"es_ES": "Control configurado.\n\nEsperando la conexión del controlador...",
"fr_FR": "Contrôle configuré.\n\nEn attente de la connexion du contrôleur...",
"he_IL": "השליטה הוגדרה.\n\nממתין לחיבור הבקר...",
"it_IT": "Controllo configurato.\n\nIn attesa della connessione del controller...",
"ja_JP": "コントロールが設定されました。\n\nコントローラーの接続を待っています...",
"ko_KR": "제어가 설정되었습니다.\n\n컨트롤러 연결 대기 중...",
"no_NO": "Kontroll konfigurert.\n\nVenter på tilkobling av kontroller...",
"pl_PL": "Sterowanie skonfigurowane.\n\nOczekiwanie na połączenie kontrolera...",
"pt_BR": "Controle configurado.\n\nAguardando conexão do controle...",
"ru_RU": "Управление настроено.\n\nОжидается подключение контроллера...",
"sv_SE": "Kontroll konfigurerad.\n\nVäntar på anslutning av kontrollen...",
"th_TH": "การควบคุมได้รับการตั้งค่าแล้ว\n\nกำลังรอการเชื่อมต่อคอนโทรลเลอร์...",
"tr_TR": "Kontrol yapılandırıldı.\n\nKontrolcü bağlantısı bekleniyor...",
"uk_UA": "Керування налаштовано.\n\nОчікується підключення контролера...",
"zh_CN": "控制已配置。\n\n等待控制器连接...",
"zh_TW": "控制已設定。\n\n等待控制器連接..."
}
},
{
"ID": "ControllerSettingsUnlink",
"Translations": {
"ar_SA": "إلغاء الربط",
"de_DE": "Entkoppeln",
"el_GR": "Αποσύνδεση",
"en_US": "Unlink",
"es_ES": "Desvincular",
"fr_FR": "Dissocier",
"he_IL": "ניתוק קישור",
"it_IT": "Scollega",
"ja_JP": "リンク解除",
"ko_KR": "연결 해제",
"no_NO": "Frakoble",
"pl_PL": "Odłącz",
"pt_BR": "Desvincular",
"ru_RU": "Отвязать",
"sv_SE": "Koppla från",
"th_TH": "ยกเลิกการเชื่อมโยง",
"tr_TR": "Bağlantıyı Kes",
"uk_UA": "Відв'язати",
"zh_CN": "解除绑定",
"zh_TW": "解除綁定"
}
},
{
"ID": "ControllerSettingsRemove",
"Translations": {
@@ -11972,31 +11872,6 @@
"zh_TW": "儲存設定檔"
}
},
{
"ID": "ControllerSettingsCancelCurrentChangesToolTip",
"Translations": {
"ar_SA": "إلغاء التغييرات الحالية",
"de_DE": "Aktuelle Änderungen abbrechen",
"el_GR": "Ακύρωση τρεχουσών αλλαγών",
"en_US": "Cancel current changes",
"es_ES": "Cancelar los cambios actuales",
"fr_FR": "Annuler les modifications en cours",
"he_IL": "ביטול השינויים הנוכחיים",
"it_IT": "Annulla le modifiche correnti",
"ja_JP": "現在の変更をキャンセル",
"ko_KR": "현재 변경 취소",
"no_NO": "Avbryt gjeldende endringer",
"pl_PL": "Anuluj bieżące zmiany",
"pt_BR": "Cancelar alterações atuais",
"ru_RU": "Отменить текущие изменения",
"sv_SE": "Avbryt aktuella ändringar",
"th_TH": "ยกเลิกการเปลี่ยนแปลงปัจจุบัน",
"tr_TR": "Geçerli değişiklikleri iptal et",
"uk_UA": "Скасувати поточні зміни",
"zh_CN": "取消当前更改",
"zh_TW": "取消當前變更"
}
},
{
"ID": "MenuBarFileToolsTakeScreenshot",
"Translations": {
@@ -24498,4 +24373,4 @@
}
}
]
}
}

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64;</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>
@@ -29,12 +29,18 @@
<TrimMode>partial</TrimMode>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<!--
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
See:
https://github.com/amwx/FluentAvalonia/issues/481
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
-->
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
@@ -57,8 +63,8 @@
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="securifybv.ShellLink" />
<PackageReference Include="Sep" />
<PackageReference Include="Silk.NET.Vulkan" />
@@ -67,7 +73,7 @@
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
@@ -84,7 +90,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>alsoft.ini</TargetPath>
</Content>

View File

@@ -269,7 +269,6 @@ namespace Ryujinx.Ava.Systems.Configuration
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = "0",
Name = "Keyboard",
PlayerIndex = PlayerIndex.Player1,
ControllerType = ControllerType.ProController,
LeftJoycon = new LeftJoyconCommonConfig<Key>

View File

@@ -22,8 +22,6 @@ namespace Ryujinx.Ava.UI.Models.Input
public float StrongRumble { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public ControllerType ControllerType { get; set; }
public PlayerIndex PlayerIndex { get; set; }
@@ -113,7 +111,6 @@ namespace Ryujinx.Ava.UI.Models.Input
if (config != null)
{
Id = config.Id;
Name = config.Name;
ControllerType = config.ControllerType;
PlayerIndex = config.PlayerIndex;
@@ -204,7 +201,6 @@ namespace Ryujinx.Ava.UI.Models.Input
StandardControllerInputConfig config = new()
{
Id = Id,
Name = Name,
Backend = InputBackendType.GamepadSDL2,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,

View File

@@ -2,14 +2,12 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using System.Xml.Linq;
namespace Ryujinx.Ava.UI.Models.Input
{
public partial class KeyboardInputConfig : BaseModel
{
public string Id { get; set; }
public string Name { get; set; }
public ControllerType ControllerType { get; set; }
public PlayerIndex PlayerIndex { get; set; }
@@ -55,7 +53,6 @@ namespace Ryujinx.Ava.UI.Models.Input
if (config != null)
{
Id = config.Id;
Name = config.Name;
ControllerType = config.ControllerType;
PlayerIndex = config.PlayerIndex;
@@ -103,7 +100,6 @@ namespace Ryujinx.Ava.UI.Models.Input
StandardKeyboardInputConfig config = new()
{
Id = Id,
Name = Name,
Backend = InputBackendType.WindowKeyboard,
PlayerIndex = PlayerIndex,
ControllerType = ControllerType,

View File

@@ -91,21 +91,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
public async void ShowMotionConfig()
{
{
await MotionInputView.Show(this);
ParentModel.IsModified = true;
}
public async void ShowRumbleConfig()
{
{
await RumbleInputView.Show(this);
ParentModel.IsModified = true;
}
public async void ShowLedConfig()
{
await LedInputView.Show(this);
ParentModel.IsModified = true;
}
public void OnParentModelChanged()

View File

@@ -51,8 +51,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private int _device;
private object _configViewModel;
[ObservableProperty] private string _profileName;
[ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged()
[ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged()
private bool _isLoaded;
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -90,40 +88,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsKeyboard => !IsController;
public bool IsRight { get; set; }
public bool IsLeft { get; set; }
public string RevertDeviceId { get; set; }
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
public bool _isChangeTrackingActive;
public bool _isModified;
public bool IsModified
{
get => _isModified;
set
{
_isModified = value;
OnPropertyChanged();
}
}
public bool IsModified { get; set; }
public event Action NotifyChangesEvent;
public string _profileChoose;
public string ProfileChoose
{
get => _profileChoose;
set
{
// When you select a profile, the settings from the profile will be applied.
// To save the settings, you still need to click the apply button
_profileChoose = value;
LoadProfile();
OnPropertyChanged();
}
}
public object ConfigViewModel
{
get => _configViewModel;
@@ -149,14 +120,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
set
{
if (IsModified)
{
{
_playerIdChoose = value;
return;
}
IsModified = false;
_playerId = value;
_isChangeTrackingActive = false;
if (!Enum.IsDefined<PlayerIndex>(_playerId))
{
@@ -164,13 +135,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
_isLoaded = false;
LoadConfiguration();
LoadDevice();
LoadProfiles();
RevertDeviceId = Devices[Device].Id;
_isLoaded = true;
_isChangeTrackingActive = true;
OnPropertyChanged();
}
}
@@ -180,8 +151,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _controller;
set
{
MarkAsChanged();
_controller = value;
if (_controller == -1)
@@ -216,11 +185,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
IsLeft = false;
break;
}
LoadInputDriver();
LoadProfiles();
}
OnPropertyChanged();
NotifyChanges();
}
@@ -260,8 +229,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _device;
set
{
MarkAsChanged();
_device = value < 0 ? 0 : value;
if (_device >= Devices.Count)
@@ -281,13 +248,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
}
FindPairedDeviceInConfigFile();
OnPropertyChanged();
NotifyChanges();
}
}
public InputConfig Config { get; set; }
public InputViewModel(UserControl owner) : this()
@@ -309,8 +274,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
PlayerId = PlayerIndex.Player1;
}
_isChangeTrackingActive = true;
}
public InputViewModel()
@@ -348,50 +311,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
}
FindPairedDeviceInConfigFile();
}
private void FindPairedDeviceInConfigFile()
{
// This function allows you to output a message about the device configuration found in the file
// NOTE: if the configuration is found, we display the message "Waiting for controller connection",
// but only if the id gamepad belongs to the selected player
NotificationIsVisible = Config != null && Devices.FirstOrDefault(d => d.Id == Config.Id).Id != Config.Id && Config.PlayerIndex == PlayerId;
if (NotificationIsVisible)
{
if (string.IsNullOrEmpty(Config.Name))
{
NotificationText = $"{LocaleManager.Instance[LocaleKeys.ControllerSettingsWaitingConnectDevice].Format("No information", Config.Id)}";
}
else
{
NotificationText = $"{LocaleManager.Instance[LocaleKeys.ControllerSettingsWaitingConnectDevice].Format(Config.Name, Config.Id)}";
}
}
}
private void MarkAsChanged()
{
//If tracking is active, then allow changing the modifier
if (!IsModified && _isChangeTrackingActive)
{
RevertDeviceId = Devices[Device].Id; // Remember the device to undo changes
IsModified = true;
}
}
public void UnlinkDevice()
{
// "Disabled" mode is available after unbinding the device
// NOTE: the IsModified flag to be able to apply the settings.
NotificationIsVisible = false;
IsModified = true;
}
public void LoadDevice()
{
if (Config == null || Config.Backend == InputBackendType.Invalid)
@@ -457,34 +378,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
}
private async void HandleOnGamepadDisconnected(string id)
private void HandleOnGamepadDisconnected(string id)
{
_isChangeTrackingActive = false; // Disable configuration change tracking
await Dispatcher.UIThread.InvokeAsync(() =>
{
LoadDevices();
IsModified = true;
RevertChanges();
FindPairedDeviceInConfigFile();
_isChangeTrackingActive = true; // Enable configuration change tracking
return System.Threading.Tasks.Task.CompletedTask;
});
Dispatcher.UIThread.Post(LoadDevices);
}
private async void HandleOnGamepadConnected(string id)
private void HandleOnGamepadConnected(string id)
{
_isChangeTrackingActive = false; // Disable configuration change tracking
await Dispatcher.UIThread.InvokeAsync(() =>
{
LoadDevices();
IsModified = true;
RevertChanges();
_isChangeTrackingActive = true;// Enable configuration change tracking
});
Dispatcher.UIThread.Post(LoadDevices);
}
private string GetCurrentGamepadId()
@@ -657,14 +558,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (activeDevice.Type == DeviceType.Keyboard)
{
string id = activeDevice.Id;
string name = activeDevice.Name;
config = new StandardKeyboardInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = id,
Name = name,
ControllerType = ControllerType.ProController,
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
@@ -714,14 +613,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
bool isNintendoStyle = Devices.ToList().FirstOrDefault(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
string id = activeDevice.Id.Split(" ")[0];
string name = activeDevice.Name;
config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = id,
Name = name,
ControllerType = ControllerType.ProController,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
@@ -791,12 +688,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
return config;
}
public void LoadProfileButton()
{
LoadProfile();
IsModified = true;
}
public async void LoadProfile()
{
if (Device == 0)
@@ -848,11 +739,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
_isLoaded = false;
config.Id = Config.Id; // Set current device id instead of changing device(independent profiles)
LoadConfiguration(config);
//LoadDevice(); This line of code hard-links profiles to controllers, the commented line allows profiles to be applied to all controllers
LoadDevice();
_isLoaded = true;
@@ -862,58 +751,54 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public async void SaveProfile()
{
if (Device == 0)
{
return;
}
if (ConfigViewModel == null)
{
return;
}
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
if (Device == 0)
{
return;
}
return;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (ConfigViewModel == null)
{
return;
}
if (validFileName)
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
InputConfig config = null;
return;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (IsKeyboard)
{
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}
if (validFileName)
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
config.ControllerType = Controllers[_controller].Type;
InputConfig config = null;
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
if (IsKeyboard)
{
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}
await File.WriteAllTextAsync(path, jsonString);
config.ControllerType = Controllers[_controller].Type;
LoadProfiles();
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
ProfileChoose = ProfileName; // Show new profile
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
await File.WriteAllTextAsync(path, jsonString);
LoadProfiles();
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
}
public async void RemoveProfile()
@@ -940,33 +825,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
LoadProfiles();
ProfileChoose = ProfilesList[0].ToString(); // Show default profile
}
}
public void RevertChanges()
{
Device = Devices.ToList().FindIndex(d => d.Id == RevertDeviceId);
LoadDevice();
LoadConfiguration();
OnPropertyChanged();
IsModified = false;
}
public void Save()
{
if (!IsModified)
{
return; //If the input settings were not touched, then do nothing
}
IsModified = false;
RevertDeviceId = Devices[Device].Id; // Remember selected device after saving
List <InputConfig> newConfig = [];
List<InputConfig> newConfig = [];
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
@@ -996,7 +862,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
: (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
config.ControllerType = Controllers[_controller].Type;
config.PlayerIndex = _playerId;
config.Name = device.Name;
int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
if (i == -1)

View File

@@ -64,9 +64,8 @@ namespace Ryujinx.Ava.UI.Views.Input
};
if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value)
{
FlagInputConfigChanged();
{
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
_changeSlider = (float)check.Value;
}
}
@@ -76,8 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
if (sender is CheckBox { IsPointerOver: true })
{
FlagInputConfigChanged();
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
_currentAssigner?.Cancel();
_currentAssigner = null;
}
@@ -104,7 +102,7 @@ namespace Ryujinx.Ava.UI.Views.Input
this.Focus(NavigationMethod.Pointer);
PointerPressed += MouseClick;
ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel);
IKeyboard keyboard =
@@ -117,7 +115,7 @@ namespace Ryujinx.Ava.UI.Views.Input
if (e.ButtonValue.HasValue)
{
Button buttonValue = e.ButtonValue.Value;
FlagInputConfigChanged();
viewModel.ParentModel.IsModified = true;
switch (button.Name)
{
@@ -211,11 +209,6 @@ namespace Ryujinx.Ava.UI.Views.Input
}
}
private void FlagInputConfigChanged()
{
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
@@ -239,6 +232,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
gamepad?.ClearLed();
}
_currentAssigner?.Cancel();
_currentAssigner = null;
}

View File

@@ -41,20 +41,13 @@
Grid.Column="0"
Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto">
<StackPanel
Orientation="Vertical"
VerticalAlignment="Center" ColumnDefinitions="Auto,*">
<TextBlock
Margin="5,0,10,0"
Width="90"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="90">
<TextBlock
Text="{ext:Locale ControllerSettingsPlayer}" />
<TextBlock
Classes="pending"
Text ="{ext:Locale ControllerSettingsModifiedNotification}"
IsVisible="{Binding IsModified}"/>
</StackPanel>
Text="{ext:Locale ControllerSettingsPlayer}" />
<ComboBox
Grid.Column="1"
Name="PlayerIndexBox"
@@ -69,18 +62,6 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button
Grid.Column="2"
MinWidth="0"
Margin="5,0,0,0"
VerticalAlignment="Center"
ToolTip.Tip="{ext:Locale ControllerSettingsCancelCurrentChangesToolTip}"
Command="{Binding RevertChanges}">
<ui:SymbolIcon
Symbol="Undo"
FontSize="15"
Height="20" />
</Button>
</Grid>
<!-- Profile Selection -->
<Grid
@@ -100,8 +81,7 @@
Name="ProfileBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SelectedItem="{Binding ProfileChoose, Mode=TwoWay}"
SelectionChanged="ComboBox_SelectionChanged"
SelectedIndex="0"
ItemsSource="{Binding ProfilesList}"
Text="{Binding ProfileName, Mode=TwoWay}" />
<Button
@@ -110,7 +90,7 @@
Margin="5,0,0,0"
VerticalAlignment="Center"
ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}"
Command="{Binding LoadProfileButton}">
Command="{Binding LoadProfile}">
<ui:SymbolIcon
Symbol="View"
FontSize="15"
@@ -168,7 +148,7 @@
MinWidth="0"
Margin="5,0,0,0"
VerticalAlignment="Center"
Command="{Binding LoadDevice}">
Command="{Binding LoadDevices}">
<ui:SymbolIcon
Symbol="Refresh"
FontSize="15"
@@ -201,37 +181,15 @@
</Grid>
</Grid>
</StackPanel>
<ContentControl IsVisible="{Binding NotificationIsVisible}">
<ContentControl.Content>
<StackPanel>
<TextBlock
Margin="5,20,0,0"
Text="{Binding NotificationText}" />
<Button
MinWidth="0"
Width="90"
Height="27"
Margin="0,10,0,0"
VerticalAlignment="Center"
Command="{Binding UnlinkDevice}">
<TextBlock
Text="{ext:Locale ControllerSettingsUnlink}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</StackPanel>
</ContentControl.Content>
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
<ContentControl.DataTemplates>
<DataTemplate DataType="viewModels:ControllerInputViewModel">
<views:ControllerInputView />
</DataTemplate>
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
<views:KeyboardInputView />
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
<ContentControl.DataTemplates>
<DataTemplate DataType="viewModels:ControllerInputViewModel">
<views:ControllerInputView />
</DataTemplate>
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
<views:KeyboardInputView />
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
</StackPanel>
</UserControl>

View File

@@ -1,5 +1,4 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
@@ -63,21 +62,12 @@ namespace Ryujinx.Ava.UI.Views.Input
}
return;
}
ViewModel.PlayerId = ViewModel.PlayerIdChoose;
ViewModel.IsModified = false;
ViewModel.PlayerId = ViewModel.PlayerIdChoose;
}
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is FAComboBox faComboBox)
{
faComboBox.IsDropDownOpen = false;
ViewModel.IsModified = true;
}
}
public void Dispose()

View File

@@ -9,8 +9,6 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System.Collections.Generic;
using System;
using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -188,63 +186,11 @@ namespace Ryujinx.Ava.UI.Views.Input
{
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
if (shouldRemoveBinding)
{
DeleteBind();
}
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private void DeleteBind()
{
if (_currentAssigner != null)
{
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
{
{ "ButtonZl", () => ViewModel.Config.ButtonZl = Key.Unbound },
{ "ButtonL", () => ViewModel.Config.ButtonL = Key.Unbound },
{ "ButtonMinus", () => ViewModel.Config.ButtonMinus = Key.Unbound },
{ "LeftStickButton", () => ViewModel.Config.LeftStickButton = Key.Unbound },
{ "LeftStickUp", () => ViewModel.Config.LeftStickUp = Key.Unbound },
{ "LeftStickDown", () => ViewModel.Config.LeftStickDown = Key.Unbound },
{ "LeftStickRight", () => ViewModel.Config.LeftStickRight = Key.Unbound },
{ "LeftStickLeft", () => ViewModel.Config.LeftStickLeft = Key.Unbound },
{ "DpadUp", () => ViewModel.Config.DpadUp = Key.Unbound },
{ "DpadDown", () => ViewModel.Config.DpadDown = Key.Unbound },
{ "DpadLeft", () => ViewModel.Config.DpadLeft = Key.Unbound },
{ "DpadRight", () => ViewModel.Config.DpadRight = Key.Unbound },
{ "LeftButtonSr", () => ViewModel.Config.LeftButtonSr = Key.Unbound },
{ "LeftButtonSl", () => ViewModel.Config.LeftButtonSl = Key.Unbound },
{ "RightButtonSr", () => ViewModel.Config.RightButtonSr = Key.Unbound },
{ "RightButtonSl", () => ViewModel.Config.RightButtonSl = Key.Unbound },
{ "ButtonZr", () => ViewModel.Config.ButtonZr = Key.Unbound },
{ "ButtonR", () => ViewModel.Config.ButtonR = Key.Unbound },
{ "ButtonPlus", () => ViewModel.Config.ButtonPlus = Key.Unbound },
{ "ButtonA", () => ViewModel.Config.ButtonA = Key.Unbound },
{ "ButtonB", () => ViewModel.Config.ButtonB = Key.Unbound },
{ "ButtonX", () => ViewModel.Config.ButtonX = Key.Unbound },
{ "ButtonY", () => ViewModel.Config.ButtonY = Key.Unbound },
{ "RightStickButton", () => ViewModel.Config.RightStickButton = Key.Unbound },
{ "RightStickUp", () => ViewModel.Config.RightStickUp = Key.Unbound },
{ "RightStickDown", () => ViewModel.Config.RightStickDown = Key.Unbound },
{ "RightStickRight", () => ViewModel.Config.RightStickRight = Key.Unbound },
{ "RightStickLeft", () => ViewModel.Config.RightStickLeft = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
{
action();
ViewModel.ParentModel.IsModified = true;
}
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);

View File

@@ -10,8 +10,6 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System.Collections.Generic;
using System;
using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -50,47 +48,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
if (shouldRemoveBinding)
{
DeleteBind();
}
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private void DeleteBind()
{
if (DataContext is not SettingsViewModel viewModel)
return;
if (_currentAssigner != null)
{
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
{
{ "ToggleVSyncMode", () => viewModel.KeyboardHotkey.ToggleVSyncMode = Key.Unbound },
{ "Screenshot", () => viewModel.KeyboardHotkey.Screenshot = Key.Unbound },
{ "ShowUI", () => viewModel.KeyboardHotkey.ShowUI = Key.Unbound },
{ "Pause", () => viewModel.KeyboardHotkey.Pause = Key.Unbound },
{ "ToggleMute", () => viewModel.KeyboardHotkey.ToggleMute = Key.Unbound },
{ "ResScaleUp", () => viewModel.KeyboardHotkey.ResScaleUp = Key.Unbound },
{ "ResScaleDown", () => viewModel.KeyboardHotkey.ResScaleDown = Key.Unbound },
{ "VolumeUp", () => viewModel.KeyboardHotkey.VolumeUp = Key.Unbound },
{ "VolumeDown", () => viewModel.KeyboardHotkey.VolumeDown = Key.Unbound },
{ "CustomVSyncIntervalIncrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = Key.Unbound },
{ "CustomVSyncIntervalDecrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
{
action();
}
}
}
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)