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.

UnityRemoteSupport.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. #if UNITY_EDITOR
  2. using System;
  3. using System.Runtime.InteropServices;
  4. using UnityEditor;
  5. using UnityEngine.InputSystem.LowLevel;
  6. namespace UnityEngine.InputSystem
  7. {
  8. /// <summary>
  9. /// Adds support for processing input-related messages sent from the <c>Unite Remote</c> app.
  10. /// </summary>
  11. /// <remarks>
  12. /// A hook in the Unity runtime allows us to observe messages received from the remote (see Modules/GenericRemoteEditor).
  13. /// We get the binary blob of each message and a shot at processing the message instead of
  14. /// the native code doing it.
  15. /// </remarks>
  16. internal static class UnityRemoteSupport
  17. {
  18. public static bool isConnected => s_State.connected;
  19. public static void Initialize()
  20. {
  21. InputRuntime.s_Instance.onUnityRemoteMessage = ProcessMessageFromUnityRemote;
  22. InputSystem.onSettingsChange += () =>
  23. {
  24. if (InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kDisableUnityRemoteSupport))
  25. {
  26. InputRuntime.s_Instance.onUnityRemoteMessage = null;
  27. if (s_State.connected)
  28. Disconnect();
  29. }
  30. else
  31. InputRuntime.s_Instance.onUnityRemoteMessage = ProcessMessageFromUnityRemote;
  32. };
  33. }
  34. private static unsafe bool ProcessMessageFromUnityRemote(IntPtr messageData)
  35. {
  36. var messageHeader = (MessageHeader*)messageData;
  37. switch (messageHeader->type)
  38. {
  39. case (byte)MessageType.Hello:
  40. if (s_State.connected)
  41. break;
  42. // Install handlers.
  43. s_State.deviceChangeHandler = OnDeviceChange;
  44. s_State.deviceCommandHandler = OnDeviceCommand; ////REVIEW: We really should have a way of installing a handler just for a specific device.
  45. InputSystem.onDeviceChange += s_State.deviceChangeHandler;
  46. InputSystem.onDeviceCommand += s_State.deviceCommandHandler;
  47. // Add devices.
  48. s_State.touchscreen = InputSystem.AddDevice<Touchscreen>();
  49. s_State.touchscreen.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  50. s_State.accelerometer = InputSystem.AddDevice<Accelerometer>();
  51. s_State.accelerometer.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  52. // Gryo etc. added only when we receive GyroSettingsMessage.
  53. s_State.connected = true;
  54. Debug.Log("Unity Remote connected to input!");
  55. break;
  56. case (byte)MessageType.Goodbye:
  57. if (!s_State.connected)
  58. break;
  59. Disconnect();
  60. Debug.Log("Unity Remote disconnected from input!");
  61. break;
  62. case (byte)MessageType.Options:
  63. var optionsMessage = (OptionsMessage*)messageData;
  64. s_State.screenSize = DetermineScreenSize(optionsMessage->dimension1, optionsMessage->dimension2);
  65. break;
  66. case (byte)MessageType.TouchInput:
  67. if (s_State.touchscreen == null)
  68. break;
  69. // Android Remote seems to not be sending the last two fields (azimuthAngle and attitudeAngle).
  70. if (messageHeader->length < 56)
  71. break;
  72. var touchMessage = (TouchInputMessage*)messageData;
  73. var phase = TouchPhase.None;
  74. switch (touchMessage->phase)
  75. {
  76. case (int)UnityEngine.TouchPhase.Began: phase = TouchPhase.Began; break;
  77. case (int)UnityEngine.TouchPhase.Canceled: phase = TouchPhase.Canceled; break;
  78. case (int)UnityEngine.TouchPhase.Ended: phase = TouchPhase.Ended; break;
  79. case (int)UnityEngine.TouchPhase.Moved: phase = TouchPhase.Moved; break;
  80. // Ignore stationary.
  81. }
  82. if (phase == default)
  83. break;
  84. InputSystem.QueueStateEvent(s_State.touchscreen, new TouchState
  85. {
  86. touchId = touchMessage->id + 1,
  87. phase = phase,
  88. position = MapRemoteTouchCoordinatesToLocal(new Vector2(touchMessage->positionX, touchMessage->positionY)),
  89. radius = new Vector2(touchMessage->radius, touchMessage->radius),
  90. pressure = touchMessage->pressure
  91. });
  92. break;
  93. case (byte)MessageType.GyroSettings:
  94. var gyroSettingsMessage = (GyroSettingsMessage*)messageData;
  95. if (!s_State.gyroInitialized)
  96. {
  97. // Message itself indicates presence of a gyro. Add the devices.
  98. s_State.gyroscope = InputSystem.AddDevice<Gyroscope>();
  99. s_State.attitude = InputSystem.AddDevice<AttitudeSensor>();
  100. s_State.gravity = InputSystem.AddDevice<GravitySensor>();
  101. s_State.linearAcceleration = InputSystem.AddDevice<LinearAccelerationSensor>();
  102. s_State.gyroscope.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  103. s_State.attitude.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  104. s_State.gravity.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  105. s_State.linearAcceleration.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  106. s_State.gyroInitialized = true;
  107. }
  108. // Disable them if they are not currently enabled.
  109. if (gyroSettingsMessage->enabled == 0)
  110. {
  111. InputSystem.DisableDevice(s_State.gyroscope);
  112. InputSystem.DisableDevice(s_State.attitude);
  113. InputSystem.DisableDevice(s_State.gravity);
  114. InputSystem.DisableDevice(s_State.linearAcceleration);
  115. }
  116. else
  117. {
  118. s_State.gyroEnabled = true;
  119. }
  120. s_State.gyroUpdateInterval = gyroSettingsMessage->receivedGyroUpdateInternal;
  121. break;
  122. case (byte)MessageType.GyroInput:
  123. var gyroInputMessage = (GyroInputMessage*)messageData;
  124. if (s_State.attitude != null && s_State.attitude.enabled)
  125. {
  126. InputSystem.QueueStateEvent(s_State.attitude, new AttitudeState
  127. {
  128. attitude = new Quaternion(gyroInputMessage->attitudeX, gyroInputMessage->attitudeY, gyroInputMessage->attitudeZ,
  129. gyroInputMessage->attitudeW)
  130. });
  131. }
  132. if (s_State.gyroscope != null && s_State.gyroscope.enabled)
  133. {
  134. InputSystem.QueueStateEvent(s_State.gyroscope, new GyroscopeState
  135. {
  136. angularVelocity = new Vector3(gyroInputMessage->rotationRateX, gyroInputMessage->rotationRateY,
  137. gyroInputMessage->rotationRateZ)
  138. });
  139. }
  140. if (s_State.gravity != null && s_State.gravity.enabled)
  141. {
  142. InputSystem.QueueStateEvent(s_State.gravity, new GravityState
  143. {
  144. gravity = new Vector3(gyroInputMessage->gravityX, gyroInputMessage->gravityY,
  145. gyroInputMessage->gravityZ)
  146. });
  147. }
  148. if (s_State.linearAcceleration != null && s_State.linearAcceleration.enabled)
  149. {
  150. InputSystem.QueueStateEvent(s_State.linearAcceleration, new LinearAccelerationState
  151. {
  152. acceleration = new Vector3(gyroInputMessage->userAccelerationX, gyroInputMessage->userAccelerationY,
  153. gyroInputMessage->userAccelerationZ)
  154. });
  155. }
  156. break;
  157. case (byte)MessageType.AccelerometerInput:
  158. if (s_State.accelerometer == null)
  159. break;
  160. var accelerometerMessage = (AccelerometerInputMessage*)messageData;
  161. InputSystem.QueueStateEvent(s_State.accelerometer, new AccelerometerState
  162. {
  163. acceleration = new Vector3(accelerometerMessage->accelerationX, accelerometerMessage->accelerationY,
  164. accelerometerMessage->accelerationZ)
  165. });
  166. break;
  167. }
  168. return false;
  169. }
  170. private static void Disconnect()
  171. {
  172. InputSystem.RemoveDevice(s_State.touchscreen);
  173. InputSystem.RemoveDevice(s_State.accelerometer);
  174. if (s_State.gyroscope != null)
  175. InputSystem.RemoveDevice(s_State.gyroscope);
  176. if (s_State.attitude != null)
  177. InputSystem.RemoveDevice(s_State.attitude);
  178. if (s_State.gravity != null)
  179. InputSystem.RemoveDevice(s_State.gravity);
  180. if (s_State.linearAcceleration != null)
  181. InputSystem.RemoveDevice(s_State.linearAcceleration);
  182. ResetGlobalState();
  183. }
  184. private static void OnDeviceChange(InputDevice device, InputDeviceChange change)
  185. {
  186. switch (change)
  187. {
  188. case InputDeviceChange.Removed:
  189. // Deal with someone manually removing one of our devices.
  190. if (device == s_State.accelerometer)
  191. s_State.accelerometer = null;
  192. else if (device == s_State.attitude)
  193. s_State.accelerometer = null;
  194. else if (device == s_State.gravity)
  195. s_State.gravity = null;
  196. else if (device == s_State.gyroscope)
  197. s_State.gyroscope = null;
  198. else if (device == s_State.touchscreen)
  199. s_State.touchscreen = null;
  200. else if (device == s_State.linearAcceleration)
  201. s_State.linearAcceleration = null;
  202. break;
  203. case InputDeviceChange.Enabled:
  204. case InputDeviceChange.Disabled:
  205. // If it's any of our devices that make up the remote gyro,
  206. // send a message to the remote.
  207. if (device == s_State.attitude || device == s_State.gravity || device == s_State.gyroscope ||
  208. device == s_State.linearAcceleration)
  209. {
  210. SyncGyroEnabledInRemote();
  211. }
  212. break;
  213. }
  214. }
  215. private static unsafe long? OnDeviceCommand(InputDevice device, InputDeviceCommand* command)
  216. {
  217. if (device != s_State.attitude && device != s_State.gyroscope && device != s_State.gravity &&
  218. device != s_State.linearAcceleration)
  219. return null;
  220. if (command->type == SetSamplingFrequencyCommand.Type)
  221. {
  222. s_State.gyroUpdateInterval = ((SetSamplingFrequencyCommand*)command)->frequency;
  223. InputRuntime.s_Instance.SetUnityRemoteGyroUpdateInterval(s_State.gyroUpdateInterval);
  224. return InputDeviceCommand.GenericSuccess;
  225. }
  226. if (command->type == QuerySamplingFrequencyCommand.Type)
  227. {
  228. ((QuerySamplingFrequencyCommand*)command)->frequency = s_State.gyroUpdateInterval;
  229. return InputDeviceCommand.GenericSuccess;
  230. }
  231. return InputDeviceCommand.GenericFailure;
  232. }
  233. private static void SyncGyroEnabledInRemote()
  234. {
  235. var enabled = (s_State.attitude?.enabled ?? false) || (s_State.gravity?.enabled ?? false) ||
  236. (s_State.gyroscope?.enabled ?? false) || (s_State.linearAcceleration?.enabled ?? false);
  237. if (enabled != s_State.gyroEnabled)
  238. {
  239. s_State.gyroEnabled = enabled;
  240. InputRuntime.s_Instance.SetUnityRemoteGyroEnabled(enabled);
  241. }
  242. }
  243. // This is taken from HandleOptionsMessage() in GenericRemote.cpp.
  244. private static Vector2 DetermineScreenSize(int dimension1, int dimension2)
  245. {
  246. const float kMaxPixels = 640 * 480; // limit the resolution to VGA
  247. float screenPixels = dimension1 * dimension2;
  248. var divider = (int)Mathf.Ceil(Mathf.Sqrt(screenPixels / kMaxPixels));
  249. if (divider == 0)
  250. return default;
  251. var hdim1 = dimension1 / divider;
  252. var hdim2 = dimension2 / divider;
  253. // GetConfigValue is private. Reflect around it.
  254. var getConfigValueMethod = typeof(EditorSettings).GetMethod("GetConfigValue");
  255. if (getConfigValueMethod != null && "Normal".Equals(getConfigValueMethod.Invoke(null, new[] { "UnityRemoteResolution" })))
  256. {
  257. hdim1 = dimension1;
  258. hdim2 = dimension2;
  259. }
  260. if (hdim1 >= 1 && hdim2 >= 1)
  261. return new Vector2(dimension1, dimension2);
  262. return default;
  263. }
  264. private static Vector2 MapRemoteTouchCoordinatesToLocal(Vector2 position)
  265. {
  266. var screenSizeRemote = s_State.screenSize;
  267. var screenSizeLocal = InputRuntime.s_Instance.screenSize;
  268. return new Vector2(
  269. position.x / screenSizeRemote.x * screenSizeLocal.x,
  270. position.y = position.y / screenSizeRemote.y * screenSizeLocal.y);
  271. }
  272. // See Editor/Src/RemoteInput/GenericRemote.cpp
  273. internal enum MessageType : byte
  274. {
  275. Invalid = 0,
  276. Hello = 1,
  277. Options = 2,
  278. GyroSettings = 3,
  279. DeviceOrientation = 4,
  280. DeviceFeatures = 5,
  281. TouchInput = 10,
  282. AccelerometerInput = 11,
  283. TrackBallInput = 12,
  284. Key = 13,
  285. GyroInput = 14,
  286. MousePresence = 15,
  287. JoystickInput = 16,
  288. JoystickNames = 17,
  289. WebCamDeviceList = 20,
  290. WebCamStream = 21,
  291. LocationServiceData = 30,
  292. CompassData = 31,
  293. Goodbye = 32,
  294. Reserved = 255,
  295. }
  296. internal interface IUnityRemoteMessage
  297. {
  298. byte staticType { get; }
  299. }
  300. [StructLayout(LayoutKind.Explicit, Size = 5)]
  301. internal struct MessageHeader
  302. {
  303. // Unfortunately, the header has an odd 5 byte length and everything
  304. // coming after it is misaligned. Reason is that native reads is as a stream
  305. // and wants to pack tightly.
  306. [FieldOffset(0)] public byte type;
  307. [FieldOffset(1)] public int length;
  308. }
  309. [StructLayout(LayoutKind.Explicit)]
  310. internal unsafe struct HelloMessage : IUnityRemoteMessage
  311. {
  312. [FieldOffset(0)] public MessageHeader header;
  313. [FieldOffset(5)] public uint protocolIdLength;
  314. [FieldOffset(9)] public fixed char protocolId[11];
  315. [FieldOffset(20)] public int protocolVersion;
  316. public byte staticType => (byte)MessageType.Hello;
  317. public static HelloMessage Create()
  318. {
  319. var msg = default(HelloMessage);
  320. msg.protocolIdLength = 11;
  321. msg.protocolId[0] = 'U';
  322. msg.protocolId[1] = 'n';
  323. msg.protocolId[2] = 'i';
  324. msg.protocolId[3] = 't';
  325. msg.protocolId[4] = 'y';
  326. msg.protocolId[5] = 'R';
  327. msg.protocolId[6] = 'e';
  328. msg.protocolId[7] = 'm';
  329. msg.protocolId[8] = 'o';
  330. msg.protocolId[9] = 't';
  331. msg.protocolId[10] = 'e';
  332. msg.protocolVersion = 0;
  333. return msg;
  334. }
  335. }
  336. [StructLayout(LayoutKind.Explicit)]
  337. internal struct OptionsMessage : IUnityRemoteMessage
  338. {
  339. [FieldOffset(0)] public MessageHeader header;
  340. [FieldOffset(5)] public int dimension1;
  341. [FieldOffset(9)] public int dimension2;
  342. public byte staticType => (byte)MessageType.Options;
  343. }
  344. [StructLayout(LayoutKind.Explicit)]
  345. internal struct GoodbyeMessage : IUnityRemoteMessage
  346. {
  347. [FieldOffset(0)] public MessageHeader header;
  348. public byte staticType => (byte)MessageType.Goodbye;
  349. }
  350. [StructLayout(LayoutKind.Explicit)]
  351. internal struct TouchInputMessage : IUnityRemoteMessage
  352. {
  353. [FieldOffset(0)] public MessageHeader header;
  354. [FieldOffset(5)] public float positionX;
  355. [FieldOffset(9)] public float positionY;
  356. [FieldOffset(13)] public ulong frame;
  357. [FieldOffset(21)] public int id;
  358. [FieldOffset(25)] public int phase;
  359. [FieldOffset(29)] public int tapCount;
  360. [FieldOffset(33)] public float radius;
  361. [FieldOffset(37)] public float radiusVariance;
  362. [FieldOffset(41)] public int type;
  363. [FieldOffset(45)] public float pressure;
  364. [FieldOffset(49)] public float maximumPossiblePressure;
  365. [FieldOffset(53)] public float azimuthAngle;
  366. [FieldOffset(57)] public float altitudeAngle;
  367. public byte staticType => (byte)MessageType.TouchInput;
  368. }
  369. [StructLayout(LayoutKind.Explicit)]
  370. internal struct GyroSettingsMessage : IUnityRemoteMessage
  371. {
  372. [FieldOffset(0)] public MessageHeader header;
  373. [FieldOffset(5)] public int enabled;
  374. [FieldOffset(9)] public float receivedGyroUpdateInternal;
  375. public byte staticType => (byte)MessageType.GyroSettings;
  376. }
  377. [StructLayout(LayoutKind.Explicit)]
  378. internal struct GyroInputMessage : IUnityRemoteMessage
  379. {
  380. [FieldOffset(0)] public MessageHeader header;
  381. [FieldOffset(5)] public float rotationRateX;
  382. [FieldOffset(9)] public float rotationRateY;
  383. [FieldOffset(13)] public float rotationRateZ;
  384. [FieldOffset(17)] public float rotationRateUnbiasedX;
  385. [FieldOffset(21)] public float rotationRateUnbiasedY;
  386. [FieldOffset(25)] public float rotationRateUnbiasedZ;
  387. [FieldOffset(29)] public float gravityX;
  388. [FieldOffset(33)] public float gravityY;
  389. [FieldOffset(37)] public float gravityZ;
  390. [FieldOffset(41)] public float userAccelerationX;
  391. [FieldOffset(45)] public float userAccelerationY;
  392. [FieldOffset(49)] public float userAccelerationZ;
  393. [FieldOffset(53)] public float attitudeX;
  394. [FieldOffset(57)] public float attitudeY;
  395. [FieldOffset(61)] public float attitudeZ;
  396. [FieldOffset(65)] public float attitudeW;
  397. public byte staticType => (byte)MessageType.GyroInput;
  398. }
  399. [StructLayout(LayoutKind.Explicit)]
  400. internal struct AccelerometerInputMessage : IUnityRemoteMessage
  401. {
  402. [FieldOffset(0)] public MessageHeader header;
  403. [FieldOffset(5)] public float accelerationX;
  404. [FieldOffset(9)] public float accelerationY;
  405. [FieldOffset(13)] public float accelerationZ;
  406. [FieldOffset(17)] public float deltaTime;
  407. public byte staticType => (byte)MessageType.AccelerometerInput;
  408. }
  409. private struct State
  410. {
  411. public bool connected;
  412. public bool gyroInitialized;
  413. public bool gyroEnabled;
  414. public float gyroUpdateInterval;
  415. public Vector2 screenSize;
  416. public Action<InputDevice, InputDeviceChange> deviceChangeHandler;
  417. public InputDeviceCommandDelegate deviceCommandHandler;
  418. // Devices that we create for receiving input from the remote.
  419. public Touchscreen touchscreen;
  420. public Accelerometer accelerometer;
  421. public Gyroscope gyroscope;
  422. public AttitudeSensor attitude;
  423. public GravitySensor gravity;
  424. public LinearAccelerationSensor linearAcceleration;
  425. }
  426. private static State s_State;
  427. ////TODO: hook this into Hakan's new cleanup mechanism
  428. internal static void ResetGlobalState()
  429. {
  430. if (s_State.deviceChangeHandler != null)
  431. InputSystem.onDeviceChange -= s_State.deviceChangeHandler;
  432. if (s_State.deviceCommandHandler != null)
  433. InputSystem.onDeviceCommand -= s_State.deviceCommandHandler;
  434. s_State = default;
  435. }
  436. }
  437. }
  438. #endif