NativeInputRuntime.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. using System;
  2. using Unity.Collections.LowLevel.Unsafe;
  3. using UnityEngineInternal.Input;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. #endif
  7. // This should be the only file referencing the API at UnityEngineInternal.Input.
  8. #if !UNITY_2019_2_OR_NEWER
  9. // The NativeInputSystem APIs are marked obsolete in 19.1, because they are becoming internal in 19.2
  10. #pragma warning disable 618
  11. #endif
  12. namespace UnityEngine.InputSystem.LowLevel
  13. {
  14. /// <summary>
  15. /// Implements <see cref="IInputRuntime"/> based on <see cref="NativeInputSystem"/>.
  16. /// </summary>
  17. internal class NativeInputRuntime : IInputRuntime
  18. {
  19. public static readonly NativeInputRuntime instance = new NativeInputRuntime();
  20. public int AllocateDeviceId()
  21. {
  22. return NativeInputSystem.AllocateDeviceId();
  23. }
  24. public void Update(InputUpdateType updateType)
  25. {
  26. NativeInputSystem.Update((NativeInputUpdateType)updateType);
  27. }
  28. public unsafe void QueueEvent(InputEvent* ptr)
  29. {
  30. NativeInputSystem.QueueInputEvent((IntPtr)ptr);
  31. }
  32. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "False positive.")]
  33. public unsafe long DeviceCommand(int deviceId, InputDeviceCommand* commandPtr)
  34. {
  35. if (commandPtr == null)
  36. throw new System.ArgumentNullException(nameof(commandPtr));
  37. return NativeInputSystem.IOCTL(deviceId, commandPtr->type, new IntPtr(commandPtr->payloadPtr), commandPtr->payloadSizeInBytes);
  38. }
  39. public unsafe InputUpdateDelegate onUpdate
  40. {
  41. get => m_OnUpdate;
  42. set
  43. {
  44. if (value != null)
  45. NativeInputSystem.onUpdate =
  46. (updateType, eventBufferPtr) =>
  47. {
  48. var buffer = new InputEventBuffer((InputEvent*)eventBufferPtr->eventBuffer,
  49. eventBufferPtr->eventCount,
  50. sizeInBytes: eventBufferPtr->sizeInBytes,
  51. capacityInBytes: eventBufferPtr->capacityInBytes);
  52. try
  53. {
  54. value((InputUpdateType)updateType, ref buffer);
  55. }
  56. catch (Exception e)
  57. {
  58. Debug.LogError($"{e.GetType().Name} during event processing of {updateType} update; resetting event buffer");
  59. Debug.LogException(e);
  60. buffer.Reset();
  61. }
  62. if (buffer.eventCount > 0)
  63. {
  64. eventBufferPtr->eventCount = buffer.eventCount;
  65. eventBufferPtr->sizeInBytes = (int)buffer.sizeInBytes;
  66. eventBufferPtr->capacityInBytes = (int)buffer.capacityInBytes;
  67. eventBufferPtr->eventBuffer =
  68. NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer.data);
  69. }
  70. else
  71. {
  72. eventBufferPtr->eventCount = 0;
  73. eventBufferPtr->sizeInBytes = 0;
  74. }
  75. };
  76. else
  77. NativeInputSystem.onUpdate = null;
  78. m_OnUpdate = value;
  79. }
  80. }
  81. public Action<InputUpdateType> onBeforeUpdate
  82. {
  83. get => m_OnBeforeUpdate;
  84. set
  85. {
  86. // This is stupid but the enum prevents us from jacking the delegate in directly.
  87. // This means we get a double dispatch here :(
  88. if (value != null)
  89. NativeInputSystem.onBeforeUpdate = updateType => value((InputUpdateType)updateType);
  90. else
  91. NativeInputSystem.onBeforeUpdate = null;
  92. m_OnBeforeUpdate = value;
  93. }
  94. }
  95. public Func<InputUpdateType, bool> onShouldRunUpdate
  96. {
  97. get => m_OnShouldRunUpdate;
  98. set
  99. {
  100. // This is stupid but the enum prevents us from jacking the delegate in directly.
  101. // This means we get a double dispatch here :(
  102. if (value != null)
  103. NativeInputSystem.onShouldRunUpdate = updateType => value((InputUpdateType)updateType);
  104. else
  105. NativeInputSystem.onShouldRunUpdate = null;
  106. m_OnShouldRunUpdate = value;
  107. }
  108. }
  109. public Action<int, string> onDeviceDiscovered
  110. {
  111. get => NativeInputSystem.onDeviceDiscovered;
  112. set => NativeInputSystem.onDeviceDiscovered = value;
  113. }
  114. public Action onShutdown
  115. {
  116. get => m_ShutdownMethod;
  117. set
  118. {
  119. if (value == null)
  120. {
  121. #if UNITY_EDITOR
  122. EditorApplication.wantsToQuit -= OnWantsToShutdown;
  123. #else
  124. Application.quitting -= OnShutdown;
  125. #endif
  126. }
  127. else if (m_ShutdownMethod == null)
  128. {
  129. #if UNITY_EDITOR
  130. EditorApplication.wantsToQuit += OnWantsToShutdown;
  131. #else
  132. Application.quitting += OnShutdown;
  133. #endif
  134. }
  135. m_ShutdownMethod = value;
  136. }
  137. }
  138. public Action<bool> onPlayerFocusChanged
  139. {
  140. get => m_FocusChangedMethod;
  141. set
  142. {
  143. if (value == null)
  144. Application.focusChanged -= OnFocusChanged;
  145. else if (m_FocusChangedMethod == null)
  146. Application.focusChanged += OnFocusChanged;
  147. m_FocusChangedMethod = value;
  148. }
  149. }
  150. public float pollingFrequency
  151. {
  152. get => m_PollingFrequency;
  153. set
  154. {
  155. m_PollingFrequency = value;
  156. NativeInputSystem.SetPollingFrequency(value);
  157. }
  158. }
  159. public double currentTime => NativeInputSystem.currentTime;
  160. ////REVIEW: this applies the offset, currentTime doesn't
  161. public double currentTimeForFixedUpdate => Time.fixedUnscaledTime + currentTimeOffsetToRealtimeSinceStartup;
  162. public double currentTimeOffsetToRealtimeSinceStartup => NativeInputSystem.currentTimeOffsetToRealtimeSinceStartup;
  163. public float unscaledGameTime => Time.unscaledTime;
  164. public bool runInBackground => Application.runInBackground;
  165. private Action m_ShutdownMethod;
  166. private InputUpdateDelegate m_OnUpdate;
  167. private Action<InputUpdateType> m_OnBeforeUpdate;
  168. private Func<InputUpdateType, bool> m_OnShouldRunUpdate;
  169. private float m_PollingFrequency = 60.0f;
  170. private bool m_DidCallOnShutdown = false;
  171. private void OnShutdown()
  172. {
  173. m_ShutdownMethod();
  174. }
  175. private bool OnWantsToShutdown()
  176. {
  177. if (!m_DidCallOnShutdown)
  178. {
  179. // we should use `EditorApplication.quitting`, but that is too late
  180. // to send an analytics event, because Analytics is already shut down
  181. // at that point. So we use `EditorApplication.wantsToQuit`, and make sure
  182. // to only use the first time. This is currently only used for analytics,
  183. // and getting analytics before we actually shut downn in some cases is
  184. // better then never.
  185. OnShutdown();
  186. m_DidCallOnShutdown = true;
  187. }
  188. return true;
  189. }
  190. private Action<bool> m_FocusChangedMethod;
  191. private void OnFocusChanged(bool focus)
  192. {
  193. m_FocusChangedMethod(focus);
  194. }
  195. public ScreenOrientation screenOrientation => Screen.orientation;
  196. public bool isInBatchMode => Application.isBatchMode;
  197. #if UNITY_EDITOR
  198. public bool isInPlayMode => EditorApplication.isPlaying;
  199. public bool isPaused => EditorApplication.isPaused;
  200. private Action<PlayModeStateChange> m_OnPlayModeChanged;
  201. private Action m_OnProjectChanged;
  202. private void OnPlayModeStateChanged(PlayModeStateChange value)
  203. {
  204. m_OnPlayModeChanged(value);
  205. }
  206. private void OnProjectChanged()
  207. {
  208. m_OnProjectChanged();
  209. }
  210. public Action<PlayModeStateChange> onPlayModeChanged
  211. {
  212. get => m_OnPlayModeChanged;
  213. set
  214. {
  215. if (value == null)
  216. EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
  217. else if (m_OnPlayModeChanged == null)
  218. EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
  219. m_OnPlayModeChanged = value;
  220. }
  221. }
  222. public Action onProjectChange
  223. {
  224. get => m_OnProjectChanged;
  225. set
  226. {
  227. if (value == null)
  228. EditorApplication.projectChanged -= OnProjectChanged;
  229. else if (m_OnProjectChanged == null)
  230. EditorApplication.projectChanged += OnProjectChanged;
  231. m_OnProjectChanged = value;
  232. }
  233. }
  234. #endif // UNITY_EDITOR
  235. public void RegisterAnalyticsEvent(string name, int maxPerHour, int maxPropertiesPerEvent)
  236. {
  237. #if UNITY_ANALYTICS
  238. const string vendorKey = "unity.input";
  239. #if UNITY_EDITOR
  240. EditorAnalytics.RegisterEventWithLimit(name, maxPerHour, maxPropertiesPerEvent, vendorKey);
  241. #else
  242. Analytics.Analytics.RegisterEvent(name, maxPerHour, maxPropertiesPerEvent, vendorKey);
  243. #endif // UNITY_EDITOR
  244. #endif // UNITY_ANALYTICS
  245. }
  246. public void SendAnalyticsEvent(string name, object data)
  247. {
  248. #if UNITY_ANALYTICS
  249. #if UNITY_EDITOR
  250. EditorAnalytics.SendEventWithLimit(name, data);
  251. #else
  252. Analytics.Analytics.SendEvent(name, data);
  253. #endif // UNITY_EDITOR
  254. #endif // UNITY_ANALYTICS
  255. }
  256. }
  257. }