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; public static int GetEventSizeWithPayload() where TState : struct { return UnsafeUtility.SizeOf() + InputEvent.kBaseEventSize + sizeof(int); } 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 (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) { 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*)device.currentStatePtr + (int)stateOffset; var eventSize = InputEvent.kBaseEventSize + sizeof(int) + stateSize; var buffer = new NativeArray((int)eventSize, 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; } } }