Skip to content

Commit ab55d24

Browse files
author
Rene Damm
authored
FIX: Touch.activeTouches not canceled on focus loss (case 1364017, #1465).
1 parent 25151d2 commit ab55d24

13 files changed

Lines changed: 218 additions & 89 deletions

File tree

Assets/Tests/InputSystem/CoreTests_Devices.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,39 @@ long DeviceCallback(int deviceId, InputDeviceCommand* command, ref bool received
19601960
Assert.That(gyro.angularVelocity.ReadValue(), Is.EqualTo(Vector3.zero));
19611961
}
19621962

1963+
class DeviceWithCustomReset : InputDevice, ICustomDeviceReset
1964+
{
1965+
[InputControl]
1966+
public AxisControl axis { get; private set; }
1967+
1968+
protected override void FinishSetup()
1969+
{
1970+
axis = GetChildControl<AxisControl>("axis");
1971+
}
1972+
1973+
public unsafe void Reset()
1974+
{
1975+
InputState.Change(axis, 1f);
1976+
}
1977+
}
1978+
1979+
[Test]
1980+
[Category("Devices")]
1981+
public void Devices_CanCustomizeResetOfDevice()
1982+
{
1983+
var device = InputSystem.AddDevice<DeviceWithCustomReset>();
1984+
1985+
Set(device.axis, 0.45f);
1986+
1987+
InputSystem.ResetDevice(device);
1988+
1989+
Assert.That(device.axis.ReadValue(), Is.EqualTo(1f));
1990+
1991+
InputSystem.ResetDevice(device, alsoResetDontResetControls: true);
1992+
1993+
Assert.That(device.axis.ReadValue(), Is.Zero);
1994+
}
1995+
19631996
[Test]
19641997
[Category("Devices")]
19651998
public void Devices_WhenDeviceIsReset_AndResetsAreObservableStateChanges()

Assets/Tests/InputSystem/Plugins/EnhancedTouchTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,4 +1140,40 @@ public unsafe void EnhancedTouch_TouchSimulation_DisablesPointerDevicesWithoutDi
11401140
Assert.That(Touchscreen.current.touches[0].isInProgress, Is.True);
11411141
Assert.That(Touchscreen.current.touches[0].position.ReadValue(), Is.EqualTo(new Vector2(123, 234)));
11421142
}
1143+
1144+
[Test]
1145+
[Category("EnhancedTouch")]
1146+
[TestCase(true)]
1147+
[TestCase(false)]
1148+
public void EnhancedTouch_ActiveTouchesGetCanceledOnFocusLoss_WithRunInBackgroundBeing(bool runInBackground)
1149+
{
1150+
runtime.runInBackground = runInBackground;
1151+
1152+
BeginTouch(1, new Vector2(123, 456));
1153+
1154+
Assert.That(Touch.activeTouches, Has.Count.EqualTo(1));
1155+
Assert.That(Touch.activeTouches[0].phase, Is.EqualTo(TouchPhase.Began));
1156+
1157+
runtime.PlayerFocusLost();
1158+
1159+
if (runInBackground)
1160+
{
1161+
// When running in the background, next update after focus loss sees touches cancelled
1162+
// and update after that sees them gone.
1163+
InputSystem.Update(InputUpdateType.Dynamic);
1164+
}
1165+
else
1166+
{
1167+
// When not running in the background, the same thing happens but only on focus gain.
1168+
runtime.PlayerFocusGained();
1169+
InputSystem.Update();
1170+
}
1171+
1172+
Assert.That(Touch.activeTouches, Has.Count.EqualTo(1));
1173+
Assert.That(Touch.activeTouches[0].phase, Is.EqualTo(TouchPhase.Canceled));
1174+
1175+
InputSystem.Update();
1176+
1177+
Assert.That(Touch.activeTouches, Is.Empty);
1178+
}
11431179
}

Assets/Tests/InputSystem/Plugins/UITests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3467,10 +3467,10 @@ public void Setup()
34673467
#endif
34683468

34693469
#if !TEMP_DISABLE_UI_TESTS_ON_TRUNK
3470-
static bool[] canRunInBackgroundValueSource = new bool[] { false, true };
3470+
static bool[] canRunInBackgroundValueSource = new[] { false, true };
34713471

34723472
[UnityTest]
3473-
public IEnumerator Run_UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButtonClickBehaviorShouldDependOnIfDeviceCanRunInBackground(
3473+
public IEnumerator UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButtonClickBehaviorShouldDependOnIfDeviceCanRunInBackground(
34743474
[ValueSource(nameof(canRunInBackgroundValueSource))] bool canRunInBackground)
34753475
{
34763476
// Whether we run in the background or not should only move the reset of the mouse button

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ however, it has to be formatted properly to pass verification tests.
1616
- All devices are now re-synced/reset in next update after entering play mode, this is needed to read current state of devices before any intentional input is provided ([case 1231907](https://issuetracker.unity3d.com/issues/mouse-coordinates-reported-as-00-until-the-first-move)).
1717
- Replaced `UnityLinkerBuildPipelineData.inputDirectory` with hardcoded `Temp` folder because `inputDirectory` is deprecated.
1818
- Deprecated `InputSettings.filterNoiseOnCurrent`. Now noise filtering is always enabled. Device only will become `.current` if any non-noise control have changed state.
19+
- A device reset (such as when focus is lost) on `Touchscreen` will now result in all ongoing touches getting cancelled instead of all touches being simply reset to default state.
1920

2021
### Fixed
2122

@@ -30,6 +31,7 @@ however, it has to be formatted properly to pass verification tests.
3031
- Fixed a device becoming `.current` (e.g. `Gamepad.current`, etc) when sending a new state event that contains no control changes (case 1377952).
3132
- Fixed calling `IsPressed` on an entire device returning `true` ([case 1374024](https://issuetracker.unity3d.com/issues/inputcontrol-dot-ispressed-always-returns-true-when-using-new-input-system)).
3233
- Fixed `InputSystem.RegisterLayoutOverride` resulting in the layout that overrides are being applied to losing the connection to its base layout ([case 1377719](https://fogbugz.unity3d.com/f/cases/1377719/)).
34+
- Fixed `Touch.activeTouches` still registering touches after the app loses focus ([case 1364017](https://issuetracker.unity3d.com/issues/input-system-new-input-system-registering-active-touches-when-app-loses-focus)).
3335

3436
#### Actions
3537

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace UnityEngine.InputSystem.LowLevel
2+
{
3+
/// <summary>
4+
/// A device that implements its own reset logic for when <see cref="InputSystem.ResetDevice"/>
5+
/// is called.
6+
/// </summary>
7+
internal interface ICustomDeviceReset
8+
{
9+
/// <summary>
10+
/// Reset the current device state.
11+
/// </summary>
12+
void Reset();
13+
}
14+
}

Packages/com.unity.inputsystem/InputSystem/Devices/ICustomDeviceReset.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Packages/com.unity.inputsystem/InputSystem/Devices/IEventMerger.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using UnityEngine.InputSystem.LowLevel;
2-
3-
namespace UnityEngine.InputSystem
1+
namespace UnityEngine.InputSystem.LowLevel
42
{
53
internal interface IEventMerger
64
{

Packages/com.unity.inputsystem/InputSystem/Devices/IEventPreProcessor.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using UnityEngine.InputSystem.LowLevel;
2-
3-
namespace UnityEngine.InputSystem
1+
namespace UnityEngine.InputSystem.LowLevel
42
{
53
/// <summary>
64
/// Gives an opportunity for device to modify event data in-place before it gets propagated to the rest of the system.

Packages/com.unity.inputsystem/InputSystem/Devices/Touchscreen.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Runtime.InteropServices;
3+
using Unity.Collections;
34
using Unity.Collections.LowLevel.Unsafe;
45
using UnityEngine.InputSystem.Controls;
56
using UnityEngine.InputSystem.Layouts;
@@ -488,7 +489,7 @@ public enum TouchPhase
488489
/// it is recommended to use the higher-level <see cref="EnhancedTouch.Touch"/> API instead.
489490
/// </remarks>
490491
[InputControlLayout(stateType = typeof(TouchscreenState), isGenericTypeOfDevice = true)]
491-
public class Touchscreen : Pointer, IInputStateCallbackReceiver, IEventMerger
492+
public class Touchscreen : Pointer, IInputStateCallbackReceiver, IEventMerger, ICustomDeviceReset
492493
{
493494
/// <summary>
494495
/// Synthetic control that has the data for the touch that is deemed the "primary" touch at the moment.
@@ -952,6 +953,45 @@ unsafe bool IInputStateCallbackReceiver.GetStateOffsetForEvent(InputControl cont
952953
return true;
953954
}
954955

956+
// Implement our own custom reset so that we can cancel touches instead of just wiping them
957+
// with default state.
958+
unsafe void ICustomDeviceReset.Reset()
959+
{
960+
var statePtr = currentStatePtr;
961+
962+
//// https://jira.unity3d.com/browse/ISX-930
963+
////TODO: Figure out a proper way to distinguish the source / reason for a state change.
964+
//// What we're doing here is constructing an event solely for the purpose of Finger.ShouldRecordTouch() not
965+
//// ignoring the state change like it does for delta resets.
966+
967+
using (var buffer = new NativeArray<byte>(StateEvent.GetEventSizeWithPayload<TouchState>(), Allocator.Temp))
968+
{
969+
var eventPtr = (StateEvent*)buffer.GetUnsafePtr();
970+
971+
eventPtr->baseEvent = new InputEvent(StateEvent.Type, buffer.Length, deviceId);
972+
973+
var primaryTouchState = (TouchState*)((byte*)statePtr + primaryTouch.stateBlock.byteOffset);
974+
if (primaryTouchState->phase.IsActive())
975+
{
976+
UnsafeUtility.MemCpy(eventPtr->state, primaryTouchState, UnsafeUtility.SizeOf<TouchState>());
977+
((TouchState*)eventPtr->state)->phase = TouchPhase.Canceled;
978+
InputState.Change(primaryTouch.phase, TouchPhase.Canceled, eventPtr: new InputEventPtr((InputEvent*)eventPtr));
979+
}
980+
981+
var touchStates = (TouchState*)((byte*)statePtr + touches[0].stateBlock.byteOffset);
982+
var touchCount = touches.Count;
983+
for (var i = 0; i < touchCount; ++i)
984+
{
985+
if (touchStates[i].phase.IsActive())
986+
{
987+
UnsafeUtility.MemCpy(eventPtr->state, &touchStates[i], UnsafeUtility.SizeOf<TouchState>());
988+
((TouchState*)eventPtr->state)->phase = TouchPhase.Canceled;
989+
InputState.Change(touches[i].phase, TouchPhase.Canceled, eventPtr: new InputEventPtr((InputEvent*)eventPtr));
990+
}
991+
}
992+
}
993+
}
994+
955995
internal static unsafe bool MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr)
956996
{
957997
if (currentEventPtr.type != StateEvent.Type || nextEventPtr.type != StateEvent.Type)

Packages/com.unity.inputsystem/InputSystem/Events/InputEvent.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ namespace UnityEngine.InputSystem.LowLevel
4848
/// some device (which, however, may or may not translate to an <see cref="InputDevice"/>; that
4949
/// part depends on whether the input system was able to create an <see cref="InputDevice"/>
5050
/// based on the information received from the backend).
51-
///
52-
/// To implement your own type of event, TODO (manual?)
5351
/// </remarks>
5452
/// <seealso cref="InputEventPtr"/>
5553
// NOTE: This has to be layout compatible with native events.

0 commit comments

Comments
 (0)