ControllerWindow.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. using Gtk;
  2. using OpenTK.Input;
  3. using Ryujinx.Common.Configuration;
  4. using Ryujinx.Common.Configuration.Hid;
  5. using Ryujinx.Common.Utilities;
  6. using Ryujinx.Configuration;
  7. using Ryujinx.HLE.FileSystem;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Reflection;
  12. using System.Text.Json;
  13. using System.Threading;
  14. using GUI = Gtk.Builder.ObjectAttribute;
  15. using Key = Ryujinx.Configuration.Hid.Key;
  16. namespace Ryujinx.Ui
  17. {
  18. public class ControllerWindow : Window
  19. {
  20. private PlayerIndex _playerIndex;
  21. private InputConfig _inputConfig;
  22. private bool _isWaitingForInput;
  23. private VirtualFileSystem _virtualFileSystem;
  24. #pragma warning disable CS0649, IDE0044
  25. [GUI] Adjustment _controllerDeadzoneLeft;
  26. [GUI] Adjustment _controllerDeadzoneRight;
  27. [GUI] Adjustment _controllerTriggerThreshold;
  28. [GUI] ComboBoxText _inputDevice;
  29. [GUI] ComboBoxText _profile;
  30. [GUI] ToggleButton _refreshInputDevicesButton;
  31. [GUI] Box _settingsBox;
  32. [GUI] Grid _leftStickKeyboard;
  33. [GUI] Grid _leftStickController;
  34. [GUI] Box _deadZoneLeftBox;
  35. [GUI] Grid _rightStickKeyboard;
  36. [GUI] Grid _rightStickController;
  37. [GUI] Box _deadZoneRightBox;
  38. [GUI] Grid _leftSideTriggerBox;
  39. [GUI] Grid _rightSideTriggerBox;
  40. [GUI] Box _triggerThresholdBox;
  41. [GUI] ComboBoxText _controllerType;
  42. [GUI] ToggleButton _lStickX;
  43. [GUI] CheckButton _invertLStickX;
  44. [GUI] ToggleButton _lStickY;
  45. [GUI] CheckButton _invertLStickY;
  46. [GUI] ToggleButton _lStickUp;
  47. [GUI] ToggleButton _lStickDown;
  48. [GUI] ToggleButton _lStickLeft;
  49. [GUI] ToggleButton _lStickRight;
  50. [GUI] ToggleButton _lStickButton;
  51. [GUI] ToggleButton _dpadUp;
  52. [GUI] ToggleButton _dpadDown;
  53. [GUI] ToggleButton _dpadLeft;
  54. [GUI] ToggleButton _dpadRight;
  55. [GUI] ToggleButton _minus;
  56. [GUI] ToggleButton _l;
  57. [GUI] ToggleButton _zL;
  58. [GUI] ToggleButton _rStickX;
  59. [GUI] CheckButton _invertRStickX;
  60. [GUI] ToggleButton _rStickY;
  61. [GUI] CheckButton _invertRStickY;
  62. [GUI] ToggleButton _rStickUp;
  63. [GUI] ToggleButton _rStickDown;
  64. [GUI] ToggleButton _rStickLeft;
  65. [GUI] ToggleButton _rStickRight;
  66. [GUI] ToggleButton _rStickButton;
  67. [GUI] ToggleButton _a;
  68. [GUI] ToggleButton _b;
  69. [GUI] ToggleButton _x;
  70. [GUI] ToggleButton _y;
  71. [GUI] ToggleButton _plus;
  72. [GUI] ToggleButton _r;
  73. [GUI] ToggleButton _zR;
  74. [GUI] ToggleButton _lSl;
  75. [GUI] ToggleButton _lSr;
  76. [GUI] ToggleButton _rSl;
  77. [GUI] ToggleButton _rSr;
  78. [GUI] Image _controllerImage;
  79. #pragma warning restore CS0649, IDE0044
  80. public ControllerWindow(PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : this(new Builder("Ryujinx.Ui.ControllerWindow.glade"), controllerId, virtualFileSystem) { }
  81. private ControllerWindow(Builder builder, PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_controllerWin").Handle)
  82. {
  83. builder.Autoconnect(this);
  84. this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
  85. _playerIndex = controllerId;
  86. _virtualFileSystem = virtualFileSystem;
  87. _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
  88. Title = $"Ryujinx - Controller Settings - {_playerIndex}";
  89. if (_playerIndex == PlayerIndex.Handheld)
  90. {
  91. _controllerType.Append(ControllerType.Handheld.ToString(), "Handheld");
  92. _controllerType.Sensitive = false;
  93. }
  94. else
  95. {
  96. _controllerType.Append(ControllerType.ProController.ToString(), "Pro Controller");
  97. _controllerType.Append(ControllerType.JoyconPair.ToString(), "Joycon Pair");
  98. _controllerType.Append(ControllerType.JoyconLeft.ToString(), "Joycon Left");
  99. _controllerType.Append(ControllerType.JoyconRight.ToString(), "Joycon Right");
  100. }
  101. _controllerType.Active = 0; // Set initial value to first in list.
  102. //Bind Events
  103. _lStickX.Clicked += Button_Pressed;
  104. _lStickY.Clicked += Button_Pressed;
  105. _lStickUp.Clicked += Button_Pressed;
  106. _lStickDown.Clicked += Button_Pressed;
  107. _lStickLeft.Clicked += Button_Pressed;
  108. _lStickRight.Clicked += Button_Pressed;
  109. _lStickButton.Clicked += Button_Pressed;
  110. _dpadUp.Clicked += Button_Pressed;
  111. _dpadDown.Clicked += Button_Pressed;
  112. _dpadLeft.Clicked += Button_Pressed;
  113. _dpadRight.Clicked += Button_Pressed;
  114. _minus.Clicked += Button_Pressed;
  115. _l.Clicked += Button_Pressed;
  116. _zL.Clicked += Button_Pressed;
  117. _lSl.Clicked += Button_Pressed;
  118. _lSr.Clicked += Button_Pressed;
  119. _rStickX.Clicked += Button_Pressed;
  120. _rStickY.Clicked += Button_Pressed;
  121. _rStickUp.Clicked += Button_Pressed;
  122. _rStickDown.Clicked += Button_Pressed;
  123. _rStickLeft.Clicked += Button_Pressed;
  124. _rStickRight.Clicked += Button_Pressed;
  125. _rStickButton.Clicked += Button_Pressed;
  126. _a.Clicked += Button_Pressed;
  127. _b.Clicked += Button_Pressed;
  128. _x.Clicked += Button_Pressed;
  129. _y.Clicked += Button_Pressed;
  130. _plus.Clicked += Button_Pressed;
  131. _r.Clicked += Button_Pressed;
  132. _zR.Clicked += Button_Pressed;
  133. _rSl.Clicked += Button_Pressed;
  134. _rSr.Clicked += Button_Pressed;
  135. // Setup current values
  136. UpdateInputDeviceList();
  137. SetAvailableOptions();
  138. ClearValues();
  139. if (_inputDevice.ActiveId != null) SetCurrentValues();
  140. }
  141. private void UpdateInputDeviceList()
  142. {
  143. _inputDevice.RemoveAll();
  144. _inputDevice.Append("disabled", "Disabled");
  145. _inputDevice.SetActiveId("disabled");
  146. _inputDevice.Append($"keyboard/{KeyboardConfig.AllKeyboardsIndex}", "All keyboards");
  147. for (int i = 0; i < 20; i++)
  148. {
  149. if (KeyboardController.GetKeyboardState(i + 1).IsConnected)
  150. _inputDevice.Append($"keyboard/{i + 1}", $"Keyboard/{i + 1}");
  151. if (GamePad.GetState(i).IsConnected)
  152. _inputDevice.Append($"controller/{i}", $"Controller/{i} ({GamePad.GetName(i)})");
  153. }
  154. switch (_inputConfig)
  155. {
  156. case KeyboardConfig keyboard:
  157. _inputDevice.SetActiveId($"keyboard/{keyboard.Index}");
  158. break;
  159. case ControllerConfig controller:
  160. _inputDevice.SetActiveId($"controller/{controller.Index}");
  161. break;
  162. }
  163. }
  164. private void SetAvailableOptions()
  165. {
  166. if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard"))
  167. {
  168. this.ShowAll();
  169. _leftStickController.Hide();
  170. _rightStickController.Hide();
  171. _deadZoneLeftBox.Hide();
  172. _deadZoneRightBox.Hide();
  173. _triggerThresholdBox.Hide();
  174. }
  175. else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller"))
  176. {
  177. this.ShowAll();
  178. _leftStickKeyboard.Hide();
  179. _rightStickKeyboard.Hide();
  180. }
  181. else
  182. {
  183. _settingsBox.Hide();
  184. }
  185. ClearValues();
  186. }
  187. private void SetCurrentValues()
  188. {
  189. SetControllerSpecificFields();
  190. SetProfiles();
  191. if (_inputDevice.ActiveId.StartsWith("keyboard") && _inputConfig is KeyboardConfig)
  192. {
  193. SetValues(_inputConfig);
  194. }
  195. else if (_inputDevice.ActiveId.StartsWith("controller") && _inputConfig is ControllerConfig)
  196. {
  197. SetValues(_inputConfig);
  198. }
  199. }
  200. private void SetControllerSpecificFields()
  201. {
  202. _leftSideTriggerBox.Hide();
  203. _rightSideTriggerBox.Hide();
  204. switch (_controllerType.ActiveId)
  205. {
  206. case "JoyconLeft":
  207. _leftSideTriggerBox.Show();
  208. break;
  209. case "JoyconRight":
  210. _rightSideTriggerBox.Show();
  211. break;
  212. }
  213. switch (_controllerType.ActiveId)
  214. {
  215. case "ProController":
  216. _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.ProCon.svg", 400, 400);
  217. break;
  218. case "JoyconLeft":
  219. _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConLeft.svg", 400, 400);
  220. break;
  221. case "JoyconRight":
  222. _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConRight.svg", 400, 400);
  223. break;
  224. default:
  225. _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConPair.svg", 400, 400);
  226. break;
  227. }
  228. }
  229. private void ClearValues()
  230. {
  231. _lStickX.Label = "Unbound";
  232. _lStickY.Label = "Unbound";
  233. _lStickUp.Label = "Unbound";
  234. _lStickDown.Label = "Unbound";
  235. _lStickLeft.Label = "Unbound";
  236. _lStickRight.Label = "Unbound";
  237. _lStickButton.Label = "Unbound";
  238. _dpadUp.Label = "Unbound";
  239. _dpadDown.Label = "Unbound";
  240. _dpadLeft.Label = "Unbound";
  241. _dpadRight.Label = "Unbound";
  242. _minus.Label = "Unbound";
  243. _l.Label = "Unbound";
  244. _zL.Label = "Unbound";
  245. _lSl.Label = "Unbound";
  246. _lSr.Label = "Unbound";
  247. _rStickX.Label = "Unbound";
  248. _rStickY.Label = "Unbound";
  249. _rStickUp.Label = "Unbound";
  250. _rStickDown.Label = "Unbound";
  251. _rStickLeft.Label = "Unbound";
  252. _rStickRight.Label = "Unbound";
  253. _rStickButton.Label = "Unbound";
  254. _a.Label = "Unbound";
  255. _b.Label = "Unbound";
  256. _x.Label = "Unbound";
  257. _y.Label = "Unbound";
  258. _plus.Label = "Unbound";
  259. _r.Label = "Unbound";
  260. _zR.Label = "Unbound";
  261. _rSl.Label = "Unbound";
  262. _rSr.Label = "Unbound";
  263. _controllerDeadzoneLeft.Value = 0;
  264. _controllerDeadzoneRight.Value = 0;
  265. _controllerTriggerThreshold.Value = 0;
  266. }
  267. private void SetValues(InputConfig config)
  268. {
  269. switch (config)
  270. {
  271. case KeyboardConfig keyboardConfig:
  272. if (!_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString()))
  273. {
  274. _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld
  275. ? ControllerType.Handheld.ToString()
  276. : ControllerType.ProController.ToString());
  277. }
  278. _lStickUp.Label = keyboardConfig.LeftJoycon.StickUp.ToString();
  279. _lStickDown.Label = keyboardConfig.LeftJoycon.StickDown.ToString();
  280. _lStickLeft.Label = keyboardConfig.LeftJoycon.StickLeft.ToString();
  281. _lStickRight.Label = keyboardConfig.LeftJoycon.StickRight.ToString();
  282. _lStickButton.Label = keyboardConfig.LeftJoycon.StickButton.ToString();
  283. _dpadUp.Label = keyboardConfig.LeftJoycon.DPadUp.ToString();
  284. _dpadDown.Label = keyboardConfig.LeftJoycon.DPadDown.ToString();
  285. _dpadLeft.Label = keyboardConfig.LeftJoycon.DPadLeft.ToString();
  286. _dpadRight.Label = keyboardConfig.LeftJoycon.DPadRight.ToString();
  287. _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString();
  288. _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString();
  289. _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString();
  290. _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString();
  291. _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString();
  292. _rStickUp.Label = keyboardConfig.RightJoycon.StickUp.ToString();
  293. _rStickDown.Label = keyboardConfig.RightJoycon.StickDown.ToString();
  294. _rStickLeft.Label = keyboardConfig.RightJoycon.StickLeft.ToString();
  295. _rStickRight.Label = keyboardConfig.RightJoycon.StickRight.ToString();
  296. _rStickButton.Label = keyboardConfig.RightJoycon.StickButton.ToString();
  297. _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString();
  298. _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString();
  299. _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString();
  300. _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString();
  301. _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString();
  302. _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString();
  303. _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString();
  304. _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString();
  305. _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString();
  306. break;
  307. case ControllerConfig controllerConfig:
  308. if (!_controllerType.SetActiveId(controllerConfig.ControllerType.ToString()))
  309. {
  310. _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld
  311. ? ControllerType.Handheld.ToString()
  312. : ControllerType.ProController.ToString());
  313. }
  314. _lStickX.Label = controllerConfig.LeftJoycon.StickX.ToString();
  315. _invertLStickX.Active = controllerConfig.LeftJoycon.InvertStickX;
  316. _lStickY.Label = controllerConfig.LeftJoycon.StickY.ToString();
  317. _invertLStickY.Active = controllerConfig.LeftJoycon.InvertStickY;
  318. _lStickButton.Label = controllerConfig.LeftJoycon.StickButton.ToString();
  319. _dpadUp.Label = controllerConfig.LeftJoycon.DPadUp.ToString();
  320. _dpadDown.Label = controllerConfig.LeftJoycon.DPadDown.ToString();
  321. _dpadLeft.Label = controllerConfig.LeftJoycon.DPadLeft.ToString();
  322. _dpadRight.Label = controllerConfig.LeftJoycon.DPadRight.ToString();
  323. _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString();
  324. _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString();
  325. _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString();
  326. _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString();
  327. _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString();
  328. _rStickX.Label = controllerConfig.RightJoycon.StickX.ToString();
  329. _invertRStickX.Active = controllerConfig.RightJoycon.InvertStickX;
  330. _rStickY.Label = controllerConfig.RightJoycon.StickY.ToString();
  331. _invertRStickY.Active = controllerConfig.RightJoycon.InvertStickY;
  332. _rStickButton.Label = controllerConfig.RightJoycon.StickButton.ToString();
  333. _a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
  334. _b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
  335. _x.Label = controllerConfig.RightJoycon.ButtonX.ToString();
  336. _y.Label = controllerConfig.RightJoycon.ButtonY.ToString();
  337. _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString();
  338. _r.Label = controllerConfig.RightJoycon.ButtonR.ToString();
  339. _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString();
  340. _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString();
  341. _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString();
  342. _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft;
  343. _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight;
  344. _controllerTriggerThreshold.Value = controllerConfig.TriggerThreshold;
  345. break;
  346. }
  347. }
  348. private InputConfig GetValues()
  349. {
  350. if (_inputDevice.ActiveId.StartsWith("keyboard"))
  351. {
  352. Enum.TryParse(_lStickUp.Label, out Key lStickUp);
  353. Enum.TryParse(_lStickDown.Label, out Key lStickDown);
  354. Enum.TryParse(_lStickLeft.Label, out Key lStickLeft);
  355. Enum.TryParse(_lStickRight.Label, out Key lStickRight);
  356. Enum.TryParse(_lStickButton.Label, out Key lStickButton);
  357. Enum.TryParse(_dpadUp.Label, out Key lDPadUp);
  358. Enum.TryParse(_dpadDown.Label, out Key lDPadDown);
  359. Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft);
  360. Enum.TryParse(_dpadRight.Label, out Key lDPadRight);
  361. Enum.TryParse(_minus.Label, out Key lButtonMinus);
  362. Enum.TryParse(_l.Label, out Key lButtonL);
  363. Enum.TryParse(_zL.Label, out Key lButtonZl);
  364. Enum.TryParse(_lSl.Label, out Key lButtonSl);
  365. Enum.TryParse(_lSr.Label, out Key lButtonSr);
  366. Enum.TryParse(_rStickUp.Label, out Key rStickUp);
  367. Enum.TryParse(_rStickDown.Label, out Key rStickDown);
  368. Enum.TryParse(_rStickLeft.Label, out Key rStickLeft);
  369. Enum.TryParse(_rStickRight.Label, out Key rStickRight);
  370. Enum.TryParse(_rStickButton.Label, out Key rStickButton);
  371. Enum.TryParse(_a.Label, out Key rButtonA);
  372. Enum.TryParse(_b.Label, out Key rButtonB);
  373. Enum.TryParse(_x.Label, out Key rButtonX);
  374. Enum.TryParse(_y.Label, out Key rButtonY);
  375. Enum.TryParse(_plus.Label, out Key rButtonPlus);
  376. Enum.TryParse(_r.Label, out Key rButtonR);
  377. Enum.TryParse(_zR.Label, out Key rButtonZr);
  378. Enum.TryParse(_rSl.Label, out Key rButtonSl);
  379. Enum.TryParse(_rSr.Label, out Key rButtonSr);
  380. return new KeyboardConfig
  381. {
  382. Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
  383. ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId),
  384. PlayerIndex = _playerIndex,
  385. LeftJoycon = new NpadKeyboardLeft
  386. {
  387. StickUp = lStickUp,
  388. StickDown = lStickDown,
  389. StickLeft = lStickLeft,
  390. StickRight = lStickRight,
  391. StickButton = lStickButton,
  392. DPadUp = lDPadUp,
  393. DPadDown = lDPadDown,
  394. DPadLeft = lDPadLeft,
  395. DPadRight = lDPadRight,
  396. ButtonMinus = lButtonMinus,
  397. ButtonL = lButtonL,
  398. ButtonZl = lButtonZl,
  399. ButtonSl = lButtonSl,
  400. ButtonSr = lButtonSr
  401. },
  402. RightJoycon = new NpadKeyboardRight
  403. {
  404. StickUp = rStickUp,
  405. StickDown = rStickDown,
  406. StickLeft = rStickLeft,
  407. StickRight = rStickRight,
  408. StickButton = rStickButton,
  409. ButtonA = rButtonA,
  410. ButtonB = rButtonB,
  411. ButtonX = rButtonX,
  412. ButtonY = rButtonY,
  413. ButtonPlus = rButtonPlus,
  414. ButtonR = rButtonR,
  415. ButtonZr = rButtonZr,
  416. ButtonSl = rButtonSl,
  417. ButtonSr = rButtonSr
  418. }
  419. };
  420. }
  421. if (_inputDevice.ActiveId.StartsWith("controller"))
  422. {
  423. Enum.TryParse(_lStickX.Label, out ControllerInputId lStickX);
  424. Enum.TryParse(_lStickY.Label, out ControllerInputId lStickY);
  425. Enum.TryParse(_lStickButton.Label, out ControllerInputId lStickButton);
  426. Enum.TryParse(_minus.Label, out ControllerInputId lButtonMinus);
  427. Enum.TryParse(_l.Label, out ControllerInputId lButtonL);
  428. Enum.TryParse(_zL.Label, out ControllerInputId lButtonZl);
  429. Enum.TryParse(_lSl.Label, out ControllerInputId lButtonSl);
  430. Enum.TryParse(_lSr.Label, out ControllerInputId lButtonSr);
  431. Enum.TryParse(_dpadUp.Label, out ControllerInputId lDPadUp);
  432. Enum.TryParse(_dpadDown.Label, out ControllerInputId lDPadDown);
  433. Enum.TryParse(_dpadLeft.Label, out ControllerInputId lDPadLeft);
  434. Enum.TryParse(_dpadRight.Label, out ControllerInputId lDPadRight);
  435. Enum.TryParse(_rStickX.Label, out ControllerInputId rStickX);
  436. Enum.TryParse(_rStickY.Label, out ControllerInputId rStickY);
  437. Enum.TryParse(_rStickButton.Label, out ControllerInputId rStickButton);
  438. Enum.TryParse(_a.Label, out ControllerInputId rButtonA);
  439. Enum.TryParse(_b.Label, out ControllerInputId rButtonB);
  440. Enum.TryParse(_x.Label, out ControllerInputId rButtonX);
  441. Enum.TryParse(_y.Label, out ControllerInputId rButtonY);
  442. Enum.TryParse(_plus.Label, out ControllerInputId rButtonPlus);
  443. Enum.TryParse(_r.Label, out ControllerInputId rButtonR);
  444. Enum.TryParse(_zR.Label, out ControllerInputId rButtonZr);
  445. Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl);
  446. Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr);
  447. return new ControllerConfig
  448. {
  449. Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
  450. ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId),
  451. PlayerIndex = _playerIndex,
  452. DeadzoneLeft = (float)_controllerDeadzoneLeft.Value,
  453. DeadzoneRight = (float)_controllerDeadzoneRight.Value,
  454. TriggerThreshold = (float)_controllerTriggerThreshold.Value,
  455. LeftJoycon = new NpadControllerLeft
  456. {
  457. InvertStickX = _invertLStickX.Active,
  458. StickX = lStickX,
  459. InvertStickY = _invertLStickY.Active,
  460. StickY = lStickY,
  461. StickButton = lStickButton,
  462. ButtonMinus = lButtonMinus,
  463. ButtonL = lButtonL,
  464. ButtonZl = lButtonZl,
  465. ButtonSl = lButtonSl,
  466. ButtonSr = lButtonSr,
  467. DPadUp = lDPadUp,
  468. DPadDown = lDPadDown,
  469. DPadLeft = lDPadLeft,
  470. DPadRight = lDPadRight
  471. },
  472. RightJoycon = new NpadControllerRight
  473. {
  474. InvertStickX = _invertRStickX.Active,
  475. StickX = rStickX,
  476. InvertStickY = _invertRStickY.Active,
  477. StickY = rStickY,
  478. StickButton = rStickButton,
  479. ButtonA = rButtonA,
  480. ButtonB = rButtonB,
  481. ButtonX = rButtonX,
  482. ButtonY = rButtonY,
  483. ButtonPlus = rButtonPlus,
  484. ButtonR = rButtonR,
  485. ButtonZr = rButtonZr,
  486. ButtonSl = rButtonSl,
  487. ButtonSr = rButtonSr
  488. }
  489. };
  490. }
  491. if (!_inputDevice.ActiveId.StartsWith("disabled"))
  492. {
  493. GtkDialog.CreateErrorDialog("Some fields entered where invalid and therefore your config was not saved.");
  494. }
  495. return null;
  496. }
  497. private static bool IsAnyKeyPressed(out Key pressedKey, int index)
  498. {
  499. KeyboardState keyboardState = KeyboardController.GetKeyboardState(index);
  500. foreach (Key key in Enum.GetValues(typeof(Key)))
  501. {
  502. if (keyboardState.IsKeyDown((OpenTK.Input.Key)key))
  503. {
  504. pressedKey = key;
  505. return true;
  506. }
  507. }
  508. pressedKey = Key.Unbound;
  509. return false;
  510. }
  511. private static bool IsAnyButtonPressed(out ControllerInputId pressedButton, int index, double triggerThreshold)
  512. {
  513. JoystickState joystickState = Joystick.GetState(index);
  514. JoystickCapabilities joystickCapabilities = Joystick.GetCapabilities(index);
  515. //Buttons
  516. for (int i = 0; i != joystickCapabilities.ButtonCount; i++)
  517. {
  518. if (joystickState.IsButtonDown(i))
  519. {
  520. Enum.TryParse($"Button{i}", out pressedButton);
  521. return true;
  522. }
  523. }
  524. //Axis
  525. for (int i = 0; i != joystickCapabilities.AxisCount; i++)
  526. {
  527. if (joystickState.GetAxis(i) > 0.5f && joystickState.GetAxis(i) > triggerThreshold)
  528. {
  529. Enum.TryParse($"Axis{i}", out pressedButton);
  530. return true;
  531. }
  532. }
  533. //Hats
  534. for (int i = 0; i != joystickCapabilities.HatCount; i++)
  535. {
  536. JoystickHatState hatState = joystickState.GetHat((JoystickHat)i);
  537. string pos = null;
  538. if (hatState.IsUp) pos = "Up";
  539. if (hatState.IsDown) pos = "Down";
  540. if (hatState.IsLeft) pos = "Left";
  541. if (hatState.IsRight) pos = "Right";
  542. if (pos == null) continue;
  543. Enum.TryParse($"Hat{i}{pos}", out pressedButton);
  544. return true;
  545. }
  546. pressedButton = ControllerInputId.Unbound;
  547. return false;
  548. }
  549. private string GetProfileBasePath()
  550. {
  551. string path = AppDataManager.ProfilesDirPath;
  552. if (_inputDevice.ActiveId.StartsWith("keyboard"))
  553. {
  554. path = System.IO.Path.Combine(path, "keyboard");
  555. }
  556. else if (_inputDevice.ActiveId.StartsWith("controller"))
  557. {
  558. path = System.IO.Path.Combine(path, "controller");
  559. }
  560. return path;
  561. }
  562. //Events
  563. private void InputDevice_Changed(object sender, EventArgs args)
  564. {
  565. SetAvailableOptions();
  566. SetControllerSpecificFields();
  567. if (_inputDevice.ActiveId != null) SetProfiles();
  568. }
  569. private void Controller_Changed(object sender, EventArgs args)
  570. {
  571. SetControllerSpecificFields();
  572. }
  573. private void RefreshInputDevicesButton_Pressed(object sender, EventArgs args)
  574. {
  575. UpdateInputDeviceList();
  576. _refreshInputDevicesButton.SetStateFlags(0, true);
  577. }
  578. private void Button_Pressed(object sender, EventArgs args)
  579. {
  580. if (_isWaitingForInput)
  581. {
  582. return;
  583. }
  584. _isWaitingForInput = true;
  585. Thread inputThread = new Thread(() =>
  586. {
  587. Button button = (ToggleButton)sender;
  588. if (_inputDevice.ActiveId.StartsWith("keyboard"))
  589. {
  590. Key pressedKey;
  591. int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
  592. while (!IsAnyKeyPressed(out pressedKey, index))
  593. {
  594. if (Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsKeyDown(OpenTK.Input.Key.Escape))
  595. {
  596. Application.Invoke(delegate
  597. {
  598. button.SetStateFlags(0, true);
  599. });
  600. _isWaitingForInput = false;
  601. return;
  602. }
  603. }
  604. Application.Invoke(delegate
  605. {
  606. button.Label = pressedKey.ToString();
  607. button.SetStateFlags(0, true);
  608. });
  609. }
  610. else if (_inputDevice.ActiveId.StartsWith("controller"))
  611. {
  612. ControllerInputId pressedButton;
  613. int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
  614. while (!IsAnyButtonPressed(out pressedButton, index, _controllerTriggerThreshold.Value))
  615. {
  616. if (Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsAnyKeyDown)
  617. {
  618. Application.Invoke(delegate
  619. {
  620. button.SetStateFlags(0, true);
  621. });
  622. _isWaitingForInput = false;
  623. return;
  624. }
  625. }
  626. Application.Invoke(delegate
  627. {
  628. button.Label = pressedButton.ToString();
  629. button.SetStateFlags(0, true);
  630. });
  631. }
  632. _isWaitingForInput = false;
  633. });
  634. inputThread.Name = "GUI.InputThread";
  635. inputThread.IsBackground = true;
  636. inputThread.Start();
  637. }
  638. private void SetProfiles()
  639. {
  640. string basePath = GetProfileBasePath();
  641. if (!Directory.Exists(basePath))
  642. {
  643. Directory.CreateDirectory(basePath);
  644. }
  645. _profile.RemoveAll();
  646. _profile.Append("default", "Default");
  647. foreach (string profile in Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories))
  648. {
  649. _profile.Append(System.IO.Path.GetFileName(profile), System.IO.Path.GetFileNameWithoutExtension(profile));
  650. }
  651. }
  652. private void ProfileLoad_Activated(object sender, EventArgs args)
  653. {
  654. ((ToggleButton)sender).SetStateFlags(0, true);
  655. if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) return;
  656. InputConfig config = null;
  657. int pos = _profile.Active;
  658. if (_profile.ActiveId == "default")
  659. {
  660. if (_inputDevice.ActiveId.StartsWith("keyboard"))
  661. {
  662. config = new KeyboardConfig
  663. {
  664. Index = 0,
  665. ControllerType = ControllerType.JoyconPair,
  666. LeftJoycon = new NpadKeyboardLeft
  667. {
  668. StickUp = Key.W,
  669. StickDown = Key.S,
  670. StickLeft = Key.A,
  671. StickRight = Key.D,
  672. StickButton = Key.F,
  673. DPadUp = Key.Up,
  674. DPadDown = Key.Down,
  675. DPadLeft = Key.Left,
  676. DPadRight = Key.Right,
  677. ButtonMinus = Key.Minus,
  678. ButtonL = Key.E,
  679. ButtonZl = Key.Q,
  680. ButtonSl = Key.Unbound,
  681. ButtonSr = Key.Unbound
  682. },
  683. RightJoycon = new NpadKeyboardRight
  684. {
  685. StickUp = Key.I,
  686. StickDown = Key.K,
  687. StickLeft = Key.J,
  688. StickRight = Key.L,
  689. StickButton = Key.H,
  690. ButtonA = Key.Z,
  691. ButtonB = Key.X,
  692. ButtonX = Key.C,
  693. ButtonY = Key.V,
  694. ButtonPlus = Key.Plus,
  695. ButtonR = Key.U,
  696. ButtonZr = Key.O,
  697. ButtonSl = Key.Unbound,
  698. ButtonSr = Key.Unbound
  699. }
  700. };
  701. }
  702. else if (_inputDevice.ActiveId.StartsWith("controller"))
  703. {
  704. config = new ControllerConfig
  705. {
  706. Index = 0,
  707. ControllerType = ControllerType.ProController,
  708. DeadzoneLeft = 0.1f,
  709. DeadzoneRight = 0.1f,
  710. TriggerThreshold = 0.5f,
  711. LeftJoycon = new NpadControllerLeft
  712. {
  713. StickX = ControllerInputId.Axis0,
  714. StickY = ControllerInputId.Axis1,
  715. StickButton = ControllerInputId.Button8,
  716. DPadUp = ControllerInputId.Hat0Up,
  717. DPadDown = ControllerInputId.Hat0Down,
  718. DPadLeft = ControllerInputId.Hat0Left,
  719. DPadRight = ControllerInputId.Hat0Right,
  720. ButtonMinus = ControllerInputId.Button6,
  721. ButtonL = ControllerInputId.Button4,
  722. ButtonZl = ControllerInputId.Axis2,
  723. ButtonSl = ControllerInputId.Unbound,
  724. ButtonSr = ControllerInputId.Unbound,
  725. InvertStickX = false,
  726. InvertStickY = false
  727. },
  728. RightJoycon = new NpadControllerRight
  729. {
  730. StickX = ControllerInputId.Axis3,
  731. StickY = ControllerInputId.Axis4,
  732. StickButton = ControllerInputId.Button9,
  733. ButtonA = ControllerInputId.Button1,
  734. ButtonB = ControllerInputId.Button0,
  735. ButtonX = ControllerInputId.Button3,
  736. ButtonY = ControllerInputId.Button2,
  737. ButtonPlus = ControllerInputId.Button7,
  738. ButtonR = ControllerInputId.Button5,
  739. ButtonZr = ControllerInputId.Axis5,
  740. ButtonSl = ControllerInputId.Unbound,
  741. ButtonSr = ControllerInputId.Unbound,
  742. InvertStickX = false,
  743. InvertStickY = false
  744. }
  745. };
  746. }
  747. }
  748. else
  749. {
  750. string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId);
  751. if (!File.Exists(path))
  752. {
  753. if (pos >= 0)
  754. {
  755. _profile.Remove(pos);
  756. }
  757. return;
  758. }
  759. try
  760. {
  761. using (Stream stream = File.OpenRead(path))
  762. {
  763. config = JsonHelper.Deserialize<ControllerConfig>(stream);
  764. }
  765. }
  766. catch (JsonException)
  767. {
  768. try
  769. {
  770. using (Stream stream = File.OpenRead(path))
  771. {
  772. config = JsonHelper.Deserialize<KeyboardConfig>(stream);
  773. }
  774. }
  775. catch { }
  776. }
  777. }
  778. SetValues(config);
  779. }
  780. private void ProfileAdd_Activated(object sender, EventArgs args)
  781. {
  782. ((ToggleButton)sender).SetStateFlags(0, true);
  783. if (_inputDevice.ActiveId == "disabled") return;
  784. InputConfig inputConfig = GetValues();
  785. ProfileDialog profileDialog = new ProfileDialog();
  786. if (inputConfig == null) return;
  787. if (profileDialog.Run() == (int)ResponseType.Ok)
  788. {
  789. string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName);
  790. string jsonString;
  791. if (inputConfig is KeyboardConfig keyboardConfig)
  792. {
  793. jsonString = JsonHelper.Serialize(keyboardConfig, true);
  794. }
  795. else
  796. {
  797. jsonString = JsonHelper.Serialize(inputConfig as ControllerConfig, true);
  798. }
  799. File.WriteAllText(path, jsonString);
  800. }
  801. profileDialog.Dispose();
  802. SetProfiles();
  803. }
  804. private void ProfileRemove_Activated(object sender, EventArgs args)
  805. {
  806. ((ToggleButton) sender).SetStateFlags(0, true);
  807. if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) return;
  808. MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are your sure you want to continue?");
  809. if (confirmDialog.Run() == (int)ResponseType.Yes)
  810. {
  811. string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId);
  812. if (File.Exists(path))
  813. {
  814. File.Delete(path);
  815. }
  816. SetProfiles();
  817. }
  818. }
  819. private void SaveToggle_Activated(object sender, EventArgs args)
  820. {
  821. InputConfig inputConfig = GetValues();
  822. var newConfig = new List<InputConfig>();
  823. newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
  824. if (_inputConfig == null && inputConfig != null)
  825. {
  826. newConfig.Add(inputConfig);
  827. }
  828. else
  829. {
  830. if (_inputDevice.ActiveId == "disabled")
  831. {
  832. newConfig.Remove(_inputConfig);
  833. }
  834. else if (inputConfig != null)
  835. {
  836. int index = newConfig.IndexOf(_inputConfig);
  837. newConfig[index] = inputConfig;
  838. }
  839. }
  840. // Atomically replace and signal input change.
  841. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
  842. ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
  843. MainWindow.SaveConfig();
  844. Dispose();
  845. }
  846. private void CloseToggle_Activated(object sender, EventArgs args)
  847. {
  848. Dispose();
  849. }
  850. }
  851. }