test
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Keep this in sync with "Packages/com.unity.inputsystem/package.json".
|
||||
// NOTE: Unfortunately, System.Version doesn't use semantic versioning so we can't include
|
||||
// "-preview" suffixes here.
|
||||
[assembly: AssemblyVersion("1.11.2")]
|
||||
[assembly: InternalsVisibleTo("Unity.InputSystem.Tests.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.InputSystem.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.InputSystem.IntegrationTests")]
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 674b72d5417404b9eac446749e3e2d5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,216 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// Provides convenience functions for creating and managing assets for test purposes.
|
||||
/// Note that all returned paths are converted to Unix paths when running on Windows
|
||||
/// for consistency and to avoid mixed path names.
|
||||
public static class AssetDatabaseUtils
|
||||
{
|
||||
private const string kAssetPath = "Assets";
|
||||
private const string kTestPath = "TestFiles";
|
||||
private const string kMetaExtension = ".meta";
|
||||
private const string kDefaultAssetExtension = "asset";
|
||||
|
||||
// Perform an operation equivalent to a file delete operation outside of Unity Editor.
|
||||
// Note that meta file is also removed to avoid generating warnings about non-clean delete.
|
||||
public static void ExternalDeleteFileOrDirectory(string path)
|
||||
{
|
||||
FileUtil.DeleteFileOrDirectory(path);
|
||||
FileUtil.DeleteFileOrDirectory(path + kMetaExtension);
|
||||
}
|
||||
|
||||
// Perform an operation equivalent to a file move operation outside of Unity Editor.
|
||||
// Note that meta file is also moved to avoid generating warnings about non-clean move.
|
||||
public static void ExternalMoveFileOrDirectory(string source, string dest)
|
||||
{
|
||||
FileUtil.MoveFileOrDirectory(source, dest);
|
||||
FileUtil.MoveFileOrDirectory(source + kMetaExtension, dest + kMetaExtension);
|
||||
}
|
||||
|
||||
// Create an asset at the given path containing the given text content.
|
||||
private static T CreateAssetAtPath<T>(string path, string content) where T : UnityEngine.Object
|
||||
{
|
||||
Debug.Assert(!File.Exists(path));
|
||||
|
||||
T obj;
|
||||
try
|
||||
{
|
||||
CreateDirectories(Path.GetDirectoryName(path));
|
||||
|
||||
File.WriteAllText(path, content);
|
||||
AssetDatabase.ImportAsset(path);
|
||||
obj = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
if (obj == null)
|
||||
throw new Exception($"Failed to create asset at \"{path}\"");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
throw;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static string SanitizePath(string path)
|
||||
{
|
||||
return path?.Replace("\\", "/");
|
||||
}
|
||||
|
||||
private static void CreateRootDirectory()
|
||||
{
|
||||
CreateDirectories(RootPath());
|
||||
}
|
||||
|
||||
// Creates all directories (including intermediate) defined in path.
|
||||
private static string CreateDirectories(string path)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
return SanitizePath(path);
|
||||
|
||||
var parentFolder = kAssetPath;
|
||||
path = path.Replace("\\", "/"); // Make sure we only get '/' separators.
|
||||
var directories = path.Split('/');
|
||||
if (directories[0] != kAssetPath)
|
||||
throw new ArgumentException(path);
|
||||
for (var i = 1; i < directories.Length; ++i)
|
||||
{
|
||||
var guid = AssetDatabase.CreateFolder(parentFolder, directories[i]);
|
||||
if (guid == string.Empty)
|
||||
throw new Exception("Failed to create path \"" + path + "\"");
|
||||
parentFolder = SanitizePath(Path.Combine(parentFolder, directories[i]));
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
return SanitizePath(path);
|
||||
}
|
||||
|
||||
// Creates a random test directory within asset folder that is automatically removed after test run.
|
||||
public static string CreateDirectory()
|
||||
{
|
||||
return CreateDirectories(RandomDirectoryPath());
|
||||
}
|
||||
|
||||
// Creates an asset in the given directory path with an explicit or random file name containing the
|
||||
// given content or the default content based on type.
|
||||
public static T CreateAsset<T>(string directoryPath, string filename = null, string content = null) where T : UnityEngine.Object
|
||||
{
|
||||
Debug.Assert(directoryPath == null || directoryPath.Contains(RootPath()));
|
||||
Debug.Assert(filename == null || !filename.Contains("/"));
|
||||
|
||||
if (directoryPath == null)
|
||||
directoryPath = RootPath();
|
||||
string path;
|
||||
if (filename != null)
|
||||
{
|
||||
path = SanitizePath(Path.Combine(directoryPath, filename));
|
||||
if (File.Exists(path))
|
||||
throw new Exception($"File already exists: {path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
path = RandomAssetFilePath(directoryPath, AssetFileExtensionFromType(typeof(T)));
|
||||
}
|
||||
|
||||
return CreateAsset<T>(path: path, content: content);
|
||||
}
|
||||
|
||||
// Creates an asset at the given path containing the specified content.
|
||||
// If path is null, a unique random file name is assigned, if content is null the default content based
|
||||
// on type (extension) is used.
|
||||
public static T CreateAsset<T>(string path = null, string content = null) where T : UnityEngine.Object
|
||||
{
|
||||
if (path == null)
|
||||
path = RandomAssetFilePath(RootPath(), AssetFileExtensionFromType(typeof(T)));
|
||||
if (content == null)
|
||||
content = DefaultContentFromType(typeof(T));
|
||||
return CreateAssetAtPath<T>(path, content);
|
||||
}
|
||||
|
||||
public static void Restore()
|
||||
{
|
||||
var root = RootPath();
|
||||
|
||||
// Delete all files in test folder
|
||||
if (!Directory.Exists(root))
|
||||
return;
|
||||
|
||||
foreach (var asset in AssetDatabase.FindAssets("", new string[] { root }))
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(asset);
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
}
|
||||
|
||||
AssetDatabase.DeleteAsset(root);
|
||||
}
|
||||
|
||||
private static string RandomName()
|
||||
{
|
||||
const double scale = int.MaxValue;
|
||||
double r = UnityEngine.Random.value;
|
||||
return "Test_" + (int)(Math.Floor(r * scale));
|
||||
}
|
||||
|
||||
private static string RandomAssetFilePath<T>(string directoryPath = null)
|
||||
{
|
||||
return RandomAssetFilePath(directoryPath, AssetFileExtensionFromType(typeof(T)));
|
||||
}
|
||||
|
||||
private static string RandomAssetFilePath(string directoryPath = null, string extension = null)
|
||||
{
|
||||
// Default to using test files root path
|
||||
if (directoryPath == null)
|
||||
directoryPath = RootPath();
|
||||
|
||||
// Default to default extension
|
||||
if (extension == null)
|
||||
extension = kDefaultAssetExtension;
|
||||
|
||||
string path;
|
||||
do
|
||||
{
|
||||
path = SanitizePath(Path.Combine(directoryPath, RandomName() + "." + extension)); // EDIT
|
||||
}
|
||||
while (File.Exists(path));
|
||||
return path;
|
||||
}
|
||||
|
||||
private static string RootPath()
|
||||
{
|
||||
return SanitizePath(Path.Combine(kAssetPath, kTestPath));
|
||||
}
|
||||
|
||||
public static string RandomDirectoryPath()
|
||||
{
|
||||
string path;
|
||||
do
|
||||
{
|
||||
path = Path.Combine(RootPath(), RandomName());
|
||||
}
|
||||
while (File.Exists(path));
|
||||
return SanitizePath(path);
|
||||
}
|
||||
|
||||
private static string AssetFileExtensionFromType(Type type)
|
||||
{
|
||||
if (type == typeof(InputActionAsset))
|
||||
return InputActionAsset.Extension;
|
||||
return kDefaultAssetExtension;
|
||||
}
|
||||
|
||||
private static string DefaultContentFromType(Type type)
|
||||
{
|
||||
if (type == typeof(InputActionAsset))
|
||||
return "{}";
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1bc613b5fab4dd4997810700b46dbf7
|
||||
timeCreated: 1708676790
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81128cc364d234170845119461c41970
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,489 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine.Analytics;
|
||||
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);
|
||||
|
||||
~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 (type == InputUpdateType.Dynamic && !dontAdvanceUnscaledGameTimeNextDynamicUpdate)
|
||||
{
|
||||
unscaledGameTime += 1 / 30f;
|
||||
dontAdvanceUnscaledGameTimeNextDynamicUpdate = false;
|
||||
}
|
||||
|
||||
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;
|
||||
dontAdvanceTimeNextDynamicUpdate = false;
|
||||
}
|
||||
|
||||
if (onUpdate != null)
|
||||
{
|
||||
var buffer = new InputEventBuffer(
|
||||
(InputEvent*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(m_EventBuffer),
|
||||
m_EventCount, m_EventWritePosition, m_EventBuffer.Length);
|
||||
|
||||
try
|
||||
{
|
||||
onUpdate(type, ref buffer);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Same order as in NativeInputRuntime
|
||||
Debug.LogException(e);
|
||||
Debug.LogError($"{e.GetType().Name} during event processing of {type} update; resetting event buffer");
|
||||
|
||||
// Rethrow exception for test runtime to enable us to assert against it in tests.
|
||||
m_EventCount = 0;
|
||||
m_EventWritePosition = 0;
|
||||
throw;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 unsafe void SetCanRunInBackground(int deviceId)
|
||||
{
|
||||
SetDeviceCommandCallback(deviceId,
|
||||
(id, command) =>
|
||||
{
|
||||
if (command->type == QueryCanRunInBackground.Type)
|
||||
{
|
||||
((QueryCanRunInBackground*)command)->canRunInBackground = true;
|
||||
return InputDeviceCommand.GenericSuccess;
|
||||
}
|
||||
return InputDeviceCommand.GenericFailure;
|
||||
});
|
||||
}
|
||||
|
||||
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; }
|
||||
#if UNITY_EDITOR
|
||||
public Action onPlayerLoopInitialization { get; set; }
|
||||
#endif
|
||||
public Action<int, string> onDeviceDiscovered { get; set; }
|
||||
public Action onShutdown { get; set; }
|
||||
public Action<bool> onPlayerFocusChanged { get; set; }
|
||||
public bool isPlayerFocused => m_HasFocus;
|
||||
public float pollingFrequency { get; set; }
|
||||
public double currentTime { get; set; }
|
||||
public double currentTimeForFixedUpdate { get; set; }
|
||||
public float unscaledGameTime { get; set; } = 1;
|
||||
public bool dontAdvanceUnscaledGameTimeNextDynamicUpdate { get; set; }
|
||||
|
||||
public double advanceTimeEachDynamicUpdate { get; set; } = 1.0 / 60;
|
||||
|
||||
public bool dontAdvanceTimeNextDynamicUpdate { get; set; }
|
||||
|
||||
public bool runInBackground { get; set; } = false;
|
||||
|
||||
public Vector2 screenSize { get; set; } = new Vector2(1024, 768);
|
||||
public ScreenOrientation screenOrientation { set; get; } = ScreenOrientation.Portrait;
|
||||
public bool normalizeScrollWheelDelta { get; set; } = true;
|
||||
public float scrollWheelDeltaPerTick { get; set; } = 1.0f;
|
||||
|
||||
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 bool isEditorActive { get; set; } = true;
|
||||
public Func<IntPtr, bool> onUnityRemoteMessage
|
||||
{
|
||||
get => m_UnityRemoteMessageHandler;
|
||||
set => m_UnityRemoteMessageHandler = value;
|
||||
}
|
||||
|
||||
public bool? unityRemoteGyroEnabled;
|
||||
public float? unityRemoteGyroUpdateInterval;
|
||||
|
||||
public void SetUnityRemoteGyroEnabled(bool value)
|
||||
{
|
||||
unityRemoteGyroEnabled = value;
|
||||
}
|
||||
|
||||
public void SetUnityRemoteGyroUpdateInterval(float interval)
|
||||
{
|
||||
unityRemoteGyroUpdateInterval = interval;
|
||||
}
|
||||
|
||||
public Action<PlayModeStateChange> onPlayModeChanged { get; set; }
|
||||
public Action onProjectChange { get; set; }
|
||||
#endif
|
||||
|
||||
public int eventCount => m_EventCount;
|
||||
|
||||
internal const int kDefaultEventBufferSize = 1024 * 512;
|
||||
|
||||
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>(kDefaultEventBufferSize, 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;
|
||||
private Func<IntPtr, bool> m_UnityRemoteMessageHandler;
|
||||
|
||||
#if UNITY_ANALYTICS || UNITY_EDITOR
|
||||
|
||||
public Action<string, int, int> onRegisterAnalyticsEvent { get; set; }
|
||||
public Action<string, object> onSendAnalyticsEvent { get; set; }
|
||||
|
||||
public void SendAnalytic(InputAnalytics.IInputAnalytic analytic)
|
||||
{
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
|
||||
// Mimic editor analytics for Unity 2023.2+ invoking TryGatherData to send
|
||||
var analyticInfoAttribute = analytic.GetType().GetCustomAttributes(
|
||||
typeof(AnalyticInfoAttribute), true).FirstOrDefault() as AnalyticInfoAttribute;
|
||||
var info = analytic.info;
|
||||
#if UNITY_EDITOR
|
||||
// Registration handled by framework
|
||||
#else
|
||||
onRegisterAnalyticsEvent?.Invoke(info.Name, info.MaxEventsPerHour, info.MaxNumberOfElements); // only to avoid writing two tests per Unity version (registration handled by framework)
|
||||
#endif
|
||||
if (analytic.TryGatherData(out var data, out var ex) && data != null && analyticInfoAttribute != null)
|
||||
onSendAnalyticsEvent?.Invoke(analyticInfoAttribute.eventName, data);
|
||||
else if (ex != null)
|
||||
throw ex; // rethrow for visibility in test scope
|
||||
|
||||
#else
|
||||
|
||||
var info = analytic.info;
|
||||
onRegisterAnalyticsEvent?.Invoke(info.Name, info.MaxEventsPerHour, info.MaxNumberOfElements);
|
||||
|
||||
if (analytic.TryGatherData(out var data, out var error))
|
||||
onSendAnalyticsEvent?.Invoke(info.Name, data);
|
||||
else
|
||||
throw error; // For visibility in tests
|
||||
|
||||
#endif // UNITY_2023_2_OR_NEWER
|
||||
}
|
||||
|
||||
#endif // UNITY_ANALYTICS || UNITY_EDITOR
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 073f4378d59364199ae19d0486842726
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
// Utility allowing access to object T as well as a dispose delegate to clean-up any resources associated with it.
|
||||
// Useful with the 'using' keyword to provide scoped RAII-like cleanup of objects in tests without having a
|
||||
// dedicated fixture handling the clean-up.
|
||||
internal sealed class ScopedDisposable<T> : IDisposable
|
||||
where T : UnityEngine.Object
|
||||
{
|
||||
private Action<T> m_Dispose;
|
||||
|
||||
public ScopedDisposable(T obj, Action<T> dispose)
|
||||
{
|
||||
value = obj;
|
||||
m_Dispose = dispose;
|
||||
}
|
||||
|
||||
public T value
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Dispose == null)
|
||||
return;
|
||||
if (value != null)
|
||||
m_Dispose(value);
|
||||
m_Dispose = null;
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience API for scoped objects.
|
||||
internal static class Scoped
|
||||
{
|
||||
public static ScopedDisposable<T> Object<T>(T obj) where T : UnityEngine.Object
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return new ScopedDisposable<T>(obj, UnityEngine.Object.DestroyImmediate);
|
||||
#else
|
||||
return new ScopedDisposable<T>(obj, UnityEngine.Object.Destroy);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static ScopedDisposable<T> Asset<T>(T obj) where T : UnityEngine.Object
|
||||
{
|
||||
return new ScopedDisposable<T>(obj, (o) => AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(o)));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cd4530e3c9047dea0f1cc17dccbf6ec
|
||||
timeCreated: 1708678710
|
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "Unity.InputSystem.TestFramework",
|
||||
"references": [
|
||||
"Unity.InputSystem",
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": ["UNITY_TESTS_FRAMEWORK"],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.test-framework",
|
||||
"expression": "",
|
||||
"define": "UNITY_TESTS_FRAMEWORK"
|
||||
}
|
||||
],
|
||||
"versionDefines": []
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc04f38471c3a459fb4d31124ee9127d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,492 @@
|
||||
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// Used to send fake input events to Windows via user32.dll.
|
||||
/// </summary>
|
||||
internal static class WinUserInput
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint SendInput(
|
||||
uint nInputs,
|
||||
[MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs,
|
||||
int cbSize);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct INPUT
|
||||
{
|
||||
public InputType type;
|
||||
public InputUnion U;
|
||||
public static int Size
|
||||
{
|
||||
get { return Marshal.SizeOf(typeof(INPUT)); }
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct InputUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public MOUSEINPUT mi;
|
||||
[FieldOffset(0)]
|
||||
public KEYBDINPUT ki;
|
||||
[FieldOffset(0)]
|
||||
public HARDWAREINPUT hi;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MOUSEINPUT
|
||||
{
|
||||
public int dx;
|
||||
public int dy;
|
||||
public MouseEventDataXButtons mouseData;
|
||||
public MOUSEEVENTF dwFlags;
|
||||
public uint time;
|
||||
public UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
public enum InputType : uint
|
||||
{
|
||||
INPUT_MOUSE,
|
||||
INPUT_KEYBOARD,
|
||||
INPUT_HARDWARE
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MouseEventDataXButtons : uint
|
||||
{
|
||||
Nothing = 0x00000000,
|
||||
XBUTTON1 = 0x00000001,
|
||||
XBUTTON2 = 0x00000002
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MOUSEEVENTF : uint
|
||||
{
|
||||
ABSOLUTE = 0x8000,
|
||||
HWHEEL = 0x01000,
|
||||
MOVE = 0x0001,
|
||||
MOVE_NOCOALESCE = 0x2000,
|
||||
LEFTDOWN = 0x0002,
|
||||
LEFTUP = 0x0004,
|
||||
RIGHTDOWN = 0x0008,
|
||||
RIGHTUP = 0x0010,
|
||||
MIDDLEDOWN = 0x0020,
|
||||
MIDDLEUP = 0x0040,
|
||||
VIRTUALDESK = 0x4000,
|
||||
WHEEL = 0x0800,
|
||||
XDOWN = 0x0080,
|
||||
XUP = 0x0100
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KEYBDINPUT
|
||||
{
|
||||
public VirtualKeyShort wVk;
|
||||
public ScanCodeShort wScan;
|
||||
public KEYEVENTF dwFlags;
|
||||
public int time;
|
||||
public UIntPtr dwExtraInfo;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum KEYEVENTF : uint
|
||||
{
|
||||
EXTENDEDKEY = 0x0001,
|
||||
KEYUP = 0x0002,
|
||||
SCANCODE = 0x0008,
|
||||
UNICODE = 0x0004
|
||||
}
|
||||
|
||||
public enum VirtualKeyShort : short
|
||||
{
|
||||
LBUTTON = 0x01,
|
||||
RBUTTON = 0x02,
|
||||
CANCEL = 0x03,
|
||||
MBUTTON = 0x04,
|
||||
XBUTTON1 = 0x05,
|
||||
XBUTTON2 = 0x06,
|
||||
BACK = 0x08,
|
||||
TAB = 0x09,
|
||||
CLEAR = 0x0C,
|
||||
RETURN = 0x0D,
|
||||
SHIFT = 0x10,
|
||||
CONTROL = 0x11,
|
||||
MENU = 0x12,
|
||||
PAUSE = 0x13,
|
||||
CAPITAL = 0x14,
|
||||
KANA = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 0x20,
|
||||
PRIOR = 0x21,
|
||||
NEXT = 0x22,
|
||||
END = 0x23,
|
||||
HOME = 0x24,
|
||||
LEFT = 0x25,
|
||||
UP = 0x26,
|
||||
RIGHT = 0x27,
|
||||
DOWN = 0x28,
|
||||
SELECT = 0x29,
|
||||
PRINT = 0x2A,
|
||||
EXECUTE = 0x2B,
|
||||
SNAPSHOT = 0x2C,
|
||||
INSERT = 0x2D,
|
||||
DELETE = 0x2E,
|
||||
HELP = 0x2F,
|
||||
KEY_0 = 0x30,
|
||||
KEY_1 = 0x31,
|
||||
KEY_2 = 0x32,
|
||||
KEY_3 = 0x33,
|
||||
KEY_4 = 0x34,
|
||||
KEY_5 = 0x35,
|
||||
KEY_6 = 0x36,
|
||||
KEY_7 = 0x37,
|
||||
KEY_8 = 0x38,
|
||||
KEY_9 = 0x39,
|
||||
KEY_A = 0x41,
|
||||
KEY_B = 0x42,
|
||||
KEY_C = 0x43,
|
||||
KEY_D = 0x44,
|
||||
KEY_E = 0x45,
|
||||
KEY_F = 0x46,
|
||||
KEY_G = 0x47,
|
||||
KEY_H = 0x48,
|
||||
KEY_I = 0x49,
|
||||
KEY_J = 0x4A,
|
||||
KEY_K = 0x4B,
|
||||
KEY_L = 0x4C,
|
||||
KEY_M = 0x4D,
|
||||
KEY_N = 0x4E,
|
||||
KEY_O = 0x4F,
|
||||
KEY_P = 0x50,
|
||||
KEY_Q = 0x51,
|
||||
KEY_R = 0x52,
|
||||
KEY_S = 0x53,
|
||||
KEY_T = 0x54,
|
||||
KEY_U = 0x55,
|
||||
KEY_V = 0x56,
|
||||
KEY_W = 0x57,
|
||||
KEY_X = 0x58,
|
||||
KEY_Y = 0x59,
|
||||
KEY_Z = 0x5A,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
BROWSER_BACK = 0xA6,
|
||||
BROWSER_FORWARD = 0xA7,
|
||||
BROWSER_REFRESH = 0xA8,
|
||||
BROWSER_STOP = 0xA9,
|
||||
BROWSER_SEARCH = 0xAA,
|
||||
BROWSER_FAVORITES = 0xAB,
|
||||
BROWSER_HOME = 0xAC,
|
||||
VOLUME_MUTE = 0xAD,
|
||||
VOLUME_DOWN = 0xAE,
|
||||
VOLUME_UP = 0xAF,
|
||||
MEDIA_NEXT_TRACK = 0xB0,
|
||||
MEDIA_PREV_TRACK = 0xB1,
|
||||
MEDIA_STOP = 0xB2,
|
||||
MEDIA_PLAY_PAUSE = 0xB3,
|
||||
LAUNCH_MAIL = 0xB4,
|
||||
LAUNCH_MEDIA_SELECT = 0xB5,
|
||||
LAUNCH_APP1 = 0xB6,
|
||||
LAUNCH_APP2 = 0xB7,
|
||||
OEM_1 = 0xBA,
|
||||
OEM_PLUS = 0xBB,
|
||||
OEM_COMMA = 0xBC,
|
||||
OEM_MINUS = 0xBD,
|
||||
OEM_PERIOD = 0xBE,
|
||||
OEM_2 = 0xBF,
|
||||
OEM_3 = 0xC0,
|
||||
OEM_4 = 0xDB,
|
||||
OEM_5 = 0xDC,
|
||||
OEM_6 = 0xDD,
|
||||
OEM_7 = 0xDE,
|
||||
OEM_8 = 0xDF,
|
||||
OEM_102 = 0xE2,
|
||||
PROCESSKEY = 0xE5,
|
||||
PACKET = 0xE7,
|
||||
ATTN = 0xF6,
|
||||
CRSEL = 0xF7,
|
||||
EXSEL = 0xF8,
|
||||
EREOF = 0xF9,
|
||||
PLAY = 0xFA,
|
||||
ZOOM = 0xFB,
|
||||
NONAME = 0xFC,
|
||||
PA1 = 0xFD,
|
||||
OEM_CLEAR = 0xFE
|
||||
}
|
||||
|
||||
public enum ScanCodeShort : short
|
||||
{
|
||||
LBUTTON = 0,
|
||||
RBUTTON = 0,
|
||||
CANCEL = 70,
|
||||
MBUTTON = 0,
|
||||
XBUTTON1 = 0,
|
||||
XBUTTON2 = 0,
|
||||
BACK = 14,
|
||||
TAB = 15,
|
||||
CLEAR = 76,
|
||||
RETURN = 28,
|
||||
SHIFT = 42,
|
||||
CONTROL = 29,
|
||||
MENU = 56,
|
||||
PAUSE = 0,
|
||||
CAPITAL = 58,
|
||||
KANA = 0,
|
||||
HANGUL = 0,
|
||||
JUNJA = 0,
|
||||
FINAL = 0,
|
||||
HANJA = 0,
|
||||
KANJI = 0,
|
||||
ESCAPE = 1,
|
||||
CONVERT = 0,
|
||||
NONCONVERT = 0,
|
||||
ACCEPT = 0,
|
||||
MODECHANGE = 0,
|
||||
SPACE = 57,
|
||||
PRIOR = 73,
|
||||
NEXT = 81,
|
||||
END = 79,
|
||||
HOME = 71,
|
||||
LEFT = 75,
|
||||
UP = 72,
|
||||
RIGHT = 77,
|
||||
DOWN = 80,
|
||||
SELECT = 0,
|
||||
PRINT = 0,
|
||||
EXECUTE = 0,
|
||||
SNAPSHOT = 84,
|
||||
INSERT = 82,
|
||||
DELETE = 83,
|
||||
HELP = 99,
|
||||
KEY_0 = 11,
|
||||
KEY_1 = 2,
|
||||
KEY_2 = 3,
|
||||
KEY_3 = 4,
|
||||
KEY_4 = 5,
|
||||
KEY_5 = 6,
|
||||
KEY_6 = 7,
|
||||
KEY_7 = 8,
|
||||
KEY_8 = 9,
|
||||
KEY_9 = 10,
|
||||
KEY_A = 30,
|
||||
KEY_B = 48,
|
||||
KEY_C = 46,
|
||||
KEY_D = 32,
|
||||
KEY_E = 18,
|
||||
KEY_F = 33,
|
||||
KEY_G = 34,
|
||||
KEY_H = 35,
|
||||
KEY_I = 23,
|
||||
KEY_J = 36,
|
||||
KEY_K = 37,
|
||||
KEY_L = 38,
|
||||
KEY_M = 50,
|
||||
KEY_N = 49,
|
||||
KEY_O = 24,
|
||||
KEY_P = 25,
|
||||
KEY_Q = 16,
|
||||
KEY_R = 19,
|
||||
KEY_S = 31,
|
||||
KEY_T = 20,
|
||||
KEY_U = 22,
|
||||
KEY_V = 47,
|
||||
KEY_W = 17,
|
||||
KEY_X = 45,
|
||||
KEY_Y = 21,
|
||||
KEY_Z = 44,
|
||||
LWIN = 91,
|
||||
RWIN = 92,
|
||||
APPS = 93,
|
||||
SLEEP = 95,
|
||||
NUMPAD0 = 82,
|
||||
NUMPAD1 = 79,
|
||||
NUMPAD2 = 80,
|
||||
NUMPAD3 = 81,
|
||||
NUMPAD4 = 75,
|
||||
NUMPAD5 = 76,
|
||||
NUMPAD6 = 77,
|
||||
NUMPAD7 = 71,
|
||||
NUMPAD8 = 72,
|
||||
NUMPAD9 = 73,
|
||||
MULTIPLY = 55,
|
||||
ADD = 78,
|
||||
SEPARATOR = 0,
|
||||
SUBTRACT = 74,
|
||||
DECIMAL = 83,
|
||||
DIVIDE = 53,
|
||||
F1 = 59,
|
||||
F2 = 60,
|
||||
F3 = 61,
|
||||
F4 = 62,
|
||||
F5 = 63,
|
||||
F6 = 64,
|
||||
F7 = 65,
|
||||
F8 = 66,
|
||||
F9 = 67,
|
||||
F10 = 68,
|
||||
F11 = 87,
|
||||
F12 = 88,
|
||||
F13 = 100,
|
||||
F14 = 101,
|
||||
F15 = 102,
|
||||
F16 = 103,
|
||||
F17 = 104,
|
||||
F18 = 105,
|
||||
F19 = 106,
|
||||
F20 = 107,
|
||||
F21 = 108,
|
||||
F22 = 109,
|
||||
F23 = 110,
|
||||
F24 = 118,
|
||||
NUMLOCK = 69,
|
||||
SCROLL = 70,
|
||||
LSHIFT = 42,
|
||||
RSHIFT = 54,
|
||||
LCONTROL = 29,
|
||||
RCONTROL = 29,
|
||||
LMENU = 56,
|
||||
RMENU = 56,
|
||||
BROWSER_BACK = 106,
|
||||
BROWSER_FORWARD = 105,
|
||||
BROWSER_REFRESH = 103,
|
||||
BROWSER_STOP = 104,
|
||||
BROWSER_SEARCH = 101,
|
||||
BROWSER_FAVORITES = 102,
|
||||
BROWSER_HOME = 50,
|
||||
VOLUME_MUTE = 32,
|
||||
VOLUME_DOWN = 46,
|
||||
VOLUME_UP = 48,
|
||||
MEDIA_NEXT_TRACK = 25,
|
||||
MEDIA_PREV_TRACK = 16,
|
||||
MEDIA_STOP = 36,
|
||||
MEDIA_PLAY_PAUSE = 34,
|
||||
LAUNCH_MAIL = 108,
|
||||
LAUNCH_MEDIA_SELECT = 109,
|
||||
LAUNCH_APP1 = 107,
|
||||
LAUNCH_APP2 = 33,
|
||||
OEM_1 = 39,
|
||||
OEM_PLUS = 13,
|
||||
OEM_COMMA = 51,
|
||||
OEM_MINUS = 12,
|
||||
OEM_PERIOD = 52,
|
||||
OEM_2 = 53,
|
||||
OEM_3 = 41,
|
||||
OEM_4 = 26,
|
||||
OEM_5 = 43,
|
||||
OEM_6 = 27,
|
||||
OEM_7 = 40,
|
||||
OEM_8 = 0,
|
||||
OEM_102 = 86,
|
||||
PROCESSKEY = 0,
|
||||
PACKET = 0,
|
||||
ATTN = 0,
|
||||
CRSEL = 0,
|
||||
EXSEL = 0,
|
||||
EREOF = 93,
|
||||
PLAY = 0,
|
||||
ZOOM = 98,
|
||||
NONAME = 0,
|
||||
PA1 = 0,
|
||||
OEM_CLEAR = 0,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HARDWAREINPUT
|
||||
{
|
||||
public int uMsg;
|
||||
public short wParamL;
|
||||
public short wParamH;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate an event sent over RDP.
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
public static void SendRDPMouseMoveEvent(int x, int y)
|
||||
{
|
||||
var mouseInput = new INPUT { type = InputType.INPUT_MOUSE };
|
||||
mouseInput.U.mi.dx = x;
|
||||
mouseInput.U.mi.dy = y;
|
||||
mouseInput.U.mi.dwFlags = MOUSEEVENTF.ABSOLUTE | MOUSEEVENTF.VIRTUALDESK | MOUSEEVENTF.MOVE;
|
||||
SendInput(1, new INPUT[] { mouseInput }, INPUT.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate a normal mouse move event with relative coordinates.
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
public static void SendMouseMoveEvent(int x, int y)
|
||||
{
|
||||
var mouseInput = new INPUT { type = InputType.INPUT_MOUSE };
|
||||
mouseInput.U.mi.dx = x;
|
||||
mouseInput.U.mi.dy = y;
|
||||
mouseInput.U.mi.dwFlags = MOUSEEVENTF.MOVE;
|
||||
SendInput(1, new INPUT[] { mouseInput }, INPUT.Size);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2c8138bd42a9a041bb4b1398ae819d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user