Skip to content

Commit

Permalink
Ability to assign hotkeys to cycle controllers for players
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeoneIsWorking committed Dec 24, 2024
1 parent b05eab2 commit e774947
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 172 deletions.
3 changes: 3 additions & 0 deletions src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Collections.Generic;

namespace Ryujinx.Common.Configuration.Hid
{
public class KeyboardHotkeys
Expand All @@ -13,5 +15,6 @@ public class KeyboardHotkeys
public Key VolumeDown { get; set; }
public Key CustomVSyncIntervalIncrement { get; set; }
public Key CustomVSyncIntervalDecrement { get; set; }
public List<Key> CycleControllers { get; set; }
}
}
23 changes: 23 additions & 0 deletions src/Ryujinx/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
Expand All @@ -50,6 +51,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -1332,6 +1334,18 @@ private bool UpdateFrame()

_viewModel.Volume = Device.GetVolume();
break;
case KeyboardHotkeyState.CycleControllersPlayer1:
case KeyboardHotkeyState.CycleControllersPlayer2:
case KeyboardHotkeyState.CycleControllersPlayer3:
case KeyboardHotkeyState.CycleControllersPlayer4:
case KeyboardHotkeyState.CycleControllersPlayer5:
case KeyboardHotkeyState.CycleControllersPlayer6:
case KeyboardHotkeyState.CycleControllersPlayer7:
case KeyboardHotkeyState.CycleControllersPlayer8:
var player = currentHotkeyState - KeyboardHotkeyState.CycleControllersPlayer1;
var ivm = new UI.ViewModels.Input.InputViewModel();
Dispatcher.UIThread.Invoke(() => ivm.CyclePlayerDevice(player));
break;
case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear();
break;
Expand Down Expand Up @@ -1414,6 +1428,15 @@ private KeyboardHotkeyState GetHotkeyState()
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
}

foreach (var cycle in ConfigurationState.Instance.Hid.Hotkeys.Value.CycleControllers?.Select((value, index) => (value, index)) ?? [])
{
if (_keyboardInterface.IsPressed((Key)cycle.value))
{
state = KeyboardHotkeyState.CycleControllersPlayer1 + cycle.index;
break;
}
}

return state;
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/Ryujinx/Assets/locales.json
Original file line number Diff line number Diff line change
Expand Up @@ -4989,6 +4989,30 @@
"zh_TW": "啟用警告日誌"
}
},
{
"ID": "SettingsTabHotkeysCycleControllers",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Cycle Controllers",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "SettingsTabLoggingEnableErrorLogs",
"Translations": {
Expand Down
8 changes: 8 additions & 0 deletions src/Ryujinx/Common/KeyboardHotkeyState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,13 @@ public enum KeyboardHotkeyState
VolumeDown,
CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement,
CycleControllersPlayer1,
CycleControllersPlayer2,
CycleControllersPlayer3,
CycleControllersPlayer4,
CycleControllersPlayer5,
CycleControllersPlayer6,
CycleControllersPlayer7,
CycleControllersPlayer8
}
}
15 changes: 15 additions & 0 deletions src/Ryujinx/UI/Models/Input/HotkeyConfig.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using DynamicData;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration.Hid;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;

namespace Ryujinx.Ava.UI.Models.Input
{
Expand Down Expand Up @@ -126,8 +131,15 @@ public Key CustomVSyncIntervalDecrement
}
}

public ObservableCollection<CycleController> CycleControllers { get; set; } = new ObservableCollection<CycleController>();
public ICommand AddCycleController { get; set; }
public ICommand RemoveCycleController { get; set; }
public bool CanRemoveCycleController => CycleControllers.Count > 0 && CycleControllers.Count < 8;

public HotkeyConfig(KeyboardHotkeys config)
{
AddCycleController = MiniCommand.Create(() => CycleControllers.Add(new CycleController(CycleControllers.Count + 1, Key.Unbound)));
RemoveCycleController = MiniCommand.Create(() => CycleControllers.Remove(CycleControllers.Last()));
if (config != null)
{
ToggleVSyncMode = config.ToggleVSyncMode;
Expand All @@ -141,7 +153,9 @@ public HotkeyConfig(KeyboardHotkeys config)
VolumeDown = config.VolumeDown;
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
CycleControllers.AddRange((config.CycleControllers ?? []).Select((x, i) => new CycleController(i + 1, x)));
}
CycleControllers.CollectionChanged += (sender, e) => OnPropertyChanged(nameof(CanRemoveCycleController));
}

public KeyboardHotkeys GetConfig()
Expand All @@ -159,6 +173,7 @@ public KeyboardHotkeys GetConfig()
VolumeDown = VolumeDown,
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
CycleControllers = CycleControllers.Select(x => x.Hotkey).ToList()
};

return config;
Expand Down
48 changes: 48 additions & 0 deletions src/Ryujinx/UI/ViewModels/CycleController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Configuration.Hid;

namespace Ryujinx.Ava.UI.ViewModels
{
public class CycleController : BaseModel
{
private string _player;
private Key _hotkey;

public string Player
{
get => _player;
set
{
_player = value;
OnPropertyChanged(nameof(Player));
}
}

public Key Hotkey
{
get => _hotkey;
set
{
_hotkey = value;
OnPropertyChanged(nameof(Hotkey));
}
}

public CycleController(int v, Key x)
{
Player = v switch
{
1 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1],
2 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2],
3 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3],
4 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4],
5 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5],
6 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6],
7 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7],
8 => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8],
_ => LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer] + " " + v
};
Hotkey = x;
}
}
}
58 changes: 33 additions & 25 deletions src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ public InputViewModel(UserControl owner) : this()

public InputViewModel()
{
_mainWindow =
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
.ApplicationLifetime).MainWindow;
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(_mainWindow);
PlayerIndexes = new ObservableCollection<PlayerModel>();
Controllers = new ObservableCollection<ControllerModel>();
Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
Expand Down Expand Up @@ -754,38 +758,34 @@ public async void SaveProfile()

return;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;

if (validFileName)
{
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;

InputConfig config = null;
if (!validFileName)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
return;
}
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");

if (IsKeyboard)
{
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}
InputConfig config = null;

config.ControllerType = Controllers[_controller].Type;
if (IsKeyboard)
{
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}

string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
config.ControllerType = Controllers[_controller].Type;

await File.WriteAllTextAsync(path, jsonString);
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);

LoadProfiles();
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
await File.WriteAllTextAsync(path, jsonString);

LoadProfiles();
}

public async void RemoveProfile()
Expand Down Expand Up @@ -899,5 +899,13 @@ public void Dispose()

AvaloniaKeyboardDriver.Dispose();
}

public void CyclePlayerDevice(int player)
{
LoadDevices();
PlayerId = (PlayerIndex)player;
Device = (Device + 1) % Devices.Count;
Save();
}
}
}
Loading

0 comments on commit e774947

Please sign in to comment.