InputDevice.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. using System;
  2. using UnityEngine.InputSystem.LowLevel;
  3. using UnityEngine.InputSystem.Utilities;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using UnityEngine.InputSystem.Layouts;
  6. using UnityEngine.InputSystem.XR;
  7. ////TODO: runtime remapping of control usages on a per-device basis
  8. ////TODO: finer-grained control over what devices deliver input while running in background
  9. //// (e.g. get gamepad input but do *not* get mouse and keyboard input)
  10. ////REVIEW: should be possible to completely hijack the input stream of a device such that its original input is suppressed
  11. ////REVIEW: can we construct the control tree of devices on demand so that the user never has to pay for
  12. //// the heap objects of devices that aren't used?
  13. // per device functions:
  14. // - update/poll
  15. // - IOCTL
  16. // - text input
  17. // - configuration change
  18. // - make current
  19. // - on remove (also resets current)
  20. //
  21. // Ideally, these would *not* be virtual methods on InputDevice but use a different process (which?)
  22. // for associating responses with devices
  23. namespace UnityEngine.InputSystem
  24. {
  25. /// <summary>
  26. /// Represents an input device which is always the root of a hierarchy of <see cref="InputControl"/> instances.
  27. /// </summary>
  28. /// <remarks>
  29. /// Input devices act as the container for control hierarchies. Every hierarchy has to have
  30. /// a device at the root. Devices cannot occur as children of other controls.
  31. ///
  32. /// Devices are usually created automatically in response to hardware being discovered by the Unity
  33. /// runtime. However, it is possible to manually add devices using methods such as <see
  34. /// cref="InputSystem.AddDevice{TDevice}(string)"/>.
  35. ///
  36. /// <example>
  37. /// <code>
  38. /// // Add a "synthetic" gamepad that isn't actually backed by hardware.
  39. /// var gamepad = InputSystem.AddDevice&lt;Gamepad&gt;();
  40. /// </code>
  41. /// </example>
  42. ///
  43. /// There are subclasses representing the most common types of devices, like <see cref="Mouse"/>,
  44. /// <see cref="Keyboard"/>, <see cref="Gamepad"/>, and <see cref="Touchscreen"/>.
  45. ///
  46. /// To create your own types of devices, you can derive from InputDevice and register your device
  47. /// as a new "layout".
  48. ///
  49. /// <example>
  50. /// <code>
  51. /// // InputControlLayoutAttribute attribute is only necessary if you want
  52. /// // to override default behavior that occurs when registering your device
  53. /// // as a layout.
  54. /// // The most common use of InputControlLayoutAttribute is to direct the system
  55. /// // to a custom "state struct" through the `stateType` property. See below for details.
  56. /// [InputControlLayout(displayName = "My Device", stateType = typeof(MyDeviceState))]
  57. /// #if UNITY_EDITOR
  58. /// [InitializeOnLoad]
  59. /// #endif
  60. /// public class MyDevice : InputDevice
  61. /// {
  62. /// public ButtonControl button { get; private set; }
  63. /// public AxisControl axis { get; private set; }
  64. ///
  65. /// // Register the device.
  66. /// static MyDevice()
  67. /// {
  68. /// // In case you want instance of your device to automatically be created
  69. /// // when specific hardware is detected by the Unity runtime, you have to
  70. /// // add one or more "device matchers" (InputDeviceMatcher) for the layout.
  71. /// // These matchers are compared to an InputDeviceDescription received from
  72. /// // the Unity runtime when a device is connected. You can add them either
  73. /// // using InputSystem.RegisterLayoutMatcher() or by directly specifying a
  74. /// // matcher when registering the layout.
  75. /// InputSystem.RegisterLayout&lt;MyDevice&gt;(
  76. /// // For the sake of demonstration, let's assume your device is a HID
  77. /// // and you want to match by PID and VID.
  78. /// matches: new InputDeviceMatcher()
  79. /// .WithInterface("HID")
  80. /// .WithCapability("PID", 1234)
  81. /// .WithCapability("VID", 5678));
  82. /// }
  83. ///
  84. /// // This is only to trigger the static class constructor to automatically run
  85. /// // in the player.
  86. /// [RuntimeInitializeOnLoadMethod]
  87. /// private static void InitializeInPlayer() {}
  88. ///
  89. /// protected override void FinishSetup()
  90. /// {
  91. /// base.FinishSetup();
  92. /// button = GetChildControl&lt;ButtonControl&gt;("button");
  93. /// axis = GetChildControl&lt;AxisControl&gt;("axis");
  94. /// }
  95. /// }
  96. ///
  97. /// // A "state struct" describes the memory format used by a device. Each device can
  98. /// // receive and store memory in its custom format. InputControls are then connected
  99. /// // the individual pieces of memory and read out values from them.
  100. /// [StructLayout(LayoutKind.Explicit, Size = 32)]
  101. /// public struct MyDeviceState : IInputStateTypeInfo
  102. /// {
  103. /// // In the case of a HID (which we assume for the sake of this demonstration),
  104. /// // the format will be "HID". In practice, the format will depend on how your
  105. /// // particular device is connected and fed into the input system.
  106. /// // The format is a simple FourCC code that "tags" state memory blocks for the
  107. /// // device to give a base level of safety checks on memory operations.
  108. /// public FourCC format => return new FourCC('H', 'I', 'D');
  109. ///
  110. /// // InputControlAttributes on fields tell the input system to create controls
  111. /// // for the public fields found in the struct.
  112. ///
  113. /// // Assume a 16bit field of buttons. Create one button that is tied to
  114. /// // bit #3 (zero-based). Note that buttons do not need to be stored as bits.
  115. /// // They can also be stored as floats or shorts, for example.
  116. /// [InputControl(name = "button", layout = "Button", bit = 3)]
  117. /// public ushort buttons;
  118. ///
  119. /// // Create a floating-point axis. The name, if not supplied, is taken from
  120. /// // the field.
  121. /// [InputControl(layout = "Axis")]
  122. /// public short axis;
  123. /// }
  124. /// </code>
  125. /// </example>
  126. ///
  127. /// Devices can have usages like any other control (<see cref="InputControl.usages"/>). Unlike other controls,
  128. /// however, usages of InputDevices are allowed to be changed on the fly without requiring a change to the
  129. /// device layout (see <see cref="InputSystem.SetDeviceUsage(InputDevice,string)"/>).
  130. ///
  131. /// For a more complete example of how to implement custom input devices, check out the "Custom Device"
  132. /// sample which you can install from the Unity package manager.
  133. ///
  134. /// And, as always, you can also find more information in the <a href="../manual/Devices.html">manual</a>.
  135. /// </remarks>
  136. /// <seealso cref="InputControl"/>
  137. /// <seealso cref="Mouse"/>
  138. /// <seealso cref="Keyboard"/>
  139. /// <seealso cref="Gamepad"/>
  140. /// <seealso cref="Touchscreen"/>
  141. [Scripting.Preserve]
  142. public class InputDevice : InputControl
  143. {
  144. /// <summary>
  145. /// Value of an invalid <see cref="deviceId"/>.
  146. /// </summary>
  147. /// <remarks>
  148. /// The input system will not assigned this ID to any device.
  149. /// </remarks>
  150. public const int InvalidDeviceId = 0;
  151. internal const int kLocalParticipantId = 0;
  152. internal const int kInvalidDeviceIndex = -1;
  153. /// <summary>
  154. /// Metadata describing the device (product name etc.).
  155. /// </summary>
  156. /// <remarks>
  157. /// The description of a device is unchanging over its lifetime and does not
  158. /// comprise data about a device's configuration (which is considered mutable).
  159. ///
  160. /// In most cases, the description for a device is supplied by the Unity runtime.
  161. /// This it the case for all <see cref="native"/> input devices. However, it is
  162. /// also possible to inject new devices in the form of device descriptions into
  163. /// the system using <see cref="InputSystem.AddDevice(InputDeviceDescription)"/>.
  164. ///
  165. /// The description of a device is what is matched by an <see cref="InputDeviceMatcher"/>
  166. /// to find the <see cref="InputControl.layout"/> to use for a device.
  167. /// </remarks>
  168. public InputDeviceDescription description => m_Description;
  169. /// <summary>
  170. /// Whether the device is currently enabled (i.e. sends and receives events).
  171. /// </summary>
  172. /// <remarks>
  173. /// A device that is disabled will not receive events. I.e. events that are being sent to the device
  174. /// will be ignored.
  175. ///
  176. /// When disabling a <see cref="native"/> device, a <see cref="DisableDeviceCommand">disable command</see> will
  177. /// also be sent to the <see cref="IInputRuntime">runtime</see>. It depends on the specific runtime whether the
  178. /// device command is supported but if it is, the device will be disabled in the runtime and no longer send
  179. /// events. This is especially important for devices such as <see cref="Sensor">sensors</see> that incur both
  180. /// computation and battery consumption overhead while enabled.
  181. ///
  182. /// Specific types of devices can choose to start out in disabled state by default. This is generally the
  183. /// case for <see cref="Sensor">sensors</see> to ensure that their overhead is only incurred when actually
  184. /// being used by the application.
  185. /// </remarks>
  186. /// <seealso cref="InputSystem.EnableDevice"/>
  187. /// <seealso cref="InputSystem.DisableDevice"/>
  188. public bool enabled
  189. {
  190. get
  191. {
  192. // Fetch state from runtime, if necessary.
  193. if ((m_DeviceFlags & DeviceFlags.DisabledStateHasBeenQueried) == 0)
  194. {
  195. var command = QueryEnabledStateCommand.Create();
  196. if (ExecuteCommand(ref command) >= 0)
  197. {
  198. if (command.isEnabled)
  199. m_DeviceFlags &= ~DeviceFlags.Disabled;
  200. else
  201. m_DeviceFlags |= DeviceFlags.Disabled;
  202. }
  203. else
  204. {
  205. // We got no response on the enable/disable state. Assume device is enabled.
  206. m_DeviceFlags &= ~DeviceFlags.Disabled;
  207. }
  208. // Only fetch enable/disable state again if we get a configuration change event.
  209. m_DeviceFlags |= DeviceFlags.DisabledStateHasBeenQueried;
  210. }
  211. return (m_DeviceFlags & DeviceFlags.Disabled) != DeviceFlags.Disabled;
  212. }
  213. }
  214. ////TODO: rename this to canReceiveInputInBackground
  215. /// <summary>
  216. /// If true, the device is capable of delivering input while the application is running in the background, i.e.
  217. /// while <c>Application.isFocused</c> is false.
  218. /// </summary>
  219. /// <value>Whether the device can generate input while in the background.</value>
  220. /// <remarks>
  221. /// Note that processing input in the background requires <c>Application.runInBackground</c> to be enabled in the
  222. /// player preferences. If this is enabled, the input system will keep running by virtue of being part of the Unity
  223. /// player loop which will keep running in the background. Note, however, that this does not necessarily mean that
  224. /// the application will necessarily receive input.
  225. ///
  226. /// Only a select set of hardware, platform, and SDK/API combinations support gathering input while not having
  227. /// input focus. The most notable set of devices are HMDs and VR controllers.
  228. ///
  229. /// The value of this property is determined by sending <see cref="QueryCanRunInBackground"/> to the device.
  230. /// </remarks>
  231. public bool canRunInBackground
  232. {
  233. get
  234. {
  235. var command = QueryCanRunInBackground.Create();
  236. if (ExecuteCommand(ref command) >= 0)
  237. return command.canRunInBackground;
  238. return false;
  239. }
  240. }
  241. /// <summary>
  242. /// Whether the device has been added to the system.
  243. /// </summary>
  244. /// <value>If true, the device is currently among the devices in <see cref="InputSystem.devices"/>.</value>
  245. /// <remarks>
  246. /// Devices may be removed at any time. Either when their hardware is unplugged or when they
  247. /// are manually removed through <see cref="InputSystem.RemoveDevice"/> or by being excluded
  248. /// through <see cref="InputSettings.supportedDevices"/>. When a device is removed, its instance,
  249. /// however, will not disappear. This property can be used to check whether the device is part
  250. /// of the current set of active devices.
  251. /// </remarks>
  252. /// <seealso cref="InputSystem.devices"/>
  253. public bool added => m_DeviceIndex != kInvalidDeviceIndex;
  254. /// <summary>
  255. /// Whether the device is mirrored from a remote input system and not actually present
  256. /// as a "real" device in the local system.
  257. /// </summary>
  258. /// <value>Whether the device mirrors a device from a remotely connected input system.</value>
  259. /// <seealso cref="InputSystem.remoting"/>
  260. /// <seealso cref="InputRemoting"/>
  261. public bool remote => (m_DeviceFlags & DeviceFlags.Remote) == DeviceFlags.Remote;
  262. /// <summary>
  263. /// Whether the device comes from the <see cref="IInputRuntime">runtime</see>
  264. /// </summary>
  265. /// <value>Whether the device has been discovered by the Unity runtime.</value>
  266. /// <remarks>
  267. /// Devices can be discovered when <see cref="IInputRuntime.onDeviceDiscovered">reported</see>
  268. /// by the runtime or they can be added manually through the various <see cref="InputSystem.AddDevice(InputDevice)">
  269. /// AddDevice</see> APIs. Devices reported by the runtime will return true for this
  270. /// property whereas devices added manually will return false.
  271. ///
  272. /// Devices reported by the runtime will usually come from the Unity engine itself.
  273. /// </remarks>
  274. /// <seealso cref="IInputRuntime"/>
  275. /// <seealso cref="IInputRuntime.onDeviceDiscovered"/>
  276. public bool native => (m_DeviceFlags & DeviceFlags.Native) == DeviceFlags.Native;
  277. /// <summary>
  278. /// Whether the device requires an extra update before rendering.
  279. /// </summary>
  280. /// <remarks>
  281. /// The value of this property is determined by <see cref="InputControlLayout.updateBeforeRender"/> in
  282. /// the device's <see cref="InputControlLayout">control layout</see>.
  283. ///
  284. /// The extra update is necessary for tracking devices that are used in rendering code. For example,
  285. /// the eye transforms of an HMD should be refreshed right before rendering as refreshing only in the
  286. /// beginning of the frame will lead to a noticeable lag.
  287. /// </remarks>
  288. /// <seealso cref="InputUpdateType.BeforeRender"/>
  289. public bool updateBeforeRender => (m_DeviceFlags & DeviceFlags.UpdateBeforeRender) == DeviceFlags.UpdateBeforeRender;
  290. /// <summary>
  291. /// Unique numeric ID for the device.
  292. /// </summary>
  293. /// <remarks>
  294. /// This is only assigned once a device has been added to the system. No two devices will receive the same
  295. /// ID and no device will receive an ID that another device used before even if the device was removed. The
  296. /// only exception to this is if a device gets re-created as part of a layout change. For example, if a new
  297. /// layout is registered that replaces the <see cref="Mouse"/> layout, all <see cref="Mouse"/> devices will
  298. /// get recreated but will keep their existing device IDs.
  299. ///
  300. /// IDs are assigned by the input runtime.
  301. /// </remarks>
  302. /// <seealso cref="IInputRuntime.AllocateDeviceId"/>
  303. public int deviceId => m_DeviceId;
  304. /// <summary>
  305. /// Timestamp of last state event used to update the device.
  306. /// </summary>
  307. /// <remarks>
  308. /// Events other than <see cref="LowLevel.StateEvent"/> and <see cref="LowLevel.DeltaStateEvent"/> will
  309. /// not cause lastUpdateTime to be changed.
  310. /// </remarks>
  311. public double lastUpdateTime => m_LastUpdateTimeInternal - InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  312. public bool wasUpdatedThisFrame => m_CurrentUpdateStepCount == InputUpdate.s_UpdateStepCount;
  313. /// <summary>
  314. /// A flattened list of controls that make up the device.
  315. /// </summary>
  316. /// <remarks>
  317. /// Does not allocate.
  318. /// </remarks>
  319. public ReadOnlyArray<InputControl> allControls
  320. {
  321. get
  322. {
  323. // Since m_ChildrenForEachControl contains the device's children as well as the children
  324. // of each control in the hierarchy, and since each control can only have a single parent,
  325. // this list will actually deliver a flattened list of all controls in the hierarchy (and without
  326. // the device itself being listed).
  327. return new ReadOnlyArray<InputControl>(m_ChildrenForEachControl);
  328. }
  329. }
  330. ////REVIEW: This violates the constraint of controls being required to not have reference types as value types.
  331. /// <inheritdoc/>
  332. public override Type valueType => typeof(byte[]);
  333. /// <inheritdoc/>
  334. public override int valueSizeInBytes => (int)m_StateBlock.alignedSizeInBytes;
  335. // This one just leads to confusion as you can access it from subclasses and then be surprised
  336. // that it doesn't only include members of those classes.
  337. [Obsolete("Use 'InputSystem.devices' instead. (UnityUpgradable) -> InputSystem.devices", error: false)]
  338. public static ReadOnlyArray<InputDevice> all => InputSystem.devices;
  339. /// <summary>
  340. /// This constructor is public for the sake of <c>Activator.CreateInstance</c> only. To construct
  341. /// devices, use methods such as <see cref="InputSystem.AddDevice{TDevice}(string)"/>. Manually
  342. /// using <c>new</c> on InputDevice will not result in a usable device.
  343. /// </summary>
  344. public InputDevice()
  345. {
  346. m_DeviceId = InvalidDeviceId;
  347. m_ParticipantId = kLocalParticipantId;
  348. m_DeviceIndex = kInvalidDeviceIndex;
  349. }
  350. ////REVIEW: Is making devices be byte[] values really all that useful? Seems better than returning nulls but
  351. //// at the same time, seems questionable.
  352. /// <inheritdoc/>
  353. public override unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize)
  354. {
  355. throw new NotImplementedException();
  356. }
  357. /// <inheritdoc/>
  358. public override unsafe object ReadValueFromStateAsObject(void* statePtr)
  359. {
  360. if (m_DeviceIndex == kInvalidDeviceIndex)
  361. return null;
  362. var numBytes = stateBlock.alignedSizeInBytes;
  363. var array = new byte[numBytes];
  364. fixed(byte* arrayPtr = array)
  365. {
  366. var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset;
  367. UnsafeUtility.MemCpy(arrayPtr, adjustedStatePtr, numBytes);
  368. }
  369. return array;
  370. }
  371. /// <inheritdoc/>
  372. public override unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize)
  373. {
  374. if (statePtr == null)
  375. throw new ArgumentNullException(nameof(statePtr));
  376. if (bufferPtr == null)
  377. throw new ArgumentNullException(nameof(bufferPtr));
  378. if (bufferSize < valueSizeInBytes)
  379. throw new ArgumentException($"Buffer too small (expected: {valueSizeInBytes}, actual: {bufferSize}");
  380. var adjustedStatePtr = (byte*)statePtr + m_StateBlock.byteOffset;
  381. UnsafeUtility.MemCpy(bufferPtr, adjustedStatePtr, m_StateBlock.alignedSizeInBytes);
  382. }
  383. /// <inheritdoc/>
  384. public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
  385. {
  386. if (firstStatePtr == null)
  387. throw new ArgumentNullException(nameof(firstStatePtr));
  388. if (secondStatePtr == null)
  389. throw new ArgumentNullException(nameof(secondStatePtr));
  390. var adjustedFirstStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset;
  391. var adjustedSecondStatePtr = (byte*)firstStatePtr + m_StateBlock.byteOffset;
  392. return UnsafeUtility.MemCmp(adjustedFirstStatePtr, adjustedSecondStatePtr,
  393. m_StateBlock.alignedSizeInBytes) == 0;
  394. }
  395. /// <summary>
  396. /// Called by the system when the configuration of the device has changed.
  397. /// </summary>
  398. /// <seealso cref="DeviceConfigurationEvent"/>
  399. internal void OnConfigurationChanged()
  400. {
  401. // Mark all controls in the hierarchy as having their config out of date.
  402. // We don't want to update configuration right away but rather wait until
  403. // someone actually depends on it.
  404. isConfigUpToDate = false;
  405. for (var i = 0; i < m_ChildrenForEachControl.Length; ++i)
  406. m_ChildrenForEachControl[i].isConfigUpToDate = false;
  407. // Make sure we fetch the enabled/disabled state again.
  408. m_DeviceFlags &= ~DeviceFlags.DisabledStateHasBeenQueried;
  409. }
  410. /// <summary>
  411. /// Make this the current device of its type.
  412. /// </summary>
  413. /// <remarks>
  414. /// This method is called automatically by the input system when a device is
  415. /// added or when input is received on it. Many types of devices have <c>.current</c>
  416. /// getters that allow querying the last used device of a specific type directly (for
  417. /// example, see <see cref="Gamepad.current"/>).
  418. ///
  419. /// There is one special case, however, related to noise. A device that has noisy controls
  420. /// (i.e. controls for which <see cref="InputControl.noisy"/> is true) may receive input events
  421. /// that contain no meaningful user interaction but are simply just noise from the device. A
  422. /// good example of this is the PS4 gamepad which has a built-in gyro and may thus constantly
  423. /// feed events into the input system even if not being actually in use. If, for example, an
  424. /// Xbox gamepad and PS4 gamepad are both connected to a PC and the user is playing with the
  425. /// Xbox gamepad, the PS4 gamepad would still constantly make itself <see cref="Gamepad.current"/>
  426. /// by simply flooding the system with events.
  427. ///
  428. /// By enabling <see cref="InputSettings.filterNoiseOnCurrent"/> (disabled by default),
  429. /// noise on <c>.current</c> getters will be filtered out and a device will only see <c>MakeCurrent</c>
  430. /// getting called if there input was detected on non-noisy controls.
  431. /// </remarks>
  432. /// <seealso cref="InputSettings.filterNoiseOnCurrent"/>
  433. /// <seealso cref="Pointer.current"/>
  434. /// <seealso cref="Gamepad.current"/>
  435. /// <seealso cref="Mouse.current"/>
  436. /// <seealso cref="Pen.current"/>
  437. public virtual void MakeCurrent()
  438. {
  439. }
  440. /// <summary>
  441. /// Called by the system when the device is added to <see cref="InputSystem.devices"/>.
  442. /// </summary>
  443. /// <remarks>
  444. /// This is called <em>after</em> the device has already been added.
  445. /// </remarks>
  446. /// <seealso cref="InputSystem.devices"/>
  447. /// <seealso cref="InputDeviceChange.Added"/>
  448. /// <seealso cref="OnRemoved"/>
  449. protected virtual void OnAdded()
  450. {
  451. }
  452. /// <summary>
  453. /// Called by the system when the device is removed from <see cref="InputSystem.devices"/>.
  454. /// </summary>
  455. /// <remarks>
  456. /// This is called <em>after</em> the device has already been removed.
  457. /// </remarks>
  458. /// <seealso cref="InputSystem.devices"/>
  459. /// <seealso cref="InputDeviceChange.Removed"/>
  460. /// <seealso cref="OnRemoved"/>
  461. protected virtual void OnRemoved()
  462. {
  463. }
  464. ////TODO: add overridable OnDisable/OnEnable that fire the device commands
  465. ////TODO: this should be overridable directly on the device in some form; can't be virtual because of AOT problems; need some other solution
  466. ////REVIEW: return just bool instead of long and require everything else to go in the command?
  467. /// <summary>
  468. /// Perform a device-specific command.
  469. /// </summary>
  470. /// <param name="command">Data for the command to be performed.</param>
  471. /// <returns>A transfer-specific return code. Negative values are considered failure codes.</returns>
  472. /// <remarks>
  473. /// Commands allow devices to set up custom protocols without having to extend
  474. /// the device API. This is most useful for devices implemented in the native Unity runtime
  475. /// which, through the command interface, may provide custom, device-specific functions.
  476. ///
  477. /// This is a low-level API. It works in a similar way to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396"
  478. /// target="_blank">DeviceIoControl</a> on Windows and <a href="https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/ioctl.2.html"
  479. /// target="_blank">ioctl</a> on UNIX-like systems.
  480. /// </remarks>
  481. public unsafe long ExecuteCommand<TCommand>(ref TCommand command)
  482. where TCommand : struct, IInputDeviceCommandInfo
  483. {
  484. // Give callbacks first shot.
  485. var manager = InputSystem.s_Manager;
  486. var callbacks = manager.m_DeviceCommandCallbacks;
  487. for (var i = 0; i < callbacks.length; ++i)
  488. {
  489. var result = callbacks[i](this, (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command));
  490. if (result.HasValue)
  491. return result.Value;
  492. }
  493. return InputRuntime.s_Instance.DeviceCommand(deviceId, ref command);
  494. }
  495. [Flags]
  496. internal enum DeviceFlags
  497. {
  498. UpdateBeforeRender = 1 << 0,
  499. HasStateCallbacks = 1 << 1,
  500. HasControlsWithDefaultState = 1 << 2,
  501. Remote = 1 << 3, // It's a local mirror of a device from a remote player connection.
  502. Native = 1 << 4, // It's a device created from data surfaced by NativeInputRuntime.
  503. Disabled = 1 << 5,
  504. DisabledStateHasBeenQueried = 1 << 6, // Whether we have fetched the current enable/disable state from the runtime.
  505. }
  506. internal DeviceFlags m_DeviceFlags;
  507. internal int m_DeviceId;
  508. internal int m_ParticipantId;
  509. internal int m_DeviceIndex; // Index in InputManager.m_Devices.
  510. internal InputDeviceDescription m_Description;
  511. /// <summary>
  512. /// Timestamp of last event we received.
  513. /// </summary>
  514. /// <seealso cref="InputEvent.time"/>
  515. internal double m_LastUpdateTimeInternal;
  516. // Update count corresponding to the current front buffers that are active on the device.
  517. // We use this to know when to flip buffers.
  518. internal uint m_CurrentUpdateStepCount;
  519. // List of aliases for all controls. Each control gets a slice of this array.
  520. // See 'InputControl.aliases'.
  521. // NOTE: The device's own aliases are part of this array as well.
  522. internal InternedString[] m_AliasesForEachControl;
  523. // List of usages for all controls. Each control gets a slice of this array.
  524. // See 'InputControl.usages'.
  525. // NOTE: The device's own usages are part of this array as well. They are always
  526. // at the *end* of the array.
  527. internal InternedString[] m_UsagesForEachControl;
  528. // This one does NOT contain the device itself, i.e. it only contains controls on the device
  529. // and may this be shorter than m_UsagesForEachControl.
  530. internal InputControl[] m_UsageToControl;
  531. // List of children for all controls. Each control gets a slice of this array.
  532. // See 'InputControl.children'.
  533. // NOTE: The device's own children are part of this array as well.
  534. internal InputControl[] m_ChildrenForEachControl;
  535. // NOTE: We don't store processors in a combined array the same way we do for
  536. // usages and children as that would require lots of casting from 'object'.
  537. /// <summary>
  538. /// If true, the device has at least one control that has an explicit default state.
  539. /// </summary>
  540. internal bool hasControlsWithDefaultState
  541. {
  542. get => (m_DeviceFlags & DeviceFlags.HasControlsWithDefaultState) == DeviceFlags.HasControlsWithDefaultState;
  543. set
  544. {
  545. if (value)
  546. m_DeviceFlags |= DeviceFlags.HasControlsWithDefaultState;
  547. else
  548. m_DeviceFlags &= ~DeviceFlags.HasControlsWithDefaultState;
  549. }
  550. }
  551. internal bool hasStateCallbacks
  552. {
  553. get => (m_DeviceFlags & DeviceFlags.HasStateCallbacks) == DeviceFlags.HasStateCallbacks;
  554. set
  555. {
  556. if (value)
  557. m_DeviceFlags |= DeviceFlags.HasStateCallbacks;
  558. else
  559. m_DeviceFlags &= ~DeviceFlags.HasStateCallbacks;
  560. }
  561. }
  562. internal void AddDeviceUsage(InternedString usage)
  563. {
  564. var controlUsageCount = m_UsageToControl.LengthSafe();
  565. var totalUsageCount = controlUsageCount + m_UsageCount;
  566. if (m_UsageCount == 0)
  567. m_UsageStartIndex = totalUsageCount;
  568. ArrayHelpers.AppendWithCapacity(ref m_UsagesForEachControl, ref totalUsageCount, usage);
  569. ++m_UsageCount;
  570. }
  571. internal void RemoveDeviceUsage(InternedString usage)
  572. {
  573. var controlUsageCount = m_UsageToControl.LengthSafe();
  574. var totalUsageCount = controlUsageCount + m_UsageCount;
  575. var index = ArrayHelpers.IndexOfValue(m_UsagesForEachControl, usage, m_UsageStartIndex, totalUsageCount);
  576. if (index == -1)
  577. return;
  578. Debug.Assert(m_UsageCount > 0);
  579. ArrayHelpers.EraseAtWithCapacity(m_UsagesForEachControl, ref totalUsageCount, index);
  580. --m_UsageCount;
  581. if (m_UsageCount == 0)
  582. m_UsageStartIndex = default;
  583. }
  584. internal void ClearDeviceUsages()
  585. {
  586. for (var i = m_UsageStartIndex; i < m_UsageCount; ++i)
  587. m_UsagesForEachControl[i] = default;
  588. m_UsageCount = default;
  589. }
  590. internal bool RequestReset()
  591. {
  592. var resetCommand = RequestResetCommand.Create();
  593. var result = device.ExecuteCommand(ref resetCommand);
  594. return result >= 0;
  595. }
  596. internal void NotifyAdded()
  597. {
  598. OnAdded();
  599. }
  600. internal void NotifyRemoved()
  601. {
  602. OnRemoved();
  603. }
  604. internal static TDevice Build<TDevice>(string layoutName = default, string layoutVariants = default, InputDeviceDescription deviceDescription = default)
  605. where TDevice : InputDevice
  606. {
  607. if (string.IsNullOrEmpty(layoutName))
  608. {
  609. layoutName = InputControlLayout.s_Layouts.TryFindLayoutForType(typeof(TDevice));
  610. if (string.IsNullOrEmpty(layoutName))
  611. layoutName = typeof(TDevice).Name;
  612. }
  613. using (InputDeviceBuilder.Ref())
  614. {
  615. InputDeviceBuilder.instance.Setup(new InternedString(layoutName), new InternedString(layoutVariants),
  616. deviceDescription: deviceDescription);
  617. var device = InputDeviceBuilder.instance.Finish();
  618. if (!(device is TDevice deviceOfType))
  619. throw new ArgumentException(
  620. $"Expected device of type '{typeof(TDevice).Name}' but got device of type '{device.GetType().Name}' instead",
  621. "TDevice");
  622. return deviceOfType;
  623. }
  624. }
  625. }
  626. }