123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- using System;
- using System.Runtime.InteropServices;
- using Unity.Collections;
- using UnityEngine.InputSystem.Utilities;
- using Unity.Collections.LowLevel.Unsafe;
-
- namespace UnityEngine.InputSystem.LowLevel
- {
- /// <summary>
- /// A complete state snapshot for an entire input device.
- /// </summary>
- /// <remarks>
- /// This is a variable-sized event.
- /// </remarks>
- [StructLayout(LayoutKind.Explicit, Size = InputEvent.kBaseEventSize + 4 + kStateDataSizeToSubtract, Pack = 1)]
- public unsafe struct StateEvent : IInputEventTypeInfo
- {
- public const int Type = 0x53544154; // 'STAT'
-
- internal const int kStateDataSizeToSubtract = 1;
-
- [FieldOffset(0)]
- public InputEvent baseEvent;
-
- /// <summary>
- /// Type code for the state stored in the event.
- /// </summary>
- [FieldOffset(InputEvent.kBaseEventSize)]
- public FourCC stateFormat;
-
- [FieldOffset(InputEvent.kBaseEventSize + sizeof(int))]
- internal fixed byte stateData[kStateDataSizeToSubtract]; // Variable-sized.
-
- public uint stateSizeInBytes => baseEvent.sizeInBytes - (InputEvent.kBaseEventSize + sizeof(int));
-
- public void* state
- {
- get
- {
- fixed(byte* data = stateData)
- {
- return data;
- }
- }
- }
-
- public InputEventPtr ToEventPtr()
- {
- fixed(StateEvent * ptr = &this)
- {
- return new InputEventPtr((InputEvent*)ptr);
- }
- }
-
- public FourCC typeStatic => Type;
-
- /// <summary>
- /// Retrieve the state stored in the event.
- /// </summary>
- /// <typeparam name="TState">Type of state expected to be stored in the event. <see cref="IInputStateTypeInfo.format"/>
- /// must match <see cref="stateFormat"/>.</typeparam>
- /// <returns>Copy of the state stored in the event.</returns>
- /// <exception cref="InvalidOperationException"><see cref="stateFormat"/> does not match <see cref="IInputStateTypeInfo.format"/>
- /// of <typeparamref name="TState"/>.</exception>
- /// <remarks>
- /// The event may contain less or more data than what is found in the struct. Only the data found in the event
- /// is copied. The remainder of the struct is left at default values.
- /// </remarks>
- /// <seealso cref="GetState{T}(InputEventPtr)"/>
- public TState GetState<TState>()
- where TState : struct, IInputStateTypeInfo
- {
- var result = default(TState);
- if (stateFormat != result.format)
- throw new InvalidOperationException($"Expected state format '{result.format}' but got '{stateFormat}' instead");
-
- UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref result), state, Math.Min(stateSizeInBytes, UnsafeUtility.SizeOf<TState>()));
-
- return result;
- }
-
- /// <summary>
- /// Retrieve the state stored in the event.
- /// </summary>
- /// <typeparam name="TState">Type of state expected to be stored in the event. <see cref="IInputStateTypeInfo.format"/>
- /// must match <see cref="stateFormat"/>.</typeparam>
- /// <param name="ptr">A pointer to an input event. The pointer is checked for <c>null</c> and
- /// for whether the type of event it refers to is indeed a StateEvent.</param>
- /// <returns>Copy of the state stored in the event.</returns>
- /// <remarks>
- /// The event may contain less or more data than what is found in the struct. Only the data found in the event
- /// is copied. The remainder of the struct is left at default values.
- /// </remarks>
- /// <exception cref="InvalidOperationException"><see cref="stateFormat"/> does not match <see cref="IInputStateTypeInfo.format"/>
- /// of <typeparamref name="TState"/>.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="ptr"/> is <c>default(InputEventPtr)</c>.</exception>
- /// <exception cref="InvalidCastException"><paramref name="ptr"/> does not refer to a StateEvent.</exception>
- /// <seealso cref="GetState{T}()"/>
- public static TState GetState<TState>(InputEventPtr ptr)
- where TState : struct, IInputStateTypeInfo
- {
- return From(ptr)->GetState<TState>();
- }
-
- public static int GetEventSizeWithPayload<TState>()
- where TState : struct
- {
- return UnsafeUtility.SizeOf<TState>() + InputEvent.kBaseEventSize + sizeof(int);
- }
-
- /// <summary>
- /// Return the given <see cref="InputEventPtr"/> as a StateEvent pointer.
- /// </summary>
- /// <param name="ptr">A pointer to an input event. The pointer is checked for <c>null</c> and
- /// for whether the type of event it refers to is indeed a StateEvent.</param>
- /// <returns>Pointer <paramref name="ptr"/> converted to a StateEvent pointer.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="ptr"/> is <c>default(InputEventPtr)</c>.</exception>
- /// <exception cref="InvalidCastException"><paramref name="ptr"/> does not refer to a StateEvent.</exception>
- public static StateEvent* From(InputEventPtr ptr)
- {
- if (!ptr.valid)
- throw new ArgumentNullException(nameof(ptr));
- if (!ptr.IsA<StateEvent>())
- throw new InvalidCastException($"Cannot cast event with type '{ptr.type}' into StateEvent");
-
- return FromUnchecked(ptr);
- }
-
- internal static StateEvent* FromUnchecked(InputEventPtr ptr)
- {
- return (StateEvent*)ptr.data;
- }
-
- /// <summary>
- /// Read the current state of <paramref name="device"/> and create a state event from it.
- /// </summary>
- /// <param name="device">Device to grab the state from. Must be a device that has been added to the system.</param>
- /// <param name="eventPtr">Receives a pointer to the newly created state event.</param>
- /// <param name="allocator">Which native allocator to allocate memory for the event from. By default, the buffer is
- /// allocated as temporary memory (<see cref="Allocator.Temp"/>. Note that this means the buffer will not be valid
- /// past the current frame. Use <see cref="Allocator.Persistent"/> if the buffer for the state event is meant to
- /// persist for longer.</param>
- /// <returns>Buffer of unmanaged memory allocated for the event.</returns>
- /// <exception cref="ArgumentException"><paramref name="device"/> has not been added to the system.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
- public static NativeArray<byte> From(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp)
- {
- return From(device, out eventPtr, allocator, useDefaultState: false);
- }
-
- /// <summary>
- /// Create a state event for the given <paramref name="device"/> and copy the default state of the device
- /// into the event.
- /// </summary>
- /// <param name="device">Device to create a state event for. Must be a device that has been added to the system.</param>
- /// <param name="eventPtr">Receives a pointer to the newly created state event.</param>
- /// <param name="allocator">Which native allocator to allocate memory for the event from. By default, the buffer is
- /// allocated as temporary memory (<see cref="Allocator.Temp"/>. Note that this means the buffer will not be valid
- /// past the current frame. Use <see cref="Allocator.Persistent"/> if the buffer for the state event is meant to
- /// persist for longer.</param>
- /// <returns>Buffer of unmanaged memory allocated for the event.</returns>
- /// <exception cref="ArgumentException"><paramref name="device"/> has not been added to the system.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
- public static NativeArray<byte> FromDefaultStateFor(InputDevice device, out InputEventPtr eventPtr, Allocator allocator = Allocator.Temp)
- {
- return From(device, out eventPtr, allocator, useDefaultState: true);
- }
-
- private static NativeArray<byte> From(InputDevice device, out InputEventPtr eventPtr, Allocator allocator, bool useDefaultState)
- {
- if (device == null)
- throw new ArgumentNullException(nameof(device));
- if (!device.added)
- throw new ArgumentException($"Device '{device}' has not been added to system",
- nameof(device));
-
- var stateFormat = device.m_StateBlock.format;
- var stateSize = device.m_StateBlock.alignedSizeInBytes;
- var stateOffset = device.m_StateBlock.byteOffset;
- var statePtr = (byte*)(useDefaultState ? device.defaultStatePtr : device.currentStatePtr) + (int)stateOffset;
- var eventSize = InputEvent.kBaseEventSize + sizeof(int) + stateSize;
-
- var buffer = new NativeArray<byte>((int)eventSize.AlignToMultipleOf(4), allocator);
- var stateEventPtr = (StateEvent*)buffer.GetUnsafePtr();
-
- stateEventPtr->baseEvent = new InputEvent(Type, (int)eventSize, device.deviceId, InputRuntime.s_Instance.currentTime);
- stateEventPtr->stateFormat = stateFormat;
-
- UnsafeUtility.MemCpy(stateEventPtr->state, statePtr, stateSize);
-
- eventPtr = stateEventPtr->ToEventPtr();
- return buffer;
- }
- }
- }
|