A game about forced loneliness, made by TACStudios
1using System;
2using System.Runtime.InteropServices;
3using Unity.Collections;
4using Unity.Collections.LowLevel.Unsafe;
5using UnityEngine.InputSystem.Utilities;
6
7namespace UnityEngine.InputSystem.LowLevel
8{
9 /// <summary>
10 /// Partial state update for an input device.
11 /// </summary>
12 /// <remarks>
13 /// Avoids having to send a full state memory snapshot when only a small
14 /// part of the state has changed.
15 /// </remarks>
16 [StructLayout(LayoutKind.Explicit, Pack = 1, Size = InputEvent.kBaseEventSize + 9)]
17 public unsafe struct DeltaStateEvent : IInputEventTypeInfo
18 {
19 public const int Type = 0x444C5441; // 'DLTA'
20
21 [FieldOffset(0)]
22 public InputEvent baseEvent;
23
24 [FieldOffset(InputEvent.kBaseEventSize)]
25 public FourCC stateFormat;
26
27 [FieldOffset(InputEvent.kBaseEventSize + 4)]
28 public uint stateOffset;
29
30 [FieldOffset(InputEvent.kBaseEventSize + 8)]
31 internal fixed byte stateData[1]; // Variable-sized.
32
33 public uint deltaStateSizeInBytes => baseEvent.sizeInBytes - (InputEvent.kBaseEventSize + 8);
34
35 public void* deltaState
36 {
37 get
38 {
39 fixed(byte* data = stateData)
40 {
41 return data;
42 }
43 }
44 }
45
46 public FourCC typeStatic => Type;
47
48 public InputEventPtr ToEventPtr()
49 {
50 fixed(DeltaStateEvent * ptr = &this)
51 {
52 return new InputEventPtr((InputEvent*)ptr);
53 }
54 }
55
56 public static DeltaStateEvent* From(InputEventPtr ptr)
57 {
58 if (!ptr.valid)
59 throw new ArgumentNullException(nameof(ptr));
60 if (!ptr.IsA<DeltaStateEvent>())
61 throw new InvalidCastException($"Cannot cast event with type '{ptr.type}' into DeltaStateEvent");
62
63 return FromUnchecked(ptr);
64 }
65
66 internal static DeltaStateEvent* FromUnchecked(InputEventPtr ptr)
67 {
68 return (DeltaStateEvent*)ptr.data;
69 }
70
71 public static NativeArray<byte> From(InputControl control, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp)
72 {
73 if (control == null)
74 throw new ArgumentNullException(nameof(control));
75 var device = control.device;
76 if (!device.added)
77 throw new ArgumentException($"Device for control '{control}' has not been added to system",
78 nameof(control));
79
80 ref var deviceStateBlock = ref device.m_StateBlock;
81 ref var controlStateBlock = ref control.m_StateBlock;
82
83 var stateFormat = deviceStateBlock.format; // The event is sent against the *device* so that's the state format we use.
84 var stateSize = 0u;
85 if (controlStateBlock.bitOffset != 0)
86 stateSize = (controlStateBlock.bitOffset + controlStateBlock.sizeInBits + 7) / 8;
87 else
88 stateSize = controlStateBlock.alignedSizeInBytes;
89 var stateOffset = controlStateBlock.byteOffset;
90 var statePtr = (byte*)control.currentStatePtr + (int)stateOffset;
91 var eventSize = InputEvent.kBaseEventSize + sizeof(int) * 2 + stateSize;
92
93 var buffer = new NativeArray<byte>((int)eventSize.AlignToMultipleOf(4), allocator);
94 var stateEventPtr = (DeltaStateEvent*)buffer.GetUnsafePtr();
95
96 stateEventPtr->baseEvent = new InputEvent(Type, (int)eventSize, device.deviceId, InputRuntime.s_Instance.currentTime);
97 stateEventPtr->stateFormat = stateFormat;
98 stateEventPtr->stateOffset = controlStateBlock.byteOffset - deviceStateBlock.byteOffset; // Make offset relative to device.
99 UnsafeUtility.MemCpy(stateEventPtr->deltaState, statePtr, stateSize);
100
101 eventPtr = stateEventPtr->ToEventPtr();
102 return buffer;
103 }
104 }
105}