ControllerWindow.cs 46 KB

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