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}