123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- using System;
- using UnityEngine.InputSystem.Utilities;
- ////TODO: add a 'devicePath' property that platforms can use to relay their internal device locators
- //// (but do *not* take it into account when comparing descriptions for disconnected devices)
- namespace UnityEngine.InputSystem.Layouts
- {
- /// <summary>
- /// Metadata for an input device.
- /// </summary>
- /// <remarks>
- /// Device descriptions are mainly used to determine which <see cref="InputControlLayout"/>
- /// to create an actual <see cref="InputDevice"/> instance from. Each description is comprised
- /// of a set of properties that each are individually optional. However, for a description
- /// to be usable, at least some need to be set. Generally, the minimum viable description
- /// for a device is one with <see cref="deviceClass"/> filled out.
- ///
- /// <example>
- /// <code>
- /// // Device description equivalent to a generic gamepad with no
- /// // further information about the device.
- /// new InputDeviceDescription
- /// {
- /// deviceClass = "Gamepad"
- /// };
- /// </code>
- /// </example>
- ///
- /// Device descriptions will usually be supplied by the Unity runtime but can also be manually
- /// fed into the system using <see cref="InputSystem.AddDevice(InputDeviceDescription)"/>. The
- /// system will remember each device description it has seen regardless of whether it was
- /// able to successfully create a device from the description. To query the list of descriptions
- /// that for whatever reason did not result in a device being created, call <see
- /// cref="InputSystem.GetUnsupportedDevices()"/>.
- ///
- /// Whenever layout registrations in the system are changed (e.g. by calling <see
- /// cref="InputSystem.RegisterLayout{T}"/> or whenever <see cref="InputSettings.supportedDevices"/>
- /// is changed, the system will go through the list of unsupported devices itself and figure out
- /// if there are device descriptions that now it can turn into devices. The same also applies
- /// in reverse; if, for example, a layout is removed that is currently used a device, the
- /// device will be removed and its description (if any) will be placed on the list of
- /// unsupported devices.
- /// </remarks>
- /// <seealso cref="InputDevice.description"/>
- /// <seealso cref="InputDeviceMatcher"/>
- [Serializable]
- public struct InputDeviceDescription : IEquatable<InputDeviceDescription>
- {
- /// <summary>
- /// How we talk to the device; usually name of the underlying backend that feeds
- /// state for the device (e.g. "HID" or "XInput").
- /// </summary>
- /// <value>Name of interface through which the device is reported.</value>
- /// <see cref="InputDeviceMatcher.WithInterface"/>
- public string interfaceName
- {
- get => m_InterfaceName;
- set => m_InterfaceName = value;
- }
- /// <summary>
- /// What the interface thinks the device classifies as.
- /// </summary>
- /// <value>Broad classification of device.</value>
- /// <remarks>
- /// If there is no layout specifically matching a device description,
- /// the device class is used as as fallback. If, for example, this field
- /// is set to "Gamepad", the "Gamepad" layout is used as a fallback.
- /// </remarks>
- /// <seealso cref="InputDeviceMatcher.WithDeviceClass"/>
- public string deviceClass
- {
- get => m_DeviceClass;
- set => m_DeviceClass = value;
- }
- /// <summary>
- /// Name of the vendor that produced the device.
- /// </summary>
- /// <value>Name of manufacturer.</value>
- /// <seealso cref="InputDeviceMatcher.WithManufacturer"/>
- public string manufacturer
- {
- get => m_Manufacturer;
- set => m_Manufacturer = value;
- }
- /// <summary>
- /// Name of the product assigned by the vendor to the device.
- /// </summary>
- /// <value>Name of product.</value>
- /// <seealso cref="InputDeviceMatcher.WithProduct"/>
- public string product
- {
- get => m_Product;
- set => m_Product = value;
- }
- /// <summary>
- /// If available, serial number for the device.
- /// </summary>
- /// <value>Serial number of device.</value>
- public string serial
- {
- get => m_Serial;
- set => m_Serial = value;
- }
- /// <summary>
- /// Version string of the device and/or driver.
- /// </summary>
- /// <value>Version of device and/or driver.</value>
- /// <seealso cref="InputDeviceMatcher.WithVersion"/>
- public string version
- {
- get => m_Version;
- set => m_Version = value;
- }
- /// <summary>
- /// An optional JSON string listing device-specific capabilities.
- /// </summary>
- /// <value>Interface-specific listing of device capabilities.</value>
- /// <remarks>
- /// The primary use of this field is to allow custom layout factories
- /// to create layouts on the fly from in-depth device descriptions delivered
- /// by external APIs.
- ///
- /// In the case of HID, for example, this field contains a JSON representation
- /// of the HID descriptor (see <see cref="HID.HID.HIDDeviceDescriptor"/>) as
- /// reported by the device driver. This descriptor contains information about
- /// all I/O elements on the device which can be used to determine the control
- /// setup and data format used by the device.
- /// </remarks>
- /// <seealso cref="InputDeviceMatcher.WithCapability{T}"/>
- public string capabilities
- {
- get => m_Capabilities;
- set => m_Capabilities = value;
- }
- /// <summary>
- /// Whether any of the properties in the description are set.
- /// </summary>
- /// <value>True if any of <see cref="interfaceName"/>, <see cref="deviceClass"/>,
- /// <see cref="manufacturer"/>, <see cref="product"/>, <see cref="serial"/>,
- /// <see cref="version"/>, or <see cref="capabilities"/> is not <c>null</c> and
- /// not empty..</value>
- public bool empty =>
- string.IsNullOrEmpty(m_InterfaceName) &&
- string.IsNullOrEmpty(m_DeviceClass) &&
- string.IsNullOrEmpty(m_Manufacturer) &&
- string.IsNullOrEmpty(m_Product) &&
- string.IsNullOrEmpty(m_Serial) &&
- string.IsNullOrEmpty(m_Version) &&
- string.IsNullOrEmpty(m_Capabilities);
- /// <summary>
- /// Return a string representation of the description useful for
- /// debugging.
- /// </summary>
- /// <returns>A script representation of the description.</returns>
- public override string ToString()
- {
- var haveProduct = !string.IsNullOrEmpty(product);
- var haveManufacturer = !string.IsNullOrEmpty(manufacturer);
- var haveInterface = !string.IsNullOrEmpty(interfaceName);
- if (haveProduct && haveManufacturer)
- {
- if (haveInterface)
- return $"{manufacturer} {product} ({interfaceName})";
- return $"{manufacturer} {product}";
- }
- if (haveProduct)
- {
- if (haveInterface)
- return $"{product} ({interfaceName})";
- return product;
- }
- if (!string.IsNullOrEmpty(deviceClass))
- {
- if (haveInterface)
- return $"{deviceClass} ({interfaceName})";
- return deviceClass;
- }
- // For some HIDs on Windows, we don't get a product and manufacturer string even though
- // the HID is guaranteed to have a product and vendor ID. Resort to printing capabilities
- // which for HIDs at least include the product and vendor ID.
- if (!string.IsNullOrEmpty(capabilities))
- {
- const int kMaxCapabilitiesLength = 40;
- var caps = capabilities;
- if (capabilities.Length > kMaxCapabilitiesLength)
- caps = caps.Substring(0, kMaxCapabilitiesLength) + "...";
- if (haveInterface)
- return $"{caps} ({interfaceName})";
- return caps;
- }
- if (haveInterface)
- return interfaceName;
- return "<Empty Device Description>";
- }
- /// <summary>
- /// Compare the description to the given <paramref name="other"/> description.
- /// </summary>
- /// <param name="other">Another device description.</param>
- /// <returns>True if the two descriptions are equivalent.</returns>
- /// <remarks>
- /// Two descriptions are equivalent if all their properties are equal
- /// (ignore case).
- /// </remarks>
- public bool Equals(InputDeviceDescription other)
- {
- return m_InterfaceName.InvariantEqualsIgnoreCase(other.m_InterfaceName) &&
- m_DeviceClass.InvariantEqualsIgnoreCase(other.m_DeviceClass) &&
- m_Manufacturer.InvariantEqualsIgnoreCase(other.m_Manufacturer) &&
- m_Product.InvariantEqualsIgnoreCase(other.m_Product) &&
- m_Serial.InvariantEqualsIgnoreCase(other.m_Serial) &&
- m_Version.InvariantEqualsIgnoreCase(other.m_Version) &&
- ////REVIEW: this would ideally compare JSON contents not just the raw string
- m_Capabilities.InvariantEqualsIgnoreCase(other.m_Capabilities);
- }
- /// <summary>
- /// Compare the description to the given object.
- /// </summary>
- /// <param name="obj">An object.</param>
- /// <returns>True if <paramref name="obj"/> is an InputDeviceDescription
- /// equivalent to this one.</returns>
- /// <seealso cref="Equals(InputDeviceDescription)"/>
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj))
- return false;
- return obj is InputDeviceDescription description && Equals(description);
- }
- /// <summary>
- /// Compute a hash code for the device description.
- /// </summary>
- /// <returns>A hash code.</returns>
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = m_InterfaceName != null ? m_InterfaceName.GetHashCode() : 0;
- hashCode = (hashCode * 397) ^ (m_DeviceClass != null ? m_DeviceClass.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (m_Manufacturer != null ? m_Manufacturer.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (m_Product != null ? m_Product.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (m_Serial != null ? m_Serial.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (m_Version != null ? m_Version.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (m_Capabilities != null ? m_Capabilities.GetHashCode() : 0);
- return hashCode;
- }
- }
- /// <summary>
- /// Compare the two device descriptions.
- /// </summary>
- /// <param name="left">First device description.</param>
- /// <param name="right">Second device description.</param>
- /// <returns>True if the two descriptions are equivalent.</returns>
- /// <seealso cref="Equals(InputDeviceDescription)"/>
- public static bool operator==(InputDeviceDescription left, InputDeviceDescription right)
- {
- return left.Equals(right);
- }
- /// <summary>
- /// Compare the two device descriptions for inequality.
- /// </summary>
- /// <param name="left">First device description.</param>
- /// <param name="right">Second device description.</param>
- /// <returns>True if the two descriptions are not equivalent.</returns>
- /// <seealso cref="Equals(InputDeviceDescription)"/>
- public static bool operator!=(InputDeviceDescription left, InputDeviceDescription right)
- {
- return !left.Equals(right);
- }
- /// <summary>
- /// Return a JSON representation of the device description.
- /// </summary>
- /// <returns>A JSON representation of the description.</returns>
- /// <remarks>
- /// <example>
- /// The result can be converted back into an InputDeviceDescription
- /// using <see cref="FromJson"/>.
- ///
- /// <code>
- /// var description = new InputDeviceDescription
- /// {
- /// interfaceName = "HID",
- /// product = "SomeDevice",
- /// capabilities = @"
- /// {
- /// ""vendorId"" : 0xABA,
- /// ""productId"" : 0xEFE
- /// }
- /// "
- /// };
- ///
- /// Debug.Log(description.ToJson());
- /// // Prints
- /// // {
- /// // "interface" : "HID",
- /// // "product" : "SomeDevice",
- /// // "capabilities" : "{ \"vendorId\" : 0xABA, \"productId\" : 0xEFF }"
- /// // }
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="FromJson"/>
- public string ToJson()
- {
- var data = new DeviceDescriptionJson
- {
- @interface = interfaceName,
- type = deviceClass,
- product = product,
- manufacturer = manufacturer,
- serial = serial,
- version = version,
- capabilities = capabilities
- };
- return JsonUtility.ToJson(data, true);
- }
- /// <summary>
- /// Read an InputDeviceDescription from its JSON representation.
- /// </summary>
- /// <param name="json">String in JSON format.</param>
- /// <exception cref="ArgumentNullException"><paramref name="json"/> is <c>null</c>.</exception>
- /// <returns>The converted </returns>
- /// <exception cref="ArgumentException">There as a parse error in <paramref name="json"/>.
- /// </exception>
- /// <remarks>
- /// <example>
- /// <code>
- /// InputDeviceDescription.FromJson(@"
- /// {
- /// ""interface"" : ""HID"",
- /// ""product"" : ""SomeDevice""
- /// }
- /// ");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="ToJson"/>
- public static InputDeviceDescription FromJson(string json)
- {
- if (json == null)
- throw new ArgumentNullException(nameof(json));
- var data = JsonUtility.FromJson<DeviceDescriptionJson>(json);
- return new InputDeviceDescription
- {
- interfaceName = data.@interface,
- deviceClass = data.type,
- product = data.product,
- manufacturer = data.manufacturer,
- serial = data.serial,
- version = data.version,
- capabilities = data.capabilities
- };
- }
- internal static bool ComparePropertyToDeviceDescriptor(string propertyName, string propertyValue, string deviceDescriptor)
- {
- // We use JsonParser instead of JsonUtility.Parse in order to not allocate GC memory here.
- var json = new JsonParser(deviceDescriptor);
- if (!json.NavigateToProperty(propertyName))
- {
- if (string.IsNullOrEmpty(propertyValue))
- return true;
- return false;
- }
- return json.CurrentPropertyHasValueEqualTo(propertyValue);
- }
- [SerializeField] private string m_InterfaceName;
- [SerializeField] private string m_DeviceClass;
- [SerializeField] private string m_Manufacturer;
- [SerializeField] private string m_Product;
- [SerializeField] private string m_Serial;
- [SerializeField] private string m_Version;
- [SerializeField] private string m_Capabilities;
- private struct DeviceDescriptionJson
- {
- public string @interface;
- public string type;
- public string product;
- public string serial;
- public string version;
- public string manufacturer;
- public string capabilities;
- }
- }
- }
|