InputState.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. using System;
  2. using Unity.Collections.LowLevel.Unsafe;
  3. using UnityEngine.InputSystem.Utilities;
  4. ////REVIEW: allow to restrict state change monitors to specific updates?
  5. namespace UnityEngine.InputSystem.LowLevel
  6. {
  7. using NotifyControlValueChangeAction = Action<InputControl, double, InputEventPtr, long>;
  8. using NotifyTimerExpiredAction = Action<InputControl, double, long, int>;
  9. /// <summary>
  10. /// Low-level APIs for working with input state memory.
  11. /// </summary>
  12. public static class InputState
  13. {
  14. /// <summary>
  15. /// The type of update that was last run or is currently being run on the input state.
  16. /// </summary>
  17. /// <remarks>
  18. /// This determines which set of buffers are currently active and thus determines which view code
  19. /// that queries input state will receive. For example, during editor updates, this will be
  20. /// <see cref="InputUpdateType.Editor"/> and the state buffers for the editor will be active.
  21. /// </remarks>
  22. public static InputUpdateType currentUpdateType => InputUpdate.s_LastUpdateType;
  23. ////FIXME: ATM this does not work for editor updates
  24. /// <summary>
  25. /// The number of times the current input state has been updated.
  26. /// </summary>
  27. public static uint updateCount => InputUpdate.s_UpdateStepCount;
  28. public static double currentTime => InputRuntime.s_Instance.currentTime + InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  29. /// <summary>
  30. /// Callback that is triggered when the state of an input device changes.
  31. /// </summary>
  32. /// <remarks>
  33. /// The first parameter is the device whose state was changed the second parameter is the event
  34. /// that triggered the change in state. Note that the latter may be <c>null</c> in case the
  35. /// change was performed directly through <see cref="Change"/> rather than through an event.
  36. /// </remarks>
  37. public static event Action<InputDevice, InputEventPtr> onChange
  38. {
  39. add => InputSystem.s_Manager.onDeviceStateChange += value;
  40. remove => InputSystem.s_Manager.onDeviceStateChange -= value;
  41. }
  42. public static unsafe void Change(InputDevice device, InputEventPtr eventPtr, InputUpdateType updateType = default)
  43. {
  44. if (device == null)
  45. throw new ArgumentNullException(nameof(device));
  46. if (!eventPtr.valid)
  47. throw new ArgumentNullException(nameof(eventPtr));
  48. // Make sure event is a StateEvent or DeltaStateEvent and has a format matching the device.
  49. FourCC stateFormat;
  50. if (eventPtr.IsA<StateEvent>())
  51. stateFormat = StateEvent.From(eventPtr)->stateFormat;
  52. else if (eventPtr.IsA<DeltaStateEvent>())
  53. stateFormat = DeltaStateEvent.From(eventPtr)->stateFormat;
  54. else
  55. {
  56. #if UNITY_EDITOR
  57. InputSystem.s_Manager.m_Diagnostics?.OnEventFormatMismatch(eventPtr, device);
  58. #endif
  59. return;
  60. }
  61. if (stateFormat != device.stateBlock.format)
  62. throw new ArgumentException(
  63. $"State format {stateFormat} from event does not match state format {device.stateBlock.format} of device {device}",
  64. nameof(eventPtr));
  65. InputSystem.s_Manager.UpdateState(device, eventPtr,
  66. updateType != default ? updateType : InputSystem.s_Manager.defaultUpdateType);
  67. }
  68. /// <summary>
  69. /// Perform one update of input state.
  70. /// </summary>
  71. /// <remarks>
  72. /// Incorporates the given state and triggers all state change monitors as needed.
  73. ///
  74. /// Note that input state changes performed with this method will not be visible on remotes as they will bypass
  75. /// event processing. It is effectively equivalent to directly writing into input state memory except that it
  76. /// also performs related tasks such as checking state change monitors, flipping buffers, or making the respective
  77. /// device current.
  78. /// </remarks>
  79. public static unsafe void Change<TState>(InputControl control, TState state, InputUpdateType updateType = default,
  80. InputEventPtr eventPtr = default)
  81. where TState : struct
  82. {
  83. if (control == null)
  84. throw new ArgumentNullException(nameof(control));
  85. if (control.stateBlock.bitOffset != 0 || control.stateBlock.sizeInBits % 8 != 0)
  86. throw new ArgumentException($"Cannot change state of bitfield control '{control}' using this method", nameof(control));
  87. var device = control.device;
  88. var stateSize = Math.Min(UnsafeUtility.SizeOf<TState>(), control.m_StateBlock.alignedSizeInBytes);
  89. var statePtr = UnsafeUtility.AddressOf(ref state);
  90. var stateOffset = control.stateBlock.byteOffset - device.stateBlock.byteOffset;
  91. InputSystem.s_Manager.UpdateState(device,
  92. updateType != default ? updateType : InputSystem.s_Manager.defaultUpdateType, statePtr, stateOffset,
  93. (uint)stateSize,
  94. eventPtr.valid
  95. ? eventPtr.internalTime
  96. : InputRuntime.s_Instance.currentTime,
  97. eventPtr: eventPtr);
  98. }
  99. public static bool IsIntegerFormat(this FourCC format)
  100. {
  101. return format == InputStateBlock.FormatBit ||
  102. format == InputStateBlock.FormatInt ||
  103. format == InputStateBlock.FormatByte ||
  104. format == InputStateBlock.FormatShort ||
  105. format == InputStateBlock.FormatSBit ||
  106. format == InputStateBlock.FormatUInt ||
  107. format == InputStateBlock.FormatUShort ||
  108. format == InputStateBlock.FormatLong ||
  109. format == InputStateBlock.FormatULong;
  110. }
  111. ////REVIEW: should these take an InputUpdateType argument?
  112. public static void AddChangeMonitor(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex = -1)
  113. {
  114. if (control == null)
  115. throw new ArgumentNullException(nameof(control));
  116. if (monitor == null)
  117. throw new ArgumentNullException(nameof(monitor));
  118. if (control.device.m_DeviceIndex == InputDevice.kInvalidDeviceIndex)
  119. throw new ArgumentException(string.Format("Device for control '{0}' has not been added to system"),
  120. nameof(control));
  121. InputSystem.s_Manager.AddStateChangeMonitor(control, monitor, monitorIndex);
  122. }
  123. public static IInputStateChangeMonitor AddChangeMonitor(InputControl control,
  124. NotifyControlValueChangeAction valueChangeCallback, int monitorIndex = -1,
  125. NotifyTimerExpiredAction timerExpiredCallback = null)
  126. {
  127. if (valueChangeCallback == null)
  128. throw new ArgumentNullException(nameof(valueChangeCallback));
  129. var monitor = new StateChangeMonitorDelegate
  130. {
  131. valueChangeCallback = valueChangeCallback,
  132. timerExpiredCallback = timerExpiredCallback
  133. };
  134. AddChangeMonitor(control, monitor, monitorIndex);
  135. return monitor;
  136. }
  137. public static void RemoveChangeMonitor(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex = -1)
  138. {
  139. if (control == null)
  140. throw new ArgumentNullException(nameof(control));
  141. if (monitor == null)
  142. throw new ArgumentNullException(nameof(monitor));
  143. InputSystem.s_Manager.RemoveStateChangeMonitor(control, monitor, monitorIndex);
  144. }
  145. /// <summary>
  146. /// Put a timeout on a previously registered state change monitor.
  147. /// </summary>
  148. /// <param name="control"></param>
  149. /// <param name="monitor"></param>
  150. /// <param name="time"></param>
  151. /// <param name="monitorIndex"></param>
  152. /// <param name="timerIndex"></param>
  153. /// <remarks>
  154. /// If by the given <paramref name="time"/>, no state change has been registered on the control monitored
  155. /// by the given <paramref name="monitor">state change monitor</paramref>, <see cref="IInputStateChangeMonitor.NotifyTimerExpired"/>
  156. /// will be called on <paramref name="monitor"/>. If a state change happens by the given <paramref name="time"/>,
  157. /// the monitor is notified as usual and the timer is automatically removed.
  158. /// </remarks>
  159. public static void AddChangeMonitorTimeout(InputControl control, IInputStateChangeMonitor monitor, double time, long monitorIndex = -1, int timerIndex = -1)
  160. {
  161. if (monitor == null)
  162. throw new ArgumentNullException(nameof(monitor));
  163. InputSystem.s_Manager.AddStateChangeMonitorTimeout(control, monitor, time, monitorIndex, timerIndex);
  164. }
  165. public static void RemoveChangeMonitorTimeout(IInputStateChangeMonitor monitor, long monitorIndex = -1, int timerIndex = -1)
  166. {
  167. if (monitor == null)
  168. throw new ArgumentNullException(nameof(monitor));
  169. InputSystem.s_Manager.RemoveStateChangeMonitorTimeout(monitor, monitorIndex, timerIndex);
  170. }
  171. private class StateChangeMonitorDelegate : IInputStateChangeMonitor
  172. {
  173. public NotifyControlValueChangeAction valueChangeCallback;
  174. public NotifyTimerExpiredAction timerExpiredCallback;
  175. public void NotifyControlStateChanged(InputControl control, double time, InputEventPtr eventPtr, long monitorIndex)
  176. {
  177. valueChangeCallback(control, time, eventPtr, monitorIndex);
  178. }
  179. public void NotifyTimerExpired(InputControl control, double time, long monitorIndex, int timerIndex)
  180. {
  181. timerExpiredCallback?.Invoke(control, time, monitorIndex, timerIndex);
  182. }
  183. }
  184. }
  185. }