Skip to content

Commit

Permalink
Rewrite Santroller HID layout registration
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNathannator committed May 27, 2023
1 parent 283e4e9 commit 888f252
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ internal static void Initialize()
// Layout finders
HidLayoutFinder.Initialize();
XInputLayoutFinder.Initialize();
// SantrollerLayoutFinder.Initialize(); // Temporarily disabled to avoid a crash bug
// this time it's not even related to the devices this is supposed to support lol

// General controls
ButtonAxisPairControl.Initialize();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.HID;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.XInput;

using Debug = UnityEngine.Debug;

namespace PlasticBand.LowLevel
{
internal enum SantrollerDeviceType
Expand Down Expand Up @@ -43,22 +37,16 @@ internal enum SantrollerRhythmType
/// </summary>
internal static class SantrollerLayoutFinder
{
// Dummy device to ensure the HID layout finder doesn't get to Santroller devices
// first and make the layout finding process ignore any layouts we might provide for them
private class SantrollerHidDevice : InputDevice { }

public const ushort VendorID = 0x1209;
public const ushort ProductID = 0x2882;
public const int VendorID = 0x1209;
public const int ProductID = 0x2882;

// Fall back to regular HID layout finder for devices without explicit layouts
private static readonly InputDeviceFindControlLayoutDelegate s_HidLayoutFinder = (InputDeviceFindControlLayoutDelegate)typeof(HID)
.GetMethod("OnFindLayoutForDevice", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new Type[]
{ typeof(InputDeviceDescription).MakeByRefType(), typeof(string), typeof(InputDeviceExecuteCommandDelegate) }, null)
.CreateDelegate(typeof(InputDeviceFindControlLayoutDelegate));
// Binary-coded decimal does not use 0xA-0xF
private const int kBcdDigitMin = 0;
private const int kBcdDigitMax = 9;

// Default device/rhythm types for XInput subtypes
private static readonly Dictionary<int, (SantrollerDeviceType type, SantrollerRhythmType rhythm)> s_XInputSubtypeToDeviceType
= new Dictionary<int, (SantrollerDeviceType type, SantrollerRhythmType rhythm)>()
= new Dictionary<int, (SantrollerDeviceType, SantrollerRhythmType)>()
{
{ (int)XInputController.DeviceSubType.Gamepad, (SantrollerDeviceType.Gamepad, SantrollerRhythmType.None) },
{ (int)XInputController.DeviceSubType.Wheel, (SantrollerDeviceType.Wheel, SantrollerRhythmType.None) },
Expand All @@ -73,72 +61,22 @@ private class SantrollerHidDevice : InputDevice { }
{ (int)XInputNonStandardSubType.StageKit, (SantrollerDeviceType.StageKit, SantrollerRhythmType.None) },
};

// Registered layouts
private static readonly Dictionary<(SantrollerDeviceType, SantrollerRhythmType?), string> s_AvailableHidLayouts
= new Dictionary<(SantrollerDeviceType, SantrollerRhythmType?), string>();

internal static void Initialize()
{
// Register dummy device
InputSystem.RegisterLayout<SantrollerHidDevice>(matches: GetHidMatcher());

// Ensure no layouts have persisted across a domain reload
s_AvailableHidLayouts.Clear();

// Register layout finder
InputSystem.onFindLayoutForDevice += FindSantrollerDeviceLayout;
}

internal static SantrollerDeviceType GetDeviceType(ushort version)
=> (SantrollerDeviceType)(version >> 8);

internal static SantrollerRhythmType GetRhythmType(ushort version)
=> (SantrollerRhythmType)((version >> 4) & 0x0F);

private static string FindSantrollerDeviceLayout(ref InputDeviceDescription description, string matchedLayout,
InputDeviceExecuteCommandDelegate executeDeviceCommand)
{
// Ignore non-HID devices
if (description.interfaceName != HidDefinitions.InterfaceName)
return null;

// If another layout resolver got to the device first, there's nothing we can do
if (!string.IsNullOrEmpty(matchedLayout) && matchedLayout != nameof(SantrollerHidDevice))
return null;
internal static int GetRevisionValue(SantrollerDeviceType deviceType, SantrollerRhythmType rhythmType, int consoleType)
=> (((int)deviceType & 0xFF) << 8) | (((int)rhythmType & 0x0F) << 4) | (consoleType & 0x0F);

// Parse version
if (!ushort.TryParse(description.version, out var version))
return null;

var deviceType = GetDeviceType(version);
var rhythmType = GetRhythmType(version);

// Check if the devicetype and rhythm type has an override registered
if (s_AvailableHidLayouts.TryGetValue((deviceType, rhythmType), out var layout))
return layout;

// A lot of devices are rhythm type independent, so also match on just the device type
if (s_AvailableHidLayouts.TryGetValue((deviceType, null), out layout))
return layout;

// We don't have a specific layout registered, fall back to the regular HID layout resolver
return s_HidLayoutFinder(ref description, null, executeDeviceCommand);
}

internal static void RegisterHIDLayout<TDevice>(SantrollerDeviceType deviceType, SantrollerRhythmType? rhythmType = null)
internal static void RegisterHIDLayout<TDevice>(SantrollerDeviceType deviceType,
SantrollerRhythmType rhythmType = SantrollerRhythmType.None)
where TDevice : InputDevice
{
// Register to the input system
InputSystem.RegisterLayout<TDevice>();

// Ensure no resolver is registered yet
if (s_AvailableHidLayouts.ContainsKey((deviceType, rhythmType)))
{
Debug.LogError($"Device type {deviceType}:{(rhythmType != null ? rhythmType.ToString() : "All")} is already registered!");
return;
}

s_AvailableHidLayouts.Add((deviceType, rhythmType), typeof(TDevice).Name);
// Register one matcher for every console type
for (int i = kBcdDigitMin; i <= kBcdDigitMax; i++)
InputSystem.RegisterLayout<TDevice>(matches: GetHidMatcher(deviceType, rhythmType, i));
}

[Conditional("UNITY_STANDALONE_WIN"), Conditional("UNITY_EDITOR_WIN")]
Expand All @@ -164,9 +102,12 @@ internal static void RegisterXInputLayout<TDevice>(int subType,
InputSystem.RegisterLayout<TDevice>(matches: GetXInputMatcher(subType, deviceType, rhythmType));
}

internal static InputDeviceMatcher GetHidMatcher()
internal static InputDeviceMatcher GetHidMatcher(SantrollerDeviceType deviceType,
SantrollerRhythmType rhythmType = SantrollerRhythmType.None,
int consoleType = 0)
{
return HidLayoutFinder.GetMatcher(VendorID, ProductID);
return HidLayoutFinder.GetMatcher(VendorID, ProductID)
.WithVersion(GetRevisionValue(deviceType, rhythmType, consoleType).ToString());
}

internal static InputDeviceMatcher GetXInputMatcher(int subType,
Expand Down

0 comments on commit 888f252

Please sign in to comment.