Skip to content
Merged
79 changes: 79 additions & 0 deletions Assets/Tests/InputSystem/Plugins/DeviceSimulatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Editor;
using UnityEngine.InputSystem.EnhancedTouch;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.TestTools;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;
using TouchPhase = UnityEngine.InputSystem.TouchPhase;
Expand Down Expand Up @@ -64,6 +65,84 @@ public void TouchscreenAddedAndRemoved()
Assert.IsFalse(touchscreen.added);
}

[Test]
[Category("Device Simulator")]
public void ConflictingDevicesAreNotDisabledOnCreate()
{
var mouse = AddNativeMouse();
Assert.That(mouse.native, Is.True);

var plugin = new InputSystemPlugin();
plugin.OnCreate();

// Conflicting devices are only disabled once the Simulator gains focus, not on create.
Assert.That(mouse.enabled, Is.True);

plugin.OnDestroy();
}

[Test]
[Category("Device Simulator")]
public void ConflictingDeviceAddedWhileSimulatorFocused_IsDisabledThenReenabledOnDestroy()
{
var plugin = new InputSystemPlugin();
plugin.OnCreate();

// Simulate the Simulator window being focused (bypasses the panel-based OnUpdate).
plugin.SetConflictingDevicesDisabled(true);
Comment thread
diverges marked this conversation as resolved.

var mouse = AddNativeMouse();

Assert.That(mouse.native, Is.True);
Assert.That(mouse.enabled, Is.False); // disabled via the OnDeviceChange gate

plugin.OnDestroy();
Assert.That(mouse.enabled, Is.True); // ReenableConflictingDevices restores it
}

[Test]
[Category("Device Simulator")]
public void ConflictingDevicesReenabledWhenSimulatorLosesFocus()
{
var mouse = AddNativeMouse();

var plugin = new InputSystemPlugin();
plugin.OnCreate();

plugin.SetConflictingDevicesDisabled(true); // Simulator gained focus
Assert.That(mouse.enabled, Is.False);

plugin.SetConflictingDevicesDisabled(false); // Simulator lost focus
Assert.That(mouse.enabled, Is.True);

plugin.OnDestroy();
}

[Test]
[Category("Device Simulator")]
public void ConflictingDeviceAddedWhileSimulatorNotFocused_StaysEnabled()
{
var plugin = new InputSystemPlugin();
plugin.OnCreate();
// m_ConflictingDevicesDisabled defaults to false (Simulator not focused).

var mouse = AddNativeMouse();

Assert.That(mouse.enabled, Is.True);

plugin.OnDestroy();
}

// Reports a native Mouse through the test runtime (device.native == true, which the plugin's
// disable logic requires) and returns the resolved device rather than relying on Mouse.current.
private Mouse AddNativeMouse()
{
var deviceId = runtime.ReportNewInputDevice(
new InputDeviceDescription { deviceClass = "Mouse", interfaceName = "Test" });
InputSystem.Update();
return (Mouse)InputSystem.GetDeviceById(deviceId);
}

private TouchEvent CreateTouch(int touchId, Vector2 position, UnityEditor.DeviceSimulation.TouchPhase phase)
{
var touch = new TouchEvent();
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed `OnMouseUpAsButton` and `OnMouseUp` being dropped in Play mode when the Game view's focus changes between a press and its release on Unity 6000.5.0a8 and newer. The legacy `SendMouseEvents` pipeline is no longer driven from `InputUpdateType.Editor` updates, which read the editor state buffer (position (0,0), not pressed) and produced a spurious mouse release that cleared the press target.
- Fixed Input Debugger window's incorrect name. It is now called 'Input Debugger' instead of 'Input Debug' [UUM-137124](https://jira.unity3d.com/browse/UUM-137124).
- Fixed `PoseControl.isTracked` always returning false when read through non-optimized code paths (e.g. Input Debugger) due to `sizeInBits = 8` causing the value to be normalized as `1/255` instead of `1.0`.
- Fixed the Device Simulator plugin keeping the real mouse and pen disabled while working in other Editor windows. Conflicting native `Mouse`/`Pen` devices are now only disabled while the Simulator window is focused and re-enabled as soon as focus moves elsewhere [UUM-145509](https://jira.unity3d.com/browse/UUM-145509).
Comment thread
diverges marked this conversation as resolved.
Outdated

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.DeviceSimulation;
using UnityEditor.UIElements;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.UIElements;

namespace UnityEngine.InputSystem.Editor
{
Expand All @@ -13,6 +16,9 @@

private bool m_InputSystemEnabled;
private bool m_Quitting;
private bool m_ConflictingDevicesDisabled;
private VisualElement m_RootElement;
private EditorWindow m_LastFocusedWindow;
private List<InputDevice> m_DisabledDevices;

public override string title => "Input System";
Expand All @@ -25,19 +31,16 @@
// Monitor whether the editor is quitting to avoid risking unsafe EnableDevice while quitting
UnityEditor.EditorApplication.quitting += OnQuitting;

// Poll the active window so conflicting devices are only disabled while the simulator is focused.
UnityEditor.EditorApplication.update += OnUpdate;

m_DisabledDevices = new List<InputDevice>();

// deviceSimulator is never null when the plugin is instantiated by a simulator window, but it can be null during unit tests
if (deviceSimulator != null)
deviceSimulator.touchScreenInput += OnTouchEvent;
InputSystem.onDeviceChange += OnDeviceChange;

// UGUI elements like a button don't get pressed when multiple pointers for example mouse and touchscreen are sending data at the same time
foreach (var device in InputSystem.devices)
{
DisableConflictingDevice(device);
}

SimulatorTouchscreen = InputSystem.AddDevice<Touchscreen>("Device Simulator Touchscreen");
}
}
Expand All @@ -56,6 +59,72 @@
});
}

public override VisualElement OnCreateUI()
{
m_RootElement = new VisualElement();
m_RootElement.Add(new HelpBox(

Check warning on line 65 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L63-L65

Added lines #L63 - L65 were not covered by tests
L10n.Tr("Manages Input System devices while the Simulator is focused."),
HelpBoxMessageType.Info));
return m_RootElement;
}

Check warning on line 69 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L68-L69

Added lines #L68 - L69 were not covered by tests

private void OnUpdate()
{
if (!EditorApplication.isPlaying)
Comment thread
diverges marked this conversation as resolved.
{
if (m_ConflictingDevicesDisabled)
{
SetConflictingDevicesDisabled(false);
m_LastFocusedWindow = null;
}
return;

Check warning on line 80 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L74-L80

Added lines #L74 - L80 were not covered by tests
}

var focusedWindow = EditorWindow.focusedWindow;
if (focusedWindow == m_LastFocusedWindow)
return;
m_LastFocusedWindow = focusedWindow;

Check warning on line 86 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L86

Added line #L86 was not covered by tests

var simulatorFocused =

Check warning on line 88 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L88

Added line #L88 was not covered by tests
m_RootElement != null
&& focusedWindow != null
&& focusedWindow.rootVisualElement.panel == m_RootElement.panel;

SetConflictingDevicesDisabled(simulatorFocused);

Check warning on line 93 in Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Editor/DeviceSimulator/InputSystemPlugin.cs#L93

Added line #L93 was not covered by tests
}

// Exposed internally so tests can drive the focus transition without a live SimulatorWindow.
// OnUpdate itself can't run in a unit test: it needs play mode and a real panel to compare against.
internal void SetConflictingDevicesDisabled(bool disabled)
{
if (disabled == m_ConflictingDevicesDisabled)
return;

if (disabled)
{
// UGUI elements like a button don't get pressed when multiple pointers for example mouse and touchscreen are sending data at the same time
foreach (var device in InputSystem.devices)
DisableConflictingDevice(device);
}
else
{
foreach (var device in m_DisabledDevices)
{
// Note that m_Quitting is used here to mitigate the problem reported in issue tracker:
// https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-10774.
// Enabling a device will call into IOCTL of backend which may be destroyed prior
// to this callback on Unity version. This is not a fix for the actual problem
// of shutdown order but a package fix to mitigate this problem.
// The core problem with the destruction order was still there in Unity 6.5.
if (device.added && !m_Quitting)
InputSystem.EnableDevice(device);
}
m_DisabledDevices.Clear();
}

m_ConflictingDevicesDisabled = disabled;
}

private void DisableConflictingDevice(InputDevice device)
{
if (device.native && (device is Mouse || device is Pen) && device.enabled)
Expand All @@ -67,6 +136,10 @@

private void OnDeviceChange(InputDevice device, InputDeviceChange change)
{
// Only disable newly added/reconnected devices while the simulator is the active window.
if (!m_ConflictingDevicesDisabled)
return;

if (change == InputDeviceChange.Added || change == InputDeviceChange.Reconnected)
DisableConflictingDevice(device);
}
Expand Down Expand Up @@ -100,20 +173,13 @@
InputSystem.onDeviceChange -= OnDeviceChange;

UnityEditor.EditorApplication.quitting -= OnQuitting;
UnityEditor.EditorApplication.update -= OnUpdate;

if (SimulatorTouchscreen != null)
InputSystem.RemoveDevice(SimulatorTouchscreen);
foreach (var device in m_DisabledDevices)
{
// Note that m_Quitting is used here to mitigate the problem reported in issue tracker:
// https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-10774.
// Enabling a device will call into IOCTL of backend which may be destroyed prior
// to this callback on Unity version. This is not a fix for the actual problem
// of shutdown order but a package fix to mitigate this problem.
// The core problem with the destruction order was still there in Unity 6.5.
if (device.added && !m_Quitting)
InputSystem.EnableDevice(device);
}

SetConflictingDevicesDisabled(false);
m_RootElement = null;
}
}

Expand Down
Loading