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;
}
}
}