InputControl.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using UnityEngine.InputSystem.Controls;
  5. using UnityEngine.InputSystem.LowLevel;
  6. using UnityEngine.InputSystem.Utilities;
  7. using Unity.Collections.LowLevel.Unsafe;
  8. using UnityEngine.InputSystem.Layouts;
  9. ////REVIEW: should EvaluateMagnitude() be called EvaluateActuation() or something similar?
  10. ////REVIEW: as soon as we gain the ability to have blittable type constraints, InputControl<TValue> should be constrained such
  11. ////REVIEW: Reading and writing is asymmetric. Writing does not involve processors, reading does.
  12. ////REVIEW: While the arrays used by controls are already nicely centralized on InputDevice, InputControls still
  13. //// hold a bunch of reference data that requires separate scanning. Can we move *all* reference data to arrays
  14. //// on InputDevice and make InputControls reference-free? Most challenging thing probably is getting rid of
  15. //// the InputDevice reference itself.
  16. ////REVIEW: how do we do stuff like smoothing over time?
  17. ////TODO: allow easier access to the default state such that you can easily create a state event containing only default state
  18. ////TODO: come up with a way where we do ReadValue on the most common forms/setups of controls and not have any virtual method dispatch but
  19. //// rather go with minimal overhead directly to reading out memory
  20. //// (this should at least cover FLT, single BIT, and INT controls; and should be able to apply the common transformations
  21. //// as per AxisControl)
  22. namespace UnityEngine.InputSystem
  23. {
  24. /// <summary>
  25. /// A typed and named source of input values in a hierarchy of controls.
  26. /// </summary>
  27. /// <remarks>
  28. /// Controls can have children which in turn may have children. At the root of the child
  29. /// hierarchy is always an <see cref="InputDevice"/> (which themselves are InputControls).
  30. ///
  31. /// Controls can be looked up by their <see cref="path"/> (see <see cref="InputControlPath.TryFindControl"/>).
  32. ///
  33. /// Each control must have a unique <see cref="name"/> within the <see cref="children"/> of
  34. /// its <see cref="parent"/>. Multiple names can be assigned to controls using aliases (see
  35. /// <see cref="aliases"/>). Name lookup is case-insensitive.
  36. ///
  37. /// For display purposes, a control may have a separate <see cref="displayName"/>. This name
  38. /// will usually correspond to what the control is caused on the actual underlying hardware.
  39. /// For example, on an Xbox gamepad, the control with the name "buttonSouth" will have a display
  40. /// name of "A". Controls that have very long display names may also have a <see cref="shortDisplayName"/>.
  41. /// This is the case for the "Left Button" on the <see cref="Mouse"/>, for example, which is
  42. /// commonly abbreviated "LMB".
  43. ///
  44. /// In addition to names, a control may have usages associated with it (see <see cref="usages"/>).
  45. /// A usage indicates how a control is meant to be used. For example, a button can be assigned
  46. /// the "PrimaryAction" usage to indicate it is the primary action button the device. Within a
  47. /// device, usages have to be unique. See <see cref="CommonUsages"/> for a list of standardized usages.
  48. ///
  49. /// Controls do not actually store values. Instead, every control receives an <see cref="InputStateBlock"/>
  50. /// which, after the control's device has been added to the system, is used to read out values
  51. /// from the device's backing store. This backing store is referred to as "state" in the API
  52. /// as opposed to "values" which represent the data resulting from reading state. The format that
  53. /// each control stores state in is specific to the control. It can vary not only between controls
  54. /// of different types but also between controls of the same type. An <see cref="AxisControl"/>,
  55. /// for example, can be stored as a float or as a byte or in a number of other formats. <see cref="stateBlock"/>
  56. /// identifies both where the control stores its state as well as the format it stores it in.
  57. ///
  58. /// Controls are generally not created directly but are created internally by the input system
  59. /// from data known as "layouts" (see <see cref="InputControlLayout"/>). Each such layout describes
  60. /// the setup of a specific hierarchy of controls. The system internally maintains a registry of
  61. /// layouts and produces devices and controls from them as needed. The layout that a control has
  62. /// been created from can be queried using <see cref="layout"/>. For most purposes, the intricacies
  63. /// of the control layout mechanisms can be ignored and it is sufficient to know the names of a
  64. /// small set of common device layouts such as "Keyboard", "Mouse", "Gamepad", and "Touchscreen".
  65. ///
  66. /// Each control has a single, fixed value type. The type can be queried at runtime using
  67. /// <see cref="valueType"/>. Most types of controls are derived from <see cref="InputControl{TValue}"/>
  68. /// which has APIs specific to the type of value of the control (e.g. <see cref="InputControl{TValue}.ReadValue()"/>.
  69. ///
  70. /// The following example demonstrates various common operations performed on input controls:
  71. ///
  72. /// <example>
  73. /// <code>
  74. /// // Look up dpad/up control on current gamepad.
  75. /// var dpadUpControl = Gamepad.current["dpad/up"];
  76. ///
  77. /// // Look up the back button on the current gamepad.
  78. /// var backButton = Gamepad.current["{Back}"];
  79. ///
  80. /// // Look up all dpad/up controls on all gamepads in the system.
  81. /// using (var controls = InputSystem.FindControls("&lt;Gamepad&gt;/dpad/up"))
  82. /// Debug.Log($"Found {controls.Count} controls");
  83. ///
  84. /// // Display the value of all controls on the current gamepad.
  85. /// foreach (var control in Gamepad.current.allControls)
  86. /// Debug.Log(controls.ReadValueAsObject());
  87. ///
  88. /// // Track the value of the left stick on the current gamepad over time.
  89. /// var leftStickHistory = new InputStateHistory(Gamepad.current.leftStick);
  90. /// leftStickHistory.Enable();
  91. /// </code>
  92. /// </example>
  93. /// <example>
  94. /// </example>
  95. /// </remarks>
  96. /// <see cref="InputControl{TValue}"/>
  97. /// <seealso cref="InputDevice"/>
  98. /// <seealso cref="InputControlPath"/>
  99. /// <seealso cref="InputStateBlock"/>
  100. [DebuggerDisplay("{DebuggerDisplay(),nq}")]
  101. [Scripting.Preserve]
  102. public abstract class InputControl
  103. {
  104. /// <summary>
  105. /// The name of the control, i.e. the final name part in its path.
  106. /// </summary>
  107. /// <remarks>
  108. /// Names of controls must be unique within the context of their parent.
  109. ///
  110. /// Note that this is the name of the control as assigned internally (like "buttonSouth")
  111. /// and not necessarily a good display name. Use <see cref="displayName"/> for
  112. /// getting more readable names for display purposes (where available).
  113. ///
  114. /// Lookup of names is case-insensitive.
  115. ///
  116. /// This is set from the name of the control in the layout.
  117. /// </remarks>
  118. /// <seealso cref="path"/>
  119. /// <seealso cref="aliases"/>
  120. /// <seealso cref="InputControlAttribute.name"/>
  121. /// <seealso cref="InputControlLayout.ControlItem.name"/>
  122. public string name => m_Name;
  123. ////TODO: protect against empty strings
  124. /// <summary>
  125. /// The text to display as the name of the control.
  126. /// </summary>
  127. /// <remarks>
  128. /// Note that the display name of a control may change over time. For example, when changing
  129. /// from a QWERTY keyboard layout to an AZERTY keyboard layout, the "q" key (which will keep
  130. /// that <see cref="name"/>) will change its display name from "q" to "a".
  131. ///
  132. /// By default, a control's display name will come from its layout. If it is not assigned
  133. /// a display name there, the display name will default to <see cref="name"/>. However, specific
  134. /// controls may override this behavior. <see cref="KeyControl"/>, for example, will set the
  135. /// display name to the actual key name corresponding to the current keyboard layout.
  136. ///
  137. /// For nested controls, the display name will include the display names of all parent controls,
  138. /// i.e. the display name will fully identify the control on the device. For example, the display
  139. /// name for the left D-Pad button on a gamepad is "D-Pad Left" and not just "Left".
  140. /// </remarks>
  141. /// <seealso cref="shortDisplayName"/>
  142. public string displayName
  143. {
  144. get
  145. {
  146. RefreshConfigurationIfNeeded();
  147. if (m_DisplayName != null)
  148. return m_DisplayName;
  149. if (m_DisplayNameFromLayout != null)
  150. return m_DisplayNameFromLayout;
  151. return m_Name;
  152. }
  153. // This is not public as a domain reload will wipe the change. This should really
  154. // come from the control itself *if* the control wants to have a custom display name
  155. // not driven by its layout.
  156. protected set => m_DisplayName = value;
  157. }
  158. /// <summary>
  159. /// An alternate, abbreviated <see cref="displayName"/> (for example "LMB" instead of "Left Button").
  160. /// </summary>
  161. /// <remarks>
  162. /// If the control has no abbreviated version, this will be null. Note that this behavior is different
  163. /// from <see cref="displayName"/> which will fall back to <see cref="name"/> if no display name has
  164. /// been assigned to the control.
  165. ///
  166. /// For nested controls, the short display name will include the short display names of all parent controls,
  167. /// i.e. the display name will fully identify the control on the device. For example, the display
  168. /// name for the left D-Pad button on a gamepad is "D-Pad \u2190" and not just "\u2190". Note that if a parent
  169. /// control has no short name, its long name will be used instead.
  170. /// </remarks>
  171. /// <seealso cref="displayName"/>
  172. public string shortDisplayName
  173. {
  174. get
  175. {
  176. RefreshConfigurationIfNeeded();
  177. if (m_ShortDisplayName != null)
  178. return m_ShortDisplayName;
  179. if (m_ShortDisplayNameFromLayout != null)
  180. return m_ShortDisplayNameFromLayout;
  181. return null;
  182. }
  183. protected set => m_ShortDisplayName = value;
  184. }
  185. /// <summary>
  186. /// Full path all the way from the root.
  187. /// </summary>
  188. /// <remarks>
  189. /// This will always be the "effective" path of the control, i.e. it will not contain
  190. /// elements such as usages (<c>"{Back}"</c>) and other elements that can be part of
  191. /// control paths used for matching. Instead, this property will always be a simple
  192. /// linear ordering of names leading from the device at the top to the control with each
  193. /// element being separated by a forward slash (<c>/</c>).
  194. ///
  195. /// Allocates on first hit. Paths are not created until someone asks for them.
  196. ///
  197. /// <example>
  198. /// Example: "/gamepad/leftStick/x"
  199. /// </example>
  200. /// </remarks>
  201. /// <seealso cref="InputControlPath"/>
  202. public string path
  203. {
  204. get
  205. {
  206. if (m_Path == null)
  207. m_Path = InputControlPath.Combine(m_Parent, m_Name);
  208. return m_Path;
  209. }
  210. }
  211. /// <summary>
  212. /// Layout the control is based on.
  213. /// </summary>
  214. /// <remarks>
  215. /// This is the layout name rather than a reference to an <see cref="InputControlLayout"/> as
  216. /// we only create layout instances during device creation and treat them
  217. /// as temporaries in general so as to not waste heap space during normal operation.
  218. /// </remarks>
  219. public string layout => m_Layout;
  220. /// <summary>
  221. /// Semicolon-separated list of variants of the control layout or "default".
  222. /// </summary>
  223. /// <example>
  224. /// "Lefty" when using the "Lefty" gamepad layout.
  225. /// </example>
  226. public string variants => m_Variants;
  227. /// <summary>
  228. /// The device that this control is a part of.
  229. /// </summary>
  230. /// <remarks>
  231. /// This is the root of the control hierarchy. For the device at the root, this
  232. /// will point to itself.
  233. /// </remarks>
  234. /// <seealso cref="InputDevice.allControls"/>
  235. public InputDevice device => m_Device;
  236. /// <summary>
  237. /// The immediate parent of the control or null if the control has no parent
  238. /// (which, once fully constructed) will only be the case for InputDevices).
  239. /// </summary>
  240. /// <seealso cref="children"/>
  241. public InputControl parent => m_Parent;
  242. /// <summary>
  243. /// List of immediate children.
  244. /// </summary>
  245. /// <remarks>
  246. /// Does not allocate.
  247. /// </remarks>
  248. /// <seealso cref="parent"/>
  249. public ReadOnlyArray<InputControl> children =>
  250. new ReadOnlyArray<InputControl>(m_Device.m_ChildrenForEachControl, m_ChildStartIndex, m_ChildCount);
  251. /// <summary>
  252. /// List of usage tags associated with the control.
  253. /// </summary>
  254. /// <remarks>
  255. /// Usages apply "semantics" to a control. Whereas the name of a control identifies a particular
  256. /// "endpoint" within the control hierarchy, the usages of a control identify particular roles
  257. /// of specific control. A simple example is <see cref="CommonUsages.Back"/> which identifies a
  258. /// control generally used to move backwards in the navigation history of a UI. On a keyboard,
  259. /// it is the escape key that generally fulfills this role whereas on a gamepad, it is generally
  260. /// the "B" / "Circle" button. Some devices may not have a control that generally fulfills this
  261. /// function and thus may not have any control with the "Back" usage.
  262. ///
  263. /// By looking up controls by usage rather than by name, it is possible to locate the correct
  264. /// control to use for certain standardized situation without having to know the particulars of
  265. /// the device or platform.
  266. ///
  267. /// <example>
  268. /// <code>
  269. /// // Bind to any control which is tagged with the "Back" usage on any device.
  270. /// var backAction = new InputAction(binding: "*/{Back}");
  271. /// </code>
  272. /// </example>
  273. ///
  274. /// Note that usages on devices work slightly differently than usages of controls on devices.
  275. /// They are also queried through this property but unlike the usages of controls, the set of
  276. /// usages of a device can be changed dynamically as the role of the device changes. For details,
  277. /// see <see cref="InputSystem.SetDeviceUsage(InputDevice,string)"/>. Controls, on the other hand,
  278. /// can currently only be assigned usages through layouts (<see cref="InputControlAttribute.usage"/>
  279. /// or <see cref="InputControlAttribute.usages"/>).
  280. /// </remarks>
  281. /// <seealso cref="InputControlAttribute.usage"/>
  282. /// <seealso cref="InputControlAttribute.usages"/>
  283. /// <seealso cref="InputSystem.SetDeviceUsage(InputDevice,string)"/>
  284. /// <seealso cref="InputSystem.AddDeviceUsage(InputDevice,string)"/>
  285. /// <seealso cref="InputSystem.RemoveDeviceUsage(InputDevice,string)"/>
  286. /// <seealso cref="CommonUsages"/>
  287. public ReadOnlyArray<InternedString> usages =>
  288. new ReadOnlyArray<InternedString>(m_Device.m_UsagesForEachControl, m_UsageStartIndex, m_UsageCount);
  289. // List of alternate names for the control.
  290. public ReadOnlyArray<InternedString> aliases =>
  291. new ReadOnlyArray<InternedString>(m_Device.m_AliasesForEachControl, m_AliasStartIndex, m_AliasCount);
  292. // Information about where the control stores its state.
  293. public InputStateBlock stateBlock => m_StateBlock;
  294. /// <summary>
  295. /// Whether the control is considered noisy.
  296. /// </summary>
  297. /// <value>True if the control produces noisy input.</value>
  298. /// <remarks>
  299. /// A control is considered "noisy" if it produces different values without necessarily requiring user
  300. /// interaction. A good example are sensors (see <see cref="Sensor"/>). For example, the PS4 controller
  301. /// which has a gyroscope sensor built into the device. Whereas sticks and buttons on the device require
  302. /// user interaction to produce non-default values, the gyro will produce varying values even if the
  303. /// device just sits there without user interaction.
  304. ///
  305. /// The value of this property is determined by the layout (<see cref="InputControlLayout"/>) that the
  306. /// control has been built from.
  307. ///
  308. /// Note that for devices (<see cref="InputDevice"/>) this property is true if any control on the device
  309. /// is marked as noisy.
  310. ///
  311. /// The primary effect of being noise is on <see cref="InputDevice.MakeCurrent"/> and
  312. /// on interactive rebinding (see <see cref="InputActionRebindingExtensions.RebindingOperation"/>).
  313. /// However, being noisy also affects automatic resetting of controls that happens when the application
  314. /// loses focus. While other controls are reset to their default value (except if <c>Application.runInBackground</c>
  315. /// is true and the device the control belongs to is marked as <see cref="InputDevice.canRunInBackground"/>),
  316. /// noisy controls will not be reset but rather remain at their current value. This is based on the assumption
  317. /// that noisy controls most often represent sensor values and snapping the last sampling value back to default
  318. /// will usually have undesirable effects on an application's simulation logic.
  319. /// </remarks>
  320. /// <seealso cref="InputControlLayout.ControlItem.isNoisy"/>
  321. /// <seealso cref="InputControlAttribute.noisy"/>
  322. public bool noisy
  323. {
  324. get => (m_ControlFlags & ControlFlags.IsNoisy) != 0;
  325. internal set
  326. {
  327. if (value)
  328. {
  329. m_ControlFlags |= ControlFlags.IsNoisy;
  330. // Making a control noisy makes all its children noisy.
  331. var list = children;
  332. for (var i = 0; i < list.Count; ++i)
  333. list[i].noisy = true;
  334. }
  335. else
  336. m_ControlFlags &= ~ControlFlags.IsNoisy;
  337. }
  338. }
  339. /// <summary>
  340. /// Whether the control is considered synthetic.
  341. /// </summary>
  342. /// <value>True if the control does not represent an actual physical control on the device.</value>
  343. /// <remarks>
  344. /// A control is considered "synthetic" if it does not correspond to an actual, physical control on the
  345. /// device. An example for this is <see cref="Keyboard.anyKey"/> or the up/down/left/right buttons added
  346. /// by <see cref="StickControl"/>.
  347. ///
  348. /// The value of this property is determined by the layout (<see cref="InputControlLayout"/>) that the
  349. /// control has been built from.
  350. ///
  351. /// The primary effect of being synthetic is in interactive rebinding (see
  352. /// <see cref="InputActionRebindingExtensions.RebindingOperation"/>) where non-synthetic
  353. /// controls will be favored over synthetic ones. This means, for example, that if both
  354. /// <c>"&lt;Gamepad&gt;/leftStick/x"</c> and <c>"&lt;Gamepad&gt;/leftStick/left"</c> are
  355. /// suitable picks, <c>"&lt;Gamepad&gt;/leftStick/x"</c> will be favored as it represents
  356. /// input from an actual physical control whereas <c>"&lt;Gamepad&gt;/leftStick/left"</c>
  357. /// represents input from a made-up control. If, however, the "left" button is the only
  358. /// viable pick, it will be accepted.
  359. /// </remarks>
  360. /// <seealso cref="InputControlLayout.ControlItem.isSynthetic"/>
  361. /// <seealso cref="InputControlAttribute.synthetic"/>
  362. public bool synthetic
  363. {
  364. get => (m_ControlFlags & ControlFlags.IsSynthetic) != 0;
  365. internal set
  366. {
  367. if (value)
  368. m_ControlFlags |= ControlFlags.IsSynthetic;
  369. else
  370. m_ControlFlags &= ~ControlFlags.IsSynthetic;
  371. }
  372. }
  373. /// <summary>
  374. /// Fetch a control from the control's hierarchy by name.
  375. /// </summary>
  376. /// <remarks>
  377. /// Note that path matching is case-insensitive.
  378. /// </remarks>
  379. /// <example>
  380. /// <code>
  381. /// gamepad["leftStick"] // Returns Gamepad.leftStick
  382. /// gamepad["leftStick/x"] // Returns Gamepad.leftStick.x
  383. /// gamepad["{PrimaryAction}"] // Returns the control with PrimaryAction usage, i.e. Gamepad.aButton
  384. /// </code>
  385. /// </example>
  386. /// <exception cref="KeyNotFoundException"><paramref name="path"/> cannot be found.</exception>
  387. /// <seealso cref="InputControlPath"/>
  388. /// <seealso cref="path"/>
  389. /// <seealso cref="TryGetChildControl"/>
  390. public InputControl this[string path]
  391. {
  392. get
  393. {
  394. var control = InputControlPath.TryFindChild(this, path);
  395. if (control == null)
  396. throw new KeyNotFoundException(
  397. $"Cannot find control '{path}' as child of '{this}'");
  398. return control;
  399. }
  400. }
  401. /// <summary>
  402. /// Returns the underlying value type of this control.
  403. /// </summary>
  404. /// <value>Type of values produced by the control.</value>
  405. /// <remarks>
  406. /// This is the type of values that are returned when reading the current value of a control
  407. /// or when reading a value of a control from an event.
  408. /// </remarks>
  409. /// <seealso cref="valueSizeInBytes"/>
  410. /// <seealso cref="ReadValueFromStateAsObject"/>
  411. public abstract Type valueType { get; }
  412. /// <summary>
  413. /// Size in bytes of values that the control returns.
  414. /// </summary>
  415. /// <seealso cref="valueType"/>
  416. public abstract int valueSizeInBytes { get; }
  417. /// <summary>
  418. /// Return a string representation of the control useful for debugging.
  419. /// </summary>
  420. /// <returns>A string representation of the control.</returns>
  421. public override string ToString()
  422. {
  423. return $"{layout}:{path}";
  424. }
  425. private string DebuggerDisplay()
  426. {
  427. // If the device hasn't been added, don't try to read the control's value.
  428. if (!device.added)
  429. return ToString();
  430. // ReadValueAsObject might throw. Revert to just ToString() in that case.
  431. try
  432. {
  433. return $"{layout}:{path}={this.ReadValueAsObject()}";
  434. }
  435. catch (Exception)
  436. {
  437. return ToString();
  438. }
  439. }
  440. /// <summary>
  441. /// Compute an absolute, normalized magnitude value that indicates the extent to which the control
  442. /// is actuated.
  443. /// </summary>
  444. /// <returns>Amount of actuation of the control or -1 if it cannot be determined.</returns>
  445. /// <remarks>
  446. /// Magnitudes do not make sense for all types of controls. For example, for a control that represents
  447. /// an enumeration of values (such as <see cref="TouchPhaseControl"/>), there is no meaningful
  448. /// linear ordering of values (one could derive a linear ordering through the actual enum values but
  449. /// their assignment may be entirely arbitrary; it is unclear whether a state of <see cref="TouchPhase.Canceled"/>
  450. /// has a higher or lower "magnitude" as a state of <see cref="TouchPhase.Began"/>).
  451. ///
  452. /// Controls that have no meaningful magnitude will return -1 when calling this method. Any negative
  453. /// return value should be considered an invalid value.
  454. /// </remarks>
  455. /// <seealso cref="EvaluateMagnitude(void*)"/>
  456. public unsafe float EvaluateMagnitude()
  457. {
  458. return EvaluateMagnitude(currentStatePtr);
  459. }
  460. /// <summary>
  461. /// Compute an absolute, normalized magnitude value that indicates the extent to which the control
  462. /// is actuated in the given state.
  463. /// </summary>
  464. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>.</param>
  465. /// <returns>Amount of actuation of the control or -1 if it cannot be determined.</returns>
  466. /// <seealso cref="EvaluateMagnitude()"/>
  467. /// <seealso cref="stateBlock"/>
  468. public virtual unsafe float EvaluateMagnitude(void* statePtr)
  469. {
  470. return -1;
  471. }
  472. public abstract unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize);
  473. /// <summary>
  474. /// Read the control's final, processed value from the given state and return the value as an object.
  475. /// </summary>
  476. /// <param name="statePtr"></param>
  477. /// <returns>The control's value as stored in <paramref name="statePtr"/>.</returns>
  478. /// <remarks>
  479. /// This method allocates GC memory and should not be used during normal gameplay operation.
  480. /// </remarks>
  481. /// <exception cref="ArgumentNullException"><paramref name="statePtr"/> is null.</exception>
  482. /// <seealso cref="ReadValueFromStateIntoBuffer"/>
  483. public abstract unsafe object ReadValueFromStateAsObject(void* statePtr);
  484. /// <summary>
  485. /// Read the control's final, processed value from the given state and store it in the given buffer.
  486. /// </summary>
  487. /// <param name="statePtr">State to read the value for the control from.</param>
  488. /// <param name="bufferPtr">Buffer to store the value in.</param>
  489. /// <param name="bufferSize">Size of <paramref name="bufferPtr"/> in bytes. Must be at least <see cref="valueSizeInBytes"/>.
  490. /// If it is smaller, <see cref="ArgumentException"/> will be thrown.</param>
  491. /// <exception cref="ArgumentNullException"><paramref name="statePtr"/> is null, or <paramref name="bufferPtr"/> is null.</exception>
  492. /// <exception cref="ArgumentException"><paramref name="bufferSize"/> is smaller than <see cref="valueSizeInBytes"/>.</exception>
  493. /// <seealso cref="ReadValueFromStateAsObject"/>
  494. /// <seealso cref="WriteValueFromBufferIntoState"/>
  495. public abstract unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize);
  496. /// <summary>
  497. /// Read a value from the given memory and store it as state.
  498. /// </summary>
  499. /// <param name="bufferPtr">Memory containing value.</param>
  500. /// <param name="bufferSize">Size of <paramref name="bufferPtr"/> in bytes. Must be at least <see cref="valueSizeInBytes"/>.</param>
  501. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>. Will receive the state
  502. /// as converted from the given value.</param>
  503. /// <remarks>
  504. /// Writing values will NOT apply processors to the given value. This can mean that when reading a value
  505. /// from a control after it has been written to its state, the resulting value differs from what was
  506. /// written.
  507. /// </remarks>
  508. /// <exception cref="NotSupportedException">The control does not support writing. This is the case, for
  509. /// example, that compute values (such as the magnitude of a vector).</exception>
  510. /// <seealso cref="ReadValueFromStateIntoBuffer"/>
  511. /// <seealso cref="WriteValueFromObjectIntoState"/>
  512. public virtual unsafe void WriteValueFromBufferIntoState(void* bufferPtr, int bufferSize, void* statePtr)
  513. {
  514. throw new NotSupportedException(
  515. $"Control '{this}' does not support writing");
  516. }
  517. /// <summary>
  518. /// Read a value object and store it as state in the given memory.
  519. /// </summary>
  520. /// <param name="value">Value for the control.</param>
  521. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>. Will receive
  522. /// the state state as converted from the given value.</param>
  523. /// <remarks>
  524. /// Writing values will NOT apply processors to the given value. This can mean that when reading a value
  525. /// from a control after it has been written to its state, the resulting value differs from what was
  526. /// written.
  527. /// </remarks>
  528. /// <exception cref="NotSupportedException">The control does not support writing. This is the case, for
  529. /// example, that compute values (such as the magnitude of a vector).</exception>
  530. /// <seealso cref="WriteValueFromBufferIntoState"/>
  531. public virtual unsafe void WriteValueFromObjectIntoState(object value, void* statePtr)
  532. {
  533. throw new NotSupportedException(
  534. $"Control '{this}' does not support writing");
  535. }
  536. /// <summary>
  537. /// Compare the value of the control as read from <paramref name="firstStatePtr"/> to that read from
  538. /// <paramref name="secondStatePtr"/> and return true if they are equal.
  539. /// </summary>
  540. /// <param name="firstStatePtr">Memory containing the control's <see cref="stateBlock"/>.</param>
  541. /// <param name="secondStatePtr">Memory containing the control's <see cref="stateBlock"/></param>
  542. /// <returns>True if the value of the control is equal in both <paramref name="firstStatePtr"/> and
  543. /// <paramref name="secondStatePtr"/>.</returns>
  544. /// <remarks>
  545. /// Unlike <see cref="CompareState"/>, this method will have to do more than just compare the memory
  546. /// for the control in the two state buffers. It will have to read out state for the control and run
  547. /// the full processing machinery for the control to turn the state into a final, processed value.
  548. /// CompareValue is thus more costly than <see cref="CompareState"/>.
  549. ///
  550. /// This method will apply epsilons (<see cref="Mathf.Epsilon"/>) when comparing floats.
  551. /// </remarks>
  552. /// <seealso cref="CompareState"/>
  553. public abstract unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr);
  554. /// <summary>
  555. /// Try to find a child control matching the given path.
  556. /// </summary>
  557. /// <param name="path">A control path. See <see cref="InputControlPath"/>.</param>
  558. /// <returns>The first direct or indirect child control that matches the given <paramref name="path"/>
  559. /// or null if no control was found to match.</returns>
  560. /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c> or empty.</exception>
  561. /// <remarks>
  562. /// Note that if the given path matches multiple child controls, only the first control
  563. /// encountered in the search will be returned.
  564. ///
  565. /// <example>
  566. /// <code>
  567. /// // Returns the leftStick control of the current gamepad.
  568. /// Gamepad.current.TryGetChildControl("leftStick");
  569. ///
  570. /// // Returns the X axis control of the leftStick on the current gamepad.
  571. /// Gamepad.current.TryGetChildControl("leftStick/x");
  572. ///
  573. /// // Returns the first control ending with "stick" in its name. Note that it
  574. /// // undetermined whether this is leftStick or rightStick (or even another stick
  575. /// // added by the given gamepad).
  576. /// Gamepad.current.TryGetChildControl("*stick");
  577. /// </code>
  578. /// </example>
  579. ///
  580. /// This method is equivalent to calling <see cref="InputControlPath.TryFindChild"/>.
  581. /// </remarks>
  582. public InputControl TryGetChildControl(string path)
  583. {
  584. if (string.IsNullOrEmpty(path))
  585. throw new ArgumentNullException(nameof(path));
  586. return InputControlPath.TryFindChild(this, path);
  587. }
  588. public TControl TryGetChildControl<TControl>(string path)
  589. where TControl : InputControl
  590. {
  591. if (string.IsNullOrEmpty(path))
  592. throw new ArgumentNullException(nameof(path));
  593. var control = TryGetChildControl(path);
  594. if (control == null)
  595. return null;
  596. var controlOfType = control as TControl;
  597. if (controlOfType == null)
  598. throw new InvalidOperationException(
  599. $"Expected control '{path}' to be of type '{typeof(TControl).Name}' but is of type '{control.GetType().Name}' instead!");
  600. return controlOfType;
  601. }
  602. public InputControl GetChildControl(string path)
  603. {
  604. if (string.IsNullOrEmpty(path))
  605. throw new ArgumentNullException(nameof(path));
  606. var control = TryGetChildControl(path);
  607. if (control == null)
  608. throw new ArgumentException($"Cannot find input control '{MakeChildPath(path)}'", nameof(path));
  609. return control;
  610. }
  611. public TControl GetChildControl<TControl>(string path)
  612. where TControl : InputControl
  613. {
  614. var control = GetChildControl(path);
  615. if (!(control is TControl controlOfType))
  616. throw new ArgumentException(
  617. $"Expected control '{path}' to be of type '{typeof(TControl).Name}' but is of type '{control.GetType().Name}' instead!", nameof(path));
  618. return controlOfType;
  619. }
  620. protected InputControl()
  621. {
  622. // Set defaults for state block setup. Subclasses may override.
  623. m_StateBlock.byteOffset = InputStateBlock.AutomaticOffset; // Request automatic layout by default.
  624. }
  625. /// <summary>
  626. /// Perform final initialization tasks after the control hierarchy has been put into place.
  627. /// </summary>
  628. /// <remarks>
  629. /// This method can be overridden to perform control- or device-specific setup work. The most
  630. /// common use case is for looking up child controls and storing them in local getters.
  631. ///
  632. /// <example>
  633. /// <code>
  634. /// public class MyDevice : InputDevice
  635. /// {
  636. /// public ButtonControl button { get; private set; }
  637. /// public AxisControl axis { get; private set; }
  638. ///
  639. /// protected override void OnFinishSetup()
  640. /// {
  641. /// // Cache controls in getters.
  642. /// button = GetChildControl("button");
  643. /// axis = GetChildControl("axis");
  644. /// }
  645. /// }
  646. /// </code>
  647. /// </example>
  648. /// </remarks>
  649. protected virtual void FinishSetup()
  650. {
  651. }
  652. /// <summary>
  653. /// Call <see cref="RefreshConfiguration"/> if the configuration has in the interim been invalidated
  654. /// by a <see cref="DeviceConfigurationEvent"/>.
  655. /// </summary>
  656. /// <remarks>
  657. /// This method is only relevant if you are implementing your own devices or new
  658. /// types of controls which are fetching configuration data from the devices (such
  659. /// as <see cref="KeyControl"/> which is fetching display names for individual keys
  660. /// from the underlying platform).
  661. ///
  662. /// This method should be called if you are accessing cached data set up by
  663. /// <see cref="RefreshConfiguration"/>.
  664. ///
  665. /// <example>
  666. /// <code>
  667. /// // Let's say your device has an associated orientation which it can be held with
  668. /// // and you want to surface both as a property and as a usage on the device.
  669. /// // Whenever your backend code detects a change in orientation, it should send
  670. /// // a DeviceConfigurationEvent to your device to signal that the configuration
  671. /// // of the device has changed. You can then implement RefreshConfiguration() to
  672. /// // read out and update the device orientation on the managed InputDevice instance.
  673. /// public class MyDevice : InputDevice
  674. /// {
  675. /// public enum Orientation
  676. /// {
  677. /// Horizontal,
  678. /// Vertical,
  679. /// }
  680. ///
  681. /// private Orientation m_Orientation;
  682. /// public Orientation orientation
  683. /// {
  684. /// get
  685. /// {
  686. /// // Call RefreshOrientation if the configuration of the device has been
  687. /// // invalidated since last time we initialized m_Orientation.
  688. /// RefreshConfigurationIfNeeded();
  689. /// return m_Orientation;
  690. /// }
  691. /// }
  692. /// protected override void RefreshConfiguration()
  693. /// {
  694. /// // Fetch the current orientation from the backend. How you do this
  695. /// // depends on your device. Using DeviceCommands is one way.
  696. /// var fetchOrientationCommand = new FetchOrientationCommand();
  697. /// ExecuteCommand(ref fetchOrientationCommand);
  698. /// m_Orientation = fetchOrientation;
  699. ///
  700. /// // Reflect the orientation on the device.
  701. /// switch (m_Orientation)
  702. /// {
  703. /// case Orientation.Vertical:
  704. /// InputSystem.RemoveDeviceUsage(this, s_Horizontal);
  705. /// InputSystem.AddDeviceUsage(this, s_Vertical);
  706. /// break;
  707. ///
  708. /// case Orientation.Horizontal:
  709. /// InputSystem.RemoveDeviceUsage(this, s_Vertical);
  710. /// InputSystem.AddDeviceUsage(this, s_Horizontal);
  711. /// break;
  712. /// }
  713. /// }
  714. ///
  715. /// private static InternedString s_Vertical = new InternedString("Vertical");
  716. /// private static InternedString s_Horizontal = new InternedString("Horizontal");
  717. /// }
  718. /// </code>
  719. /// </example>
  720. /// </remarks>
  721. /// <seealso cref="RefreshConfiguration"/>
  722. protected void RefreshConfigurationIfNeeded()
  723. {
  724. if (!isConfigUpToDate)
  725. {
  726. RefreshConfiguration();
  727. isConfigUpToDate = true;
  728. }
  729. }
  730. protected virtual void RefreshConfiguration()
  731. {
  732. }
  733. protected internal InputStateBlock m_StateBlock;
  734. ////REVIEW: shouldn't these sit on the device?
  735. protected internal unsafe void* currentStatePtr => InputStateBuffers.GetFrontBufferForDevice(ResolveDeviceIndex());
  736. protected internal unsafe void* previousFrameStatePtr => InputStateBuffers.GetBackBufferForDevice(ResolveDeviceIndex());
  737. protected internal unsafe void* defaultStatePtr => InputStateBuffers.s_DefaultStateBuffer;
  738. /// <summary>
  739. /// Return the memory that holds the noise mask for the control.
  740. /// </summary>
  741. /// <value>Noise bit mask for the control.</value>
  742. /// <remarks>
  743. /// Like with all state blocks, the specific memory block for the control is found at the memory
  744. /// region specified by <see cref="stateBlock"/>.
  745. ///
  746. /// The noise mask can be overlaid as a bit mask over the state for the control. When doing so, all state
  747. /// that is noise will be masked out whereas all state that isn't will come through unmodified. In other words,
  748. /// any bit that is set in the noise mask indicates that the corresponding bit in the control's state memory
  749. /// is noise.
  750. /// </remarks>
  751. /// <seealso cref="noisy"/>
  752. protected internal unsafe void* noiseMaskPtr => InputStateBuffers.s_NoiseMaskBuffer;
  753. /// <summary>
  754. /// The offset of this control's state relative to its device root.
  755. /// </summary>
  756. /// <remarks>
  757. /// Once a device has been added to the system, its state block will get allocated
  758. /// in the global state buffers and the offset of the device's state block will
  759. /// get baked into all of the controls on the device. This property always returns
  760. /// the "unbaked" offset.
  761. /// </remarks>
  762. protected internal uint stateOffsetRelativeToDeviceRoot
  763. {
  764. get
  765. {
  766. var deviceStateOffset = device.m_StateBlock.byteOffset;
  767. Debug.Assert(deviceStateOffset <= m_StateBlock.byteOffset);
  768. return m_StateBlock.byteOffset - deviceStateOffset;
  769. }
  770. }
  771. // This data is initialized by InputDeviceBuilder.
  772. internal InternedString m_Name;
  773. internal string m_Path;
  774. internal string m_DisplayName; // Display name set by the control itself (may be null).
  775. internal string m_DisplayNameFromLayout; // Display name coming from layout (may be null).
  776. internal string m_ShortDisplayName; // Short display name set by the control itself (may be null).
  777. internal string m_ShortDisplayNameFromLayout; // Short display name coming from layout (may be null).
  778. internal InternedString m_Layout;
  779. internal InternedString m_Variants;
  780. internal InputDevice m_Device;
  781. internal InputControl m_Parent;
  782. internal int m_UsageCount;
  783. internal int m_UsageStartIndex;
  784. internal int m_AliasCount;
  785. internal int m_AliasStartIndex;
  786. internal int m_ChildCount;
  787. internal int m_ChildStartIndex;
  788. internal ControlFlags m_ControlFlags;
  789. ////REVIEW: store these in arrays in InputDevice instead?
  790. internal PrimitiveValue m_DefaultState;
  791. internal PrimitiveValue m_MinValue;
  792. internal PrimitiveValue m_MaxValue;
  793. [Flags]
  794. internal enum ControlFlags
  795. {
  796. ConfigUpToDate = 1 << 0,
  797. IsNoisy = 1 << 1,
  798. IsSynthetic = 1 << 2,
  799. }
  800. internal bool isConfigUpToDate
  801. {
  802. get => (m_ControlFlags & ControlFlags.ConfigUpToDate) == ControlFlags.ConfigUpToDate;
  803. set
  804. {
  805. if (value)
  806. m_ControlFlags |= ControlFlags.ConfigUpToDate;
  807. else
  808. m_ControlFlags &= ~ControlFlags.ConfigUpToDate;
  809. }
  810. }
  811. internal bool hasDefaultState => !m_DefaultState.isEmpty;
  812. // This method exists only to not slap the internal interaction on all overrides of
  813. // FinishSetup().
  814. internal void CallFinishSetupRecursive()
  815. {
  816. var list = children;
  817. for (var i = 0; i < list.Count; ++i)
  818. list[i].CallFinishSetupRecursive();
  819. FinishSetup();
  820. }
  821. internal string MakeChildPath(string path)
  822. {
  823. if (this is InputDevice)
  824. return path;
  825. return $"{this.path}/{path}";
  826. }
  827. internal void BakeOffsetIntoStateBlockRecursive(uint offset)
  828. {
  829. m_StateBlock.byteOffset += offset;
  830. var list = children;
  831. for (var i = 0; i < list.Count; ++i)
  832. list[i].BakeOffsetIntoStateBlockRecursive(offset);
  833. }
  834. internal int ResolveDeviceIndex()
  835. {
  836. var deviceIndex = m_Device.m_DeviceIndex;
  837. if (deviceIndex == InputDevice.kInvalidDeviceIndex)
  838. throw new InvalidOperationException(
  839. $"Cannot query value of control '{path}' before '{device.name}' has been added to system!");
  840. return deviceIndex;
  841. }
  842. internal virtual void AddProcessor(object first)
  843. {
  844. }
  845. }
  846. /// <summary>
  847. /// Base class for input controls with a specific value type.
  848. /// </summary>
  849. /// <typeparam name="TValue">Type of value captured by the control. Note that this does not mean
  850. /// that the control has to store data in the given value format. A control that captures float
  851. /// values, for example, may be stored in state as byte values instead.</typeparam>
  852. [Scripting.Preserve]
  853. public abstract class InputControl<TValue> : InputControl
  854. where TValue : struct
  855. {
  856. public override Type valueType => typeof(TValue);
  857. public override int valueSizeInBytes => UnsafeUtility.SizeOf<TValue>();
  858. /// <summary>
  859. /// Get the control's current value as read from <see cref="InputControl.currentStatePtr"/>
  860. /// </summary>
  861. /// <returns>The control's current value.</returns>
  862. /// <remarks>
  863. /// This can only be called on devices that have been added to the system (<see cref="InputDevice.added"/>).
  864. /// </remarks>
  865. public TValue ReadValue()
  866. {
  867. unsafe
  868. {
  869. return ReadValueFromState(currentStatePtr);
  870. }
  871. }
  872. ////REVIEW: is 'frame' really the best wording here?
  873. /// <summary>
  874. /// Get the control's value from the previous frame (<see cref="InputControl.previousFrameStatePtr"/>).
  875. /// </summary>
  876. /// <returns>The control's value in the previous frame.</returns>
  877. public TValue ReadValueFromPreviousFrame()
  878. {
  879. unsafe
  880. {
  881. return ReadValueFromState(previousFrameStatePtr);
  882. }
  883. }
  884. /// <summary>
  885. /// Get the control's default value.
  886. /// </summary>
  887. /// <returns>The control's default value.</returns>
  888. /// <remarks>
  889. /// This is not necessarily equivalent to <c>default(TValue)</c>. A control's default value is determined
  890. /// by reading its value from the default state (<see cref="InputControl.defaultStatePtr"/>) which in turn
  891. /// is determined from settings in the control's registered layout (<see cref="InputControlLayout.ControlItem.defaultState"/>).
  892. /// </remarks>
  893. public TValue ReadDefaultValue()
  894. {
  895. unsafe
  896. {
  897. return ReadValueFromState(defaultStatePtr);
  898. }
  899. }
  900. public unsafe TValue ReadValueFromState(void* statePtr)
  901. {
  902. if (statePtr == null)
  903. throw new ArgumentNullException(nameof(statePtr));
  904. return ProcessValue(ReadUnprocessedValueFromState(statePtr));
  905. }
  906. public TValue ReadUnprocessedValue()
  907. {
  908. unsafe
  909. {
  910. return ReadUnprocessedValueFromState(currentStatePtr);
  911. }
  912. }
  913. public abstract unsafe TValue ReadUnprocessedValueFromState(void* statePtr);
  914. /// <inheritdoc />
  915. public override unsafe object ReadValueFromStateAsObject(void* statePtr)
  916. {
  917. return ReadValueFromState(statePtr);
  918. }
  919. /// <inheritdoc />
  920. public override unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize)
  921. {
  922. if (statePtr == null)
  923. throw new ArgumentNullException(nameof(statePtr));
  924. if (bufferPtr == null)
  925. throw new ArgumentNullException(nameof(bufferPtr));
  926. var numBytes = UnsafeUtility.SizeOf<TValue>();
  927. if (bufferSize < numBytes)
  928. throw new ArgumentException(
  929. $"bufferSize={bufferSize} < sizeof(TValue)={numBytes}", nameof(bufferSize));
  930. var value = ReadValueFromState(statePtr);
  931. var valuePtr = UnsafeUtility.AddressOf(ref value);
  932. UnsafeUtility.MemCpy(bufferPtr, valuePtr, numBytes);
  933. }
  934. public override unsafe void WriteValueFromBufferIntoState(void* bufferPtr, int bufferSize, void* statePtr)
  935. {
  936. if (bufferPtr == null)
  937. throw new ArgumentNullException(nameof(bufferPtr));
  938. if (statePtr == null)
  939. throw new ArgumentNullException(nameof(statePtr));
  940. var numBytes = UnsafeUtility.SizeOf<TValue>();
  941. if (bufferSize < numBytes)
  942. throw new ArgumentException(
  943. $"bufferSize={bufferSize} < sizeof(TValue)={numBytes}", nameof(bufferSize));
  944. // C# won't let us use a pointer to a generically defined type. Work
  945. // around this by using UnsafeUtility.
  946. var value = default(TValue);
  947. var valuePtr = UnsafeUtility.AddressOf(ref value);
  948. UnsafeUtility.MemCpy(valuePtr, bufferPtr, numBytes);
  949. WriteValueIntoState(value, statePtr);
  950. }
  951. /// <inheritdoc />
  952. public override unsafe void WriteValueFromObjectIntoState(object value, void* statePtr)
  953. {
  954. if (statePtr == null)
  955. throw new ArgumentNullException(nameof(statePtr));
  956. if (value == null)
  957. throw new ArgumentNullException(nameof(value));
  958. // If value is not of expected type, try to convert.
  959. if (!(value is TValue))
  960. value = Convert.ChangeType(value, typeof(TValue));
  961. var valueOfType = (TValue)value;
  962. WriteValueIntoState(valueOfType, statePtr);
  963. }
  964. public virtual unsafe void WriteValueIntoState(TValue value, void* statePtr)
  965. {
  966. ////REVIEW: should we be able to even tell from layouts which controls support writing and which don't?
  967. throw new NotSupportedException(
  968. $"Control '{this}' does not support writing");
  969. }
  970. /// <inheritdoc />
  971. public override unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize)
  972. {
  973. if (buffer == null)
  974. throw new ArgumentNullException(nameof(buffer));
  975. var valueSize = UnsafeUtility.SizeOf<TValue>();
  976. if (bufferSize < valueSize)
  977. throw new ArgumentException(
  978. $"Expecting buffer of at least {valueSize} bytes for value of type {typeof(TValue).Name} but got buffer of only {bufferSize} bytes instead",
  979. nameof(bufferSize));
  980. var value = default(TValue);
  981. var valuePtr = UnsafeUtility.AddressOf(ref value);
  982. UnsafeUtility.MemCpy(valuePtr, buffer, valueSize);
  983. return value;
  984. }
  985. public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
  986. {
  987. ////REVIEW: should we first compare state here? if there's no change in state, there can be no change in value and we can skip the rest
  988. var firstValue = ReadValueFromState(firstStatePtr);
  989. var secondValue = ReadValueFromState(secondStatePtr);
  990. var firstValuePtr = UnsafeUtility.AddressOf(ref firstValue);
  991. var secondValuePtr = UnsafeUtility.AddressOf(ref secondValue);
  992. // NOTE: We're comparing raw memory of processed values here (which are guaranteed to be structs or
  993. // primitives), not state. Means we don't have to take bits into account here.
  994. return UnsafeUtility.MemCmp(firstValuePtr, secondValuePtr, UnsafeUtility.SizeOf<TValue>()) != 0;
  995. }
  996. public TValue ProcessValue(TValue value)
  997. {
  998. if (m_ProcessorStack.length > 0)
  999. {
  1000. value = m_ProcessorStack.firstValue.Process(value, this);
  1001. if (m_ProcessorStack.additionalValues != null)
  1002. for (var i = 0; i < m_ProcessorStack.length - 1; ++i)
  1003. value = m_ProcessorStack.additionalValues[i].Process(value, this);
  1004. }
  1005. return value;
  1006. }
  1007. internal InlinedArray<InputProcessor<TValue>> m_ProcessorStack;
  1008. // Only layouts are allowed to modify the processor stack.
  1009. internal TProcessor TryGetProcessor<TProcessor>()
  1010. where TProcessor : InputProcessor<TValue>
  1011. {
  1012. if (m_ProcessorStack.length > 0)
  1013. {
  1014. if (m_ProcessorStack.firstValue is TProcessor processor)
  1015. return processor;
  1016. if (m_ProcessorStack.additionalValues != null)
  1017. for (var i = 0; i < m_ProcessorStack.length - 1; ++i)
  1018. if (m_ProcessorStack.additionalValues[i] is TProcessor result)
  1019. return result;
  1020. }
  1021. return default;
  1022. }
  1023. internal override void AddProcessor(object processor)
  1024. {
  1025. if (!(processor is InputProcessor<TValue> processorOfType))
  1026. throw new ArgumentException(
  1027. $"Cannot add processor of type '{processor.GetType().Name}' to control of type '{GetType().Name}'", nameof(processor));
  1028. m_ProcessorStack.Append(processorOfType);
  1029. }
  1030. internal InputProcessor<TValue>[] processors => m_ProcessorStack.ToArray();
  1031. }
  1032. }