Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions Assets/Tests/InputSystem/Plugins/HIDTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,79 @@ public void Devices_CanParseHIDDescriptor_WithSignedLogicalMinAndMaxValues(byte
}
}

// Regression test for https://jira.unity3d.com/browse/UUM-143659.
//
// HID devices whose report descriptor declares a hat switch with Report Size 8
// (rather than the standard 4 bits, e.g. ESP32-BLE-Gamepad) caused an
// IndexOutOfRangeException inside InputDeviceBuilder.InsertControlBitRangeNode
// while constructing the control tree. InputManager caught the exception and
// logged "Could not create a device for ...", so the device never showed up.
[Test]
[Category("Devices")]
[Description("Regression test for case UUM-143659")]
public void Devices_CanCreateGenericHID_FromGamepadWithEightBitHatSwitch()
Comment thread
LeoUnity marked this conversation as resolved.
{
// Minimal Gamepad collection that contains a hat switch whose Report Size
// is declared as 8 bits (instead of the usual 4). Modelled on the
// descriptor reported in UUM-143659 (ESP32-BLE-Gamepad library).
var reportDescriptor = new byte[]
{
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x05, // Usage (Game Pad)
0xA1, 0x01, // Collection (Application)

// Two 8-bit axes so the control tree has more than one leaf.
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs)

// Hat switch with the problematic 8-bit Report Size.
0xA1, 0x00, // Collection (Physical)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x08, // Logical Maximum (8)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x12, // Unit (SI Rotation, Length: Centimeter)
0x75, 0x08, // Report Size (8) <-- bug: 8 instead of 4
0x95, 0x01, // Report Count (1)
0x81, 0x42, // Input (Data,Var,Abs,Null State)
0xC0, // End Collection

0xC0, // End Collection
};

var deviceId = runtime.AllocateDeviceId();
SetDeviceCommandCallbackToReturnReportDescriptor(deviceId, reportDescriptor);

runtime.ReportNewInputDevice(
new InputDeviceDescription
{
interfaceName = HID.kHIDInterface,
manufacturer = "TestVendor",
product = "TestEightBitHatGamepad",
capabilities = new HID.HIDDeviceDescriptor
{
vendorId = 0xE502,
productId = 0xBBAB
}.ToJson()
}.ToJson(), deviceId);

// Device construction runs through InputDeviceBuilder. Before the fix
// this raises IndexOutOfRangeException in InsertControlBitRangeNode,
// which InputManager catches and turns into a LogType.Error
// ("Could not create a device for ...") — that error fails the test.
InputSystem.Update();

Assert.That(InputSystem.GetDeviceById(deviceId), Is.Not.Null,
"Device should be created without throwing IndexOutOfRangeException.");
}

[Test]
[Category("Devices")]
public void Devices_CanCreateGenericHID_FromDeviceWithParsedReportDescriptor()
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed InputSystem.onAnyButtonPress fails to trigger when the device receives a touch [UUM-137930](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-137930).
- Fixed an incorrect ArraysHelper.HaveDuplicateReferences implementation that didn't use its arguments right [ISXB-1792] (https://github.com/Unity-Technologies/InputSystem/pull/2376)
- Fixed `InputAction.IsPressed`, `WasPressedThisFrame`, and `WasReleasedThisFrame` using a `ButtonControl`'s `pressPoint` when a binding also had an explicit `PressInteraction` with its own `pressPoint`, which could make those APIs disagree with the interaction's press and release behavior. Action-level press APIs now follow the interaction threshold when both are set explicitly.
- Fixed `IndexOutOfRangeException` in `InputDeviceBuilder` when connecting an HID gamepad whose report descriptor declares a hat switch with Report Size 8 (e.g. ESP32-BLE-Gamepad). The HID layer now anchors the hat's directional sub-controls to the hat's own byte instead of letting the layout system auto-allocate a fresh byte for each [UUM-143659](https://jira.unity3d.com/browse/UUM-143659).

### Changed
- Action-level `IsPressed`, `WasPressedThisFrame`, and `WasReleasedThisFrame` for bindings to `Vector2Control` / `StickControl` no longer consult a per-control `pressPoint` on the vector (that field was removed). Use a `Press` interaction to set a custom threshold, or rely on `defaultButtonPressPoint`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,12 +871,21 @@ internal void AddChildControls(ref HIDElementDescriptor element, string controlN

////REVIEW: this probably only works with hatswitches that have their null value at logicalMax+1

// All four sub-controls overlap the hat itself — they read the same value
// and only differ in the DiscreteButton range parameters above.
// WithByteOffset(0) anchors them to the hat parent's byte (FinalizeControlHierarchy
// adds the parent's byteOffset afterwards). Without it, the layout system
// auto-allocates a fresh byte for each — fine for 4-bit hats (bit-addressing,
// they pack into one byte) but for 8-bit hats each sub-control claims its own
// byte, pushes them past the device's report size, and InputDeviceBuilder's
// bit-range tree builder infinite-loops on the dangling controls. See UUM-143659.
builder.AddControl(controlName + "/up")
.WithFormat(InputStateBlock.FormatBit)
.WithLayout("DiscreteButton")
.WithParameters(string.Format(CultureInfo.InvariantCulture,
"minValue={0},maxValue={1},nullValue={2},wrapAtValue={3}",
logicalMax, logicalMin + 1, nullValue.ToString(), logicalMax))
.WithByteOffset(0)
.WithBitOffset((uint)element.reportOffsetInBits % 8)
.WithSizeInBits((uint)reportSizeInBits);

Expand All @@ -886,6 +895,7 @@ internal void AddChildControls(ref HIDElementDescriptor element, string controlN
.WithParameters(string.Format(CultureInfo.InvariantCulture,
"minValue={0},maxValue={1}",
logicalMin + 1, logicalMin + 3))
.WithByteOffset(0)
.WithBitOffset((uint)element.reportOffsetInBits % 8)
.WithSizeInBits((uint)reportSizeInBits);

Expand All @@ -895,6 +905,7 @@ internal void AddChildControls(ref HIDElementDescriptor element, string controlN
.WithParameters(string.Format(CultureInfo.InvariantCulture,
"minValue={0},maxValue={1}",
logicalMin + 3, logicalMin + 5))
.WithByteOffset(0)
.WithBitOffset((uint)element.reportOffsetInBits % 8)
.WithSizeInBits((uint)reportSizeInBits);

Expand All @@ -904,6 +915,7 @@ internal void AddChildControls(ref HIDElementDescriptor element, string controlN
.WithParameters(string.Format(CultureInfo.InvariantCulture,
"minValue={0},maxValue={1}",
logicalMin + 5, logicalMin + 7))
.WithByteOffset(0)
.WithBitOffset((uint)element.reportOffsetInBits % 8)
.WithSizeInBits((uint)reportSizeInBits);
}
Expand Down
Loading