Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to setup hotkeys to cycle through controllers #166

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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