DeltaStateEvent.cs 3.7 KB

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