diff --git a/assets/locales.json b/assets/locales.json
index 27c92ccad..533bd5cc3 100644
--- a/assets/locales.json
+++ b/assets/locales.json
@@ -24373,12 +24373,12 @@
}
},
{
- "ID": "SettingsTabHotkeysCycleInputDevicePlayer1",
+ "ID": "SettingsTabHotkeysCycleInputDevicePlayerX",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
- "en_US": "Cycle Input Device Player 1:",
+ "en_US": "Cycle Input Device {0}:",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
@@ -24398,12 +24398,12 @@
}
},
{
- "ID": "SettingsTabHotkeysCycleInputDevicePlayer2",
+ "ID": "ControllerOverlayTitle",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
- "en_US": "Cycle Input Device Player 2:",
+ "en_US": "Controller Bindings",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
@@ -24423,12 +24423,12 @@
}
},
{
- "ID": "SettingsTabHotkeysCycleInputDevicePlayer3",
+ "ID": "ControllerOverlayNoController",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
- "en_US": "Cycle Input Device Player 3:",
+ "en_US": "No controller assigned",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
@@ -24448,12 +24448,62 @@
}
},
{
- "ID": "SettingsTabHotkeysCycleInputDevicePlayer4",
+ "ID": "ControllerOverlayKeyboard",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
- "en_US": "Cycle Input Device Player 4:",
+ "en_US": "Keyboard",
+ "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": "ControllerOverlayController",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Controller",
+ "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": "ControllerOverlayUnknown",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Unknown",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
index f84434ba1..cc6ec55d5 100644
--- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
+++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -19,5 +19,10 @@ namespace Ryujinx.Common.Configuration.Hid
public Key CycleInputDevicePlayer2 { get; set; }
public Key CycleInputDevicePlayer3 { get; set; }
public Key CycleInputDevicePlayer4 { get; set; }
+ public Key CycleInputDevicePlayer5 { get; set; }
+ public Key CycleInputDevicePlayer6 { get; set; }
+ public Key CycleInputDevicePlayer7 { get; set; }
+ public Key CycleInputDevicePlayer8 { get; set; }
+ public Key CycleInputDeviceHandheld { get; set; }
}
}
diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs b/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs
index 69795d48e..d22389ed5 100644
--- a/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs
+++ b/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs
@@ -8,6 +8,18 @@ using OriginalInputBackendType = Ryujinx.Common.Configuration.Hid.InputBackendTy
namespace Ryujinx.Graphics.Gpu.Overlay
{
+ ///
+ /// Localization strings for the controller overlay
+ ///
+ public class ControllerOverlayLocalization
+ {
+ public string TitleText { get; set; } = "Controller Bindings";
+ public string NoControllerText { get; set; } = "No controller assigned";
+ public string KeyboardText { get; set; } = "Keyboard";
+ public string ControllerText { get; set; } = "Controller";
+ public string UnknownText { get; set; } = "Unknown";
+ }
+
///
/// Controller overlay that shows controller bindings matching the original AXAML design
///
@@ -23,9 +35,11 @@ namespace Ryujinx.Graphics.Gpu.Overlay
private const float PlayerTextSize = 22;
private float _lifespan = 0f;
+ private ControllerOverlayLocalization _localization;
- public ControllerOverlay() : base("ControllerOverlay")
+ public ControllerOverlay(ControllerOverlayLocalization localization) : base("ControllerOverlay")
{
+ _localization = localization;
CreateBaseElements();
}
@@ -42,8 +56,8 @@ namespace Ryujinx.Graphics.Gpu.Overlay
};
AddElement(background);
- // Title text
- var titleText = new TextElement(Padding + 30, Padding, "Controller Bindings", TitleTextSize, SKColors.White)
+ // Title text (will be updated with localized text)
+ var titleText = new TextElement(Padding + 30, Padding, _localization.TitleText, TitleTextSize, SKColors.White)
{
Name = "TitleText",
FontStyle = SKFontStyle.Bold
@@ -52,21 +66,27 @@ namespace Ryujinx.Graphics.Gpu.Overlay
}
///
- /// Show controller bindings matching the original AXAML implementation
+ /// Show controller bindings with localized strings
///
public void ShowControllerBindings(List inputConfigs, int durationSeconds = 3)
{
+ // Update title text
+ var titleElement = FindElement("TitleText");
+ if (titleElement != null)
+ {
+ titleElement.Text = _localization.TitleText;
+ }
+
// Reset lifespan and opacity
_lifespan = durationSeconds;
// Clear existing player bindings
ClearPlayerBindings();
- // Debug: Log input data
- // Group controllers by player index
+ // Group controllers by player index (support all players + handheld)
var playerBindings = new Dictionary>();
- foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= OriginalPlayerIndex.Player4))
+ foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= OriginalPlayerIndex.Handheld))
{
if (!playerBindings.ContainsKey(config.PlayerIndex))
{
@@ -77,10 +97,17 @@ namespace Ryujinx.Graphics.Gpu.Overlay
float currentY = Padding + 40; // After title
- // Add player bindings to UI
- for (int i = 0; i < 4; i++)
+ // Add player bindings to UI (support 8 players + handheld)
+ var playerIndices = new[]
+ {
+ OriginalPlayerIndex.Player1, OriginalPlayerIndex.Player2, OriginalPlayerIndex.Player3, OriginalPlayerIndex.Player4,
+ OriginalPlayerIndex.Player5, OriginalPlayerIndex.Player6, OriginalPlayerIndex.Player7, OriginalPlayerIndex.Player8,
+ OriginalPlayerIndex.Handheld
+ };
+
+ for (int i = 0; i < playerIndices.Length; i++)
{
- var playerIndex = (OriginalPlayerIndex)i;
+ var playerIndex = playerIndices[i];
float rowY = currentY + (i * (PlayerRowHeight + PlayerSpacing));
// Player number with colored background (circular badge)
@@ -93,13 +120,14 @@ namespace Ryujinx.Graphics.Gpu.Overlay
AddElement(playerBadge);
// Player number text
- var playerLabel = new TextElement(Padding + 12, rowY + 2, $"P{i + 1}", PlayerTextSize, SKColors.White)
+ string playerLabel = playerIndex == OriginalPlayerIndex.Handheld ? "H" : $"P{(int)playerIndex + 1}";
+ var playerLabelElement = new TextElement(Padding + 12, rowY + 2, playerLabel, PlayerTextSize, SKColors.White)
{
Name = $"PlayerLabel_{i}",
FontStyle = SKFontStyle.Bold,
TextAlign = SKTextAlign.Center
};
- AddElement(playerLabel);
+ AddElement(playerLabelElement);
// Controller info
if (playerBindings.ContainsKey(playerIndex))
@@ -107,37 +135,32 @@ namespace Ryujinx.Graphics.Gpu.Overlay
var controllers = playerBindings[playerIndex];
var controllerNames = controllers.Select(c => GetControllerDisplayName(c)).ToList();
- var controllerText = new TextElement(Padding + 56, rowY + 2, string.Join(", ", controllerNames), PlayerTextSize, new SKColor(144, 238, 144)) // LightGreen
+ var controllerTextElement = new TextElement(Padding + 56, rowY + 2, string.Join(", ", controllerNames), PlayerTextSize, new SKColor(144, 238, 144)) // LightGreen
{
Name = $"ControllerText_{i}",
FontStyle = SKFontStyle.Bold
};
- AddElement(controllerText);
+ AddElement(controllerTextElement);
}
else
{
- var noControllerText = new TextElement(Padding + 56, rowY + 2, "No controller assigned", PlayerTextSize, new SKColor(128, 128, 128)) // Gray
+ var noControllerTextElement = new TextElement(Padding + 56, rowY + 2, _localization.NoControllerText, PlayerTextSize, new SKColor(128, 128, 128)) // Gray
{
Name = $"NoControllerText_{i}",
FontStyle = SKFontStyle.Italic
};
- AddElement(noControllerText);
+ AddElement(noControllerTextElement);
}
}
// Calculate total height and update background
- float totalHeight = Padding + 40 + (4 * (PlayerRowHeight + PlayerSpacing)) + Padding + 40; // Extra space for duration text
+ float totalHeight = Padding + 40 + (playerIndices.Length * (PlayerRowHeight + PlayerSpacing)) + Padding + 20;
var background = FindElement("Background");
if (background != null)
{
background.Height = totalHeight;
}
- // Duration text at bottom
- string durationText = durationSeconds == 1
- ? "This overlay will disappear in 1 second"
- : $"This overlay will disappear in {durationSeconds} seconds";
-
// Show the overlay (position will be set by Window class with actual dimensions)
IsVisible = true;
}
@@ -150,19 +173,24 @@ namespace Ryujinx.Graphics.Gpu.Overlay
1 => new SKColor(54, 162, 235), // Blue for Player 2
2 => new SKColor(255, 206, 84), // Yellow for Player 3
3 => new SKColor(75, 192, 192), // Green for Player 4
+ 4 => new SKColor(153, 102, 255), // Purple for Player 5
+ 5 => new SKColor(255, 159, 64), // Orange for Player 6
+ 6 => new SKColor(199, 199, 199), // Light Gray for Player 7
+ 7 => new SKColor(83, 102, 255), // Indigo for Player 8
+ 8 => new SKColor(255, 99, 132), // Pink for Handheld
_ => new SKColor(128, 128, 128) // Gray fallback
};
}
- private static string GetControllerDisplayName(OriginalInputConfig config)
+ private string GetControllerDisplayName(OriginalInputConfig config)
{
if (string.IsNullOrEmpty(config.Name))
{
return config.Backend switch
{
- OriginalInputBackendType.WindowKeyboard => "Keyboard",
- OriginalInputBackendType.GamepadSDL2 => "Controller",
- _ => "Unknown"
+ OriginalInputBackendType.WindowKeyboard => _localization.KeyboardText,
+ OriginalInputBackendType.GamepadSDL2 => _localization.ControllerText,
+ _ => _localization.UnknownText
};
}
diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs b/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs
index 57122d73d..849a1e5af 100644
--- a/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs
+++ b/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs
@@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
///
public class ImageElement : OverlayElement
{
- private SKBitmap? _bitmap;
- private byte[]? _imageData;
- private string? _imagePath;
+ private SKBitmap _bitmap;
+ private byte[] _imageData;
+ private string _imagePath;
public SKFilterQuality FilterQuality { get; set; } = SKFilterQuality.Medium;
public bool MaintainAspectRatio { get; set; } = true;
diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs
index 0c59967a0..e07aa29e2 100644
--- a/src/Ryujinx.Graphics.Gpu/Window.cs
+++ b/src/Ryujinx.Graphics.Gpu/Window.cs
@@ -107,9 +107,6 @@ namespace Ryujinx.Graphics.Gpu
_overlayManager = new OverlayManager();
_frameQueue = new ConcurrentQueue();
-
- // Initialize controller overlay
- InitializeControllerOverlay();
}
///
@@ -258,12 +255,11 @@ namespace Ryujinx.Graphics.Gpu
}
///
- /// Initialize controller overlay
+ /// Add overlay to the overlay manager
///
- private void InitializeControllerOverlay()
+ public void AddOverlay(Overlay.Overlay overlay)
{
- var controllerOverlay = new ControllerOverlay();
- _overlayManager.AddOverlay(controllerOverlay);
+ _overlayManager.AddOverlay(overlay);
}
///
@@ -363,17 +359,6 @@ namespace Ryujinx.Graphics.Gpu
return false;
}
- ///
- /// Show controller overlay with the provided input configurations
- ///
- /// List of input configurations to display
- /// Duration to show the overlay in seconds
- public void ShowControllerBindings(List inputConfigs, int durationSeconds = 3)
- {
- var controllerOverlay = _overlayManager.FindOverlay("ControllerOverlay") as ControllerOverlay;
- controllerOverlay?.ShowControllerBindings(inputConfigs, durationSeconds);
- }
-
///
/// Get the overlay manager for external access
///
diff --git a/src/Ryujinx/Common/KeyboardHotkeyState.cs b/src/Ryujinx/Common/KeyboardHotkeyState.cs
index f7b7406d2..2df1d5970 100644
--- a/src/Ryujinx/Common/KeyboardHotkeyState.cs
+++ b/src/Ryujinx/Common/KeyboardHotkeyState.cs
@@ -19,5 +19,10 @@ namespace Ryujinx.Ava.Common
CycleInputDevicePlayer2,
CycleInputDevicePlayer3,
CycleInputDevicePlayer4,
+ CycleInputDevicePlayer5,
+ CycleInputDevicePlayer6,
+ CycleInputDevicePlayer7,
+ CycleInputDevicePlayer8,
+ CycleInputDeviceHandheld,
}
}
diff --git a/src/Ryujinx/Common/Markup/MarkupExtensions.cs b/src/Ryujinx/Common/Markup/MarkupExtensions.cs
index b2ed01517..07d7b3613 100644
--- a/src/Ryujinx/Common/Markup/MarkupExtensions.cs
+++ b/src/Ryujinx/Common/Markup/MarkupExtensions.cs
@@ -1,3 +1,4 @@
+using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Projektanker.Icons.Avalonia;
using Ryujinx.Ava.Common.Locale;
@@ -18,11 +19,19 @@ namespace Ryujinx.Ava.Common.Markup
internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension
{
+ public IValueConverter Converter { get; set; }
+
public override string Name => "Translation";
protected override string Value => LocaleManager.Instance[key];
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
- => bindingExtension.Source = LocaleManager.Instance;
+ {
+ bindingExtension.Source = LocaleManager.Instance;
+ if (Converter != null)
+ {
+ bindingExtension.Converter = Converter;
+ }
+ }
}
internal class WindowTitleExtension(LocaleKeys key, bool includeVersion) : BasicMarkupExtension
diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs
index 6c9cec18a..756771de5 100644
--- a/src/Ryujinx/Systems/AppHost.cs
+++ b/src/Ryujinx/Systems/AppHost.cs
@@ -33,6 +33,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
+using Ryujinx.Graphics.Gpu.Overlay;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem;
@@ -129,6 +130,7 @@ namespace Ryujinx.Ava.Systems
private readonly bool _isFirmwareTitle;
private readonly Lock _lockObject = new();
+ private ControllerOverlay _controllerOverlay;
public event EventHandler AppExit;
public event EventHandler StatusUpdatedEvent;
@@ -932,6 +934,18 @@ namespace Ryujinx.Ava.Systems
_viewModel.UiHandler
)
);
+
+ // Initialize controller overlay with localization
+ var localization = new ControllerOverlayLocalization
+ {
+ TitleText = LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle],
+ NoControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayNoController],
+ KeyboardText = LocaleManager.Instance[LocaleKeys.ControllerOverlayKeyboard],
+ ControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayController],
+ UnknownText = LocaleManager.Instance[LocaleKeys.ControllerOverlayUnknown]
+ };
+ _controllerOverlay = new ControllerOverlay(localization);
+ Device.Gpu.Window.AddOverlay(_controllerOverlay);
}
private static IHardwareDeviceDriver InitializeAudio()
@@ -1350,6 +1364,21 @@ namespace Ryujinx.Ava.Systems
case KeyboardHotkeyState.CycleInputDevicePlayer4:
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player4);
break;
+ case KeyboardHotkeyState.CycleInputDevicePlayer5:
+ CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player5);
+ break;
+ case KeyboardHotkeyState.CycleInputDevicePlayer6:
+ CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player6);
+ break;
+ case KeyboardHotkeyState.CycleInputDevicePlayer7:
+ CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player7);
+ break;
+ case KeyboardHotkeyState.CycleInputDevicePlayer8:
+ CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player8);
+ break;
+ case KeyboardHotkeyState.CycleInputDeviceHandheld:
+ CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Handheld);
+ break;
case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear();
break;
@@ -1458,7 +1487,7 @@ namespace Ryujinx.Ava.Systems
NpadManager.ReloadConfiguration(currentConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
// Show controller overlay
- ShowControllerOverlay(currentConfig);
+ ShowControllerOverlay(currentConfig, ConfigurationState.Instance.ControllerOverlayInputCycleDuration.Value);
}
private InputConfig CreateDefaultInputConfig((DeviceType Type, string Id, string Name) device, PlayerIndex playerIndex)
@@ -1476,21 +1505,17 @@ namespace Ryujinx.Ava.Systems
return null;
}
-
-
- private void ShowControllerOverlay(List inputConfigs)
+ public void ShowControllerOverlay(List inputConfigs, int duration)
{
try
{
- // Show overlay through the GPU context window directly
- if (Device?.Gpu?.Window != null)
+ if (_controllerOverlay != null)
{
- int duration = ConfigurationState.Instance.ControllerOverlayInputCycleDuration.Value;
- Device.Gpu.Window.ShowControllerBindings(inputConfigs, duration);
+ _controllerOverlay.ShowControllerBindings(inputConfigs, duration);
}
else
{
- Logger.Warning?.Print(LogClass.Application, "AppHost: Cannot show overlay - Device.Gpu.Window is null");
+ Logger.Warning?.Print(LogClass.Application, "AppHost: Cannot show overlay - ControllerOverlay is null");
}
}
catch (Exception ex)
@@ -1567,6 +1592,26 @@ namespace Ryujinx.Ava.Systems
{
state = KeyboardHotkeyState.CycleInputDevicePlayer4;
}
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer5))
+ {
+ state = KeyboardHotkeyState.CycleInputDevicePlayer5;
+ }
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer6))
+ {
+ state = KeyboardHotkeyState.CycleInputDevicePlayer6;
+ }
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer7))
+ {
+ state = KeyboardHotkeyState.CycleInputDevicePlayer7;
+ }
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer8))
+ {
+ state = KeyboardHotkeyState.CycleInputDevicePlayer8;
+ }
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDeviceHandheld))
+ {
+ state = KeyboardHotkeyState.CycleInputDeviceHandheld;
+ }
return state;
}
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
index 0f45d04d1..eaa92c92a 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Systems.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 71;
+ public const int CurrentVersion = 72;
///
/// Version of the configuration file format
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
index 6ad687688..3cf2e04d7 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
@@ -486,6 +486,34 @@ namespace Ryujinx.Ava.Systems.Configuration
{
cff.ControllerOverlayGameStartDuration = 3;
cff.ControllerOverlayInputCycleDuration = 2;
+ }),
+ (72, static cff =>
+ {
+ cff.Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVSyncMode = cff.Hotkeys.ToggleVSyncMode,
+ Screenshot = cff.Hotkeys.Screenshot,
+ ShowUI = cff.Hotkeys.ShowUI,
+ Pause = cff.Hotkeys.Pause,
+ ToggleMute = cff.Hotkeys.ToggleMute,
+ ResScaleUp = cff.Hotkeys.ResScaleUp,
+ ResScaleDown = cff.Hotkeys.ResScaleDown,
+ VolumeUp = cff.Hotkeys.VolumeUp,
+ VolumeDown = cff.Hotkeys.VolumeDown,
+ CustomVSyncIntervalIncrement = cff.Hotkeys.CustomVSyncIntervalIncrement,
+ CustomVSyncIntervalDecrement = cff.Hotkeys.CustomVSyncIntervalDecrement,
+ TurboMode = cff.Hotkeys.TurboMode,
+ TurboModeWhileHeld = cff.Hotkeys.TurboModeWhileHeld,
+ CycleInputDevicePlayer1 = Key.Unbound,
+ CycleInputDevicePlayer2 = Key.Unbound,
+ CycleInputDevicePlayer3 = Key.Unbound,
+ CycleInputDevicePlayer4 = Key.Unbound,
+ CycleInputDevicePlayer5 = Key.Unbound,
+ CycleInputDevicePlayer6 = Key.Unbound,
+ CycleInputDevicePlayer7 = Key.Unbound,
+ CycleInputDevicePlayer8 = Key.Unbound,
+ CycleInputDeviceHandheld = Key.Unbound
+ };
})
);
}
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
index 71f73bc65..0d4211e1b 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
@@ -273,7 +273,16 @@ namespace Ryujinx.Ava.Systems.Configuration
CustomVSyncIntervalIncrement = Key.Unbound,
CustomVSyncIntervalDecrement = Key.Unbound,
TurboMode = Key.Unbound,
- TurboModeWhileHeld = false
+ TurboModeWhileHeld = false,
+ CycleInputDevicePlayer1 = Key.Unbound,
+ CycleInputDevicePlayer2 = Key.Unbound,
+ CycleInputDevicePlayer3 = Key.Unbound,
+ CycleInputDevicePlayer4 = Key.Unbound,
+ CycleInputDevicePlayer5 = Key.Unbound,
+ CycleInputDevicePlayer6 = Key.Unbound,
+ CycleInputDevicePlayer7 = Key.Unbound,
+ CycleInputDevicePlayer8 = Key.Unbound,
+ CycleInputDeviceHandheld = Key.Unbound
};
Hid.RainbowSpeed.Value = 1f;
Hid.InputConfig.Value =
diff --git a/src/Ryujinx/UI/Helpers/Converters/PlayerHotkeyLabelConverter.cs b/src/Ryujinx/UI/Helpers/Converters/PlayerHotkeyLabelConverter.cs
new file mode 100644
index 000000000..9ac7392a6
--- /dev/null
+++ b/src/Ryujinx/UI/Helpers/Converters/PlayerHotkeyLabelConverter.cs
@@ -0,0 +1,28 @@
+using Avalonia.Data.Converters;
+using Ryujinx.Ava.Common.Locale;
+using System;
+using System.Globalization;
+
+namespace Ryujinx.Ava.UI.Helpers
+{
+ internal class PlayerHotkeyLabelConverter : IValueConverter
+ {
+ public static readonly PlayerHotkeyLabelConverter Instance = new();
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is string playerName && !string.IsNullOrEmpty(playerName))
+ {
+ string baseText = LocaleManager.Instance[LocaleKeys.SettingsTabHotkeysCycleInputDevicePlayerX];
+ return string.Format(baseText, playerName);
+ }
+
+ return string.Empty;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs
index 78ab40678..b6de26aa0 100644
--- a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs
+++ b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs
@@ -40,6 +40,16 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private Key _cycleInputDevicePlayer4;
+ [ObservableProperty] private Key _cycleInputDevicePlayer5;
+
+ [ObservableProperty] private Key _cycleInputDevicePlayer6;
+
+ [ObservableProperty] private Key _cycleInputDevicePlayer7;
+
+ [ObservableProperty] private Key _cycleInputDevicePlayer8;
+
+ [ObservableProperty] private Key _cycleInputDeviceHandheld;
+
public HotkeyConfig(KeyboardHotkeys config)
{
if (config == null)
@@ -62,6 +72,11 @@ namespace Ryujinx.Ava.UI.Models.Input
CycleInputDevicePlayer2 = config.CycleInputDevicePlayer2;
CycleInputDevicePlayer3 = config.CycleInputDevicePlayer3;
CycleInputDevicePlayer4 = config.CycleInputDevicePlayer4;
+ CycleInputDevicePlayer5 = config.CycleInputDevicePlayer5;
+ CycleInputDevicePlayer6 = config.CycleInputDevicePlayer6;
+ CycleInputDevicePlayer7 = config.CycleInputDevicePlayer7;
+ CycleInputDevicePlayer8 = config.CycleInputDevicePlayer8;
+ CycleInputDeviceHandheld = config.CycleInputDeviceHandheld;
}
public KeyboardHotkeys GetConfig() =>
@@ -83,7 +98,12 @@ namespace Ryujinx.Ava.UI.Models.Input
CycleInputDevicePlayer1 = CycleInputDevicePlayer1,
CycleInputDevicePlayer2 = CycleInputDevicePlayer2,
CycleInputDevicePlayer3 = CycleInputDevicePlayer3,
- CycleInputDevicePlayer4 = CycleInputDevicePlayer4
+ CycleInputDevicePlayer4 = CycleInputDevicePlayer4,
+ CycleInputDevicePlayer5 = CycleInputDevicePlayer5,
+ CycleInputDevicePlayer6 = CycleInputDevicePlayer6,
+ CycleInputDevicePlayer7 = CycleInputDevicePlayer7,
+ CycleInputDevicePlayer8 = CycleInputDevicePlayer8,
+ CycleInputDeviceHandheld = CycleInputDeviceHandheld
};
}
}
diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
index 1a8bce7dc..4c73f0aa4 100644
--- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
@@ -1600,9 +1600,9 @@ namespace Ryujinx.Ava.UI.ViewModels
// Code where conditions will be executed after loading user configuration
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != backendThreadingInit)
{
- Rebooter.RebootAppWithGame(application.Path,
+ Rebooter.RebootAppWithGame(application.Path,
[
- "--bt",
+ "--bt",
ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()
]);
@@ -1710,10 +1710,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// Always show overlay - if no configs, it will show test data
int duration = ConfigurationState.Instance.ControllerOverlayGameStartDuration.Value;
// Show overlay through the GPU context window directly
- if (AppHost?.Device?.Gpu?.Window != null)
- {
- AppHost.Device.Gpu.Window.ShowControllerBindings(inputConfigs, duration);
- }
+ AppHost.ShowControllerOverlay(inputConfigs, duration);
}
catch (Exception ex)
{
diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml
index ba82812b9..e120a4ef1 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml
+++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml
@@ -121,29 +121,59 @@
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs
index 7f481f82e..e547f2e70 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs
+++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs
@@ -86,7 +86,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
{ "CycleInputDevicePlayer1", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer1 = Key.Unbound },
{ "CycleInputDevicePlayer2", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer2 = Key.Unbound },
{ "CycleInputDevicePlayer3", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer3 = Key.Unbound },
- { "CycleInputDevicePlayer4", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer4 = Key.Unbound }
+ { "CycleInputDevicePlayer4", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer4 = Key.Unbound },
+ { "CycleInputDevicePlayer5", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer5 = Key.Unbound },
+ { "CycleInputDevicePlayer6", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer6 = Key.Unbound },
+ { "CycleInputDevicePlayer7", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer7 = Key.Unbound },
+ { "CycleInputDevicePlayer8", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer8 = Key.Unbound },
+ { "CycleInputDeviceHandheld", () => viewModel.KeyboardHotkey.CycleInputDeviceHandheld = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
@@ -178,6 +183,21 @@ namespace Ryujinx.Ava.UI.Views.Settings
case "CycleInputDevicePlayer4":
ViewModel.KeyboardHotkey.CycleInputDevicePlayer4 = buttonValue.AsHidType();
break;
+ case "CycleInputDevicePlayer5":
+ ViewModel.KeyboardHotkey.CycleInputDevicePlayer5 = buttonValue.AsHidType();
+ break;
+ case "CycleInputDevicePlayer6":
+ ViewModel.KeyboardHotkey.CycleInputDevicePlayer6 = buttonValue.AsHidType();
+ break;
+ case "CycleInputDevicePlayer7":
+ ViewModel.KeyboardHotkey.CycleInputDevicePlayer7 = buttonValue.AsHidType();
+ break;
+ case "CycleInputDevicePlayer8":
+ ViewModel.KeyboardHotkey.CycleInputDevicePlayer8 = buttonValue.AsHidType();
+ break;
+ case "CycleInputDeviceHandheld":
+ ViewModel.KeyboardHotkey.CycleInputDeviceHandheld = buttonValue.AsHidType();
+ break;
}
});
}