first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84ba8e63f115487ca072084bbebf0e55
|
||||
timeCreated: 1517014866
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to tell the runtime to no longer send events for the given device.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct DisableDeviceCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('D', 'S', 'B', 'L'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static DisableDeviceCommand Create()
|
||||
{
|
||||
return new DisableDeviceCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aad56faafe6d9d3458c93a9e43759461
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to re-enable a device that has been disabled with <see cref="DisableDeviceCommand"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct EnableDeviceCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('E', 'N', 'B', 'L'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static EnableDeviceCommand Create()
|
||||
{
|
||||
return new EnableDeviceCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d76c8b08c287594c9bf5d6c70fb8699
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Device Command that enables IME Composition within the application. Primarily handled by Keyboard devices.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = InputDeviceCommand.kBaseCommandSize + sizeof(byte))]
|
||||
public unsafe struct EnableIMECompositionCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('I', 'M', 'E', 'M'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + +sizeof(uint);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true, and if true, Input Method Editors will be used while typing.
|
||||
/// </summary>
|
||||
public bool imeEnabled
|
||||
{
|
||||
get { return m_ImeEnabled != 0; }
|
||||
}
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
byte m_ImeEnabled;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static EnableIMECompositionCommand Create(bool enabled)
|
||||
{
|
||||
return new EnableIMECompositionCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, InputDeviceCommand.kBaseCommandSize + sizeof(byte)),
|
||||
m_ImeEnabled = enabled ? byte.MaxValue : (byte)0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e0ea5a9f932f6541962b1bf7934f7c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface implemented by all input device command structs which reports the data format identifier of the command.
|
||||
/// </summary>
|
||||
public interface IInputDeviceCommandInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The data format identifier of the device command as a <see cref="FourCC"/> code.
|
||||
/// </summary>
|
||||
FourCC typeStatic { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2339479d3a44fcb902e62c60f0a05af
|
||||
timeCreated: 1517015727
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Device command to instruct the underlying platform to pair a user account to the targeted device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// If successful, the platform should then send an <see cref="DeviceConfigurationEvent"/>
|
||||
/// to signal that the device configuration has been changed. In response, a <see cref="QueryUserIdCommand"/>
|
||||
/// may be sent to fetch the paired user ID from the device.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct InitiateUserAccountPairingCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('P', 'A', 'I', 'R'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Enum values mandated by native code")]
|
||||
public enum Result
|
||||
{
|
||||
/// <summary>
|
||||
/// User pairing UI has been successfully opened.
|
||||
/// </summary>
|
||||
SuccessfullyInitiated = 1,
|
||||
|
||||
/// <summary>
|
||||
/// System does not support application-invoked user pairing.
|
||||
/// </summary>
|
||||
ErrorNotSupported = (int)InputDeviceCommand.GenericFailure,
|
||||
|
||||
/// <summary>
|
||||
/// There already is a pairing operation in progress and the system does not support
|
||||
/// pairing multiple devices at the same time.
|
||||
/// </summary>
|
||||
ErrorAlreadyInProgress = -2,
|
||||
}
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static InitiateUserAccountPairingCommand Create()
|
||||
{
|
||||
return new InitiateUserAccountPairingCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b200cbcb815aa44418fe8ada8cb78696
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
////REVIEW: why is this passing the command by pointer instead of by ref?
|
||||
/// <summary>
|
||||
/// Delegate used by <see cref="InputSystem.onDeviceCommand"/>.
|
||||
/// </summary>
|
||||
public unsafe delegate long? InputDeviceCommandDelegate(InputDevice device, InputDeviceCommand* command);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for executing <see cref="InputDeviceCommand"/>s inside <see cref="InputSystem.onFindLayoutForDevice"/>.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to execute.</param>
|
||||
/// <seealso cref="InputSystem.onFindLayoutForDevice"/>
|
||||
/// <seealso cref="Layouts.InputDeviceFindControlLayoutDelegate"/>
|
||||
public delegate long InputDeviceExecuteCommandDelegate(ref InputDeviceCommand command);
|
||||
|
||||
/// <summary>
|
||||
/// Data header for a command send to an <see cref="InputDevice"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Commands are essentially synchronously processed events send directly
|
||||
/// to a specific device. Their primary use is to expose device-specific
|
||||
/// functions without having to extend the C# API used to communicate
|
||||
/// between input code and backend device implementations (which may sit
|
||||
/// in native code).
|
||||
///
|
||||
/// Like input events, device commands use <see cref="FourCC"/> codes
|
||||
/// to indicate their type.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kBaseCommandSize)]
|
||||
public struct InputDeviceCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
////TODO: Remove kBaseCommandSize
|
||||
internal const int kBaseCommandSize = 8;
|
||||
public const int BaseCommandSize = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Generic failure code for <see cref="InputDevice.ExecuteCommand{TCommand}"/> calls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Any negative return value for an <see cref="InputDevice.ExecuteCommand{TCommand}"/> call should be considered failure.
|
||||
/// </remarks>
|
||||
public const long GenericFailure = -1;
|
||||
|
||||
public const long GenericSuccess = 1;
|
||||
|
||||
[FieldOffset(0)] public FourCC type;
|
||||
[FieldOffset(4)] public int sizeInBytes;
|
||||
|
||||
public int payloadSizeInBytes => sizeInBytes - kBaseCommandSize;
|
||||
|
||||
public unsafe void* payloadPtr
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed(void* thisPtr = &this)
|
||||
{
|
||||
return ((byte*)thisPtr) + kBaseCommandSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InputDeviceCommand(FourCC type, int sizeInBytes = kBaseCommandSize)
|
||||
{
|
||||
this.type = type;
|
||||
this.sizeInBytes = sizeInBytes;
|
||||
}
|
||||
|
||||
public static unsafe NativeArray<byte> AllocateNative(FourCC type, int payloadSize)
|
||||
{
|
||||
var sizeInBytes = payloadSize + kBaseCommandSize;
|
||||
var buffer = new NativeArray<byte>(sizeInBytes, Allocator.Temp);
|
||||
|
||||
var commandPtr = (InputDeviceCommand*)NativeArrayUnsafeUtility.GetUnsafePtr(buffer);
|
||||
commandPtr->type = type;
|
||||
commandPtr->sizeInBytes = sizeInBytes;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return new FourCC(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2158c2dfcbb94a3092caa77d698d5010
|
||||
timeCreated: 1517014875
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Queries to see if this device is able to continue to send updates and state changes when the application is not if focus.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputDevice.canRunInBackground"/>
|
||||
[StructLayout(LayoutKind.Explicit, Size = InputDeviceCommand.kBaseCommandSize + sizeof(bool))]
|
||||
public struct QueryCanRunInBackground : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('Q', 'R', 'I', 'B');
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(bool);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public bool canRunInBackground;
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryCanRunInBackground Create()
|
||||
{
|
||||
return new QueryCanRunInBackground
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
canRunInBackground = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a10a64abdfd8f6340a5b6b54bf28812a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Query dimensions of a device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is usually used to query screen dimensions from pointer devices.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct QueryDimensionsCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('D', 'I', 'M', 'S'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float) * 2;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public Vector2 outDimensions;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static QueryDimensionsCommand Create()
|
||||
{
|
||||
return new QueryDimensionsCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8ce1fd8f845226439f6b4ee991c9f78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////REVIEW: This mechanism sucks. We should have this conversion without the device having to support it through an IOCTL. A Pointer
|
||||
//// should just inherently have this conversion mechanism on its controls that operate in screen space.
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
internal struct QueryEditorWindowCoordinatesCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('E', 'W', 'P', 'S');
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float) * 2;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public Vector2 inOutCoordinates;
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryEditorWindowCoordinatesCommand Create(Vector2 playerWindowCoordinates)
|
||||
{
|
||||
return new QueryEditorWindowCoordinatesCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
inOutCoordinates = playerWindowCoordinates
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c764fcc1188a6e489f8c305ff4077a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to find out whether a device is currently enabled or not.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct QueryEnabledStateCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('Q', 'E', 'N', 'B');
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(bool);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public bool isEnabled;
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryEnabledStateCommand Create()
|
||||
{
|
||||
return new QueryEnabledStateCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e6d0cbdd012f434392878fdc293f974
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to query the current name of a key according to the current keyboard layout.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public unsafe struct QueryKeyNameCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('K', 'Y', 'C', 'F');
|
||||
|
||||
internal const int kMaxNameLength = 256;
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + kMaxNameLength + 4;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public int scanOrKeyCode;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 4)]
|
||||
public fixed byte nameBuffer[kMaxNameLength];
|
||||
|
||||
public string ReadKeyName()
|
||||
{
|
||||
fixed(QueryKeyNameCommand * thisPtr = &this)
|
||||
{
|
||||
return StringHelpers.ReadStringFromBuffer(new IntPtr(thisPtr->nameBuffer), kMaxNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryKeyNameCommand Create(Key key)
|
||||
{
|
||||
return new QueryKeyNameCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
scanOrKeyCode = (int)key
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e49477f346c4f3a8c9e5fcdcde317ed
|
||||
timeCreated: 1517018273
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Command to query the name of the current keyboard layout from a device.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = InputDeviceCommand.kBaseCommandSize + kMaxNameLength)]
|
||||
public unsafe struct QueryKeyboardLayoutCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('K', 'B', 'L', 'T'); } }
|
||||
|
||||
internal const int kMaxNameLength = 256;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public fixed byte nameBuffer[kMaxNameLength];
|
||||
|
||||
/// <summary>
|
||||
/// Read the current keyboard layout name from <see cref="nameBuffer"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ReadLayoutName()
|
||||
{
|
||||
fixed(QueryKeyboardLayoutCommand * thisPtr = &this)
|
||||
return StringHelpers.ReadStringFromBuffer(new IntPtr(thisPtr->nameBuffer), kMaxNameLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string to <see cref="nameBuffer"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">Keyboard layout name.</param>
|
||||
public void WriteLayoutName(string name)
|
||||
{
|
||||
fixed(QueryKeyboardLayoutCommand * thisPtr = &this)
|
||||
StringHelpers.WriteStringToBuffer(name, new IntPtr(thisPtr->nameBuffer), kMaxNameLength);
|
||||
}
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryKeyboardLayoutCommand Create()
|
||||
{
|
||||
return new QueryKeyboardLayoutCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, InputDeviceCommand.kBaseCommandSize + kMaxNameLength)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 620180a2562c422893989c28645cef24
|
||||
timeCreated: 1517017122
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Query the ID and the name of the user paired to the device the command is sent to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This command is only supported on platforms where devices can be paired to user accounts
|
||||
/// at the platform level. Currently this is the case for Xbox and PS4. On Switch, <see
|
||||
/// cref="InitiateUserAccountPairingCommand"/> is supported but the platform does not store
|
||||
/// associations established between devices and users that way.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public unsafe struct QueryPairedUserAccountCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('P', 'A', 'C', 'C');
|
||||
|
||||
internal const int kMaxNameLength = 256;
|
||||
internal const int kMaxIdLength = 256;
|
||||
|
||||
////REVIEW: is this too heavy to allocate on the stack?
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + 8 + kMaxNameLength * 2 + kMaxIdLength * 2;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames", Justification = "`Result` matches other command result names")]
|
||||
[Flags]
|
||||
public enum Result : long
|
||||
{
|
||||
// Leave bit #0 unused so as to not lead to possible confusion with GenericSuccess.
|
||||
|
||||
/// <summary>
|
||||
/// The device is currently paired to a user account.
|
||||
/// </summary>
|
||||
DevicePairedToUserAccount = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// The system is currently displaying a prompt for the user to select an account to
|
||||
/// use the device with.
|
||||
/// </summary>
|
||||
UserAccountSelectionInProgress = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// User account selection completed.
|
||||
/// </summary>
|
||||
UserAccountSelectionComplete = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// The system had been displaying a prompt
|
||||
/// </summary>
|
||||
UserAccountSelectionCanceled = 1 << 4,
|
||||
}
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
/// <summary>
|
||||
/// Handle of the user account at the platform level.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this is wide enough to store a pointer and does not necessarily need to be a plain integer.
|
||||
/// How the backend determines handles for user accounts is up to the backend.
|
||||
///
|
||||
/// Be aware that a handle is not guaranteed to be valid beyond the current application run. For stable,
|
||||
/// persistent user account handles,use <see cref="id"/>.
|
||||
/// </remarks>
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public ulong handle;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 8)]
|
||||
internal fixed byte nameBuffer[kMaxNameLength * 2];
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 8 + kMaxNameLength * 2)]
|
||||
internal fixed byte idBuffer[kMaxNameLength * 2];
|
||||
|
||||
/// <summary>
|
||||
/// Persistent ID of the user account the platform level.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This ID is guaranteed to not change between application runs, device restarts, and the user
|
||||
/// changing user names on the account.
|
||||
///
|
||||
/// Use this ID to associate persistent settings with.
|
||||
/// </remarks>
|
||||
public string id
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed(byte* idBufferPtr = idBuffer)
|
||||
return StringHelpers.ReadStringFromBuffer(new IntPtr(idBufferPtr), kMaxIdLength);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
var length = value.Length;
|
||||
if (length > kMaxIdLength)
|
||||
throw new ArgumentException($"ID '{value}' exceeds maximum supported length of {kMaxIdLength} characters", nameof(value));
|
||||
|
||||
fixed(byte* idBufferPtr = idBuffer)
|
||||
{
|
||||
StringHelpers.WriteStringToBuffer(value, new IntPtr(idBufferPtr), kMaxIdLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the user account at the platform level.
|
||||
/// </summary>
|
||||
public string name
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed(byte* nameBufferPtr = nameBuffer)
|
||||
return StringHelpers.ReadStringFromBuffer(new IntPtr(nameBufferPtr), kMaxNameLength);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
var length = value.Length;
|
||||
if (length > kMaxNameLength)
|
||||
throw new ArgumentException($"Name '{value}' exceeds maximum supported length of {kMaxNameLength} characters", nameof(value));
|
||||
|
||||
fixed(byte* nameBufferPtr = nameBuffer)
|
||||
{
|
||||
StringHelpers.WriteStringToBuffer(value, new IntPtr(nameBufferPtr), kMaxNameLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static QueryPairedUserAccountCommand Create()
|
||||
{
|
||||
return new QueryPairedUserAccountCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c6284fecb53e48ee832bc14c64be6fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
internal struct QuerySamplingFrequencyCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('S', 'M', 'P', 'L'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public float frequency;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static QuerySamplingFrequencyCommand Create()
|
||||
{
|
||||
return new QuerySamplingFrequencyCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1659bf437d849824999b2b9ae9e1518b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: remove this one; superseded by QueryPairedUserAccountCommand
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
internal unsafe struct QueryUserIdCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('U', 'S', 'E', 'R'); } }
|
||||
|
||||
public const int kMaxIdLength = 256;
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + kMaxIdLength * 2;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public fixed byte idBuffer[kMaxIdLength * 2];
|
||||
|
||||
public string ReadId()
|
||||
{
|
||||
fixed(QueryUserIdCommand * thisPtr = &this)
|
||||
{
|
||||
return StringHelpers.ReadStringFromBuffer(new IntPtr(thisPtr->idBuffer), kMaxIdLength);
|
||||
}
|
||||
}
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static QueryUserIdCommand Create()
|
||||
{
|
||||
return new QueryUserIdCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c784a1510a154625903484b84c8ddaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// A command to tell the runtime to reset the device to it's default state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This triggers an event being sent from the device that represents an empty, or untouched device.
|
||||
/// </remarks>
|
||||
/// <seealso cref="RequestSyncCommand"/>
|
||||
[StructLayout(LayoutKind.Explicit, Size = InputDeviceCommand.kBaseCommandSize)]
|
||||
public struct RequestResetCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('R', 'S', 'E', 'T');
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static RequestResetCommand Create()
|
||||
{
|
||||
return new RequestResetCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90a43988da5cf85498b40180b9040a38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// A command to tell the runtime to sync the device to it's last known state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This triggers an event from the underlying device that represents the whole, current state.
|
||||
/// </remarks>
|
||||
/// <seealso cref="RequestResetCommand"/>
|
||||
[StructLayout(LayoutKind.Explicit, Size = InputDeviceCommand.kBaseCommandSize)]
|
||||
public struct RequestSyncCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type => new FourCC('S', 'Y', 'N', 'C');
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
public FourCC typeStatic => Type;
|
||||
|
||||
public static RequestSyncCommand Create()
|
||||
{
|
||||
return new RequestSyncCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0c637405fa4842cc92e389df8729bc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the position for IME dialogs. This is in pixels, from the upper left corner going down and to the right.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public unsafe struct SetIMECursorPositionCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('I', 'M', 'E', 'P'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + (sizeof(float) * 2);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
public Vector2 position
|
||||
{
|
||||
get { return m_Position; }
|
||||
}
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
Vector2 m_Position;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static SetIMECursorPositionCommand Create(Vector2 cursorPosition)
|
||||
{
|
||||
return new SetIMECursorPositionCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
m_Position = cursorPosition
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6873ac51fc9d6dc4dbb6d9dc77a5eb49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////REVIEW: switch this to interval-in-seconds instead of Hz?
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// For a device that is sampled periodically, set the frequency at which the device
|
||||
/// is sampled.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
public struct SetSamplingFrequencyCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('S', 'S', 'P', 'L'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float);
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public float frequency;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static SetSamplingFrequencyCommand Create(float frequency)
|
||||
{
|
||||
return new SetSamplingFrequencyCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
frequency = frequency
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3894aabfd6ab70245859fa873e1a6552
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
internal struct WarpMousePositionCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('W', 'P', 'M', 'S'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float) * 2;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public Vector2 warpPositionInPlayerDisplaySpace;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static WarpMousePositionCommand Create(Vector2 position)
|
||||
{
|
||||
return new WarpMousePositionCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
warpPositionInPlayerDisplaySpace = position
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f018dacefe574045a76769f5f355f3b1
|
||||
timeCreated: 1517107079
|
||||
@@ -0,0 +1,809 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Haptics;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////TODO: come up with consistent naming for buttons; (xxxButton? xxx?)
|
||||
|
||||
////REVIEW: should we add a gyro as a standard feature of gamepads?
|
||||
|
||||
////TODO: allow to be used for mouse simulation
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Default state layout for gamepads.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Be aware that unlike some other devices such as <see cref="Mouse"/> or <see cref="Touchscreen"/>,
|
||||
/// gamepad devices tend to have wildly varying state formats, i.e. forms in which they internally
|
||||
/// store their input data. In practice, even on the same platform gamepads will often store
|
||||
/// their data in different formats. This means that <see cref="GamepadState"/> will often <em>not</em>
|
||||
/// be the format in which a particular gamepad (such as <see cref="XInput.XInputController"/>,
|
||||
/// for example) stores its data.
|
||||
///
|
||||
/// If your gamepad data is arriving in a different format, you should extend the "Gamepad" layout and customize its Controls.
|
||||
///
|
||||
/// A real-world example of this is the Xbox Controller on macOS, which is supported through HID. Its layout looks like this:
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// {
|
||||
/// "name" : "XboxGamepadOSX",
|
||||
/// "extend" : "Gamepad",
|
||||
/// "format" : "HID",
|
||||
/// "device" : { "interface" : "HID", "product" : "Xbox.*Controller" },
|
||||
/// "controls" : [
|
||||
/// { "name" : "leftShoulder", "offset" : 2, "bit" : 8 },
|
||||
/// { "name" : "rightShoulder", "offset" : 2, "bit" : 9 },
|
||||
/// { "name" : "leftStickPress", "offset" : 2, "bit" : 14 },
|
||||
/// { "name" : "rightStickPress", "offset" : 2, "bit" : 15 },
|
||||
/// { "name" : "buttonSouth", "offset" : 2, "bit" : 12 },
|
||||
/// { "name" : "buttonEast", "offset" : 2, "bit" : 13 },
|
||||
/// { "name" : "buttonWest", "offset" : 2, "bit" : 14 },
|
||||
/// { "name" : "buttonNorth", "offset" : 2, "bit" : 15 },
|
||||
/// { "name" : "dpad", "offset" : 2 },
|
||||
/// { "name" : "dpad/up", "offset" : 0, "bit" : 8 },
|
||||
/// { "name" : "dpad/down", "offset" : 0, "bit" : 9 },
|
||||
/// { "name" : "dpad/left", "offset" : 0, "bit" : 10 },
|
||||
/// { "name" : "dpad/right", "offset" : 0, "bit" : 11 },
|
||||
/// { "name" : "start", "offset" : 2, "bit" : 4 },
|
||||
/// { "name" : "select", "offset" : 2, "bit" : 5 },
|
||||
/// { "name" : "xbox", "offset" : 2, "bit" : 2, "layout" : "Button" },
|
||||
/// { "name" : "leftTrigger", "offset" : 4, "format" : "BYTE" },
|
||||
/// { "name" : "rightTrigger", "offset" : 5, "format" : "BYTE" },
|
||||
/// { "name" : "leftStick", "offset" : 6, "format" : "VC2S" },
|
||||
/// { "name" : "leftStick/x", "offset" : 0, "format" : "SHRT", "parameters" : "normalize,normalizeMin=-0.5,normalizeMax=0.5" },
|
||||
/// { "name" : "leftStick/y", "offset" : 2, "format" : "SHRT", "parameters" : "invert,normalize,normalizeMin=-0.5,normalizeMax=0.5" },
|
||||
/// { "name" : "rightStick", "offset" : 10, "format" : "VC2S" },
|
||||
/// { "name" : "rightStick/x", "offset" : 0, "format" : "SHRT", "parameters" : "normalize,normalizeMin=-0.5,normalizeMax=0.5" },
|
||||
/// { "name" : "rightStick/y", "offset" : 2, "format" : "SHRT", "parameters" : "invert,normalize,normalizeMin=-0.5,normalizeMax=0.5" }
|
||||
/// ]
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// The same principle applies if some buttons on your Device are swapped, for example. In this case, you can remap their offsets.
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
/// <seealso cref="Gamepad"/>
|
||||
// NOTE: Must match GamepadInputState in native.
|
||||
[StructLayout(LayoutKind.Explicit, Size = 28)]
|
||||
public struct GamepadState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC Format => new FourCC('G', 'P', 'A', 'D');
|
||||
|
||||
// On Sony consoles, we use the platform defaults as the gamepad-wide short default names.
|
||||
#if UNITY_PS4 || UNITY_PS5
|
||||
internal const string ButtonSouthShortDisplayName = "Cross";
|
||||
internal const string ButtonNorthShortDisplayName = "Triangle";
|
||||
internal const string ButtonWestShortDisplayName = "Square";
|
||||
internal const string ButtonEastShortDisplayName = "Circle";
|
||||
#elif UNITY_SWITCH
|
||||
internal const string ButtonSouthShortDisplayName = "B";
|
||||
internal const string ButtonNorthShortDisplayName = "X";
|
||||
internal const string ButtonWestShortDisplayName = "Y";
|
||||
internal const string ButtonEastShortDisplayName = "A";
|
||||
#else
|
||||
internal const string ButtonSouthShortDisplayName = "A";
|
||||
internal const string ButtonNorthShortDisplayName = "Y";
|
||||
internal const string ButtonWestShortDisplayName = "X";
|
||||
internal const string ButtonEastShortDisplayName = "B";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Button bit mask.
|
||||
/// </summary>
|
||||
/// <seealso cref="GamepadButton"/>
|
||||
/// <seealso cref="Gamepad.buttonSouth"/>
|
||||
/// <seealso cref="Gamepad.buttonNorth"/>
|
||||
/// <seealso cref="Gamepad.buttonWest"/>
|
||||
/// <seealso cref="Gamepad.buttonSouth"/>
|
||||
/// <seealso cref="Gamepad.leftShoulder"/>
|
||||
/// <seealso cref="Gamepad.rightShoulder"/>
|
||||
/// <seealso cref="Gamepad.startButton"/>
|
||||
/// <seealso cref="Gamepad.selectButton"/>
|
||||
/// <seealso cref="Gamepad.leftStickButton"/>
|
||||
/// <seealso cref="Gamepad.rightStickButton"/>
|
||||
////REVIEW: do we want the name to correspond to what's actually on the device?
|
||||
[InputControl(name = "dpad", layout = "Dpad", usage = "Hatswitch", displayName = "D-Pad", format = "BIT", sizeInBits = 4, bit = 0)]
|
||||
[InputControl(name = "buttonSouth", layout = "Button", bit = (uint)GamepadButton.South, usages = new[] { "PrimaryAction", "Submit" }, aliases = new[] { "a", "cross" }, displayName = "Button South", shortDisplayName = ButtonSouthShortDisplayName)]
|
||||
[InputControl(name = "buttonWest", layout = "Button", bit = (uint)GamepadButton.West, usage = "SecondaryAction", aliases = new[] { "x", "square" }, displayName = "Button West", shortDisplayName = ButtonWestShortDisplayName)]
|
||||
[InputControl(name = "buttonNorth", layout = "Button", bit = (uint)GamepadButton.North, aliases = new[] { "y", "triangle" }, displayName = "Button North", shortDisplayName = ButtonNorthShortDisplayName)]
|
||||
[InputControl(name = "buttonEast", layout = "Button", bit = (uint)GamepadButton.East, usages = new[] { "Back", "Cancel" }, aliases = new[] { "b", "circle" }, displayName = "Button East", shortDisplayName = ButtonEastShortDisplayName)]
|
||||
////FIXME: 'Press' naming is inconsistent with 'Button' naming
|
||||
[InputControl(name = "leftStickPress", layout = "Button", bit = (uint)GamepadButton.LeftStick, displayName = "Left Stick Press")]
|
||||
[InputControl(name = "rightStickPress", layout = "Button", bit = (uint)GamepadButton.RightStick, displayName = "Right Stick Press")]
|
||||
[InputControl(name = "leftShoulder", layout = "Button", bit = (uint)GamepadButton.LeftShoulder, displayName = "Left Shoulder", shortDisplayName = "LB")]
|
||||
[InputControl(name = "rightShoulder", layout = "Button", bit = (uint)GamepadButton.RightShoulder, displayName = "Right Shoulder", shortDisplayName = "RB")]
|
||||
////REVIEW: seems like these two should get less ambiguous names as well
|
||||
[InputControl(name = "start", layout = "Button", bit = (uint)GamepadButton.Start, usage = "Menu", displayName = "Start")]
|
||||
[InputControl(name = "select", layout = "Button", bit = (uint)GamepadButton.Select, displayName = "Select")]
|
||||
[FieldOffset(0)]
|
||||
public uint buttons;
|
||||
|
||||
/// <summary>
|
||||
/// A 2D vector representing the current position of the left stick on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>Each axis of the 2D vector's range goes from -1 to 1. 0 represents the stick in its center position, and -1 or 1 represents the the stick pushed to its extent in each direction along the axis.</remarks>
|
||||
/// <seealso cref="Gamepad.leftStick"/>
|
||||
[InputControl(layout = "Stick", usage = "Primary2DMotion", processors = "stickDeadzone", displayName = "Left Stick", shortDisplayName = "LS")]
|
||||
[FieldOffset(4)]
|
||||
public Vector2 leftStick;
|
||||
|
||||
/// <summary>
|
||||
/// A 2D vector representing the current position of the right stick on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>Each axis of the 2D vector's range goes from -1 to 1.
|
||||
/// 0 represents the stick in its center position.
|
||||
/// -1 or 1 represents the stick pushed to its extent in each direction along the axis.</remarks>
|
||||
/// <seealso cref="Gamepad.rightStick"/>
|
||||
[InputControl(layout = "Stick", usage = "Secondary2DMotion", processors = "stickDeadzone", displayName = "Right Stick", shortDisplayName = "RS")]
|
||||
[FieldOffset(12)]
|
||||
public Vector2 rightStick;
|
||||
|
||||
////REVIEW: should left and right trigger get deadzones?
|
||||
|
||||
/// <summary>
|
||||
/// The current position of the left trigger on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>The value's range goes from 0 to 1.
|
||||
/// 0 represents the trigger in its neutral position.
|
||||
/// 1 represents the trigger in its fully pressed position.</remarks>
|
||||
/// <seealso cref="Gamepad.leftTrigger"/>
|
||||
[InputControl(layout = "Button", format = "FLT", usage = "SecondaryTrigger", displayName = "Left Trigger", shortDisplayName = "LT")]
|
||||
[FieldOffset(20)]
|
||||
public float leftTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// The current position of the right trigger on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>The value's range goes from 0 to 1.
|
||||
/// 0 represents the trigger in its neutral position.
|
||||
/// 1 represents the trigger in its fully pressed position.</remarks>
|
||||
/// <seealso cref="Gamepad.rightTrigger"/>
|
||||
[InputControl(layout = "Button", format = "FLT", usage = "SecondaryTrigger", displayName = "Right Trigger", shortDisplayName = "RT")]
|
||||
[FieldOffset(24)]
|
||||
public float rightTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// State format tag for GamepadState.
|
||||
/// </summary>
|
||||
/// <remarks> Holds the format tag for GamepadState ("GPAD")</remarks>
|
||||
public FourCC format => Format;
|
||||
|
||||
/// <summary>
|
||||
/// Create a gamepad state with the given buttons being pressed.
|
||||
/// </summary>
|
||||
/// <param name="buttons">Buttons to put into pressed state.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="buttons"/> is <c>null</c>.</exception>
|
||||
public GamepadState(params GamepadButton[] buttons)
|
||||
: this()
|
||||
{
|
||||
if (buttons == null)
|
||||
throw new ArgumentNullException(nameof(buttons));
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask");
|
||||
var bit = 1U << (int)button;
|
||||
this.buttons |= bit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the specific buttons to be pressed or unpressed.
|
||||
/// </summary>
|
||||
/// <param name="button">A gamepad button.</param>
|
||||
/// <param name="value">Whether to set <paramref name="button"/> to be pressed or not pressed in
|
||||
/// <see cref="buttons"/>.</param>
|
||||
/// <returns>GamepadState with a modified <see cref="buttons"/> mask.</returns>
|
||||
public GamepadState WithButton(GamepadButton button, bool value = true)
|
||||
{
|
||||
Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask");
|
||||
var bit = 1U << (int)button;
|
||||
if (value)
|
||||
buttons |= bit;
|
||||
else
|
||||
buttons &= ~bit;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
////NOTE: The bit positions here based on the enum value are also used in native.
|
||||
/// <summary>
|
||||
/// Enum of common gamepad buttons.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be used as an array indexer on the <see cref="Gamepad"/> class to get individual button controls.
|
||||
/// </remarks>
|
||||
public enum GamepadButton
|
||||
{
|
||||
// Dpad buttons. Important to be first in the bitfield as we'll
|
||||
// point the DpadControl to it.
|
||||
// IMPORTANT: Order has to match what is expected by DpadControl.
|
||||
|
||||
/// <summary>
|
||||
/// The up button on a gamepad's dpad.
|
||||
/// </summary>
|
||||
DpadUp = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The down button on a gamepad's dpad.
|
||||
/// </summary>
|
||||
DpadDown = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The left button on a gamepad's dpad.
|
||||
/// </summary>
|
||||
DpadLeft = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The right button on a gamepad's dpad.
|
||||
/// </summary>
|
||||
DpadRight = 3,
|
||||
|
||||
// Face buttons. We go with a north/south/east/west naming as that
|
||||
// clearly disambiguates where we expect the respective button to be.
|
||||
|
||||
/// <summary>
|
||||
/// The upper action button on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="Y"/> and <see cref="Triangle"/> which are the Xbox and PlayStation controller names for this button.
|
||||
/// </remarks>
|
||||
North = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The right action button on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="B"/> and <see cref="Circle"/> which are the Xbox and PlayStation controller names for this button.
|
||||
/// </remarks>
|
||||
East = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The lower action button on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="A"/> and <see cref="Cross"/> which are the Xbox and PlayStation controller names for this button.
|
||||
/// </remarks>
|
||||
South = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The left action button on a gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="X"/> and <see cref="Square"/> which are the Xbox and PlayStation controller names for this button.
|
||||
/// </remarks>
|
||||
West = 7,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The button pressed by pressing down the left stick on a gamepad.
|
||||
/// </summary>
|
||||
LeftStick = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The button pressed by pressing down the right stick on a gamepad.
|
||||
/// </summary>
|
||||
RightStick = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The left shoulder button on a gamepad.
|
||||
/// </summary>
|
||||
LeftShoulder = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The right shoulder button on a gamepad.
|
||||
/// </summary>
|
||||
RightShoulder = 11,
|
||||
|
||||
/// <summary>
|
||||
/// The start button.
|
||||
/// </summary>
|
||||
Start = 12,
|
||||
|
||||
/// <summary>
|
||||
/// The select button.
|
||||
/// </summary>
|
||||
Select = 13,
|
||||
|
||||
// For values that are not part of the buttons bitmask in GamepadState, assign large values that are outside
|
||||
// the 32bit bit range.
|
||||
|
||||
/// <summary>
|
||||
/// The left trigger button on a gamepad.
|
||||
/// </summary>
|
||||
LeftTrigger = 32,
|
||||
|
||||
/// <summary>
|
||||
/// The right trigger button on a gamepad.
|
||||
/// </summary>
|
||||
RightTrigger = 33,
|
||||
|
||||
/// <summary>
|
||||
/// The X button on an Xbox controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="West"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
X = West,
|
||||
/// <summary>
|
||||
/// The Y button on an Xbox controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="North"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
Y = North,
|
||||
/// <summary>
|
||||
/// The A button on an Xbox controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="South"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
A = South,
|
||||
/// <summary>
|
||||
/// The B button on an Xbox controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="East"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
B = East,
|
||||
|
||||
/// <summary>
|
||||
/// The cross button on a PlayStation controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="South"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
Cross = South,
|
||||
/// <summary>
|
||||
/// The square button on a PlayStation controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="West"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
Square = West,
|
||||
/// <summary>
|
||||
/// The triangle button on a PlayStation controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="North"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
Triangle = North,
|
||||
/// <summary>
|
||||
/// The circle button on a PlayStation controller.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Identical to <see cref="East"/>, which is the generic name of this button.
|
||||
/// </remarks>
|
||||
Circle = East,
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// An Xbox-style gamepad with two sticks, a D-Pad, four face buttons, two triggers,
|
||||
/// two shoulder buttons, and two menu buttons that usually sit in the midsection of the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Gamepad layout provides a standardized layouts for gamepads. Generally, if a specific
|
||||
/// device is represented as a Gamepad, the controls, such as the face buttons, are guaranteed
|
||||
/// to be mapped correctly and consistently. If, based on the set of supported devices available
|
||||
/// to the input system, this cannot be guaranteed, a given device is usually represented as a
|
||||
/// generic <see cref="Joystick"/> or as just a plain <see cref="HID.HID"/> instead.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code source="../../DocCodeSamples.Tests/GamepadExample.cs" />
|
||||
/// </example>
|
||||
/// <seealso cref="all"/>
|
||||
/// <seealso cref="current"/>
|
||||
/// <seealso cref="GamepadState"/>
|
||||
/// <seealso cref="InputDevice"/>
|
||||
/// <seealso cref="SetMotorSpeeds"/>
|
||||
/// <seealso cref="ButtonControl.wasPressedThisFrame"/>
|
||||
[InputControlLayout(stateType = typeof(GamepadState), isGenericTypeOfDevice = true)]
|
||||
public class Gamepad : InputDevice, IDualMotorRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// The left face button of the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the X/Square face button.
|
||||
/// On an Xbox controller, this is the <see cref="xButton"/> and on the PS4 controller, this is the
|
||||
/// <see cref="squareButton"/>.
|
||||
/// </remarks>
|
||||
public ButtonControl buttonWest { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The top face button of the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the Y/Triangle face button.
|
||||
/// On an Xbox controller, this is the <see cref="yButton"/> and on the PS4 controller, this is the
|
||||
/// <see cref="triangleButton"/>.
|
||||
/// </remarks>
|
||||
public ButtonControl buttonNorth { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The bottom face button of the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the A/Cross face button.
|
||||
/// On an Xbox controller, this is the <see cref="aButton"/> and on the PS4 controller, this is the
|
||||
/// <see cref="crossButton"/>.
|
||||
/// </remarks>
|
||||
public ButtonControl buttonSouth { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The right face button of the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the B/Circle face button.
|
||||
/// On an Xbox controller, this is the <see cref="bButton"/> and on the PS4 controller, this is the
|
||||
/// <see cref="circleButton"/>.
|
||||
/// </remarks>
|
||||
public ButtonControl buttonEast { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button that gets triggered when <see cref="leftStick"/> is pressed down.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing a click with the left stick.</remarks>
|
||||
public ButtonControl leftStickButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button that gets triggered when <see cref="rightStick"/> is pressed down.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing a click with the right stick.</remarks>
|
||||
public ButtonControl rightStickButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The right button in the middle section of the gamepad (called "menu" on Xbox
|
||||
/// controllers and "options" on PS4 controllers).
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the right button in midsection.</remarks>
|
||||
public ButtonControl startButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The left button in the middle section of the gamepad (called "view" on Xbox
|
||||
/// controllers and "share" on PS4 controllers).
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the left button in midsection.</remarks>
|
||||
public ButtonControl selectButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The 4-way directional pad on the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the d-pad.</remarks>
|
||||
public DpadControl dpad { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The left shoulder/bumper button that sits on top of <see cref="leftTrigger"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the left shoulder button.
|
||||
/// On Xbox controllers, this is usually called "left bumper" whereas on PS4
|
||||
/// controllers, this button is referred to as "L1".
|
||||
/// </remarks>
|
||||
public ButtonControl leftShoulder { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The right shoulder/bumper button that sits on top of <see cref="rightTrigger"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Control representing the right shoulder button.
|
||||
/// On Xbox controllers, this is usually called "right bumper" whereas on PS4
|
||||
/// controllers, this button is referred to as "R1".
|
||||
/// </remarks>
|
||||
public ButtonControl rightShoulder { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The left thumbstick on the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the left thumbstick.</remarks>
|
||||
public StickControl leftStick { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The right thumbstick on the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the right thumbstick.</remarks>
|
||||
public StickControl rightStick { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The left trigger button sitting below <see cref="leftShoulder"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the left trigger button.
|
||||
/// On PS4 controllers, this button is referred to as "L2".
|
||||
/// </remarks>
|
||||
public ButtonControl leftTrigger { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The right trigger button sitting below <see cref="rightShoulder"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Control representing the right trigger button.
|
||||
/// On PS4 controllers, this button is referred to as "R2".
|
||||
/// </remarks>
|
||||
public ButtonControl rightTrigger { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonSouth"/>. Xbox-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl aButton => buttonSouth;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonEast"/>. Xbox-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl bButton => buttonEast;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonWest"/> Xbox-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl xButton => buttonWest;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonNorth"/>. Xbox-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl yButton => buttonNorth;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonNorth"/>. PS4-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl triangleButton => buttonNorth;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonWest"/>. PS4-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl squareButton => buttonWest;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonEast"/>. PS4-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl circleButton => buttonEast;
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="buttonSouth"/>. PS4-style alias.
|
||||
/// </summary>
|
||||
public ButtonControl crossButton => buttonSouth;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a gamepad button by its <see cref="GamepadButton"/> enumeration
|
||||
/// constant.
|
||||
/// </summary>
|
||||
/// <param name="button">Button to retrieve.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="button"/> is not a valid gamepad
|
||||
/// button value.</exception>
|
||||
public ButtonControl this[GamepadButton button]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case GamepadButton.North: return buttonNorth;
|
||||
case GamepadButton.South: return buttonSouth;
|
||||
case GamepadButton.East: return buttonEast;
|
||||
case GamepadButton.West: return buttonWest;
|
||||
case GamepadButton.Start: return startButton;
|
||||
case GamepadButton.Select: return selectButton;
|
||||
case GamepadButton.LeftShoulder: return leftShoulder;
|
||||
case GamepadButton.RightShoulder: return rightShoulder;
|
||||
case GamepadButton.LeftTrigger: return leftTrigger;
|
||||
case GamepadButton.RightTrigger: return rightTrigger;
|
||||
case GamepadButton.LeftStick: return leftStickButton;
|
||||
case GamepadButton.RightStick: return rightStickButton;
|
||||
case GamepadButton.DpadUp: return dpad.up;
|
||||
case GamepadButton.DpadDown: return dpad.down;
|
||||
case GamepadButton.DpadLeft: return dpad.left;
|
||||
case GamepadButton.DpadRight: return dpad.right;
|
||||
default:
|
||||
throw new InvalidEnumArgumentException(nameof(button), (int)button, typeof(GamepadButton));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The gamepad last used/connected by the player or <c>null</c> if there is no gamepad connected
|
||||
/// to the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When added, a device is automatically made current (see <see cref="InputDevice.MakeCurrent"/>), so
|
||||
/// when connecting a gamepad, it will also become current. After that, it will only become current again
|
||||
/// when input change on non-noisy controls (see <see cref="InputControl.noisy"/>) is received. It will also
|
||||
/// be available once <see cref="all"/> is queried.
|
||||
///
|
||||
/// For local multiplayer scenarios (or whenever there are multiple gamepads that need to be usable
|
||||
/// in a concurrent fashion), it is not recommended to rely on this property. Instead, it is recommended
|
||||
/// to use <see cref="PlayerInput"/> or <see cref="Users.InputUser"/>.
|
||||
/// </remarks>
|
||||
public static Gamepad current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of gamepads currently connected to the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns all currently connected gamepads.
|
||||
///
|
||||
/// Does not cause GC allocation.
|
||||
///
|
||||
/// Do <em>not</em> hold on to the value returned by this getter but rather query it whenever
|
||||
/// you need it. Whenever the gamepad setup changes, the value returned by this getter
|
||||
/// is invalidated.
|
||||
///
|
||||
/// Alternately, for querying a single gamepad, you can use <see cref="current"/> for example.
|
||||
/// </remarks>
|
||||
public new static ReadOnlyArray<Gamepad> all => new ReadOnlyArray<Gamepad>(s_Gamepads, 0, s_GamepadCount);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
////REVIEW: what's actually faster/better... storing these in properties or doing the lookup on the fly?
|
||||
buttonWest = GetChildControl<ButtonControl>("buttonWest");
|
||||
buttonNorth = GetChildControl<ButtonControl>("buttonNorth");
|
||||
buttonSouth = GetChildControl<ButtonControl>("buttonSouth");
|
||||
buttonEast = GetChildControl<ButtonControl>("buttonEast");
|
||||
|
||||
startButton = GetChildControl<ButtonControl>("start");
|
||||
selectButton = GetChildControl<ButtonControl>("select");
|
||||
|
||||
leftStickButton = GetChildControl<ButtonControl>("leftStickPress");
|
||||
rightStickButton = GetChildControl<ButtonControl>("rightStickPress");
|
||||
|
||||
dpad = GetChildControl<DpadControl>("dpad");
|
||||
|
||||
leftShoulder = GetChildControl<ButtonControl>("leftShoulder");
|
||||
rightShoulder = GetChildControl<ButtonControl>("rightShoulder");
|
||||
|
||||
leftStick = GetChildControl<StickControl>("leftStick");
|
||||
rightStick = GetChildControl<StickControl>("rightStick");
|
||||
|
||||
leftTrigger = GetChildControl<ButtonControl>("leftTrigger");
|
||||
rightTrigger = GetChildControl<ButtonControl>("rightTrigger");
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the gamepad the <see cref="current"/> gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called automatically by the system when there is input on a gamepad.
|
||||
///
|
||||
/// More remarks are available in <see cref="InputDevice.MakeCurrent()"/> when it comes to devices with
|
||||
/// <see cref="InputControl.noisy"/> controls.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using System;
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem;
|
||||
///
|
||||
/// public class MakeCurrentGamepadExample : MonoBehaviour
|
||||
/// {
|
||||
/// void Update()
|
||||
/// {
|
||||
/// /// Make the first Gamepad always the current one
|
||||
/// if (Gamepad.all.Count > 0)
|
||||
/// {
|
||||
/// Gamepad.all[0].MakeCurrent();
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="InputDevice.OnAdded"/>
|
||||
/// <summary>
|
||||
/// Called when a gamepad is added to the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Override this method if you want to do additional processing when a gamepad becomes connected. After this method is called, the gamepad is automatically added to the list of <see cref="all"/> gamepads.
|
||||
/// </remarks>
|
||||
protected override void OnAdded()
|
||||
{
|
||||
ArrayHelpers.AppendWithCapacity(ref s_Gamepads, ref s_GamepadCount, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="InputDevice.OnRemoved"/>
|
||||
/// <summary>
|
||||
/// Called when the gamepad is removed from the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Override this method if you want to do additional processing when a gamepad becomes disconnected. After this method is called, the gamepad is automatically removed from the list of <see cref="all"/> gamepads.
|
||||
/// </remarks>
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
if (current == this)
|
||||
current = null;
|
||||
|
||||
// Remove from `all`.
|
||||
var index = ArrayHelpers.IndexOfReference(s_Gamepads, this, s_GamepadCount);
|
||||
if (index != -1)
|
||||
ArrayHelpers.EraseAtWithCapacity(s_Gamepads, ref s_GamepadCount, index);
|
||||
else
|
||||
{
|
||||
Debug.Assert(false,
|
||||
$"Gamepad {this} seems to not have been added but is being removed (gamepad list: {string.Join(", ", all)})"); // Put in else to not allocate on normal path.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause rumble effects on the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It will pause rumble effects and save the current motor speeds.
|
||||
/// Resume from those speeds with <see cref="ResumeHaptics"/>.
|
||||
/// Some devices such as <see cref="DualShock.DualSenseGamepadHID"/> and
|
||||
/// <see cref="DualShock.DualShock4GamepadHID"/> can also set the LED color when this method is called.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IDualMotorRumble"/>
|
||||
/// <example>
|
||||
/// <code source="../../DocCodeSamples.Tests/GamepadHapticsExample.cs"/>
|
||||
/// </example>
|
||||
public virtual void PauseHaptics()
|
||||
{
|
||||
m_Rumble.PauseHaptics(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume rumble effects on the gamepad.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It will resume rumble effects from the previously set motor speeds, such as motor speeds saved when
|
||||
/// calling <see cref="PauseHaptics"/>.
|
||||
/// Some devices such as <see cref="DualShock.DualSenseGamepadHID"/> and
|
||||
/// <see cref="DualShock.DualShock4GamepadHID"/> can also set the LED color when this method is called.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IDualMotorRumble"/>
|
||||
/// <example>
|
||||
/// <code source="../../DocCodeSamples.Tests/GamepadHapticsExample.cs"/>
|
||||
/// </example>
|
||||
public virtual void ResumeHaptics()
|
||||
{
|
||||
m_Rumble.ResumeHaptics(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets rumble effects on the gamepad by setting motor speeds to 0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some devices such as <see cref="DualShock.DualSenseGamepadHID"/> and
|
||||
/// <see cref="DualShock.DualShock4GamepadHID"/> can also set the LED color when this method is called.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IDualMotorRumble"/>
|
||||
/// <example>
|
||||
/// <code source="../../DocCodeSamples.Tests/GamepadHapticsExample.cs"/>
|
||||
/// </example>
|
||||
public virtual void ResetHaptics()
|
||||
{
|
||||
m_Rumble.ResetHaptics(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <example>
|
||||
/// <code source="../../DocCodeSamples.Tests/GamepadHapticsExample.cs"/>
|
||||
/// </example>
|
||||
public virtual void SetMotorSpeeds(float lowFrequency, float highFrequency)
|
||||
{
|
||||
m_Rumble.SetMotorSpeeds(this, lowFrequency, highFrequency);
|
||||
}
|
||||
|
||||
private DualMotorRumble m_Rumble;
|
||||
|
||||
private static int s_GamepadCount;
|
||||
private static Gamepad[] s_Gamepads;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 644895dd25f14937a1d11481e2338925
|
||||
timeCreated: 1506745703
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e286fc65ad3d4645b213e71b11e963e4
|
||||
timeCreated: 1516644432
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
////REVIEW: should we keep an explicit playback status? ATM calling ResumeHaptics() will re-issue last set motor speed regardless of pause state
|
||||
|
||||
namespace UnityEngine.InputSystem.Haptics
|
||||
{
|
||||
/// <summary>
|
||||
/// Common implementation of dual motor rumbling.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct is meant for use in devices that implement <see cref="IDualMotorRumble"/>.
|
||||
/// </remarks>
|
||||
internal struct DualMotorRumble
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalized [0..1] speed of the low-frequency (usually left) motor.
|
||||
/// </summary>
|
||||
/// <value>Speed of left motor.</value>
|
||||
public float lowFrequencyMotorSpeed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized [0..1] speed of the high-frequency (usually right) motor.
|
||||
/// </summary>
|
||||
/// <value>Speed of right motor.</value>
|
||||
public float highFrequencyMotorSpeed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether either of the motors is currently set to non-zero speeds.
|
||||
/// </summary>
|
||||
/// <value>True if the motors are currently turned on.</value>
|
||||
/// <remarks>
|
||||
/// Does not take pausing into account, i.e. <see cref="lowFrequencyMotorSpeed"/> and/or
|
||||
/// <see cref="highFrequencyMotorSpeed"/> may be non-zero but haptics on the device
|
||||
/// may actually be paused with <see cref="PauseHaptics"/>.
|
||||
/// </remarks>
|
||||
public bool isRumbling =>
|
||||
!Mathf.Approximately(lowFrequencyMotorSpeed, 0f)
|
||||
|| !Mathf.Approximately(highFrequencyMotorSpeed, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// Stops haptics by setting motor speeds to zero.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sets both motor speeds to zero while retaining the current values for <see cref="lowFrequencyMotorSpeed"/>
|
||||
/// and <see cref="highFrequencyMotorSpeed"/>.
|
||||
/// It will only send the command if <see cref="isRumbling"/> is true.
|
||||
/// </remarks>
|
||||
/// <param name="device">Device to send command to.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
|
||||
public void PauseHaptics(InputDevice device)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
|
||||
if (!isRumbling)
|
||||
return;
|
||||
|
||||
var command = DualMotorRumbleCommand.Create(0f, 0f);
|
||||
device.ExecuteCommand(ref command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume haptics by setting motor speeds to the current values of <see cref="lowFrequencyMotorSpeed"/>
|
||||
/// and <see cref="highFrequencyMotorSpeed"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It will only set motor speeds if <see cref="isRumbling"/> is true
|
||||
/// </remarks>
|
||||
/// <param name="device">Device to send command to.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
|
||||
public void ResumeHaptics(InputDevice device)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
|
||||
if (!isRumbling)
|
||||
return;
|
||||
|
||||
SetMotorSpeeds(device, lowFrequencyMotorSpeed, highFrequencyMotorSpeed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset haptics by setting motor speeds to zero.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sets <see cref="lowFrequencyMotorSpeed"/> and <see cref="highFrequencyMotorSpeed"/> to zero.
|
||||
/// It will only set motor speeds if <see cref="isRumbling"/> is true.
|
||||
/// </remarks>
|
||||
/// <param name="device">Device to send command to.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
|
||||
public void ResetHaptics(InputDevice device)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
|
||||
if (!isRumbling)
|
||||
return;
|
||||
|
||||
SetMotorSpeeds(device, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the speed of the low-frequency (usually left) and high-frequency (usually right) motor
|
||||
/// on <paramref name="device"/>. Updates <see cref="lowFrequencyMotorSpeed"/> and
|
||||
/// <see cref="highFrequencyMotorSpeed"/>.
|
||||
/// </summary>
|
||||
/// <param name="device">Device to send command to.</param>
|
||||
/// <param name="lowFrequency">Speed of the low-frequency (left) motor. Normalized [0..1] value
|
||||
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
|
||||
/// be clamped into range.</param>
|
||||
/// <param name="highFrequency">Speed of the high-frequency (right) motor. Normalized [0..1] value
|
||||
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
|
||||
/// be clamped into range.</param>
|
||||
/// <remarks>
|
||||
/// Sends <see cref="DualMotorRumbleCommand"/> to <paramref name="device"/>.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
|
||||
public void SetMotorSpeeds(InputDevice device, float lowFrequency, float highFrequency)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
|
||||
lowFrequencyMotorSpeed = Mathf.Clamp(lowFrequency, 0.0f, 1.0f);
|
||||
highFrequencyMotorSpeed = Mathf.Clamp(highFrequency, 0.0f, 1.0f);
|
||||
|
||||
var command = DualMotorRumbleCommand.Create(lowFrequencyMotorSpeed, highFrequencyMotorSpeed);
|
||||
device.ExecuteCommand(ref command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4a1c749d40b4bf985d6b32d7db339bb
|
||||
timeCreated: 1517012194
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = kSize)]
|
||||
internal struct DualMotorRumbleCommand : IInputDeviceCommandInfo
|
||||
{
|
||||
public static FourCC Type { get { return new FourCC('R', 'M', 'B', 'L'); } }
|
||||
|
||||
internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(float) * 2;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public InputDeviceCommand baseCommand;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize)]
|
||||
public float lowFrequencyMotorSpeed;
|
||||
|
||||
[FieldOffset(InputDeviceCommand.kBaseCommandSize + 4)]
|
||||
public float highFrequencyMotorSpeed;
|
||||
|
||||
public FourCC typeStatic
|
||||
{
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public static DualMotorRumbleCommand Create(float lowFrequency, float highFrequency)
|
||||
{
|
||||
return new DualMotorRumbleCommand
|
||||
{
|
||||
baseCommand = new InputDeviceCommand(Type, kSize),
|
||||
lowFrequencyMotorSpeed = lowFrequency,
|
||||
highFrequencyMotorSpeed = highFrequency
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee46ea662a1d4d2397d0b3a45baf67ef
|
||||
timeCreated: 1517015642
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace UnityEngine.InputSystem.Haptics
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple haptics interface that allows to control two motors individually.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Dual-motor control is most common on gamepads (see <see cref="Gamepad"/>) such as
|
||||
/// Xbox and PlayStation controllers.
|
||||
/// </remarks>
|
||||
public interface IDualMotorRumble : IHaptics
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the motor speeds of the low-frequency (usually on the left) and high-frequency
|
||||
/// (usually on the right) motors.
|
||||
/// </summary>
|
||||
/// <param name="lowFrequency">Speed of the low-frequency (left) motor. Normalized [0..1] value
|
||||
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
|
||||
/// be clamped into range.</param>
|
||||
/// <param name="highFrequency">Speed of the high-frequency (right) motor. Normalized [0..1] value
|
||||
/// with 1 indicating maximum speed and 0 indicating the motor is turned off. Will automatically
|
||||
/// be clamped into range.</param>
|
||||
/// <remarks>
|
||||
/// Note that hardware will put limits on the level of control you have over the motors.
|
||||
/// Rumbling the motors at maximum speed for an extended period of time may cause them to turn
|
||||
/// off for some time to prevent overheating. Also, how quickly the motors react and how often
|
||||
/// the speed can be updated will depend on the hardware and drivers.
|
||||
/// </remarks>
|
||||
void SetMotorSpeeds(float lowFrequency, float highFrequency);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6982a6ffcdbf47a6aa68fd4418622395
|
||||
timeCreated: 1516644451
|
||||
@@ -0,0 +1,81 @@
|
||||
////REVIEW: Devices usually will automatically shut down haptics if they haven't received a haptics command in some time.
|
||||
//// How should we deal with that? Should haptics automatically refresh themselves periodically while they are set?
|
||||
|
||||
////REVIEW: Do we need a mute in addition to a pause?
|
||||
|
||||
namespace UnityEngine.InputSystem.Haptics
|
||||
{
|
||||
/// <summary>
|
||||
/// Base interface for haptics on input devices.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To support haptics, an <see cref="InputDevice"/> has to implement one or more
|
||||
/// haptics interfaces.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// class MyDevice : InputDevice, IDualMotorRumble
|
||||
/// {
|
||||
/// private DualMotorRumble m_Rumble;
|
||||
///
|
||||
/// public void SetMotorSpeeds(float lowFrequency, float highFrequency)
|
||||
/// {
|
||||
/// m_Rumble.SetMotorSpeeds(lowFrequency, highFrequency);
|
||||
/// }
|
||||
///
|
||||
/// public void PauseHaptics()
|
||||
/// {
|
||||
/// m_Rumble.PauseHaptics();
|
||||
/// }
|
||||
///
|
||||
/// public void ResumeHaptics()
|
||||
/// {
|
||||
/// m_Rumble.ResumeHaptics();
|
||||
/// }
|
||||
///
|
||||
/// public void ResetHaptics()
|
||||
/// {
|
||||
/// m_Rumble.ResetHaptics();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="InputSystem.PauseHaptics"/>
|
||||
/// <seealso cref="InputSystem.ResumeHaptics"/>
|
||||
/// <seealso cref="InputSystem.ResetHaptics"/>
|
||||
public interface IHaptics
|
||||
{
|
||||
/// <summary>
|
||||
/// Pause haptics playback on the device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should preserve current playback settings (such as motor speed levels
|
||||
/// or effect playback positions) but shut down feedback effects on the device.
|
||||
///
|
||||
/// If proper resumption of effects is not possible, playback should be stopped
|
||||
/// and <see cref="ResumeHaptics"/> is allowed to be a no-operation.
|
||||
///
|
||||
/// Note that haptics playback states are not required to survive domain reloads
|
||||
/// in the editor.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ResumeHaptics"/>
|
||||
void PauseHaptics();
|
||||
|
||||
/// <summary>
|
||||
/// Resume haptics playback on the device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be called after calling <see cref="PauseHaptics"/>. Otherwise does
|
||||
/// nothing.
|
||||
/// </remarks>
|
||||
void ResumeHaptics();
|
||||
|
||||
/// <summary>
|
||||
/// Reset haptics playback on the device to its default state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will turn off all haptics effects that may be playing on the device.
|
||||
/// </remarks>
|
||||
void ResetHaptics();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 179e4e4b8003406d97463b101f15eb8b
|
||||
timeCreated: 1516752847
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// A device that implements its own reset logic for when <see cref="InputSystem.ResetDevice"/>
|
||||
/// is called.
|
||||
/// </summary>
|
||||
internal interface ICustomDeviceReset
|
||||
{
|
||||
/// <summary>
|
||||
/// Reset the current device state.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08dab706fb9641539767891cee0c7d3c
|
||||
timeCreated: 1636546307
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
internal interface IEventMerger
|
||||
{
|
||||
bool MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7522ce067dfd4acf8e9e136fc7a0ab64
|
||||
timeCreated: 1628242797
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gives an opportunity for device to modify event data in-place before it gets propagated to the rest of the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If device also implements <see cref="IEventMerger"/> it will run first, because we don't process events ahead-of-time.
|
||||
/// </remarks>
|
||||
internal interface IEventPreProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Preprocess the event. !!! Beware !!! currently events can only shrink or stay the same size.
|
||||
/// </summary>
|
||||
/// <param name="currentEventPtr">The event to preprocess.</param>
|
||||
/// <returns>True if event should be processed further, false if event should be skipped and ignored.</returns>
|
||||
bool PreProcessEvent(InputEventPtr currentEventPtr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0be28dce931f40f8a48953c52e03d5d7
|
||||
timeCreated: 1631188213
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface to allow custom input devices to receive callbacks when the input system is updated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an <see cref="InputDevice"/> class implements the IInputUpdateCallbackReceiver interface, any instance of the
|
||||
/// InputDevice will have it's <see cref="OnUpdate"/> method called whenever the input system updates. This can be used
|
||||
/// to implement custom state update logic for virtual input devices which track some state in the project.
|
||||
/// </remarks>
|
||||
public interface IInputUpdateCallbackReceiver
|
||||
{
|
||||
void OnUpdate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb7aebed4ffd404da3399f60b272c7ee
|
||||
timeCreated: 1508656424
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for <see cref="InputDevice"/> classes that can receive text input events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface should be implemented by devices that are meant to receive text
|
||||
/// input through <see cref="TextEvent"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TextEvent"/>
|
||||
/// <seealso cref="IMECompositionEvent"/>
|
||||
public interface ITextInputReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// A single, fully-formed Unicode character has been typed on the device.
|
||||
/// </summary>
|
||||
/// <param name="character">Character that was typed. Note that in case the character is part of
|
||||
/// a surrogate pair, this method is called first with the high surrogate and then with the
|
||||
/// low surrogate character.</param>
|
||||
/// <remarks>
|
||||
/// This method is called on a device when a <see cref="TextEvent"/> is received
|
||||
/// for the device. <paramref name="character"/> is the <see cref="TextEvent.character"/>
|
||||
/// from the event.
|
||||
///
|
||||
/// Note that this method will be called *twice* for a single <see cref="TextEvent"/>
|
||||
/// in case the given UTF-32 (encoding in the event) needs to be represented as UTF-16
|
||||
/// (encoding of <c>char</c> in C#) surrogate.
|
||||
/// </remarks>
|
||||
void OnTextInput(char character);
|
||||
|
||||
/// <summary>
|
||||
/// Called when an IME composition is in-progress or finished.
|
||||
/// </summary>
|
||||
/// <param name="compositionString">The current composition.</param>
|
||||
/// <seealso cref="IMECompositionEvent"/>
|
||||
/// <seealso cref="Keyboard.onIMECompositionChange"/>
|
||||
/// <remarks>
|
||||
/// The method will be repeatedly called with the current string while composition is in progress.
|
||||
/// Once composition finishes, the method will be called one more time with a blank composition
|
||||
/// string.
|
||||
/// </remarks>
|
||||
void OnIMECompositionChanged(IMECompositionString compositionString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b5bc678fe5c44624a0f00e148fc2670
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 250f0b2038f8466aaa09814a4b1c5f46
|
||||
timeCreated: 1506738413
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1284e66e3be9424db2c22a37b5a3cf0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates what type of change related to an <see cref="InputDevice">input device</see> occurred.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use <see cref="InputSystem.onDeviceChange"/> to receive notifications about changes
|
||||
/// to the input device setup in the system.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// InputSystem.onDeviceChange +=
|
||||
/// (device, change) =>
|
||||
/// {
|
||||
/// switch (change)
|
||||
/// {
|
||||
/// case InputDeviceChange.Added:
|
||||
/// Debug.Log($"Device {device} was added");
|
||||
/// break;
|
||||
/// case InputDeviceChange.Removed:
|
||||
/// Debug.Log($"Device {device} was removed");
|
||||
/// break;
|
||||
/// }
|
||||
/// };
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public enum InputDeviceChange
|
||||
{
|
||||
/// <summary>
|
||||
/// A new device was added to the system. This is triggered <em>after</em> the device
|
||||
/// has already been added, i.e. it already appears on <see cref="InputSystem.devices"/>.
|
||||
///
|
||||
/// See also <see cref="InputSystem.AddDevice{TDevice}(string)"/> and <see cref="InputDevice.added"/>.
|
||||
/// </summary>
|
||||
Added,
|
||||
|
||||
/// <summary>
|
||||
/// An existing device was removed from the system. This is triggered <em>after</em> the
|
||||
/// device has already been removed, i.e. it already has been cleared from <see cref="InputSystem.devices"/>.
|
||||
///
|
||||
/// Other than when a device is removed programmatically, this happens when a device
|
||||
/// is unplugged from the system. Subsequent to the notification, the system will remove
|
||||
/// the <see cref="InputDevice"/> instance from its list and remove the device's
|
||||
/// recorded input state.
|
||||
///
|
||||
/// See also <see cref="InputSystem.RemoveDevice"/>.
|
||||
/// </summary>
|
||||
Removed,
|
||||
|
||||
/// <summary>
|
||||
/// A device reported by the <see cref="IInputRuntime"/> was <see cref="Removed"/> but was
|
||||
/// retained by the system as <see cref="InputSystem.disconnectedDevices">disconnected</see>.
|
||||
///
|
||||
/// See also <see cref="InputSystem.disconnectedDevices"/>.
|
||||
/// </summary>
|
||||
Disconnected,
|
||||
|
||||
/// <summary>
|
||||
/// A device that was previously retained as <see cref="Disconnected"/> has been re-discovered
|
||||
/// and has been <see cref="Added"/> to the system again.
|
||||
///
|
||||
/// See also <see cref="InputSystem.disconnectedDevices"/>.
|
||||
/// </summary>
|
||||
Reconnected,
|
||||
|
||||
/// <summary>
|
||||
/// An existing device was re-enabled after having been <see cref="Disabled"/>.
|
||||
///
|
||||
/// See also <see cref="InputSystem.EnableDevice"/> and <see cref="InputDevice.enabled"/>.
|
||||
/// </summary>
|
||||
Enabled,
|
||||
|
||||
/// <summary>
|
||||
/// An existing device was disabled.
|
||||
///
|
||||
/// See also <see cref="InputSystem.DisableDevice"/> and <see cref="InputDevice.enabled"/>.
|
||||
/// </summary>
|
||||
Disabled,
|
||||
|
||||
/// <summary>
|
||||
/// The usages on a device have changed.
|
||||
///
|
||||
/// This may signal, for example, that what was the right hand XR controller before
|
||||
/// is now the left hand controller.
|
||||
///
|
||||
/// See also <see cref="InputSystem.SetDeviceUsage(InputDevice,string)"/> and
|
||||
/// <see cref="InputControl.usages"/>.
|
||||
/// </summary>
|
||||
UsageChanged,
|
||||
|
||||
/// <summary>
|
||||
/// The configuration of a device has changed.
|
||||
///
|
||||
/// This may signal, for example, that the layout used by the keyboard has changed or
|
||||
/// that, on a console, a gamepad has changed which player ID(s) it is assigned to.
|
||||
///
|
||||
/// See also <see cref="DeviceConfigurationEvent"/> and <see cref="InputSystem.QueueConfigChangeEvent"/>.
|
||||
/// </summary>
|
||||
ConfigurationChanged,
|
||||
|
||||
/// <summary>
|
||||
/// Device is being "soft" reset but in a way that excludes <see cref="Layouts.InputControlLayout.ControlItem.dontReset"/>
|
||||
/// controls such as mouse positions. This can happen during application focus changes
|
||||
/// (see <see cref="InputSettings.backgroundBehavior"/>) or when <see cref="InputSystem.ResetDevice"/>
|
||||
/// is called explicitly.
|
||||
///
|
||||
/// This notification is sent before the actual reset happens.
|
||||
/// </summary>
|
||||
SoftReset,
|
||||
|
||||
/// <summary>
|
||||
/// Device is being "hard" reset, i.e. every control is reset to its default value. This happens only
|
||||
/// when explicitly forced through <see cref="InputSystem.ResetDevice"/>.
|
||||
///
|
||||
/// This notification is sent before the actual reset happens.
|
||||
/// </summary>
|
||||
HardReset,
|
||||
|
||||
[Obsolete("Destroyed enum has been deprecated.")]
|
||||
Destroyed,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af015677ecab4644b2382c6b37c2d95c
|
||||
timeCreated: 1506750675
|
||||
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: add a 'devicePath' property that platforms can use to relay their internal device locators
|
||||
//// (but do *not* take it into account when comparing descriptions for disconnected devices)
|
||||
|
||||
namespace UnityEngine.InputSystem.Layouts
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata for an input device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Device descriptions are mainly used to determine which <see cref="InputControlLayout"/>
|
||||
/// to create an actual <see cref="InputDevice"/> instance from. Each description is comprised
|
||||
/// of a set of properties that each are individually optional. However, for a description
|
||||
/// to be usable, at least some need to be set. Generally, the minimum viable description
|
||||
/// for a device is one with <see cref="deviceClass"/> filled out.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Device description equivalent to a generic gamepad with no
|
||||
/// // further information about the device.
|
||||
/// new InputDeviceDescription
|
||||
/// {
|
||||
/// deviceClass = "Gamepad"
|
||||
/// };
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Device descriptions will usually be supplied by the Unity runtime but can also be manually
|
||||
/// fed into the system using <see cref="InputSystem.AddDevice(InputDeviceDescription)"/>. The
|
||||
/// system will remember each device description it has seen regardless of whether it was
|
||||
/// able to successfully create a device from the description. To query the list of descriptions
|
||||
/// that for whatever reason did not result in a device being created, call <see
|
||||
/// cref="InputSystem.GetUnsupportedDevices()"/>.
|
||||
///
|
||||
/// Whenever layout registrations in the system are changed (e.g. by calling <see
|
||||
/// cref="InputSystem.RegisterLayout{T}"/> or whenever <see cref="InputSettings.supportedDevices"/>
|
||||
/// is changed, the system will go through the list of unsupported devices itself and figure out
|
||||
/// if there are device descriptions that now it can turn into devices. The same also applies
|
||||
/// in reverse; if, for example, a layout is removed that is currently used a device, the
|
||||
/// device will be removed and its description (if any) will be placed on the list of
|
||||
/// unsupported devices.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputDevice.description"/>
|
||||
/// <seealso cref="InputDeviceMatcher"/>
|
||||
[Serializable]
|
||||
public struct InputDeviceDescription : IEquatable<InputDeviceDescription>
|
||||
{
|
||||
/// <summary>
|
||||
/// How we talk to the device; usually name of the underlying backend that feeds
|
||||
/// state for the device (e.g. "HID" or "XInput").
|
||||
/// </summary>
|
||||
/// <value>Name of interface through which the device is reported.</value>
|
||||
/// <see cref="InputDeviceMatcher.WithInterface"/>
|
||||
public string interfaceName
|
||||
{
|
||||
get => m_InterfaceName;
|
||||
set => m_InterfaceName = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What the interface thinks the device classifies as.
|
||||
/// </summary>
|
||||
/// <value>Broad classification of device.</value>
|
||||
/// <remarks>
|
||||
/// If there is no layout specifically matching a device description,
|
||||
/// the device class is used as as fallback. If, for example, this field
|
||||
/// is set to "Gamepad", the "Gamepad" layout is used as a fallback.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputDeviceMatcher.WithDeviceClass"/>
|
||||
public string deviceClass
|
||||
{
|
||||
get => m_DeviceClass;
|
||||
set => m_DeviceClass = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the vendor that produced the device.
|
||||
/// </summary>
|
||||
/// <value>Name of manufacturer.</value>
|
||||
/// <seealso cref="InputDeviceMatcher.WithManufacturer"/>
|
||||
public string manufacturer
|
||||
{
|
||||
get => m_Manufacturer;
|
||||
set => m_Manufacturer = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the product assigned by the vendor to the device.
|
||||
/// </summary>
|
||||
/// <value>Name of product.</value>
|
||||
/// <seealso cref="InputDeviceMatcher.WithProduct"/>
|
||||
public string product
|
||||
{
|
||||
get => m_Product;
|
||||
set => m_Product = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If available, serial number for the device.
|
||||
/// </summary>
|
||||
/// <value>Serial number of device.</value>
|
||||
public string serial
|
||||
{
|
||||
get => m_Serial;
|
||||
set => m_Serial = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version string of the device and/or driver.
|
||||
/// </summary>
|
||||
/// <value>Version of device and/or driver.</value>
|
||||
/// <seealso cref="InputDeviceMatcher.WithVersion"/>
|
||||
public string version
|
||||
{
|
||||
get => m_Version;
|
||||
set => m_Version = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional JSON string listing device-specific capabilities.
|
||||
/// </summary>
|
||||
/// <value>Interface-specific listing of device capabilities.</value>
|
||||
/// <remarks>
|
||||
/// The primary use of this field is to allow custom layout factories
|
||||
/// to create layouts on the fly from in-depth device descriptions delivered
|
||||
/// by external APIs.
|
||||
///
|
||||
/// In the case of HID, for example, this field contains a JSON representation
|
||||
/// of the HID descriptor (see <see cref="HID.HID.HIDDeviceDescriptor"/>) as
|
||||
/// reported by the device driver. This descriptor contains information about
|
||||
/// all I/O elements on the device which can be used to determine the control
|
||||
/// setup and data format used by the device.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputDeviceMatcher.WithCapability{T}"/>
|
||||
public string capabilities
|
||||
{
|
||||
get => m_Capabilities;
|
||||
set => m_Capabilities = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether any of the properties in the description are set.
|
||||
/// </summary>
|
||||
/// <value>True if any of <see cref="interfaceName"/>, <see cref="deviceClass"/>,
|
||||
/// <see cref="manufacturer"/>, <see cref="product"/>, <see cref="serial"/>,
|
||||
/// <see cref="version"/>, or <see cref="capabilities"/> is not <c>null</c> and
|
||||
/// not empty.</value>
|
||||
public bool empty =>
|
||||
string.IsNullOrEmpty(m_InterfaceName) &&
|
||||
string.IsNullOrEmpty(m_DeviceClass) &&
|
||||
string.IsNullOrEmpty(m_Manufacturer) &&
|
||||
string.IsNullOrEmpty(m_Product) &&
|
||||
string.IsNullOrEmpty(m_Serial) &&
|
||||
string.IsNullOrEmpty(m_Version) &&
|
||||
string.IsNullOrEmpty(m_Capabilities);
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of the description useful for
|
||||
/// debugging.
|
||||
/// </summary>
|
||||
/// <returns>A script representation of the description.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var haveProduct = !string.IsNullOrEmpty(product);
|
||||
var haveManufacturer = !string.IsNullOrEmpty(manufacturer);
|
||||
var haveInterface = !string.IsNullOrEmpty(interfaceName);
|
||||
|
||||
if (haveProduct && haveManufacturer)
|
||||
{
|
||||
if (haveInterface)
|
||||
return $"{manufacturer} {product} ({interfaceName})";
|
||||
|
||||
return $"{manufacturer} {product}";
|
||||
}
|
||||
|
||||
if (haveProduct)
|
||||
{
|
||||
if (haveInterface)
|
||||
return $"{product} ({interfaceName})";
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(deviceClass))
|
||||
{
|
||||
if (haveInterface)
|
||||
return $"{deviceClass} ({interfaceName})";
|
||||
|
||||
return deviceClass;
|
||||
}
|
||||
|
||||
// For some HIDs on Windows, we don't get a product and manufacturer string even though
|
||||
// the HID is guaranteed to have a product and vendor ID. Resort to printing capabilities
|
||||
// which for HIDs at least include the product and vendor ID.
|
||||
if (!string.IsNullOrEmpty(capabilities))
|
||||
{
|
||||
const int kMaxCapabilitiesLength = 40;
|
||||
|
||||
var caps = capabilities;
|
||||
if (capabilities.Length > kMaxCapabilitiesLength)
|
||||
caps = caps.Substring(0, kMaxCapabilitiesLength) + "...";
|
||||
|
||||
if (haveInterface)
|
||||
return $"{caps} ({interfaceName})";
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
if (haveInterface)
|
||||
return interfaceName;
|
||||
|
||||
return "<Empty Device Description>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the description to the given <paramref name="other"/> description.
|
||||
/// </summary>
|
||||
/// <param name="other">Another device description.</param>
|
||||
/// <returns>True if the two descriptions are equivalent.</returns>
|
||||
/// <remarks>
|
||||
/// Two descriptions are equivalent if all their properties are equal
|
||||
/// (ignore case).
|
||||
/// </remarks>
|
||||
public bool Equals(InputDeviceDescription other)
|
||||
{
|
||||
return m_InterfaceName.InvariantEqualsIgnoreCase(other.m_InterfaceName) &&
|
||||
m_DeviceClass.InvariantEqualsIgnoreCase(other.m_DeviceClass) &&
|
||||
m_Manufacturer.InvariantEqualsIgnoreCase(other.m_Manufacturer) &&
|
||||
m_Product.InvariantEqualsIgnoreCase(other.m_Product) &&
|
||||
m_Serial.InvariantEqualsIgnoreCase(other.m_Serial) &&
|
||||
m_Version.InvariantEqualsIgnoreCase(other.m_Version) &&
|
||||
////REVIEW: this would ideally compare JSON contents not just the raw string
|
||||
m_Capabilities.InvariantEqualsIgnoreCase(other.m_Capabilities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the description to the given object.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object.</param>
|
||||
/// <returns>True if <paramref name="obj"/> is an InputDeviceDescription
|
||||
/// equivalent to this one.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceDescription)"/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
return false;
|
||||
return obj is InputDeviceDescription description && Equals(description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute a hash code for the device description.
|
||||
/// </summary>
|
||||
/// <returns>A hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = m_InterfaceName != null ? m_InterfaceName.GetHashCode() : 0;
|
||||
hashCode = (hashCode * 397) ^ (m_DeviceClass != null ? m_DeviceClass.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (m_Manufacturer != null ? m_Manufacturer.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (m_Product != null ? m_Product.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (m_Serial != null ? m_Serial.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (m_Version != null ? m_Version.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (m_Capabilities != null ? m_Capabilities.GetHashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two device descriptions.
|
||||
/// </summary>
|
||||
/// <param name="left">First device description.</param>
|
||||
/// <param name="right">Second device description.</param>
|
||||
/// <returns>True if the two descriptions are equivalent.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceDescription)"/>
|
||||
public static bool operator==(InputDeviceDescription left, InputDeviceDescription right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two device descriptions for inequality.
|
||||
/// </summary>
|
||||
/// <param name="left">First device description.</param>
|
||||
/// <param name="right">Second device description.</param>
|
||||
/// <returns>True if the two descriptions are not equivalent.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceDescription)"/>
|
||||
public static bool operator!=(InputDeviceDescription left, InputDeviceDescription right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a JSON representation of the device description.
|
||||
/// </summary>
|
||||
/// <returns>A JSON representation of the description.</returns>
|
||||
/// <remarks>
|
||||
/// <example>
|
||||
/// The result can be converted back into an InputDeviceDescription
|
||||
/// using <see cref="FromJson"/>.
|
||||
///
|
||||
/// <code>
|
||||
/// var description = new InputDeviceDescription
|
||||
/// {
|
||||
/// interfaceName = "HID",
|
||||
/// product = "SomeDevice",
|
||||
/// capabilities = @"
|
||||
/// {
|
||||
/// ""vendorId"" : 0xABA,
|
||||
/// ""productId"" : 0xEFE
|
||||
/// }
|
||||
/// "
|
||||
/// };
|
||||
///
|
||||
/// Debug.Log(description.ToJson());
|
||||
/// // Prints
|
||||
/// // {
|
||||
/// // "interface" : "HID",
|
||||
/// // "product" : "SomeDevice",
|
||||
/// // "capabilities" : "{ \"vendorId\" : 0xABA, \"productId\" : 0xEFF }"
|
||||
/// // }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="FromJson"/>
|
||||
public string ToJson()
|
||||
{
|
||||
var data = new DeviceDescriptionJson
|
||||
{
|
||||
@interface = interfaceName,
|
||||
type = deviceClass,
|
||||
product = product,
|
||||
manufacturer = manufacturer,
|
||||
serial = serial,
|
||||
version = version,
|
||||
capabilities = capabilities
|
||||
};
|
||||
return JsonUtility.ToJson(data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an InputDeviceDescription from its JSON representation.
|
||||
/// </summary>
|
||||
/// <param name="json">String in JSON format.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="json"/> is <c>null</c>.</exception>
|
||||
/// <returns>The converted </returns>
|
||||
/// <exception cref="ArgumentException">There as a parse error in <paramref name="json"/>.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// InputDeviceDescription.FromJson(@"
|
||||
/// {
|
||||
/// ""interface"" : ""HID"",
|
||||
/// ""product"" : ""SomeDevice""
|
||||
/// }
|
||||
/// ");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="ToJson"/>
|
||||
public static InputDeviceDescription FromJson(string json)
|
||||
{
|
||||
if (json == null)
|
||||
throw new ArgumentNullException(nameof(json));
|
||||
|
||||
var data = JsonUtility.FromJson<DeviceDescriptionJson>(json);
|
||||
|
||||
return new InputDeviceDescription
|
||||
{
|
||||
interfaceName = data.@interface,
|
||||
deviceClass = data.type,
|
||||
product = data.product,
|
||||
manufacturer = data.manufacturer,
|
||||
serial = data.serial,
|
||||
version = data.version,
|
||||
capabilities = data.capabilities
|
||||
};
|
||||
}
|
||||
|
||||
internal static bool ComparePropertyToDeviceDescriptor(string propertyName, JsonParser.JsonString propertyValue, string deviceDescriptor)
|
||||
{
|
||||
// We use JsonParser instead of JsonUtility.Parse in order to not allocate GC memory here.
|
||||
|
||||
var json = new JsonParser(deviceDescriptor);
|
||||
if (!json.NavigateToProperty(propertyName))
|
||||
{
|
||||
if (propertyValue.text.isEmpty)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return json.CurrentPropertyHasValueEqualTo(propertyValue);
|
||||
}
|
||||
|
||||
[SerializeField] private string m_InterfaceName;
|
||||
[SerializeField] private string m_DeviceClass;
|
||||
[SerializeField] private string m_Manufacturer;
|
||||
[SerializeField] private string m_Product;
|
||||
[SerializeField] private string m_Serial;
|
||||
[SerializeField] private string m_Version;
|
||||
[SerializeField] private string m_Capabilities;
|
||||
|
||||
private struct DeviceDescriptionJson
|
||||
{
|
||||
public string @interface;
|
||||
public string type;
|
||||
public string product;
|
||||
public string serial;
|
||||
public string version;
|
||||
public string manufacturer;
|
||||
public string capabilities;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98406e6253bf4ccba72ff32ee85f5efa
|
||||
timeCreated: 1506763077
|
||||
@@ -0,0 +1,683 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.Layouts
|
||||
{
|
||||
/// <summary>
|
||||
/// Specification that can be matched against an <see cref="InputDeviceDescription"/>. This is
|
||||
/// used to find which <see cref="InputControlLayout"/> to create for a device when it is discovered.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each matcher is basically a set of key/value pairs where each value may either be
|
||||
/// a regular expression or a plain value object. The central method for testing a given matcher
|
||||
/// against an <see cref="InputDeviceDescription"/> is <see cref="MatchPercentage"/>.
|
||||
///
|
||||
/// Various helper methods such as <see cref="WithInterface"/> or <see cref="WithCapability{TValue}"/>
|
||||
/// assist with creating matchers.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // A matcher that matches a PS4 controller by name.
|
||||
/// new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithManufacturer("Sony.+Entertainment") // Regular expression
|
||||
/// .WithProduct("Wireless Controller"));
|
||||
///
|
||||
/// // A matcher that matches the same controller by PID and VID.
|
||||
/// new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithCapability("vendorId", 0x54C) // Sony Entertainment.
|
||||
/// .WithCapability("productId", 0x9CC)); // Wireless controller.
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// For each registered <see cref="InputControlLayout"/> in the system that represents
|
||||
/// a device, arbitrary many matchers can be added. A matcher can be supplied either
|
||||
/// at registration time or at any point after using <see cref="InputSystem.RegisterLayoutMatcher"/>.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Supply a matcher at registration time.
|
||||
/// InputSystem.RegisterLayout<DualShock4GamepadHID>(
|
||||
/// matches: new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithCapability("vendorId", 0x54C) // Sony Entertainment.
|
||||
/// .WithCapability("productId", 0x9CC)); // Wireless controller.
|
||||
///
|
||||
/// // Supply a matcher for an already registered layout.
|
||||
/// // This can be called repeatedly and will add another matcher
|
||||
/// // each time.
|
||||
/// InputSystem.RegisterLayoutMatcher<DualShock4GamepadHID>(
|
||||
/// matches: new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithManufacturer("Sony.+Entertainment")
|
||||
/// .WithProduct("Wireless Controller"));
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputDeviceDescription"/>
|
||||
/// <seealso cref="InputDevice.description"/>
|
||||
/// <seealso cref="InputSystem.RegisterLayoutMatcher"/>
|
||||
public struct InputDeviceMatcher : IEquatable<InputDeviceMatcher>
|
||||
{
|
||||
private KeyValuePair<InternedString, object>[] m_Patterns;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the matcher has been default-initialized and contains no
|
||||
/// matching <see cref="patterns"/>.
|
||||
/// </summary>
|
||||
/// <value>Whether the matcher contains any matching patterns.</value>
|
||||
/// <seealso cref="patterns"/>
|
||||
public bool empty => m_Patterns == null;
|
||||
|
||||
/// <summary>
|
||||
/// The list of patterns to match.
|
||||
/// </summary>
|
||||
/// <value>List of matching patterns.</value>
|
||||
/// <remarks>
|
||||
/// Each pattern is comprised of a key and a value. The key determines which part
|
||||
/// of an <see cref="InputDeviceDescription"/> to match.
|
||||
///
|
||||
/// The value represents the expected value. This can be either a plain string
|
||||
/// (matched case-insensitive) or a regular expression.
|
||||
/// </remarks>
|
||||
/// <seealso cref="WithInterface"/>
|
||||
/// <seealso cref="WithCapability{TValue}"/>
|
||||
/// <seealso cref="WithProduct"/>
|
||||
/// <seealso cref="WithManufacturer"/>
|
||||
/// <seealso cref="WithVersion"/>
|
||||
/// <seealso cref="WithDeviceClass"/>
|
||||
public IEnumerable<KeyValuePair<string, object>> patterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Patterns == null)
|
||||
yield break;
|
||||
|
||||
var count = m_Patterns.Length;
|
||||
for (var i = 0; i < count; ++i)
|
||||
yield return new KeyValuePair<string, object>(m_Patterns[i].Key.ToString(), m_Patterns[i].Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match an <see cref="InputDeviceDescription.interfaceName"/>.
|
||||
/// </summary>
|
||||
/// <param name="pattern">String to match.</param>
|
||||
/// <param name="supportRegex">If true (default), <paramref name="pattern"/> can be
|
||||
/// a regular expression.</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.interfaceName"/>
|
||||
public InputDeviceMatcher WithInterface(string pattern, bool supportRegex = true)
|
||||
{
|
||||
return With(kInterfaceKey, pattern, supportRegex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match a <see cref="InputDeviceDescription.deviceClass"/>.
|
||||
/// </summary>
|
||||
/// <param name="pattern">String to match.</param>
|
||||
/// <param name="supportRegex">If true (default), <paramref name="pattern"/> can be
|
||||
/// a regular expression.</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.deviceClass"/>
|
||||
public InputDeviceMatcher WithDeviceClass(string pattern, bool supportRegex = true)
|
||||
{
|
||||
return With(kDeviceClassKey, pattern, supportRegex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match a <see cref="InputDeviceDescription.manufacturer"/>.
|
||||
/// </summary>
|
||||
/// <param name="pattern">String to match.</param>
|
||||
/// <param name="supportRegex">If true (default), <paramref name="pattern"/> can be
|
||||
/// a regular expression.</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.manufacturer"/>
|
||||
public InputDeviceMatcher WithManufacturer(string pattern, bool supportRegex = true)
|
||||
{
|
||||
return With(kManufacturerKey, pattern, supportRegex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern (simple string) to <see cref="patterns"/> to match a <see cref="InputDeviceDescription.manufacturer"/>.
|
||||
/// </summary>
|
||||
/// <param name="noRegExPattern">String to match - simple keyword search in a device manufacturer string (eg "SONY").</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.manufacturer"/>
|
||||
public InputDeviceMatcher WithManufacturerContains(string noRegExPattern)
|
||||
{
|
||||
return With(kManufacturerContainsKey, noRegExPattern, supportRegex: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match a <see cref="InputDeviceDescription.product"/>.
|
||||
/// </summary>
|
||||
/// <param name="pattern">String to match.</param>
|
||||
/// <param name="supportRegex">If true (default), <paramref name="pattern"/> can be
|
||||
/// a regular expression.</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.product"/>
|
||||
public InputDeviceMatcher WithProduct(string pattern, bool supportRegex = true)
|
||||
{
|
||||
return With(kProductKey, pattern, supportRegex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match a <see cref="InputDeviceDescription.version"/>.
|
||||
/// </summary>
|
||||
/// <param name="pattern">String to match.</param>
|
||||
/// <param name="supportRegex">If true (default), <paramref name="pattern"/> can be
|
||||
/// a regular expression.</param>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <seealso cref="InputDeviceDescription.version"/>
|
||||
public InputDeviceMatcher WithVersion(string pattern, bool supportRegex = true)
|
||||
{
|
||||
return With(kVersionKey, pattern, supportRegex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a pattern to <see cref="patterns"/> to match an individual capability in <see cref="InputDeviceDescription.capabilities"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the JSON property using '/' as a separator,
|
||||
/// e.g. <c>"elements/count"</c>.</param>
|
||||
/// <param name="value">Value to match. This can be a string, a regular expression,
|
||||
/// a boolean, an integer, or a float. Floating-point numbers are matched with respect
|
||||
/// for <c>Mathf.Epsilon</c>. Values are converted between types automatically as
|
||||
/// needed (meaning that a bool can be compared to a string, for example).</param>
|
||||
/// <typeparam name="TValue">Type of value to match.</typeparam>
|
||||
/// <returns>The modified device matcher with the added pattern.</returns>
|
||||
/// <remarks>
|
||||
/// Capabilities are stored as JSON strings in <see cref="InputDeviceDescription.capabilities"/>.
|
||||
/// A matcher has the ability to match specific properties from the JSON object
|
||||
/// contained in the capabilities string.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // The description for a HID will usually have a HIDDeviceDescriptor in
|
||||
/// // JSON format found on its InputDeviceDescription.capabilities. So, a
|
||||
/// // real-world device description could look the equivalent of this:
|
||||
/// var description = new InputDeviceDescription
|
||||
/// {
|
||||
/// interfaceName = "HID",
|
||||
/// capabilities = new HID.HIDDeviceDescriptor
|
||||
/// {
|
||||
/// vendorId = 0x54C,
|
||||
/// productId = 0x9CC
|
||||
/// }.ToJson()
|
||||
/// };
|
||||
///
|
||||
/// // We can create a device matcher that looks for those to properties
|
||||
/// // directly in the JSON object.
|
||||
/// new InputDeviceMatcher()
|
||||
/// .WithCapability("vendorId", 0x54C)
|
||||
/// .WithCapability("productId", 0x9CC);
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Properties in nested objects can be referenced by separating properties
|
||||
/// with <c>/</c> and properties in arrays can be indexed with <c>[..]</c>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputDeviceDescription.capabilities"/>
|
||||
public InputDeviceMatcher WithCapability<TValue>(string path, TValue value)
|
||||
{
|
||||
return With(new InternedString(path), value);
|
||||
}
|
||||
|
||||
private InputDeviceMatcher With(InternedString key, object value, bool supportRegex = true)
|
||||
{
|
||||
// If it's a string, check whether it's a regex.
|
||||
if (supportRegex && value is string str)
|
||||
{
|
||||
var mayBeRegex = !str.All(ch => char.IsLetterOrDigit(ch) || char.IsWhiteSpace(ch)) &&
|
||||
!double.TryParse(str, out var _); // Avoid '.' in floats forcing the value to be a regex.
|
||||
if (mayBeRegex)
|
||||
value = new Regex(str, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
// Add to list.
|
||||
var result = this;
|
||||
ArrayHelpers.Append(ref result.m_Patterns, new KeyValuePair<InternedString, object>(key, value));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the level of matching to the given <paramref name="deviceDescription"/>.
|
||||
/// </summary>
|
||||
/// <param name="deviceDescription">A device description.</param>
|
||||
/// <returns>A score usually in the range between 0 and 1.</returns>
|
||||
/// <remarks>
|
||||
/// The algorithm computes a score of how well the matcher matches the given description.
|
||||
/// Essentially, a matcher that matches every single property that is present (as in
|
||||
/// not <c>null</c> and not an empty string) in <paramref name="deviceDescription"/> receives
|
||||
/// a score of 1, a matcher that matches none a score of 0. Matches that match only a subset
|
||||
/// receive a score in-between.
|
||||
///
|
||||
/// An exception to this are capabilities. Every single match of a capability is counted
|
||||
/// as one property match and added to the score. This means that matchers that match
|
||||
/// on multiple capabilities may actually achieve a score >1.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var description = new InputDeviceDescription
|
||||
/// {
|
||||
/// interfaceName = "HID",
|
||||
/// product = "MadeUpDevice",
|
||||
/// capabilities = new HID.HIDDeviceDescriptor
|
||||
/// {
|
||||
/// vendorId = 0xABC,
|
||||
/// productId = 0xDEF
|
||||
/// }.ToJson()
|
||||
/// };
|
||||
///
|
||||
/// // This matcher will achieve a score of 0.666 (2/3) as it
|
||||
/// // matches two out of three available properties.
|
||||
/// new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithProduct("MadeUpDevice");
|
||||
///
|
||||
/// // This matcher will achieve a score of 1 despite not matching
|
||||
/// // 'product'. The reason is that it matches two keys in
|
||||
/// // 'capabilities'.
|
||||
/// new InputDeviceMatcher()
|
||||
/// .WithInterface("HID")
|
||||
/// .WithCapability("vendorId", 0xABC)
|
||||
/// .WithCapability("productId", 0xDEF);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public float MatchPercentage(InputDeviceDescription deviceDescription)
|
||||
{
|
||||
if (empty)
|
||||
return 0;
|
||||
|
||||
// Go through all patterns. Score is 0 if any of the patterns
|
||||
// doesn't match.
|
||||
var numPatterns = m_Patterns.Length;
|
||||
for (var i = 0; i < numPatterns; ++i)
|
||||
{
|
||||
var key = m_Patterns[i].Key;
|
||||
var pattern = m_Patterns[i].Value;
|
||||
|
||||
if (key == kInterfaceKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.interfaceName)
|
||||
|| !MatchSingleProperty(pattern, deviceDescription.interfaceName))
|
||||
return 0;
|
||||
}
|
||||
else if (key == kDeviceClassKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.deviceClass)
|
||||
|| !MatchSingleProperty(pattern, deviceDescription.deviceClass))
|
||||
return 0;
|
||||
}
|
||||
else if (key == kManufacturerKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.manufacturer)
|
||||
|| !MatchSingleProperty(pattern, deviceDescription.manufacturer))
|
||||
return 0;
|
||||
}
|
||||
else if (key == kManufacturerContainsKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.manufacturer)
|
||||
|| !MatchSinglePropertyContains(pattern, deviceDescription.manufacturer))
|
||||
return 0;
|
||||
}
|
||||
else if (key == kProductKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.product)
|
||||
|| !MatchSingleProperty(pattern, deviceDescription.product))
|
||||
return 0;
|
||||
}
|
||||
else if (key == kVersionKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceDescription.version)
|
||||
|| !MatchSingleProperty(pattern, deviceDescription.version))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Capabilities match. Take the key as a path into the JSON
|
||||
// object and match the value found at the given path.
|
||||
|
||||
if (string.IsNullOrEmpty(deviceDescription.capabilities))
|
||||
return 0;
|
||||
|
||||
var graph = new JsonParser(deviceDescription.capabilities);
|
||||
if (!graph.NavigateToProperty(key.ToString()) ||
|
||||
!graph.CurrentPropertyHasValueEqualTo(new JsonParser.JsonValue { type = JsonParser.JsonValueType.Any, anyValue = pattern}))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// All patterns matched. Our score is determined by the number of properties
|
||||
// we matched against.
|
||||
var propertyCountInDescription = GetNumPropertiesIn(deviceDescription);
|
||||
var scorePerProperty = 1.0f / propertyCountInDescription;
|
||||
|
||||
return numPatterns * scorePerProperty;
|
||||
}
|
||||
|
||||
private static bool MatchSingleProperty(object pattern, string value)
|
||||
{
|
||||
// String match.
|
||||
if (pattern is string str)
|
||||
return string.Compare(str, value, StringComparison.OrdinalIgnoreCase) == 0;
|
||||
|
||||
// Regex match.
|
||||
if (pattern is Regex regex)
|
||||
return regex.IsMatch(value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool MatchSinglePropertyContains(object pattern, string value)
|
||||
{
|
||||
// String match.
|
||||
if (pattern is string str)
|
||||
return value.Contains(str, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int GetNumPropertiesIn(InputDeviceDescription description)
|
||||
{
|
||||
var count = 0;
|
||||
if (!string.IsNullOrEmpty(description.interfaceName))
|
||||
count += 1;
|
||||
if (!string.IsNullOrEmpty(description.deviceClass))
|
||||
count += 1;
|
||||
if (!string.IsNullOrEmpty(description.manufacturer))
|
||||
count += 1;
|
||||
if (!string.IsNullOrEmpty(description.product))
|
||||
count += 1;
|
||||
if (!string.IsNullOrEmpty(description.version))
|
||||
count += 1;
|
||||
if (!string.IsNullOrEmpty(description.capabilities))
|
||||
count += 1;
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produce a matcher that matches the given device description verbatim.
|
||||
/// </summary>
|
||||
/// <param name="deviceDescription">A device description.</param>
|
||||
/// <returns>A matcher that matches <paramref name="deviceDescription"/> exactly.</returns>
|
||||
/// <remarks>
|
||||
/// This method can be used to produce a matcher for an existing device description,
|
||||
/// e.g. when writing a layout <see cref="InputControlLayout.Builder"/> that produces
|
||||
/// layouts for devices on the fly.
|
||||
/// </remarks>
|
||||
public static InputDeviceMatcher FromDeviceDescription(InputDeviceDescription deviceDescription)
|
||||
{
|
||||
var matcher = new InputDeviceMatcher();
|
||||
if (!string.IsNullOrEmpty(deviceDescription.interfaceName))
|
||||
matcher = matcher.WithInterface(deviceDescription.interfaceName, false);
|
||||
if (!string.IsNullOrEmpty(deviceDescription.deviceClass))
|
||||
matcher = matcher.WithDeviceClass(deviceDescription.deviceClass, false);
|
||||
if (!string.IsNullOrEmpty(deviceDescription.manufacturer))
|
||||
matcher = matcher.WithManufacturer(deviceDescription.manufacturer, false);
|
||||
if (!string.IsNullOrEmpty(deviceDescription.product))
|
||||
matcher = matcher.WithProduct(deviceDescription.product, false);
|
||||
if (!string.IsNullOrEmpty(deviceDescription.version))
|
||||
matcher = matcher.WithVersion(deviceDescription.version, false);
|
||||
// We don't include capabilities in this conversion.
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation useful for debugging. Lists the
|
||||
/// <see cref="patterns"/> contained in the matcher.
|
||||
/// </summary>
|
||||
/// <returns>A string representation of the matcher.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (empty)
|
||||
return "<empty>";
|
||||
|
||||
var result = string.Empty;
|
||||
foreach (var pattern in m_Patterns)
|
||||
{
|
||||
if (result.Length > 0)
|
||||
result += $",{pattern.Key}={pattern.Value}";
|
||||
else
|
||||
result += $"{pattern.Key}={pattern.Value}";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether this matcher is equivalent to the <paramref name="other"/> matcher.
|
||||
/// </summary>
|
||||
/// <param name="other">Another device matcher.</param>
|
||||
/// <returns>True if the two matchers are equivalent.</returns>
|
||||
/// <remarks>
|
||||
/// Two matchers are equivalent if they contain the same number of patterns and the
|
||||
/// same pattern occurs in each of the matchers. Order of the patterns does not
|
||||
/// matter.
|
||||
/// </remarks>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "False positive.")]
|
||||
public bool Equals(InputDeviceMatcher other)
|
||||
{
|
||||
if (m_Patterns == other.m_Patterns)
|
||||
return true;
|
||||
|
||||
if (m_Patterns == null || other.m_Patterns == null)
|
||||
return false;
|
||||
|
||||
if (m_Patterns.Length != other.m_Patterns.Length)
|
||||
return false;
|
||||
|
||||
// Pattern count matches. Compare pattern by pattern. Order of patterns doesn't matter.
|
||||
for (var i = 0; i < m_Patterns.Length; ++i)
|
||||
{
|
||||
var thisPattern = m_Patterns[i];
|
||||
var foundPattern = false;
|
||||
for (var n = 0; n < m_Patterns.Length; ++n)
|
||||
{
|
||||
var otherPattern = other.m_Patterns[n];
|
||||
if (thisPattern.Key != otherPattern.Key)
|
||||
continue;
|
||||
if (!thisPattern.Value.Equals(otherPattern.Value))
|
||||
return false;
|
||||
foundPattern = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundPattern)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare this matcher to another.
|
||||
/// </summary>
|
||||
/// <param name="obj">A matcher object or <c>null</c>.</param>
|
||||
/// <returns>True if the matcher is equivalent.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceMatcher)"/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
return false;
|
||||
return obj is InputDeviceMatcher matcher && Equals(matcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two matchers for equivalence.
|
||||
/// </summary>
|
||||
/// <param name="left">First device matcher.</param>
|
||||
/// <param name="right">Second device matcher.</param>
|
||||
/// <returns>True if the two matchers are equivalent.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceMatcher)"/>
|
||||
public static bool operator==(InputDeviceMatcher left, InputDeviceMatcher right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two matchers for non-equivalence.
|
||||
/// </summary>
|
||||
/// <param name="left">First device matcher.</param>
|
||||
/// <param name="right">Second device matcher.</param>
|
||||
/// <returns>True if the two matchers are not equivalent.</returns>
|
||||
/// <seealso cref="Equals(InputDeviceMatcher)"/>
|
||||
public static bool operator!=(InputDeviceMatcher left, InputDeviceMatcher right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute a hash code for the device matcher.
|
||||
/// </summary>
|
||||
/// <returns>A hash code for the matcher.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_Patterns != null ? m_Patterns.GetHashCode() : 0;
|
||||
}
|
||||
|
||||
private static readonly InternedString kInterfaceKey = new InternedString("interface");
|
||||
private static readonly InternedString kDeviceClassKey = new InternedString("deviceClass");
|
||||
private static readonly InternedString kManufacturerKey = new InternedString("manufacturer");
|
||||
private static readonly InternedString kManufacturerContainsKey = new InternedString("manufacturerContains");
|
||||
private static readonly InternedString kProductKey = new InternedString("product");
|
||||
private static readonly InternedString kVersionKey = new InternedString("version");
|
||||
|
||||
[Serializable]
|
||||
internal struct MatcherJson
|
||||
{
|
||||
public string @interface;
|
||||
public string[] interfaces;
|
||||
public string deviceClass;
|
||||
public string[] deviceClasses;
|
||||
public string manufacturer;
|
||||
public string manufacturerContains;
|
||||
public string[] manufacturers;
|
||||
public string product;
|
||||
public string[] products;
|
||||
public string version;
|
||||
public string[] versions;
|
||||
public Capability[] capabilities;
|
||||
|
||||
public struct Capability
|
||||
{
|
||||
public string path;
|
||||
public string value;
|
||||
}
|
||||
|
||||
public static MatcherJson FromMatcher(InputDeviceMatcher matcher)
|
||||
{
|
||||
if (matcher.empty)
|
||||
return new MatcherJson();
|
||||
|
||||
var json = new MatcherJson();
|
||||
foreach (var pattern in matcher.m_Patterns)
|
||||
{
|
||||
var key = pattern.Key;
|
||||
var value = pattern.Value.ToString();
|
||||
|
||||
if (key == kInterfaceKey)
|
||||
{
|
||||
if (json.@interface == null)
|
||||
json.@interface = value;
|
||||
else
|
||||
ArrayHelpers.Append(ref json.interfaces, value);
|
||||
}
|
||||
else if (key == kDeviceClassKey)
|
||||
{
|
||||
if (json.deviceClass == null)
|
||||
json.deviceClass = value;
|
||||
else
|
||||
ArrayHelpers.Append(ref json.deviceClasses, value);
|
||||
}
|
||||
else if (key == kManufacturerKey)
|
||||
{
|
||||
if (json.manufacturer == null)
|
||||
json.manufacturer = value;
|
||||
else
|
||||
ArrayHelpers.Append(ref json.manufacturers, value);
|
||||
}
|
||||
else if (key == kProductKey)
|
||||
{
|
||||
if (json.product == null)
|
||||
json.product = value;
|
||||
else
|
||||
ArrayHelpers.Append(ref json.products, value);
|
||||
}
|
||||
else if (key == kVersionKey)
|
||||
{
|
||||
if (json.version == null)
|
||||
json.version = value;
|
||||
else
|
||||
ArrayHelpers.Append(ref json.versions, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayHelpers.Append(ref json.capabilities, new Capability {path = key, value = value});
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public InputDeviceMatcher ToMatcher()
|
||||
{
|
||||
var matcher = new InputDeviceMatcher();
|
||||
|
||||
////TODO: get rid of the piecemeal array allocation and do it in one step
|
||||
|
||||
// Interfaces.
|
||||
if (!string.IsNullOrEmpty(@interface))
|
||||
matcher = matcher.WithInterface(@interface);
|
||||
if (interfaces != null)
|
||||
foreach (var value in interfaces)
|
||||
matcher = matcher.WithInterface(value);
|
||||
|
||||
// Device classes.
|
||||
if (!string.IsNullOrEmpty(deviceClass))
|
||||
matcher = matcher.WithDeviceClass(deviceClass);
|
||||
if (deviceClasses != null)
|
||||
foreach (var value in deviceClasses)
|
||||
matcher = matcher.WithDeviceClass(value);
|
||||
|
||||
// Manufacturer (string or regex)
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
matcher = matcher.WithManufacturer(manufacturer);
|
||||
if (manufacturers != null)
|
||||
foreach (var value in manufacturers)
|
||||
matcher = matcher.WithManufacturer(value);
|
||||
|
||||
// ManufacturerContains (simple string, can occur anywhere in the reported manufacturer string)
|
||||
if (!string.IsNullOrEmpty(manufacturerContains))
|
||||
matcher = matcher.WithManufacturerContains(manufacturerContains);
|
||||
|
||||
// Product.
|
||||
if (!string.IsNullOrEmpty(product))
|
||||
matcher = matcher.WithProduct(product);
|
||||
if (products != null)
|
||||
foreach (var value in products)
|
||||
matcher = matcher.WithProduct(value);
|
||||
|
||||
// Version.
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
matcher = matcher.WithVersion(version);
|
||||
if (versions != null)
|
||||
foreach (var value in versions)
|
||||
matcher = matcher.WithVersion(value);
|
||||
|
||||
// Capabilities.
|
||||
if (capabilities != null)
|
||||
foreach (var value in capabilities)
|
||||
////FIXME: we're turning all values into strings here
|
||||
matcher = matcher.WithCapability(value.path, value.value);
|
||||
|
||||
return matcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bb362cb9a8353d44997049a67efe591
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
internal struct JoystickState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('J', 'O', 'Y');
|
||||
|
||||
[InputControl(name = "trigger", displayName = "Trigger", layout = "Button", usages = new[] { "PrimaryTrigger", "PrimaryAction", "Submit" }, bit = (int)Button.Trigger)]
|
||||
public int buttons;
|
||||
|
||||
[InputControl(displayName = "Stick", layout = "Stick", usage = "Primary2DMotion", processors = "stickDeadzone")]
|
||||
public Vector2 stick;
|
||||
|
||||
public enum Button
|
||||
{
|
||||
// IMPORTANT: Order has to match what is expected by DpadControl.
|
||||
HatSwitchUp,
|
||||
HatSwitchDown,
|
||||
HatSwitchLeft,
|
||||
HatSwitchRight,
|
||||
|
||||
Trigger
|
||||
}
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// A joystick with an arbitrary number of buttons and axes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Joysticks are somewhat hard to classify as there is little commonality other
|
||||
/// than that there is one main stick 2D control and at least one button. From the
|
||||
/// input system perspective, everything that is not a <see cref="Gamepad"/> and
|
||||
/// that has at least one <see cref="stick"/> and one <see cref="trigger"/> control
|
||||
/// is considered a candidate for being a joystick.
|
||||
///
|
||||
/// Optionally, a joystick may also have the ability to <see cref="twist"/>, i.e.
|
||||
/// for the stick to rotate around its own axis, and at least one <see cref="hatswitch"/>.
|
||||
///
|
||||
/// Note that devices based on Joystick may have many more controls. Joystick
|
||||
/// itself only defines a minimum required to separate joysticks as a concept
|
||||
/// from other types of devices.
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(JoystickState), isGenericTypeOfDevice = true)]
|
||||
public class Joystick : InputDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// The primary trigger button of the joystick.
|
||||
/// </summary>
|
||||
/// <value>Control representing the primary trigger button.</value>
|
||||
/// <remarks>
|
||||
/// This is the <see cref="ButtonControl"/> type control on the joystick
|
||||
/// that has the <see cref="CommonUsages.PrimaryTrigger"/> usage.
|
||||
/// </remarks>
|
||||
public ButtonControl trigger { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The 2D axis of the stick itself.
|
||||
/// </summary>
|
||||
/// <value>Control representing the main joystick axis.</value>
|
||||
/// <remarks>
|
||||
/// This is the <see cref="StickControl"/> type control on the joystick
|
||||
/// that has the <see cref="CommonUsages.Primary2DMotion"/> usage.
|
||||
/// </remarks>
|
||||
public StickControl stick { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional control representing the rotation of the stick around its
|
||||
/// own axis (i.e. side-to-side circular motion). If not supported, will be
|
||||
/// <c>null</c>.
|
||||
/// </summary>
|
||||
/// <value>Control representing the twist motion of the joystick.</value>
|
||||
/// <remarks>
|
||||
/// This is the <see cref="AxisControl"/> type control on the joystick
|
||||
/// that has the <see cref="CommonUsages.Twist"/> usage.
|
||||
/// </remarks>
|
||||
public AxisControl twist { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional control representing a four-way "hat switch" on the
|
||||
/// joystick. If not supported, will be <c>null</c>.
|
||||
/// </summary>
|
||||
/// <value>Control representing a hatswitch on the joystick.</value>
|
||||
/// <remarks>
|
||||
/// Hat switches are usually thumb-operated four-way switches that operate
|
||||
/// much like the "d-pad" on a gamepad (see <see cref="Gamepad.dpad"/>).
|
||||
/// If present, this is the <see cref="Vector2Control"/> type control on the
|
||||
/// joystick that has the <see cref="CommonUsages.Hatswitch"/> usage.
|
||||
/// </remarks>
|
||||
public Vector2Control hatswitch { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The joystick that was added or used last. Null if there is none.
|
||||
/// </summary>
|
||||
/// <value>Joystick that was added or used last.</value>
|
||||
/// <remarks>
|
||||
/// See <see cref="InputDevice.MakeCurrent"/> for details about when a device
|
||||
/// is made current.
|
||||
/// </remarks>
|
||||
/// <seealso cref="all"/>
|
||||
public static Joystick current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of joysticks currently connected to the system.
|
||||
/// </summary>
|
||||
/// <value>All currently connected joystick.</value>
|
||||
/// <remarks>
|
||||
/// Does not cause GC allocation.
|
||||
///
|
||||
/// Do <em>not</em> hold on to the value returned by this getter but rather query it whenever
|
||||
/// you need it. Whenever the joystick setup changes, the value returned by this getter
|
||||
/// is invalidated.
|
||||
/// </remarks>
|
||||
/// <seealso cref="current"/>
|
||||
public new static ReadOnlyArray<Joystick> all => new ReadOnlyArray<Joystick>(s_Joysticks, 0, s_JoystickCount);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the joystick has been created but before it is added
|
||||
/// to the system.
|
||||
/// </summary>
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
// Mandatory controls.
|
||||
trigger = GetChildControl<ButtonControl>("{PrimaryTrigger}");
|
||||
stick = GetChildControl<StickControl>("{Primary2DMotion}");
|
||||
|
||||
// Optional controls.
|
||||
twist = TryGetChildControl<AxisControl>("{Twist}");
|
||||
hatswitch = TryGetChildControl<Vector2Control>("{Hatswitch}");
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the joystick the <see cref="current"/> one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called automatically by the input system when a device
|
||||
/// receives input or is added to the system. See <see cref="InputDevice.MakeCurrent"/>
|
||||
/// for details.
|
||||
/// </remarks>
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the joystick is added to the system.
|
||||
/// </summary>
|
||||
protected override void OnAdded()
|
||||
{
|
||||
ArrayHelpers.AppendWithCapacity(ref s_Joysticks, ref s_JoystickCount, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the joystick is removed from the system.
|
||||
/// </summary>
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
|
||||
if (current == this)
|
||||
current = null;
|
||||
|
||||
// Remove from `all`.
|
||||
var index = ArrayHelpers.IndexOfReference(s_Joysticks, this, s_JoystickCount);
|
||||
if (index != -1)
|
||||
ArrayHelpers.EraseAtWithCapacity(s_Joysticks, ref s_JoystickCount, index);
|
||||
else
|
||||
{
|
||||
Debug.Assert(false,
|
||||
$"Joystick {this} seems to not have been added but is being removed (joystick list: {string.Join(", ", all)})"); // Put in else to not allocate on normal path.
|
||||
}
|
||||
}
|
||||
|
||||
private static int s_JoystickCount;
|
||||
private static Joystick[] s_Joysticks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e14a578f61014fccab81ed0f1bc21948
|
||||
timeCreated: 1507753901
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 864a08de8a08473ebf82029d80696449
|
||||
timeCreated: 1506740159
|
||||
@@ -0,0 +1,355 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: option to allow to constrain mouse input to the screen area (i.e. no input once mouse leaves player window)
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Combine a single pointer with buttons and a scroll wheel.
|
||||
/// </summary>
|
||||
// IMPORTANT: State layout must match with MouseInputState in native.
|
||||
[StructLayout(LayoutKind.Explicit, Size = 30)]
|
||||
public struct MouseState : IInputStateTypeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Memory format identifier for MouseState.
|
||||
/// </summary>
|
||||
/// <value>Returns "MOUS".</value>
|
||||
/// <seealso cref="InputStateBlock.format"/>
|
||||
public static FourCC Format => new FourCC('M', 'O', 'U', 'S');
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space position of the mouse in pixels.
|
||||
/// </summary>
|
||||
/// <value>Position of mouse on screen.</value>
|
||||
/// <remarks>
|
||||
/// On Windows, delta originates from RAWINPUT API.
|
||||
/// Note: This value might not update every frame, particularly if your project is running at a high frame rates. This value might also update at a different time than the <see cref="Pointer.delta"/>. If you need a delta value that correlates with position, you should compute it based on the previous position value.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pointer.position"/>
|
||||
[InputControl(usage = "Point", dontReset = true)] // Mouse should stay put when we reset devices.
|
||||
[FieldOffset(0)]
|
||||
public Vector2 position;
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space motion delta of the mouse in pixels.
|
||||
/// </summary>
|
||||
/// <value>Mouse movement.</value>
|
||||
/// <remarks>
|
||||
/// On Windows, delta originates from RAWINPUT API.
|
||||
/// Note: This value might not update every frame, particularly if your project is running at a high frame rates. This value might also update at a different time than the <see cref="Pointer.position"/>. If you need a delta value that correlates with position, you should compute it based on the previous position value.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pointer.delta"/>
|
||||
[InputControl(usage = "Secondary2DMotion", layout = "Delta")]
|
||||
[FieldOffset(8)]
|
||||
public Vector2 delta;
|
||||
|
||||
////REVIEW: have half-axis buttons on the scroll axes? (up, down, left, right)
|
||||
/// <summary>
|
||||
/// Scroll-wheel delta of the mouse.
|
||||
/// </summary>
|
||||
/// <value>Scroll wheel delta.</value>
|
||||
/// <seealso cref="Mouse.scroll"/>
|
||||
[InputControl(displayName = "Scroll", layout = "Delta")]
|
||||
[InputControl(name = "scroll/x", aliases = new[] { "horizontal" }, usage = "ScrollHorizontal", displayName = "Left/Right")]
|
||||
[InputControl(name = "scroll/y", aliases = new[] { "vertical" }, usage = "ScrollVertical", displayName = "Up/Down", shortDisplayName = "Wheel")]
|
||||
[FieldOffset(16)]
|
||||
public Vector2 scroll;
|
||||
|
||||
/// <summary>
|
||||
/// Button mask for which buttons on the mouse are currently pressed.
|
||||
/// </summary>
|
||||
/// <value>Button state mask.</value>
|
||||
/// <seealso cref="MouseButton"/>
|
||||
/// <seealso cref="Mouse.leftButton"/>
|
||||
/// <seealso cref="Mouse.middleButton"/>
|
||||
/// <seealso cref="Mouse.rightButton"/>
|
||||
/// <seealso cref="Mouse.forwardButton"/>
|
||||
/// <seealso cref="Mouse.backButton"/>
|
||||
[InputControl(name = "press", useStateFrom = "leftButton", synthetic = true, usages = new string[0])]
|
||||
[InputControl(name = "leftButton", layout = "Button", bit = (int)MouseButton.Left, usage = "PrimaryAction", displayName = "Left Button", shortDisplayName = "LMB")]
|
||||
[InputControl(name = "rightButton", layout = "Button", bit = (int)MouseButton.Right, usage = "SecondaryAction", displayName = "Right Button", shortDisplayName = "RMB")]
|
||||
[InputControl(name = "middleButton", layout = "Button", bit = (int)MouseButton.Middle, displayName = "Middle Button", shortDisplayName = "MMB")]
|
||||
[InputControl(name = "forwardButton", layout = "Button", bit = (int)MouseButton.Forward, usage = "Forward", displayName = "Forward")]
|
||||
[InputControl(name = "backButton", layout = "Button", bit = (int)MouseButton.Back, usage = "Back", displayName = "Back")]
|
||||
[FieldOffset(24)]
|
||||
// "Park" all the controls that are common to pointers but aren't use for mice such that they get
|
||||
// appended to the end of device state where they will always have default values.
|
||||
////FIXME: InputDeviceBuilder will get fooled and set up an incorrect state layout if we don't force this to VEC2; InputControlLayout will
|
||||
//// "infer" USHT as the format which will then end up with a layout where two 4 byte float controls are "packed" into a 16bit sized parent;
|
||||
//// in other words, setting VEC2 here manually should *not* be necessary
|
||||
[InputControl(name = "pressure", layout = "Axis", usage = "Pressure", offset = InputStateBlock.AutomaticOffset, format = "FLT", sizeInBits = 32)]
|
||||
[InputControl(name = "radius", layout = "Vector2", usage = "Radius", offset = InputStateBlock.AutomaticOffset, format = "VEC2", sizeInBits = 64)]
|
||||
[InputControl(name = "pointerId", layout = "Digital", format = "BIT", sizeInBits = 1, offset = InputStateBlock.AutomaticOffset)] // Will stay at 0.
|
||||
public ushort buttons;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the display that was moused.
|
||||
/// </summary>
|
||||
[InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")]
|
||||
[FieldOffset(26)]
|
||||
public ushort displayIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Number of clicks performed in succession.
|
||||
/// </summary>
|
||||
/// <value>Successive click count.</value>
|
||||
/// <seealso cref="Mouse.clickCount"/>
|
||||
[InputControl(name = "clickCount", layout = "Integer", displayName = "Click Count", synthetic = true)]
|
||||
[FieldOffset(28)]
|
||||
public ushort clickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Set the button mask for the given button.
|
||||
/// </summary>
|
||||
/// <param name="button">Button whose state to set.</param>
|
||||
/// <param name="state">Whether to set the bit on or off.</param>
|
||||
/// <returns>The same MouseState with the change applied.</returns>
|
||||
/// <seealso cref="buttons"/>
|
||||
public MouseState WithButton(MouseButton button, bool state = true)
|
||||
{
|
||||
Debug.Assert((int)button < 16, $"Expected button < 16, so we fit into the 16 bit wide bitmask");
|
||||
var bit = 1U << (int)button;
|
||||
if (state)
|
||||
buttons |= (ushort)bit;
|
||||
else
|
||||
buttons &= (ushort)~bit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="Format"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputStateBlock.format"/>
|
||||
public FourCC format => Format;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Button indices for <see cref="MouseState.buttons"/>.
|
||||
/// </summary>
|
||||
public enum MouseButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Left mouse button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse.leftButton"/>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// Right mouse button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse.rightButton"/>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Middle mouse button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse.middleButton"/>
|
||||
Middle,
|
||||
|
||||
/// <summary>
|
||||
/// Second side button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse.forwardButton"/>
|
||||
Forward,
|
||||
|
||||
/// <summary>
|
||||
/// First side button.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse.backButton"/>
|
||||
Back
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// An input device representing a mouse.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Adds a scroll wheel and a typical 5-button setup with a left, middle, right,
|
||||
/// forward and backward button.
|
||||
///
|
||||
/// To control cursor display and behavior, use <see cref="UnityEngine.Cursor"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
///
|
||||
/// <code>
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.InputSystem;
|
||||
///
|
||||
/// public class ExampleScript : MonoBehaviour
|
||||
/// {
|
||||
/// void Update()
|
||||
/// {
|
||||
/// // If there is a current mouse and the left button was pressed
|
||||
/// if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
|
||||
/// {
|
||||
/// // handle left mouse button being pressed
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="Pointer"/>
|
||||
[InputControlLayout(stateType = typeof(MouseState), isGenericTypeOfDevice = true)]
|
||||
public class Mouse : Pointer, IInputStateCallbackReceiver
|
||||
{
|
||||
/// <summary>
|
||||
/// Control representing horizontal and vertical scroll wheels of a Mouse device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <c>x</c> component corresponds to the horizontal scroll wheel, the
|
||||
/// <c>y</c> component to the vertical scroll wheel. Most mice do not have
|
||||
/// horizontal scroll wheels and will thus only see activity on <c>y</c>.
|
||||
/// </remarks>
|
||||
public DeltaControl scroll { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing left button of a Mouse device.
|
||||
/// </summary>
|
||||
public ButtonControl leftButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing middle button of a Mouse device.
|
||||
/// </summary>
|
||||
public ButtonControl middleButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing right button of a Mouse device.
|
||||
/// </summary>
|
||||
public ButtonControl rightButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing the first side button, often labeled/used as "back", of a Mouse device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On Windows, this corresponds to <c>RI_MOUSE_BUTTON_4</c>.
|
||||
/// </remarks>
|
||||
public ButtonControl backButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing the second side button, often labeled/used as "forward", of a Mouse device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On Windows, this corresponds to <c>RI_MOUSE_BUTTON_5</c>.
|
||||
/// </remarks>
|
||||
public ButtonControl forwardButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Control representing the number of times any of the mouse buttons has been clicked in succession within
|
||||
/// the system-defined click time threshold.
|
||||
/// </summary>
|
||||
public IntegerControl clickCount { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mouse that was added or updated last or null if there is no mouse
|
||||
/// connected to the system.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To set a mouse device as current, use <see cref="Mouse.MakeCurrent"/>.
|
||||
/// </remarks>
|
||||
public new static Mouse current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the mouse becomes the current mouse.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called automatically by the system when there is input on a connected mouse.
|
||||
/// </remarks>
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the mouse is added to the system.
|
||||
/// </summary>
|
||||
protected override void OnAdded()
|
||||
{
|
||||
base.OnAdded();
|
||||
|
||||
if (native && s_PlatformMouseDevice == null)
|
||||
s_PlatformMouseDevice = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the device is removed from the system.
|
||||
/// </summary>
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
internal static Mouse s_PlatformMouseDevice;
|
||||
|
||||
////REVIEW: how should we handle this being called from EditorWindow's? (where the editor window space processor will turn coordinates automatically into editor window space)
|
||||
/// <summary>
|
||||
/// Move the operating system's mouse cursor by performing a device command in a similar way to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396" >
|
||||
/// DeviceIoControl</a> on Windows and <a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/ioctl.2.html#//apple_ref/doc/man/2/ioctl" >ioctl</a>
|
||||
/// on UNIX-like systems.
|
||||
/// </summary>
|
||||
/// <param name="position">New position in player window space.</param>
|
||||
/// <remarks>
|
||||
/// The <see cref="Pointer.position"/> property will not update immediately but rather will update in the
|
||||
/// next input update.
|
||||
/// </remarks>
|
||||
public void WarpCursorPosition(Vector2 position)
|
||||
{
|
||||
var command = WarpMousePositionCommand.Create(position);
|
||||
ExecuteCommand(ref command);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
scroll = GetChildControl<DeltaControl>("scroll");
|
||||
leftButton = GetChildControl<ButtonControl>("leftButton");
|
||||
middleButton = GetChildControl<ButtonControl>("middleButton");
|
||||
rightButton = GetChildControl<ButtonControl>("rightButton");
|
||||
forwardButton = GetChildControl<ButtonControl>("forwardButton");
|
||||
backButton = GetChildControl<ButtonControl>("backButton");
|
||||
displayIndex = GetChildControl<IntegerControl>("displayIndex");
|
||||
clickCount = GetChildControl<IntegerControl>("clickCount");
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="IInputStateCallbackReceiver.OnNextUpdate"/> for the mouse.
|
||||
/// </summary>
|
||||
protected new void OnNextUpdate()
|
||||
{
|
||||
base.OnNextUpdate();
|
||||
InputState.Change(scroll, Vector2.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="IInputStateCallbackReceiver.OnStateEvent"/> for the mouse.
|
||||
/// </summary>
|
||||
/// <param name="eventPtr">Pointer to an <see cref="InputEvent"/>. Makes it easier to
|
||||
/// work with InputEvents and hides the unsafe operations necessary to work with them.
|
||||
/// </param>
|
||||
protected new unsafe void OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
scroll.AccumulateValueInEvent(currentStatePtr, eventPtr);
|
||||
base.OnStateEvent(eventPtr);
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnNextUpdate()
|
||||
{
|
||||
OnNextUpdate();
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
OnStateEvent(eventPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d472007a1b9243feb0043606b73288cd
|
||||
timeCreated: 1507591637
|
||||
@@ -0,0 +1,388 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: expose whether pen actually has eraser and which barrel buttons it has
|
||||
|
||||
////TODO: hook up pointerId in backend to allow identifying different pens
|
||||
|
||||
////REVIEW: have surface distance property to detect how far pen is when hovering?
|
||||
|
||||
////REVIEW: does it make sense to have orientation support for pen, too?
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Default state layout for pen devices.
|
||||
/// </summary>
|
||||
// IMPORTANT: Must match with PenInputState in native.
|
||||
[StructLayout(LayoutKind.Explicit, Size = 36)]
|
||||
public struct PenState : IInputStateTypeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Format code for PenState.
|
||||
/// </summary>
|
||||
/// <value>Returns "PEN ".</value>
|
||||
/// <seealso cref="InputStateBlock.format"/>
|
||||
public static FourCC Format => new FourCC('P', 'E', 'N');
|
||||
|
||||
/// <summary>
|
||||
/// Current screen-space position of the pen.
|
||||
/// </summary>
|
||||
/// <value>Screen-space position.</value>
|
||||
/// <seealso cref="Pointer.position"/>
|
||||
[InputControl(usage = "Point", dontReset = true)]
|
||||
[FieldOffset(0)]
|
||||
public Vector2 position;
|
||||
|
||||
/// <summary>
|
||||
/// Screen-space motion delta.
|
||||
/// </summary>
|
||||
/// <value>Screen-space motion delta.</value>
|
||||
/// <seealso cref="Pointer.delta"/>
|
||||
[InputControl(usage = "Secondary2DMotion", layout = "Delta")]
|
||||
[FieldOffset(8)]
|
||||
public Vector2 delta;
|
||||
|
||||
/// <summary>
|
||||
/// The way the pen is leaned over perpendicular to the tablet surface. X goes [-1..1] left to right
|
||||
/// (with -1 and 1 being completely flush to the surface) and Y goes [-1..1] bottom to top.
|
||||
/// </summary>
|
||||
/// <value>Amount pen is leaning over.</value>
|
||||
/// <seealso cref="Pen.tilt"/>
|
||||
[InputControl(layout = "Vector2", displayName = "Tilt", usage = "Tilt")]
|
||||
[FieldOffset(16)]
|
||||
public Vector2 tilt;
|
||||
|
||||
/// <summary>
|
||||
/// Pressure with which the pen is pressed against the surface. 0 is none, 1 is full pressure.
|
||||
/// </summary>
|
||||
/// <value>Pressure with which the pen is pressed.</value>
|
||||
/// <remarks>
|
||||
/// May go beyond 1 depending on pressure calibration on the system. The maximum pressure point
|
||||
/// may be set to less than the physical maximum pressure point determined by the hardware.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pointer.pressure"/>
|
||||
[InputControl(layout = "Analog", usage = "Pressure", defaultState = 0.0f)]
|
||||
[FieldOffset(24)]
|
||||
public float pressure;
|
||||
|
||||
/// <summary>
|
||||
/// Amount by which the pen is rotated around itself.
|
||||
/// </summary>
|
||||
/// <value>Rotation of the pen around itself.</value>
|
||||
/// <seealso cref="Pen.twist"/>
|
||||
[InputControl(layout = "Axis", displayName = "Twist", usage = "Twist")]
|
||||
[FieldOffset(28)]
|
||||
public float twist;
|
||||
|
||||
/// <summary>
|
||||
/// Button mask for which buttons on the pen are active.
|
||||
/// </summary>
|
||||
/// <value>Bitmask for buttons on the pen.</value>
|
||||
[InputControl(name = "tip", displayName = "Tip", layout = "Button", bit = (int)PenButton.Tip, usage = "PrimaryAction")]
|
||||
[InputControl(name = "press", useStateFrom = "tip", synthetic = true, usages = new string[0])]
|
||||
[InputControl(name = "eraser", displayName = "Eraser", layout = "Button", bit = (int)PenButton.Eraser)]
|
||||
[InputControl(name = "inRange", displayName = "In Range?", layout = "Button", bit = (int)PenButton.InRange, synthetic = true)]
|
||||
[InputControl(name = "barrel1", displayName = "Barrel Button #1", layout = "Button", bit = (int)PenButton.BarrelFirst, alias = "barrelFirst", usage = "SecondaryAction")]
|
||||
[InputControl(name = "barrel2", displayName = "Barrel Button #2", layout = "Button", bit = (int)PenButton.BarrelSecond, alias = "barrelSecond")]
|
||||
[InputControl(name = "barrel3", displayName = "Barrel Button #3", layout = "Button", bit = (int)PenButton.BarrelThird, alias = "barrelThird")]
|
||||
[InputControl(name = "barrel4", displayName = "Barrel Button #4", layout = "Button", bit = (int)PenButton.BarrelFourth, alias = "barrelFourth")]
|
||||
// "Park" unused controls.
|
||||
[InputControl(name = "radius", layout = "Vector2", format = "VEC2", sizeInBits = 64, usage = "Radius", offset = InputStateBlock.AutomaticOffset)]
|
||||
[InputControl(name = "pointerId", layout = "Digital", format = "UINT", sizeInBits = 32, offset = InputStateBlock.AutomaticOffset)] ////TODO: this should be used
|
||||
[FieldOffset(32)]
|
||||
public ushort buttons;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the display that was touched.
|
||||
/// </summary>
|
||||
[InputControl(name = "displayIndex", displayName = "Display Index", layout = "Integer")]
|
||||
[FieldOffset(34)]
|
||||
ushort displayIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Set or unset the bit in <see cref="buttons"/> for the given <paramref name="button"/>.
|
||||
/// </summary>
|
||||
/// <param name="button">Button whose state to set.</param>
|
||||
/// <param name="state">Whether the button is on or off.</param>
|
||||
/// <returns>Same PenState with an updated <see cref="buttons"/> mask.</returns>
|
||||
public PenState WithButton(PenButton button, bool state = true)
|
||||
{
|
||||
Debug.Assert((int)button < 16, $"Expected button < 16, so we fit into the 16 bit wide bitmask");
|
||||
var bit = 1U << (int)button;
|
||||
if (state)
|
||||
buttons |= (ushort)bit;
|
||||
else
|
||||
buttons &= (ushort)~bit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public FourCC format => Format;
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of buttons on a <see cref="Pen"/>.
|
||||
/// </summary>
|
||||
public enum PenButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Button at the tip of a pen.
|
||||
/// </summary>
|
||||
/// <seealso cref="Pen.tip"/>
|
||||
Tip,
|
||||
|
||||
/// <summary>
|
||||
/// Button located end of pen opposite to <see cref="Tip"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Pens do not necessarily have an eraser. If a pen doesn't, the respective button
|
||||
/// does nothing and will always be unpressed.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pen.eraser"/>
|
||||
Eraser,
|
||||
|
||||
/// <summary>
|
||||
/// First button on the side of the pen.
|
||||
/// </summary>
|
||||
/// <see cref="Pen.firstBarrelButton"/>
|
||||
BarrelFirst,
|
||||
|
||||
/// <summary>
|
||||
/// Second button on the side of the pen.
|
||||
/// </summary>
|
||||
/// <seealso cref="Pen.secondBarrelButton"/>
|
||||
BarrelSecond,
|
||||
|
||||
/// <summary>
|
||||
/// Artificial button that indicates whether the pen is in detection range or not.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Range detection may not be supported by a pen/tablet.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Pen.inRange"/>
|
||||
InRange,
|
||||
|
||||
/// <summary>
|
||||
/// Third button on the side of the pen.
|
||||
/// </summary>
|
||||
/// <seealso cref="Pen.thirdBarrelButton"/>
|
||||
BarrelThird,
|
||||
|
||||
/// <summary>
|
||||
/// Fourth button on the side of the pen.
|
||||
/// </summary>
|
||||
/// <see cref="Pen.fourthBarrelButton"/>
|
||||
BarrelFourth,
|
||||
|
||||
/// <summary>
|
||||
/// Synonym for <see cref="BarrelFirst"/>.
|
||||
/// </summary>
|
||||
Barrel1 = BarrelFirst,
|
||||
|
||||
/// <summary>
|
||||
/// Synonym for <see cref="BarrelSecond"/>.
|
||||
/// </summary>
|
||||
Barrel2 = BarrelSecond,
|
||||
|
||||
/// <summary>
|
||||
/// Synonym for <see cref="BarrelThird"/>.
|
||||
/// </summary>
|
||||
Barrel3 = BarrelThird,
|
||||
|
||||
/// <summary>
|
||||
/// Synonym for <see cref="BarrelFourth"/>.
|
||||
/// </summary>
|
||||
Barrel4 = BarrelFourth,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a pen/stylus input device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Unlike mice but like touch, pens are absolute pointing devices moving across a fixed
|
||||
/// surface area.
|
||||
///
|
||||
/// The <see cref="tip"/> acts as a button that is considered pressed as long as the pen is in contact with the
|
||||
/// tablet surface.
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(PenState), isGenericTypeOfDevice = true)]
|
||||
public class Pen : Pointer
|
||||
{
|
||||
////TODO: give the tip and eraser a very low press point
|
||||
/// <summary>
|
||||
/// The tip button of the pen.
|
||||
/// </summary>
|
||||
/// <value>Control representing the tip button.</value>
|
||||
/// <seealso cref="PenButton.Tip"/>
|
||||
public ButtonControl tip { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The eraser button of the pen, i.e. the button on the end opposite to the tip.
|
||||
/// </summary>
|
||||
/// <value>Control representing the eraser button.</value>
|
||||
/// <remarks>
|
||||
/// If the pen does not have an eraser button, this control will still be present
|
||||
/// but will not trigger.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.Eraser"/>
|
||||
public ButtonControl eraser { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button on the side of the pen barrel and located closer to the tip of the pen.
|
||||
/// </summary>
|
||||
/// <value>Control representing the first side button.</value>
|
||||
/// <remarks>
|
||||
/// If the pen does not have barrel buttons, this control will still be present
|
||||
/// but will not trigger.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.BarrelFirst"/>
|
||||
public ButtonControl firstBarrelButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The button on the side of the pen barrel and located closer to the eraser end of the pen.
|
||||
/// </summary>
|
||||
/// <value>Control representing the second side button.</value>
|
||||
/// <remarks>
|
||||
/// If the pen does not have barrel buttons, this control will still be present
|
||||
/// but will not trigger.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.BarrelSecond"/>
|
||||
public ButtonControl secondBarrelButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Third button the side of the pen barrel.
|
||||
/// </summary>
|
||||
/// <value>Control representing the third side button.</value>
|
||||
/// <remarks>
|
||||
/// If the pen does not have a third barrel buttons, this control will still be present
|
||||
/// but will not trigger.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.BarrelThird"/>
|
||||
public ButtonControl thirdBarrelButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fourth button the side of the pen barrel.
|
||||
/// </summary>
|
||||
/// <value>Control representing the fourth side button.</value>
|
||||
/// <remarks>
|
||||
/// If the pen does not have a fourth barrel buttons, this control will still be present
|
||||
/// but will not trigger.
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.BarrelFourth"/>
|
||||
public ButtonControl fourthBarrelButton { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Button control that indicates whether the pen is in range of the tablet surface or not.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a synthetic control (<see cref="InputControl.synthetic"/>).
|
||||
///
|
||||
/// If range detection is not supported by the pen, this button will always be "pressed".
|
||||
/// </remarks>
|
||||
/// <seealso cref="PenButton.InRange"/>
|
||||
public ButtonControl inRange { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Orientation of the pen relative to the tablet surface, i.e. the amount by which it is leaning
|
||||
/// over along the X and Y axis.
|
||||
/// </summary>
|
||||
/// <value>Control presenting the amount the pen is leaning over.</value>
|
||||
/// <remarks>
|
||||
/// X axis goes from [-1..1] left to right with -1 and 1 meaning the pen is flush with the tablet surface. Y axis
|
||||
/// goes from [-1..1] bottom to top.
|
||||
/// </remarks>
|
||||
public Vector2Control tilt { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rotation of the pointer around its own axis. 0 means the pointer is facing away from the user (12 'o clock position)
|
||||
/// and ~1 means the pointer has been rotated clockwise almost one full rotation.
|
||||
/// </summary>
|
||||
/// <value>Control representing the twist of the pen around itself.</value>
|
||||
/// <remarks>
|
||||
/// Twist is generally only supported by pens and even among pens, twist support is rare. An example product that
|
||||
/// supports twist is the Wacom Art Pen.
|
||||
///
|
||||
/// The axis of rotation is the vector facing away from the pointer surface when the pointer is facing straight up
|
||||
/// (i.e. the surface normal of the pointer surface). When the pointer is tilted, the rotation axis is tilted along
|
||||
/// with it.
|
||||
/// </remarks>
|
||||
public AxisControl twist { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The pen that was active or connected last or <c>null</c> if there is no pen.
|
||||
/// </summary>
|
||||
public new static Pen current { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the given pen button.
|
||||
/// </summary>
|
||||
/// <param name="button">Pen button to return.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="button"/> is not a valid pen button.</exception>
|
||||
public ButtonControl this[PenButton button]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case PenButton.Tip: return tip;
|
||||
case PenButton.Eraser: return eraser;
|
||||
case PenButton.BarrelFirst: return firstBarrelButton;
|
||||
case PenButton.BarrelSecond: return secondBarrelButton;
|
||||
case PenButton.BarrelThird: return thirdBarrelButton;
|
||||
case PenButton.BarrelFourth: return fourthBarrelButton;
|
||||
case PenButton.InRange: return inRange;
|
||||
default:
|
||||
throw new InvalidEnumArgumentException(nameof(button), (int)button, typeof(PenButton));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make this the last used pen, i.e. <see cref="current"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called automatically by the system when a pen is added or receives
|
||||
/// input.
|
||||
/// </remarks>
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the pen is removed from the system.
|
||||
/// </summary>
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
tip = GetChildControl<ButtonControl>("tip");
|
||||
eraser = GetChildControl<ButtonControl>("eraser");
|
||||
firstBarrelButton = GetChildControl<ButtonControl>("barrel1");
|
||||
secondBarrelButton = GetChildControl<ButtonControl>("barrel2");
|
||||
thirdBarrelButton = GetChildControl<ButtonControl>("barrel3");
|
||||
fourthBarrelButton = GetChildControl<ButtonControl>("barrel4");
|
||||
inRange = GetChildControl<ButtonControl>("inRange");
|
||||
tilt = GetChildControl<Vector2Control>("tilt");
|
||||
twist = GetChildControl<AxisControl>("twist");
|
||||
displayIndex = GetChildControl<IntegerControl>("displayIndex");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4eac2aa5bc9a4fc1b87308eec2acf8b1
|
||||
timeCreated: 1508374574
|
||||
@@ -0,0 +1,263 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
////TODO: add capabilities indicating whether pressure is supported
|
||||
|
||||
////REVIEW: is there an opportunity to collapse "press" and "pressure" into one? after all, if there's any pressure, isn't the pointer pressed?
|
||||
|
||||
////REVIEW: should "displayIndex" be called "windowIndex"? or be part of a better thought-out multi-display API altogether?
|
||||
|
||||
////REVIEW: add click and clickCount controls directly to Pointer?
|
||||
//// (I gave this a look but in my initial try, found it somewhat difficult to add click detection at the Pointer level due
|
||||
//// to the extra state it involves)
|
||||
|
||||
////REVIEW: should we put lock state directly on Pointer?
|
||||
|
||||
////REVIEW: should pointer IDs be required to be globally unique across pointing devices?
|
||||
////REVIEW: should we create new devices instead of using pointer IDs?
|
||||
|
||||
////FIXME: pointer deltas in EditorWindows need to be Y *down*
|
||||
|
||||
////REVIEW: kill EditorWindowSpace processor and add GetPositionInEditorWindowSpace() and GetDeltaInEditorWindowSpace()?
|
||||
//// (if we do this, every touch control has to get this, too)
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Default state structure for pointer devices.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct PointerState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('P', 'T', 'R');
|
||||
|
||||
uint pointerId;
|
||||
|
||||
/// <summary>
|
||||
/// Position of the pointer in screen space.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[InputControl(layout = "Vector2", displayName = "Position", usage = "Point", processors = "AutoWindowSpace", dontReset = true)]
|
||||
#else
|
||||
[InputControl(layout = "Vector2", displayName = "Position", usage = "Point", dontReset = true)]
|
||||
#endif
|
||||
public Vector2 position;
|
||||
|
||||
////REVIEW: if we have Secondary2DMotion on this, seems like this should be normalized
|
||||
[InputControl(layout = "Delta", displayName = "Delta", usage = "Secondary2DMotion")]
|
||||
public Vector2 delta;
|
||||
|
||||
[InputControl(layout = "Analog", displayName = "Pressure", usage = "Pressure", defaultState = 1f)]
|
||||
public float pressure;
|
||||
|
||||
[InputControl(layout = "Vector2", displayName = "Radius", usage = "Radius")]
|
||||
public Vector2 radius;
|
||||
|
||||
[InputControl(name = "press", displayName = "Press", layout = "Button", format = "BIT", bit = 0)]
|
||||
public ushort buttons;
|
||||
|
||||
[InputControl(name = "displayIndex", layout = "Integer", displayName = "Display Index")]
|
||||
public ushort displayIndex;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for pointer-style devices moving on a 2D screen.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class abstracts over general "pointing" behavior where a pointer is moved across a 2D
|
||||
/// surface. Operating at the <c>Pointer</c> level allows treating <see>Mouse</see>, <see>Pen</see>,
|
||||
/// and <see>Touchscreen</see> all as pointers with a set of shared behaviors.
|
||||
///
|
||||
/// Note that a pointer may have "multi-point" ability as is the case with multi-touch where
|
||||
/// multiple touches represent multiple concurrent "pointers". However, for any pointer device
|
||||
/// with multiple pointers, only one pointer is considered "primary" and drives the pointer
|
||||
/// controls present on the base class.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Mouse"/>
|
||||
/// <seealso cref="Pen"/>
|
||||
/// <seealso cref="Touchscreen"/>
|
||||
[InputControlLayout(stateType = typeof(PointerState), isGenericTypeOfDevice = true)]
|
||||
public class Pointer : InputDevice, IInputStateCallbackReceiver
|
||||
{
|
||||
////REVIEW: shouldn't this be done for every touch position, too?
|
||||
/// <summary>
|
||||
/// The current pointer coordinates in window space.
|
||||
/// </summary>
|
||||
/// <value>Control representing the current position of the pointer on screen.</value>
|
||||
/// <remarks>
|
||||
/// Within player code, the coordinates are in the coordinate space of Unity's <c>Display</c>.
|
||||
///
|
||||
/// Within editor code, the coordinates are in the coordinate space of the current <c>EditorWindow</c>
|
||||
/// This means that if you query the <see cref="Mouse"/> <see cref="position"/> in <c>EditorWindow.OnGUI</c>, for example,
|
||||
/// the returned 2D vector will be in the coordinate space of your local GUI (same as
|
||||
/// <c>Event.mousePosition</c>).
|
||||
/// </remarks>
|
||||
public Vector2Control position { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current window-space motion delta of the pointer.
|
||||
/// </summary>
|
||||
/// <value>Control representing the motion delta of the pointer.</value>
|
||||
/// <remarks>
|
||||
/// Every time a pointer is moved, it generates a motion delta. This control represents
|
||||
/// this motion.
|
||||
///
|
||||
/// Note that some pointers have the ability to generate motion deltas <em>without</em>
|
||||
/// actually changing the position of the pointer. This is the case for <see cref="Mouse"/>
|
||||
/// which even when, for example, bumping up against the edges of the screen or when being
|
||||
/// locked in place, can generate motion. This means that activity on <c>delta</c> is not
|
||||
/// necessarily correlated with activity on <see cref="position"/>.
|
||||
///
|
||||
/// Deltas have two special behaviors attached to them that makes them quite unique
|
||||
/// among input controls.
|
||||
///
|
||||
/// For one, deltas will automatically reset to <c>(0,0)</c> between frames. If, for example,
|
||||
/// the current delta value is <c>(12,8)</c>, then after the next <see cref="InputSystem.Update"/>,
|
||||
/// the delta is automatically set to <c>(0,0)</c>. More precisely, deltas will reset as part
|
||||
/// of <see cref="InputSystem.onBeforeUpdate"/>. This happens every time regardless of whether
|
||||
/// there are pending motion events for the pointer or not. But because it happens in
|
||||
/// <see cref="InputSystem.onBeforeUpdate"/> (that is, <em>before</em> events are processed),
|
||||
/// subsequent motion deltas are incorporated normally.
|
||||
///
|
||||
/// Note that the resetting is visible to <see cref="InputAction"/>s. This means that when
|
||||
/// binding to a delta control from an action that is not using <see cref="InputActionType.PassThrough"/>,
|
||||
/// you will see the action getting cancelled at the start of every frame. With a <c>PassThrough</c>
|
||||
/// actions, you will instead see it perform one extra time with a zero value.
|
||||
///
|
||||
/// The other special behavior of deltas is accumulation. When receiving more than one
|
||||
/// motion update in a frame, deltas will not simply switch from one value to the other
|
||||
/// but instead accumulate them. For example, if two events are received for a pointer
|
||||
/// in a frame and one has a motion delta of <c>(1,1)</c> and the other has a motion delta
|
||||
/// of <c>(2,2)</c>, then once <see cref="InputSystem.Update"/> has finished processing
|
||||
/// events, the value of the delta control will be <c>(3,3)</c> and not <c>(2,2)</c>.
|
||||
///
|
||||
/// Note that just like resetting, accumulation is also visible to <see cref="InputAction"/>s.
|
||||
/// This means that because the delta control changes value twice, the action will trigger
|
||||
/// twice but the value when it is triggered the second time will be <c>(3,3)</c> and
|
||||
/// not <c>(2,2)</c> even though that's the value received from the event.
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputControlExtensions.AccumulateValueInEvent"/>
|
||||
public DeltaControl delta { get; protected set; }
|
||||
|
||||
////REVIEW: move this down to only TouchScreen?
|
||||
/// <summary>
|
||||
/// Window-space radius of the pointer contact with the surface.
|
||||
/// </summary>
|
||||
/// <value>Control representing the horizontal and vertical extents of the pointer contact.</value>
|
||||
/// <remarks>
|
||||
/// Usually, only touch input has radius detection.
|
||||
/// </remarks>
|
||||
/// <seealso cref="TouchControl.radius"/>
|
||||
public Vector2Control radius { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized pressure with which the pointer is currently pressed while in contact with the pointer surface.
|
||||
/// </summary>
|
||||
/// <value>Control representing the pressure with which the pointer is pressed down.</value>
|
||||
/// <remarks>
|
||||
/// This is only meaningful for pointing devices that support pressure. Mice do not, pens usually do, and touch
|
||||
/// usually does on mobile platforms.
|
||||
///
|
||||
/// Note that it is possible for the value to go above 1 even though it is considered normalized. The reason is
|
||||
/// that calibration on the system can put the maximum pressure point below the physically supported maximum value.
|
||||
/// </remarks>
|
||||
public AxisControl pressure { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the pointer is pressed down.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// What this means exactly depends on the nature of the pointer. For mice (<see cref="Mouse"/>), it means
|
||||
/// that the left button is pressed. For pens (<see cref="Pen"/>), it means that the pen tip is touching
|
||||
/// the screen/tablet surface. For touchscreens (<see cref="Touchscreen"/>), it means that there is at least
|
||||
/// one finger touching the screen.
|
||||
/// </remarks>
|
||||
public ButtonControl press { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of the display the Pointer is currently on. This is useful for multiple screen setups.
|
||||
/// This may not be supported on all platforms. When unsupported, this will always produce the index of the primary display i.e. zero.
|
||||
/// <see href="https://docs.unity3d.com/ScriptReference/Display.html"/>
|
||||
/// </summary>
|
||||
public IntegerControl displayIndex { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The pointer that was added or used last by the user or <c>null</c> if there is no pointer
|
||||
/// device connected to the system.
|
||||
/// </summary>
|
||||
/// <value>Currently active <c>Pointer</c> or <c>null</c>.</value>
|
||||
public static Pointer current { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
position = GetChildControl<Vector2Control>("position");
|
||||
delta = GetChildControl<DeltaControl>("delta");
|
||||
radius = GetChildControl<Vector2Control>("radius");
|
||||
pressure = GetChildControl<AxisControl>("pressure");
|
||||
press = GetChildControl<ButtonControl>("press");
|
||||
displayIndex = GetChildControl<IntegerControl>("displayIndex");
|
||||
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the input system advances by one frame.
|
||||
/// </summary>
|
||||
/// <seealso cref="InputSystem.Update"/>
|
||||
protected void OnNextUpdate()
|
||||
{
|
||||
InputState.Change(delta, Vector2.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the pointer receives a state event.
|
||||
/// </summary>
|
||||
/// <param name="eventPtr">The input event.</param>
|
||||
protected unsafe void OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
////FIXME: This stuff makes pointer events too expensive; find a better way.
|
||||
delta.AccumulateValueInEvent(currentStatePtr, eventPtr);
|
||||
InputState.Change(this, eventPtr);
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnNextUpdate()
|
||||
{
|
||||
OnNextUpdate();
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
OnStateEvent(eventPtr);
|
||||
}
|
||||
|
||||
bool IInputStateCallbackReceiver.GetStateOffsetForEvent(InputControl control, InputEventPtr eventPtr, ref uint offset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d806525c0444400588f5a7800297b7b5
|
||||
timeCreated: 1506745710
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6600059bab4cd4a4ab586e5bb07df47b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40cf321da8ee4fc428f65b23423528d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,877 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was auto-generated by com.unity.inputsystem:InputLayoutCodeGenerator
|
||||
// version 1.13.0
|
||||
// from "Mouse" layout
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
// Suppress warnings from local variables for control references
|
||||
// that we don't end up using.
|
||||
#pragma warning disable CS0219
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
internal partial class FastMouse : UnityEngine.InputSystem.Mouse
|
||||
{
|
||||
public const string metadata = "AutoWindowSpace;Vector2;Delta;Button;Axis;Digital;Integer;Mouse;Pointer";
|
||||
public FastMouse()
|
||||
{
|
||||
var builder = this.Setup(30, 10, 2)
|
||||
.WithName("Mouse")
|
||||
.WithDisplayName("Mouse")
|
||||
.WithChildren(0, 14)
|
||||
.WithLayout(new InternedString("Mouse"))
|
||||
.WithStateBlock(new InputStateBlock { format = new FourCC(1297044819), sizeInBits = 392 });
|
||||
|
||||
var kVector2Layout = new InternedString("Vector2");
|
||||
var kDeltaLayout = new InternedString("Delta");
|
||||
var kButtonLayout = new InternedString("Button");
|
||||
var kAxisLayout = new InternedString("Axis");
|
||||
var kDigitalLayout = new InternedString("Digital");
|
||||
var kIntegerLayout = new InternedString("Integer");
|
||||
|
||||
// /Mouse/position
|
||||
var ctrlMouseposition = Initialize_ctrlMouseposition(kVector2Layout, this);
|
||||
|
||||
// /Mouse/delta
|
||||
var ctrlMousedelta = Initialize_ctrlMousedelta(kDeltaLayout, this);
|
||||
|
||||
// /Mouse/scroll
|
||||
var ctrlMousescroll = Initialize_ctrlMousescroll(kDeltaLayout, this);
|
||||
|
||||
// /Mouse/press
|
||||
var ctrlMousepress = Initialize_ctrlMousepress(kButtonLayout, this);
|
||||
|
||||
// /Mouse/leftButton
|
||||
var ctrlMouseleftButton = Initialize_ctrlMouseleftButton(kButtonLayout, this);
|
||||
|
||||
// /Mouse/rightButton
|
||||
var ctrlMouserightButton = Initialize_ctrlMouserightButton(kButtonLayout, this);
|
||||
|
||||
// /Mouse/middleButton
|
||||
var ctrlMousemiddleButton = Initialize_ctrlMousemiddleButton(kButtonLayout, this);
|
||||
|
||||
// /Mouse/forwardButton
|
||||
var ctrlMouseforwardButton = Initialize_ctrlMouseforwardButton(kButtonLayout, this);
|
||||
|
||||
// /Mouse/backButton
|
||||
var ctrlMousebackButton = Initialize_ctrlMousebackButton(kButtonLayout, this);
|
||||
|
||||
// /Mouse/pressure
|
||||
var ctrlMousepressure = Initialize_ctrlMousepressure(kAxisLayout, this);
|
||||
|
||||
// /Mouse/radius
|
||||
var ctrlMouseradius = Initialize_ctrlMouseradius(kVector2Layout, this);
|
||||
|
||||
// /Mouse/pointerId
|
||||
var ctrlMousepointerId = Initialize_ctrlMousepointerId(kDigitalLayout, this);
|
||||
|
||||
// /Mouse/displayIndex
|
||||
var ctrlMousedisplayIndex = Initialize_ctrlMousedisplayIndex(kIntegerLayout, this);
|
||||
|
||||
// /Mouse/clickCount
|
||||
var ctrlMouseclickCount = Initialize_ctrlMouseclickCount(kIntegerLayout, this);
|
||||
|
||||
// /Mouse/position/x
|
||||
var ctrlMousepositionx = Initialize_ctrlMousepositionx(kAxisLayout, ctrlMouseposition);
|
||||
|
||||
// /Mouse/position/y
|
||||
var ctrlMousepositiony = Initialize_ctrlMousepositiony(kAxisLayout, ctrlMouseposition);
|
||||
|
||||
// /Mouse/delta/up
|
||||
var ctrlMousedeltaup = Initialize_ctrlMousedeltaup(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/delta/down
|
||||
var ctrlMousedeltadown = Initialize_ctrlMousedeltadown(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/delta/left
|
||||
var ctrlMousedeltaleft = Initialize_ctrlMousedeltaleft(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/delta/right
|
||||
var ctrlMousedeltaright = Initialize_ctrlMousedeltaright(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/delta/x
|
||||
var ctrlMousedeltax = Initialize_ctrlMousedeltax(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/delta/y
|
||||
var ctrlMousedeltay = Initialize_ctrlMousedeltay(kAxisLayout, ctrlMousedelta);
|
||||
|
||||
// /Mouse/scroll/up
|
||||
var ctrlMousescrollup = Initialize_ctrlMousescrollup(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/scroll/down
|
||||
var ctrlMousescrolldown = Initialize_ctrlMousescrolldown(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/scroll/left
|
||||
var ctrlMousescrollleft = Initialize_ctrlMousescrollleft(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/scroll/right
|
||||
var ctrlMousescrollright = Initialize_ctrlMousescrollright(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/scroll/x
|
||||
var ctrlMousescrollx = Initialize_ctrlMousescrollx(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/scroll/y
|
||||
var ctrlMousescrolly = Initialize_ctrlMousescrolly(kAxisLayout, ctrlMousescroll);
|
||||
|
||||
// /Mouse/radius/x
|
||||
var ctrlMouseradiusx = Initialize_ctrlMouseradiusx(kAxisLayout, ctrlMouseradius);
|
||||
|
||||
// /Mouse/radius/y
|
||||
var ctrlMouseradiusy = Initialize_ctrlMouseradiusy(kAxisLayout, ctrlMouseradius);
|
||||
|
||||
// Usages.
|
||||
builder.WithControlUsage(0, new InternedString("Point"), ctrlMouseposition);
|
||||
builder.WithControlUsage(1, new InternedString("Secondary2DMotion"), ctrlMousedelta);
|
||||
builder.WithControlUsage(2, new InternedString("ScrollHorizontal"), ctrlMousescrollx);
|
||||
builder.WithControlUsage(3, new InternedString("ScrollVertical"), ctrlMousescrolly);
|
||||
builder.WithControlUsage(4, new InternedString("PrimaryAction"), ctrlMouseleftButton);
|
||||
builder.WithControlUsage(5, new InternedString("SecondaryAction"), ctrlMouserightButton);
|
||||
builder.WithControlUsage(6, new InternedString("Forward"), ctrlMouseforwardButton);
|
||||
builder.WithControlUsage(7, new InternedString("Back"), ctrlMousebackButton);
|
||||
builder.WithControlUsage(8, new InternedString("Pressure"), ctrlMousepressure);
|
||||
builder.WithControlUsage(9, new InternedString("Radius"), ctrlMouseradius);
|
||||
|
||||
// Aliases.
|
||||
builder.WithControlAlias(0, new InternedString("horizontal"));
|
||||
builder.WithControlAlias(1, new InternedString("vertical"));
|
||||
|
||||
// Control getters/arrays.
|
||||
this.scroll = ctrlMousescroll;
|
||||
this.leftButton = ctrlMouseleftButton;
|
||||
this.middleButton = ctrlMousemiddleButton;
|
||||
this.rightButton = ctrlMouserightButton;
|
||||
this.backButton = ctrlMousebackButton;
|
||||
this.forwardButton = ctrlMouseforwardButton;
|
||||
this.clickCount = ctrlMouseclickCount;
|
||||
this.position = ctrlMouseposition;
|
||||
this.delta = ctrlMousedelta;
|
||||
this.radius = ctrlMouseradius;
|
||||
this.pressure = ctrlMousepressure;
|
||||
this.press = ctrlMousepress;
|
||||
this.displayIndex = ctrlMousedisplayIndex;
|
||||
ctrlMouseposition.x = ctrlMousepositionx;
|
||||
ctrlMouseposition.y = ctrlMousepositiony;
|
||||
ctrlMousedelta.up = ctrlMousedeltaup;
|
||||
ctrlMousedelta.down = ctrlMousedeltadown;
|
||||
ctrlMousedelta.left = ctrlMousedeltaleft;
|
||||
ctrlMousedelta.right = ctrlMousedeltaright;
|
||||
ctrlMousedelta.x = ctrlMousedeltax;
|
||||
ctrlMousedelta.y = ctrlMousedeltay;
|
||||
ctrlMousescroll.up = ctrlMousescrollup;
|
||||
ctrlMousescroll.down = ctrlMousescrolldown;
|
||||
ctrlMousescroll.left = ctrlMousescrollleft;
|
||||
ctrlMousescroll.right = ctrlMousescrollright;
|
||||
ctrlMousescroll.x = ctrlMousescrollx;
|
||||
ctrlMousescroll.y = ctrlMousescrolly;
|
||||
ctrlMouseradius.x = ctrlMouseradiusx;
|
||||
ctrlMouseradius.y = ctrlMouseradiusy;
|
||||
|
||||
// State offset to control index map.
|
||||
builder.WithStateOffsetToControlIndexMap(new uint[]
|
||||
{
|
||||
32782u, 16809999u, 33587218u, 33587219u, 33587220u, 50364432u, 50364433u, 50364437u, 67141656u, 67141657u
|
||||
, 67141658u, 83918870u, 83918871u, 83918875u, 100664323u, 100664324u, 101188613u, 101712902u, 102237191u, 102761480u
|
||||
, 109068300u, 117456909u, 134250505u, 167804956u, 184582173u, 201327627u
|
||||
});
|
||||
|
||||
builder.WithControlTree(new byte[]
|
||||
{
|
||||
// Control tree nodes as bytes
|
||||
135, 1, 1, 0, 0, 0, 0, 196, 0, 3, 0, 0, 0, 0, 135, 1, 23, 0, 0, 0, 0, 128, 0, 5, 0, 0, 0, 0, 196, 0
|
||||
, 11, 0, 0, 0, 0, 64, 0, 7, 0, 0, 0, 1, 128, 0, 9, 0, 3, 0, 1, 32, 0, 255, 255, 1, 0, 1, 64, 0, 255, 255
|
||||
, 2, 0, 1, 96, 0, 255, 255, 7, 0, 3, 128, 0, 255, 255, 4, 0, 3, 193, 0, 13, 0, 0, 0, 0, 196, 0, 19, 0, 0, 0
|
||||
, 0, 161, 0, 15, 0, 10, 0, 4, 193, 0, 17, 0, 14, 0, 4, 145, 0, 255, 255, 18, 0, 3, 161, 0, 255, 255, 21, 0, 3, 192
|
||||
, 0, 255, 255, 0, 0, 0, 193, 0, 255, 255, 24, 0, 2, 195, 0, 21, 0, 0, 0, 0, 196, 0, 255, 255, 28, 0, 1, 194, 0, 255
|
||||
, 255, 26, 0, 1, 195, 0, 255, 255, 27, 0, 1, 32, 1, 25, 0, 0, 0, 0, 135, 1, 41, 0, 0, 0, 0, 240, 0, 27, 0, 0
|
||||
, 0, 0, 32, 1, 39, 0, 0, 0, 0, 224, 0, 29, 0, 0, 0, 0, 240, 0, 255, 255, 41, 0, 1, 210, 0, 31, 0, 39, 0, 1
|
||||
, 224, 0, 255, 255, 40, 0, 1, 203, 0, 33, 0, 0, 0, 0, 210, 0, 255, 255, 0, 0, 0, 200, 0, 35, 0, 0, 0, 0, 203, 0
|
||||
, 255, 255, 0, 0, 0, 198, 0, 37, 0, 0, 0, 0, 200, 0, 255, 255, 0, 0, 0, 197, 0, 255, 255, 29, 0, 1, 198, 0, 255, 255
|
||||
, 0, 0, 0, 8, 1, 255, 255, 30, 0, 1, 32, 1, 255, 255, 31, 0, 1, 128, 1, 43, 0, 0, 0, 0, 135, 1, 47, 0, 0, 0
|
||||
, 0, 80, 1, 255, 255, 32, 0, 2, 128, 1, 45, 0, 34, 0, 2, 104, 1, 255, 255, 36, 0, 1, 128, 1, 255, 255, 37, 0, 1, 132
|
||||
, 1, 49, 0, 0, 0, 0, 135, 1, 255, 255, 0, 0, 0, 130, 1, 51, 0, 0, 0, 0, 132, 1, 255, 255, 0, 0, 0, 129, 1, 255
|
||||
, 255, 38, 0, 1, 130, 1, 255, 255, 0, 0, 0
|
||||
}, new ushort[]
|
||||
{
|
||||
// Control tree node indicies
|
||||
|
||||
0, 14, 15, 1, 16, 17, 21, 18, 19, 20, 2, 22, 23, 27, 2, 22, 23, 27, 24, 25, 26, 24, 25, 26, 3, 4, 5, 6, 7, 8
|
||||
, 9, 9, 10, 28, 10, 28, 29, 29, 11, 12, 12, 13
|
||||
});
|
||||
|
||||
builder.Finish();
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.Vector2Control Initialize_ctrlMouseposition(InternedString kVector2Layout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseposition = new UnityEngine.InputSystem.Controls.Vector2Control();
|
||||
ctrlMouseposition.Setup()
|
||||
.At(this, 0)
|
||||
.WithParent(parent)
|
||||
.WithChildren(14, 2)
|
||||
.WithName("position")
|
||||
.WithDisplayName("Position")
|
||||
.WithLayout(kVector2Layout)
|
||||
.WithUsages(0, 1)
|
||||
.DontReset(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1447379762),
|
||||
byteOffset = 0,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 64
|
||||
})
|
||||
#if UNITY_EDITOR
|
||||
.WithProcessor<InputProcessor<UnityEngine.Vector2>, UnityEngine.Vector2>(new UnityEngine.InputSystem.Processors.EditorWindowSpaceProcessor())
|
||||
#endif
|
||||
.Finish();
|
||||
return ctrlMouseposition;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.DeltaControl Initialize_ctrlMousedelta(InternedString kDeltaLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedelta = new UnityEngine.InputSystem.Controls.DeltaControl();
|
||||
ctrlMousedelta.Setup()
|
||||
.At(this, 1)
|
||||
.WithParent(parent)
|
||||
.WithChildren(16, 6)
|
||||
.WithName("delta")
|
||||
.WithDisplayName("Delta")
|
||||
.WithLayout(kDeltaLayout)
|
||||
.WithUsages(1, 1)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1447379762),
|
||||
byteOffset = 8,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 64
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedelta;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.DeltaControl Initialize_ctrlMousescroll(InternedString kDeltaLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescroll = new UnityEngine.InputSystem.Controls.DeltaControl();
|
||||
ctrlMousescroll.Setup()
|
||||
.At(this, 2)
|
||||
.WithParent(parent)
|
||||
.WithChildren(22, 6)
|
||||
.WithName("scroll")
|
||||
.WithDisplayName("Scroll")
|
||||
.WithLayout(kDeltaLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1447379762),
|
||||
byteOffset = 16,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 64
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescroll;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMousepress(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousepress = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMousepress.Setup()
|
||||
.At(this, 3)
|
||||
.WithParent(parent)
|
||||
.WithName("press")
|
||||
.WithDisplayName("Press")
|
||||
.WithLayout(kButtonLayout)
|
||||
.IsSynthetic(true)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMousepress;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMouseleftButton(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseleftButton = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMouseleftButton.Setup()
|
||||
.At(this, 4)
|
||||
.WithParent(parent)
|
||||
.WithName("leftButton")
|
||||
.WithDisplayName("Left Button")
|
||||
.WithShortDisplayName("LMB")
|
||||
.WithLayout(kButtonLayout)
|
||||
.WithUsages(4, 1)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMouseleftButton;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMouserightButton(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouserightButton = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMouserightButton.Setup()
|
||||
.At(this, 5)
|
||||
.WithParent(parent)
|
||||
.WithName("rightButton")
|
||||
.WithDisplayName("Right Button")
|
||||
.WithShortDisplayName("RMB")
|
||||
.WithLayout(kButtonLayout)
|
||||
.WithUsages(5, 1)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 1,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMouserightButton;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMousemiddleButton(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousemiddleButton = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMousemiddleButton.Setup()
|
||||
.At(this, 6)
|
||||
.WithParent(parent)
|
||||
.WithName("middleButton")
|
||||
.WithDisplayName("Middle Button")
|
||||
.WithShortDisplayName("MMB")
|
||||
.WithLayout(kButtonLayout)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 2,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMousemiddleButton;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMouseforwardButton(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseforwardButton = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMouseforwardButton.Setup()
|
||||
.At(this, 7)
|
||||
.WithParent(parent)
|
||||
.WithName("forwardButton")
|
||||
.WithDisplayName("Forward")
|
||||
.WithLayout(kButtonLayout)
|
||||
.WithUsages(6, 1)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 3,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMouseforwardButton;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlMousebackButton(InternedString kButtonLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousebackButton = new UnityEngine.InputSystem.Controls.ButtonControl();
|
||||
ctrlMousebackButton.Setup()
|
||||
.At(this, 8)
|
||||
.WithParent(parent)
|
||||
.WithName("backButton")
|
||||
.WithDisplayName("Back")
|
||||
.WithLayout(kButtonLayout)
|
||||
.WithUsages(7, 1)
|
||||
.IsButton(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 24,
|
||||
bitOffset = 4,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.WithMinAndMax(0, 1)
|
||||
.Finish();
|
||||
return ctrlMousebackButton;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousepressure(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousepressure = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousepressure.Setup()
|
||||
.At(this, 9)
|
||||
.WithParent(parent)
|
||||
.WithName("pressure")
|
||||
.WithDisplayName("Pressure")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithUsages(8, 1)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 32,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.WithDefaultState(1)
|
||||
.Finish();
|
||||
return ctrlMousepressure;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.Vector2Control Initialize_ctrlMouseradius(InternedString kVector2Layout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseradius = new UnityEngine.InputSystem.Controls.Vector2Control();
|
||||
ctrlMouseradius.Setup()
|
||||
.At(this, 10)
|
||||
.WithParent(parent)
|
||||
.WithChildren(28, 2)
|
||||
.WithName("radius")
|
||||
.WithDisplayName("Radius")
|
||||
.WithLayout(kVector2Layout)
|
||||
.WithUsages(9, 1)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1447379762),
|
||||
byteOffset = 40,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 64
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMouseradius;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.IntegerControl Initialize_ctrlMousepointerId(InternedString kDigitalLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousepointerId = new UnityEngine.InputSystem.Controls.IntegerControl();
|
||||
ctrlMousepointerId.Setup()
|
||||
.At(this, 11)
|
||||
.WithParent(parent)
|
||||
.WithName("pointerId")
|
||||
.WithDisplayName("pointerId")
|
||||
.WithLayout(kDigitalLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1112101920),
|
||||
byteOffset = 48,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 1
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousepointerId;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.IntegerControl Initialize_ctrlMousedisplayIndex(InternedString kIntegerLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedisplayIndex = new UnityEngine.InputSystem.Controls.IntegerControl();
|
||||
ctrlMousedisplayIndex.Setup()
|
||||
.At(this, 12)
|
||||
.WithParent(parent)
|
||||
.WithName("displayIndex")
|
||||
.WithDisplayName("Display Index")
|
||||
.WithLayout(kIntegerLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1431521364),
|
||||
byteOffset = 26,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 16
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedisplayIndex;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.IntegerControl Initialize_ctrlMouseclickCount(InternedString kIntegerLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseclickCount = new UnityEngine.InputSystem.Controls.IntegerControl();
|
||||
ctrlMouseclickCount.Setup()
|
||||
.At(this, 13)
|
||||
.WithParent(parent)
|
||||
.WithName("clickCount")
|
||||
.WithDisplayName("Click Count")
|
||||
.WithLayout(kIntegerLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1431521364),
|
||||
byteOffset = 28,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 16
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMouseclickCount;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousepositionx(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousepositionx = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousepositionx.Setup()
|
||||
.At(this, 14)
|
||||
.WithParent(parent)
|
||||
.WithName("x")
|
||||
.WithDisplayName("Position X")
|
||||
.WithShortDisplayName("Position X")
|
||||
.WithLayout(kAxisLayout)
|
||||
.DontReset(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 0,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousepositionx;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousepositiony(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousepositiony = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousepositiony.Setup()
|
||||
.At(this, 15)
|
||||
.WithParent(parent)
|
||||
.WithName("y")
|
||||
.WithDisplayName("Position Y")
|
||||
.WithShortDisplayName("Position Y")
|
||||
.WithLayout(kAxisLayout)
|
||||
.DontReset(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 4,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousepositiony;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltaup(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltaup = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMax = 3.402823E+38f };
|
||||
ctrlMousedeltaup.Setup()
|
||||
.At(this, 16)
|
||||
.WithParent(parent)
|
||||
.WithName("up")
|
||||
.WithDisplayName("Delta Up")
|
||||
.WithShortDisplayName("Delta Up")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 12,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltaup;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltadown(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltadown = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMin = -3.402823E+38f, invert = true };
|
||||
ctrlMousedeltadown.Setup()
|
||||
.At(this, 17)
|
||||
.WithParent(parent)
|
||||
.WithName("down")
|
||||
.WithDisplayName("Delta Down")
|
||||
.WithShortDisplayName("Delta Down")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 12,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltadown;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltaleft(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltaleft = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMin = -3.402823E+38f, invert = true };
|
||||
ctrlMousedeltaleft.Setup()
|
||||
.At(this, 18)
|
||||
.WithParent(parent)
|
||||
.WithName("left")
|
||||
.WithDisplayName("Delta Left")
|
||||
.WithShortDisplayName("Delta Left")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 8,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltaleft;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltaright(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltaright = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMax = 3.402823E+38f };
|
||||
ctrlMousedeltaright.Setup()
|
||||
.At(this, 19)
|
||||
.WithParent(parent)
|
||||
.WithName("right")
|
||||
.WithDisplayName("Delta Right")
|
||||
.WithShortDisplayName("Delta Right")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 8,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltaright;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltax(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltax = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousedeltax.Setup()
|
||||
.At(this, 20)
|
||||
.WithParent(parent)
|
||||
.WithName("x")
|
||||
.WithDisplayName("Delta X")
|
||||
.WithShortDisplayName("Delta X")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 8,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltax;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousedeltay(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousedeltay = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousedeltay.Setup()
|
||||
.At(this, 21)
|
||||
.WithParent(parent)
|
||||
.WithName("y")
|
||||
.WithDisplayName("Delta Y")
|
||||
.WithShortDisplayName("Delta Y")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 12,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousedeltay;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrollup(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrollup = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMax = 3.402823E+38f };
|
||||
ctrlMousescrollup.Setup()
|
||||
.At(this, 22)
|
||||
.WithParent(parent)
|
||||
.WithName("up")
|
||||
.WithDisplayName("Scroll Up")
|
||||
.WithShortDisplayName("Scroll Up")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 20,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrollup;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrolldown(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrolldown = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMin = -3.402823E+38f, invert = true };
|
||||
ctrlMousescrolldown.Setup()
|
||||
.At(this, 23)
|
||||
.WithParent(parent)
|
||||
.WithName("down")
|
||||
.WithDisplayName("Scroll Down")
|
||||
.WithShortDisplayName("Scroll Down")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 20,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrolldown;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrollleft(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrollleft = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMin = -3.402823E+38f, invert = true };
|
||||
ctrlMousescrollleft.Setup()
|
||||
.At(this, 24)
|
||||
.WithParent(parent)
|
||||
.WithName("left")
|
||||
.WithDisplayName("Scroll Left")
|
||||
.WithShortDisplayName("Scroll Left")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 16,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrollleft;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrollright(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrollright = new UnityEngine.InputSystem.Controls.AxisControl { clamp = UnityEngine.InputSystem.Controls.AxisControl.Clamp.BeforeNormalize, clampMax = 3.402823E+38f };
|
||||
ctrlMousescrollright.Setup()
|
||||
.At(this, 25)
|
||||
.WithParent(parent)
|
||||
.WithName("right")
|
||||
.WithDisplayName("Scroll Right")
|
||||
.WithShortDisplayName("Scroll Right")
|
||||
.WithLayout(kAxisLayout)
|
||||
.IsSynthetic(true)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 16,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrollright;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrollx(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrollx = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousescrollx.Setup()
|
||||
.At(this, 26)
|
||||
.WithParent(parent)
|
||||
.WithName("x")
|
||||
.WithDisplayName("Scroll Left/Right")
|
||||
.WithShortDisplayName("Scroll Left/Right")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithUsages(2, 1)
|
||||
.WithAliases(0, 1)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 16,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrollx;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMousescrolly(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMousescrolly = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMousescrolly.Setup()
|
||||
.At(this, 27)
|
||||
.WithParent(parent)
|
||||
.WithName("y")
|
||||
.WithDisplayName("Scroll Up/Down")
|
||||
.WithShortDisplayName("Scroll Wheel")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithUsages(3, 1)
|
||||
.WithAliases(1, 1)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 20,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMousescrolly;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMouseradiusx(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseradiusx = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMouseradiusx.Setup()
|
||||
.At(this, 28)
|
||||
.WithParent(parent)
|
||||
.WithName("x")
|
||||
.WithDisplayName("Radius X")
|
||||
.WithShortDisplayName("Radius X")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 40,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMouseradiusx;
|
||||
}
|
||||
|
||||
private UnityEngine.InputSystem.Controls.AxisControl Initialize_ctrlMouseradiusy(InternedString kAxisLayout, InputControl parent)
|
||||
{
|
||||
var ctrlMouseradiusy = new UnityEngine.InputSystem.Controls.AxisControl();
|
||||
ctrlMouseradiusy.Setup()
|
||||
.At(this, 29)
|
||||
.WithParent(parent)
|
||||
.WithName("y")
|
||||
.WithDisplayName("Radius Y")
|
||||
.WithShortDisplayName("Radius Y")
|
||||
.WithLayout(kAxisLayout)
|
||||
.WithStateBlock(new InputStateBlock
|
||||
{
|
||||
format = new FourCC(1179407392),
|
||||
byteOffset = 44,
|
||||
bitOffset = 0,
|
||||
sizeInBits = 32
|
||||
})
|
||||
.Finish();
|
||||
return ctrlMouseradiusy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a390ac9b9af0fb49b702bae14eaed84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
internal partial class FastMouse : IInputStateCallbackReceiver, IEventMerger
|
||||
{
|
||||
protected new void OnNextUpdate()
|
||||
{
|
||||
// Changing these separately seems to not result in much of a difference
|
||||
// compared to just doing an InputState.Change with a complete MouseState.
|
||||
InputState.Change(delta, Vector2.zero, InputState.currentUpdateType);
|
||||
InputState.Change(scroll, Vector2.zero, InputState.currentUpdateType);
|
||||
}
|
||||
|
||||
// For FastMouse, we know that our layout is MouseState so we can just go directly
|
||||
// to memory.
|
||||
|
||||
protected new unsafe void OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
if (eventPtr.type != StateEvent.Type)
|
||||
{
|
||||
base.OnStateEvent(eventPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
var stateEvent = StateEvent.FromUnchecked(eventPtr);
|
||||
if (stateEvent->stateFormat != MouseState.Format)
|
||||
{
|
||||
base.OnStateEvent(eventPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
var newState = *(MouseState*)stateEvent->state;
|
||||
var stateFromDevice = (MouseState*)((byte*)currentStatePtr + m_StateBlock.byteOffset);
|
||||
|
||||
newState.delta += stateFromDevice->delta;
|
||||
newState.scroll += stateFromDevice->scroll;
|
||||
|
||||
InputState.Change(this, ref newState, InputState.currentUpdateType, eventPtr: eventPtr);
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnNextUpdate()
|
||||
{
|
||||
OnNextUpdate();
|
||||
}
|
||||
|
||||
void IInputStateCallbackReceiver.OnStateEvent(InputEventPtr eventPtr)
|
||||
{
|
||||
OnStateEvent(eventPtr);
|
||||
}
|
||||
|
||||
internal static unsafe bool MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr)
|
||||
{
|
||||
if (currentEventPtr.type != StateEvent.Type || nextEventPtr.type != StateEvent.Type)
|
||||
return false;
|
||||
|
||||
var currentEvent = StateEvent.FromUnchecked(currentEventPtr);
|
||||
var nextEvent = StateEvent.FromUnchecked(nextEventPtr);
|
||||
|
||||
if (currentEvent->stateFormat != MouseState.Format || nextEvent->stateFormat != MouseState.Format)
|
||||
return false;
|
||||
|
||||
var currentState = (MouseState*)currentEvent->state;
|
||||
var nextState = (MouseState*)nextEvent->state;
|
||||
|
||||
// if buttons or clickCount changed we need to process it, so don't merge events together
|
||||
if (currentState->buttons != nextState->buttons || currentState->clickCount != nextState->clickCount)
|
||||
return false;
|
||||
|
||||
nextState->delta += currentState->delta;
|
||||
nextState->scroll += currentState->scroll;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IEventMerger.MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr)
|
||||
{
|
||||
return MergeForward(currentEventPtr, nextEventPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c425cd71d0042cbbd24386f4e4b9ec1
|
||||
timeCreated: 1604771146
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30858d79688750445a07107b96c917fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3be39d9a3c9496a9a619d6dc185f5f3
|
||||
timeCreated: 1510272259
|
||||
@@ -0,0 +1,791 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: show remote device IDs in the debugger
|
||||
|
||||
////TODO: remote timestamps need to be translated to local timestamps; doesn't make sense for remote events getting
|
||||
//// processed on the local timeline as is when the originating timeline may be quite different
|
||||
|
||||
////TODO: support actions
|
||||
|
||||
////TODO: support input users
|
||||
|
||||
////TODO: text input events
|
||||
|
||||
////TODO: support remoting of device commands
|
||||
|
||||
////TODO: Reuse memory allocated for messages instead of allocating separately for each message.
|
||||
|
||||
////REVIEW: it seems that the various XXXMsg struct should be public; ATM doesn't seem like working with the message interface is practical
|
||||
|
||||
////REVIEW: the namespacing mechanism for layouts which changes base layouts means that layouts can't be played
|
||||
//// around with on the editor side but will only be changed once they're updated in the player
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the activity and data of an InputManager observable in message form.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can act as both the sender and receiver of these message so the flow is fully bidirectional,
|
||||
/// i.e. the InputManager on either end can mirror its layouts, devices, and events over
|
||||
/// to the other end. This permits streaming input not just from the player to the editor but
|
||||
/// also feeding input from the editor back into the player.
|
||||
///
|
||||
/// Remoting sits entirely on top of the input system as an optional piece of functionality.
|
||||
/// In development players and the editor, we enable it automatically but in non-development
|
||||
/// players it has to be explicitly requested by the user.
|
||||
///
|
||||
/// To see devices and input from players in the editor, open the Input Debugger through
|
||||
/// "Windows >> Input Debugger".
|
||||
/// </remarks>
|
||||
/// <seealso cref="InputSystem.remoting"/>
|
||||
public sealed class InputRemoting : IObservable<InputRemoting.Message>, IObserver<InputRemoting.Message>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of possible types of messages exchanged between two InputRemoting instances.
|
||||
/// </summary>
|
||||
public enum MessageType
|
||||
{
|
||||
Connect,
|
||||
Disconnect,
|
||||
NewLayout,
|
||||
NewDevice,
|
||||
NewEvents,
|
||||
RemoveDevice,
|
||||
RemoveLayout, // Not used ATM.
|
||||
ChangeUsages,
|
||||
StartSending,
|
||||
StopSending,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A message exchanged between two InputRemoting instances.
|
||||
/// </summary>
|
||||
public struct Message
|
||||
{
|
||||
/// <summary>
|
||||
/// For messages coming in, numeric ID of the sender of the message. For messages
|
||||
/// going out, numeric ID of the targeted receiver of the message.
|
||||
/// </summary>
|
||||
public int participantId;
|
||||
public MessageType type;
|
||||
public byte[] data;
|
||||
}
|
||||
|
||||
public bool sending
|
||||
{
|
||||
get => (m_Flags & Flags.Sending) == Flags.Sending;
|
||||
private set
|
||||
{
|
||||
if (value)
|
||||
m_Flags |= Flags.Sending;
|
||||
else
|
||||
m_Flags &= ~Flags.Sending;
|
||||
}
|
||||
}
|
||||
|
||||
internal InputRemoting(InputManager manager, bool startSendingOnConnect = false)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
|
||||
m_LocalManager = manager;
|
||||
|
||||
if (startSendingOnConnect)
|
||||
m_Flags |= Flags.StartSendingOnConnect;
|
||||
|
||||
//when listening for newly added layouts, must filter out ones we've added from remote
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start sending messages for data and activity in the local input system
|
||||
/// to observers.
|
||||
/// </summary>
|
||||
/// <seealso cref="sending"/>
|
||||
/// <seealso cref="StopSending"/>
|
||||
public void StartSending()
|
||||
{
|
||||
if (sending)
|
||||
return;
|
||||
|
||||
////TODO: send events in bulk rather than one-by-one
|
||||
m_LocalManager.onEvent += SendEvent;
|
||||
m_LocalManager.onDeviceChange += SendDeviceChange;
|
||||
m_LocalManager.onLayoutChange += SendLayoutChange;
|
||||
|
||||
sending = true;
|
||||
|
||||
SendInitialMessages();
|
||||
}
|
||||
|
||||
public void StopSending()
|
||||
{
|
||||
if (!sending)
|
||||
return;
|
||||
|
||||
m_LocalManager.onEvent -= SendEvent;
|
||||
m_LocalManager.onDeviceChange -= SendDeviceChange;
|
||||
m_LocalManager.onLayoutChange -= SendLayoutChange;
|
||||
|
||||
sending = false;
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnNext(Message msg)
|
||||
{
|
||||
switch (msg.type)
|
||||
{
|
||||
case MessageType.Connect:
|
||||
ConnectMsg.Process(this);
|
||||
break;
|
||||
case MessageType.Disconnect:
|
||||
DisconnectMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewLayout:
|
||||
NewLayoutMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewDevice:
|
||||
NewDeviceMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.NewEvents:
|
||||
NewEventsMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.ChangeUsages:
|
||||
ChangeUsageMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.RemoveDevice:
|
||||
RemoveDeviceMsg.Process(this, msg);
|
||||
break;
|
||||
case MessageType.StartSending:
|
||||
StartSendingMsg.Process(this);
|
||||
break;
|
||||
case MessageType.StopSending:
|
||||
StopSendingMsg.Process(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnError(Exception error)
|
||||
{
|
||||
}
|
||||
|
||||
void IObserver<Message>.OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<Message> observer)
|
||||
{
|
||||
if (observer == null)
|
||||
throw new ArgumentNullException(nameof(observer));
|
||||
|
||||
var subscriber = new Subscriber {owner = this, observer = observer};
|
||||
ArrayHelpers.Append(ref m_Subscribers, subscriber);
|
||||
|
||||
return subscriber;
|
||||
}
|
||||
|
||||
private void SendInitialMessages()
|
||||
{
|
||||
SendAllGeneratedLayouts();
|
||||
SendAllDevices();
|
||||
}
|
||||
|
||||
private void SendAllGeneratedLayouts()
|
||||
{
|
||||
foreach (var entry in m_LocalManager.m_Layouts.layoutBuilders)
|
||||
SendLayout(entry.Key);
|
||||
}
|
||||
|
||||
private void SendLayout(string layoutName)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
var message = NewLayoutMsg.Create(this, layoutName);
|
||||
if (message != null)
|
||||
Send(message.Value);
|
||||
}
|
||||
|
||||
private void SendAllDevices()
|
||||
{
|
||||
var devices = m_LocalManager.devices;
|
||||
foreach (var device in devices)
|
||||
SendDevice(device);
|
||||
}
|
||||
|
||||
private void SendDevice(InputDevice device)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
// Don't mirror remote devices to other remotes.
|
||||
if (device.remote)
|
||||
return;
|
||||
|
||||
var newDeviceMessage = NewDeviceMsg.Create(device);
|
||||
Send(newDeviceMessage);
|
||||
|
||||
// Send current state. We do this here in this case as the device
|
||||
// may have been added some time ago and thus have already received events.
|
||||
var stateEventMessage = NewEventsMsg.CreateStateEvent(device);
|
||||
Send(stateEventMessage);
|
||||
}
|
||||
|
||||
private unsafe void SendEvent(InputEventPtr eventPtr, InputDevice device)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
////REVIEW: we probably want to have better control over this and allow producing local events
|
||||
//// against remote devices which *are* indeed sent across the wire
|
||||
// Don't send events that came in from remote devices.
|
||||
if (device != null && device.remote)
|
||||
return;
|
||||
|
||||
var message = NewEventsMsg.Create(eventPtr.data, 1);
|
||||
Send(message);
|
||||
}
|
||||
|
||||
private void SendDeviceChange(InputDevice device, InputDeviceChange change)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
// Don't mirror remote devices to other remotes.
|
||||
if (device.remote)
|
||||
return;
|
||||
|
||||
Message msg;
|
||||
switch (change)
|
||||
{
|
||||
case InputDeviceChange.Added:
|
||||
msg = NewDeviceMsg.Create(device);
|
||||
break;
|
||||
case InputDeviceChange.Removed:
|
||||
msg = RemoveDeviceMsg.Create(device);
|
||||
break;
|
||||
case InputDeviceChange.UsageChanged:
|
||||
msg = ChangeUsageMsg.Create(device);
|
||||
break;
|
||||
////FIXME: This creates a double reset event in case the reset itself happens from a reset event that we are also remoting at the same time.
|
||||
case InputDeviceChange.SoftReset:
|
||||
msg = NewEventsMsg.CreateResetEvent(device, false);
|
||||
break;
|
||||
case InputDeviceChange.HardReset:
|
||||
msg = NewEventsMsg.CreateResetEvent(device, true);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
private void SendLayoutChange(string layout, InputControlLayoutChange change)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
// Ignore changes made to layouts that aren't generated. We don't send those over
|
||||
// the wire.
|
||||
if (!m_LocalManager.m_Layouts.IsGeneratedLayout(new InternedString(layout)))
|
||||
return;
|
||||
|
||||
// We're only interested in new generated layouts popping up or existing ones
|
||||
// getting replaced.
|
||||
if (change != InputControlLayoutChange.Added && change != InputControlLayoutChange.Replaced)
|
||||
return;
|
||||
|
||||
var message = NewLayoutMsg.Create(this, layout);
|
||||
if (message != null)
|
||||
Send(message.Value);
|
||||
}
|
||||
|
||||
private void Send(Message msg)
|
||||
{
|
||||
foreach (var subscriber in m_Subscribers)
|
||||
subscriber.observer.OnNext(msg);
|
||||
}
|
||||
|
||||
private int FindOrCreateSenderRecord(int senderId)
|
||||
{
|
||||
// Try to find existing.
|
||||
if (m_Senders != null)
|
||||
{
|
||||
var senderCount = m_Senders.Length;
|
||||
for (var i = 0; i < senderCount; ++i)
|
||||
if (m_Senders[i].senderId == senderId)
|
||||
return i;
|
||||
}
|
||||
|
||||
// Create new.
|
||||
var sender = new RemoteSender
|
||||
{
|
||||
senderId = senderId,
|
||||
};
|
||||
return ArrayHelpers.Append(ref m_Senders, sender);
|
||||
}
|
||||
|
||||
private static InternedString BuildLayoutNamespace(int senderId)
|
||||
{
|
||||
return new InternedString($"Remote::{senderId}");
|
||||
}
|
||||
|
||||
private int FindLocalDeviceId(int remoteDeviceId, int senderIndex)
|
||||
{
|
||||
var localDevices = m_Senders[senderIndex].devices;
|
||||
if (localDevices != null)
|
||||
{
|
||||
var numLocalDevices = localDevices.Length;
|
||||
|
||||
for (var i = 0; i < numLocalDevices; ++i)
|
||||
{
|
||||
if (localDevices[i].remoteId == remoteDeviceId)
|
||||
return localDevices[i].localId;
|
||||
}
|
||||
}
|
||||
|
||||
return InputDevice.InvalidDeviceId;
|
||||
}
|
||||
|
||||
private InputDevice TryGetDeviceByRemoteId(int remoteDeviceId, int senderIndex)
|
||||
{
|
||||
var localId = FindLocalDeviceId(remoteDeviceId, senderIndex);
|
||||
return m_LocalManager.TryGetDeviceById(localId);
|
||||
}
|
||||
|
||||
internal InputManager manager => m_LocalManager;
|
||||
|
||||
private Flags m_Flags;
|
||||
private InputManager m_LocalManager; // Input system we mirror input from and to.
|
||||
private Subscriber[] m_Subscribers; // Receivers we send input to.
|
||||
private RemoteSender[] m_Senders; // Senders we receive input from.
|
||||
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
Sending = 1 << 0,
|
||||
StartSendingOnConnect = 1 << 1
|
||||
}
|
||||
|
||||
// Data we keep about a unique sender that we receive input data
|
||||
// from. We keep track of the layouts and devices we added to
|
||||
// the local system.
|
||||
[Serializable]
|
||||
internal struct RemoteSender
|
||||
{
|
||||
public int senderId;
|
||||
public InternedString[] layouts; // Each item is the unqualified name of the layout (without namespace)
|
||||
public RemoteInputDevice[] devices;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal struct RemoteInputDevice
|
||||
{
|
||||
public int remoteId; // Device ID used by sender.
|
||||
public int localId; // Device ID used by us in local system.
|
||||
|
||||
public InputDeviceDescription description;
|
||||
}
|
||||
|
||||
internal class Subscriber : IDisposable
|
||||
{
|
||||
public InputRemoting owner;
|
||||
public IObserver<Message> observer;
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayHelpers.Erase(ref owner.m_Subscribers, this);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConnectMsg
|
||||
{
|
||||
public static void Process(InputRemoting receiver)
|
||||
{
|
||||
if (receiver.sending)
|
||||
receiver.SendInitialMessages();
|
||||
else if ((receiver.m_Flags & Flags.StartSendingOnConnect) == Flags.StartSendingOnConnect)
|
||||
receiver.StartSending();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StartSendingMsg
|
||||
{
|
||||
public static void Process(InputRemoting receiver)
|
||||
{
|
||||
receiver.StartSending();
|
||||
}
|
||||
}
|
||||
|
||||
private static class StopSendingMsg
|
||||
{
|
||||
public static void Process(InputRemoting receiver)
|
||||
{
|
||||
receiver.StopSending();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveRemoteDevices(int participantId)
|
||||
{
|
||||
var senderIndex = FindOrCreateSenderRecord(participantId);
|
||||
|
||||
// Remove devices added by remote.
|
||||
var devices = m_Senders[senderIndex].devices;
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (var remoteDevice in devices)
|
||||
{
|
||||
var device = m_LocalManager.TryGetDeviceById(remoteDevice.localId);
|
||||
if (device != null)
|
||||
m_LocalManager.RemoveDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayHelpers.EraseAt(ref m_Senders, senderIndex);
|
||||
}
|
||||
|
||||
private static class DisconnectMsg
|
||||
{
|
||||
public static void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
Debug.Log("DisconnectMsg.Process");
|
||||
|
||||
receiver.RemoveRemoteDevices(msg.participantId);
|
||||
receiver.StopSending();
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote input system that there's a new layout.
|
||||
private static class NewLayoutMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public string name;
|
||||
public string layoutJson;
|
||||
public bool isOverride;
|
||||
}
|
||||
|
||||
public static Message? Create(InputRemoting sender, string layoutName)
|
||||
{
|
||||
// Try to load the layout. Ignore the layout if it couldn't
|
||||
// be loaded.
|
||||
InputControlLayout layout;
|
||||
try
|
||||
{
|
||||
layout = sender.m_LocalManager.TryLoadControlLayout(new InternedString(layoutName));
|
||||
if (layout == null)
|
||||
{
|
||||
Debug.Log(string.Format(
|
||||
"Could not find layout '{0}' meant to be sent through remote connection; this should not happen",
|
||||
layoutName));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.Log($"Could not load layout '{layoutName}'; not sending to remote listeners (exception: {exception})");
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = new Data
|
||||
{
|
||||
name = layoutName,
|
||||
layoutJson = layout.ToJson(),
|
||||
isOverride = layout.isOverride
|
||||
};
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewLayout,
|
||||
data = SerializeData(data)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
var senderIndex = receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
|
||||
var internedLayoutName = new InternedString(data.name);
|
||||
receiver.m_LocalManager.RegisterControlLayout(data.layoutJson, data.name, data.isOverride);
|
||||
ArrayHelpers.Append(ref receiver.m_Senders[senderIndex].layouts, internedLayoutName);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote input system that there's a new device.
|
||||
private static class NewDeviceMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public string name;
|
||||
public string layout;
|
||||
public int deviceId;
|
||||
public string[] usages;
|
||||
public InputDeviceDescription description;
|
||||
}
|
||||
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
Debug.Assert(!device.remote, "Device being sent to remotes should be a local device, not a remote one");
|
||||
|
||||
var data = new Data
|
||||
{
|
||||
name = device.name,
|
||||
layout = device.layout,
|
||||
deviceId = device.deviceId,
|
||||
description = device.description,
|
||||
usages = device.usages.Select(x => x.ToString()).ToArray()
|
||||
};
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewDevice,
|
||||
data = SerializeData(data)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
var senderIndex = receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
|
||||
// Make sure we haven't already seen the device.
|
||||
var devices = receiver.m_Senders[senderIndex].devices;
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (var entry in devices)
|
||||
if (entry.remoteId == data.deviceId)
|
||||
{
|
||||
Debug.LogError(string.Format(
|
||||
"Already received device with id {0} (layout '{1}', description '{3}) from remote {2}",
|
||||
data.deviceId,
|
||||
data.layout, msg.participantId, data.description));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create device.
|
||||
InputDevice device;
|
||||
try
|
||||
{
|
||||
////REVIEW: this gives remote devices names the same way that local devices receive them; should we make remote status visible in the name?
|
||||
var internedLayoutName = new InternedString(data.layout);
|
||||
device = receiver.m_LocalManager.AddDevice(internedLayoutName, data.name);
|
||||
device.m_ParticipantId = msg.participantId;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Could not create remote device '{data.description}' with layout '{data.layout}' locally (exception: {exception})");
|
||||
return;
|
||||
}
|
||||
////FIXME: Setting this here like so means none of this is visible during onDeviceChange
|
||||
device.m_Description = data.description;
|
||||
device.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
|
||||
foreach (var usage in data.usages)
|
||||
receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(usage));
|
||||
|
||||
// Remember it.
|
||||
var record = new RemoteInputDevice
|
||||
{
|
||||
remoteId = data.deviceId,
|
||||
localId = device.deviceId,
|
||||
description = data.description,
|
||||
};
|
||||
ArrayHelpers.Append(ref receiver.m_Senders[senderIndex].devices, record);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell remote system there's new input events.
|
||||
private static class NewEventsMsg
|
||||
{
|
||||
public static unsafe Message CreateResetEvent(InputDevice device, bool isHardReset)
|
||||
{
|
||||
var resetEvent = DeviceResetEvent.Create(device.deviceId, isHardReset);
|
||||
return Create((InputEvent*)UnsafeUtility.AddressOf(ref resetEvent), 1);
|
||||
}
|
||||
|
||||
public static unsafe Message CreateStateEvent(InputDevice device)
|
||||
{
|
||||
using (StateEvent.From(device, out var eventPtr))
|
||||
return Create(eventPtr.data, 1);
|
||||
}
|
||||
|
||||
public static unsafe Message Create(InputEvent* events, int eventCount)
|
||||
{
|
||||
// Find total size of event buffer we need.
|
||||
var totalSize = 0u;
|
||||
var eventPtr = new InputEventPtr(events);
|
||||
for (var i = 0; i < eventCount; ++i, eventPtr = eventPtr.Next())
|
||||
totalSize = totalSize.AlignToMultipleOf(4) + eventPtr.sizeInBytes;
|
||||
|
||||
// Copy event data to buffer. Would be nice if we didn't have to do that
|
||||
// but unfortunately we need a byte[] and can't just pass the 'events' IntPtr
|
||||
// directly.
|
||||
var data = new byte[totalSize];
|
||||
fixed(byte* dataPtr = data)
|
||||
{
|
||||
UnsafeUtility.MemCpy(dataPtr, events, totalSize);
|
||||
}
|
||||
|
||||
// Done.
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.NewEvents,
|
||||
data = data
|
||||
};
|
||||
}
|
||||
|
||||
public static unsafe void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
var manager = receiver.m_LocalManager;
|
||||
|
||||
fixed(byte* dataPtr = msg.data)
|
||||
{
|
||||
var dataEndPtr = new IntPtr(dataPtr + msg.data.Length);
|
||||
var eventCount = 0;
|
||||
var eventPtr = new InputEventPtr((InputEvent*)dataPtr);
|
||||
var senderIndex = receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
// Don't use IntPtr.ToInt64() function, on 32 bit systems, the pointer is first converted to Int32 and then casted to Int64
|
||||
// Thus for big pointer value, you might get a negative value even though the pointer value will be less than Int64.MaxValue
|
||||
while ((void*)eventPtr.data < dataEndPtr.ToPointer())
|
||||
{
|
||||
// Patch up device ID to refer to local device and send event.
|
||||
var remoteDeviceId = eventPtr.deviceId;
|
||||
var localDeviceId = receiver.FindLocalDeviceId(remoteDeviceId, senderIndex);
|
||||
eventPtr.deviceId = localDeviceId;
|
||||
|
||||
if (localDeviceId != InputDevice.InvalidDeviceId)
|
||||
{
|
||||
////TODO: add API to send events in bulk rather than one by one
|
||||
manager.QueueEvent(eventPtr);
|
||||
}
|
||||
|
||||
++eventCount;
|
||||
eventPtr = eventPtr.Next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChangeUsageMsg
|
||||
{
|
||||
[Serializable]
|
||||
public struct Data
|
||||
{
|
||||
public int deviceId;
|
||||
public string[] usages;
|
||||
}
|
||||
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
var data = new Data
|
||||
{
|
||||
deviceId = device.deviceId,
|
||||
usages = device.usages.Select(x => x.ToString()).ToArray()
|
||||
};
|
||||
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.ChangeUsages,
|
||||
data = SerializeData(data)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
var senderIndex = receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var data = DeserializeData<Data>(msg.data);
|
||||
|
||||
var device = receiver.TryGetDeviceByRemoteId(data.deviceId, senderIndex);
|
||||
if (device != null)
|
||||
{
|
||||
foreach (var deviceUsage in device.usages)
|
||||
{
|
||||
if (!data.usages.Contains(deviceUsage))
|
||||
receiver.m_LocalManager.RemoveDeviceUsage(device, new InternedString(deviceUsage));
|
||||
}
|
||||
|
||||
foreach (var dataUsage in data.usages)
|
||||
{
|
||||
var internedDataUsage = new InternedString(dataUsage);
|
||||
if (!device.usages.Contains(internedDataUsage))
|
||||
receiver.m_LocalManager.AddDeviceUsage(device, new InternedString(dataUsage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemoveDeviceMsg
|
||||
{
|
||||
public static Message Create(InputDevice device)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
type = MessageType.RemoveDevice,
|
||||
data = BitConverter.GetBytes(device.deviceId)
|
||||
};
|
||||
}
|
||||
|
||||
public static void Process(InputRemoting receiver, Message msg)
|
||||
{
|
||||
var senderIndex = receiver.FindOrCreateSenderRecord(msg.participantId);
|
||||
var remoteDeviceId = BitConverter.ToInt32(msg.data, 0);
|
||||
|
||||
var device = receiver.TryGetDeviceByRemoteId(remoteDeviceId, senderIndex);
|
||||
if (device != null)
|
||||
receiver.m_LocalManager.RemoveDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] SerializeData<TData>(TData data)
|
||||
{
|
||||
var json = JsonUtility.ToJson(data);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
private static TData DeserializeData<TData>(byte[] data)
|
||||
{
|
||||
var json = Encoding.UTF8.GetString(data);
|
||||
return JsonUtility.FromJson<TData>(json);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
// State we want to take across domain reloads. We can only take some of the
|
||||
// state across. Subscriptions will be lost and have to be manually restored.
|
||||
[Serializable]
|
||||
internal struct SerializedState
|
||||
{
|
||||
public int senderId;
|
||||
public RemoteSender[] senders;
|
||||
|
||||
// We can't take these across domain reloads but we want to take them across
|
||||
// InputSystem.Save/Restore.
|
||||
[NonSerialized] public Subscriber[] subscribers;
|
||||
}
|
||||
|
||||
internal SerializedState SaveState()
|
||||
{
|
||||
return new SerializedState
|
||||
{
|
||||
senders = m_Senders,
|
||||
subscribers = m_Subscribers
|
||||
};
|
||||
}
|
||||
|
||||
internal void RestoreState(SerializedState state, InputManager manager)
|
||||
{
|
||||
m_LocalManager = manager;
|
||||
m_Senders = state.senders;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f617c1bc34684f738c0d2cb4d0650f35
|
||||
timeCreated: 1510522987
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using UnityEngine.Networking.PlayerConnection;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
// Transports input remoting messages from and to players. Can be used to
|
||||
// make input on either side fully available on the other side. I.e. player
|
||||
// input can be fully debugged in the editor and editor input can conversely
|
||||
// be fed into the player.
|
||||
//
|
||||
// NOTE: The Unity EditorConnection/PlayerConnection mechanism requires this to
|
||||
// be a ScriptableObject as it will register every listeners as a persistent
|
||||
// one.
|
||||
[Serializable]
|
||||
internal class RemoteInputPlayerConnection :
|
||||
#if UNITY_EDITOR
|
||||
// In the editor, we need to make sure that we get the same instance after domain reloads.
|
||||
// Otherwise, callbacks we have registered before the reload will no longer be valid, because
|
||||
// the object instance they point to will not deserialize to a valid object. So we use a
|
||||
// ScriptableSingleton instance, which fullfills these requirements. In the player, we need to
|
||||
// use a simple ScriptableObject, as ScriptableSingleton is an editor-only class.
|
||||
ScriptableSingleton<RemoteInputPlayerConnection>,
|
||||
#else
|
||||
ScriptableObject,
|
||||
#endif
|
||||
IObserver<InputRemoting.Message>, IObservable<InputRemoting.Message>
|
||||
{
|
||||
public static readonly Guid kNewDeviceMsg = new Guid("fcd9651ded40425995dfa6aeb78f1f1c");
|
||||
public static readonly Guid kNewLayoutMsg = new Guid("fccfec2b7369466d88502a9dd38505f4");
|
||||
public static readonly Guid kNewEventsMsg = new Guid("53546641df1347bc8aa315278a603586");
|
||||
public static readonly Guid kRemoveDeviceMsg = new Guid("e5e299b2d9e44255b8990bb71af8922d");
|
||||
public static readonly Guid kChangeUsagesMsg = new Guid("b9fe706dfc854d7ca109a5e38d7db730");
|
||||
public static readonly Guid kStartSendingMsg = new Guid("0d58e99045904672b3ef34b8797d23cb");
|
||||
public static readonly Guid kStopSendingMsg = new Guid("548716b2534a45369ab0c9323fc8b4a8");
|
||||
|
||||
public void Bind(IEditorPlayerConnection connection, bool isConnected)
|
||||
{
|
||||
if (m_Connection != null)
|
||||
{
|
||||
if (m_Connection == connection)
|
||||
return;
|
||||
throw new InvalidOperationException("Already bound to an IEditorPlayerConnection");
|
||||
}
|
||||
|
||||
// If there's already connections on the given IEditorPlayerConnection,
|
||||
// calling RegisterConnection() will invoke the given callback for every
|
||||
// already existing connection. However, it seems to do so only in the
|
||||
// editor which is why we do the 'isConnected' dance below.
|
||||
connection.RegisterConnection(OnConnected);
|
||||
|
||||
connection.RegisterDisconnection(OnDisconnected);
|
||||
|
||||
connection.Register(kNewDeviceMsg, OnNewDevice);
|
||||
connection.Register(kNewLayoutMsg, OnNewLayout);
|
||||
connection.Register(kNewEventsMsg, OnNewEvents);
|
||||
connection.Register(kRemoveDeviceMsg, OnRemoveDevice);
|
||||
connection.Register(kChangeUsagesMsg, OnChangeUsages);
|
||||
|
||||
connection.Register(kStartSendingMsg, OnStartSending);
|
||||
connection.Register(kStopSendingMsg, OnStopSending);
|
||||
|
||||
m_Connection = connection;
|
||||
|
||||
if (isConnected)
|
||||
OnConnected(0);
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<InputRemoting.Message> observer)
|
||||
{
|
||||
if (observer == null)
|
||||
throw new System.ArgumentNullException(nameof(observer));
|
||||
|
||||
var subscriber = new Subscriber {owner = this, observer = observer};
|
||||
ArrayHelpers.Append(ref m_Subscribers, subscriber);
|
||||
|
||||
if (m_ConnectedIds != null)
|
||||
{
|
||||
foreach (var id in m_ConnectedIds)
|
||||
observer.OnNext(new InputRemoting.Message { type = InputRemoting.MessageType.Connect, participantId = id });
|
||||
}
|
||||
|
||||
return subscriber;
|
||||
}
|
||||
|
||||
////REVIEW: given that the PlayerConnection will connect to the editor regardless, we end up
|
||||
//// on this path whether input remoting is enabled or not
|
||||
private void OnConnected(int id)
|
||||
{
|
||||
if (m_ConnectedIds != null && ArrayHelpers.Contains(m_ConnectedIds, id))
|
||||
return;
|
||||
|
||||
ArrayHelpers.Append(ref m_ConnectedIds, id);
|
||||
|
||||
SendToSubscribers(InputRemoting.MessageType.Connect, new MessageEventArgs {playerId = id});
|
||||
}
|
||||
|
||||
private void OnDisconnected(int id)
|
||||
{
|
||||
if (m_ConnectedIds == null || !ArrayHelpers.Contains(m_ConnectedIds, id))
|
||||
return;
|
||||
|
||||
ArrayHelpers.Erase(ref m_ConnectedIds, id);
|
||||
|
||||
SendToSubscribers(InputRemoting.MessageType.Disconnect, new MessageEventArgs {playerId = id});
|
||||
}
|
||||
|
||||
private void OnNewDevice(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.NewDevice, args);
|
||||
}
|
||||
|
||||
private void OnNewLayout(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.NewLayout, args);
|
||||
}
|
||||
|
||||
private void OnNewEvents(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.NewEvents, args);
|
||||
}
|
||||
|
||||
private void OnRemoveDevice(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.RemoveDevice, args);
|
||||
}
|
||||
|
||||
private void OnChangeUsages(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.ChangeUsages, args);
|
||||
}
|
||||
|
||||
private void OnStartSending(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.StartSending, args);
|
||||
}
|
||||
|
||||
private void OnStopSending(MessageEventArgs args)
|
||||
{
|
||||
SendToSubscribers(InputRemoting.MessageType.StopSending, args);
|
||||
}
|
||||
|
||||
private void SendToSubscribers(InputRemoting.MessageType type, MessageEventArgs args)
|
||||
{
|
||||
if (m_Subscribers == null)
|
||||
return;
|
||||
|
||||
var msg = new InputRemoting.Message
|
||||
{
|
||||
participantId = args.playerId,
|
||||
type = type,
|
||||
data = args.data
|
||||
};
|
||||
|
||||
for (var i = 0; i < m_Subscribers.Length; ++i)
|
||||
m_Subscribers[i].observer.OnNext(msg);
|
||||
}
|
||||
|
||||
void IObserver<InputRemoting.Message>.OnNext(InputRemoting.Message msg)
|
||||
{
|
||||
if (m_Connection == null)
|
||||
return;
|
||||
|
||||
////TODO: this should really be sending to a specific player in the editor (can't
|
||||
//// do that through the IEditorPlayerConnection interface though)
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
case InputRemoting.MessageType.NewDevice:
|
||||
m_Connection.Send(kNewDeviceMsg, msg.data);
|
||||
break;
|
||||
case InputRemoting.MessageType.NewLayout:
|
||||
m_Connection.Send(kNewLayoutMsg, msg.data);
|
||||
break;
|
||||
case InputRemoting.MessageType.NewEvents:
|
||||
m_Connection.Send(kNewEventsMsg, msg.data);
|
||||
break;
|
||||
case InputRemoting.MessageType.ChangeUsages:
|
||||
m_Connection.Send(kChangeUsagesMsg, msg.data);
|
||||
break;
|
||||
case InputRemoting.MessageType.RemoveDevice:
|
||||
m_Connection.Send(kRemoveDeviceMsg, msg.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IObserver<InputRemoting.Message>.OnError(Exception error)
|
||||
{
|
||||
}
|
||||
|
||||
void IObserver<InputRemoting.Message>.OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
[SerializeField] private IEditorPlayerConnection m_Connection;
|
||||
[NonSerialized] private Subscriber[] m_Subscribers;
|
||||
[SerializeField] private int[] m_ConnectedIds;
|
||||
|
||||
private class Subscriber : IDisposable
|
||||
{
|
||||
public RemoteInputPlayerConnection owner;
|
||||
public IObserver<InputRemoting.Message> observer;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayHelpers.Erase(ref owner.m_Subscribers, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 153c0ac2dec541bc824175008b97615d
|
||||
timeCreated: 1507603633
|
||||
@@ -0,0 +1,697 @@
|
||||
using System;
|
||||
using UnityEngine.InputSystem.Controls;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: make the sensors return values through their device
|
||||
//// (e.g. GravitySensor should itself be an InputControl returning a Vector3 value which is the gravity value)
|
||||
|
||||
////REVIEW: Is there a better way than having all the sensor classes?
|
||||
|
||||
namespace UnityEngine.InputSystem.LowLevel
|
||||
{
|
||||
internal struct AccelerometerState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('A', 'C', 'C', 'L');
|
||||
|
||||
[InputControl(displayName = "Acceleration", processors = "CompensateDirection", noisy = true)]
|
||||
public Vector3 acceleration;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
|
||||
internal struct GyroscopeState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('G', 'Y', 'R', 'O');
|
||||
|
||||
[InputControl(displayName = "Angular Velocity", processors = "CompensateDirection", noisy = true)]
|
||||
public Vector3 angularVelocity;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
|
||||
internal struct GravityState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('G', 'R', 'V', ' ');
|
||||
|
||||
[InputControl(displayName = "Gravity", processors = "CompensateDirection", noisy = true)]
|
||||
public Vector3 gravity;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
|
||||
internal struct AttitudeState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('A', 'T', 'T', 'D');
|
||||
|
||||
[InputControl(displayName = "Attitude", processors = "CompensateRotation", noisy = true)]
|
||||
public Quaternion attitude;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
|
||||
internal struct LinearAccelerationState : IInputStateTypeInfo
|
||||
{
|
||||
public static FourCC kFormat => new FourCC('L', 'A', 'A', 'C');
|
||||
|
||||
[InputControl(displayName = "Acceleration", processors = "CompensateDirection", noisy = true)]
|
||||
public Vector3 acceleration;
|
||||
|
||||
public FourCC format => kFormat;
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine.InputSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class representing any sensor kind of input device.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sensors represent device environmental sensors, such as <see cref="Accelerometer"/>s, <see cref="Gyroscope"/>s,
|
||||
/// <see cref="GravitySensor"/>s and others.
|
||||
///
|
||||
/// Unlike other devices, sensor devices usually start out in a disabled state in order to reduce energy
|
||||
/// consumption (i.e. preserve battery life) when the sensors are not in fact used. To enable a specific sensor,
|
||||
/// call <see cref="InputSystem.EnableDevice"/> on the device instance.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Enable the gyroscope.
|
||||
/// InputSystem.EnableDevice(Gyroscope.current);
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// Sensors are usually sampled automatically by the platform at regular intervals. For example, if a sensor
|
||||
/// is sampled at 50Hz, the platform will queue an event with an update at a rate of roughly 50 events per
|
||||
/// second. The default sampling rate for a sensor is usually platform-specific. A custom sampling frequency
|
||||
/// can be set through <see cref="samplingFrequency"/> but be aware that there may be limitations for how fast
|
||||
/// a given sensor can be sampled.
|
||||
/// </remarks>
|
||||
[InputControlLayout(isGenericTypeOfDevice = true)]
|
||||
public class Sensor : InputDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// The frequency (in Hertz) at which the underlying sensor will be refreshed and at which update
|
||||
/// events for it will be queued.
|
||||
/// </summary>
|
||||
/// <value>Times per second at which the sensor is refreshed.</value>
|
||||
/// <remarks>
|
||||
/// Note that when setting sampling frequencies, there may be limits on the range of frequencies
|
||||
/// supported by the underlying hardware/platform.
|
||||
///
|
||||
/// To support querying sampling frequencies, a sensor device must implement <see cref="QuerySamplingFrequencyCommand"/>.
|
||||
/// To support setting frequencies, it must implemenet <see cref="SetSamplingFrequencyCommand"/>.
|
||||
/// </remarks>
|
||||
/// <exception cref="NotSupportedException">Thrown when reading the property and the underlying
|
||||
/// sensor does not support querying of sampling frequencies.</exception>
|
||||
public float samplingFrequency
|
||||
{
|
||||
get
|
||||
{
|
||||
var command = QuerySamplingFrequencyCommand.Create();
|
||||
if (ExecuteCommand(ref command) >= 0)
|
||||
return command.frequency;
|
||||
throw new NotSupportedException($"Device '{this}' does not support querying sampling frequency");
|
||||
}
|
||||
set
|
||||
{
|
||||
////REVIEW: should this throw NotSupportedException, too?
|
||||
var command = SetSamplingFrequencyCommand.Create(value);
|
||||
ExecuteCommand(ref command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing an accelerometer sensor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An accelerometer let's you measure the acceleration of a device, and can be useful to control content by moving a device around.
|
||||
/// Note that the accelerometer will report the acceleration measured on a device both due to moving the device around, and due gravity
|
||||
/// pulling the device down. You can use <see cref="GravitySensor"/> and <see cref="LinearAccelerationSensor"/> to get decoupled values
|
||||
/// for these.
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// class MyBehavior : MonoBehaviour
|
||||
/// {
|
||||
/// protected void OnEnable()
|
||||
/// {
|
||||
/// // All sensors start out disabled so they have to manually be enabled first.
|
||||
/// InputSystem.EnableDevice(Accelerometer.current);
|
||||
/// }
|
||||
///
|
||||
/// protected void OnDisable()
|
||||
/// {
|
||||
/// InputSystem.DisableDevice(Accelerometer.current);
|
||||
/// }
|
||||
///
|
||||
/// protected void Update()
|
||||
/// {
|
||||
/// var acceleration = Accelerometer.current.acceleration.ReadValue();
|
||||
/// //...
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(AccelerometerState))]
|
||||
public class Accelerometer : Sensor
|
||||
{
|
||||
public Vector3Control acceleration { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The accelerometer that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current accelerometer or <c>null</c>.</value>
|
||||
public static Accelerometer current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
acceleration = GetChildControl<Vector3Control>("acceleration");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing a gyroscope sensor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A gyroscope lets you measure the angular velocity of a device, and can be useful to control content by rotating a device.
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(GyroscopeState))]
|
||||
public class Gyroscope : Sensor
|
||||
{
|
||||
public Vector3Control angularVelocity { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The gyroscope that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current gyroscope or <c>null</c>.</value>
|
||||
public static Gyroscope current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
angularVelocity = GetChildControl<Vector3Control>("angularVelocity");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing a gravity sensor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A gravity sensor let's you determine the direction of the gravity vector relative to a device, and can be useful to control content by device orientation.
|
||||
/// This is usually derived from a hardware <see cref="Accelerometer"/>, by subtracting the effect of linear acceleration (see <see cref="LinearAccelerationSensor"/>).
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(GravityState), displayName = "Gravity")]
|
||||
public class GravitySensor : Sensor
|
||||
{
|
||||
public Vector3Control gravity { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The gravity sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current gravity sensor or <c>null</c>.</value>
|
||||
public static GravitySensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
gravity = GetChildControl<Vector3Control>("gravity");
|
||||
base.FinishSetup();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
//// REVIEW: Is this name good enough, possible other name RotationVector, here's how Android docs describe it. "A rotation vector sensor reports the orientation of the device relative to the East-North-Up coordinates frame."
|
||||
//// This is the same as https://docs.unity3d.com/ScriptReference/Gyroscope-attitude.html
|
||||
/// <summary>
|
||||
/// Input device representing an attitude sensor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An attitude sensor let's you determine the orientation of a device, and can be useful to control content by rotating a device.
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(AttitudeState), displayName = "Attitude")]
|
||||
public class AttitudeSensor : Sensor
|
||||
{
|
||||
public QuaternionControl attitude { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The attitude sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current attitude sensor or <c>null</c>.</value>
|
||||
public static AttitudeSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
attitude = GetChildControl<QuaternionControl>("attitude");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing linear acceleration affecting the device playing the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An accelerometer let's you measure the acceleration of a device, and can be useful to control content by moving a device around.
|
||||
/// Linear acceleration is the acceleration of a device unaffected by gravity forces.
|
||||
/// This is usually derived from a hardware <see cref="Accelerometer"/>, by subtracting the effect of gravity (see <see cref="GravitySensor"/>).
|
||||
/// </remarks>
|
||||
[InputControlLayout(stateType = typeof(LinearAccelerationState), displayName = "Linear Acceleration")]
|
||||
public class LinearAccelerationSensor : Sensor
|
||||
{
|
||||
public Vector3Control acceleration { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The linear acceleration sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current linear acceleration sensor or <c>null</c>.</value>
|
||||
public static LinearAccelerationSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
acceleration = GetChildControl<Vector3Control>("acceleration");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the magnetic field affecting the device playing the content.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Magnetic Field")]
|
||||
public class MagneticFieldSensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Strength of the magnetic field reported by the sensor.
|
||||
/// </summary>
|
||||
/// <value>Control representing the strength of the magnetic field.</value>
|
||||
/// <remarks>
|
||||
/// Values are in micro-Tesla (uT) and measure the ambient magnetic field in the X, Y and Z axis.
|
||||
/// </remarks>
|
||||
[InputControl(displayName = "Magnetic Field", noisy = true)]
|
||||
public Vector3Control magneticField { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The linear acceleration sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current linear acceleration sensor or <c>null</c>.</value>
|
||||
public static MagneticFieldSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
magneticField = GetChildControl<Vector3Control>("magneticField");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the ambient light measured by the device playing the content.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Light")]
|
||||
public class LightSensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Light level in SI lux units.
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Light Level", noisy = true)]
|
||||
public AxisControl lightLevel { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The light sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current light sensor or <c>null</c>.</value>
|
||||
public static LightSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
lightLevel = GetChildControl<AxisControl>("lightLevel");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the atmospheric pressure measured by the device playing the content.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Pressure")]
|
||||
public class PressureSensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Atmospheric pressure in hPa (millibar).
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Atmospheric Pressure", noisy = true)]
|
||||
public AxisControl atmosphericPressure { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The pressure sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current pressure sensor or <c>null</c>.</value>
|
||||
public static PressureSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
atmosphericPressure = GetChildControl<AxisControl>("atmosphericPressure");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the proximity of the device playing the content to the user.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The proximity sensor is usually used by phones to determine if the user is holding the phone to their ear or not.
|
||||
/// </remarks>
|
||||
[InputControlLayout(displayName = "Proximity")]
|
||||
public class ProximitySensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Proximity sensor distance measured in centimeters.
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Distance", noisy = true)]
|
||||
public AxisControl distance { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The proximity sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current proximity sensor or <c>null</c>.</value>
|
||||
public static ProximitySensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
distance = GetChildControl<AxisControl>("distance");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the ambient air humidity measured by the device playing the content.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Humidity")]
|
||||
public class HumiditySensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative ambient air humidity in percent.
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Relative Humidity", noisy = true)]
|
||||
public AxisControl relativeHumidity { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The humidity sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current humidity sensor or <c>null</c>.</value>
|
||||
public static HumiditySensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
relativeHumidity = GetChildControl<AxisControl>("relativeHumidity");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the ambient air temperature measured by the device playing the content.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Ambient Temperature")]
|
||||
public class AmbientTemperatureSensor : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// Temperature in degree Celsius.
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Ambient Temperature", noisy = true)]
|
||||
public AxisControl ambientTemperature { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ambient temperature sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current ambient temperature sensor or <c>null</c>.</value>
|
||||
public static AmbientTemperatureSensor current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
ambientTemperature = GetChildControl<AxisControl>("ambientTemperature");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Input device representing the foot steps taken by the user as measured by the device playing the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// On iOS, access to the step counter must be enabled via <see cref="InputSettings.iOSSettings.motionUsage"/>.
|
||||
/// </remarks>
|
||||
[InputControlLayout(displayName = "Step Counter")]
|
||||
public class StepCounter : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of steps taken by the user since the last reboot while activated.
|
||||
/// </summary>
|
||||
[InputControl(displayName = "Step Counter", noisy = true)]
|
||||
public IntegerControl stepCounter { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The step counter that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current step counter or <c>null</c>.</value>
|
||||
public static StepCounter current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
stepCounter = GetChildControl<IntegerControl>("stepCounter");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hinge angle sensor.
|
||||
/// This sensor is usually available on foldable devices.
|
||||
/// Note: The step resolution for angle is device dependentent, on Android you can query the sensor resolution by querying device capabilities.
|
||||
/// </summary>
|
||||
[InputControlLayout(displayName = "Hinge Angle")]
|
||||
public class HingeAngle : Sensor
|
||||
{
|
||||
/// <summary>
|
||||
/// The angle in degrees on how much the device is unfolded.
|
||||
/// </summary>
|
||||
/// <value>0 means fully folded, 180 means fully unfolded.</value>
|
||||
public AxisControl angle { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The hinge angle sensor that was last added or had activity last.
|
||||
/// </summary>
|
||||
/// <value>Current hinge angle sensor or <c>null</c>.</value>
|
||||
public static HingeAngle current { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void MakeCurrent()
|
||||
{
|
||||
base.MakeCurrent();
|
||||
current = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnRemoved()
|
||||
{
|
||||
base.OnRemoved();
|
||||
if (current == this)
|
||||
current = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FinishSetup()
|
||||
{
|
||||
angle = GetChildControl<AxisControl>("angle");
|
||||
base.FinishSetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2064cdc23a1f4ec2a5b9b345e46960ed
|
||||
timeCreated: 1511082527
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ade00164ed474391ba591b362f77149f
|
||||
timeCreated: 1507173591
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user