ControllerSettingsViewModel.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  1. using Avalonia.Collections;
  2. using Avalonia.Controls;
  3. using Avalonia.Controls.ApplicationLifetimes;
  4. using Avalonia.Svg.Skia;
  5. using Avalonia.Threading;
  6. using LibHac.Bcat;
  7. using LibHac.Tools.Fs;
  8. using Ryujinx.Ava.Common.Locale;
  9. using Ryujinx.Ava.Input;
  10. using Ryujinx.Ava.UI.Controls;
  11. using Ryujinx.Ava.UI.Helpers;
  12. using Ryujinx.Ava.UI.Models;
  13. using Ryujinx.Ava.UI.Windows;
  14. using Ryujinx.Common;
  15. using Ryujinx.Common.Configuration;
  16. using Ryujinx.Common.Configuration.Hid;
  17. using Ryujinx.Common.Configuration.Hid.Controller;
  18. using Ryujinx.Common.Configuration.Hid.Controller.Motion;
  19. using Ryujinx.Common.Configuration.Hid.Keyboard;
  20. using Ryujinx.Common.Logging;
  21. using Ryujinx.Common.Utilities;
  22. using Ryujinx.Input;
  23. using Ryujinx.Ui.Common.Configuration;
  24. using System;
  25. using System.Collections.Generic;
  26. using System.Collections.ObjectModel;
  27. using System.IO;
  28. using System.Linq;
  29. using System.Text.Json;
  30. using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
  31. using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
  32. using Key = Ryujinx.Common.Configuration.Hid.Key;
  33. namespace Ryujinx.Ava.UI.ViewModels
  34. {
  35. public class ControllerSettingsViewModel : BaseModel, IDisposable
  36. {
  37. private const string Disabled = "disabled";
  38. private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
  39. private const string JoyConPairResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg";
  40. private const string JoyConLeftResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg";
  41. private const string JoyConRightResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg";
  42. private const string KeyboardString = "keyboard";
  43. private const string ControllerString = "controller";
  44. private readonly MainWindow _mainWindow;
  45. private PlayerIndex _playerId;
  46. private int _controller;
  47. private int _controllerNumber = 0;
  48. private string _controllerImage;
  49. private int _device;
  50. private object _configuration;
  51. private string _profileName;
  52. private bool _isLoaded;
  53. private readonly UserControl _owner;
  54. public IGamepadDriver AvaloniaKeyboardDriver { get; }
  55. public IGamepad SelectedGamepad { get; private set; }
  56. public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
  57. public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
  58. internal ObservableCollection<ControllerModel> Controllers { get; set; }
  59. public AvaloniaList<string> ProfilesList { get; set; }
  60. public AvaloniaList<string> DeviceList { get; set; }
  61. // XAML Flags
  62. public bool ShowSettings => _device > 0;
  63. public bool IsController => _device > 1;
  64. public bool IsKeyboard => !IsController;
  65. public bool IsRight { get; set; }
  66. public bool IsLeft { get; set; }
  67. public bool IsModified { get; set; }
  68. public object Configuration
  69. {
  70. get => _configuration;
  71. set
  72. {
  73. _configuration = value;
  74. OnPropertyChanged();
  75. }
  76. }
  77. public PlayerIndex PlayerId
  78. {
  79. get => _playerId;
  80. set
  81. {
  82. if (IsModified)
  83. {
  84. return;
  85. }
  86. IsModified = false;
  87. _playerId = value;
  88. if (!Enum.IsDefined(typeof(PlayerIndex), _playerId))
  89. {
  90. _playerId = PlayerIndex.Player1;
  91. }
  92. LoadConfiguration();
  93. LoadDevice();
  94. LoadProfiles();
  95. _isLoaded = true;
  96. OnPropertyChanged();
  97. }
  98. }
  99. public int Controller
  100. {
  101. get => _controller;
  102. set
  103. {
  104. _controller = value;
  105. if (_controller == -1)
  106. {
  107. _controller = 0;
  108. }
  109. if (Controllers.Count > 0 && value < Controllers.Count && _controller > -1)
  110. {
  111. ControllerType controller = Controllers[_controller].Type;
  112. IsLeft = true;
  113. IsRight = true;
  114. switch (controller)
  115. {
  116. case ControllerType.Handheld:
  117. ControllerImage = JoyConPairResource;
  118. break;
  119. case ControllerType.ProController:
  120. ControllerImage = ProControllerResource;
  121. break;
  122. case ControllerType.JoyconPair:
  123. ControllerImage = JoyConPairResource;
  124. break;
  125. case ControllerType.JoyconLeft:
  126. ControllerImage = JoyConLeftResource;
  127. IsRight = false;
  128. break;
  129. case ControllerType.JoyconRight:
  130. ControllerImage = JoyConRightResource;
  131. IsLeft = false;
  132. break;
  133. }
  134. LoadInputDriver();
  135. LoadProfiles();
  136. }
  137. OnPropertyChanged();
  138. NotifyChanges();
  139. }
  140. }
  141. public string ControllerImage
  142. {
  143. get => _controllerImage;
  144. set
  145. {
  146. _controllerImage = value;
  147. OnPropertyChanged();
  148. OnPropertyChanged(nameof(Image));
  149. }
  150. }
  151. public SvgImage Image
  152. {
  153. get
  154. {
  155. SvgImage image = new SvgImage();
  156. if (!string.IsNullOrWhiteSpace(_controllerImage))
  157. {
  158. SvgSource source = new SvgSource();
  159. source.Load(EmbeddedResources.GetStream(_controllerImage));
  160. image.Source = source;
  161. }
  162. return image;
  163. }
  164. }
  165. public string ProfileName
  166. {
  167. get => _profileName; set
  168. {
  169. _profileName = value;
  170. OnPropertyChanged();
  171. }
  172. }
  173. public int Device
  174. {
  175. get => _device;
  176. set
  177. {
  178. _device = value < 0 ? 0 : value;
  179. if (_device >= Devices.Count)
  180. {
  181. return;
  182. }
  183. var selected = Devices[_device].Type;
  184. if (selected != DeviceType.None)
  185. {
  186. LoadControllers();
  187. if (_isLoaded)
  188. {
  189. LoadConfiguration(LoadDefaultConfiguration());
  190. }
  191. }
  192. OnPropertyChanged();
  193. NotifyChanges();
  194. }
  195. }
  196. public InputConfig Config { get; set; }
  197. public ControllerSettingsViewModel(UserControl owner) : this()
  198. {
  199. _owner = owner;
  200. if (Program.PreviewerDetached)
  201. {
  202. _mainWindow =
  203. (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
  204. .ApplicationLifetime).MainWindow;
  205. AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
  206. _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
  207. _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
  208. if (_mainWindow.ViewModel.AppHost != null)
  209. {
  210. _mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
  211. }
  212. _isLoaded = false;
  213. LoadDevices();
  214. PlayerId = PlayerIndex.Player1;
  215. }
  216. }
  217. public ControllerSettingsViewModel()
  218. {
  219. PlayerIndexes = new ObservableCollection<PlayerModel>();
  220. Controllers = new ObservableCollection<ControllerModel>();
  221. Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
  222. ProfilesList = new AvaloniaList<string>();
  223. DeviceList = new AvaloniaList<string>();
  224. ControllerImage = ProControllerResource;
  225. PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer1]));
  226. PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer2]));
  227. PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer3]));
  228. PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer4]));
  229. PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer5]));
  230. PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer6]));
  231. PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer7]));
  232. PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance[LocaleKeys.ControllerSettingsPlayer8]));
  233. PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsHandheld]));
  234. }
  235. private void LoadConfiguration(InputConfig inputConfig = null)
  236. {
  237. Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
  238. if (Config is StandardKeyboardInputConfig keyboardInputConfig)
  239. {
  240. Configuration = new InputConfiguration<Key, ConfigStickInputId>(keyboardInputConfig);
  241. }
  242. if (Config is StandardControllerInputConfig controllerInputConfig)
  243. {
  244. Configuration = new InputConfiguration<ConfigGamepadInputId, ConfigStickInputId>(controllerInputConfig);
  245. }
  246. }
  247. public void LoadDevice()
  248. {
  249. if (Config == null || Config.Backend == InputBackendType.Invalid)
  250. {
  251. Device = 0;
  252. }
  253. else
  254. {
  255. var type = DeviceType.None;
  256. if (Config is StandardKeyboardInputConfig)
  257. {
  258. type = DeviceType.Keyboard;
  259. }
  260. if (Config is StandardControllerInputConfig)
  261. {
  262. type = DeviceType.Controller;
  263. }
  264. var item = Devices.FirstOrDefault(x => x.Type == type && x.Id == Config.Id);
  265. if (item != default)
  266. {
  267. Device = Devices.ToList().FindIndex(x => x.Id == item.Id);
  268. }
  269. else
  270. {
  271. Device = 0;
  272. }
  273. }
  274. }
  275. public async void ShowMotionConfig()
  276. {
  277. await MotionSettingsWindow.Show(this);
  278. }
  279. public async void ShowRumbleConfig()
  280. {
  281. await RumbleSettingsWindow.Show(this);
  282. }
  283. private void LoadInputDriver()
  284. {
  285. if (_device < 0)
  286. {
  287. return;
  288. }
  289. string id = GetCurrentGamepadId();
  290. var type = Devices[Device].Type;
  291. if (type == DeviceType.None)
  292. {
  293. return;
  294. }
  295. else if (type == DeviceType.Keyboard)
  296. {
  297. if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
  298. {
  299. // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused...
  300. SelectedGamepad = AvaloniaKeyboardDriver.GetGamepad(id);
  301. }
  302. else
  303. {
  304. SelectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
  305. }
  306. }
  307. else
  308. {
  309. SelectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
  310. }
  311. }
  312. private void HandleOnGamepadDisconnected(string id)
  313. {
  314. Dispatcher.UIThread.Post(() =>
  315. {
  316. LoadDevices();
  317. });
  318. }
  319. private void HandleOnGamepadConnected(string id)
  320. {
  321. Dispatcher.UIThread.Post(() =>
  322. {
  323. LoadDevices();
  324. });
  325. }
  326. private string GetCurrentGamepadId()
  327. {
  328. if (_device < 0)
  329. {
  330. return string.Empty;
  331. }
  332. var device = Devices[Device];
  333. if (device.Type == DeviceType.None)
  334. {
  335. return null;
  336. }
  337. return device.Id.Split(" ")[0];
  338. }
  339. public void LoadControllers()
  340. {
  341. Controllers.Clear();
  342. if (_playerId == PlayerIndex.Handheld)
  343. {
  344. Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeHandheld]));
  345. Controller = 0;
  346. }
  347. else
  348. {
  349. Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeProController]));
  350. Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConPair]));
  351. Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConLeft]));
  352. Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance[LocaleKeys.ControllerSettingsControllerTypeJoyConRight]));
  353. if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
  354. {
  355. Controller = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
  356. }
  357. else
  358. {
  359. Controller = 0;
  360. }
  361. }
  362. }
  363. private static string GetShortGamepadName(string str)
  364. {
  365. const string Ellipsis = "...";
  366. const int MaxSize = 50;
  367. if (str.Length > MaxSize)
  368. {
  369. return $"{str.AsSpan(0, MaxSize - Ellipsis.Length)}{Ellipsis}";
  370. }
  371. return str;
  372. }
  373. private static string GetShortGamepadId(string str)
  374. {
  375. const string Hyphen = "-";
  376. const int Offset = 1;
  377. return str.Substring(str.IndexOf(Hyphen) + Offset);
  378. }
  379. public void LoadDevices()
  380. {
  381. lock (Devices)
  382. {
  383. Devices.Clear();
  384. DeviceList.Clear();
  385. Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
  386. foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
  387. {
  388. using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
  389. if (gamepad != null)
  390. {
  391. Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
  392. }
  393. }
  394. foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
  395. {
  396. using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
  397. if (gamepad != null)
  398. {
  399. if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
  400. {
  401. _controllerNumber++;
  402. }
  403. Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({_controllerNumber})"));
  404. }
  405. }
  406. _controllerNumber = 0;
  407. DeviceList.AddRange(Devices.Select(x => x.Name));
  408. Device = Math.Min(Device, DeviceList.Count);
  409. }
  410. }
  411. private string GetProfileBasePath()
  412. {
  413. string path = AppDataManager.ProfilesDirPath;
  414. var type = Devices[Device == -1 ? 0 : Device].Type;
  415. if (type == DeviceType.Keyboard)
  416. {
  417. path = Path.Combine(path, KeyboardString);
  418. }
  419. else if (type == DeviceType.Controller)
  420. {
  421. path = Path.Combine(path, ControllerString);
  422. }
  423. return path;
  424. }
  425. private void LoadProfiles()
  426. {
  427. ProfilesList.Clear();
  428. string basePath = GetProfileBasePath();
  429. if (!Directory.Exists(basePath))
  430. {
  431. Directory.CreateDirectory(basePath);
  432. }
  433. ProfilesList.Add((LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault]));
  434. foreach (string profile in Directory.GetFiles(basePath, "*.json", SearchOption.AllDirectories))
  435. {
  436. ProfilesList.Add(Path.GetFileNameWithoutExtension(profile));
  437. }
  438. if (string.IsNullOrWhiteSpace(ProfileName))
  439. {
  440. ProfileName = LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault];
  441. }
  442. }
  443. public InputConfig LoadDefaultConfiguration()
  444. {
  445. var activeDevice = Devices.FirstOrDefault();
  446. if (Devices.Count > 0 && Device < Devices.Count && Device >= 0)
  447. {
  448. activeDevice = Devices[Device];
  449. }
  450. InputConfig config;
  451. if (activeDevice.Type == DeviceType.Keyboard)
  452. {
  453. string id = activeDevice.Id;
  454. config = new StandardKeyboardInputConfig
  455. {
  456. Version = InputConfig.CurrentVersion,
  457. Backend = InputBackendType.WindowKeyboard,
  458. Id = id,
  459. ControllerType = ControllerType.ProController,
  460. LeftJoycon = new LeftJoyconCommonConfig<Key>
  461. {
  462. DpadUp = Key.Up,
  463. DpadDown = Key.Down,
  464. DpadLeft = Key.Left,
  465. DpadRight = Key.Right,
  466. ButtonMinus = Key.Minus,
  467. ButtonL = Key.E,
  468. ButtonZl = Key.Q,
  469. ButtonSl = Key.Unbound,
  470. ButtonSr = Key.Unbound
  471. },
  472. LeftJoyconStick =
  473. new JoyconConfigKeyboardStick<Key>
  474. {
  475. StickUp = Key.W,
  476. StickDown = Key.S,
  477. StickLeft = Key.A,
  478. StickRight = Key.D,
  479. StickButton = Key.F
  480. },
  481. RightJoycon = new RightJoyconCommonConfig<Key>
  482. {
  483. ButtonA = Key.Z,
  484. ButtonB = Key.X,
  485. ButtonX = Key.C,
  486. ButtonY = Key.V,
  487. ButtonPlus = Key.Plus,
  488. ButtonR = Key.U,
  489. ButtonZr = Key.O,
  490. ButtonSl = Key.Unbound,
  491. ButtonSr = Key.Unbound
  492. },
  493. RightJoyconStick = new JoyconConfigKeyboardStick<Key>
  494. {
  495. StickUp = Key.I,
  496. StickDown = Key.K,
  497. StickLeft = Key.J,
  498. StickRight = Key.L,
  499. StickButton = Key.H
  500. }
  501. };
  502. }
  503. else if (activeDevice.Type == DeviceType.Controller)
  504. {
  505. bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
  506. string id = activeDevice.Id.Split(" ")[0];
  507. config = new StandardControllerInputConfig
  508. {
  509. Version = InputConfig.CurrentVersion,
  510. Backend = InputBackendType.GamepadSDL2,
  511. Id = id,
  512. ControllerType = ControllerType.ProController,
  513. DeadzoneLeft = 0.1f,
  514. DeadzoneRight = 0.1f,
  515. RangeLeft = 1.0f,
  516. RangeRight = 1.0f,
  517. TriggerThreshold = 0.5f,
  518. LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
  519. {
  520. DpadUp = ConfigGamepadInputId.DpadUp,
  521. DpadDown = ConfigGamepadInputId.DpadDown,
  522. DpadLeft = ConfigGamepadInputId.DpadLeft,
  523. DpadRight = ConfigGamepadInputId.DpadRight,
  524. ButtonMinus = ConfigGamepadInputId.Minus,
  525. ButtonL = ConfigGamepadInputId.LeftShoulder,
  526. ButtonZl = ConfigGamepadInputId.LeftTrigger,
  527. ButtonSl = ConfigGamepadInputId.Unbound,
  528. ButtonSr = ConfigGamepadInputId.Unbound
  529. },
  530. LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
  531. {
  532. Joystick = ConfigStickInputId.Left,
  533. StickButton = ConfigGamepadInputId.LeftStick,
  534. InvertStickX = false,
  535. InvertStickY = false
  536. },
  537. RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
  538. {
  539. ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
  540. ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
  541. ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
  542. ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
  543. ButtonPlus = ConfigGamepadInputId.Plus,
  544. ButtonR = ConfigGamepadInputId.RightShoulder,
  545. ButtonZr = ConfigGamepadInputId.RightTrigger,
  546. ButtonSl = ConfigGamepadInputId.Unbound,
  547. ButtonSr = ConfigGamepadInputId.Unbound
  548. },
  549. RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
  550. {
  551. Joystick = ConfigStickInputId.Right,
  552. StickButton = ConfigGamepadInputId.RightStick,
  553. InvertStickX = false,
  554. InvertStickY = false
  555. },
  556. Motion = new StandardMotionConfigController
  557. {
  558. MotionBackend = MotionInputBackendType.GamepadDriver,
  559. EnableMotion = true,
  560. Sensitivity = 100,
  561. GyroDeadzone = 1
  562. },
  563. Rumble = new RumbleConfigController
  564. {
  565. StrongRumble = 1f,
  566. WeakRumble = 1f,
  567. EnableRumble = false
  568. }
  569. };
  570. }
  571. else
  572. {
  573. config = new InputConfig();
  574. }
  575. config.PlayerIndex = _playerId;
  576. return config;
  577. }
  578. public async void LoadProfile()
  579. {
  580. if (Device == 0)
  581. {
  582. return;
  583. }
  584. InputConfig config = null;
  585. if (string.IsNullOrWhiteSpace(ProfileName))
  586. {
  587. return;
  588. }
  589. if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
  590. {
  591. config = LoadDefaultConfiguration();
  592. }
  593. else
  594. {
  595. string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
  596. if (!File.Exists(path))
  597. {
  598. var index = ProfilesList.IndexOf(ProfileName);
  599. if (index != -1)
  600. {
  601. ProfilesList.RemoveAt(index);
  602. }
  603. return;
  604. }
  605. try
  606. {
  607. using (Stream stream = File.OpenRead(path))
  608. {
  609. config = JsonHelper.Deserialize<InputConfig>(stream);
  610. }
  611. }
  612. catch (JsonException) { }
  613. catch (InvalidOperationException)
  614. {
  615. Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
  616. await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogProfileInvalidProfileErrorMessage, ProfileName));
  617. return;
  618. }
  619. }
  620. if (config != null)
  621. {
  622. _isLoaded = false;
  623. LoadConfiguration(config);
  624. LoadDevice();
  625. _isLoaded = true;
  626. NotifyChanges();
  627. }
  628. }
  629. public async void SaveProfile()
  630. {
  631. if (Device == 0)
  632. {
  633. return;
  634. }
  635. if (Configuration == null)
  636. {
  637. return;
  638. }
  639. if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
  640. {
  641. await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
  642. return;
  643. }
  644. else
  645. {
  646. bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
  647. if (validFileName)
  648. {
  649. string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
  650. InputConfig config = null;
  651. if (IsKeyboard)
  652. {
  653. config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
  654. }
  655. else if (IsController)
  656. {
  657. config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
  658. }
  659. config.ControllerType = Controllers[_controller].Type;
  660. string jsonString = JsonHelper.Serialize(config, true);
  661. await File.WriteAllTextAsync(path, jsonString);
  662. LoadProfiles();
  663. }
  664. else
  665. {
  666. await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
  667. }
  668. }
  669. }
  670. public async void RemoveProfile()
  671. {
  672. if (Device == 0 || ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault] || ProfilesList.IndexOf(ProfileName) == -1)
  673. {
  674. return;
  675. }
  676. UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
  677. LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileTitle],
  678. LocaleManager.Instance[LocaleKeys.DialogProfileDeleteProfileMessage],
  679. LocaleManager.Instance[LocaleKeys.InputDialogYes],
  680. LocaleManager.Instance[LocaleKeys.InputDialogNo],
  681. LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
  682. if (result == UserResult.Yes)
  683. {
  684. string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
  685. if (File.Exists(path))
  686. {
  687. File.Delete(path);
  688. }
  689. LoadProfiles();
  690. }
  691. }
  692. public void Save()
  693. {
  694. IsModified = false;
  695. List<InputConfig> newConfig = new();
  696. newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
  697. newConfig.Remove(newConfig.Find(x => x == null));
  698. if (Device == 0)
  699. {
  700. newConfig.Remove(newConfig.Find(x => x.PlayerIndex == this.PlayerId));
  701. }
  702. else
  703. {
  704. var device = Devices[Device];
  705. if (device.Type == DeviceType.Keyboard)
  706. {
  707. var inputConfig = Configuration as InputConfiguration<Key, ConfigStickInputId>;
  708. inputConfig.Id = device.Id;
  709. }
  710. else
  711. {
  712. var inputConfig = Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>;
  713. inputConfig.Id = device.Id.Split(" ")[0];
  714. }
  715. var config = !IsController
  716. ? (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig()
  717. : (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
  718. config.ControllerType = Controllers[_controller].Type;
  719. config.PlayerIndex = _playerId;
  720. int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
  721. if (i == -1)
  722. {
  723. newConfig.Add(config);
  724. }
  725. else
  726. {
  727. newConfig[i] = config;
  728. }
  729. }
  730. _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
  731. // Atomically replace and signal input change.
  732. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
  733. ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
  734. ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
  735. }
  736. public void NotifyChange(string property)
  737. {
  738. OnPropertyChanged(property);
  739. }
  740. public void NotifyChanges()
  741. {
  742. OnPropertyChanged(nameof(Configuration));
  743. OnPropertyChanged(nameof(IsController));
  744. OnPropertyChanged(nameof(ShowSettings));
  745. OnPropertyChanged(nameof(IsKeyboard));
  746. OnPropertyChanged(nameof(IsRight));
  747. OnPropertyChanged(nameof(IsLeft));
  748. }
  749. public void Dispose()
  750. {
  751. _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
  752. _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
  753. _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
  754. SelectedGamepad?.Dispose();
  755. AvaloniaKeyboardDriver.Dispose();
  756. }
  757. }
  758. }