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