A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine.InputSystem.Utilities;
3
4////REVIEW: nuke this and force raw pointers on all code using events?
5
6namespace UnityEngine.InputSystem.LowLevel
7{
8 /// <summary>
9 /// Pointer to an <see cref="InputEvent"/>. Makes it easier to work with InputEvents and hides
10 /// the unsafe operations necessary to work with them.
11 /// </summary>
12 /// <remarks>
13 /// Note that event pointers generally refer to event buffers that are continually reused. This means
14 /// that event pointers should not be held on to. Instead, to hold onto event data, manually copy
15 /// an event to a buffer.
16 /// </remarks>
17 public unsafe struct InputEventPtr : IEquatable<InputEventPtr>
18 {
19 // C# does not allow us to have pointers to structs that have managed data members. Since
20 // this can't be guaranteed for generic type parameters, they can't be used with pointers.
21 // This is why we cannot make InputEventPtr generic or have a generic method that returns
22 // a pointer to a specific type of event.
23 private readonly InputEvent* m_EventPtr;
24
25 /// <summary>
26 /// Initialize the pointer to refer to the given event.
27 /// </summary>
28 /// <param name="eventPtr">Pointer to an event. Can be <c>null</c>.</param>
29 public InputEventPtr(InputEvent* eventPtr)
30 {
31 m_EventPtr = eventPtr;
32 }
33
34 /// <summary>
35 /// Whether the pointer is not <c>null</c>.
36 /// </summary>
37 /// <value>True if the struct refers to an event.</value>
38 public bool valid => m_EventPtr != null;
39
40 /// <summary>
41 /// Whether the event is considered "handled" and should not be processed further.
42 /// </summary>
43 /// <remarks>
44 /// This is used in two ways. Setting it from inside <see cref="InputSystem.onEvent"/> will
45 /// cause the event to not be processed further. If it is a <see cref="StateEvent"/> or
46 /// <see cref="DeltaStateEvent"/>, the <see cref="InputDevice"/> targeted by the event will
47 /// not receive the state change.
48 ///
49 /// Setting this flag from inside a state change monitor (see <see cref="InputState.AddChangeMonitor(InputControl,IInputStateChangeMonitor,long,uint)"/>)
50 /// will prevent other monitors on the same <see cref="IInputStateChangeMonitor"/> not receiving the state change.
51 /// </remarks>
52 /// <exception cref="InvalidOperationException">The event pointer instance is not <see cref="valid"/>.</exception>
53 public bool handled
54 {
55 get
56 {
57 if (!valid)
58 return false;
59 return m_EventPtr->handled;
60 }
61 set
62 {
63 if (!valid)
64 throw new InvalidOperationException("The InputEventPtr is not valid.");
65 m_EventPtr->handled = value;
66 }
67 }
68
69 public int id
70 {
71 get
72 {
73 if (!valid)
74 return 0;
75 return m_EventPtr->eventId;
76 }
77 set
78 {
79 if (!valid)
80 throw new InvalidOperationException("The InputEventPtr is not valid.");
81 m_EventPtr->eventId = value;
82 }
83 }
84
85 public FourCC type
86 {
87 get
88 {
89 if (!valid)
90 return new FourCC();
91 return m_EventPtr->type;
92 }
93 }
94
95 public uint sizeInBytes
96 {
97 get
98 {
99 if (!valid)
100 return 0;
101 return m_EventPtr->sizeInBytes;
102 }
103 }
104
105 public int deviceId
106 {
107 get
108 {
109 if (!valid)
110 return InputDevice.InvalidDeviceId;
111 return m_EventPtr->deviceId;
112 }
113 set
114 {
115 if (!valid)
116 throw new InvalidOperationException("The InputEventPtr is not valid.");
117 m_EventPtr->deviceId = value;
118 }
119 }
120
121 public double time
122 {
123 get => valid ? m_EventPtr->time : 0.0;
124 set
125 {
126 if (!valid)
127 throw new InvalidOperationException("The InputEventPtr is not valid.");
128 m_EventPtr->time = value;
129 }
130 }
131
132 internal double internalTime
133 {
134 get => valid ? m_EventPtr->internalTime : 0.0;
135 set
136 {
137 if (!valid)
138 throw new InvalidOperationException("The InputEventPtr is not valid.");
139 m_EventPtr->internalTime = value;
140 }
141 }
142
143 public InputEvent* data => m_EventPtr;
144
145 // The stateFormat, stateSizeInBytes, and stateOffset properties are very
146 // useful for debugging.
147
148 internal FourCC stateFormat
149 {
150 get
151 {
152 var eventType = type;
153 if (eventType == StateEvent.Type)
154 return StateEvent.FromUnchecked(this)->stateFormat;
155 if (eventType == DeltaStateEvent.Type)
156 return DeltaStateEvent.FromUnchecked(this)->stateFormat;
157 throw new InvalidOperationException("Event must be a StateEvent or DeltaStateEvent but is " + this);
158 }
159 }
160
161 internal uint stateSizeInBytes
162 {
163 get
164 {
165 if (IsA<StateEvent>())
166 return StateEvent.From(this)->stateSizeInBytes;
167 if (IsA<DeltaStateEvent>())
168 return DeltaStateEvent.From(this)->deltaStateSizeInBytes;
169 throw new InvalidOperationException("Event must be a StateEvent or DeltaStateEvent but is " + this);
170 }
171 }
172
173 internal uint stateOffset
174 {
175 get
176 {
177 if (IsA<DeltaStateEvent>())
178 return DeltaStateEvent.From(this)->stateOffset;
179 throw new InvalidOperationException("Event must be a DeltaStateEvent but is " + this);
180 }
181 }
182
183 public bool IsA<TOtherEvent>()
184 where TOtherEvent : struct, IInputEventTypeInfo
185 {
186 if (m_EventPtr == null)
187 return false;
188
189 // NOTE: Important to say `default` instead of `new TOtherEvent()` here. The latter will result in a call to
190 // `Activator.CreateInstance` on Mono and thus allocate GC memory.
191 TOtherEvent otherEvent = default;
192 return m_EventPtr->type == otherEvent.typeStatic;
193 }
194
195 // NOTE: It is your responsibility to know *if* there actually another event following this one in memory.
196 public InputEventPtr Next()
197 {
198 if (!valid)
199 return new InputEventPtr();
200
201 return new InputEventPtr(InputEvent.GetNextInMemory(m_EventPtr));
202 }
203
204 public override string ToString()
205 {
206 if (!valid)
207 return "null";
208
209 // il2cpp has a bug which makes builds fail if this is written as 'return m_EventPtr->ToString()'.
210 // Gives an error about "trying to constrain an invalid type".
211 // Writing it as a two-step operation like here makes it build cleanly.
212 var eventPtr = *m_EventPtr;
213 return eventPtr.ToString();
214 }
215
216 /// <summary>
217 /// Return the plain pointer wrapped around by the struct.
218 /// </summary>
219 /// <returns>A plain pointer. Can be <c>null</c>.</returns>
220 public InputEvent* ToPointer()
221 {
222 return this;
223 }
224
225 public bool Equals(InputEventPtr other)
226 {
227 return m_EventPtr == other.m_EventPtr || InputEvent.Equals(m_EventPtr, other.m_EventPtr);
228 }
229
230 public override bool Equals(object obj)
231 {
232 if (ReferenceEquals(null, obj))
233 return false;
234 return obj is InputEventPtr ptr && Equals(ptr);
235 }
236
237 public override int GetHashCode()
238 {
239 return unchecked((int)(long)m_EventPtr);
240 }
241
242 public static bool operator==(InputEventPtr left, InputEventPtr right)
243 {
244 return left.m_EventPtr == right.m_EventPtr;
245 }
246
247 public static bool operator!=(InputEventPtr left, InputEventPtr right)
248 {
249 return left.m_EventPtr != right.m_EventPtr;
250 }
251
252 public static implicit operator InputEventPtr(InputEvent* eventPtr)
253 {
254 return new InputEventPtr(eventPtr);
255 }
256
257 public static InputEventPtr From(InputEvent* eventPtr)
258 {
259 return new InputEventPtr(eventPtr);
260 }
261
262 public static implicit operator InputEvent*(InputEventPtr eventPtr)
263 {
264 return eventPtr.data;
265 }
266
267 // Make annoying Microsoft code analyzer happy.
268 public static InputEvent* FromInputEventPtr(InputEventPtr eventPtr)
269 {
270 return eventPtr.data;
271 }
272 }
273}