Add haptics support to ControllerDefinition and the Controller stack
still no cores which support it, "Debug" is still hardcoded, still uses holding Fast Forward hotkey to trigger no idea how OverrideAdapter, or the IInputAdapters, or the Bk2/BkmController will work, I've just thrown NotImplementedException from those
This commit is contained in:
parent
bb3fddcb5f
commit
6f47492d95
|
@ -17,6 +17,7 @@ namespace BizHawk.Client.Common
|
|||
_axes[kvp.Key] = kvp.Value.Neutral;
|
||||
_axisRanges[kvp.Key] = kvp.Value;
|
||||
}
|
||||
foreach (var channel in Definition.HapticsChannels) _haptics[channel] = 0;
|
||||
}
|
||||
|
||||
public ControllerDefinition Definition { get; private set; }
|
||||
|
@ -25,11 +26,17 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public int AxisValue(string name) => _axes[name];
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot()
|
||||
=> _haptics.Select(kvp => (kvp.Key, kvp.Value)).ToArray();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => _haptics[name] = strength;
|
||||
|
||||
private readonly WorkingDictionary<string, List<string>> _bindings = new WorkingDictionary<string, List<string>>();
|
||||
private readonly WorkingDictionary<string, bool> _buttons = new WorkingDictionary<string, bool>();
|
||||
private readonly WorkingDictionary<string, int> _axes = new WorkingDictionary<string, int>();
|
||||
private readonly Dictionary<string, AxisSpec> _axisRanges = new WorkingDictionary<string, AxisSpec>();
|
||||
private readonly Dictionary<string, AnalogBind> _axisBindings = new Dictionary<string, AnalogBind>();
|
||||
private readonly Dictionary<string, int> _haptics = new WorkingDictionary<string, int>();
|
||||
private readonly Dictionary<string, FeedbackBind> _feedbackBindings = new Dictionary<string, FeedbackBind>();
|
||||
|
||||
/// <summary>don't do this</summary>
|
||||
|
@ -95,11 +102,14 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public void PrepareHapticsForHost(SimpleController finalHostController, int debug)
|
||||
public void PrepareHapticsForHost(SimpleController finalHostController)
|
||||
{
|
||||
foreach (var kvp in _feedbackBindings)
|
||||
{
|
||||
finalHostController.SetHapticChannelStrength(kvp.Value.GamepadPrefix + kvp.Value.Channel, (int) ((double) debug * kvp.Value.Prescale));
|
||||
if (_haptics.TryGetValue(kvp.Key, out var strength))
|
||||
{
|
||||
finalHostController.SetHapticChannelStrength(kvp.Value.GamepadPrefix + kvp.Value.Channel, (int) ((double) strength * kvp.Value.Prescale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ namespace BizHawk.Client.Common
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Array.Empty<(string, int)>();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) {}
|
||||
|
||||
/// <summary>
|
||||
/// uses the bindings to latch our own logical button state from the source controller's button state (which are assumed to be the physical side of the binding).
|
||||
/// this will clobber any existing data (use OR_* or other functions to layer in additional input sources)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
|
@ -18,6 +19,10 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public int AxisValue(string name) => 0;
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Array.Empty<(string, int)>();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) {}
|
||||
|
||||
/// <summary>
|
||||
/// Call this once per frame to do the timekeeping for the hold and release
|
||||
/// </summary>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
@ -20,6 +22,10 @@ namespace BizHawk.Client.Common
|
|||
// this works in the code because SourceOr is the autofire controller
|
||||
public int AxisValue(string name) => Source.AxisValue(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
|
||||
public IController Source { get; set; }
|
||||
internal IController SourceAnd { get; set; }
|
||||
}
|
||||
|
@ -42,6 +48,10 @@ namespace BizHawk.Client.Common
|
|||
// this works in the code because SourceOr is the autofire controller
|
||||
public int AxisValue(string name) => Source.AxisValue(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
|
||||
public IController Source { get; set; }
|
||||
internal IController SourceXor { get; set; }
|
||||
}
|
||||
|
@ -60,6 +70,10 @@ namespace BizHawk.Client.Common
|
|||
// this works in the code because SourceOr is the autofire controller
|
||||
public int AxisValue(string name) => Source.AxisValue(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
|
||||
public IController Source { get; set; }
|
||||
internal IController SourceOr { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
@ -13,6 +15,10 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public int AxisValue(string name) => Curr.AxisValue(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Curr.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Curr.SetHapticChannelStrength(name, strength);
|
||||
|
||||
public IController Source { get; set; }
|
||||
|
||||
private IController Curr => Source ?? NullController.Instance;
|
||||
|
|
|
@ -117,8 +117,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
if (feedbackBinds.TryGetValue(def.Name, out var fBinds))
|
||||
{
|
||||
const string channel = "Debug";
|
||||
// foreach (var channel in def.HapticsChannels)
|
||||
foreach (var channel in def.HapticsChannels)
|
||||
{
|
||||
if (fBinds.TryGetValue(channel, out var bind)) ret.BindFeedbackChannel(channel, bind);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ namespace BizHawk.Client.Common
|
|||
? _axisOverrides[name]
|
||||
: 0;
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
public IEnumerable<string> Overrides => _overrides.Select(kvp => kvp.Key);
|
||||
|
||||
public IEnumerable<string> AxisOverrides => _axisOverrides.Select(kvp => kvp.Key);
|
||||
|
|
|
@ -39,6 +39,10 @@ namespace BizHawk.Client.Common
|
|||
return Source.AxisValue(name);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
|
||||
public IController Source { get; set; }
|
||||
|
||||
private List<string> _justPressed = new List<string>();
|
||||
|
@ -141,6 +145,10 @@ namespace BizHawk.Client.Common
|
|||
return Source.AxisValue(name);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
|
||||
// TODO: Change the AutoHold adapter to be one of these, with an 'Off' value of 0?
|
||||
// Probably would have slightly lower performance, but it seems weird to have such a similar class that is only used once.
|
||||
private int _on;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using BizHawk.Common.StringExtensions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
|
@ -62,5 +64,9 @@ namespace BizHawk.Client.Common
|
|||
|
||||
// The float format implies no U+D and no L+R no matter what, so just passthru
|
||||
public int AxisValue(string name) => Source.AxisValue(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Source.GetHapticsSnapshot();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => Source.SetHapticChannelStrength(name, strength);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,10 @@ namespace BizHawk.Client.Common
|
|||
public bool IsPressed(string button) => _myBoolButtons[button];
|
||||
public int AxisValue(string name) => _myAxisControls[name];
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
public void SetFrom(IController source)
|
||||
{
|
||||
foreach (var button in Definition.BoolButtons)
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using BizHawk.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
|
@ -45,6 +48,10 @@ namespace BizHawk.Client.Common
|
|||
return _myAxisControls[name];
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // no idea --yoshi
|
||||
|
||||
/// <summary>
|
||||
/// latches all buttons from the supplied mnemonic string
|
||||
/// </summary>
|
||||
|
|
|
@ -703,9 +703,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
// ...but prepare haptics first, those get read in ProcessInput
|
||||
var finalHostController = (ControllerInputCoalescer) InputManager.ControllerInputCoalescer;
|
||||
// for now, vibrate the first gamepad when the Fast Forward hotkey is held, using the value from the previous (host) frame
|
||||
InputManager.ActiveController.PrepareHapticsForHost(
|
||||
finalHostController,
|
||||
debug: InputManager.ClientControls.IsPressed("Fast Forward") ? int.MaxValue : 0);
|
||||
InputManager.ActiveController.SetHapticChannelStrength("Debug", InputManager.ClientControls.IsPressed("Fast Forward") ? int.MaxValue : 0);
|
||||
InputManager.ActiveController.PrepareHapticsForHost(finalHostController);
|
||||
ProcessInput(
|
||||
_hotkeyCoalescer,
|
||||
finalHostController,
|
||||
|
|
|
@ -239,7 +239,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
LoadToPanel(
|
||||
FeedbacksTab,
|
||||
_emulator.ControllerDefinition.Name,
|
||||
/*_emulator.ControllerDefinition.HapticsChannels*/new[] { "Debug" },
|
||||
_emulator.ControllerDefinition.HapticsChannels,
|
||||
_emulator.ControllerDefinition.CategoryLabels,
|
||||
haptics,
|
||||
new(string.Empty, string.Empty, 1.0f),
|
||||
|
|
|
@ -10,9 +10,7 @@ namespace BizHawk.Emulation.Common
|
|||
/// <seealso cref="IEmulator" />
|
||||
public class ControllerDefinition
|
||||
{
|
||||
public ControllerDefinition()
|
||||
{
|
||||
}
|
||||
public ControllerDefinition() => HapticsChannels.Add("Debug");
|
||||
|
||||
public ControllerDefinition(ControllerDefinition source)
|
||||
: this()
|
||||
|
@ -35,6 +33,9 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
public readonly AxisDict Axes = new AxisDict();
|
||||
|
||||
/// <summary>Contains names of virtual haptic feedback channels, e.g. <c>{ "P1 Mono" }</c>, <c>{ "P2 Left", "P2 Right" }</c>.</summary>
|
||||
public List<string> HapticsChannels { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category labels. These labels provide a means of categorizing controls in various controller display and config screens
|
||||
/// </summary>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
|
@ -18,6 +19,10 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
public int AxisValue(string name) => 0;
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Array.Empty<(string, int)>();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) {}
|
||||
|
||||
public static readonly NullController Instance = new NullController();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
|
@ -31,24 +32,32 @@ namespace BizHawk.Emulation.Common
|
|||
int playerNext = 1;
|
||||
foreach (var def in controllers)
|
||||
{
|
||||
var remaps = new Dictionary<string, string>();
|
||||
Dictionary<string, string> buttonAxisRemaps = new();
|
||||
Dictionary<string, string> feedbackRemaps = new();
|
||||
|
||||
foreach (string s in def.BoolButtons)
|
||||
{
|
||||
string r = Allocate(s, ref plr, ref playerNext);
|
||||
ret.BoolButtons.Add(r);
|
||||
remaps[s] = r;
|
||||
buttonAxisRemaps[s] = r;
|
||||
}
|
||||
|
||||
foreach (var kvp in def.Axes)
|
||||
{
|
||||
string r = Allocate(kvp.Key, ref plr, ref playerNext);
|
||||
ret.Axes.Add(r, kvp.Value);
|
||||
remaps[kvp.Key] = r;
|
||||
buttonAxisRemaps[kvp.Key] = r;
|
||||
}
|
||||
|
||||
foreach (var s in def.HapticsChannels)
|
||||
{
|
||||
string r = Allocate(s, ref plr, ref playerNext);
|
||||
ret.HapticsChannels.Add(r);
|
||||
feedbackRemaps[s] = r;
|
||||
}
|
||||
|
||||
plr = playerNext;
|
||||
unmergers.Add(new ControlDefUnMerger(remaps));
|
||||
unmergers.Add(new ControlDefUnMerger(buttonAxisRemaps, feedbackRemaps));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -57,22 +66,24 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
public class ControlDefUnMerger
|
||||
{
|
||||
private readonly Dictionary<string, string> _remaps;
|
||||
|
||||
public ControlDefUnMerger(Dictionary<string, string> remaps)
|
||||
{
|
||||
_remaps = remaps;
|
||||
}
|
||||
|
||||
private class DummyController : IController
|
||||
{
|
||||
private readonly IController _src;
|
||||
private readonly Dictionary<string, string> _remaps;
|
||||
/// <inheritdoc cref="ControlDefUnMerger._buttonAxisRemaps"/>
|
||||
private readonly IReadOnlyDictionary<string, string> _buttonAxisRemaps;
|
||||
|
||||
public DummyController(IController src, Dictionary<string, string> remaps)
|
||||
/// <inheritdoc cref="ControlDefUnMerger._buttonAxisRemaps"/>
|
||||
private readonly IReadOnlyDictionary<string, string> _feedbackRemaps;
|
||||
|
||||
private readonly IController _src;
|
||||
|
||||
public DummyController(
|
||||
IController src,
|
||||
IReadOnlyDictionary<string, string> buttonAxisRemaps,
|
||||
IReadOnlyDictionary<string, string> feedbackRemaps)
|
||||
{
|
||||
_src = src;
|
||||
_remaps = remaps;
|
||||
_buttonAxisRemaps = buttonAxisRemaps;
|
||||
_feedbackRemaps = feedbackRemaps;
|
||||
}
|
||||
|
||||
/// <exception cref="NotImplementedException">always</exception>
|
||||
|
@ -80,18 +91,36 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
public bool IsPressed(string button)
|
||||
{
|
||||
return _src.IsPressed(_remaps[button]);
|
||||
return _src.IsPressed(_buttonAxisRemaps[button]);
|
||||
}
|
||||
|
||||
public int AxisValue(string name)
|
||||
{
|
||||
return _src.AxisValue(_remaps[name]);
|
||||
return _src.AxisValue(_buttonAxisRemaps[name]);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot()
|
||||
=> _src.GetHapticsSnapshot()
|
||||
.Select(hapticsEntry => (_feedbackRemaps.First(kvpRemap => kvpRemap.Value == hapticsEntry.Name).Value, hapticsEntry.Strength)) // reverse lookup
|
||||
.ToArray();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => _src.SetHapticChannelStrength(_feedbackRemaps[name], strength);
|
||||
}
|
||||
|
||||
public IController UnMerge(IController c)
|
||||
/// <remarks>these need to be separate because it's expected that <c>"P1 Left"</c> will appear in both</remarks>
|
||||
private readonly IReadOnlyDictionary<string, string> _buttonAxisRemaps;
|
||||
|
||||
/// <inheritdoc cref="_buttonAxisRemaps"/>
|
||||
private readonly IReadOnlyDictionary<string, string> _feedbackRemaps;
|
||||
|
||||
public ControlDefUnMerger(
|
||||
IReadOnlyDictionary<string, string> buttonAxisRemaps,
|
||||
IReadOnlyDictionary<string, string> feedbackRemaps)
|
||||
{
|
||||
return new DummyController(c, _remaps);
|
||||
_buttonAxisRemaps = buttonAxisRemaps;
|
||||
_feedbackRemaps = feedbackRemaps;
|
||||
}
|
||||
|
||||
public IController UnMerge(IController c) => new DummyController(c, _buttonAxisRemaps, _feedbackRemaps);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace BizHawk.Emulation.Common
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Common
|
||||
{
|
||||
public interface IController
|
||||
{
|
||||
|
@ -7,6 +9,9 @@
|
|||
/// </summary>
|
||||
ControllerDefinition Definition { get; }
|
||||
|
||||
/// <seealso cref="SetHapticChannelStrength"/>
|
||||
IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current state of a boolean control
|
||||
/// </summary>
|
||||
|
@ -16,5 +21,9 @@
|
|||
/// Returns the state of an axis control
|
||||
/// </summary>
|
||||
int AxisValue(string name);
|
||||
|
||||
/// <param name="name">haptic channel name e.g. "P1 Mono", "P2 Left"</param>
|
||||
/// <param name="strength">0..<see cref="int.MaxValue"/></param>
|
||||
void SetHapticChannelStrength(string name, int strength);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -95,5 +96,9 @@ namespace BizHawk.Emulation.Common
|
|||
{
|
||||
return _buttons[name];
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => Array.Empty<(string, int)>();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue