File size: 10,055 Bytes
18a519f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | using System;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine.InputSystem.Utilities;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.InputSystem.LowLevel
{
/// <summary>
/// A complete state snapshot for an entire input device.
/// </summary>
/// <remarks>
/// This is a variable-sized event.
/// </remarks>
[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;
/// <summary>
/// Type code for the state stored in the event.
/// </summary>
[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;
/// <summary>
/// Retrieve the state stored in the event.
/// </summary>
/// <typeparam name="TState">Type of state expected to be stored in the event. <see cref="IInputStateTypeInfo.format"/>
/// must match <see cref="stateFormat"/>.</typeparam>
/// <returns>Copy of the state stored in the event.</returns>
/// <exception cref="InvalidOperationException"><see cref="stateFormat"/> does not match <see cref="IInputStateTypeInfo.format"/>
/// of <typeparamref name="TState"/>.</exception>
/// <remarks>
/// 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.
/// </remarks>
/// <seealso cref="GetState{T}(InputEventPtr)"/>
public TState GetState<TState>()
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<TState>()));
return result;
}
/// <summary>
/// Retrieve the state stored in the event.
/// </summary>
/// <typeparam name="TState">Type of state expected to be stored in the event. <see cref="IInputStateTypeInfo.format"/>
/// must match <see cref="stateFormat"/>.</typeparam>
/// <param name="ptr">A pointer to an input event. The pointer is checked for <c>null</c> and
/// for whether the type of event it refers to is indeed a StateEvent.</param>
/// <returns>Copy of the state stored in the event.</returns>
/// <remarks>
/// 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.
/// </remarks>
/// <exception cref="InvalidOperationException"><see cref="stateFormat"/> does not match <see cref="IInputStateTypeInfo.format"/>
/// of <typeparamref name="TState"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="ptr"/> is <c>default(InputEventPtr)</c>.</exception>
/// <exception cref="InvalidCastException"><paramref name="ptr"/> does not refer to a StateEvent.</exception>
/// <seealso cref="GetState{T}()"/>
public static TState GetState<TState>(InputEventPtr ptr)
where TState : struct, IInputStateTypeInfo
{
return From(ptr)->GetState<TState>();
}
public static int GetEventSizeWithPayload<TState>()
where TState : struct
{
return UnsafeUtility.SizeOf<TState>() + InputEvent.kBaseEventSize + sizeof(int);
}
/// <summary>
/// Return the given <see cref="InputEventPtr"/> as a StateEvent pointer.
/// </summary>
/// <param name="ptr">A pointer to an input event. The pointer is checked for <c>null</c> and
/// for whether the type of event it refers to is indeed a StateEvent.</param>
/// <returns>Pointer <paramref name="ptr"/> converted to a StateEvent pointer.</returns>
/// <exception cref="ArgumentNullException"><paramref name="ptr"/> is <c>default(InputEventPtr)</c>.</exception>
/// <exception cref="InvalidCastException"><paramref name="ptr"/> does not refer to a StateEvent.</exception>
public static StateEvent* From(InputEventPtr ptr)
{
if (!ptr.valid)
throw new ArgumentNullException(nameof(ptr));
if (!ptr.IsA<StateEvent>())
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;
}
/// <summary>
/// Read the current state of <paramref name="device"/> and create a state event from it.
/// </summary>
/// <param name="device">Device to grab the state from. Must be a device that has been added to the system.</param>
/// <param name="eventPtr">Receives a pointer to the newly created state event.</param>
/// <param name="allocator">Which native allocator to allocate memory for the event from. By default, the buffer is
/// allocated as temporary memory (<see cref="Allocator.Temp"/>. Note that this means the buffer will not be valid
/// past the current frame. Use <see cref="Allocator.Persistent"/> if the buffer for the state event is meant to
/// persist for longer.</param>
/// <returns>Buffer of unmanaged memory allocated for the event.</returns>
/// <exception cref="ArgumentException"><paramref name="device"/> has not been added to the system.</exception>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
public static NativeArray<byte> From(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp)
{
return From(device, out eventPtr, allocator, useDefaultState: false);
}
/// <summary>
/// Create a state event for the given <paramref name="device"/> and copy the default state of the device
/// into the event.
/// </summary>
/// <param name="device">Device to create a state event for. Must be a device that has been added to the system.</param>
/// <param name="eventPtr">Receives a pointer to the newly created state event.</param>
/// <param name="allocator">Which native allocator to allocate memory for the event from. By default, the buffer is
/// allocated as temporary memory (<see cref="Allocator.Temp"/>. Note that this means the buffer will not be valid
/// past the current frame. Use <see cref="Allocator.Persistent"/> if the buffer for the state event is meant to
/// persist for longer.</param>
/// <returns>Buffer of unmanaged memory allocated for the event.</returns>
/// <exception cref="ArgumentException"><paramref name="device"/> has not been added to the system.</exception>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
public static NativeArray<byte> FromDefaultStateFor(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp)
{
return From(device, out eventPtr, allocator, useDefaultState: true);
}
private static NativeArray<byte> 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<byte>((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;
}
}
}
|