public void Press(InputControl control) { using (DeltaStateEvent.From(control, out var eventPtr)) SetUpAndQueueEvent(eventPtr, control, 1f); InputSystem.Update(); }
public void Release(InputControl control, Vector2 input) { using (DeltaStateEvent.From(control, out var eventPtr)) SetUpAndQueueEvent(eventPtr, control, input); InputSystem.Update(); }
internal unsafe IntPtr GetStatePtrFromStateEvent(InputEventPtr eventPtr) { if (!eventPtr.valid) { throw new ArgumentNullException("eventPtr"); } uint stateOffset; FourCC stateFormat; uint stateSizeInBytes; IntPtr statePtr; if (eventPtr.IsA <DeltaStateEvent>()) { var deltaEvent = DeltaStateEvent.From(eventPtr); // If it's a delta event, we need to subtract the delta state offset if it's not set to the root of the device stateOffset = deltaEvent->stateOffset; stateFormat = deltaEvent->stateFormat; stateSizeInBytes = deltaEvent->deltaStateSizeInBytes; statePtr = deltaEvent->deltaState; } else if (eventPtr.IsA <StateEvent>()) { var stateEvent = StateEvent.From(eventPtr); stateOffset = 0; stateFormat = stateEvent->stateFormat; stateSizeInBytes = stateEvent->stateSizeInBytes; statePtr = stateEvent->state; } else { throw new ArgumentException("Event must be a state or delta state event", "eventPtr"); } // Make sure we have a state event compatible with our device. The event doesn't // have to be specifically for our device (we don't require device IDs to match) but // the formats have to match and the size must be within range of what we're trying // to read. if (stateFormat != device.m_StateBlock.format) { throw new InvalidOperationException( string.Format( "Cannot read control '{0}' from {1} with format {2}; device '{3}' expects format {4}", path, eventPtr.type, stateFormat, device, device.m_StateBlock.format)); } // Once a device has been added, global state buffer offsets are baked into control hierarchies. // We need to unsubtract those offsets here. stateOffset += device.m_StateBlock.byteOffset; if (m_StateBlock.byteOffset - stateOffset + m_StateBlock.alignedSizeInBytes > stateSizeInBytes) { return(IntPtr.Zero); } return(new IntPtr(statePtr.ToInt64() - (int)stateOffset)); }
/// <summary> /// Set the control to the given value by sending a state event with the value to the /// control's device. /// </summary> /// <param name="control">An input control on a device that has been added to the system.</param> /// <param name="state">New value for the input control.</param> /// <param name="time">Timestamp to use for the state event. If -1 (default), current time is used (see <see cref="InputTestFixture.currentTime"/>).</param> /// <param name="timeOffset">Offset to apply to the current time. This is an alternative to <paramref name="time"/>. By default, no offset is applied.</param> /// <param name="queueEventOnly">If true, no <see cref="InputSystem.Update"/> will be performed after queueing the event. This will only put /// the state event on the event queue and not do anything else. The default is to call <see cref="InputSystem.Update"/> after queuing the event. /// Note that not issuing an update means the state of the device will not change yet. This may affect subsequent Set/Press/Release/etc calls /// as they will not yet see the state change. /// /// Note that this parameter will be ignored if the test is a <c>[UnityTest]</c>. Multi-frame /// playmode tests will automatically process input as part of the Unity player loop.</param> /// <typeparam name="TValue">Value type of the given control.</typeparam> /// <example> /// <code> /// var gamepad = InputSystem.AddDevice<Gamepad>(); /// Set(gamepad.leftButton, 1); /// </code> /// </example> public void Set <TValue>(InputControl <TValue> control, TValue state, double time = -1, double timeOffset = 0, bool queueEventOnly = false) where TValue : struct { if (control == null) { throw new ArgumentNullException(nameof(control)); } if (!control.device.added) { throw new ArgumentException( $"Device of control '{control}' has not been added to the system", nameof(control)); } if (IsUnityTest()) { queueEventOnly = true; } void SetUpAndQueueEvent(InputEventPtr eventPtr) { ////REVIEW: should we by default take the time from the device here? if (time >= 0) { eventPtr.time = time; } eventPtr.time += timeOffset; control.WriteValueIntoEvent(state, eventPtr); InputSystem.QueueEvent(eventPtr); } // Touchscreen does not support delta events involving TouchState. if (control is TouchControl) { using (StateEvent.From(control.device, out var eventPtr)) SetUpAndQueueEvent(eventPtr); } else { // We use delta state events rather than full state events here to mitigate the following problem: // Grabbing state from the device will preserve the current values of controls covered in the state. // However, running an update may alter the value of one or more of those controls. So with a full // state event, we may be writing outdated data back into the device. For example, in the case of delta // controls which will reset in OnBeforeUpdate(). // // Using delta events, we may still grab state outside of just the one control in case we're looking at // bit-addressed controls but at least we can avoid the problem for the majority of controls. using (DeltaStateEvent.From(control, out var eventPtr)) SetUpAndQueueEvent(eventPtr); } if (!queueEventOnly) { InputSystem.Update(); } }
private unsafe void ColumnGUI(Rect cellRect, InputEventPtr eventPtr, int column) { CenterRectUsingSingleLineHeight(ref cellRect); switch (column) { case (int)ColumnId.Id: GUI.Label(cellRect, eventPtr.id.ToString()); break; case (int)ColumnId.Type: GUI.Label(cellRect, eventPtr.type.ToString()); break; case (int)ColumnId.Device: GUI.Label(cellRect, eventPtr.deviceId.ToString()); break; case (int)ColumnId.Size: GUI.Label(cellRect, eventPtr.sizeInBytes.ToString()); break; case (int)ColumnId.Time: GUI.Label(cellRect, eventPtr.time.ToString("0.0000s")); break; case (int)ColumnId.Details: if (eventPtr.IsA <DeltaStateEvent>()) { var deltaEventPtr = DeltaStateEvent.From(eventPtr); GUI.Label(cellRect, $"Format={deltaEventPtr->stateFormat}, Offset={deltaEventPtr->stateOffset}"); } else if (eventPtr.IsA <StateEvent>()) { var stateEventPtr = StateEvent.From(eventPtr); GUI.Label(cellRect, $"Format={stateEventPtr->stateFormat}"); } else if (eventPtr.IsA <TextEvent>()) { var textEventPtr = TextEvent.From(eventPtr); GUI.Label(cellRect, $"Character='{(char) textEventPtr->character}'"); } break; } }
/// <summary> /// Checks an Input Event for any significant changes that would be considered user activity. /// </summary> /// <param name="inputEvent">The input event being checked for changes</param> /// <param name="device">The input device being checked against </param> /// <param name="offset">The offset into the device that the event is placed</param> /// <param name="sizeInbytes">The size of the event in bytes</param> /// <returns>True if any changes exist in the event once the device has been filtered through for noise and non-significant changes. False otherwise.</returns> public unsafe bool HasValidData(InputDevice device, InputEventPtr inputEvent, uint offset, uint sizeInbytes) { if (elements.Length == 0) { return(true); } if ((offset + sizeInbytes) * 8 > device.stateBlock.sizeInBits) { return(false); } bool result = false; IntPtr noiseFilterPtr = InputStateBuffers.s_NoiseFilterBuffer; if (noiseFilterPtr != IntPtr.Zero) { IntPtr ptrToEventState = IntPtr.Zero; if (inputEvent.IsA <StateEvent>()) { StateEvent *stateEvent = StateEvent.From(inputEvent); ptrToEventState = stateEvent->state; } else if (inputEvent.IsA <DeltaStateEvent>()) { DeltaStateEvent *stateEvent = DeltaStateEvent.From(inputEvent); ptrToEventState = stateEvent->deltaState; } if (ptrToEventState != IntPtr.Zero) { result = BitmaskHelpers.CheckForMaskedValues(ptrToEventState, noiseFilterPtr, offset, sizeInbytes * 8); for (int i = 0; i < elements.Length && !result; i++) { result = elements[i].HasValidData(inputEvent, device); } } } return(result); }
public void Events_CanCreateDeltaStateEventFromControl() { var gamepad = InputSystem.AddDevice <Gamepad>(); Set(gamepad.buttonSouth, 1); Set(gamepad.buttonNorth, 1); Set(gamepad.leftTrigger, 0.123f); using (DeltaStateEvent.From(gamepad.buttonNorth, out var eventPtr)) { Assert.That(gamepad.buttonNorth.ReadValueFromEvent(eventPtr, out var val), Is.True); Assert.That(val, Is.EqualTo(1).Within(0.00001)); gamepad.buttonNorth.WriteValueIntoEvent(0f, eventPtr); InputSystem.QueueEvent(eventPtr); InputSystem.Update(); Assert.That(gamepad.buttonNorth.ReadValue(), Is.Zero); } }
private unsafe byte[] GetEventStateBuffer(InputEventPtr eventPtr, InputControl control) { // Must be an event carrying state. if (!eventPtr.IsA <StateEvent>() && !eventPtr.IsA <DeltaStateEvent>()) { throw new ArgumentException("Event must be state or delta event", "eventPtr"); } // Get state data. void *dataPtr; uint dataSize; uint stateSize; uint stateOffset = 0; if (eventPtr.IsA <DeltaStateEvent>()) { var deltaEventPtr = DeltaStateEvent.From(eventPtr); stateSize = control.stateBlock.alignedSizeInBytes; stateOffset = deltaEventPtr->stateOffset; dataPtr = deltaEventPtr->deltaState.ToPointer(); dataSize = deltaEventPtr->deltaStateSizeInBytes; } else { var stateEventPtr = StateEvent.From(eventPtr); dataSize = stateSize = stateEventPtr->stateSizeInBytes; dataPtr = stateEventPtr->state.ToPointer(); } // Copy event data. var buffer = new byte[stateSize]; fixed(byte *bufferPtr = buffer) { UnsafeUtility.MemCpy(bufferPtr + stateOffset, dataPtr, dataSize); } return(buffer); }
public static unsafe void *GetStatePtrFromStateEvent(this InputControl control, InputEventPtr eventPtr) { if (control == null) { throw new ArgumentNullException(nameof(control)); } if (!eventPtr.valid) { throw new ArgumentNullException(nameof(eventPtr)); } uint stateOffset; FourCC stateFormat; uint stateSizeInBytes; void * statePtr; if (eventPtr.IsA <DeltaStateEvent>()) { var deltaEvent = DeltaStateEvent.From(eventPtr); // If it's a delta event, we need to subtract the delta state offset if it's not set to the root of the device stateOffset = deltaEvent->stateOffset; stateFormat = deltaEvent->stateFormat; stateSizeInBytes = deltaEvent->deltaStateSizeInBytes; statePtr = deltaEvent->deltaState; } else if (eventPtr.IsA <StateEvent>()) { var stateEvent = StateEvent.From(eventPtr); stateOffset = 0; stateFormat = stateEvent->stateFormat; stateSizeInBytes = stateEvent->stateSizeInBytes; statePtr = stateEvent->state; } else { throw new ArgumentException("Event must be a state or delta state event", "eventPtr"); } // Make sure we have a state event compatible with our device. The event doesn't // have to be specifically for our device (we don't require device IDs to match) but // the formats have to match and the size must be within range of what we're trying // to read. var device = control.device; if (stateFormat != device.m_StateBlock.format) { throw new InvalidOperationException( $"Cannot read control '{control.path}' from {eventPtr.type} with format {stateFormat}; device '{device}' expects format {device.m_StateBlock.format}"); } // Once a device has been added, global state buffer offsets are baked into control hierarchies. // We need to unsubtract those offsets here. stateOffset += device.m_StateBlock.byteOffset; if (control.m_StateBlock.byteOffset - stateOffset + control.m_StateBlock.alignedSizeInBytes > stateSizeInBytes) { return(null); } return((byte *)statePtr - (int)stateOffset); }
/// <summary> /// Checks an Input Event for any significant changes that would be considered user activity. /// </summary> /// <param name="inputEvent">The input event being checked for changes</param> /// <param name="device">The input device being checked against </param> /// <param name="offset">The offset into the device that the event is placed</param> /// <param name="sizeInBytes">The size of the event in bytes</param> /// <returns>True if any changes exist in the event once the device has been filtered through for noise and non-significant changes. False otherwise.</returns> public unsafe bool EventHasValidData(InputDevice device, InputEventPtr inputEvent, uint offset, uint sizeInBytes) { if (!inputEvent.valid) { throw new ArgumentException("Invalid or unset event being checked.", "inputEvent"); } if (device == null) { throw new ArgumentNullException("device"); } if (IsEmpty()) { return(true); } if ((offset + sizeInBytes) * 8 > device.stateBlock.sizeInBits) { return(false); } var noiseFilterPtr = InputStateBuffers.s_NoiseBitmaskBuffer; if (noiseFilterPtr == IntPtr.Zero) { throw new Exception("Noise Filter Buffer is uninitialized while trying to check state events for data."); } var ptrToEventState = IntPtr.Zero; if (inputEvent.IsA <StateEvent>()) { var stateEvent = StateEvent.From(inputEvent); ptrToEventState = stateEvent->state; } else if (inputEvent.IsA <DeltaStateEvent>()) { var stateEvent = DeltaStateEvent.From(inputEvent); ptrToEventState = stateEvent->deltaState; } else { throw new ArgumentException(string.Format( "Invalid event type '{0}', we can only check for valid data on StateEvents and DeltaStateEvents.", inputEvent.type), "inputEvent"); } if (MemoryHelpers.HasAnyNonZeroBitsAfterMaskingWithBuffer(ptrToEventState, noiseFilterPtr, offset, sizeInBytes * 8)) { return(true); } for (var i = 0; i < elements.Length; i++) { if (elements[i].EventHasValidData(inputEvent, device)) { return(true); } } return(false); }