From 95d60cae29cc7166efba14f6ad742ef6ac26ba98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Tue, 18 Aug 2026 09:55:59 +0200 Subject: [PATCH 1/7] NEW: Pen.isSupported, Mouse.isSupported and Touchscreen.isPressureSupported [ISX-2046] Legacy UnityEngine.Input conflates "is X supported on this platform" with "is an X present right now", and the Input System had no equivalent for the first question at all. Input.stylusTouchSupported reports true on any iPad new enough to pair an Apple Pencil, whether or not one is paired. Input.mousePresent is genuine detection on Windows and the consoles but a hardcoded true on macOS and Linux. These three properties answer the capability question. Presence keeps its existing answer, Device.current != null. The queries go to the engine's system endpoint, which is addressed by a reserved device id that is never registered, so it never appears in the device list and no device can answer a question about the platform. That endpoint lands separately in the engine repository under the same ticket. Touch pressure is answered at platform scope rather than per touchscreen because that is the scope at which the answer exists: every platform sources it from a device model or an OS API property rather than by enumerating digitizers. A per-instance property can be added later, preferring a per-device answer over this one, without changing what is here. The commands and the InputCapabilitySupport tristate are internal rather than public, unlike the rest of the Commands folder. They target the system endpoint, so there is no custom device for a user to answer them for, and keeping them internal avoids freezing a capability model that the low level input API is expected to revisit. The engine answers with a tristate whose Unknown is zero, so an unimplemented query reads as "we do not know" rather than a confident false; the public properties collapse anything other than Supported to false. Both the properties and the plumbing are gated on UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES, so the API is absent rather than present-and-always-false on engine versions that cannot answer. The version expression is currently the local development engine and must be updated to whichever version the engine side actually ships in. ExecuteGlobalCommand is replaced by ExecuteSystemCommand. The former had no callers, having been orphaned when UseWindowsGamingInputCommand was removed by ISXB-927, and its premise was wrong: it addressed device id 0 on the assumption that the engine routes such commands by FourCC alone, but InputDeviceIOCTL resolves the id against the device registry and 0 is the invalid-device sentinel, so nothing sent there could ever be answered. Capabilities cannot change while the process runs, so each is queried at most once. The cache lives on InputManager rather than in a static, so a domain reload or a test installing a different runtime discards it without needing an explicit reset hook. Tests cover each state including Unknown, that the properties answer with no device added, that a query is never delivered to a real device, that repeated reads issue one command, that nothing answering reports false rather than throwing, and that the enum values, the FourCC codes and the payload size all agree with the engine's own constants. --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 164 ++++++++++++++++++ .../Unity.InputSystem.Tests.asmdef | 5 + Packages/com.unity.inputsystem/CHANGELOG.md | 19 ++ .../corresponding-old-new-api.md | 31 +++- .../Commands/InputCapabilitySupport.cs | 43 +++++ .../Commands/InputCapabilitySupport.cs.meta | 11 ++ .../Commands/QueryMouseSupportedCommand.cs | 45 +++++ .../QueryMouseSupportedCommand.cs.meta | 11 ++ .../Commands/QueryPenSupportedCommand.cs | 45 +++++ .../Commands/QueryPenSupportedCommand.cs.meta | 11 ++ .../QueryTouchPressureSupportedCommand.cs | 49 ++++++ ...QueryTouchPressureSupportedCommand.cs.meta | 11 ++ .../InputSystem/Runtime/Devices/Mouse.cs | 60 +++++++ .../InputSystem/Runtime/Devices/Pen.cs | 73 ++++++++ .../Runtime/Devices/Touchscreen.cs | 54 ++++++ .../InputSystem/Runtime/InputManager.cs | 71 +++++++- .../InputSystem/Unity.InputSystem.asmdef | 5 + 17 files changed, 701 insertions(+), 7 deletions(-) create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs.meta diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index ed90efe605..41122cd6a2 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -5895,4 +5895,168 @@ public unsafe void Devices_DoesntErrorOutOnMaxTouchCount() BeginTouch(i, new Vector2(i * 1.0f, i * 2.0f), time: 0); }, Throws.Nothing); } + +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + // Platform capability queries. These are addressed to the engine's system endpoint rather than + // to a device, because they answer "can this platform do X" rather than "is an X connected". + // What a given platform actually answers is asserted natively in the engine repository, from + // PlatformDependent, where the file location gates the test to that platform. + + private unsafe void AnswerCapabilityQuery(FourCC type, InputCapabilitySupport answer) + { + runtime.SetDeviceCommandCallback(NativeInputCapabilities.systemDeviceId, + (id, command) => + { + if (command->type != type) + return InputDeviceCommand.GenericFailure; + + *(InputCapabilitySupport*)((byte*)command + InputDeviceCommand.kBaseCommandSize) = answer; + return InputDeviceCommand.GenericSuccess; + }); + } + + [Test] + [Category("Devices")] + [TestCase(InputCapabilitySupport.Supported, true)] + [TestCase(InputCapabilitySupport.NotSupported, false)] + // Unknown collapses to false: the platform has not answered, and a maybe is not something a + // bool property can express. + [TestCase(InputCapabilitySupport.Unknown, false)] + public void Devices_PenIsSupported_ReflectsWhatThePlatformAnswers(InputCapabilitySupport answer, bool expected) + { + AnswerCapabilityQuery(QueryPenSupportedCommand.Type, answer); + + Assert.That(Pen.isSupported, Is.EqualTo(expected)); + } + + [Test] + [Category("Devices")] + [TestCase(InputCapabilitySupport.Supported, true)] + [TestCase(InputCapabilitySupport.NotSupported, false)] + [TestCase(InputCapabilitySupport.Unknown, false)] + public void Devices_MouseIsSupported_ReflectsWhatThePlatformAnswers(InputCapabilitySupport answer, bool expected) + { + AnswerCapabilityQuery(QueryMouseSupportedCommand.Type, answer); + + Assert.That(Mouse.isSupported, Is.EqualTo(expected)); + } + + [Test] + [Category("Devices")] + [TestCase(InputCapabilitySupport.Supported, true)] + [TestCase(InputCapabilitySupport.NotSupported, false)] + [TestCase(InputCapabilitySupport.Unknown, false)] + public void Devices_TouchscreenIsPressureSupported_ReflectsWhatThePlatformAnswers(InputCapabilitySupport answer, bool expected) + { + AnswerCapabilityQuery(QueryTouchPressureSupportedCommand.Type, answer); + + Assert.That(Touchscreen.isPressureSupported, Is.EqualTo(expected)); + } + + // The properties describe the platform, not a device, so they must answer without one. This is + // the case that separates them from Device.current != null. + [Test] + [Category("Devices")] + public void Devices_CapabilityQueries_AreAnsweredWithNoDeviceAdded() + { + AnswerCapabilityQuery(QueryPenSupportedCommand.Type, InputCapabilitySupport.Supported); + + Assert.That(InputSystem.devices, Is.Empty); + Assert.That(Pen.isSupported, Is.True); + Assert.That(Pen.current, Is.Null); + } + + // The endpoint is addressed by a reserved id. A capability query must not be delivered to a + // real device, which would let a device answer a question about the platform. + [Test] + [Category("Devices")] + public unsafe void Devices_CapabilityQueries_AreNotDeliveredToDevices() + { + var pen = InputSystem.AddDevice(); + var receivedByDevice = 0; + runtime.SetDeviceCommandCallback(pen, + (id, command) => + { + if (command->type == QueryPenSupportedCommand.Type) + ++receivedByDevice; + return InputDeviceCommand.GenericFailure; + }); + AnswerCapabilityQuery(QueryPenSupportedCommand.Type, InputCapabilitySupport.Supported); + + Assert.That(Pen.isSupported, Is.True); + Assert.That(receivedByDevice, Is.Zero); + } + + // A platform capability cannot change while the application runs, so reading the property + // repeatedly must not keep issuing commands. + [Test] + [Category("Devices")] + public unsafe void Devices_CapabilityQueries_AreOnlyIssuedOnce() + { + var queryCount = 0; + runtime.SetDeviceCommandCallback(NativeInputCapabilities.systemDeviceId, + (id, command) => + { + if (command->type != QueryPenSupportedCommand.Type) + return InputDeviceCommand.GenericFailure; + + ++queryCount; + *(InputCapabilitySupport*)((byte*)command + InputDeviceCommand.kBaseCommandSize) = + InputCapabilitySupport.Supported; + return InputDeviceCommand.GenericSuccess; + }); + + Assert.That(Pen.isSupported, Is.True); + Assert.That(Pen.isSupported, Is.True); + Assert.That(Pen.isSupported, Is.True); + + Assert.That(queryCount, Is.EqualTo(1)); + } + + // Nothing answers, which is what an engine without the endpoint looks like. The property must + // report false rather than throwing, and must not retry on every read. + [Test] + [Category("Devices")] + public void Devices_CapabilityQueries_ReportFalseWhenNothingAnswers() + { + Assert.That(Pen.isSupported, Is.False); + Assert.That(Mouse.isSupported, Is.False); + Assert.That(Touchscreen.isPressureSupported, Is.False); + } + + // Nothing generates the mirror of the engine's enum, and a reordering would silently invert + // Supported and NotSupported across the boundary. The engine pins the same values from its side. + [Test] + [Category("Devices")] + public void Devices_CapabilitySupport_MatchesTheEngineWireValues() + { + Assert.That((byte)InputCapabilitySupport.Unknown, Is.EqualTo((byte)CapabilityState.Unknown)); + Assert.That((byte)InputCapabilitySupport.NotSupported, Is.EqualTo((byte)CapabilityState.NotSupported)); + Assert.That((byte)InputCapabilitySupport.Supported, Is.EqualTo((byte)CapabilityState.Supported)); + } + + // Same reasoning for the codes: the package spells them as FourCC characters, matching every + // other command in the Commands folder, while the engine declares them as integer constants. + [Test] + [Category("Devices")] + public void Devices_CapabilityQueryCodes_MatchTheEngineCodes() + { + Assert.That((int)QueryPenSupportedCommand.Type, Is.EqualTo(NativeInputCapabilities.queryPenSupported)); + Assert.That((int)QueryMouseSupportedCommand.Type, Is.EqualTo(NativeInputCapabilities.queryMouseSupported)); + Assert.That((int)QueryTouchPressureSupportedCommand.Type, + Is.EqualTo(NativeInputCapabilities.queryTouchPressureSupported)); + } + + // The payload the package sends must be exactly the one byte the engine's payload validation + // accepts. The base command header is stripped before it reaches native. + [Test] + [Category("Devices")] + public void Devices_CapabilityQueryPayload_IsOneByteAfterTheCommandHeader() + { + Assert.That(QueryPenSupportedCommand.kSize - InputDeviceCommand.kBaseCommandSize, Is.EqualTo(1)); + Assert.That(QueryMouseSupportedCommand.kSize - InputDeviceCommand.kBaseCommandSize, Is.EqualTo(1)); + Assert.That(QueryTouchPressureSupportedCommand.kSize - InputDeviceCommand.kBaseCommandSize, Is.EqualTo(1)); + } + +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES } diff --git a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef index 7c7a31cd0a..cf1d4ef1b2 100644 --- a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef +++ b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef @@ -81,6 +81,11 @@ "name": "Unity", "expression": "6000.5.0a8", "define": "UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS" + }, + { + "name": "Unity", + "expression": "6000.7.0a6", + "define": "UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES" } ], "noEngineReferences": false diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 9a6d3514da..63aafdedca 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - yyyy-mm-dd +### Added + +- Added `Pen.isSupported`, `Mouse.isSupported` and `Touchscreen.isPressureSupported`, reporting what the current platform is capable of rather than what is connected right now. Use them to decide whether to offer device-specific functionality at all. These are not drop-in replacements for the legacy `UnityEngine.Input.stylusTouchSupported` and `Input.mousePresent`: those conflated capability with presence, and on platforms where legacy did real hardware detection the new properties report capability instead, so they can be `true` where legacy was `false`. To act on input, check `Device.current != null && Device.current.enabled` rather than `Device.current != null` alone; a non-null `current` means a device object is registered, which several platforms do unconditionally, and `enabled` is what tells you it is active. The properties require an Editor version that can answer the query and are not compiled in on older versions. `Pen.isSupported` and `Touchscreen.isPressureSupported` come from [ISX-2046](https://jira.unity3d.com/browse/ISX-2046); `Mouse.isSupported` is the short-term scope of [ISX-2079](https://jira.unity3d.com/browse/ISX-2079), whose remaining half is a real presence primitive rather than a capability one + + ```csharp + // Before, legacy input. Reported true on any iPad new enough to pair a Pencil, whether or + // not one was paired, so the two questions could not be told apart. + if (Input.stylusTouchSupported) { } + if (Input.mousePresent) { } + + // After. Capability is its own question with its own answer. + if (Pen.isSupported) { } // could a pen ever work on this platform + if (Mouse.isSupported) { } // could a mouse ever work on this platform + + // Acting on input is a different question, and needs both parts. A non-null current means a + // device object is registered, not that hardware is attached; enabled is what says it is active. + if (Pen.current != null && Pen.current.enabled) { } + ``` + ### Fixed - Fixed the Inspector help button for a selected `.inputactions` asset ("Open Reference for Input Action Importer") opening a missing documentation page; it now links to the Action Assets manual page [UUM-149518](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-149518) diff --git a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md index dcad91390f..46fccea658 100644 --- a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md +++ b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md @@ -67,6 +67,29 @@ Directly reading hardware controls bypasses the new Input System's action-based [`Input.imeIsSelected`](https://docs.unity3d.com/ScriptReference/Input-imeIsSelected.html)|Use: [`Keyboard.current.imeSelected`](xref:UnityEngine.InputSystem.Keyboard) [`Input.inputString`](https://docs.unity3d.com/ScriptReference/Input-inputString.html)|Subscribe to the [`Keyboard.onTextInput`](xref:UnityEngine.InputSystem.Keyboard) event:
`Keyboard.current.onTextInput += character => /* ... */;` +## Device capability and device availability + +Several old Input Manager properties, such as `Input.mousePresent` and `Input.stylusTouchSupported`, answered +two questions at once, and answered them differently depending on the platform. On some platforms they were a +hardcoded constant meaning roughly "this platform has this kind of device", and on others they performed real +hardware detection. + +The new Input System separates the two: + +- **Can this platform do it at all?** Use the capability properties: [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse), + [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) and [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen). + These never change while the application runs, so read them once and decide whether to offer device-specific + functionality. +- **Is a device available to read from right now?** Use `Device.current != null && Device.current.enabled`. + Both parts matter. A non-null `current` only means a device object is registered, which some platforms do + unconditionally regardless of whether hardware is attached, and `enabled` is what tells you the device is + active. The Device Simulator is a good example of the difference: while simulating a touch device it disables + the native mouse and pen without removing them, so `current` stays non-null while `enabled` becomes false. + +Because the old properties mixed these two meanings, the capability properties are **not drop-in replacements**. +On platforms where the old property performed real detection, the new property answers the capability question +instead, so it can be `true` where the old one was `false`. The tables below note this per API. + ## Mouse `MonoBehaviour.OnMouse` events, such as [MonoBehaviour.OnMouseDown](https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseDown.html), are supported in Unity 6.4 and later. @@ -77,19 +100,19 @@ Directly reading hardware controls bypasses the new Input System's action-based [`Input.GetMouseButtonDown`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html)
Example: `Input.GetMouseButtonDown(0)`|Use [`wasPressedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasPressedThisFrame` [`Input.GetMouseButtonUp`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonUp.html)
Example: `Input.GetMouseButtonUp(0)`|Use [`wasReleasedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasReleasedThisFrame` [`Input.mousePosition`](https://docs.unity3d.com/ScriptReference/Input-mousePosition.html)|Use [`Mouse.current.position.ReadValue()`](xref:UnityEngine.InputSystem.Mouse)
Example: `Vector2 position = Mouse.current.position.ReadValue();`
**Note:** Mouse simulation from touch isn't implemented yet. -[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|No corresponding API yet. +[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|Use [`isSupported`](xref:UnityEngine.InputSystem.Mouse) to check whether the platform supports mouse input at all.
Example: `if (Mouse.isSupported) ShowMouseSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Input System does not currently deliver mouse input on iOS, iPadOS or visionOS, so `Mouse.isSupported` is `false` there even though the platform itself supports indirect mice. Requires a recent Editor version. ## Touch and Pen |Input Manager (Old)|Input System (New)| |--|--| [`Input.GetTouch`](https://docs.unity3d.com/ScriptReference/Input.GetTouch.html)
For example:
`Touch touch = Input.GetTouch(0);`
`Vector2 touchPos = touch.position;`|Use [`EnhancedTouch.Touch.activeTouches[i]`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
Example: `Vector2 touchPos = EnhancedTouch.Touch.activeTouches[0].position;`
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport). -[`Input.multiTouchEnabled`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|No corresponding API yet. +[`Input.multiTouchEnabled`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|There is no direct equivalent, because this is a setting rather than a hardware capability. To get the same first-touch-wins behaviour, read [`primaryTouch`](xref:UnityEngine.InputSystem.Touchscreen) instead of iterating all touches, or bind to `/primaryTouch`.
Example: `if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)`
**Note:** Two differences from setting `Input.multiTouchEnabled = false`. First, `primaryTouch` filters only itself: [`touches`](xref:UnityEngine.InputSystem.Touchscreen), the `/touch*` bindings and [`EnhancedTouch`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch) still report every finger, whereas the legacy setting suppressed additional touches globally. Second, when the finger that started the primary touch lifts while other fingers are still down, the primary touch is retained rather than ended until the last finger is released, so a control bound to it stays actuated in the meantime. [`Input.simulateMouseWithTouches`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|No corresponding API yet. -[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|No corresponding API yet. +[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|Use [`isSupported`](xref:UnityEngine.InputSystem.Pen) to check whether the platform supports pen input at all.
Example: `if (Pen.isSupported) ShowPenSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Requires a recent Editor version. [`Input.touchCount`](https://docs.unity3d.com/ScriptReference/Input-touchCount.html)|[`EnhancedTouch.Touch.activeTouches.Count`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouchSupport.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) [`Input.touches`](https://docs.unity3d.com/scriptreference/input-touches.html)|[`EnhancedTouch.Touch.activeTouches`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) -[`Input.touchPressureSupported`](https://docs.unity3d.com/ScriptReference/Input-touchPressureSupported.html)|No corresponding API yet. +[`Input.touchPressureSupported`](https://docs.unity3d.com/ScriptReference/Input-touchPressureSupported.html)|Use [`isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen) to check whether the platform delivers a real pressure value with touch input.
Example: `if (Touchscreen.isPressureSupported) UsePressureForBrushWidth();`
**Note:** When this is `false`, [`pressure`](xref:UnityEngine.InputSystem.Controls.TouchControl) reports a constant `1` while a finger is down rather than a measured value. This is a platform-wide answer rather than a per-device one. Requires a recent Editor version. [`Input.touchSupported`](https://docs.unity3d.com/ScriptReference/Input-touchSupported.html)|[`Touchscreen.current != null`](xref:UnityEngine.InputSystem.Touchscreen) [`Input.backButtonLeavesApp`](https://docs.unity3d.com/ScriptReference/Input-backButtonLeavesApp.html)|No corresponding API yet. [`GetPenEvent`](https://docs.unity3d.com/ScriptReference/Input.GetPenEvent.html)
[`GetLastPenContactEvent`](https://docs.unity3d.com/ScriptReference/Input.GetLastPenContactEvent.html)
[`ResetPenEvents`](https://docs.unity3d.com/ScriptReference/Input.ResetPenEvents.html)
[`ClearLastPenContactEvent`](https://docs.unity3d.com/ScriptReference/Input.ClearLastPenContactEvent.html)|Use: [`Pen.current`](xref:UnityEngine.InputSystem.Pen)
See the [Pen, tablet and stylus support](devices-pen.md) docs for more information. diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs new file mode 100644 index 0000000000..643ea8f140 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs @@ -0,0 +1,43 @@ +////TODO: the UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES version expression in +//// Unity.InputSystem.asmdef and Unity.InputSystem.Tests.asmdef is still a local development +//// Editor version. It must be set to the version that actually ships the engine side of +//// ISX-2046 before this merges, or the gate will enable code referencing engine symbols that +//// standard builds of that version do not have. Recorded here because asmdef files are JSON +//// and cannot carry a comment of their own. + +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES +namespace UnityEngine.InputSystem.LowLevel +{ + /// + /// Answer to a platform capability query, meaning what the platform can deliver rather than + /// what is currently connected. + /// + /// + /// Mirrors CapabilityState in the engine's Modules/Input/InputDeviceIOCTL.h, whose + /// wire values are pinned by tests on both sides. is zero so that an + /// unwritten payload, or a platform that has not implemented a query, reads as "we do not know" + /// rather than as a confident . + /// + /// The value space is open. Treat anything other than as not supported + /// rather than rejecting it, because a newer engine may answer with a value this version of the + /// package does not know about. + /// + internal enum InputCapabilitySupport : byte + { + /// + /// The platform has no answer, typically because it has not implemented the query yet. + /// + Unknown = 0, + + /// + /// The platform definitively cannot deliver it. + /// + NotSupported = 1, + + /// + /// The platform definitively can deliver it. + /// + Supported = 2 + } +} +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs.meta new file mode 100644 index 0000000000..a929bf2129 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02d29d523ce34539874c1f75cf4e7093 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs new file mode 100644 index 0000000000..00d023cb86 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs @@ -0,0 +1,45 @@ +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES +using System.Runtime.InteropServices; +using UnityEngine.InputSystem.Utilities; + +namespace UnityEngine.InputSystem.LowLevel +{ + /// + /// Queries whether the platform can deliver mouse input at all, as opposed to whether a mouse is + /// connected right now. + /// + /// + /// Addressed to the engine's system endpoint rather than to a device, so it is sent through + /// rather than + /// . Presence is answered by the device list. + /// + /// The FourCC must match kInputFourCCIOCTLQueryMouseSupported in the engine's + /// Modules/Input/InputFourCC.h. + /// + /// + [StructLayout(LayoutKind.Explicit, Size = kSize)] + internal struct QueryMouseSupportedCommand : IInputDeviceCommandInfo + { + public static FourCC Type => new FourCC('Q', 'M', 'O', 'U'); + + internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(byte); + + [FieldOffset(0)] + public InputDeviceCommand baseCommand; + + [FieldOffset(InputDeviceCommand.kBaseCommandSize)] + public InputCapabilitySupport isSupported; + + public FourCC typeStatic => Type; + + public static QueryMouseSupportedCommand Create() + { + return new QueryMouseSupportedCommand + { + baseCommand = new InputDeviceCommand(Type, kSize), + isSupported = InputCapabilitySupport.Unknown + }; + } + } +} +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs.meta new file mode 100644 index 0000000000..3990362cc1 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e57d51102344b9ba77c44776fb91b3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs new file mode 100644 index 0000000000..a247b147f6 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs @@ -0,0 +1,45 @@ +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES +using System.Runtime.InteropServices; +using UnityEngine.InputSystem.Utilities; + +namespace UnityEngine.InputSystem.LowLevel +{ + /// + /// Queries whether the platform can deliver pen input at all, as opposed to whether a pen is + /// connected right now. + /// + /// + /// Addressed to the engine's system endpoint rather than to a device, so it is sent through + /// rather than + /// . Presence is answered by the device list. + /// + /// The FourCC must match kInputFourCCIOCTLQueryPenSupported in the engine's + /// Modules/Input/InputFourCC.h. + /// + /// + [StructLayout(LayoutKind.Explicit, Size = kSize)] + internal struct QueryPenSupportedCommand : IInputDeviceCommandInfo + { + public static FourCC Type => new FourCC('Q', 'P', 'E', 'N'); + + internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(byte); + + [FieldOffset(0)] + public InputDeviceCommand baseCommand; + + [FieldOffset(InputDeviceCommand.kBaseCommandSize)] + public InputCapabilitySupport isSupported; + + public FourCC typeStatic => Type; + + public static QueryPenSupportedCommand Create() + { + return new QueryPenSupportedCommand + { + baseCommand = new InputDeviceCommand(Type, kSize), + isSupported = InputCapabilitySupport.Unknown + }; + } + } +} +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs.meta new file mode 100644 index 0000000000..4bc5f97fca --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 37e5432c8c304ccf8ff9d9ee9d2f5945 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs new file mode 100644 index 0000000000..3c59260e58 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs @@ -0,0 +1,49 @@ +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES +using System.Runtime.InteropServices; +using UnityEngine.InputSystem.Utilities; + +namespace UnityEngine.InputSystem.LowLevel +{ + /// + /// Queries whether the platform delivers a real pressure value with touch input, as opposed to + /// the constant 1.0 reported by platforms that have touch but no pressure. + /// + /// + /// Addressed to the engine's system endpoint rather than to a device, so it is sent through + /// rather than + /// . + /// + /// This is answered at platform scope rather than per touchscreen, because that is the scope at + /// which the answer exists: every platform sources it from a device-model or OS-API property + /// rather than by enumerating digitizers. + /// + /// The FourCC must match kInputFourCCIOCTLQueryTouchPressureSupported in the engine's + /// Modules/Input/InputFourCC.h. + /// + /// + [StructLayout(LayoutKind.Explicit, Size = kSize)] + internal struct QueryTouchPressureSupportedCommand : IInputDeviceCommandInfo + { + public static FourCC Type => new FourCC('Q', 'T', 'P', 'S'); + + internal const int kSize = InputDeviceCommand.kBaseCommandSize + sizeof(byte); + + [FieldOffset(0)] + public InputDeviceCommand baseCommand; + + [FieldOffset(InputDeviceCommand.kBaseCommandSize)] + public InputCapabilitySupport isSupported; + + public FourCC typeStatic => Type; + + public static QueryTouchPressureSupportedCommand Create() + { + return new QueryTouchPressureSupportedCommand + { + baseCommand = new InputDeviceCommand(Type, kSize), + isSupported = InputCapabilitySupport.Unknown + }; + } + } +} +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs.meta new file mode 100644 index 0000000000..a874646728 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e7f6f16acbb4932bcd1cd9a52ccf843 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs index 7cbfd52515..51c15cbfe0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs @@ -257,6 +257,66 @@ public class Mouse : Pointer, IInputStateCallbackReceiver /// public new static Mouse current { get; private set; } +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// + /// Whether the current platform can deliver mouse input at all, regardless of whether a + /// mouse is connected right now. + /// + /// True if the platform supports mouse input. + /// + /// This answers "could a mouse work here", which is the question to ask when deciding + /// whether to offer mouse-specific functionality in a UI. To ask whether a mouse is + /// available to read from right now, check both and + /// , as described below. + /// + /// Legacy UnityEngine.Input conflated the two under Input.mousePresent, which is + /// a hardcoded true on Windows, macOS, Linux and WebGL, and genuine detection only on iOS, + /// Android, UWP and the consoles. + /// + /// A false value means either that the platform does not support mouse input or that it could + /// not determine the answer. The two are deliberately not distinguished, because a caller + /// deciding whether to offer functionality wants the same behaviour in both cases. It does not + /// mean the Editor was unable to ask, since this property only exists on Editor versions that + /// can. + /// + /// The answer cannot change while the application runs, so it is queried once and cached. + /// + /// Three checks are easy to confuse, in increasing strictness. This property asks whether the + /// platform could ever deliver mouse input. Mouse.current != null asks only whether a device + /// object is registered, which is not the same as hardware being attached, since several + /// platforms register unconditionally. Mouse.current != null && Mouse.current.enabled + /// adds whether it is currently active, and that is the check to make before acting on input. + /// + /// The two clauses catch different things. current != null is what catches a disconnect, + /// since removal nulls current, though that relies on the platform reporting removal at all. + /// enabled does not become false on unplug: it tracks whether the device is enabled for + /// input, through and . + /// + /// The Device Simulator makes that visible: while simulating a touch device it disables the native + /// Mouse without removing it, so current stays non-null while enabled is false. + /// Read it from the main thread: resolving mouse support can require a platform API that is + /// main-thread only, and the first read is the one that resolves it. + /// + /// + /// + /// + /// // Whether a mouse could work here, which is the question to ask when deciding whether to + /// // show mouse-specific settings. True on desktop platforms with no mouse plugged in. + /// if (Mouse.isSupported) + /// ShowMouseSensitivitySetting(); + /// + /// // Whether one is usable right now, which needs both parts: a non-null current only means a + /// // device object is registered, and enabled is what tells you it is active. + /// if (Mouse.current != null && Mouse.current.enabled) + /// Debug.Log(Mouse.current.position.ReadValue()); + /// + /// + /// + /// + /// + public static bool isSupported => InputSystem.manager.IsMouseSupported(); +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// /// Called when the mouse becomes the current mouse. /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs index 9c0f8e5a07..bcc07c1526 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs @@ -322,6 +322,79 @@ public class Pen : Pointer /// public new static Pen current { get; internal set; } +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// + /// Whether the current platform can deliver pen input at all, regardless of whether a pen is + /// connected right now. + /// + /// True if the platform supports pen input. + /// + /// This answers "could a pen work here", which is the question to ask when deciding whether + /// to offer pen-specific functionality in a UI. To ask whether a pen is available to read + /// from right now, check both and , + /// as described below. + /// + /// Legacy UnityEngine.Input conflated the two: Input.stylusTouchSupported + /// reports true on any iPad new enough to pair an Apple Pencil, whether or not one is paired. + /// + /// A false value means either that the platform does not support pen input or that it could not + /// determine the answer. The two are deliberately not distinguished, because a caller deciding + /// whether to offer functionality wants the same behaviour in both cases. It does not mean the + /// Editor was unable to ask, since this property only exists on Editor versions that can. + /// + /// The answer cannot change while the application runs, so it is queried once and cached. + /// + /// Three checks are easy to confuse, in increasing strictness. This property asks whether the + /// platform could ever deliver pen input. Pen.current != null asks only whether a device + /// object is registered, which is not the same as hardware being attached, since several + /// platforms register unconditionally. Pen.current != null && Pen.current.enabled + /// adds whether it is currently active, and that is the check to make before acting on input. + /// + /// The two clauses catch different things. current != null is what catches a disconnect, + /// since removal nulls current, though that relies on the platform reporting removal at all. + /// enabled does not become false on unplug: it tracks whether the device is enabled for + /// input, through and . + /// + /// The Device Simulator makes that visible: while simulating a touch device it disables the native + /// Pen without removing it, so current stays non-null while enabled is false. + /// Read it from the main thread: resolving pen support can require a platform API that is + /// main-thread only, and the first read is the one that resolves it. + /// + /// + /// + /// + /// using UnityEngine; + /// using UnityEngine.InputSystem; + /// + /// public class ExampleScript : MonoBehaviour + /// { + /// private bool m_ShowPenSettings; + /// + /// void Start() + /// { + /// // Decide once whether to offer pen-specific functionality at all. This is true on + /// // a platform that can deliver pen input, even when no pen is connected yet. + /// m_ShowPenSettings = Pen.isSupported; + /// } + /// + /// void Update() + /// { + /// // Whether a pen is usable right now is a different question, and needs both a + /// // registered device and that device being enabled. + /// if (Pen.current != null && Pen.current.enabled && Pen.current.tip.wasPressedThisFrame) + /// { + /// // handle the pen tip being pressed + /// } + /// } + /// } + /// + /// + /// + /// + /// + public static bool isSupported => InputSystem.manager.IsPenSupported(); +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// /// Return the given pen button. /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs index 7a82e6a177..88214358d7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs @@ -520,6 +520,60 @@ protected TouchControl[] touchControlArray /// Current touch screen. public new static Touchscreen current { get; internal set; } +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// + /// Whether the current platform delivers a real pressure value with touch input. + /// + /// True if the platform supports touch pressure. + /// + /// When this is false, reports a constant 1 while a + /// finger is down rather than a measured value, so treating it as an analog signal produces + /// no variation. Platforms with no touchscreen at all also report false. + /// + /// This is deliberately a platform-scoped answer rather than a per-device one, because that + /// is the scope at which it exists: every platform sources it from a device model or an OS + /// API property rather than by enumerating digitizers. Read it as "this platform delivers + /// pressure", not "this particular touchscreen does". + /// + /// A false value means either that the platform does not deliver touch pressure or that it could + /// not determine the answer. The two are deliberately not distinguished, because a caller + /// deciding whether to treat pressure as an analog signal wants the same behaviour in both + /// cases. It does not mean the Editor was unable to ask, since this property only exists on + /// Editor versions that can. + /// + /// The "could not determine" case is real rather than theoretical: on some platforms the OS + /// supplies a pressure value whether or not the attached digitizer measures one, so only a + /// per-device query could tell a real reading from a constant, and the platform reports that it + /// does not know. + /// + /// The answer cannot change while the application runs, so it is queried once and cached. + /// + /// Note that Touchscreen.current != null does not mean a touchscreen is physically present. + /// Some platforms register one unconditionally, so it can be non-null on hardware with no touch at + /// all. Before acting on input, check Touchscreen.current != null && + /// Touchscreen.current.enabled: a non-null current only means a device object is + /// registered, and enabled is what says it is active. + /// Read it from the main thread: resolving touch pressure support can require a platform API that is + /// main-thread only, and the first read is the one that resolves it. + /// + /// + /// + /// + /// // Only treat pressure as an analog signal where the platform actually measures it. + /// // Elsewhere it is a constant 1 while the finger is down, so this would do nothing. + /// var brushWidth = Touchscreen.isPressureSupported + /// && Touchscreen.current != null && Touchscreen.current.enabled + /// ? Touchscreen.current.primaryTouch.pressure.ReadValue() * maxBrushWidth + /// : defaultBrushWidth; + /// + /// + /// + /// + /// + /// + public static bool isPressureSupported => InputSystem.manager.IsTouchPressureSupported(); +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// /// The current global settings for Touchscreen devices. /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs index 994538ecc2..23b9edc444 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs @@ -3125,14 +3125,79 @@ internal void ApplyActions() DelegateHelpers.InvokeCallbacksSafe(ref m_ActionsChangedListeners, k_InputOnActionsChangeMarker, "InputSystem.onActionsChange"); } - internal unsafe long ExecuteGlobalCommand(ref TCommand command) +#if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + /// + /// Sends a command to the engine's system endpoint, which answers questions about the + /// platform rather than about any one device. + /// + /// + /// This replaces an earlier ExecuteGlobalCommand, which addressed device id 0 on the premise + /// that the engine routes such commands by FourCC alone. It does not: InputDeviceIOCTL + /// resolves the id against the device registry and 0 is the invalid-device sentinel, so a + /// command sent there could never be answered. That helper had no callers, having been + /// orphaned when UseWindowsGamingInputCommand was removed. + /// + /// The endpoint is addressed by a reserved device id that is deliberately never registered, + /// so it never appears in the device list. On an engine without the endpoint the id is simply + /// unknown and the command fails, which callers read as "we do not know". + /// + internal unsafe long ExecuteSystemCommand(ref TCommand command) where TCommand : struct, IInputDeviceCommandInfo { var ptr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command); - // device id is irrelevant as we route it based on fourcc internally - return InputRuntime.s_Instance.DeviceCommand(0, ptr); + return InputRuntime.s_Instance.DeviceCommand(NativeInputCapabilities.systemDeviceId, ptr); } + // Platform capabilities cannot change while the process runs, so each is queried at most + // once. These are instance fields rather than statics on purpose: a domain reload or a test + // installing a different runtime builds a new InputManager, which discards the cache without + // needing an explicit reset hook. A failed query caches as Unknown so that an engine which + // cannot answer is asked once rather than on every read. + private InputCapabilitySupport? m_PenSupported; + private InputCapabilitySupport? m_MouseSupported; + private InputCapabilitySupport? m_TouchPressureSupported; + + internal bool IsPenSupported() + { + if (!m_PenSupported.HasValue) + { + var command = QueryPenSupportedCommand.Create(); + m_PenSupported = ExecuteSystemCommand(ref command) >= 0 + ? command.isSupported + : InputCapabilitySupport.Unknown; + } + + return m_PenSupported.Value == InputCapabilitySupport.Supported; + } + + internal bool IsMouseSupported() + { + if (!m_MouseSupported.HasValue) + { + var command = QueryMouseSupportedCommand.Create(); + m_MouseSupported = ExecuteSystemCommand(ref command) >= 0 + ? command.isSupported + : InputCapabilitySupport.Unknown; + } + + return m_MouseSupported.Value == InputCapabilitySupport.Supported; + } + + internal bool IsTouchPressureSupported() + { + if (!m_TouchPressureSupported.HasValue) + { + var command = QueryTouchPressureSupportedCommand.Create(); + m_TouchPressureSupported = ExecuteSystemCommand(ref command) >= 0 + ? command.isSupported + : InputCapabilitySupport.Unknown; + } + + return m_TouchPressureSupported.Value == InputCapabilitySupport.Supported; + } + +#endif // UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES + internal void AddAvailableDevicesThatAreNowRecognized() { for (var i = 0; i < m_AvailableDeviceCount; ++i) diff --git a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef index 84e2b9faeb..d8f5257253 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef +++ b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef @@ -101,6 +101,11 @@ "name": "Unity", "expression": "6000.5.0a8", "define": "UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS" + }, + { + "name": "Unity", + "expression": "6000.7.0a6", + "define": "UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES" } ], "noEngineReferences": false From 0a68b7625e66cc6e775ec504aaa6ae4961dda65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Wed, 19 Aug 2026 15:02:46 +0200 Subject: [PATCH 2/7] Trim API and changelog docs to what a caller needs Review feedback: the docs had accumulated the reasoning behind the change rather than what a reader needs at the call site. - InputManager.ExecuteSystemCommand: drop the remarks block. Its first paragraph was history about the ExecuteGlobalCommand it replaced, and the second restated what the three call sites just below already show. - Query{Pen,Mouse,TouchPressure}SupportedCommand: drop the presence clause that duplicated the summary directly above it, and the platform-scope rationale that the engine's PlatformSystemCapabilities.h already owns. Kept the routing note, which is what stops these being sent through InputDevice.ExecuteCommand like every sibling in the folder, and the FourCC-must-match constraint, which is hand-mirrored across two repos. - InputCapabilitySupport: drop the TODO about the unpinned version expression. The merge gate is the open review thread on the asmdef, which is enforced; a comment is not. - Mouse/Pen/Touchscreen.isSupported: trim the remarks to what a caller needs. The legacy Input Manager comparison belongs in the migration documentation, which the remaining text now points at for the availability check. Kept what false means, the concrete undetermined case, and the main-thread contract. - InputManager.ExecuteSystemCommand: send through m_Runtime rather than InputRuntime.s_Instance, matching every other DeviceCommand call site and staying correct when a test installs its own runtime. - CHANGELOG: drop the feature explanation and the code snippet, reference the migration documentation instead, and cite ISX-2079 alongside ISX-2046 since Mouse.isSupported is that ticket's deliverable. - corresponding-old-new-api.md: qualify the property names in the migration table, link the legacy properties, and unpin a Unity 5.5 docs URL. --- Packages/com.unity.inputsystem/CHANGELOG.md | 17 +--------- .../corresponding-old-new-api.md | 13 ++++---- .../Commands/InputCapabilitySupport.cs | 7 ---- .../Commands/QueryMouseSupportedCommand.cs | 2 +- .../Commands/QueryPenSupportedCommand.cs | 2 +- .../QueryTouchPressureSupportedCommand.cs | 4 --- .../InputSystem/Runtime/Devices/Mouse.cs | 33 +++++-------------- .../InputSystem/Runtime/Devices/Pen.cs | 31 +++++------------ .../Runtime/Devices/Touchscreen.cs | 29 +++++++--------- .../InputSystem/Runtime/InputManager.cs | 13 +------- 10 files changed, 38 insertions(+), 113 deletions(-) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 63aafdedca..98a3ff7e13 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -9,22 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added -- Added `Pen.isSupported`, `Mouse.isSupported` and `Touchscreen.isPressureSupported`, reporting what the current platform is capable of rather than what is connected right now. Use them to decide whether to offer device-specific functionality at all. These are not drop-in replacements for the legacy `UnityEngine.Input.stylusTouchSupported` and `Input.mousePresent`: those conflated capability with presence, and on platforms where legacy did real hardware detection the new properties report capability instead, so they can be `true` where legacy was `false`. To act on input, check `Device.current != null && Device.current.enabled` rather than `Device.current != null` alone; a non-null `current` means a device object is registered, which several platforms do unconditionally, and `enabled` is what tells you it is active. The properties require an Editor version that can answer the query and are not compiled in on older versions. `Pen.isSupported` and `Touchscreen.isPressureSupported` come from [ISX-2046](https://jira.unity3d.com/browse/ISX-2046); `Mouse.isSupported` is the short-term scope of [ISX-2079](https://jira.unity3d.com/browse/ISX-2079), whose remaining half is a real presence primitive rather than a capability one - - ```csharp - // Before, legacy input. Reported true on any iPad new enough to pair a Pencil, whether or - // not one was paired, so the two questions could not be told apart. - if (Input.stylusTouchSupported) { } - if (Input.mousePresent) { } - - // After. Capability is its own question with its own answer. - if (Pen.isSupported) { } // could a pen ever work on this platform - if (Mouse.isSupported) { } // could a mouse ever work on this platform - - // Acting on input is a different question, and needs both parts. A non-null current means a - // device object is registered, not that hardware is attached; enabled is what says it is active. - if (Pen.current != null && Pen.current.enabled) { } - ``` +- Added `Pen.isSupported`, `Mouse.isSupported` and `Touchscreen.isPressureSupported`, reporting what the current platform is capable of rather than what is connected right now. These are not drop-in replacements for the legacy `UnityEngine.Input` equivalents; see the "Device capability and device availability" section of the migration documentation for how they differ, and for how to check whether a device is available to read from. [ISX-2046] [ISX-2079] ### Fixed diff --git a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md index 46fccea658..d2423a0ed9 100644 --- a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md +++ b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md @@ -69,14 +69,15 @@ Directly reading hardware controls bypasses the new Input System's action-based ## Device capability and device availability -Several old Input Manager properties, such as `Input.mousePresent` and `Input.stylusTouchSupported`, answered +Several Input Manager properties, such as [`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html) and +[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html), answered two questions at once, and answered them differently depending on the platform. On some platforms they were a hardcoded constant meaning roughly "this platform has this kind of device", and on others they performed real hardware detection. The new Input System separates the two: - -- **Can this platform do it at all?** Use the capability properties: [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse), +- **Does this platform even support this input interface?** + Use the capability properties: [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse), [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) and [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen). These never change while the application runs, so read them once and decide whether to offer device-specific functionality. @@ -100,7 +101,7 @@ instead, so it can be `true` where the old one was `false`. The tables below not [`Input.GetMouseButtonDown`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html)
Example: `Input.GetMouseButtonDown(0)`|Use [`wasPressedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasPressedThisFrame` [`Input.GetMouseButtonUp`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonUp.html)
Example: `Input.GetMouseButtonUp(0)`|Use [`wasReleasedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasReleasedThisFrame` [`Input.mousePosition`](https://docs.unity3d.com/ScriptReference/Input-mousePosition.html)|Use [`Mouse.current.position.ReadValue()`](xref:UnityEngine.InputSystem.Mouse)
Example: `Vector2 position = Mouse.current.position.ReadValue();`
**Note:** Mouse simulation from touch isn't implemented yet. -[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|Use [`isSupported`](xref:UnityEngine.InputSystem.Mouse) to check whether the platform supports mouse input at all.
Example: `if (Mouse.isSupported) ShowMouseSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Input System does not currently deliver mouse input on iOS, iPadOS or visionOS, so `Mouse.isSupported` is `false` there even though the platform itself supports indirect mice. Requires a recent Editor version. +[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|Use [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse) to check whether the platform supports mouse input at all.
Example: `if (Mouse.isSupported) ShowMouseSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Input System does not currently deliver mouse input on iOS, iPadOS or visionOS, so `Mouse.isSupported` is `false` there even though the platform itself supports indirect mice. Requires a recent Editor version. ## Touch and Pen @@ -109,10 +110,10 @@ instead, so it can be `true` where the old one was `false`. The tables below not [`Input.GetTouch`](https://docs.unity3d.com/ScriptReference/Input.GetTouch.html)
For example:
`Touch touch = Input.GetTouch(0);`
`Vector2 touchPos = touch.position;`|Use [`EnhancedTouch.Touch.activeTouches[i]`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
Example: `Vector2 touchPos = EnhancedTouch.Touch.activeTouches[0].position;`
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport). [`Input.multiTouchEnabled`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|There is no direct equivalent, because this is a setting rather than a hardware capability. To get the same first-touch-wins behaviour, read [`primaryTouch`](xref:UnityEngine.InputSystem.Touchscreen) instead of iterating all touches, or bind to `/primaryTouch`.
Example: `if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)`
**Note:** Two differences from setting `Input.multiTouchEnabled = false`. First, `primaryTouch` filters only itself: [`touches`](xref:UnityEngine.InputSystem.Touchscreen), the `/touch*` bindings and [`EnhancedTouch`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch) still report every finger, whereas the legacy setting suppressed additional touches globally. Second, when the finger that started the primary touch lifts while other fingers are still down, the primary touch is retained rather than ended until the last finger is released, so a control bound to it stays actuated in the meantime. [`Input.simulateMouseWithTouches`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|No corresponding API yet. -[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|Use [`isSupported`](xref:UnityEngine.InputSystem.Pen) to check whether the platform supports pen input at all.
Example: `if (Pen.isSupported) ShowPenSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Requires a recent Editor version. +[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|Use [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) to check whether the platform supports pen input at all.
Example: `if (Pen.isSupported) ShowPenSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Requires a recent Editor version. [`Input.touchCount`](https://docs.unity3d.com/ScriptReference/Input-touchCount.html)|[`EnhancedTouch.Touch.activeTouches.Count`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouchSupport.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) [`Input.touches`](https://docs.unity3d.com/scriptreference/input-touches.html)|[`EnhancedTouch.Touch.activeTouches`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) -[`Input.touchPressureSupported`](https://docs.unity3d.com/ScriptReference/Input-touchPressureSupported.html)|Use [`isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen) to check whether the platform delivers a real pressure value with touch input.
Example: `if (Touchscreen.isPressureSupported) UsePressureForBrushWidth();`
**Note:** When this is `false`, [`pressure`](xref:UnityEngine.InputSystem.Controls.TouchControl) reports a constant `1` while a finger is down rather than a measured value. This is a platform-wide answer rather than a per-device one. Requires a recent Editor version. +[`Input.touchPressureSupported`](https://docs.unity3d.com/ScriptReference/Input-touchPressureSupported.html)|Use [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen) to check whether the platform delivers a real pressure value with touch input.
Example: `if (Touchscreen.isPressureSupported) UsePressureForBrushWidth();`
**Note:** When this is `false`, [`pressure`](xref:UnityEngine.InputSystem.Controls.TouchControl) reports a constant `1` while a finger is down rather than a measured value. This is a platform-wide answer rather than a per-device one. Requires a recent Editor version. [`Input.touchSupported`](https://docs.unity3d.com/ScriptReference/Input-touchSupported.html)|[`Touchscreen.current != null`](xref:UnityEngine.InputSystem.Touchscreen) [`Input.backButtonLeavesApp`](https://docs.unity3d.com/ScriptReference/Input-backButtonLeavesApp.html)|No corresponding API yet. [`GetPenEvent`](https://docs.unity3d.com/ScriptReference/Input.GetPenEvent.html)
[`GetLastPenContactEvent`](https://docs.unity3d.com/ScriptReference/Input.GetLastPenContactEvent.html)
[`ResetPenEvents`](https://docs.unity3d.com/ScriptReference/Input.ResetPenEvents.html)
[`ClearLastPenContactEvent`](https://docs.unity3d.com/ScriptReference/Input.ClearLastPenContactEvent.html)|Use: [`Pen.current`](xref:UnityEngine.InputSystem.Pen)
See the [Pen, tablet and stylus support](devices-pen.md) docs for more information. diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs index 643ea8f140..df33870aeb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs @@ -1,10 +1,3 @@ -////TODO: the UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES version expression in -//// Unity.InputSystem.asmdef and Unity.InputSystem.Tests.asmdef is still a local development -//// Editor version. It must be set to the version that actually ships the engine side of -//// ISX-2046 before this merges, or the gate will enable code referencing engine symbols that -//// standard builds of that version do not have. Recorded here because asmdef files are JSON -//// and cannot carry a comment of their own. - #if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES namespace UnityEngine.InputSystem.LowLevel { diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs index 00d023cb86..506af81acc 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs @@ -11,7 +11,7 @@ namespace UnityEngine.InputSystem.LowLevel /// /// Addressed to the engine's system endpoint rather than to a device, so it is sent through /// rather than - /// . Presence is answered by the device list. + /// . /// /// The FourCC must match kInputFourCCIOCTLQueryMouseSupported in the engine's /// Modules/Input/InputFourCC.h. diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs index a247b147f6..bdc11a5ba8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs @@ -11,7 +11,7 @@ namespace UnityEngine.InputSystem.LowLevel /// /// Addressed to the engine's system endpoint rather than to a device, so it is sent through /// rather than - /// . Presence is answered by the device list. + /// . /// /// The FourCC must match kInputFourCCIOCTLQueryPenSupported in the engine's /// Modules/Input/InputFourCC.h. diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs index 3c59260e58..2a5164f05a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs @@ -13,10 +13,6 @@ namespace UnityEngine.InputSystem.LowLevel /// rather than /// . /// - /// This is answered at platform scope rather than per touchscreen, because that is the scope at - /// which the answer exists: every platform sources it from a device-model or OS-API property - /// rather than by enumerating digitizers. - /// /// The FourCC must match kInputFourCCIOCTLQueryTouchPressureSupported in the engine's /// Modules/Input/InputFourCC.h. /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs index 51c15cbfe0..40a9bb30b2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs @@ -265,36 +265,19 @@ public class Mouse : Pointer, IInputStateCallbackReceiver /// True if the platform supports mouse input. /// /// This answers "could a mouse work here", which is the question to ask when deciding - /// whether to offer mouse-specific functionality in a UI. To ask whether a mouse is - /// available to read from right now, check both and - /// , as described below. - /// - /// Legacy UnityEngine.Input conflated the two under Input.mousePresent, which is - /// a hardcoded true on Windows, macOS, Linux and WebGL, and genuine detection only on iOS, - /// Android, UWP and the consoles. + /// whether to offer mouse-specific functionality in a UI. The answer cannot change while the + /// application runs. /// /// A false value means either that the platform does not support mouse input or that it could /// not determine the answer. The two are deliberately not distinguished, because a caller - /// deciding whether to offer functionality wants the same behaviour in both cases. It does not - /// mean the Editor was unable to ask, since this property only exists on Editor versions that - /// can. - /// - /// The answer cannot change while the application runs, so it is queried once and cached. - /// - /// Three checks are easy to confuse, in increasing strictness. This property asks whether the - /// platform could ever deliver mouse input. Mouse.current != null asks only whether a device - /// object is registered, which is not the same as hardware being attached, since several - /// platforms register unconditionally. Mouse.current != null && Mouse.current.enabled - /// adds whether it is currently active, and that is the check to make before acting on input. + /// deciding whether to offer functionality wants the same behaviour in both cases. It never + /// means the Editor was unable to ask, since the property only exists where it can. /// - /// The two clauses catch different things. current != null is what catches a disconnect, - /// since removal nulls current, though that relies on the platform reporting removal at all. - /// enabled does not become false on unplug: it tracks whether the device is enabled for - /// input, through and . + /// Whether a mouse is available to read from right now is a separate question, and needs both + /// and . See the "Device capability and + /// device availability" section of the Input Manager migration documentation. /// - /// The Device Simulator makes that visible: while simulating a touch device it disables the native - /// Mouse without removing it, so current stays non-null while enabled is false. - /// Read it from the main thread: resolving mouse support can require a platform API that is + /// Read this from the main thread: resolving the answer can require a platform API that is /// main-thread only, and the first read is the one that resolves it. /// /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs index bcc07c1526..c1135401ba 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs @@ -330,34 +330,19 @@ public class Pen : Pointer /// True if the platform supports pen input. /// /// This answers "could a pen work here", which is the question to ask when deciding whether - /// to offer pen-specific functionality in a UI. To ask whether a pen is available to read - /// from right now, check both and , - /// as described below. - /// - /// Legacy UnityEngine.Input conflated the two: Input.stylusTouchSupported - /// reports true on any iPad new enough to pair an Apple Pencil, whether or not one is paired. + /// to offer pen-specific functionality in a UI. The answer cannot change while the application + /// runs. /// /// A false value means either that the platform does not support pen input or that it could not /// determine the answer. The two are deliberately not distinguished, because a caller deciding - /// whether to offer functionality wants the same behaviour in both cases. It does not mean the - /// Editor was unable to ask, since this property only exists on Editor versions that can. - /// - /// The answer cannot change while the application runs, so it is queried once and cached. - /// - /// Three checks are easy to confuse, in increasing strictness. This property asks whether the - /// platform could ever deliver pen input. Pen.current != null asks only whether a device - /// object is registered, which is not the same as hardware being attached, since several - /// platforms register unconditionally. Pen.current != null && Pen.current.enabled - /// adds whether it is currently active, and that is the check to make before acting on input. + /// whether to offer functionality wants the same behaviour in both cases. It never means the + /// Editor was unable to ask, since the property only exists where it can. /// - /// The two clauses catch different things. current != null is what catches a disconnect, - /// since removal nulls current, though that relies on the platform reporting removal at all. - /// enabled does not become false on unplug: it tracks whether the device is enabled for - /// input, through and . + /// Whether a pen is available to read from right now is a separate question, and needs both + /// and . See the "Device capability and + /// device availability" section of the Input Manager migration documentation. /// - /// The Device Simulator makes that visible: while simulating a touch device it disables the native - /// Pen without removing it, so current stays non-null while enabled is false. - /// Read it from the main thread: resolving pen support can require a platform API that is + /// Read this from the main thread: resolving the answer can require a platform API that is /// main-thread only, and the first read is the one that resolves it. /// /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs index 88214358d7..67acaeb6cd 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs @@ -530,30 +530,23 @@ protected TouchControl[] touchControlArray /// finger is down rather than a measured value, so treating it as an analog signal produces /// no variation. Platforms with no touchscreen at all also report false. /// - /// This is deliberately a platform-scoped answer rather than a per-device one, because that - /// is the scope at which it exists: every platform sources it from a device model or an OS - /// API property rather than by enumerating digitizers. Read it as "this platform delivers - /// pressure", not "this particular touchscreen does". + /// Read this as "this platform delivers pressure" rather than "this particular touchscreen + /// does": it is a platform-scoped answer, not a per-device one. The answer cannot change while + /// the application runs. /// /// A false value means either that the platform does not deliver touch pressure or that it could /// not determine the answer. The two are deliberately not distinguished, because a caller /// deciding whether to treat pressure as an analog signal wants the same behaviour in both - /// cases. It does not mean the Editor was unable to ask, since this property only exists on - /// Editor versions that can. + /// cases. It never means the Editor was unable to ask, since the property only exists where it + /// can. The "could not determine" case is real: on some platforms the OS supplies a pressure + /// value whether or not the attached digitizer measures one, so only a per-device query could + /// tell a real reading from a constant. /// - /// The "could not determine" case is real rather than theoretical: on some platforms the OS - /// supplies a pressure value whether or not the attached digitizer measures one, so only a - /// per-device query could tell a real reading from a constant, and the platform reports that it - /// does not know. + /// Whether a touchscreen is available to read from right now is a separate question, and needs + /// both and . See the "Device capability + /// and device availability" section of the Input Manager migration documentation. /// - /// The answer cannot change while the application runs, so it is queried once and cached. - /// - /// Note that Touchscreen.current != null does not mean a touchscreen is physically present. - /// Some platforms register one unconditionally, so it can be non-null on hardware with no touch at - /// all. Before acting on input, check Touchscreen.current != null && - /// Touchscreen.current.enabled: a non-null current only means a device object is - /// registered, and enabled is what says it is active. - /// Read it from the main thread: resolving touch pressure support can require a platform API that is + /// Read this from the main thread: resolving the answer can require a platform API that is /// main-thread only, and the first read is the one that resolves it. /// /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs index 23b9edc444..bc6b67452c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs @@ -3130,22 +3130,11 @@ internal void ApplyActions() /// Sends a command to the engine's system endpoint, which answers questions about the /// platform rather than about any one device. /// - /// - /// This replaces an earlier ExecuteGlobalCommand, which addressed device id 0 on the premise - /// that the engine routes such commands by FourCC alone. It does not: InputDeviceIOCTL - /// resolves the id against the device registry and 0 is the invalid-device sentinel, so a - /// command sent there could never be answered. That helper had no callers, having been - /// orphaned when UseWindowsGamingInputCommand was removed. - /// - /// The endpoint is addressed by a reserved device id that is deliberately never registered, - /// so it never appears in the device list. On an engine without the endpoint the id is simply - /// unknown and the command fails, which callers read as "we do not know". - /// internal unsafe long ExecuteSystemCommand(ref TCommand command) where TCommand : struct, IInputDeviceCommandInfo { var ptr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command); - return InputRuntime.s_Instance.DeviceCommand(NativeInputCapabilities.systemDeviceId, ptr); + return m_Runtime.DeviceCommand(NativeInputCapabilities.systemDeviceId, ptr); } // Platform capabilities cannot change while the process runs, so each is queried at most From e46584ae3d3708abfdd85a4b52697930dd9117fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 20 Aug 2026 14:00:19 +0200 Subject: [PATCH 3/7] Correct what the capability tests claim about native platform coverage --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 41122cd6a2..988a3b244d 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -5899,8 +5899,10 @@ public unsafe void Devices_DoesntErrorOutOnMaxTouchCount() #if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES // Platform capability queries. These are addressed to the engine's system endpoint rather than // to a device, because they answer "can this platform do X" rather than "is an X connected". - // What a given platform actually answers is asserted natively in the engine repository, from - // PlatformDependent, where the file location gates the test to that platform. + // The tests below drive the answer through a mocked runtime, so they cover this side of the + // exchange only. The engine's own tests assert that whichever platform they run on returns a + // valid state and that the endpoint rejects malformed payloads. No test asserts the answer a + // named platform gives, so a wrong per-platform answer is caught by review, not by CI. private unsafe void AnswerCapabilityQuery(FourCC type, InputCapabilitySupport answer) { From 6026581ac219b13e8b79fcc209d2335e9eef3ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 20 Aug 2026 14:00:25 +0200 Subject: [PATCH 4/7] Rewrite the capability property docs for an end-user reader --- .../InputSystem/Runtime/Devices/Mouse.cs | 26 +++++---------- .../InputSystem/Runtime/Devices/Pen.cs | 26 +++++---------- .../Runtime/Devices/Touchscreen.cs | 33 +++++++------------ 3 files changed, 27 insertions(+), 58 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs index 40a9bb30b2..5ce28744c8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Mouse.cs @@ -262,34 +262,24 @@ public class Mouse : Pointer, IInputStateCallbackReceiver /// Whether the current platform can deliver mouse input at all, regardless of whether a /// mouse is connected right now. /// - /// True if the platform supports mouse input. /// - /// This answers "could a mouse work here", which is the question to ask when deciding - /// whether to offer mouse-specific functionality in a UI. The answer cannot change while the - /// application runs. + /// Use this to decide whether to offer mouse-specific functionality, such as a sensitivity + /// setting. It is true on a platform where a mouse can work even when none is connected, and + /// it doesn't change while the application runs. /// - /// A false value means either that the platform does not support mouse input or that it could - /// not determine the answer. The two are deliberately not distinguished, because a caller - /// deciding whether to offer functionality wants the same behaviour in both cases. It never - /// means the Editor was unable to ask, since the property only exists where it can. + /// To find out whether a mouse is connected and delivering input, use + /// and instead. /// - /// Whether a mouse is available to read from right now is a separate question, and needs both - /// and . See the "Device capability and - /// device availability" section of the Input Manager migration documentation. - /// - /// Read this from the main thread: resolving the answer can require a platform API that is - /// main-thread only, and the first read is the one that resolves it. + /// A false value means the platform doesn't support mouse input, or that it couldn't determine + /// an answer. The two cases aren't distinguished. /// /// /// /// - /// // Whether a mouse could work here, which is the question to ask when deciding whether to - /// // show mouse-specific settings. True on desktop platforms with no mouse plugged in. + /// // True on a desktop platform with no mouse plugged in. /// if (Mouse.isSupported) /// ShowMouseSensitivitySetting(); /// - /// // Whether one is usable right now, which needs both parts: a non-null current only means a - /// // device object is registered, and enabled is what tells you it is active. /// if (Mouse.current != null && Mouse.current.enabled) /// Debug.Log(Mouse.current.position.ReadValue()); /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs index c1135401ba..47828bec52 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Pen.cs @@ -327,23 +327,16 @@ public class Pen : Pointer /// Whether the current platform can deliver pen input at all, regardless of whether a pen is /// connected right now. /// - /// True if the platform supports pen input. /// - /// This answers "could a pen work here", which is the question to ask when deciding whether - /// to offer pen-specific functionality in a UI. The answer cannot change while the application - /// runs. + /// Use this to decide whether to offer pen-specific functionality, such as a pressure or tilt + /// setting. It is true on a platform where a pen can work even when none is connected, and it + /// doesn't change while the application runs. /// - /// A false value means either that the platform does not support pen input or that it could not - /// determine the answer. The two are deliberately not distinguished, because a caller deciding - /// whether to offer functionality wants the same behaviour in both cases. It never means the - /// Editor was unable to ask, since the property only exists where it can. + /// To find out whether a pen is connected and delivering input, use and + /// instead. /// - /// Whether a pen is available to read from right now is a separate question, and needs both - /// and . See the "Device capability and - /// device availability" section of the Input Manager migration documentation. - /// - /// Read this from the main thread: resolving the answer can require a platform API that is - /// main-thread only, and the first read is the one that resolves it. + /// A false value means the platform doesn't support pen input, or that it couldn't determine an + /// answer. The two cases aren't distinguished. /// /// /// @@ -357,15 +350,12 @@ public class Pen : Pointer /// /// void Start() /// { - /// // Decide once whether to offer pen-specific functionality at all. This is true on - /// // a platform that can deliver pen input, even when no pen is connected yet. + /// // True on a platform that can deliver pen input, even when no pen is connected yet. /// m_ShowPenSettings = Pen.isSupported; /// } /// /// void Update() /// { - /// // Whether a pen is usable right now is a different question, and needs both a - /// // registered device and that device being enabled. /// if (Pen.current != null && Pen.current.enabled && Pen.current.tip.wasPressedThisFrame) /// { /// // handle the pen tip being pressed diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs index 67acaeb6cd..33cba6fbea 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Touchscreen.cs @@ -524,36 +524,25 @@ protected TouchControl[] touchControlArray /// /// Whether the current platform delivers a real pressure value with touch input. /// - /// True if the platform supports touch pressure. /// - /// When this is false, reports a constant 1 while a - /// finger is down rather than a measured value, so treating it as an analog signal produces - /// no variation. Platforms with no touchscreen at all also report false. + /// Use this to decide whether to treat as an analog signal. + /// When it is false, reports a constant 1 while a finger is + /// down instead of a measured value. It doesn't change while the application runs. /// - /// Read this as "this platform delivers pressure" rather than "this particular touchscreen - /// does": it is a platform-scoped answer, not a per-device one. The answer cannot change while - /// the application runs. + /// The answer covers the platform, not an individual touchscreen. Platforms with no touchscreen + /// at all also report false. /// - /// A false value means either that the platform does not deliver touch pressure or that it could - /// not determine the answer. The two are deliberately not distinguished, because a caller - /// deciding whether to treat pressure as an analog signal wants the same behaviour in both - /// cases. It never means the Editor was unable to ask, since the property only exists where it - /// can. The "could not determine" case is real: on some platforms the OS supplies a pressure - /// value whether or not the attached digitizer measures one, so only a per-device query could - /// tell a real reading from a constant. + /// A false value means the platform doesn't deliver touch pressure, or that it couldn't + /// determine an answer. The two cases aren't distinguished. On some platforms the OS supplies a + /// pressure value whether or not the attached digitizer measures one, and there is no per-device + /// query to tell a real reading from a constant. /// - /// Whether a touchscreen is available to read from right now is a separate question, and needs - /// both and . See the "Device capability - /// and device availability" section of the Input Manager migration documentation. - /// - /// Read this from the main thread: resolving the answer can require a platform API that is - /// main-thread only, and the first read is the one that resolves it. + /// To find out whether a touchscreen is connected and delivering input, use + /// and instead. /// /// /// /// - /// // Only treat pressure as an analog signal where the platform actually measures it. - /// // Elsewhere it is a constant 1 while the finger is down, so this would do nothing. /// var brushWidth = Touchscreen.isPressureSupported /// && Touchscreen.current != null && Touchscreen.current.enabled /// ? Touchscreen.current.primaryTouch.pressure.ReadValue() * maxBrushWidth From 40f5b7dceaa127ac94f86c5dae593f6fe238b0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 20 Aug 2026 14:00:33 +0200 Subject: [PATCH 5/7] Document the cached device reference pitfall in the migration guide --- Packages/com.unity.inputsystem/CHANGELOG.md | 2 +- .../corresponding-old-new-api.md | 52 +++++++++++++------ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 98a3ff7e13..0bd9242b03 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added -- Added `Pen.isSupported`, `Mouse.isSupported` and `Touchscreen.isPressureSupported`, reporting what the current platform is capable of rather than what is connected right now. These are not drop-in replacements for the legacy `UnityEngine.Input` equivalents; see the "Device capability and device availability" section of the migration documentation for how they differ, and for how to check whether a device is available to read from. [ISX-2046] [ISX-2079] +- Added `Mouse.isSupported`, `Pen.isSupported` and `Touchscreen.isPressureSupported`, which report what the current platform is capable of rather than which devices are connected. Refer to [Corresponding old and new APIs](xref:input-system-old-new-apis). [ISX-2046] [ISX-2079] ### Fixed diff --git a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md index d2423a0ed9..3a2817294d 100644 --- a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md +++ b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md @@ -76,20 +76,42 @@ hardcoded constant meaning roughly "this platform has this kind of device", and hardware detection. The new Input System separates the two: -- **Does this platform even support this input interface?** + +- **Does this platform support this kind of input at all?** Use the capability properties: [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse), - [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) and [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen). - These never change while the application runs, so read them once and decide whether to offer device-specific - functionality. -- **Is a device available to read from right now?** Use `Device.current != null && Device.current.enabled`. - Both parts matter. A non-null `current` only means a device object is registered, which some platforms do - unconditionally regardless of whether hardware is attached, and `enabled` is what tells you the device is - active. The Device Simulator is a good example of the difference: while simulating a touch device it disables - the native mouse and pen without removing them, so `current` stays non-null while `enabled` becomes false. - -Because the old properties mixed these two meanings, the capability properties are **not drop-in replacements**. -On platforms where the old property performed real detection, the new property answers the capability question -instead, so it can be `true` where the old one was `false`. The tables below note this per API. + [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) and + [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen). These don't change while the + application runs, so read them once and decide whether to offer device-specific functionality. +- **Can a device deliver input right now?** + Use `Device.current != null && Device.current.enabled`. A non-null `current` only means a device object is + registered, which some platforms do whether or not hardware is attached, and + [`enabled`](xref:UnityEngine.InputSystem.InputDevice) is what tells you the device delivers input. The Device + Simulator shows the difference: while simulating a touch device it disables the native mouse and pen without + removing them, so `current` stays non-null while `enabled` becomes false. + +Read `current` each time rather than caching a device reference. Removing a device doesn't disable it, so a device +that has been removed still reports `enabled` as `true`. A cached reference therefore needs +[`added`](xref:UnityEngine.InputSystem.InputDevice) as well: + +```csharp +// Cached once, for example in a field holding the pad assigned to a player. +var gamepad = Gamepad.current; + +// The pad is then unplugged, so the Input System removes the device. +Debug.Log(gamepad.enabled); // True. Removing a device does not disable it. +Debug.Log(gamepad.added); // False. It is no longer in InputSystem.devices. + +// So a cached reference needs both checks, where reading current needs only enabled. +if (gamepad.added && gamepad.enabled) + Debug.Log(gamepad.leftStick.ReadValue()); +``` + +Reading `current` at the point of use avoids this, because removing a device resets `current` to `null`. + +The capability properties answer a different question from the Input Manager properties they replace, so the two +can report different values. On platforms where an Input Manager property performed real hardware detection, the +capability property reports what the platform supports instead, which can be `true` where the old property was +`false`. The tables below note where this applies. ## Mouse @@ -101,7 +123,7 @@ instead, so it can be `true` where the old one was `false`. The tables below not [`Input.GetMouseButtonDown`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html)
Example: `Input.GetMouseButtonDown(0)`|Use [`wasPressedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasPressedThisFrame` [`Input.GetMouseButtonUp`](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonUp.html)
Example: `Input.GetMouseButtonUp(0)`|Use [`wasReleasedThisFrame`](xref:UnityEngine.InputSystem.Controls.ButtonControl) on the corresponding mouse button.
Example: `InputSystem.Mouse.current.leftButton.wasReleasedThisFrame` [`Input.mousePosition`](https://docs.unity3d.com/ScriptReference/Input-mousePosition.html)|Use [`Mouse.current.position.ReadValue()`](xref:UnityEngine.InputSystem.Mouse)
Example: `Vector2 position = Mouse.current.position.ReadValue();`
**Note:** Mouse simulation from touch isn't implemented yet. -[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|Use [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse) to check whether the platform supports mouse input at all.
Example: `if (Mouse.isSupported) ShowMouseSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Input System does not currently deliver mouse input on iOS, iPadOS or visionOS, so `Mouse.isSupported` is `false` there even though the platform itself supports indirect mice. Requires a recent Editor version. +[`Input.mousePresent`](https://docs.unity3d.com/ScriptReference/Input-mousePresent.html)|Use [`Mouse.isSupported`](xref:UnityEngine.InputSystem.Mouse) to check whether the platform supports mouse input at all.
Example: `if (Mouse.isSupported) ShowMouseSettings();`
**Note:** Answers a different question from the Input Manager property. Refer to [Device capability and device availability](#device-capability-and-device-availability). Input System does not currently deliver mouse input on iOS, iPadOS or visionOS, so `Mouse.isSupported` is `false` there even though the platform itself supports indirect mice. Requires a recent Editor version. ## Touch and Pen @@ -110,7 +132,7 @@ instead, so it can be `true` where the old one was `false`. The tables below not [`Input.GetTouch`](https://docs.unity3d.com/ScriptReference/Input.GetTouch.html)
For example:
`Touch touch = Input.GetTouch(0);`
`Vector2 touchPos = touch.position;`|Use [`EnhancedTouch.Touch.activeTouches[i]`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
Example: `Vector2 touchPos = EnhancedTouch.Touch.activeTouches[0].position;`
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport). [`Input.multiTouchEnabled`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|There is no direct equivalent, because this is a setting rather than a hardware capability. To get the same first-touch-wins behaviour, read [`primaryTouch`](xref:UnityEngine.InputSystem.Touchscreen) instead of iterating all touches, or bind to `/primaryTouch`.
Example: `if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)`
**Note:** Two differences from setting `Input.multiTouchEnabled = false`. First, `primaryTouch` filters only itself: [`touches`](xref:UnityEngine.InputSystem.Touchscreen), the `/touch*` bindings and [`EnhancedTouch`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch) still report every finger, whereas the legacy setting suppressed additional touches globally. Second, when the finger that started the primary touch lifts while other fingers are still down, the primary touch is retained rather than ended until the last finger is released, so a control bound to it stays actuated in the meantime. [`Input.simulateMouseWithTouches`](https://docs.unity3d.com/ScriptReference/Input-multiTouchEnabled.html)|No corresponding API yet. -[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|Use [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) to check whether the platform supports pen input at all.
Example: `if (Pen.isSupported) ShowPenSettings();`
**Note:** Not a drop-in replacement; see [Device capability and device availability](#device-capability-and-device-availability) above. Requires a recent Editor version. +[`Input.stylusTouchSupported`](https://docs.unity3d.com/ScriptReference/Input-stylusTouchSupported.html)|Use [`Pen.isSupported`](xref:UnityEngine.InputSystem.Pen) to check whether the platform supports pen input at all.
Example: `if (Pen.isSupported) ShowPenSettings();`
**Note:** Answers a different question from the Input Manager property. Refer to [Device capability and device availability](#device-capability-and-device-availability). Requires a recent Editor version. [`Input.touchCount`](https://docs.unity3d.com/ScriptReference/Input-touchCount.html)|[`EnhancedTouch.Touch.activeTouches.Count`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouchSupport.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) [`Input.touches`](https://docs.unity3d.com/scriptreference/input-touches.html)|[`EnhancedTouch.Touch.activeTouches`](xref:UnityEngine.InputSystem.EnhancedTouch.Touch)
**Note:** Enable enhanced touch support first by calling [`EnhancedTouch.Enable()`](xref:UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport) [`Input.touchPressureSupported`](https://docs.unity3d.com/ScriptReference/Input-touchPressureSupported.html)|Use [`Touchscreen.isPressureSupported`](xref:UnityEngine.InputSystem.Touchscreen) to check whether the platform delivers a real pressure value with touch input.
Example: `if (Touchscreen.isPressureSupported) UsePressureForBrushWidth();`
**Note:** When this is `false`, [`pressure`](xref:UnityEngine.InputSystem.Controls.TouchControl) reports a constant `1` while a finger is down rather than a measured value. This is a platform-wide answer rather than a per-device one. Requires a recent Editor version. From 3068ef5dfedea80c41500e165753f8094c82aed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 20 Aug 2026 14:14:37 +0200 Subject: [PATCH 6/7] Stop referencing engine source paths from the package docs --- .../Runtime/Devices/Commands/InputCapabilitySupport.cs | 8 ++++---- .../Devices/Commands/QueryMouseSupportedCommand.cs | 2 +- .../Runtime/Devices/Commands/QueryPenSupportedCommand.cs | 2 +- .../Commands/QueryTouchPressureSupportedCommand.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs index df33870aeb..ba81c91eab 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/InputCapabilitySupport.cs @@ -6,10 +6,10 @@ namespace UnityEngine.InputSystem.LowLevel /// what is currently connected. /// /// - /// Mirrors CapabilityState in the engine's Modules/Input/InputDeviceIOCTL.h, whose - /// wire values are pinned by tests on both sides. is zero so that an - /// unwritten payload, or a platform that has not implemented a query, reads as "we do not know" - /// rather than as a confident . + /// Mirrors the engine's CapabilityState, whose wire values are pinned by tests on both + /// sides. is zero so that an unwritten payload, or a platform that has not + /// implemented a query, reads as "we do not know" rather than as a confident + /// . /// /// The value space is open. Treat anything other than as not supported /// rather than rejecting it, because a newer engine may answer with a value this version of the diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs index 506af81acc..bd67c63312 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryMouseSupportedCommand.cs @@ -14,7 +14,7 @@ namespace UnityEngine.InputSystem.LowLevel /// . /// /// The FourCC must match kInputFourCCIOCTLQueryMouseSupported in the engine's - /// Modules/Input/InputFourCC.h. + /// input module. /// /// [StructLayout(LayoutKind.Explicit, Size = kSize)] diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs index bdc11a5ba8..1267228799 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryPenSupportedCommand.cs @@ -14,7 +14,7 @@ namespace UnityEngine.InputSystem.LowLevel /// . /// /// The FourCC must match kInputFourCCIOCTLQueryPenSupported in the engine's - /// Modules/Input/InputFourCC.h. + /// input module. /// /// [StructLayout(LayoutKind.Explicit, Size = kSize)] diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs index 2a5164f05a..e510d28162 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Commands/QueryTouchPressureSupportedCommand.cs @@ -14,7 +14,7 @@ namespace UnityEngine.InputSystem.LowLevel /// . /// /// The FourCC must match kInputFourCCIOCTLQueryTouchPressureSupported in the engine's - /// Modules/Input/InputFourCC.h. + /// input module. /// /// [StructLayout(LayoutKind.Explicit, Size = kSize)] From 0951ea02e8f956e84ab309f36bb4147a877d9560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Thu, 20 Aug 2026 18:08:13 +0200 Subject: [PATCH 7/7] Remove the capability test comment describing absent coverage Review feedback from Leo: commenting on what the tests do not assert argues rather than informs, and the reasoning was not strong enough to justify it. The rest of the block restated what the test names already say, and the system-endpoint addressing it described is both visible in the code as NativeInputCapabilities.systemDeviceId and asserted by Devices_CapabilityQueries_AreNotDeliveredToDevices. --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 988a3b244d..ca0932c8f2 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -5897,13 +5897,6 @@ public unsafe void Devices_DoesntErrorOutOnMaxTouchCount() } #if UNITY_INPUTSYSTEM_SUPPORTS_CAPABILITY_QUERIES - // Platform capability queries. These are addressed to the engine's system endpoint rather than - // to a device, because they answer "can this platform do X" rather than "is an X connected". - // The tests below drive the answer through a mocked runtime, so they cover this side of the - // exchange only. The engine's own tests assert that whichever platform they run on returns a - // valid state and that the endpoint rejects malformed payloads. No test asserts the answer a - // named platform gives, so a wrong per-platform answer is caught by review, not by CI. - private unsafe void AnswerCapabilityQuery(FourCC type, InputCapabilitySupport answer) { runtime.SetDeviceCommandCallback(NativeInputCapabilities.systemDeviceId,