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}