No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InputControl.cs 73KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Runtime.CompilerServices;
  5. using UnityEngine.InputSystem.Controls;
  6. using UnityEngine.InputSystem.LowLevel;
  7. using UnityEngine.InputSystem.Utilities;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using UnityEngine.InputSystem.Layouts;
  10. ////REVIEW: should EvaluateMagnitude() be called EvaluateActuation() or something similar?
  11. ////REVIEW: as soon as we gain the ability to have blittable type constraints, InputControl<TValue> should be constrained such
  12. ////REVIEW: Reading and writing is asymmetric. Writing does not involve processors, reading does.
  13. ////REVIEW: While the arrays used by controls are already nicely centralized on InputDevice, InputControls still
  14. //// hold a bunch of reference data that requires separate scanning. Can we move *all* reference data to arrays
  15. //// on InputDevice and make InputControls reference-free? Most challenging thing probably is getting rid of
  16. //// the InputDevice reference itself.
  17. ////REVIEW: how do we do stuff like smoothing over time?
  18. ////TODO: allow easier access to the default state such that you can easily create a state event containing only default state
  19. ////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
  20. //// rather go with minimal overhead directly to reading out memory
  21. //// (this should at least cover FLT, single BIT, and INT controls; and should be able to apply the common transformations
  22. //// as per AxisControl)
  23. namespace UnityEngine.InputSystem
  24. {
  25. /// <summary>
  26. /// A typed and named source of input values in a hierarchy of controls.
  27. /// </summary>
  28. /// <remarks>
  29. /// Controls can have children which in turn may have children. At the root of the child
  30. /// hierarchy is always an <see cref="InputDevice"/> (which themselves are InputControls).
  31. ///
  32. /// Controls can be looked up by their <see cref="path"/> (see <see cref="InputControlPath.TryFindControl"/>).
  33. ///
  34. /// Each control must have a unique <see cref="name"/> within the <see cref="children"/> of
  35. /// its <see cref="parent"/>. Multiple names can be assigned to controls using aliases (see
  36. /// <see cref="aliases"/>). Name lookup is case-insensitive.
  37. ///
  38. /// For display purposes, a control may have a separate <see cref="displayName"/>. This name
  39. /// will usually correspond to what the control is caused on the actual underlying hardware.
  40. /// For example, on an Xbox gamepad, the control with the name "buttonSouth" will have a display
  41. /// name of "A". Controls that have very long display names may also have a <see cref="shortDisplayName"/>.
  42. /// This is the case for the "Left Button" on the <see cref="Mouse"/>, for example, which is
  43. /// commonly abbreviated "LMB".
  44. ///
  45. /// In addition to names, a control may have usages associated with it (see <see cref="usages"/>).
  46. /// A usage indicates how a control is meant to be used. For example, a button can be assigned
  47. /// the "PrimaryAction" usage to indicate it is the primary action button the device. Within a
  48. /// device, usages have to be unique. See <see cref="CommonUsages"/> for a list of standardized usages.
  49. ///
  50. /// Controls do not actually store values. Instead, every control receives an <see cref="InputStateBlock"/>
  51. /// which, after the control's device has been added to the system, is used to read out values
  52. /// from the device's backing store. This backing store is referred to as "state" in the API
  53. /// as opposed to "values" which represent the data resulting from reading state. The format that
  54. /// each control stores state in is specific to the control. It can vary not only between controls
  55. /// of different types but also between controls of the same type. An <see cref="AxisControl"/>,
  56. /// for example, can be stored as a float or as a byte or in a number of other formats. <see cref="stateBlock"/>
  57. /// identifies both where the control stores its state as well as the format it stores it in.
  58. ///
  59. /// Controls are generally not created directly but are created internally by the input system
  60. /// from data known as "layouts" (see <see cref="InputControlLayout"/>). Each such layout describes
  61. /// the setup of a specific hierarchy of controls. The system internally maintains a registry of
  62. /// layouts and produces devices and controls from them as needed. The layout that a control has
  63. /// been created from can be queried using <see cref="layout"/>. For most purposes, the intricacies
  64. /// of the control layout mechanisms can be ignored and it is sufficient to know the names of a
  65. /// small set of common device layouts such as "Keyboard", "Mouse", "Gamepad", and "Touchscreen".
  66. ///
  67. /// Each control has a single, fixed value type. The type can be queried at runtime using
  68. /// <see cref="valueType"/>. Most types of controls are derived from <see cref="InputControl{TValue}"/>
  69. /// which has APIs specific to the type of value of the control (e.g. <see cref="InputControl{TValue}.ReadValue()"/>.
  70. ///
  71. /// The following example demonstrates various common operations performed on input controls:
  72. ///
  73. /// <example>
  74. /// <code>
  75. /// // Look up dpad/up control on current gamepad.
  76. /// var dpadUpControl = Gamepad.current["dpad/up"];
  77. ///
  78. /// // Look up the back button on the current gamepad.
  79. /// var backButton = Gamepad.current["{Back}"];
  80. ///
  81. /// // Look up all dpad/up controls on all gamepads in the system.
  82. /// using (var controls = InputSystem.FindControls("&lt;Gamepad&gt;/dpad/up"))
  83. /// Debug.Log($"Found {controls.Count} controls");
  84. ///
  85. /// // Display the value of all controls on the current gamepad.
  86. /// foreach (var control in Gamepad.current.allControls)
  87. /// Debug.Log(controls.ReadValueAsObject());
  88. ///
  89. /// // Track the value of the left stick on the current gamepad over time.
  90. /// var leftStickHistory = new InputStateHistory(Gamepad.current.leftStick);
  91. /// leftStickHistory.Enable();
  92. /// </code>
  93. /// </example>
  94. /// <example>
  95. /// </example>
  96. /// </remarks>
  97. /// <see cref="InputControl{TValue}"/>
  98. /// <seealso cref="InputDevice"/>
  99. /// <seealso cref="InputControlPath"/>
  100. /// <seealso cref="InputStateBlock"/>
  101. [DebuggerDisplay("{DebuggerDisplay(),nq}")]
  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. /// that is, 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. {
  334. if (null != list[i])
  335. list[i].noisy = true;
  336. }
  337. }
  338. else
  339. m_ControlFlags &= ~ControlFlags.IsNoisy;
  340. }
  341. }
  342. /// <summary>
  343. /// Whether the control is considered synthetic.
  344. /// </summary>
  345. /// <value>True if the control does not represent an actual physical control on the device.</value>
  346. /// <remarks>
  347. /// A control is considered "synthetic" if it does not correspond to an actual, physical control on the
  348. /// device. An example for this is <see cref="Keyboard.anyKey"/> or the up/down/left/right buttons added
  349. /// by <see cref="StickControl"/>.
  350. ///
  351. /// The value of this property is determined by the layout (<see cref="InputControlLayout"/>) that the
  352. /// control has been built from.
  353. ///
  354. /// The primary effect of being synthetic is in interactive rebinding (see
  355. /// <see cref="InputActionRebindingExtensions.RebindingOperation"/>) where non-synthetic
  356. /// controls will be favored over synthetic ones. This means, for example, that if both
  357. /// <c>"&lt;Gamepad&gt;/leftStick/x"</c> and <c>"&lt;Gamepad&gt;/leftStick/left"</c> are
  358. /// suitable picks, <c>"&lt;Gamepad&gt;/leftStick/x"</c> will be favored as it represents
  359. /// input from an actual physical control whereas <c>"&lt;Gamepad&gt;/leftStick/left"</c>
  360. /// represents input from a made-up control. If, however, the "left" button is the only
  361. /// viable pick, it will be accepted.
  362. /// </remarks>
  363. /// <seealso cref="InputControlLayout.ControlItem.isSynthetic"/>
  364. /// <seealso cref="InputControlAttribute.synthetic"/>
  365. public bool synthetic
  366. {
  367. get => (m_ControlFlags & ControlFlags.IsSynthetic) != 0;
  368. internal set
  369. {
  370. if (value)
  371. m_ControlFlags |= ControlFlags.IsSynthetic;
  372. else
  373. m_ControlFlags &= ~ControlFlags.IsSynthetic;
  374. }
  375. }
  376. /// <summary>
  377. /// Fetch a control from the control's hierarchy by name.
  378. /// </summary>
  379. /// <remarks>
  380. /// Note that path matching is case-insensitive.
  381. /// </remarks>
  382. /// <example>
  383. /// <code>
  384. /// gamepad["leftStick"] // Returns Gamepad.leftStick
  385. /// gamepad["leftStick/x"] // Returns Gamepad.leftStick.x
  386. /// gamepad["{PrimaryAction}"] // Returns the control with PrimaryAction usage, that is, Gamepad.aButton
  387. /// </code>
  388. /// </example>
  389. /// <exception cref="KeyNotFoundException"><paramref name="path"/> cannot be found.</exception>
  390. /// <seealso cref="InputControlPath"/>
  391. /// <seealso cref="path"/>
  392. /// <seealso cref="TryGetChildControl"/>
  393. public InputControl this[string path]
  394. {
  395. get
  396. {
  397. var control = InputControlPath.TryFindChild(this, path);
  398. if (control == null)
  399. throw new KeyNotFoundException(
  400. $"Cannot find control '{path}' as child of '{this}'");
  401. return control;
  402. }
  403. }
  404. /// <summary>
  405. /// Returns the underlying value type of this control.
  406. /// </summary>
  407. /// <value>Type of values produced by the control.</value>
  408. /// <remarks>
  409. /// This is the type of values that are returned when reading the current value of a control
  410. /// or when reading a value of a control from an event.
  411. /// </remarks>
  412. /// <seealso cref="valueSizeInBytes"/>
  413. /// <seealso cref="ReadValueFromStateAsObject"/>
  414. public abstract Type valueType { get; }
  415. /// <summary>
  416. /// Size in bytes of values that the control returns.
  417. /// </summary>
  418. /// <seealso cref="valueType"/>
  419. public abstract int valueSizeInBytes { get; }
  420. /// <summary>
  421. /// Compute an absolute, normalized magnitude value that indicates the extent to which the control
  422. /// is actuated. Shortcut for <see cref="EvaluateMagnitude()"/>.
  423. /// </summary>
  424. /// <returns>Amount of actuation of the control or -1 if it cannot be determined.</returns>
  425. /// <seealso cref="EvaluateMagnitude(void*)"/>
  426. /// <seealso cref="EvaluateMagnitude()"/>
  427. public float magnitude => EvaluateMagnitude();
  428. /// <summary>
  429. /// Return a string representation of the control useful for debugging.
  430. /// </summary>
  431. /// <returns>A string representation of the control.</returns>
  432. public override string ToString()
  433. {
  434. return $"{layout}:{path}";
  435. }
  436. private string DebuggerDisplay()
  437. {
  438. // If the device hasn't been added, don't try to read the control's value.
  439. if (!device.added)
  440. return ToString();
  441. // ReadValueAsObject might throw. Revert to just ToString() in that case.
  442. try
  443. {
  444. return $"{layout}:{path}={this.ReadValueAsObject()}";
  445. }
  446. catch (Exception)
  447. {
  448. return ToString();
  449. }
  450. }
  451. ////REVIEW: The -1 behavior seems bad; probably better to just return 1 for controls that do not support finer levels of actuation
  452. /// <summary>
  453. /// Compute an absolute, normalized magnitude value that indicates the extent to which the control
  454. /// is actuated.
  455. /// </summary>
  456. /// <returns>Amount of actuation of the control or -1 if it cannot be determined.</returns>
  457. /// <remarks>
  458. /// Magnitudes do not make sense for all types of controls. For example, for a control that represents
  459. /// an enumeration of values (such as <see cref="TouchPhaseControl"/>), there is no meaningful
  460. /// linear ordering of values (one could derive a linear ordering through the actual enum values but
  461. /// their assignment may be entirely arbitrary; it is unclear whether a state of <see cref="TouchPhase.Canceled"/>
  462. /// has a higher or lower "magnitude" as a state of <see cref="TouchPhase.Began"/>).
  463. ///
  464. /// Controls that have no meaningful magnitude will return -1 when calling this method. Any negative
  465. /// return value should be considered an invalid value.
  466. /// </remarks>
  467. /// <seealso cref="EvaluateMagnitude(void*)"/>
  468. public unsafe float EvaluateMagnitude()
  469. {
  470. return EvaluateMagnitude(currentStatePtr);
  471. }
  472. /// <summary>
  473. /// Compute an absolute, normalized magnitude value that indicates the extent to which the control
  474. /// is actuated in the given state.
  475. /// </summary>
  476. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>.</param>
  477. /// <returns>Amount of actuation of the control or -1 if it cannot be determined.</returns>
  478. /// <seealso cref="EvaluateMagnitude()"/>
  479. /// <seealso cref="stateBlock"/>
  480. public virtual unsafe float EvaluateMagnitude(void* statePtr)
  481. {
  482. return -1;
  483. }
  484. public abstract unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize);
  485. /// <summary>
  486. /// Read the control's final, processed value from the given state and return the value as an object.
  487. /// </summary>
  488. /// <param name="statePtr"></param>
  489. /// <returns>The control's value as stored in <paramref name="statePtr"/>.</returns>
  490. /// <remarks>
  491. /// This method allocates GC memory and should not be used during normal gameplay operation.
  492. /// </remarks>
  493. /// <exception cref="ArgumentNullException"><paramref name="statePtr"/> is null.</exception>
  494. /// <seealso cref="ReadValueFromStateIntoBuffer"/>
  495. public abstract unsafe object ReadValueFromStateAsObject(void* statePtr);
  496. /// <summary>
  497. /// Read the control's final, processed value from the given state and store it in the given buffer.
  498. /// </summary>
  499. /// <param name="statePtr">State to read the value for the control from.</param>
  500. /// <param name="bufferPtr">Buffer to store the value in.</param>
  501. /// <param name="bufferSize">Size of <paramref name="bufferPtr"/> in bytes. Must be at least <see cref="valueSizeInBytes"/>.
  502. /// If it is smaller, <see cref="ArgumentException"/> will be thrown.</param>
  503. /// <exception cref="ArgumentNullException"><paramref name="statePtr"/> is null, or <paramref name="bufferPtr"/> is null.</exception>
  504. /// <exception cref="ArgumentException"><paramref name="bufferSize"/> is smaller than <see cref="valueSizeInBytes"/>.</exception>
  505. /// <seealso cref="ReadValueFromStateAsObject"/>
  506. /// <seealso cref="WriteValueFromBufferIntoState"/>
  507. public abstract unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize);
  508. /// <summary>
  509. /// Read a value from the given memory and store it as state.
  510. /// </summary>
  511. /// <param name="bufferPtr">Memory containing value.</param>
  512. /// <param name="bufferSize">Size of <paramref name="bufferPtr"/> in bytes. Must be at least <see cref="valueSizeInBytes"/>.</param>
  513. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>. Will receive the state
  514. /// as converted from the given value.</param>
  515. /// <remarks>
  516. /// Writing values will NOT apply processors to the given value. This can mean that when reading a value
  517. /// from a control after it has been written to its state, the resulting value differs from what was
  518. /// written.
  519. /// </remarks>
  520. /// <exception cref="NotSupportedException">The control does not support writing. This is the case, for
  521. /// example, that compute values (such as the magnitude of a vector).</exception>
  522. /// <seealso cref="ReadValueFromStateIntoBuffer"/>
  523. /// <seealso cref="WriteValueFromObjectIntoState"/>
  524. public virtual unsafe void WriteValueFromBufferIntoState(void* bufferPtr, int bufferSize, void* statePtr)
  525. {
  526. throw new NotSupportedException(
  527. $"Control '{this}' does not support writing");
  528. }
  529. /// <summary>
  530. /// Read a value object and store it as state in the given memory.
  531. /// </summary>
  532. /// <param name="value">Value for the control.</param>
  533. /// <param name="statePtr">State containing the control's <see cref="stateBlock"/>. Will receive
  534. /// the state state as converted from the given value.</param>
  535. /// <remarks>
  536. /// Writing values will NOT apply processors to the given value. This can mean that when reading a value
  537. /// from a control after it has been written to its state, the resulting value differs from what was
  538. /// written.
  539. /// </remarks>
  540. /// <exception cref="NotSupportedException">The control does not support writing. This is the case, for
  541. /// example, that compute values (such as the magnitude of a vector).</exception>
  542. /// <seealso cref="WriteValueFromBufferIntoState"/>
  543. public virtual unsafe void WriteValueFromObjectIntoState(object value, void* statePtr)
  544. {
  545. throw new NotSupportedException(
  546. $"Control '{this}' does not support writing");
  547. }
  548. /// <summary>
  549. /// Compare the value of the control as read from <paramref name="firstStatePtr"/> to that read from
  550. /// <paramref name="secondStatePtr"/> and return true if they are equal.
  551. /// </summary>
  552. /// <param name="firstStatePtr">Memory containing the control's <see cref="stateBlock"/>.</param>
  553. /// <param name="secondStatePtr">Memory containing the control's <see cref="stateBlock"/></param>
  554. /// <returns>True if the value of the control is equal in both <paramref name="firstStatePtr"/> and
  555. /// <paramref name="secondStatePtr"/>.</returns>
  556. /// <remarks>
  557. /// Unlike <see cref="CompareValue"/>, this method will have to do more than just compare the memory
  558. /// for the control in the two state buffers. It will have to read out state for the control and run
  559. /// the full processing machinery for the control to turn the state into a final, processed value.
  560. /// CompareValue is thus more costly than <see cref="CompareValue"/>.
  561. ///
  562. /// This method will apply epsilons (<see cref="Mathf.Epsilon"/>) when comparing floats.
  563. /// </remarks>
  564. /// <seealso cref="CompareValue"/>
  565. public abstract unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr);
  566. /// <summary>
  567. /// Try to find a child control matching the given path.
  568. /// </summary>
  569. /// <param name="path">A control path. See <see cref="InputControlPath"/>.</param>
  570. /// <returns>The first direct or indirect child control that matches the given <paramref name="path"/>
  571. /// or null if no control was found to match.</returns>
  572. /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c> or empty.</exception>
  573. /// <remarks>
  574. /// Note that if the given path matches multiple child controls, only the first control
  575. /// encountered in the search will be returned.
  576. ///
  577. /// <example>
  578. /// <code>
  579. /// // Returns the leftStick control of the current gamepad.
  580. /// Gamepad.current.TryGetChildControl("leftStick");
  581. ///
  582. /// // Returns the X axis control of the leftStick on the current gamepad.
  583. /// Gamepad.current.TryGetChildControl("leftStick/x");
  584. ///
  585. /// // Returns the first control ending with "stick" in its name. Note that it
  586. /// // undetermined whether this is leftStick or rightStick (or even another stick
  587. /// // added by the given gamepad).
  588. /// Gamepad.current.TryGetChildControl("*stick");
  589. /// </code>
  590. /// </example>
  591. ///
  592. /// This method is equivalent to calling <see cref="InputControlPath.TryFindChild"/>.
  593. /// </remarks>
  594. public InputControl TryGetChildControl(string path)
  595. {
  596. if (string.IsNullOrEmpty(path))
  597. throw new ArgumentNullException(nameof(path));
  598. return InputControlPath.TryFindChild(this, path);
  599. }
  600. public TControl TryGetChildControl<TControl>(string path)
  601. where TControl : InputControl
  602. {
  603. if (string.IsNullOrEmpty(path))
  604. throw new ArgumentNullException(nameof(path));
  605. var control = TryGetChildControl(path);
  606. if (control == null)
  607. return null;
  608. var controlOfType = control as TControl;
  609. if (controlOfType == null)
  610. throw new InvalidOperationException(
  611. $"Expected control '{path}' to be of type '{typeof(TControl).Name}' but is of type '{control.GetType().Name}' instead!");
  612. return controlOfType;
  613. }
  614. public InputControl GetChildControl(string path)
  615. {
  616. if (string.IsNullOrEmpty(path))
  617. throw new ArgumentNullException(nameof(path));
  618. var control = TryGetChildControl(path);
  619. if (control == null)
  620. throw new ArgumentException($"Cannot find input control '{MakeChildPath(path)}'", nameof(path));
  621. return control;
  622. }
  623. public TControl GetChildControl<TControl>(string path)
  624. where TControl : InputControl
  625. {
  626. var control = GetChildControl(path);
  627. if (!(control is TControl controlOfType))
  628. throw new ArgumentException(
  629. $"Expected control '{path}' to be of type '{typeof(TControl).Name}' but is of type '{control.GetType().Name}' instead!", nameof(path));
  630. return controlOfType;
  631. }
  632. protected InputControl()
  633. {
  634. // Set defaults for state block setup. Subclasses may override.
  635. m_StateBlock.byteOffset = InputStateBlock.AutomaticOffset; // Request automatic layout by default.
  636. }
  637. /// <summary>
  638. /// Perform final initialization tasks after the control hierarchy has been put into place.
  639. /// </summary>
  640. /// <remarks>
  641. /// This method can be overridden to perform control- or device-specific setup work. The most
  642. /// common use case is for looking up child controls and storing them in local getters.
  643. ///
  644. /// <example>
  645. /// <code>
  646. /// public class MyDevice : InputDevice
  647. /// {
  648. /// public ButtonControl button { get; private set; }
  649. /// public AxisControl axis { get; private set; }
  650. ///
  651. /// protected override void OnFinishSetup()
  652. /// {
  653. /// // Cache controls in getters.
  654. /// button = GetChildControl("button");
  655. /// axis = GetChildControl("axis");
  656. /// }
  657. /// }
  658. /// </code>
  659. /// </example>
  660. /// </remarks>
  661. protected virtual void FinishSetup()
  662. {
  663. }
  664. /// <summary>
  665. /// Call <see cref="RefreshConfiguration"/> if the configuration has in the interim been invalidated
  666. /// by a <see cref="DeviceConfigurationEvent"/>.
  667. /// </summary>
  668. /// <remarks>
  669. /// This method is only relevant if you are implementing your own devices or new
  670. /// types of controls which are fetching configuration data from the devices (such
  671. /// as <see cref="KeyControl"/> which is fetching display names for individual keys
  672. /// from the underlying platform).
  673. ///
  674. /// This method should be called if you are accessing cached data set up by
  675. /// <see cref="RefreshConfiguration"/>.
  676. ///
  677. /// <example>
  678. /// <code>
  679. /// // Let's say your device has an associated orientation which it can be held with
  680. /// // and you want to surface both as a property and as a usage on the device.
  681. /// // Whenever your backend code detects a change in orientation, it should send
  682. /// // a DeviceConfigurationEvent to your device to signal that the configuration
  683. /// // of the device has changed. You can then implement RefreshConfiguration() to
  684. /// // read out and update the device orientation on the managed InputDevice instance.
  685. /// public class MyDevice : InputDevice
  686. /// {
  687. /// public enum Orientation
  688. /// {
  689. /// Horizontal,
  690. /// Vertical,
  691. /// }
  692. ///
  693. /// private Orientation m_Orientation;
  694. /// public Orientation orientation
  695. /// {
  696. /// get
  697. /// {
  698. /// // Call RefreshOrientation if the configuration of the device has been
  699. /// // invalidated since last time we initialized m_Orientation.
  700. /// RefreshConfigurationIfNeeded();
  701. /// return m_Orientation;
  702. /// }
  703. /// }
  704. /// protected override void RefreshConfiguration()
  705. /// {
  706. /// // Fetch the current orientation from the backend. How you do this
  707. /// // depends on your device. Using DeviceCommands is one way.
  708. /// var fetchOrientationCommand = new FetchOrientationCommand();
  709. /// ExecuteCommand(ref fetchOrientationCommand);
  710. /// m_Orientation = fetchOrientation;
  711. ///
  712. /// // Reflect the orientation on the device.
  713. /// switch (m_Orientation)
  714. /// {
  715. /// case Orientation.Vertical:
  716. /// InputSystem.RemoveDeviceUsage(this, s_Horizontal);
  717. /// InputSystem.AddDeviceUsage(this, s_Vertical);
  718. /// break;
  719. ///
  720. /// case Orientation.Horizontal:
  721. /// InputSystem.RemoveDeviceUsage(this, s_Vertical);
  722. /// InputSystem.AddDeviceUsage(this, s_Horizontal);
  723. /// break;
  724. /// }
  725. /// }
  726. ///
  727. /// private static InternedString s_Vertical = new InternedString("Vertical");
  728. /// private static InternedString s_Horizontal = new InternedString("Horizontal");
  729. /// }
  730. /// </code>
  731. /// </example>
  732. /// </remarks>
  733. /// <seealso cref="RefreshConfiguration"/>
  734. protected void RefreshConfigurationIfNeeded()
  735. {
  736. if (!isConfigUpToDate)
  737. {
  738. RefreshConfiguration();
  739. isConfigUpToDate = true;
  740. }
  741. }
  742. protected virtual void RefreshConfiguration()
  743. {
  744. }
  745. ////TODO: drop protected access
  746. protected internal InputStateBlock m_StateBlock;
  747. ////REVIEW: shouldn't these sit on the device?
  748. protected internal unsafe void* currentStatePtr => InputStateBuffers.GetFrontBufferForDevice(GetDeviceIndex());
  749. protected internal unsafe void* previousFrameStatePtr => InputStateBuffers.GetBackBufferForDevice(GetDeviceIndex());
  750. protected internal unsafe void* defaultStatePtr => InputStateBuffers.s_DefaultStateBuffer;
  751. /// <summary>
  752. /// Return the memory that holds the noise mask for the control.
  753. /// </summary>
  754. /// <value>Noise bit mask for the control.</value>
  755. /// <remarks>
  756. /// Like with all state blocks, the specific memory block for the control is found at the memory
  757. /// region specified by <see cref="stateBlock"/>.
  758. ///
  759. /// The noise mask can be overlaid as a bit mask over the state for the control. When doing so, all state
  760. /// that is noise will be masked out whereas all state that isn't will come through unmodified. In other words,
  761. /// any bit that is set in the noise mask indicates that the corresponding bit in the control's state memory
  762. /// is noise.
  763. /// </remarks>
  764. /// <seealso cref="noisy"/>
  765. protected internal unsafe void* noiseMaskPtr => InputStateBuffers.s_NoiseMaskBuffer;
  766. /// <summary>
  767. /// The offset of this control's state relative to its device root.
  768. /// </summary>
  769. /// <remarks>
  770. /// Once a device has been added to the system, its state block will get allocated
  771. /// in the global state buffers and the offset of the device's state block will
  772. /// get baked into all of the controls on the device. This property always returns
  773. /// the "unbaked" offset.
  774. /// </remarks>
  775. protected internal uint stateOffsetRelativeToDeviceRoot
  776. {
  777. get
  778. {
  779. var deviceStateOffset = device.m_StateBlock.byteOffset;
  780. Debug.Assert(deviceStateOffset <= m_StateBlock.byteOffset);
  781. return m_StateBlock.byteOffset - deviceStateOffset;
  782. }
  783. }
  784. // This data is initialized by InputDeviceBuilder.
  785. internal InternedString m_Name;
  786. internal string m_Path;
  787. internal string m_DisplayName; // Display name set by the control itself (may be null).
  788. internal string m_DisplayNameFromLayout; // Display name coming from layout (may be null).
  789. internal string m_ShortDisplayName; // Short display name set by the control itself (may be null).
  790. internal string m_ShortDisplayNameFromLayout; // Short display name coming from layout (may be null).
  791. internal InternedString m_Layout;
  792. internal InternedString m_Variants;
  793. internal InputDevice m_Device;
  794. internal InputControl m_Parent;
  795. internal int m_UsageCount;
  796. internal int m_UsageStartIndex;
  797. internal int m_AliasCount;
  798. internal int m_AliasStartIndex;
  799. internal int m_ChildCount;
  800. internal int m_ChildStartIndex;
  801. internal ControlFlags m_ControlFlags;
  802. // Value caching
  803. // These values will be set to true during state updates if the control has actually changed value.
  804. // Set to true initially so default state will be returned on the first call
  805. internal bool m_CachedValueIsStale = true;
  806. internal bool m_UnprocessedCachedValueIsStale = true;
  807. ////REVIEW: store these in arrays in InputDevice instead?
  808. internal PrimitiveValue m_DefaultState;
  809. internal PrimitiveValue m_MinValue;
  810. internal PrimitiveValue m_MaxValue;
  811. internal FourCC m_OptimizedControlDataType;
  812. /// <summary>
  813. /// For some types of control you can safely read/write state memory directly
  814. /// which is much faster than calling ReadUnprocessedValueFromState/WriteValueIntoState.
  815. /// This method returns a type that you can use for reading/writing the control directly,
  816. /// or it returns InputStateBlock.kFormatInvalid if it's not possible for this type of control.
  817. /// </summary>
  818. /// <remarks>
  819. /// For example, AxisControl might be a "float" in state memory, and if no processing is applied during reading (e.g. no invert/scale/etc),
  820. /// then you could read it as float in memory directly without calling ReadUnprocessedValueFromState, which is faster.
  821. /// Additionally, if you have a Vector3Control which uses 3 AxisControls as consecutive floats in memory,
  822. /// you can cast the Vector3Control state memory directly to Vector3 without calling ReadUnprocessedValueFromState on x/y/z axes.
  823. ///
  824. /// The value returned for any given control is computed automatically by the Input System, when the control's setup configuration changes. <see cref="InputControl.CalculateOptimizedControlDataType"/>
  825. /// There are some parameter changes which don't trigger a configuration change (such as the clamp, invert, normalize, and scale parameters on AxisControl),
  826. /// so if you modify these, the optimized data type is not automatically updated. In this situation, you should manually update it by calling <see cref="InputControl.ApplyParameterChanges"/>.
  827. /// </remarks>
  828. public FourCC optimizedControlDataType => m_OptimizedControlDataType;
  829. /// <summary>
  830. /// Calculates and returns a optimized data type that can represent a control's value in memory directly.
  831. /// The value then is cached in <see cref="InputControl.optimizedControlDataType"/>.
  832. /// This method is for internal use only, you should not call this from your own code.
  833. /// </summary>
  834. protected virtual FourCC CalculateOptimizedControlDataType()
  835. {
  836. return InputStateBlock.kFormatInvalid;
  837. }
  838. /// <summary>
  839. /// Apply built-in parameters changes (e.g. <see cref="AxisControl.invert"/>, others), recompute <see cref="InputControl.optimizedControlDataType"/> for impacted controls and clear cached value.
  840. /// </summary>
  841. /// <remarks>
  842. /// </remarks>
  843. public void ApplyParameterChanges()
  844. {
  845. // First we go through all children of our own hierarchy
  846. SetOptimizedControlDataTypeRecursively();
  847. // Then we go through all parents up to the root, because our own change might influence their optimization status
  848. // e.g. let's say we have a tree where root is Vector3 and children are three AxisControl
  849. // And user is calling this method on AxisControl which goes from Float to NotOptimized.
  850. // Then we need to also transition Vector3 to NotOptimized as well.
  851. var currentParent = parent;
  852. while (currentParent != null)
  853. {
  854. currentParent.SetOptimizedControlDataType();
  855. currentParent = currentParent.parent;
  856. }
  857. // Also use this method to mark cached values as stale
  858. MarkAsStaleRecursively();
  859. }
  860. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  861. private void SetOptimizedControlDataType()
  862. {
  863. // setting check need to be inline so we clear optimizations if setting is disabled after the fact
  864. m_OptimizedControlDataType = InputSettings.optimizedControlsFeatureEnabled
  865. ? CalculateOptimizedControlDataType()
  866. : (FourCC)InputStateBlock.kFormatInvalid;
  867. }
  868. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  869. internal void SetOptimizedControlDataTypeRecursively()
  870. {
  871. // Need to go depth-first because CalculateOptimizedControlDataType might depend on computed values of children
  872. if (m_ChildCount > 0)
  873. {
  874. foreach (var inputControl in children)
  875. inputControl.SetOptimizedControlDataTypeRecursively();
  876. }
  877. SetOptimizedControlDataType();
  878. }
  879. // This function exists to warn users to start using ApplyParameterChanges for edge cases that were previously not intentionally supported,
  880. // where control properties suddenly change underneath us without us anticipating that.
  881. // This is mainly to AxisControl fields being public and capable of changing at any time even if we were not anticipated such a usage pattern.
  882. // Also it's not clear if InputControl.stateBlock.format can potentially change at any time, likely not.
  883. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  884. // Only do this check in development builds and editor in hope that it will be sufficient to catch any misuse during development.
  885. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
  886. internal void EnsureOptimizationTypeHasNotChanged()
  887. {
  888. if (!InputSettings.optimizedControlsFeatureEnabled)
  889. return;
  890. var currentOptimizedControlDataType = CalculateOptimizedControlDataType();
  891. if (currentOptimizedControlDataType != optimizedControlDataType)
  892. {
  893. Debug.LogError(
  894. $"Control '{name}' / '{path}' suddenly changed optimization state due to either format " +
  895. $"change or control parameters change (was '{optimizedControlDataType}' but became '{currentOptimizedControlDataType}'), " +
  896. "this hinders control hot path optimization, please call control.ApplyParameterChanges() " +
  897. "after the changes to the control to fix this error.");
  898. // Automatically fix the issue
  899. // Note this function is only executed in editor and development builds
  900. m_OptimizedControlDataType = currentOptimizedControlDataType;
  901. }
  902. if (m_ChildCount > 0)
  903. {
  904. foreach (var inputControl in children)
  905. inputControl.EnsureOptimizationTypeHasNotChanged();
  906. }
  907. }
  908. [Flags]
  909. internal enum ControlFlags
  910. {
  911. ConfigUpToDate = 1 << 0,
  912. IsNoisy = 1 << 1,
  913. IsSynthetic = 1 << 2,
  914. IsButton = 1 << 3,
  915. DontReset = 1 << 4,
  916. SetupFinished = 1 << 5, // Can't be modified once this is set.
  917. UsesStateFromOtherControl = 1 << 6,
  918. }
  919. internal bool isSetupFinished
  920. {
  921. get => (m_ControlFlags & ControlFlags.SetupFinished) == ControlFlags.SetupFinished;
  922. set
  923. {
  924. if (value)
  925. m_ControlFlags |= ControlFlags.SetupFinished;
  926. else
  927. m_ControlFlags &= ~ControlFlags.SetupFinished;
  928. }
  929. }
  930. internal bool isButton
  931. {
  932. get => (m_ControlFlags & ControlFlags.IsButton) == ControlFlags.IsButton;
  933. set
  934. {
  935. if (value)
  936. m_ControlFlags |= ControlFlags.IsButton;
  937. else
  938. m_ControlFlags &= ~ControlFlags.IsButton;
  939. }
  940. }
  941. internal bool isConfigUpToDate
  942. {
  943. get => (m_ControlFlags & ControlFlags.ConfigUpToDate) == ControlFlags.ConfigUpToDate;
  944. set
  945. {
  946. if (value)
  947. m_ControlFlags |= ControlFlags.ConfigUpToDate;
  948. else
  949. m_ControlFlags &= ~ControlFlags.ConfigUpToDate;
  950. }
  951. }
  952. internal bool dontReset
  953. {
  954. get => (m_ControlFlags & ControlFlags.DontReset) == ControlFlags.DontReset;
  955. set
  956. {
  957. if (value)
  958. m_ControlFlags |= ControlFlags.DontReset;
  959. else
  960. m_ControlFlags &= ~ControlFlags.DontReset;
  961. }
  962. }
  963. internal bool usesStateFromOtherControl
  964. {
  965. get => (m_ControlFlags & ControlFlags.UsesStateFromOtherControl) == ControlFlags.UsesStateFromOtherControl;
  966. set
  967. {
  968. if (value)
  969. m_ControlFlags |= ControlFlags.UsesStateFromOtherControl;
  970. else
  971. m_ControlFlags &= ~ControlFlags.UsesStateFromOtherControl;
  972. }
  973. }
  974. internal bool hasDefaultState => !m_DefaultState.isEmpty;
  975. // This method exists only to not slap the internal interaction on all overrides of
  976. // FinishSetup().
  977. internal void CallFinishSetupRecursive()
  978. {
  979. var list = children;
  980. for (var i = 0; i < list.Count; ++i)
  981. list[i].CallFinishSetupRecursive();
  982. FinishSetup();
  983. SetOptimizedControlDataTypeRecursively();
  984. }
  985. internal string MakeChildPath(string path)
  986. {
  987. if (this is InputDevice)
  988. return path;
  989. return $"{this.path}/{path}";
  990. }
  991. internal void BakeOffsetIntoStateBlockRecursive(uint offset)
  992. {
  993. m_StateBlock.byteOffset += offset;
  994. var list = children;
  995. for (var i = 0; i < list.Count; ++i)
  996. list[i].BakeOffsetIntoStateBlockRecursive(offset);
  997. }
  998. internal int GetDeviceIndex()
  999. {
  1000. var deviceIndex = m_Device.m_DeviceIndex;
  1001. if (deviceIndex == InputDevice.kInvalidDeviceIndex)
  1002. throw new InvalidOperationException(
  1003. $"Cannot query value of control '{path}' before '{device.name}' has been added to system!");
  1004. return deviceIndex;
  1005. }
  1006. internal bool IsValueConsideredPressed(float value)
  1007. {
  1008. if (isButton)
  1009. return ((ButtonControl)this).IsValueConsideredPressed(value);
  1010. return value >= ButtonControl.s_GlobalDefaultButtonPressPoint;
  1011. }
  1012. internal virtual void AddProcessor(object first)
  1013. {
  1014. }
  1015. internal void MarkAsStale()
  1016. {
  1017. m_CachedValueIsStale = true;
  1018. m_UnprocessedCachedValueIsStale = true;
  1019. }
  1020. internal void MarkAsStaleRecursively()
  1021. {
  1022. MarkAsStale();
  1023. foreach (var inputControl in children)
  1024. inputControl.MarkAsStale();
  1025. }
  1026. #if UNITY_EDITOR
  1027. internal virtual IEnumerable<object> GetProcessors()
  1028. {
  1029. yield return null;
  1030. }
  1031. #endif
  1032. }
  1033. /// <summary>
  1034. /// Base class for input controls with a specific value type.
  1035. /// </summary>
  1036. /// <typeparam name="TValue">Type of value captured by the control. Note that this does not mean
  1037. /// that the control has to store data in the given value format. A control that captures float
  1038. /// values, for example, may be stored in state as byte values instead.</typeparam>
  1039. public abstract class InputControl<TValue> : InputControl
  1040. where TValue : struct
  1041. {
  1042. public override Type valueType => typeof(TValue);
  1043. public override int valueSizeInBytes => UnsafeUtility.SizeOf<TValue>();
  1044. /// <summary>
  1045. /// Returns the current value of the control after processors have been applied.
  1046. /// </summary>
  1047. /// <returns>The controls current value.</returns>
  1048. /// <remarks>
  1049. /// This can only be called on devices that have been added to the system (<see cref="InputDevice.added"/>).
  1050. ///
  1051. /// If internal feature "USE_READ_VALUE_CACHING" is enabled, then this property implements caching
  1052. /// to avoid applying processors when the underlying control has not changed.
  1053. /// With this in mind, be aware of processors that use global state, such as the <see cref="Processors.AxisDeadzoneProcessor"/>.
  1054. /// Unless the control unprocessed value has been changed, input system settings changed or <see cref="InputControl.ApplyParameterChanges()"/> invoked,
  1055. /// the processors will not run and calls to <see cref="value"/> will return the same result as previous calls.
  1056. ///
  1057. /// If a processor requires to be run on every read, override <see cref="InputProcessor.cachingPolicy"/> property
  1058. /// in the processor and set it to <see cref="InputProcessor.CachingPolicy.EvaluateOnEveryRead"/>.
  1059. ///
  1060. /// To improve debugging try setting "PARANOID_READ_VALUE_CACHING_CHECKS" internal feature flag to check if cache value is still consistent.
  1061. ///
  1062. /// Also note that this property returns the result as ref readonly. If custom control states are in use, i.e.
  1063. /// any controls not shipped with the Input System package, be careful of accidental defensive copies
  1064. /// <see href="https://docs.microsoft.com/en-us/dotnet/csharp/write-safe-efficient-code#avoid-defensive-copies"/>.
  1065. /// </remarks>
  1066. /// <seealso cref="ReadValue"/>
  1067. public ref readonly TValue value
  1068. {
  1069. get
  1070. {
  1071. #if UNITY_EDITOR
  1072. if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
  1073. return ref ReadStateInEditor();
  1074. #endif
  1075. if (
  1076. // if feature is disabled we re-evaluate every call
  1077. !InputSettings.readValueCachingFeatureEnabled
  1078. // if cached value is stale we re-evaluate and clear the flag
  1079. || m_CachedValueIsStale
  1080. // if a processor in stack needs to be re-evaluated, but unprocessedValue is still can be cached
  1081. || evaluateProcessorsEveryRead
  1082. )
  1083. {
  1084. m_CachedValue = ProcessValue(unprocessedValue);
  1085. m_CachedValueIsStale = false;
  1086. }
  1087. #if DEBUG
  1088. else if (InputSettings.paranoidReadValueCachingChecksEnabled)
  1089. {
  1090. var oldUnprocessedValue = m_UnprocessedCachedValue;
  1091. var newUnprocessedValue = unprocessedValue;
  1092. var currentProcessedValue = ProcessValue(newUnprocessedValue);
  1093. if (CompareValue(ref newUnprocessedValue, ref oldUnprocessedValue))
  1094. {
  1095. // don't warn if unprocessedValue caching failed
  1096. m_CachedValue = currentProcessedValue;
  1097. }
  1098. else if (CompareValue(ref currentProcessedValue, ref m_CachedValue))
  1099. {
  1100. // processors are not behaving as expected if unprocessedValue stays the same but processedValue changed
  1101. var namesList = new List<string>();
  1102. foreach (var inputProcessor in m_ProcessorStack)
  1103. namesList.Add(inputProcessor.ToString());
  1104. var names = string.Join(", ", namesList);
  1105. Debug.LogError(
  1106. "Cached processed value unexpectedly became outdated due to InputProcessor's returning a different value, " +
  1107. $"new value '{currentProcessedValue}' old value '{m_CachedValue}', current processors are: {names}. " +
  1108. "If your processor need to be recomputed on every read please add \"public override CachingPolicy cachingPolicy => CachingPolicy.EvaluateOnEveryRead;\" to the processor.");
  1109. m_CachedValue = currentProcessedValue;
  1110. }
  1111. }
  1112. #endif
  1113. return ref m_CachedValue;
  1114. }
  1115. }
  1116. internal unsafe ref readonly TValue unprocessedValue
  1117. {
  1118. get
  1119. {
  1120. #if UNITY_EDITOR
  1121. if (InputUpdate.s_LatestUpdateType.IsEditorUpdate())
  1122. return ref ReadUnprocessedStateInEditor();
  1123. #endif
  1124. if (
  1125. // if feature is disabled we re-evaluate every call
  1126. !InputSettings.readValueCachingFeatureEnabled
  1127. // if cached value is stale we re-evaluate and clear the flag
  1128. || m_UnprocessedCachedValueIsStale
  1129. )
  1130. {
  1131. m_UnprocessedCachedValue = ReadUnprocessedValueFromState(currentStatePtr);
  1132. m_UnprocessedCachedValueIsStale = false;
  1133. }
  1134. #if DEBUG
  1135. else if (InputSettings.paranoidReadValueCachingChecksEnabled)
  1136. {
  1137. var currentUnprocessedValue = ReadUnprocessedValueFromState(currentStatePtr);
  1138. if (CompareValue(ref currentUnprocessedValue, ref m_UnprocessedCachedValue))
  1139. {
  1140. Debug.LogError($"Cached unprocessed value unexpectedly became outdated for unknown reason, new value '{currentUnprocessedValue}' old value '{m_UnprocessedCachedValue}'.");
  1141. m_UnprocessedCachedValue = currentUnprocessedValue;
  1142. }
  1143. }
  1144. #endif
  1145. return ref m_UnprocessedCachedValue;
  1146. }
  1147. }
  1148. /// <summary>
  1149. /// Returns the current value of the control after processors have been applied.
  1150. /// </summary>
  1151. /// <returns>The controls current value.</returns>
  1152. /// <remarks>
  1153. /// This can only be called on devices that have been added to the system (<see cref="InputDevice.added"/>).
  1154. ///
  1155. /// If internal feature "USE_READ_VALUE_CACHING" is enabled, then this property implements caching
  1156. /// to avoid applying processors when the underlying control has not changed.
  1157. /// With this in mind, be aware of processors that use global state, such as the <see cref="Processors.AxisDeadzoneProcessor"/>.
  1158. /// Unless the control unprocessed value has been changed, input system settings changed or <see cref="InputControl.ApplyParameterChanges()"/> invoked,
  1159. /// the processors will not run and calls to <see cref="value"/> will return the same result as previous calls.
  1160. ///
  1161. /// If a processor requires to be run on every read, override <see cref="InputProcessor.cachingPolicy"/> property
  1162. /// in the processor and set it to <see cref="InputProcessor.CachingPolicy.EvaluateOnEveryRead"/>.
  1163. ///
  1164. /// To improve debugging try setting "PARANOID_READ_VALUE_CACHING_CHECKS" internal feature flag to check if cache value is still consistent.
  1165. /// <see href="https://docs.microsoft.com/en-us/dotnet/csharp/write-safe-efficient-code#avoid-defensive-copies"/>.
  1166. /// </remarks>
  1167. /// <seealso cref="value"/>
  1168. public TValue ReadValue()
  1169. {
  1170. return value;
  1171. }
  1172. ////REVIEW: is 'frame' really the best wording here?
  1173. /// <summary>
  1174. /// Get the control's value from the previous frame (<see cref="InputControl.previousFrameStatePtr"/>).
  1175. /// </summary>
  1176. /// <returns>The control's value in the previous frame.</returns>
  1177. public TValue ReadValueFromPreviousFrame()
  1178. {
  1179. unsafe
  1180. {
  1181. return ReadValueFromState(previousFrameStatePtr);
  1182. }
  1183. }
  1184. /// <summary>
  1185. /// Get the control's default value.
  1186. /// </summary>
  1187. /// <returns>The control's default value.</returns>
  1188. /// <remarks>
  1189. /// This is not necessarily equivalent to <c>default(TValue)</c>. A control's default value is determined
  1190. /// by reading its value from the default state (<see cref="InputControl.defaultStatePtr"/>) which in turn
  1191. /// is determined from settings in the control's registered layout (<see cref="InputControlLayout.ControlItem.defaultState"/>).
  1192. /// </remarks>
  1193. public TValue ReadDefaultValue()
  1194. {
  1195. unsafe
  1196. {
  1197. return ReadValueFromState(defaultStatePtr);
  1198. }
  1199. }
  1200. public unsafe TValue ReadValueFromState(void* statePtr)
  1201. {
  1202. if (statePtr == null)
  1203. throw new ArgumentNullException(nameof(statePtr));
  1204. return ProcessValue(ReadUnprocessedValueFromState(statePtr));
  1205. }
  1206. /// <summary>
  1207. /// Read value from provided <paramref name="statePtr"/> and apply processors. Try cache result if possible.
  1208. /// </summary>
  1209. /// <param name="statePtr">State pointer to read from.</param>
  1210. /// <returns>The controls current value.</returns>
  1211. /// <remarks>
  1212. /// If <paramref name="statePtr"/> is "currentStatePtr", then read will be done via <see cref="value"/> property to improve performance.
  1213. /// </remarks>
  1214. /// <seealso cref="value"/>
  1215. public unsafe TValue ReadValueFromStateWithCaching(void* statePtr)
  1216. {
  1217. return statePtr == currentStatePtr ? value : ReadValueFromState(statePtr);
  1218. }
  1219. /// <summary>
  1220. /// Read value from provided <paramref name="statePtr"/>. Try cache result if possible.
  1221. /// </summary>
  1222. /// <param name="statePtr">State pointer to read from.</param>
  1223. /// <returns>The controls current value.</returns>
  1224. /// <remarks>
  1225. /// If <paramref name="statePtr"/> is "currentStatePtr", then read will be done via <see cref="unprocessedValue"/> property to improve performance.
  1226. /// </remarks>
  1227. /// <seealso cref="value"/>
  1228. public unsafe TValue ReadUnprocessedValueFromStateWithCaching(void* statePtr)
  1229. {
  1230. return statePtr == currentStatePtr ? unprocessedValue : ReadUnprocessedValueFromState(statePtr);
  1231. }
  1232. public TValue ReadUnprocessedValue()
  1233. {
  1234. return unprocessedValue;
  1235. }
  1236. public abstract unsafe TValue ReadUnprocessedValueFromState(void* statePtr);
  1237. /// <inheritdoc />
  1238. public override unsafe object ReadValueFromStateAsObject(void* statePtr)
  1239. {
  1240. return ReadValueFromState(statePtr);
  1241. }
  1242. /// <inheritdoc />
  1243. public override unsafe void ReadValueFromStateIntoBuffer(void* statePtr, void* bufferPtr, int bufferSize)
  1244. {
  1245. if (statePtr == null)
  1246. throw new ArgumentNullException(nameof(statePtr));
  1247. if (bufferPtr == null)
  1248. throw new ArgumentNullException(nameof(bufferPtr));
  1249. var numBytes = UnsafeUtility.SizeOf<TValue>();
  1250. if (bufferSize < numBytes)
  1251. throw new ArgumentException(
  1252. $"bufferSize={bufferSize} < sizeof(TValue)={numBytes}", nameof(bufferSize));
  1253. var value = ReadValueFromState(statePtr);
  1254. var valuePtr = UnsafeUtility.AddressOf(ref value);
  1255. UnsafeUtility.MemCpy(bufferPtr, valuePtr, numBytes);
  1256. }
  1257. public override unsafe void WriteValueFromBufferIntoState(void* bufferPtr, int bufferSize, void* statePtr)
  1258. {
  1259. if (bufferPtr == null)
  1260. throw new ArgumentNullException(nameof(bufferPtr));
  1261. if (statePtr == null)
  1262. throw new ArgumentNullException(nameof(statePtr));
  1263. var numBytes = UnsafeUtility.SizeOf<TValue>();
  1264. if (bufferSize < numBytes)
  1265. throw new ArgumentException(
  1266. $"bufferSize={bufferSize} < sizeof(TValue)={numBytes}", nameof(bufferSize));
  1267. // C# won't let us use a pointer to a generically defined type. Work
  1268. // around this by using UnsafeUtility.
  1269. var value = default(TValue);
  1270. var valuePtr = UnsafeUtility.AddressOf(ref value);
  1271. UnsafeUtility.MemCpy(valuePtr, bufferPtr, numBytes);
  1272. WriteValueIntoState(value, statePtr);
  1273. }
  1274. /// <inheritdoc />
  1275. public override unsafe void WriteValueFromObjectIntoState(object value, void* statePtr)
  1276. {
  1277. if (statePtr == null)
  1278. throw new ArgumentNullException(nameof(statePtr));
  1279. if (value == null)
  1280. throw new ArgumentNullException(nameof(value));
  1281. // If value is not of expected type, try to convert.
  1282. if (!(value is TValue))
  1283. value = Convert.ChangeType(value, typeof(TValue));
  1284. var valueOfType = (TValue)value;
  1285. WriteValueIntoState(valueOfType, statePtr);
  1286. }
  1287. public virtual unsafe void WriteValueIntoState(TValue value, void* statePtr)
  1288. {
  1289. ////REVIEW: should we be able to even tell from layouts which controls support writing and which don't?
  1290. throw new NotSupportedException(
  1291. $"Control '{this}' does not support writing");
  1292. }
  1293. /// <inheritdoc />
  1294. public override unsafe object ReadValueFromBufferAsObject(void* buffer, int bufferSize)
  1295. {
  1296. if (buffer == null)
  1297. throw new ArgumentNullException(nameof(buffer));
  1298. var valueSize = UnsafeUtility.SizeOf<TValue>();
  1299. if (bufferSize < valueSize)
  1300. throw new ArgumentException(
  1301. $"Expecting buffer of at least {valueSize} bytes for value of type {typeof(TValue).Name} but got buffer of only {bufferSize} bytes instead",
  1302. nameof(bufferSize));
  1303. var value = default(TValue);
  1304. var valuePtr = UnsafeUtility.AddressOf(ref value);
  1305. UnsafeUtility.MemCpy(valuePtr, buffer, valueSize);
  1306. return value;
  1307. }
  1308. private static unsafe bool CompareValue(ref TValue firstValue, ref TValue secondValue)
  1309. {
  1310. var firstValuePtr = UnsafeUtility.AddressOf(ref firstValue);
  1311. var secondValuePtr = UnsafeUtility.AddressOf(ref secondValue);
  1312. // NOTE: We're comparing raw memory of processed values here (which are guaranteed to be structs or
  1313. // primitives), not state. Means we don't have to take bits into account here.
  1314. return UnsafeUtility.MemCmp(firstValuePtr, secondValuePtr, UnsafeUtility.SizeOf<TValue>()) != 0;
  1315. }
  1316. public override unsafe bool CompareValue(void* firstStatePtr, void* secondStatePtr)
  1317. {
  1318. ////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
  1319. var firstValue = ReadValueFromState(firstStatePtr);
  1320. var secondValue = ReadValueFromState(secondStatePtr);
  1321. return CompareValue(ref firstValue, ref secondValue);
  1322. }
  1323. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1324. public TValue ProcessValue(TValue value)
  1325. {
  1326. ProcessValue(ref value);
  1327. return value;
  1328. }
  1329. /// <summary>
  1330. /// Applies all control processors to the passed value.
  1331. /// </summary>
  1332. /// <param name="value"></param>
  1333. /// <remarks>
  1334. /// Use this overload when your state struct is large to avoid creating copies of the state.
  1335. /// </remarks>
  1336. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1337. public void ProcessValue(ref TValue value)
  1338. {
  1339. if (m_ProcessorStack.length <= 0)
  1340. return;
  1341. value = m_ProcessorStack.firstValue.Process(value, this);
  1342. if (m_ProcessorStack.additionalValues == null)
  1343. return;
  1344. for (var i = 0; i < m_ProcessorStack.length - 1; ++i)
  1345. value = m_ProcessorStack.additionalValues[i].Process(value, this);
  1346. }
  1347. internal InlinedArray<InputProcessor<TValue>> m_ProcessorStack;
  1348. private TValue m_CachedValue;
  1349. private TValue m_UnprocessedCachedValue;
  1350. #if UNITY_EDITOR
  1351. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1352. private unsafe ref readonly TValue ReadStateInEditor()
  1353. {
  1354. // we don't use cached values during editor updates because editor updates cause controls to look at a
  1355. // different block of state memory, and since the cached values are from the play mode memory, we'd
  1356. // end up returning the wrong values.
  1357. m_EditorValue = ReadValueFromState(currentStatePtr);
  1358. return ref m_EditorValue;
  1359. }
  1360. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1361. private unsafe ref readonly TValue ReadUnprocessedStateInEditor()
  1362. {
  1363. m_UnprocessedEditorValue = ReadUnprocessedValueFromState(currentStatePtr);
  1364. return ref m_UnprocessedEditorValue;
  1365. }
  1366. // these fields are just to work with the fact that the 'value' property is ref readonly, so we
  1367. // need somewhere with a known lifetime to store these so they can be returned by ref.
  1368. private TValue m_EditorValue;
  1369. private TValue m_UnprocessedEditorValue;
  1370. #endif
  1371. // Only layouts are allowed to modify the processor stack.
  1372. internal TProcessor TryGetProcessor<TProcessor>()
  1373. where TProcessor : InputProcessor<TValue>
  1374. {
  1375. if (m_ProcessorStack.length > 0)
  1376. {
  1377. if (m_ProcessorStack.firstValue is TProcessor processor)
  1378. return processor;
  1379. if (m_ProcessorStack.additionalValues != null)
  1380. for (var i = 0; i < m_ProcessorStack.length - 1; ++i)
  1381. if (m_ProcessorStack.additionalValues[i] is TProcessor result)
  1382. return result;
  1383. }
  1384. return default;
  1385. }
  1386. internal override void AddProcessor(object processor)
  1387. {
  1388. if (!(processor is InputProcessor<TValue> processorOfType))
  1389. throw new ArgumentException(
  1390. $"Cannot add processor of type '{processor.GetType().Name}' to control of type '{GetType().Name}'", nameof(processor));
  1391. m_ProcessorStack.Append(processorOfType);
  1392. }
  1393. #if UNITY_EDITOR
  1394. internal override IEnumerable<object> GetProcessors()
  1395. {
  1396. foreach (var processor in m_ProcessorStack)
  1397. yield return processor;
  1398. }
  1399. #endif
  1400. internal bool evaluateProcessorsEveryRead = false;
  1401. protected override void FinishSetup()
  1402. {
  1403. foreach (var processor in m_ProcessorStack)
  1404. if (processor.cachingPolicy == InputProcessor.CachingPolicy.EvaluateOnEveryRead)
  1405. evaluateProcessorsEveryRead = true;
  1406. base.FinishSetup();
  1407. }
  1408. internal InputProcessor<TValue>[] processors => m_ProcessorStack.ToArray();
  1409. }
  1410. }