using System; using System.Runtime.InteropServices; using Unity.Collections; using UnityEngine.InputSystem.Utilities; using Unity.Collections.LowLevel.Unsafe; namespace UnityEngine.InputSystem.LowLevel { /// /// A complete state snapshot for an entire input device. /// /// /// This is a variable-sized event. /// [StructLayout(LayoutKind.Explicit, Size = InputEvent.kBaseEventSize + 4 + kStateDataSizeToSubtract, Pack = 1)] public unsafe struct StateEvent : IInputEventTypeInfo { public const int Type = 0x53544154; // 'STAT' internal const int kStateDataSizeToSubtract = 1; [FieldOffset(0)] public InputEvent baseEvent; /// /// Type code for the state stored in the event. /// [FieldOffset(InputEvent.kBaseEventSize)] public FourCC stateFormat; [FieldOffset(InputEvent.kBaseEventSize + sizeof(int))] internal fixed byte stateData[kStateDataSizeToSubtract]; // Variable-sized. public uint stateSizeInBytes => baseEvent.sizeInBytes - (InputEvent.kBaseEventSize + sizeof(int)); public void* state { get { fixed(byte* data = stateData) { return data; } } } public InputEventPtr ToEventPtr() { fixed(StateEvent * ptr = &this) { return new InputEventPtr((InputEvent*)ptr); } } public FourCC typeStatic => Type; /// /// Retrieve the state stored in the event. /// /// Type of state expected to be stored in the event. /// must match . /// Copy of the state stored in the event. /// does not match /// of . /// /// The event may contain less or more data than what is found in the struct. Only the data found in the event /// is copied. The remainder of the struct is left at default values. /// /// public TState GetState() where TState : struct, IInputStateTypeInfo { var result = default(TState); if (stateFormat != result.format) throw new InvalidOperationException($"Expected state format '{result.format}' but got '{stateFormat}' instead"); UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref result), state, Math.Min(stateSizeInBytes, UnsafeUtility.SizeOf())); return result; } /// /// Retrieve the state stored in the event. /// /// Type of state expected to be stored in the event. /// must match . /// A pointer to an input event. The pointer is checked for null and /// for whether the type of event it refers to is indeed a StateEvent. /// Copy of the state stored in the event. /// /// The event may contain less or more data than what is found in the struct. Only the data found in the event /// is copied. The remainder of the struct is left at default values. /// /// does not match /// of . /// is default(InputEventPtr). /// does not refer to a StateEvent. /// public static TState GetState(InputEventPtr ptr) where TState : struct, IInputStateTypeInfo { return From(ptr)->GetState(); } public static int GetEventSizeWithPayload() where TState : struct { return UnsafeUtility.SizeOf() + InputEvent.kBaseEventSize + sizeof(int); } /// /// Return the given as a StateEvent pointer. /// /// A pointer to an input event. The pointer is checked for null and /// for whether the type of event it refers to is indeed a StateEvent. /// Pointer converted to a StateEvent pointer. /// is default(InputEventPtr). /// does not refer to a StateEvent. public static StateEvent* From(InputEventPtr ptr) { if (!ptr.valid) throw new ArgumentNullException(nameof(ptr)); if (!ptr.IsA()) throw new InvalidCastException($"Cannot cast event with type '{ptr.type}' into StateEvent"); return FromUnchecked(ptr); } internal static StateEvent* FromUnchecked(InputEventPtr ptr) { return (StateEvent*)ptr.data; } /// /// Read the current state of and create a state event from it. /// /// Device to grab the state from. Must be a device that has been added to the system. /// Receives a pointer to the newly created state event. /// Which native allocator to allocate memory for the event from. By default, the buffer is /// allocated as temporary memory (. Note that this means the buffer will not be valid /// past the current frame. Use if the buffer for the state event is meant to /// persist for longer. /// Buffer of unmanaged memory allocated for the event. /// has not been added to the system. /// is null. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")] public static NativeArray From(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp) { return From(device, out eventPtr, allocator, useDefaultState: false); } /// /// Create a state event for the given and copy the default state of the device /// into the event. /// /// Device to create a state event for. Must be a device that has been added to the system. /// Receives a pointer to the newly created state event. /// Which native allocator to allocate memory for the event from. By default, the buffer is /// allocated as temporary memory (. Note that this means the buffer will not be valid /// past the current frame. Use if the buffer for the state event is meant to /// persist for longer. /// Buffer of unmanaged memory allocated for the event. /// has not been added to the system. /// is null. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")] public static NativeArray FromDefaultStateFor(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp) { return From(device, out eventPtr, allocator, useDefaultState: true); } private static NativeArray From(InputDevice device, out InputEventPtr eventPtr, Allocator allocator, bool useDefaultState) { if (device == null) throw new ArgumentNullException(nameof(device)); if (!device.added) throw new ArgumentException($"Device '{device}' has not been added to system", nameof(device)); var stateFormat = device.m_StateBlock.format; var stateSize = device.m_StateBlock.alignedSizeInBytes; var stateOffset = device.m_StateBlock.byteOffset; var statePtr = (byte*)(useDefaultState ? device.defaultStatePtr : device.currentStatePtr) + (int)stateOffset; var eventSize = InputEvent.kBaseEventSize + sizeof(int) + stateSize; var buffer = new NativeArray((int)eventSize.AlignToMultipleOf(4), allocator); var stateEventPtr = (StateEvent*)buffer.GetUnsafePtr(); stateEventPtr->baseEvent = new InputEvent(Type, (int)eventSize, device.deviceId, InputRuntime.s_Instance.currentTime); stateEventPtr->stateFormat = stateFormat; UnsafeUtility.MemCpy(stateEventPtr->state, statePtr, stateSize); eventPtr = stateEventPtr->ToEventPtr(); return buffer; } } }