123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- using System;
- using System.Collections.Generic;
- using NUnit.Framework;
- using UnityEngine.InputSystem.LowLevel;
- using Unity.Collections;
- using Unity.Collections.LowLevel.Unsafe;
- using UnityEngine.InputSystem.Layouts;
- using UnityEngine.InputSystem.Utilities;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- namespace UnityEngine.InputSystem
- {
- /// <summary>
- /// An implementation of <see cref="IInputRuntime"/> for use during tests.
- /// </summary>
- /// <remarks>
- /// This class is only available in the editor and in development players.
- ///
- /// The test runtime replaces the services usually supplied by <see cref="UnityEngineInternal.Input.NativeInputSystem"/>.
- /// </remarks>
- /// <seealso cref="InputTestFixture.runtime"/>
- internal class InputTestRuntime : IInputRuntime, IDisposable
- {
- public unsafe delegate long DeviceCommandCallback(int deviceId, InputDeviceCommand* command);
- public bool hasFocus => m_HasFocus;
- ~InputTestRuntime()
- {
- Dispose();
- }
- public int AllocateDeviceId()
- {
- var result = m_NextDeviceId;
- ++m_NextDeviceId;
- return result;
- }
- public unsafe void Update(InputUpdateType type)
- {
- if (!onShouldRunUpdate.Invoke(type))
- return;
- lock (m_Lock)
- {
- if (m_NewDeviceDiscoveries != null && m_NewDeviceDiscoveries.Count > 0)
- {
- if (onDeviceDiscovered != null)
- foreach (var entry in m_NewDeviceDiscoveries)
- onDeviceDiscovered(entry.Key, entry.Value);
- m_NewDeviceDiscoveries.Clear();
- }
- onBeforeUpdate?.Invoke(type);
- // Advance time *after* onBeforeUpdate so that events generated from onBeforeUpdate
- // don't get bumped into the following update.
- if (type == InputUpdateType.Dynamic && !dontAdvanceTimeNextDynamicUpdate)
- currentTime += advanceTimeEachDynamicUpdate;
- if (onUpdate != null)
- {
- var buffer = new InputEventBuffer(
- (InputEvent*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_EventBuffer),
- m_EventCount, m_EventWritePosition, m_EventBuffer.Length);
- onUpdate(type, ref buffer);
- m_EventCount = buffer.eventCount;
- m_EventWritePosition = (int)buffer.sizeInBytes;
- if (NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(buffer.data) !=
- NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_EventBuffer))
- m_EventBuffer = buffer.data;
- }
- else
- {
- m_EventCount = 0;
- m_EventWritePosition = 0;
- }
- dontAdvanceTimeNextDynamicUpdate = false;
- }
- }
- public unsafe void QueueEvent(InputEvent* eventPtr)
- {
- var eventSize = eventPtr->sizeInBytes;
- var alignedEventSize = eventSize.AlignToMultipleOf(4);
- lock (m_Lock)
- {
- eventPtr->eventId = m_NextEventId;
- eventPtr->handled = false;
- ++m_NextEventId;
- // Enlarge buffer, if we have to.
- if ((m_EventWritePosition + alignedEventSize) > m_EventBuffer.Length)
- {
- var newBufferSize = m_EventBuffer.Length + Mathf.Max((int)alignedEventSize, 1024);
- var newBuffer = new NativeArray<byte>(newBufferSize, Allocator.Persistent);
- UnsafeUtility.MemCpy(newBuffer.GetUnsafePtr(), m_EventBuffer.GetUnsafePtr(), m_EventWritePosition);
- m_EventBuffer.Dispose();
- m_EventBuffer = newBuffer;
- }
- // Copy event.
- UnsafeUtility.MemCpy((byte*)m_EventBuffer.GetUnsafePtr() + m_EventWritePosition, eventPtr, eventSize);
- m_EventWritePosition += (int)alignedEventSize;
- ++m_EventCount;
- }
- }
- public void SetDeviceCommandCallback(InputDevice device, DeviceCommandCallback callback)
- {
- SetDeviceCommandCallback(device.deviceId, callback);
- }
- public void SetDeviceCommandCallback(int deviceId, DeviceCommandCallback callback)
- {
- lock (m_Lock)
- {
- if (m_DeviceCommandCallbacks == null)
- m_DeviceCommandCallbacks = new List<KeyValuePair<int, DeviceCommandCallback>>();
- else
- {
- for (var i = 0; i < m_DeviceCommandCallbacks.Count; ++i)
- {
- if (m_DeviceCommandCallbacks[i].Key == deviceId)
- {
- m_DeviceCommandCallbacks[i] = new KeyValuePair<int, DeviceCommandCallback>(deviceId, callback);
- return;
- }
- }
- }
- m_DeviceCommandCallbacks.Add(new KeyValuePair<int, DeviceCommandCallback>(deviceId, callback));
- }
- }
- public void SetDeviceCommandCallback<TCommand>(int deviceId, TCommand result)
- where TCommand : struct, IInputDeviceCommandInfo
- {
- bool? receivedCommand = null;
- unsafe
- {
- SetDeviceCommandCallback(deviceId,
- (id, commandPtr) =>
- {
- if (commandPtr->type == result.typeStatic)
- {
- Assert.That(receivedCommand.HasValue, Is.False);
- receivedCommand = true;
- UnsafeUtility.MemCpy(commandPtr, UnsafeUtility.AddressOf(ref result),
- UnsafeUtility.SizeOf<TCommand>());
- return InputDeviceCommand.GenericSuccess;
- }
- return InputDeviceCommand.GenericFailure;
- });
- }
- }
- public unsafe long DeviceCommand(int deviceId, InputDeviceCommand* commandPtr)
- {
- lock (m_Lock)
- {
- if (commandPtr->type == QueryPairedUserAccountCommand.Type)
- {
- foreach (var pairing in userAccountPairings)
- {
- if (pairing.deviceId != deviceId)
- continue;
- var queryPairedUser = (QueryPairedUserAccountCommand*)commandPtr;
- queryPairedUser->handle = pairing.userHandle;
- queryPairedUser->name = pairing.userName;
- queryPairedUser->id = pairing.userId;
- return (long)QueryPairedUserAccountCommand.Result.DevicePairedToUserAccount;
- }
- }
- var result = InputDeviceCommand.GenericFailure;
- if (m_DeviceCommandCallbacks != null)
- foreach (var entry in m_DeviceCommandCallbacks)
- {
- if (entry.Key == deviceId)
- {
- result = entry.Value(deviceId, commandPtr);
- if (result >= 0)
- return result;
- }
- }
- return result;
- }
- }
- public void InvokePlayerFocusChanged(bool newFocusState)
- {
- m_HasFocus = newFocusState;
- onPlayerFocusChanged?.Invoke(newFocusState);
- }
- public void PlayerFocusLost()
- {
- InvokePlayerFocusChanged(false);
- }
- public void PlayerFocusGained()
- {
- InvokePlayerFocusChanged(true);
- }
- public int ReportNewInputDevice(string deviceDescriptor, int deviceId = InputDevice.InvalidDeviceId)
- {
- lock (m_Lock)
- {
- if (deviceId == InputDevice.InvalidDeviceId)
- deviceId = AllocateDeviceId();
- if (m_NewDeviceDiscoveries == null)
- m_NewDeviceDiscoveries = new List<KeyValuePair<int, string>>();
- m_NewDeviceDiscoveries.Add(new KeyValuePair<int, string>(deviceId, deviceDescriptor));
- return deviceId;
- }
- }
- public int ReportNewInputDevice(InputDeviceDescription description, int deviceId = InputDevice.InvalidDeviceId,
- ulong userHandle = 0, string userName = null, string userId = null)
- {
- deviceId = ReportNewInputDevice(description.ToJson(), deviceId);
- // If we have user information, automatically set up
- if (userHandle != 0)
- AssociateInputDeviceWithUser(deviceId, userHandle, userName, userId);
- return deviceId;
- }
- public int ReportNewInputDevice<TDevice>(int deviceId = InputDevice.InvalidDeviceId,
- ulong userHandle = 0, string userName = null, string userId = null)
- where TDevice : InputDevice
- {
- return ReportNewInputDevice(
- new InputDeviceDescription {deviceClass = typeof(TDevice).Name, interfaceName = "Test"}, deviceId,
- userHandle, userName, userId);
- }
- public unsafe void ReportInputDeviceRemoved(int deviceId)
- {
- var removeEvent = DeviceRemoveEvent.Create(deviceId);
- var removeEventPtr = UnsafeUtility.AddressOf(ref removeEvent);
- QueueEvent((InputEvent*)removeEventPtr);
- }
- public void ReportInputDeviceRemoved(InputDevice device)
- {
- if (device == null)
- throw new ArgumentNullException(nameof(device));
- ReportInputDeviceRemoved(device.deviceId);
- }
- public void AssociateInputDeviceWithUser(int deviceId, ulong userHandle, string userName = null, string userId = null)
- {
- var existingIndex = -1;
- for (var i = 0; i < userAccountPairings.Count; ++i)
- if (userAccountPairings[i].deviceId == deviceId)
- {
- existingIndex = i;
- break;
- }
- if (userHandle == 0)
- {
- if (existingIndex != -1)
- userAccountPairings.RemoveAt(existingIndex);
- }
- else if (existingIndex != -1)
- {
- userAccountPairings[existingIndex] =
- new PairedUser
- {
- deviceId = deviceId,
- userHandle = userHandle,
- userName = userName,
- userId = userId,
- };
- }
- else
- {
- userAccountPairings.Add(
- new PairedUser
- {
- deviceId = deviceId,
- userHandle = userHandle,
- userName = userName,
- userId = userId,
- });
- }
- }
- public void AssociateInputDeviceWithUser(InputDevice device, ulong userHandle, string userName = null, string userId = null)
- {
- AssociateInputDeviceWithUser(device.deviceId, userHandle, userName, userId);
- }
- public struct PairedUser
- {
- public int deviceId;
- public ulong userHandle;
- public string userName;
- public string userId;
- }
- public InputUpdateDelegate onUpdate { get; set; }
- public Action<InputUpdateType> onBeforeUpdate { get; set; }
- public Func<InputUpdateType, bool> onShouldRunUpdate { get; set; }
- public Action<int, string> onDeviceDiscovered { get; set; }
- public Action onShutdown { get; set; }
- public Action<bool> onPlayerFocusChanged { get; set; }
- public float pollingFrequency { get; set; }
- public double currentTime { get; set; }
- public double currentTimeForFixedUpdate { get; set; }
- public float unscaledGameTime { get; set; } = 1;
- public double advanceTimeEachDynamicUpdate { get; set; } = 1.0 / 60;
- public bool dontAdvanceTimeNextDynamicUpdate { get; set; }
- public bool runInBackground { get; set; } = false;
- public ScreenOrientation screenOrientation { set; get; } = ScreenOrientation.Portrait;
- public List<PairedUser> userAccountPairings
- {
- get
- {
- if (m_UserPairings == null)
- m_UserPairings = new List<PairedUser>();
- return m_UserPairings;
- }
- }
- public void Dispose()
- {
- m_EventBuffer.Dispose();
- GC.SuppressFinalize(this);
- }
- public double currentTimeOffsetToRealtimeSinceStartup
- {
- get => m_CurrentTimeOffsetToRealtimeSinceStartup;
- set
- {
- m_CurrentTimeOffsetToRealtimeSinceStartup = value;
- InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup = value;
- }
- }
- public bool isInBatchMode { get; set; }
- #if UNITY_EDITOR
- public bool isInPlayMode { get; set; } = true;
- public bool isPaused { get; set; }
- public Action<PlayModeStateChange> onPlayModeChanged { get; set; }
- public Action onProjectChange { get; set; }
- #endif
- public int eventCount => m_EventCount;
- private bool m_HasFocus = true;
- private int m_NextDeviceId = 1;
- private int m_NextEventId = 1;
- internal int m_EventCount;
- private int m_EventWritePosition;
- private NativeArray<byte> m_EventBuffer = new NativeArray<byte>(1024 * 1024, Allocator.Persistent);
- private List<PairedUser> m_UserPairings;
- private List<KeyValuePair<int, string>> m_NewDeviceDiscoveries;
- private List<KeyValuePair<int, DeviceCommandCallback>> m_DeviceCommandCallbacks;
- private object m_Lock = new object();
- private double m_CurrentTimeOffsetToRealtimeSinceStartup;
- #if UNITY_ANALYTICS || UNITY_EDITOR
- public Action<string, int, int> onRegisterAnalyticsEvent { get; set; }
- public Action<string, object> onSendAnalyticsEvent { get; set; }
- public void RegisterAnalyticsEvent(string name, int maxPerHour, int maxPropertiesPerEvent)
- {
- onRegisterAnalyticsEvent?.Invoke(name, maxPerHour, maxPropertiesPerEvent);
- }
- public void SendAnalyticsEvent(string name, object data)
- {
- onSendAnalyticsEvent?.Invoke(name, data);
- }
- #endif // UNITY_ANALYTICS || UNITY_EDITOR
- }
- }
|