diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index 29520cdf0c..ea80bac519 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -326,5 +326,7 @@ namespace BizHawk.Client.Common // ReSharper disable once UnusedMember.Global public string LastWrittenFromDetailed { get; set; } = VersionInfo.GetEmuVersion(); + + public EHostInputMethod HostInputMethod { get; set; } = EHostInputMethod.OpenTK; } } \ No newline at end of file diff --git a/BizHawk.Client.Common/config/ConfigEnums.cs b/BizHawk.Client.Common/config/ConfigEnums.cs index c94fac86e5..48c40971c5 100644 --- a/BizHawk.Client.Common/config/ConfigEnums.cs +++ b/BizHawk.Client.Common/config/ConfigEnums.cs @@ -40,4 +40,10 @@ Tas = 3, N64Tas = 4 } + + public enum EHostInputMethod + { + OpenTK = 0, + DirectInput = 1 + } } diff --git a/BizHawk.Client.EmuHawk/Input/GamePad.cs b/BizHawk.Client.EmuHawk/Input/GamePad.cs index 497acfb33d..45bcb1a8f9 100644 --- a/BizHawk.Client.EmuHawk/Input/GamePad.cs +++ b/BizHawk.Client.EmuHawk/Input/GamePad.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Windows.Forms; using BizHawk.Common; @@ -15,7 +14,7 @@ namespace BizHawk.Client.EmuHawk private static readonly List Devices = new List(); private static DirectInput _directInput; - public static void Initialize(Control parent) + public static void Initialize(IntPtr mainFormHandle) { lock (SyncObj) { @@ -31,7 +30,7 @@ namespace BizHawk.Client.EmuHawk continue; // Don't input XBOX 360 controllers into here; we'll process them via XInput (there are limitations in some trigger axes when xbox pads go over xinput) var joystick = new Joystick(_directInput, device.InstanceGuid); - joystick.SetCooperativeLevel(parent.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); + joystick.SetCooperativeLevel(mainFormHandle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); foreach (DeviceObjectInstance deviceObject in joystick.GetObjects()) { if ((deviceObject.ObjectType & ObjectDeviceType.Axis) != 0) @@ -125,7 +124,7 @@ namespace BizHawk.Client.EmuHawk return; } - public IEnumerable<(string AxisID, float Value)> GetFloats() + public IEnumerable<(string AxisID, float Value)> GetAxes() { var pis = typeof(JoystickState).GetProperties(); foreach (var pi in pis) diff --git a/BizHawk.Client.EmuHawk/Input/GamePad360.cs b/BizHawk.Client.EmuHawk/Input/GamePad360.cs index be2a4ec8d9..c0bcd1ac21 100644 --- a/BizHawk.Client.EmuHawk/Input/GamePad360.cs +++ b/BizHawk.Client.EmuHawk/Input/GamePad360.cs @@ -154,7 +154,7 @@ namespace BizHawk.Client.EmuHawk } } - public IEnumerable<(string AxisID, float Value)> GetFloats() + public IEnumerable<(string AxisID, float Value)> GetAxes() { var g = _state.Gamepad; diff --git a/BizHawk.Client.EmuHawk/Input/HostInputAdapters.cs b/BizHawk.Client.EmuHawk/Input/HostInputAdapters.cs new file mode 100644 index 0000000000..13f80510b6 --- /dev/null +++ b/BizHawk.Client.EmuHawk/Input/HostInputAdapters.cs @@ -0,0 +1,98 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; + +using static BizHawk.Client.EmuHawk.Input; + +namespace BizHawk.Client.EmuHawk +{ + /// this was easier than trying to make static classes instantiable... + public interface HostInputAdapter + { + void DeInitAll(); + + void FirstInitAll(IntPtr mainFormHandle); + + void ReInitGamepads(IntPtr mainFormHandle); + + void PreprocessHostGamepads(); + + void ProcessHostGamepads(Action handleButton, Action handleAxis); + + IEnumerable ProcessHostKeyboards(); + } + + internal sealed class DirectInputAdapter : HostInputAdapter + { + public void DeInitAll() + { + KeyInput.Cleanup(); + GamePad.Cleanup(); + } + + public void FirstInitAll(IntPtr mainFormHandle) + { + KeyInput.Initialize(mainFormHandle); + IPCKeyInput.Initialize(); + ReInitGamepads(mainFormHandle); + } + + public void ReInitGamepads(IntPtr mainFormHandle) + { + GamePad.Initialize(mainFormHandle); + GamePad360.Initialize(); + } + + public void PreprocessHostGamepads() + { + GamePad.UpdateAll(); + GamePad360.UpdateAll(); + } + + public void ProcessHostGamepads(Action handleButton, Action handleAxis) + { + foreach (var pad in GamePad360.EnumerateDevices()) + { + var inputNamePrefix = $"X{pad.PlayerNumber} "; + for (int b = 0, n = pad.NumButtons; b < n; b++) handleButton(inputNamePrefix + pad.ButtonName(b), pad.Pressed(b), InputFocus.Pad); + foreach (var (axisName, f) in pad.GetAxes()) handleAxis(inputNamePrefix + axisName, f); + } + foreach (var pad in GamePad.EnumerateDevices()) + { + var inputNamePrefix = $"J{pad.PlayerNumber} "; + for (int b = 0, n = pad.NumButtons; b < n; b++) handleButton(inputNamePrefix + pad.ButtonName(b), pad.Pressed(b), InputFocus.Pad); + foreach (var (axisName, f) in pad.GetAxes()) handleAxis(inputNamePrefix + axisName, f); + } + } + + public IEnumerable ProcessHostKeyboards() => KeyInput.Update().Concat(IPCKeyInput.Update()); + } + + internal sealed class OpenTKInputAdapter : HostInputAdapter + { + public void DeInitAll() {} + + public void FirstInitAll(IntPtr mainFormHandle) + { + OTK_Keyboard.Initialize(); + OTK_GamePad.Initialize(); + } + + public void ReInitGamepads(IntPtr mainFormHandle) {} + + public void PreprocessHostGamepads() => OTK_GamePad.UpdateAll(); + + public void ProcessHostGamepads(Action handleButton, Action handleAxis) + { + foreach (var pad in OTK_GamePad.EnumerateDevices()) + { + foreach (var but in pad.buttonObjects) handleButton(pad.InputNamePrefix + but.ButtonName, but.ButtonAction(), InputFocus.Pad); + foreach (var (axisID, f) in pad.GetAxes()) handleAxis($"{pad.InputNamePrefix}{axisID} Axis", f); + } + } + + public IEnumerable ProcessHostKeyboards() => OTK_Keyboard.Update(); + } +} diff --git a/BizHawk.Client.EmuHawk/Input/Input.cs b/BizHawk.Client.EmuHawk/Input/Input.cs index f044a8ab36..584f54f12d 100644 --- a/BizHawk.Client.EmuHawk/Input/Input.cs +++ b/BizHawk.Client.EmuHawk/Input/Input.cs @@ -114,9 +114,19 @@ namespace BizHawk.Client.EmuHawk Alt = 262144 } - public static Input Instance { get; private set; } + private static readonly Lazy _instance = new Lazy(() => new Input()); + + public static Input Instance => _instance.Value; + private readonly Thread UpdateThread; + public readonly HostInputAdapter Adapter = Global.Config.HostInputMethod switch + { + EHostInputMethod.OpenTK => new OpenTKInputAdapter(), + EHostInputMethod.DirectInput => new DirectInputAdapter(), + _ => throw new Exception() + }; + private Input() { UpdateThread = new Thread(UpdateThreadProc) @@ -127,39 +137,6 @@ namespace BizHawk.Client.EmuHawk UpdateThread.Start(); } - public static void Initialize(Control parent) - { -#if true - OTK_Keyboard.Initialize(); - OTK_GamePad.Initialize(); -#else - if (OSTailoredCode.IsUnixHost) - { - OTK_Keyboard.Initialize(); - OTK_GamePad.Initialize(); - } - else - { - KeyInput.Initialize(parent); - IPCKeyInput.Initialize(); - GamePad.Initialize(parent); - GamePad360.Initialize(); - } -#endif - Instance = new Input(); - } - - public static void Cleanup() - { -#if false - if (!OSTailoredCode.IsUnixHost) - { - KeyInput.Cleanup(); - GamePad.Cleanup(); - } -#endif - } - public enum InputEventType { Press, Release @@ -292,6 +269,12 @@ namespace BizHawk.Client.EmuHawk } } + private void HandleAxis(string axis, float newValue) + { + if (_trackDeltas) _axisDeltas[axis] += Math.Abs(newValue - _axisValues[axis]); + _axisValues[axis] = newValue; + } + private static ModifierKey ButtonToModifierKey(string button) => button switch { "LeftShift" => ModifierKey.Shift, @@ -356,23 +339,8 @@ namespace BizHawk.Client.EmuHawk { while (true) { -#if true - var keyEvents = OTK_Keyboard.Update(); - OTK_GamePad.UpdateAll(); -#else - var keyEvents = OSTailoredCode.IsUnixHost - ? OTK_Keyboard.Update() - : KeyInput.Update().Concat(IPCKeyInput.Update()); - if (OSTailoredCode.IsUnixHost) - { - OTK_GamePad.UpdateAll(); - } - else - { - GamePad.UpdateAll(); - GamePad360.UpdateAll(); - } -#endif + var keyEvents = Adapter.ProcessHostKeyboards(); + Adapter.PreprocessHostGamepads(); //this block is going to massively modify data structures that the binding method uses, so we have to lock it all lock (this) @@ -386,57 +354,7 @@ namespace BizHawk.Client.EmuHawk lock (_axisValues) { //_axisValues.Clear(); - - // analyze OpenTK xinput (or is it libinput?) - foreach (var pad in OTK_GamePad.EnumerateDevices()) - { - foreach (var but in pad.buttonObjects) - { - HandleButton(pad.InputNamePrefix + but.ButtonName, but.ButtonAction(), InputFocus.Pad); - } - foreach (var (axisID, f) in pad.GetAxes()) - { - var n = $"{pad.InputNamePrefix}{axisID} Axis"; - if (_trackDeltas) _axisDeltas[n] += Math.Abs(f - _axisValues[n]); - _axisValues[n] = f; - } - } - -#if false - // analyze xinput - foreach (var pad in GamePad360.EnumerateDevices()) - { - string xName = $"X{pad.PlayerNumber} "; - for (int b = 0; b < pad.NumButtons; b++) - HandleButton(xName + pad.ButtonName(b), pad.Pressed(b), InputFocus.Pad); - foreach (var sv in pad.GetAxes()) - { - string n = xName + sv.Item1; - float f = sv.Item2; - if (_trackDeltas) - _axisDeltas[n] += Math.Abs(f - _axisValues[n]); - _axisValues[n] = f; - } - } - - // analyze joysticks - foreach (var pad in GamePad.EnumerateDevices()) - { - string jName = $"J{pad.PlayerNumber} "; - for (int b = 0; b < pad.NumButtons; b++) - HandleButton(jName + pad.ButtonName(b), pad.Pressed(b), InputFocus.Pad); - foreach (var sv in pad.GetAxes()) - { - string n = jName + sv.Item1; - float f = sv.Item2; - //if (n == "J5 RotationZ") - // System.Diagnostics.Debugger.Break(); - if (_trackDeltas) - _axisDeltas[n] += Math.Abs(f - _axisValues[n]); - _axisValues[n] = f; - } - } -#endif + Adapter.ProcessHostGamepads(HandleButton, HandleAxis); // analyze moose // other sorts of mouse api (raw input) could easily be added as a separate listing under a different class diff --git a/BizHawk.Client.EmuHawk/Input/Keyboard.cs b/BizHawk.Client.EmuHawk/Input/Keyboard.cs index f0fea6692a..2309311edc 100644 --- a/BizHawk.Client.EmuHawk/Input/Keyboard.cs +++ b/BizHawk.Client.EmuHawk/Input/Keyboard.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; -using System.Windows.Forms; +using System; +using System.Collections.Generic; + using SlimDX; using SlimDX.DirectInput; @@ -12,7 +13,7 @@ namespace BizHawk.Client.EmuHawk private static DirectInput _directInput; private static Keyboard _keyboard; - public static void Initialize(Control parent) + public static void Initialize(IntPtr mainFormHandle) { lock (SyncObj) { @@ -21,7 +22,7 @@ namespace BizHawk.Client.EmuHawk _directInput = new DirectInput(); _keyboard = new Keyboard(_directInput); - _keyboard.SetCooperativeLevel(parent.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); + _keyboard.SetCooperativeLevel(mainFormHandle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); _keyboard.Properties.BufferSize = 8; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 62a0153b25..e7dea2abf7 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -316,7 +316,7 @@ namespace BizHawk.Client.EmuHawk Sound?.StartSound(); }; - Input.Initialize(this); + Input.Instance.Adapter.FirstInitAll(Handle); InitControls(); Global.InputManager.ActiveController = new Controller(NullController.Instance.Definition); @@ -2721,13 +2721,7 @@ namespace BizHawk.Client.EmuHawk // Alt key hacks protected override void WndProc(ref Message m) { - switch (m.Msg) - { - case WmDeviceChange: - GamePad.Initialize(this); - GamePad360.Initialize(); - break; - } + if (m.Msg == WmDeviceChange) Input.Instance.Adapter.ReInitGamepads(Handle); // this is necessary to trap plain alt keypresses so that only our hotkey system gets them if (m.Msg == 0x0112) // WM_SYSCOMMAND diff --git a/BizHawk.Client.EmuHawk/Program.cs b/BizHawk.Client.EmuHawk/Program.cs index 414786f27e..d221394bec 100644 --- a/BizHawk.Client.EmuHawk/Program.cs +++ b/BizHawk.Client.EmuHawk/Program.cs @@ -242,7 +242,7 @@ namespace BizHawk.Client.EmuHawk GlobalWin.Sound?.Dispose(); GlobalWin.Sound = null; GlobalWin.GL.Dispose(); - Input.Cleanup(); + Input.Instance.Adapter.DeInitAll(); } //cleanup: diff --git a/BizHawk.Client.EmuHawk/config/EmuHawkOptions.Designer.cs b/BizHawk.Client.EmuHawk/config/EmuHawkOptions.Designer.cs index 50798c37f3..03278e9801 100644 --- a/BizHawk.Client.EmuHawk/config/EmuHawkOptions.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/EmuHawkOptions.Designer.cs @@ -88,6 +88,10 @@ this.rbLuaInterface = new BizHawk.WinForms.Controls.RadioButtonEx(grpLuaEngine.Tracker); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.flpDialogButtons = new BizHawk.WinForms.Controls.LocSzSingleRowFLP(); + this.grpInputMethod = new BizHawk.WinForms.Controls.SzGroupBoxEx(); + this.flpGrpInputMethod = new BizHawk.WinForms.Controls.LocSingleRowFLP(); + this.rbInputMethodOpenTK = new BizHawk.WinForms.Controls.RadioButtonEx(grpInputMethod.Tracker); + this.rbInputMethodDirectInput = new BizHawk.WinForms.Controls.RadioButtonEx(grpInputMethod.Tracker); this.tcDialog.SuspendLayout(); this.tpGeneral.SuspendLayout(); this.flpTpGeneral.SuspendLayout(); @@ -111,6 +115,8 @@ this.grpLuaEngine.SuspendLayout(); this.flpGrpLuaEngine.SuspendLayout(); this.flpDialogButtons.SuspendLayout(); + this.grpInputMethod.SuspendLayout(); + this.flpGrpInputMethod.SuspendLayout(); this.SuspendLayout(); // // btnDialogOK @@ -142,7 +148,7 @@ this.tcDialog.Location = new System.Drawing.Point(4, 4); this.tcDialog.Name = "tcDialog"; this.tcDialog.SelectedIndex = 0; - this.tcDialog.Size = new System.Drawing.Size(379, 363); + this.tcDialog.Size = new System.Drawing.Size(379, 389); this.tcDialog.TabIndex = 0; // // tpGeneral @@ -160,6 +166,7 @@ this.flpTpGeneral.Controls.Add(this.cbNeverAskForSave); this.flpTpGeneral.Controls.Add(this.flpNoFocusEmulate); this.flpTpGeneral.Controls.Add(this.flpNoFocusInput); + this.flpTpGeneral.Controls.Add(this.grpInputMethod); this.flpTpGeneral.Controls.Add(this.cbNonQWERTY); this.flpTpGeneral.Controls.Add(this.grpStartup); this.flpTpGeneral.Name = "flpTpGeneral"; @@ -492,18 +499,44 @@ this.flpDialogButtons.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.flpDialogButtons.Controls.Add(this.btnDialogOK); this.flpDialogButtons.Controls.Add(this.btnDialogCancel); - this.flpDialogButtons.Location = new System.Drawing.Point(247, 370); + this.flpDialogButtons.Location = new System.Drawing.Point(247, 396); this.flpDialogButtons.MinimumSize = new System.Drawing.Size(24, 24); this.flpDialogButtons.Name = "flpDialogButtons"; this.flpDialogButtons.Size = new System.Drawing.Size(132, 29); // + // grpInputMethod + // + this.grpInputMethod.Controls.Add(this.flpGrpInputMethod); + this.grpInputMethod.Name = "grpInputMethod"; + this.grpInputMethod.Size = new System.Drawing.Size(334, 45); + this.grpInputMethod.Text = "Input Method (requires restart)"; + // + // flpGrpInputMethod + // + this.flpGrpInputMethod.Controls.Add(this.rbInputMethodDirectInput); + this.flpGrpInputMethod.Controls.Add(this.rbInputMethodOpenTK); + this.flpGrpInputMethod.Location = new System.Drawing.Point(4, 12); + this.flpGrpInputMethod.Name = "flpGrpInputMethod"; + // + // rbInputMethodOpenTK + // + this.rbInputMethodOpenTK.Name = "rbInputMethodOpenTK"; + this.rbInputMethodOpenTK.Tag = BizHawk.Client.Common.EHostInputMethod.OpenTK; + this.rbInputMethodOpenTK.Text = "OpenTK"; + // + // rbInputMethodDirectInput + // + this.rbInputMethodDirectInput.Name = "rbInputMethodDirectInput"; + this.rbInputMethodDirectInput.Tag = BizHawk.Client.Common.EHostInputMethod.DirectInput; + this.rbInputMethodDirectInput.Text = "DirectInput"; + // // EmuHawkOptions // this.AcceptButton = this.btnDialogOK; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnDialogCancel; - this.ClientSize = new System.Drawing.Size(385, 405); + this.ClientSize = new System.Drawing.Size(385, 431); this.Controls.Add(this.flpDialogButtons); this.Controls.Add(this.tcDialog); this.MinimumSize = new System.Drawing.Size(401, 444); @@ -555,6 +588,10 @@ this.flpGrpLuaEngine.ResumeLayout(false); this.flpGrpLuaEngine.PerformLayout(); this.flpDialogButtons.ResumeLayout(false); + this.grpInputMethod.ResumeLayout(false); + this.grpInputMethod.PerformLayout(); + this.flpGrpInputMethod.ResumeLayout(false); + this.flpGrpInputMethod.PerformLayout(); this.ResumeLayout(false); } @@ -620,5 +657,9 @@ private BizHawk.WinForms.Controls.SzGroupBoxEx grpLuaEngine; private BizHawk.WinForms.Controls.LocSingleColumnFLP flpGrpLuaEngine; private BizHawk.WinForms.Controls.LocSzSingleRowFLP flpDialogButtons; + private BizHawk.WinForms.Controls.SzGroupBoxEx grpInputMethod; + private BizHawk.WinForms.Controls.LocSingleRowFLP flpGrpInputMethod; + private BizHawk.WinForms.Controls.RadioButtonEx rbInputMethodDirectInput; + private BizHawk.WinForms.Controls.RadioButtonEx rbInputMethodOpenTK; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/EmuHawkOptions.cs b/BizHawk.Client.EmuHawk/config/EmuHawkOptions.cs index 78008b11fe..ec494fe5d4 100644 --- a/BizHawk.Client.EmuHawk/config/EmuHawkOptions.cs +++ b/BizHawk.Client.EmuHawk/config/EmuHawkOptions.cs @@ -64,6 +64,17 @@ namespace BizHawk.Client.EmuHawk cbNoFocusEmulate.Checked = _config.RunInBackground; cbNoFocusInput.Checked = _config.AcceptBackgroundInput; cbNoFocusInputGamepadOnly.Checked = _config.AcceptBackgroundInputControllerOnly; + switch (_config.HostInputMethod) + { + case EHostInputMethod.OpenTK: + rbInputMethodOpenTK.Checked = true; + break; + case EHostInputMethod.DirectInput: + rbInputMethodDirectInput.Checked = true; + break; + default: + throw new InvalidOperationException(); + } cbNonQWERTY.Checked = _config.HandleAlternateKeyboardLayouts; cbNeverAskForSave.Checked = _config.SuppressAskSave; cbSingleInstance.Checked = _config.SingleInstanceMode; @@ -100,6 +111,7 @@ namespace BizHawk.Client.EmuHawk _config.RunInBackground = cbNoFocusEmulate.Checked; _config.AcceptBackgroundInput = cbNoFocusInput.Checked; _config.AcceptBackgroundInputControllerOnly = cbNoFocusInputGamepadOnly.Checked; + _config.HostInputMethod = grpInputMethod.Tracker.GetSelectionTagAs() ?? throw new InvalidOperationException(); _config.HandleAlternateKeyboardLayouts = cbNonQWERTY.Checked; _config.SuppressAskSave = cbNeverAskForSave.Checked; _config.SingleInstanceMode = cbSingleInstance.Checked;