Encapsulate input method, revive DirectInput, cleanup Input.Instance

This commit is contained in:
YoshiRulz 2020-04-17 00:46:51 +10:00
parent 0aa8d71a3f
commit fa1c494ad8
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
11 changed files with 194 additions and 123 deletions

View File

@ -326,5 +326,7 @@ namespace BizHawk.Client.Common
// ReSharper disable once UnusedMember.Global // ReSharper disable once UnusedMember.Global
public string LastWrittenFromDetailed { get; set; } = VersionInfo.GetEmuVersion(); public string LastWrittenFromDetailed { get; set; } = VersionInfo.GetEmuVersion();
public EHostInputMethod HostInputMethod { get; set; } = EHostInputMethod.OpenTK;
} }
} }

View File

@ -40,4 +40,10 @@
Tas = 3, Tas = 3,
N64Tas = 4 N64Tas = 4
} }
public enum EHostInputMethod
{
OpenTK = 0,
DirectInput = 1
}
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows.Forms;
using BizHawk.Common; using BizHawk.Common;
@ -15,7 +14,7 @@ namespace BizHawk.Client.EmuHawk
private static readonly List<GamePad> Devices = new List<GamePad>(); private static readonly List<GamePad> Devices = new List<GamePad>();
private static DirectInput _directInput; private static DirectInput _directInput;
public static void Initialize(Control parent) public static void Initialize(IntPtr mainFormHandle)
{ {
lock (SyncObj) 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) 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); 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()) foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
{ {
if ((deviceObject.ObjectType & ObjectDeviceType.Axis) != 0) if ((deviceObject.ObjectType & ObjectDeviceType.Axis) != 0)
@ -125,7 +124,7 @@ namespace BizHawk.Client.EmuHawk
return; return;
} }
public IEnumerable<(string AxisID, float Value)> GetFloats() public IEnumerable<(string AxisID, float Value)> GetAxes()
{ {
var pis = typeof(JoystickState).GetProperties(); var pis = typeof(JoystickState).GetProperties();
foreach (var pi in pis) foreach (var pi in pis)

View File

@ -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; var g = _state.Gamepad;

View File

@ -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
{
/// <remarks>this was easier than trying to make static classes instantiable...</remarks>
public interface HostInputAdapter
{
void DeInitAll();
void FirstInitAll(IntPtr mainFormHandle);
void ReInitGamepads(IntPtr mainFormHandle);
void PreprocessHostGamepads();
void ProcessHostGamepads(Action<string?, bool, InputFocus> handleButton, Action<string?, float> handleAxis);
IEnumerable<KeyEvent> 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<string?, bool, InputFocus> handleButton, Action<string?, float> 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<KeyEvent> 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<string?, bool, InputFocus> handleButton, Action<string?, float> 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<KeyEvent> ProcessHostKeyboards() => OTK_Keyboard.Update();
}
}

View File

@ -114,9 +114,19 @@ namespace BizHawk.Client.EmuHawk
Alt = 262144 Alt = 262144
} }
public static Input Instance { get; private set; } private static readonly Lazy<Input> _instance = new Lazy<Input>(() => new Input());
public static Input Instance => _instance.Value;
private readonly Thread UpdateThread; 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() private Input()
{ {
UpdateThread = new Thread(UpdateThreadProc) UpdateThread = new Thread(UpdateThreadProc)
@ -127,39 +137,6 @@ namespace BizHawk.Client.EmuHawk
UpdateThread.Start(); 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 public enum InputEventType
{ {
Press, Release 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 private static ModifierKey ButtonToModifierKey(string button) => button switch
{ {
"LeftShift" => ModifierKey.Shift, "LeftShift" => ModifierKey.Shift,
@ -356,23 +339,8 @@ namespace BizHawk.Client.EmuHawk
{ {
while (true) while (true)
{ {
#if true var keyEvents = Adapter.ProcessHostKeyboards();
var keyEvents = OTK_Keyboard.Update(); Adapter.PreprocessHostGamepads();
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
//this block is going to massively modify data structures that the binding method uses, so we have to lock it all //this block is going to massively modify data structures that the binding method uses, so we have to lock it all
lock (this) lock (this)
@ -386,57 +354,7 @@ namespace BizHawk.Client.EmuHawk
lock (_axisValues) lock (_axisValues)
{ {
//_axisValues.Clear(); //_axisValues.Clear();
Adapter.ProcessHostGamepads(HandleButton, HandleAxis);
// 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
// analyze moose // analyze moose
// other sorts of mouse api (raw input) could easily be added as a separate listing under a different class // other sorts of mouse api (raw input) could easily be added as a separate listing under a different class

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System;
using System.Windows.Forms; using System.Collections.Generic;
using SlimDX; using SlimDX;
using SlimDX.DirectInput; using SlimDX.DirectInput;
@ -12,7 +13,7 @@ namespace BizHawk.Client.EmuHawk
private static DirectInput _directInput; private static DirectInput _directInput;
private static Keyboard _keyboard; private static Keyboard _keyboard;
public static void Initialize(Control parent) public static void Initialize(IntPtr mainFormHandle)
{ {
lock (SyncObj) lock (SyncObj)
{ {
@ -21,7 +22,7 @@ namespace BizHawk.Client.EmuHawk
_directInput = new DirectInput(); _directInput = new DirectInput();
_keyboard = new Keyboard(_directInput); _keyboard = new Keyboard(_directInput);
_keyboard.SetCooperativeLevel(parent.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); _keyboard.SetCooperativeLevel(mainFormHandle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
_keyboard.Properties.BufferSize = 8; _keyboard.Properties.BufferSize = 8;
} }
} }

View File

@ -316,7 +316,7 @@ namespace BizHawk.Client.EmuHawk
Sound?.StartSound(); Sound?.StartSound();
}; };
Input.Initialize(this); Input.Instance.Adapter.FirstInitAll(Handle);
InitControls(); InitControls();
Global.InputManager.ActiveController = new Controller(NullController.Instance.Definition); Global.InputManager.ActiveController = new Controller(NullController.Instance.Definition);
@ -2721,13 +2721,7 @@ namespace BizHawk.Client.EmuHawk
// Alt key hacks // Alt key hacks
protected override void WndProc(ref Message m) protected override void WndProc(ref Message m)
{ {
switch (m.Msg) if (m.Msg == WmDeviceChange) Input.Instance.Adapter.ReInitGamepads(Handle);
{
case WmDeviceChange:
GamePad.Initialize(this);
GamePad360.Initialize();
break;
}
// this is necessary to trap plain alt keypresses so that only our hotkey system gets them // this is necessary to trap plain alt keypresses so that only our hotkey system gets them
if (m.Msg == 0x0112) // WM_SYSCOMMAND if (m.Msg == 0x0112) // WM_SYSCOMMAND

View File

@ -242,7 +242,7 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.Sound?.Dispose(); GlobalWin.Sound?.Dispose();
GlobalWin.Sound = null; GlobalWin.Sound = null;
GlobalWin.GL.Dispose(); GlobalWin.GL.Dispose();
Input.Cleanup(); Input.Instance.Adapter.DeInitAll();
} }
//cleanup: //cleanup:

View File

@ -88,6 +88,10 @@
this.rbLuaInterface = new BizHawk.WinForms.Controls.RadioButtonEx(grpLuaEngine.Tracker); this.rbLuaInterface = new BizHawk.WinForms.Controls.RadioButtonEx(grpLuaEngine.Tracker);
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.flpDialogButtons = new BizHawk.WinForms.Controls.LocSzSingleRowFLP(); 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.tcDialog.SuspendLayout();
this.tpGeneral.SuspendLayout(); this.tpGeneral.SuspendLayout();
this.flpTpGeneral.SuspendLayout(); this.flpTpGeneral.SuspendLayout();
@ -111,6 +115,8 @@
this.grpLuaEngine.SuspendLayout(); this.grpLuaEngine.SuspendLayout();
this.flpGrpLuaEngine.SuspendLayout(); this.flpGrpLuaEngine.SuspendLayout();
this.flpDialogButtons.SuspendLayout(); this.flpDialogButtons.SuspendLayout();
this.grpInputMethod.SuspendLayout();
this.flpGrpInputMethod.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnDialogOK // btnDialogOK
@ -142,7 +148,7 @@
this.tcDialog.Location = new System.Drawing.Point(4, 4); this.tcDialog.Location = new System.Drawing.Point(4, 4);
this.tcDialog.Name = "tcDialog"; this.tcDialog.Name = "tcDialog";
this.tcDialog.SelectedIndex = 0; 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; this.tcDialog.TabIndex = 0;
// //
// tpGeneral // tpGeneral
@ -160,6 +166,7 @@
this.flpTpGeneral.Controls.Add(this.cbNeverAskForSave); this.flpTpGeneral.Controls.Add(this.cbNeverAskForSave);
this.flpTpGeneral.Controls.Add(this.flpNoFocusEmulate); this.flpTpGeneral.Controls.Add(this.flpNoFocusEmulate);
this.flpTpGeneral.Controls.Add(this.flpNoFocusInput); this.flpTpGeneral.Controls.Add(this.flpNoFocusInput);
this.flpTpGeneral.Controls.Add(this.grpInputMethod);
this.flpTpGeneral.Controls.Add(this.cbNonQWERTY); this.flpTpGeneral.Controls.Add(this.cbNonQWERTY);
this.flpTpGeneral.Controls.Add(this.grpStartup); this.flpTpGeneral.Controls.Add(this.grpStartup);
this.flpTpGeneral.Name = "flpTpGeneral"; 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.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.btnDialogOK);
this.flpDialogButtons.Controls.Add(this.btnDialogCancel); 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.MinimumSize = new System.Drawing.Size(24, 24);
this.flpDialogButtons.Name = "flpDialogButtons"; this.flpDialogButtons.Name = "flpDialogButtons";
this.flpDialogButtons.Size = new System.Drawing.Size(132, 29); 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 // EmuHawkOptions
// //
this.AcceptButton = this.btnDialogOK; this.AcceptButton = this.btnDialogOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnDialogCancel; 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.flpDialogButtons);
this.Controls.Add(this.tcDialog); this.Controls.Add(this.tcDialog);
this.MinimumSize = new System.Drawing.Size(401, 444); this.MinimumSize = new System.Drawing.Size(401, 444);
@ -555,6 +588,10 @@
this.flpGrpLuaEngine.ResumeLayout(false); this.flpGrpLuaEngine.ResumeLayout(false);
this.flpGrpLuaEngine.PerformLayout(); this.flpGrpLuaEngine.PerformLayout();
this.flpDialogButtons.ResumeLayout(false); this.flpDialogButtons.ResumeLayout(false);
this.grpInputMethod.ResumeLayout(false);
this.grpInputMethod.PerformLayout();
this.flpGrpInputMethod.ResumeLayout(false);
this.flpGrpInputMethod.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@ -620,5 +657,9 @@
private BizHawk.WinForms.Controls.SzGroupBoxEx grpLuaEngine; private BizHawk.WinForms.Controls.SzGroupBoxEx grpLuaEngine;
private BizHawk.WinForms.Controls.LocSingleColumnFLP flpGrpLuaEngine; private BizHawk.WinForms.Controls.LocSingleColumnFLP flpGrpLuaEngine;
private BizHawk.WinForms.Controls.LocSzSingleRowFLP flpDialogButtons; 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;
} }
} }

View File

@ -64,6 +64,17 @@ namespace BizHawk.Client.EmuHawk
cbNoFocusEmulate.Checked = _config.RunInBackground; cbNoFocusEmulate.Checked = _config.RunInBackground;
cbNoFocusInput.Checked = _config.AcceptBackgroundInput; cbNoFocusInput.Checked = _config.AcceptBackgroundInput;
cbNoFocusInputGamepadOnly.Checked = _config.AcceptBackgroundInputControllerOnly; 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; cbNonQWERTY.Checked = _config.HandleAlternateKeyboardLayouts;
cbNeverAskForSave.Checked = _config.SuppressAskSave; cbNeverAskForSave.Checked = _config.SuppressAskSave;
cbSingleInstance.Checked = _config.SingleInstanceMode; cbSingleInstance.Checked = _config.SingleInstanceMode;
@ -100,6 +111,7 @@ namespace BizHawk.Client.EmuHawk
_config.RunInBackground = cbNoFocusEmulate.Checked; _config.RunInBackground = cbNoFocusEmulate.Checked;
_config.AcceptBackgroundInput = cbNoFocusInput.Checked; _config.AcceptBackgroundInput = cbNoFocusInput.Checked;
_config.AcceptBackgroundInputControllerOnly = cbNoFocusInputGamepadOnly.Checked; _config.AcceptBackgroundInputControllerOnly = cbNoFocusInputGamepadOnly.Checked;
_config.HostInputMethod = grpInputMethod.Tracker.GetSelectionTagAs<EHostInputMethod>() ?? throw new InvalidOperationException();
_config.HandleAlternateKeyboardLayouts = cbNonQWERTY.Checked; _config.HandleAlternateKeyboardLayouts = cbNonQWERTY.Checked;
_config.SuppressAskSave = cbNeverAskForSave.Checked; _config.SuppressAskSave = cbNeverAskForSave.Checked;
_config.SingleInstanceMode = cbSingleInstance.Checked; _config.SingleInstanceMode = cbSingleInstance.Checked;