Skip to content

Commit bd804af

Browse files
committed
Add test to validate stick values
Also creates a simple layout struct for a device with only a single analog stick where the HID report descriptor states that the x and y values are between -127 and 127.
1 parent 9a5f397 commit bd804af

File tree

1 file changed

+118
-21
lines changed

1 file changed

+118
-21
lines changed

Assets/Tests/InputSystem/Plugins/HIDTests.cs

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -218,27 +218,9 @@ public void Devices_CanCreateGenericHID_FromDeviceWithBinaryReportDescriptor()
218218

219219
// The HID report descriptor is fetched from the device via an IOCTL.
220220
var deviceId = runtime.AllocateDeviceId();
221-
unsafe
222-
{
223-
runtime.SetDeviceCommandCallback(deviceId,
224-
(id, commandPtr) =>
225-
{
226-
if (commandPtr->type == HID.QueryHIDReportDescriptorSizeDeviceCommandType)
227-
return reportDescriptor.Length;
228221

229-
if (commandPtr->type == HID.QueryHIDReportDescriptorDeviceCommandType
230-
&& commandPtr->payloadSizeInBytes >= reportDescriptor.Length)
231-
{
232-
fixed(byte* ptr = reportDescriptor)
233-
{
234-
UnsafeUtility.MemCpy(commandPtr->payloadPtr, ptr, reportDescriptor.Length);
235-
return reportDescriptor.Length;
236-
}
237-
}
222+
SetDeviceCommandCallbackToReturnReportDescriptor(deviceId, reportDescriptor);
238223

239-
return InputDeviceCommand.GenericFailure;
240-
});
241-
}
242224
// Report device.
243225
runtime.ReportNewInputDevice(
244226
new InputDeviceDescription
@@ -309,6 +291,111 @@ public void Devices_CanCreateGenericHID_FromDeviceWithBinaryReportDescriptor()
309291
////TODO: check hat switch
310292
}
311293

294+
// This is used to mock out the IOCTL the HID device driver would use to return
295+
// the report descriptor and its size.
296+
unsafe void SetDeviceCommandCallbackToReturnReportDescriptor(int deviceId, byte[] reportDescriptor)
297+
{
298+
runtime.SetDeviceCommandCallback(deviceId,
299+
(id, commandPtr) =>
300+
{
301+
if (commandPtr->type == HID.QueryHIDReportDescriptorSizeDeviceCommandType)
302+
return reportDescriptor.Length;
303+
304+
if (commandPtr->type == HID.QueryHIDReportDescriptorDeviceCommandType
305+
&& commandPtr->payloadSizeInBytes >= reportDescriptor.Length)
306+
{
307+
fixed(byte* ptr = reportDescriptor)
308+
{
309+
UnsafeUtility.MemCpy(commandPtr->payloadPtr, ptr, reportDescriptor.Length);
310+
return reportDescriptor.Length;
311+
}
312+
}
313+
314+
return InputDeviceCommand.GenericFailure;
315+
});
316+
}
317+
318+
[Test]
319+
[Category("HID Devices")]
320+
public void Devices_CanCrateGenericHID_WithSignedLogicalMinAndMaxSticks()
321+
{
322+
// This is a HID report descriptor for a simple device with two analog sticks;
323+
// Similar to a user that reported an issue in Discussions:
324+
// https://discussions.unity.com/t/input-system-reading-invalid-values-from-hall-effect-keyboards/1684840/3
325+
var reportDescriptor = new byte[]
326+
{
327+
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
328+
0x09, 0x05, // Usage (Game Pad)
329+
0xA1, 0x01, // Collection (Application)
330+
0x85, 0x01, // Report ID (1)
331+
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
332+
0x09, 0x30, // Usage (X)
333+
0x09, 0x31, // Usage (Y)
334+
0x15, 0x81, // Logical Minimum (-127)
335+
0x25, 0x7F, // Logical Maximum (127)
336+
0x75, 0x08, // Report Size (8)
337+
0x95, 0x02, // Report Count (2)
338+
0x81, 0x02, // Input (Data,Var,Abs)
339+
0xC0, // End Collection
340+
};
341+
342+
// The HID report descriptor is fetched from the device via an IOCTL.
343+
var deviceId = runtime.AllocateDeviceId();
344+
345+
// Callback to return the desired report descriptor.
346+
SetDeviceCommandCallbackToReturnReportDescriptor(deviceId, reportDescriptor);
347+
348+
// Report device.
349+
runtime.ReportNewInputDevice(
350+
new InputDeviceDescription
351+
{
352+
interfaceName = HID.kHIDInterface,
353+
manufacturer = "TestVendor",
354+
product = "TestHID",
355+
capabilities = new HID.HIDDeviceDescriptor
356+
{
357+
vendorId = 0x321,
358+
productId = 0x432
359+
}.ToJson()
360+
}.ToJson(), deviceId);
361+
362+
InputSystem.Update();
363+
364+
var device = (Joystick)InputSystem.GetDeviceById(deviceId);
365+
Assert.That(device, Is.Not.Null);
366+
Assert.That(device, Is.TypeOf<Joystick>());
367+
368+
// Stick vector 2 should be centered at (0,0).
369+
Assert.That(device.stick.ReadValue(), Is.EqualTo(new Vector2(0f, 0f)).Using(Vector2EqualityComparer.Instance));
370+
371+
// Queue event with stick pushed to bottom. We assume Y axis is inverted by default in HID devices.
372+
// See HID.HIDElementDescriptor.DetermineParameters()
373+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickByte { reportId = 1, x = 0, y = 127 });
374+
InputSystem.Update();
375+
376+
Assert.That(device.stick.ReadValue() , Is.EqualTo(new Vector2(0f, -1f)).Using(Vector2EqualityComparer.Instance));
377+
378+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickByte { reportId = 1, x = 0, y = -127 });
379+
InputSystem.Update();
380+
381+
Assert.That(device.stick.ReadValue(), Is.EqualTo(new Vector2(0f, 1f)).Using(Vector2EqualityComparer.Instance));
382+
383+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickByte { reportId = 1, x = 127, y = 0 });
384+
InputSystem.Update();
385+
386+
Assert.That(device.stick.ReadValue() , Is.EqualTo(new Vector2(1f, 0f)).Using(Vector2EqualityComparer.Instance));
387+
388+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickByte { reportId = 1, x = -127, y = 0 });
389+
InputSystem.Update();
390+
391+
Assert.That(device.stick.ReadValue(), Is.EqualTo(new Vector2(-1f, 0f)).Using(Vector2EqualityComparer.Instance));
392+
393+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickByte { reportId = 1, x = 127, y = 127 });
394+
InputSystem.Update();
395+
396+
Assert.That(device.stick.ReadValue(), Is.EqualTo(new Vector2(0.7071f, -0.7071f)).Using(Vector2EqualityComparer.Instance));
397+
}
398+
312399
[Test]
313400
[Category("Devices")]
314401
public void Devices_CanCreateGenericHID_FromDeviceWithParsedReportDescriptor()
@@ -1026,7 +1113,7 @@ public void Devices_GenericHIDConvertsXAndYUsagesToStickControl()
10261113
}
10271114

10281115
[StructLayout(LayoutKind.Explicit)]
1029-
struct SimpleJoystickLayout : IInputStateTypeInfo
1116+
struct SimpleJoystickLayoutWithStickUshort : IInputStateTypeInfo
10301117
{
10311118
[FieldOffset(0)] public byte reportId;
10321119
[FieldOffset(1)] public ushort x;
@@ -1035,6 +1122,16 @@ struct SimpleJoystickLayout : IInputStateTypeInfo
10351122
public FourCC format => new FourCC('H', 'I', 'D');
10361123
}
10371124

1125+
[StructLayout(LayoutKind.Explicit)]
1126+
struct SimpleJoystickLayoutWithStickByte : IInputStateTypeInfo
1127+
{
1128+
[FieldOffset(0)] public byte reportId;
1129+
[FieldOffset(1)] public sbyte x;
1130+
[FieldOffset(2)] public sbyte y;
1131+
1132+
public FourCC format => new FourCC('H', 'I', 'D');
1133+
}
1134+
10381135
[Test]
10391136
[Category("Devices")]
10401137
public void Devices_GenericHIDXAndYDrivesStickControl()
@@ -1069,7 +1166,7 @@ public void Devices_GenericHIDXAndYDrivesStickControl()
10691166
Assert.That(device, Is.TypeOf<Joystick>());
10701167
Assert.That(device["Stick"], Is.TypeOf<StickControl>());
10711168

1072-
InputSystem.QueueStateEvent(device, new SimpleJoystickLayout { reportId = 1, x = ushort.MaxValue, y = ushort.MinValue });
1169+
InputSystem.QueueStateEvent(device, new SimpleJoystickLayoutWithStickUshort { reportId = 1, x = ushort.MaxValue, y = ushort.MinValue });
10731170
InputSystem.Update();
10741171

10751172
Assert.That(device["stick"].ReadValueAsObject(),

0 commit comments

Comments
 (0)