This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8806f46699a44c39a6a66c5d07772871
timeCreated: 1510872623

View File

@@ -0,0 +1,233 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
////TODO: use two columns for treeview and separate name and value
namespace UnityEngine.InputSystem.HID.Editor
{
/// <summary>
/// A window that dumps a raw HID descriptor in a tree view.
/// </summary>
/// <remarks>
/// Not specific to InputDevices of type <see cref="HID"/> so that it can work with
/// any <see cref="InputDevice"/> created for a device using the "HID" interface.
/// </remarks>
internal class HIDDescriptorWindow : EditorWindow, ISerializationCallbackReceiver
{
public static void CreateOrShowExisting(int deviceId, InputDeviceDescription deviceDescription)
{
// See if we have an existing window for the device and if so pop it
// in front.
if (s_OpenWindows != null)
{
for (var i = 0; i < s_OpenWindows.Count; ++i)
{
var existingWindow = s_OpenWindows[i];
if (existingWindow.m_DeviceId == deviceId)
{
existingWindow.Show();
existingWindow.Focus();
return;
}
}
}
// No, so create a new one.
var window = CreateInstance<HIDDescriptorWindow>();
window.InitializeWith(deviceId, deviceDescription);
window.minSize = new Vector2(270, 200);
window.Show();
window.titleContent = new GUIContent("HID Descriptor");
}
public void Awake()
{
AddToList();
}
public void OnDestroy()
{
RemoveFromList();
}
public void OnGUI()
{
if (!m_Initialized)
InitializeWith(m_DeviceId, m_DeviceDescription);
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label(m_Label, GUILayout.MinWidth(100), GUILayout.ExpandWidth(true));
GUILayout.EndHorizontal();
var rect = EditorGUILayout.GetControlRect(GUILayout.ExpandHeight(true));
m_TreeView.OnGUI(rect);
}
private void InitializeWith(int deviceId, InputDeviceDescription deviceDescription)
{
m_DeviceId = deviceId;
m_DeviceDescription = deviceDescription;
m_Initialized = true;
// Set up tree view for HID descriptor.
var hidDescriptor = HID.ReadHIDDeviceDescriptor(ref m_DeviceDescription,
(ref InputDeviceCommand command) => InputRuntime.s_Instance.DeviceCommand(m_DeviceId, ref command));
if (m_TreeViewState == null)
m_TreeViewState = new TreeViewState();
m_TreeView = new HIDDescriptorTreeView(m_TreeViewState, hidDescriptor);
m_TreeView.SetExpanded(1, true);
m_Label = new GUIContent(
$"HID Descriptor for '{deviceDescription.manufacturer} {deviceDescription.product}'");
}
[NonSerialized] private bool m_Initialized;
[NonSerialized] private HIDDescriptorTreeView m_TreeView;
[NonSerialized] private GUIContent m_Label;
[SerializeField] private int m_DeviceId;
[SerializeField] private InputDeviceDescription m_DeviceDescription;
[SerializeField] private TreeViewState m_TreeViewState;
private void AddToList()
{
if (s_OpenWindows == null)
s_OpenWindows = new List<HIDDescriptorWindow>();
if (!s_OpenWindows.Contains(this))
s_OpenWindows.Add(this);
}
private void RemoveFromList()
{
if (s_OpenWindows != null)
s_OpenWindows.Remove(this);
}
private static List<HIDDescriptorWindow> s_OpenWindows;
private class HIDDescriptorTreeView : TreeView
{
private HID.HIDDeviceDescriptor m_Descriptor;
public HIDDescriptorTreeView(TreeViewState state, HID.HIDDeviceDescriptor descriptor)
: base(state)
{
m_Descriptor = descriptor;
Reload();
}
protected override TreeViewItem BuildRoot()
{
var id = 0;
var root = new TreeViewItem
{
id = id++,
depth = -1
};
var item = BuildDeviceItem(m_Descriptor, ref id);
root.AddChild(item);
return root;
}
private TreeViewItem BuildDeviceItem(HID.HIDDeviceDescriptor device, ref int id)
{
var item = new TreeViewItem
{
id = id++,
depth = 0,
displayName = "Device"
};
AddChild(item, string.Format("Vendor ID: 0x{0:X}", device.vendorId), ref id);
AddChild(item, string.Format("Product ID: 0x{0:X}", device.productId), ref id);
AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)device.usagePage, device.usagePage), ref id);
AddChild(item, string.Format("Usage: 0x{0:X}", device.usage), ref id);
AddChild(item, "Input Report Size: " + device.inputReportSize, ref id);
AddChild(item, "Output Report Size: " + device.outputReportSize, ref id);
AddChild(item, "Feature Report Size: " + device.featureReportSize, ref id);
// Elements.
if (device.elements != null)
{
var elementCount = device.elements.Length;
var elements = AddChild(item, elementCount + " Elements", ref id);
for (var i = 0; i < elementCount; ++i)
BuildElementItem(i, elements, device.elements[i], ref id);
}
else
AddChild(item, "0 Elements", ref id);
////TODO: collections
return item;
}
private TreeViewItem BuildElementItem(int index, TreeViewItem parent, HID.HIDElementDescriptor element, ref int id)
{
var item = AddChild(parent, string.Format("Element {0} ({1})", index, element.reportType), ref id);
string usagePageString = HID.UsagePageToString(element.usagePage);
string usageString = HID.UsageToString(element.usagePage, element.usage);
AddChild(item, string.Format("Usage Page: 0x{0:X} ({1})", (uint)element.usagePage, usagePageString), ref id);
if (usageString != null)
AddChild(item, string.Format("Usage: 0x{0:X} ({1})", element.usage, usageString), ref id);
else
AddChild(item, string.Format("Usage: 0x{0:X}", element.usage), ref id);
AddChild(item, "Report Type: " + element.reportType, ref id);
AddChild(item, "Report ID: " + element.reportId, ref id);
AddChild(item, "Report Size in Bits: " + element.reportSizeInBits, ref id);
AddChild(item, "Report Bit Offset: " + element.reportOffsetInBits, ref id);
AddChild(item, "Collection Index: " + element.collectionIndex, ref id);
AddChild(item, string.Format("Unit: {0:X}", element.unit), ref id);
AddChild(item, string.Format("Unit Exponent: {0:X}", element.unitExponent), ref id);
AddChild(item, "Logical Min: " + element.logicalMin, ref id);
AddChild(item, "Logical Max: " + element.logicalMax, ref id);
AddChild(item, "Physical Min: " + element.physicalMin, ref id);
AddChild(item, "Physical Max: " + element.physicalMax, ref id);
AddChild(item, "Has Null State?: " + element.hasNullState, ref id);
AddChild(item, "Has Preferred State?: " + element.hasPreferredState, ref id);
AddChild(item, "Is Array?: " + element.isArray, ref id);
AddChild(item, "Is Non-Linear?: " + element.isNonLinear, ref id);
AddChild(item, "Is Relative?: " + element.isRelative, ref id);
AddChild(item, "Is Constant?: " + element.isConstant, ref id);
AddChild(item, "Is Wrapping?: " + element.isWrapping, ref id);
return item;
}
private TreeViewItem AddChild(TreeViewItem parent, string displayName, ref int id)
{
var item = new TreeViewItem
{
id = id++,
depth = parent.depth + 1,
displayName = displayName
};
parent.AddChild(item);
return item;
}
}
public void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
AddToList();
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ae42a3986cf84fe2aa1df4aa984dd806
timeCreated: 1513554255

View File

@@ -0,0 +1,480 @@
using System;
using System.Collections.Generic;
////TODO: array support
////TODO: delimiter support
////TODO: designator support
#pragma warning disable CS0649
namespace UnityEngine.InputSystem.HID
{
/// <summary>
/// Turns binary HID descriptors into <see cref="HID.HIDDeviceDescriptor"/> instances.
/// </summary>
/// <remarks>
/// For information about the format, see the <a href="http://www.usb.org/developers/hidpage/HID1_11.pdf">
/// Device Class Definition for Human Interface Devices</a> section 6.2.2.
/// </remarks>
internal static class HIDParser
{
/// <summary>
/// Parse a HID report descriptor as defined by section 6.2.2 of the
/// <a href="http://www.usb.org/developers/hidpage/HID1_11.pdf">HID
/// specification</a> and add the elements and collections from the
/// descriptor to the given <paramref name="deviceDescriptor"/>.
/// </summary>
/// <param name="buffer">Buffer containing raw HID report descriptor.</param>
/// <param name="deviceDescriptor">HID device descriptor to complete with the information
/// from the report descriptor. Elements and collections will get added to this descriptor.</param>
/// <returns>True if the report descriptor was successfully parsed.</returns>
/// <remarks>
/// Will also set <see cref="HID.HIDDeviceDescriptor.inputReportSize"/>,
/// <see cref="HID.HIDDeviceDescriptor.outputReportSize"/>, and
/// <see cref="HID.HIDDeviceDescriptor.featureReportSize"/>.
/// </remarks>
public static unsafe bool ParseReportDescriptor(byte[] buffer, ref HID.HIDDeviceDescriptor deviceDescriptor)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
fixed(byte* bufferPtr = buffer)
{
return ParseReportDescriptor(bufferPtr, buffer.Length, ref deviceDescriptor);
}
}
public unsafe static bool ParseReportDescriptor(byte* bufferPtr, int bufferLength, ref HID.HIDDeviceDescriptor deviceDescriptor)
{
// Item state.
var localItemState = new HIDItemStateLocal();
var globalItemState = new HIDItemStateGlobal();
// Lists where we accumulate the data from the HID items.
var reports = new List<HIDReportData>();
var elements = new List<HID.HIDElementDescriptor>();
var collections = new List<HID.HIDCollectionDescriptor>();
var currentCollection = -1;
// Parse the linear list of items.
var endPtr = bufferPtr + bufferLength;
var currentPtr = bufferPtr;
while (currentPtr < endPtr)
{
var firstByte = *currentPtr;
////TODO
if (firstByte == 0xFE)
throw new NotImplementedException("long item support");
// Read item header.
var itemSize = (byte)(firstByte & 0x3);
var itemTypeAndTag = (byte)(firstByte & 0xFC);
++currentPtr;
// Process item.
switch (itemTypeAndTag)
{
// ------------ Global Items --------------
// These set item state permanently until it is reset.
// Usage Page
case (int)HIDItemTypeAndTag.UsagePage:
globalItemState.usagePage = ReadData(itemSize, currentPtr, endPtr);
break;
// Report Count
case (int)HIDItemTypeAndTag.ReportCount:
globalItemState.reportCount = ReadData(itemSize, currentPtr, endPtr);
break;
// Report Size
case (int)HIDItemTypeAndTag.ReportSize:
globalItemState.reportSize = ReadData(itemSize, currentPtr, endPtr);
break;
// Report ID
case (int)HIDItemTypeAndTag.ReportID:
globalItemState.reportId = ReadData(itemSize, currentPtr, endPtr);
break;
// Logical Minimum
case (int)HIDItemTypeAndTag.LogicalMinimum:
globalItemState.logicalMinimum = ReadData(itemSize, currentPtr, endPtr);
break;
// Logical Maximum
case (int)HIDItemTypeAndTag.LogicalMaximum:
globalItemState.logicalMaximum = ReadData(itemSize, currentPtr, endPtr);
break;
// Physical Minimum
case (int)HIDItemTypeAndTag.PhysicalMinimum:
globalItemState.physicalMinimum = ReadData(itemSize, currentPtr, endPtr);
break;
// Physical Maximum
case (int)HIDItemTypeAndTag.PhysicalMaximum:
globalItemState.physicalMaximum = ReadData(itemSize, currentPtr, endPtr);
break;
// Unit Exponent
case (int)HIDItemTypeAndTag.UnitExponent:
globalItemState.unitExponent = ReadData(itemSize, currentPtr, endPtr);
break;
// Unit
case (int)HIDItemTypeAndTag.Unit:
globalItemState.unit = ReadData(itemSize, currentPtr, endPtr);
break;
// ------------ Local Items --------------
// These set the state for the very next elements to be generated.
// Usage
case (int)HIDItemTypeAndTag.Usage:
localItemState.SetUsage(ReadData(itemSize, currentPtr, endPtr));
break;
// Usage Minimum
case (int)HIDItemTypeAndTag.UsageMinimum:
localItemState.usageMinimum = ReadData(itemSize, currentPtr, endPtr);
break;
// Usage Maximum
case (int)HIDItemTypeAndTag.UsageMaximum:
localItemState.usageMaximum = ReadData(itemSize, currentPtr, endPtr);
break;
// ------------ Main Items --------------
// These emit things into the descriptor based on the local and global item state.
// Collection
case (int)HIDItemTypeAndTag.Collection:
// Start new collection.
var parentCollection = currentCollection;
currentCollection = collections.Count;
collections.Add(new HID.HIDCollectionDescriptor
{
type = (HID.HIDCollectionType)ReadData(itemSize, currentPtr, endPtr),
parent = parentCollection,
usagePage = globalItemState.GetUsagePage(0, ref localItemState),
usage = localItemState.GetUsage(0),
firstChild = elements.Count
});
HIDItemStateLocal.Reset(ref localItemState);
break;
// EndCollection
case (int)HIDItemTypeAndTag.EndCollection:
if (currentCollection == -1)
return false;
// Close collection.
var collection = collections[currentCollection];
collection.childCount = elements.Count - collection.firstChild;
collections[currentCollection] = collection;
// Switch back to parent collection (if any).
currentCollection = collection.parent;
HIDItemStateLocal.Reset(ref localItemState);
break;
// Input/Output/Feature
case (int)HIDItemTypeAndTag.Input:
case (int)HIDItemTypeAndTag.Output:
case (int)HIDItemTypeAndTag.Feature:
// Determine report type.
var reportType = itemTypeAndTag == (int)HIDItemTypeAndTag.Input
? HID.HIDReportType.Input
: itemTypeAndTag == (int)HIDItemTypeAndTag.Output
? HID.HIDReportType.Output
: HID.HIDReportType.Feature;
// Find report.
var reportIndex = HIDReportData.FindOrAddReport(globalItemState.reportId, reportType, reports);
var report = reports[reportIndex];
// If we have a report ID, then reports start with an 8 byte report ID.
// Shift our offsets accordingly.
if (report.currentBitOffset == 0 && globalItemState.reportId.HasValue)
report.currentBitOffset = 8;
// Add elements to report.
var reportCount = globalItemState.reportCount.GetValueOrDefault(1);
var flags = ReadData(itemSize, currentPtr, endPtr);
for (var i = 0; i < reportCount; ++i)
{
var element = new HID.HIDElementDescriptor
{
usage = localItemState.GetUsage(i) & 0xFFFF, // Mask off usage page, if set.
usagePage = globalItemState.GetUsagePage(i, ref localItemState),
reportType = reportType,
reportSizeInBits = globalItemState.reportSize.GetValueOrDefault(8),
reportOffsetInBits = report.currentBitOffset,
reportId = globalItemState.reportId.GetValueOrDefault(1),
flags = (HID.HIDElementFlags)flags,
logicalMin = globalItemState.logicalMinimum.GetValueOrDefault(0),
logicalMax = globalItemState.logicalMaximum.GetValueOrDefault(0),
physicalMin = globalItemState.GetPhysicalMin(),
physicalMax = globalItemState.GetPhysicalMax(),
unitExponent = globalItemState.unitExponent.GetValueOrDefault(0),
unit = globalItemState.unit.GetValueOrDefault(0),
};
report.currentBitOffset += element.reportSizeInBits;
elements.Add(element);
}
reports[reportIndex] = report;
HIDItemStateLocal.Reset(ref localItemState);
break;
}
if (itemSize == 3)
currentPtr += 4;
else
currentPtr += itemSize;
}
deviceDescriptor.elements = elements.ToArray();
deviceDescriptor.collections = collections.ToArray();
// Set usage and usage page on device descriptor to what's
// on the toplevel application collection.
foreach (var collection in collections)
{
if (collection.parent == -1 && collection.type == HID.HIDCollectionType.Application)
{
deviceDescriptor.usage = collection.usage;
deviceDescriptor.usagePage = collection.usagePage;
break;
}
}
return true;
}
private unsafe static int ReadData(int itemSize, byte* currentPtr, byte* endPtr)
{
if (itemSize == 0)
return 0;
// Read byte.
if (itemSize == 1)
{
if (currentPtr >= endPtr)
return 0;
return *currentPtr;
}
// Read short.
if (itemSize == 2)
{
if (currentPtr + 2 >= endPtr)
return 0;
var data1 = *currentPtr;
var data2 = *(currentPtr + 1);
return (data2 << 8) | data1;
}
// Read int.
if (itemSize == 3) // Item size 3 means 4 bytes!
{
if (currentPtr + 4 >= endPtr)
return 0;
var data1 = *currentPtr;
var data2 = *(currentPtr + 1);
var data3 = *(currentPtr + 2);
var data4 = *(currentPtr + 3);
return (data4 << 24) | (data3 << 24) | (data2 << 8) | data1;
}
Debug.Assert(false, "Should not reach here");
return 0;
}
private struct HIDReportData
{
public int reportId;
public HID.HIDReportType reportType;
public int currentBitOffset;
public static int FindOrAddReport(int? reportId, HID.HIDReportType reportType, List<HIDReportData> reports)
{
var id = 1;
if (reportId.HasValue)
id = reportId.Value;
for (var i = 0; i < reports.Count; ++i)
{
if (reports[i].reportId == id && reports[i].reportType == reportType)
return i;
}
reports.Add(new HIDReportData
{
reportId = id,
reportType = reportType
});
return reports.Count - 1;
}
}
// All types and tags with size bits (low order two bits) masked out (i.e. being 0).
private enum HIDItemTypeAndTag
{
Input = 0x80,
Output = 0x90,
Feature = 0xB0,
Collection = 0xA0,
EndCollection = 0xC0,
UsagePage = 0x04,
LogicalMinimum = 0x14,
LogicalMaximum = 0x24,
PhysicalMinimum = 0x34,
PhysicalMaximum = 0x44,
UnitExponent = 0x54,
Unit = 0x64,
ReportSize = 0x74,
ReportID = 0x84,
ReportCount = 0x94,
Push = 0xA4,
Pop = 0xB4,
Usage = 0x08,
UsageMinimum = 0x18,
UsageMaximum = 0x28,
DesignatorIndex = 0x38,
DesignatorMinimum = 0x48,
DesignatorMaximum = 0x58,
StringIndex = 0x78,
StringMinimum = 0x88,
StringMaximum = 0x98,
Delimiter = 0xA8,
}
// State that needs to be defined for each main item separately.
// See section 6.2.2.8
private struct HIDItemStateLocal
{
public int? usage;
public int? usageMinimum;
public int? usageMaximum;
public int? designatorIndex;
public int? designatorMinimum;
public int? designatorMaximum;
public int? stringIndex;
public int? stringMinimum;
public int? stringMaximum;
public List<int> usageList;
// Wipe state but preserve usageList allocation.
public static void Reset(ref HIDItemStateLocal state)
{
var usageList = state.usageList;
state = new HIDItemStateLocal();
if (usageList != null)
{
usageList.Clear();
state.usageList = usageList;
}
}
// Usage can be set repeatedly to provide an enumeration of usages.
public void SetUsage(int value)
{
if (usage.HasValue)
{
if (usageList == null)
usageList = new List<int>();
usageList.Add(usage.Value);
}
usage = value;
}
// Get usage for Nth element in [0-reportCount] list.
public int GetUsage(int index)
{
// If we have minimum and maximum usage, interpolate between that.
if (usageMinimum.HasValue && usageMaximum.HasValue)
{
var min = usageMinimum.Value;
var max = usageMaximum.Value;
var range = max - min;
if (range < 0)
return 0;
if (index >= range)
return max;
return min + index;
}
// If we have a list of usages, index into that.
if (usageList != null && usageList.Count > 0)
{
var usageCount = usageList.Count;
if (index >= usageCount)
return usage.Value;
return usageList[index];
}
if (usage.HasValue)
return usage.Value;
////TODO: min/max
return 0;
}
}
// State that is carried over from main item to main item.
// See section 6.2.2.7
private struct HIDItemStateGlobal
{
public int? usagePage;
public int? logicalMinimum;
public int? logicalMaximum;
public int? physicalMinimum;
public int? physicalMaximum;
public int? unitExponent;
public int? unit;
public int? reportSize;
public int? reportCount;
public int? reportId;
public HID.UsagePage GetUsagePage(int index, ref HIDItemStateLocal localItemState)
{
if (!usagePage.HasValue)
{
var usage = localItemState.GetUsage(index);
return (HID.UsagePage)(usage >> 16);
}
return (HID.UsagePage)usagePage.Value;
}
public int GetPhysicalMin()
{
if (physicalMinimum == null || physicalMaximum == null ||
(physicalMinimum.Value == 0 && physicalMaximum.Value == 0))
return logicalMinimum.GetValueOrDefault(0);
return physicalMinimum.Value;
}
public int GetPhysicalMax()
{
if (physicalMinimum == null || physicalMaximum == null ||
(physicalMinimum.Value == 0 && physicalMaximum.Value == 0))
return logicalMaximum.GetValueOrDefault(0);
return physicalMaximum.Value;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a736049645e8c4781aedf679c2ee6be6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,154 @@
using System.Linq;
using UnityEngine.InputSystem.Utilities;
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.InputSystem.Editor;
using UnityEngine.InputSystem.HID.Editor;
#endif
namespace UnityEngine.InputSystem.HID
{
using ShouldCreateHIDCallback = System.Func<HID.HIDDeviceDescriptor, bool?>;
/// <summary>
/// Adds support for generic HID devices to the input system.
/// </summary>
/// <remarks>
/// Even without this module, HIDs can be used on platforms where we
/// support HID has a native backend (Windows and OSX, at the moment).
/// However, each supported HID requires a layout specifically targeting
/// it as a product.
///
/// What this module adds is the ability to turn any HID with usable
/// controls into an InputDevice. It will make a best effort to figure
/// out a suitable class for the device and will use the HID elements
/// present in the HID report descriptor to populate the device.
///
/// If there is an existing product-specific layout for a HID, it will
/// take precedence and HIDSupport will leave the device alone.
/// </remarks>
public static class HIDSupport
{
/// <summary>
/// A pair of HID usage page and HID usage number.
/// </summary>
/// <remarks>
/// Used to describe a HID usage for the <see cref="supportedHIDUsages"/> property.
/// </remarks>
public struct HIDPageUsage
{
/// <summary>
/// The usage page.
/// </summary>
public HID.UsagePage page;
/// <summary>
/// A number specifying the usage on the usage page.
/// </summary>
public int usage;
/// <summary>
/// Create a HIDPageUsage struct by specifying a page and usage.
/// </summary>
public HIDPageUsage(HID.UsagePage page, int usage)
{
this.page = page;
this.usage = usage;
}
/// <summary>
/// Create a HIDPageUsage struct from the GenericDesktop usage page by specifying the usage.
/// </summary>
public HIDPageUsage(HID.GenericDesktop usage)
{
page = HID.UsagePage.GenericDesktop;
this.usage = (int)usage;
}
}
private static HIDPageUsage[] s_SupportedHIDUsages;
/// <summary>
/// An array of HID usages the input is configured to support.
/// </summary>
/// <remarks>
/// The input system will only create <see cref="InputDevice"/>s for HIDs with usages
/// listed in this array. Any other HID will be ignored. This saves the input system from
/// spending resources on creating layouts and devices for HIDs which are not supported or
/// not usable for game input.
///
/// By default, this includes only <see cref="HID.GenericDesktop.Joystick"/>,
/// <see cref="HID.GenericDesktop.Gamepad"/> and <see cref="HID.GenericDesktop.MultiAxisController"/>,
/// but you can set this property to include any other HID usages.
///
/// Note that currently on macOS, the only HID usages which can be enabled are
/// <see cref="HID.GenericDesktop.Joystick"/>, <see cref="HID.GenericDesktop.Gamepad"/>,
/// <see cref="HID.GenericDesktop.MultiAxisController"/>, <see cref="HID.GenericDesktop.TabletPCControls"/>,
/// and <see cref="HID.GenericDesktop.AssistiveControl"/>.
/// </remarks>
public static ReadOnlyArray<HIDPageUsage> supportedHIDUsages
{
get => s_SupportedHIDUsages;
set
{
s_SupportedHIDUsages = value.ToArray();
// Add HIDs we now support.
InputSystem.s_Manager.AddAvailableDevicesThatAreNowRecognized();
// Remove HIDs we no longer support.
for (var i = 0; i < InputSystem.devices.Count; ++i)
{
var device = InputSystem.devices[i];
if (device is HID hid && !s_SupportedHIDUsages.Contains(new HIDPageUsage(hid.hidDescriptor.usagePage, hid.hidDescriptor.usage)))
{
// Remove the entire generated layout. This will also remove all devices based on it.
InputSystem.RemoveLayout(device.layout);
--i;
}
}
}
}
/// <summary>
/// Add support for generic HIDs to InputSystem.
/// </summary>
#if UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
public
#else
internal
#endif
static void Initialize()
{
s_SupportedHIDUsages = new[]
{
new HIDPageUsage(HID.GenericDesktop.Joystick),
new HIDPageUsage(HID.GenericDesktop.Gamepad),
new HIDPageUsage(HID.GenericDesktop.MultiAxisController),
};
InputSystem.RegisterLayout<HID>();
InputSystem.onFindLayoutForDevice += HID.OnFindLayoutForDevice;
// Add toolbar button to any devices using the "HID" interface. Opens
// a windows to browse the HID descriptor of the device.
#if UNITY_EDITOR
InputDeviceDebuggerWindow.onToolbarGUI +=
device =>
{
if (device.description.interfaceName == HID.kHIDInterface)
{
if (GUILayout.Button(s_HIDDescriptor, EditorStyles.toolbarButton))
{
HIDDescriptorWindow.CreateOrShowExisting(device.deviceId, device.description);
}
}
};
#endif
}
#if UNITY_EDITOR
private static readonly GUIContent s_HIDDescriptor = new GUIContent("HID Descriptor");
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f0b0cfb7c484e43823c1a038c97a5f3
timeCreated: 1511133984