From 230667f8966f723507f2893c2d0f5661d27b392a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Tue, 2 Sep 2025 11:29:05 +0200 Subject: [PATCH 1/8] Added test case that reproduces the reported bug. --- Assets/Tests/InputSystem/CorePerformanceTests.cs | 2 +- Assets/Tests/InputSystem/CoreTests_Devices.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Assets/Tests/InputSystem/CorePerformanceTests.cs b/Assets/Tests/InputSystem/CorePerformanceTests.cs index 04132fca85..e092f29bb4 100644 --- a/Assets/Tests/InputSystem/CorePerformanceTests.cs +++ b/Assets/Tests/InputSystem/CorePerformanceTests.cs @@ -126,7 +126,7 @@ public void Performance_ReadEveryKey() int keyIndex = 0; foreach (var key in keyboard.allKeys) { - if (++keyIndex == (int)KeyEx.IMESelected) // Skip IMESelected as it's not a real key. + if (++keyIndex == (int)KeyEx.IMESelected) // Skip IMESelected as it's not a real key. TODO This is incorrect, it should not even be there continue; key.ReadValue(); } diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 070b1c7ce9..8bfa2ed2c0 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -5894,4 +5894,18 @@ public unsafe void Devices_DoesntErrorOutOnMaxTouchCount() BeginTouch(i, new Vector2(i * 1.0f, i * 2.0f), time: 0); }, Throws.Nothing); } + + // Note: Tested inside this class for now since there are no dedicated device test classes for built-in devices, + // only for plugin devices. + [Test, Description("https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1541")] + [Category("Devices")] + public void Devices_KeyboardAllKeys_EnumeratesAllKeyControls() + { + var keyboard = InputSystem.AddDevice(); + int index = 0; + foreach (var key in keyboard.allKeys) + { + Assert.NotNull(key, $"Key at index {index++} was null"); + } + } } From a3d8078a423ae67c77e5d144893bbeaa92836093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Tue, 2 Sep 2025 19:42:45 +0200 Subject: [PATCH 2/8] Added more Keyboard tests. --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 23 ----- .../InputSystem/CoreTests_Devices_Keyboard.cs | 86 +++++++++++++++++++ .../CoreTests_Devices_Keyboard.cs.meta | 3 + 3 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs create mode 100644 Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs.meta diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 8bfa2ed2c0..7c4006f6ee 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -2849,15 +2849,6 @@ public void Devices_CanGetNameOfCurrentKeyboardLayout() Assert.That(keyboard.keyboardLayout, Is.EqualTo("new")); } - [Test] - [Category("Devices")] - public void Devices_CanGetKeyCodeFromKeyboardKey() - { - var keyboard = InputSystem.AddDevice(); - - Assert.That(keyboard.aKey.keyCode, Is.EqualTo(Key.A)); - } - [Test] [Category("Devices")] public void Devices_CanLookUpKeyFromKeyboardUsingKeyCode() @@ -5894,18 +5885,4 @@ public unsafe void Devices_DoesntErrorOutOnMaxTouchCount() BeginTouch(i, new Vector2(i * 1.0f, i * 2.0f), time: 0); }, Throws.Nothing); } - - // Note: Tested inside this class for now since there are no dedicated device test classes for built-in devices, - // only for plugin devices. - [Test, Description("https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1541")] - [Category("Devices")] - public void Devices_KeyboardAllKeys_EnumeratesAllKeyControls() - { - var keyboard = InputSystem.AddDevice(); - int index = 0; - foreach (var key in keyboard.allKeys) - { - Assert.NotNull(key, $"Key at index {index++} was null"); - } - } } diff --git a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs new file mode 100644 index 0000000000..507797ce15 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs @@ -0,0 +1,86 @@ +using System; +using System.Linq; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.InputSystem; + +// Functional testing of built-in device UnityEngine.InputSystem.Keyboard. + +partial class CoreTests +{ + [Test] + [Category("Devices")] + public void Devices_Keyboard_CanGetKeyCodeFromKeyboardKey() + { + var keyboard = InputSystem.AddDevice(); + + Assert.That(keyboard.aKey.keyCode, Is.EqualTo(Key.A)); + Assert.That(keyboard.bKey.keyCode, Is.EqualTo(Key.B)); + Assert.That(keyboard.cKey.keyCode, Is.EqualTo(Key.C)); + Assert.That(keyboard.dKey.keyCode, Is.EqualTo(Key.D)); + Assert.That(keyboard.eKey.keyCode, Is.EqualTo(Key.E)); + Assert.That(keyboard.fKey.keyCode, Is.EqualTo(Key.F)); + Assert.That(keyboard.gKey.keyCode, Is.EqualTo(Key.G)); + // TODO Make this complete + } + + [Test, Description("https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1541")] + [Category("Devices")] + public void Devices_Keyboard_AllKeysEnumeratesAllKeyControls() + { + var keyboard = InputSystem.AddDevice(); + var index = 0; + foreach (var key in keyboard.allKeys) + { + if (index == 109) + Debug.Log("TEMP"); + Assert.NotNull(key, $"Key at index {index++} was null"); + } + } + + [Test] + [Category("Devices")] + public void Devices_Keyboard_AllKeysShouldContainKeyControlsCorrespondingToAllKeys() + { + var keyboard = InputSystem.AddDevice(); + var allKeys = keyboard.allKeys; + foreach (var key in Enum.GetValues(typeof(Key)).Cast()) + { + Assert.That(allKeys.Contains(keyboard.spaceKey), Is.True); + } + } + + [Test] + [Category("Devices")] + public void Devices_Keyboard_SubscriptOperatorCanLookupKeyControlOfCorrespondingKey() + { + var keyboard = InputSystem.AddDevice(); + foreach (var key in Enum.GetValues(typeof(Key)).Cast()) + { + // Key.None is documented as an invalid key so skip it in this test and instead we verify in test below + // that exception is thrown if attempting to lookup key-control using it. + if (key == Key.None) + continue; + if (key == Key.IMESelected) + continue; + + var keyControl = keyboard[key]; + Assert.That(keyControl, Is.Not.Null); + Assert.That(keyControl.keyCode, Is.EqualTo(key)); + } + } + + [Test] + [Category("Devices")] + public void Devices_Keyboard_SubscriptOperatorThrowsForInvalidOrOutOfRangeKey() + { + var keyboard = InputSystem.AddDevice(); + var minKey = Enum.GetValues(typeof(Key)).Cast().Min(); + var invalidMin = (Key)((int)minKey)-1; + var maxKey = Enum.GetValues(typeof(Key)).Cast().Max(); + var invalidMax = (Key)((int)maxKey)+1; + Assert.Throws(() => { _ = keyboard[Key.None]; }); + Assert.Throws(() => { _ = keyboard[invalidMin]; }); + Assert.Throws(() => { _ = keyboard[invalidMax]; }); + } +} diff --git a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs.meta b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs.meta new file mode 100644 index 0000000000..0ca1051475 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3fc203e954a44c9b833505b627e36ea2 +timeCreated: 1756806075 \ No newline at end of file From 1803b60ce9e5e2cba2d7d9f04f16ff64f422463c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Wed, 3 Sep 2025 08:08:25 +0200 Subject: [PATCH 3/8] Fixed an issue where using the subscript operator for IMESelected (obsolete key) would result in null KeyControl being returned. --- .../InputSystem/CoreTests_Devices_Keyboard.cs | 157 ++++++++++++++++-- .../InputSystem/Devices/Keyboard.cs | 13 +- .../Devices/Precompiled/FastKeyboard.cs | 37 ++++- 3 files changed, 186 insertions(+), 21 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs index 507797ce15..a6a45c03ad 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs @@ -8,12 +8,29 @@ partial class CoreTests { + private static readonly Key[] sKeys = Enum.GetValues(typeof(Key)).Cast().ToArray(); + [Test] [Category("Devices")] public void Devices_Keyboard_CanGetKeyCodeFromKeyboardKey() { var keyboard = InputSystem.AddDevice(); + Assert.That(keyboard.spaceKey.keyCode, Is.EqualTo(Key.Space)); + Assert.That(keyboard.enterKey.keyCode, Is.EqualTo(Key.Enter)); + Assert.That(keyboard.tabKey.keyCode, Is.EqualTo(Key.Tab)); + Assert.That(keyboard.backquoteKey.keyCode, Is.EqualTo(Key.Backquote)); + Assert.That(keyboard.quoteKey.keyCode, Is.EqualTo(Key.Quote)); + Assert.That(keyboard.semicolonKey.keyCode, Is.EqualTo(Key.Semicolon)); + Assert.That(keyboard.commaKey.keyCode, Is.EqualTo(Key.Comma)); + Assert.That(keyboard.periodKey.keyCode, Is.EqualTo(Key.Period)); + Assert.That(keyboard.slashKey.keyCode, Is.EqualTo(Key.Slash)); + Assert.That(keyboard.backslashKey.keyCode, Is.EqualTo(Key.Backslash)); + Assert.That(keyboard.leftBracketKey.keyCode, Is.EqualTo(Key.LeftBracket)); + Assert.That(keyboard.rightBracketKey.keyCode, Is.EqualTo(Key.RightBracket)); + Assert.That(keyboard.minusKey.keyCode, Is.EqualTo(Key.Minus)); + Assert.That(keyboard.equalsKey.keyCode, Is.EqualTo(Key.Equals)); + Assert.That(keyboard.aKey.keyCode, Is.EqualTo(Key.A)); Assert.That(keyboard.bKey.keyCode, Is.EqualTo(Key.B)); Assert.That(keyboard.cKey.keyCode, Is.EqualTo(Key.C)); @@ -21,9 +38,120 @@ public void Devices_Keyboard_CanGetKeyCodeFromKeyboardKey() Assert.That(keyboard.eKey.keyCode, Is.EqualTo(Key.E)); Assert.That(keyboard.fKey.keyCode, Is.EqualTo(Key.F)); Assert.That(keyboard.gKey.keyCode, Is.EqualTo(Key.G)); - // TODO Make this complete + Assert.That(keyboard.hKey.keyCode, Is.EqualTo(Key.H)); + Assert.That(keyboard.iKey.keyCode, Is.EqualTo(Key.I)); + Assert.That(keyboard.jKey.keyCode, Is.EqualTo(Key.J)); + Assert.That(keyboard.kKey.keyCode, Is.EqualTo(Key.K)); + Assert.That(keyboard.lKey.keyCode, Is.EqualTo(Key.L)); + Assert.That(keyboard.mKey.keyCode, Is.EqualTo(Key.M)); + Assert.That(keyboard.nKey.keyCode, Is.EqualTo(Key.N)); + Assert.That(keyboard.oKey.keyCode, Is.EqualTo(Key.O)); + Assert.That(keyboard.pKey.keyCode, Is.EqualTo(Key.P)); + Assert.That(keyboard.qKey.keyCode, Is.EqualTo(Key.Q)); + Assert.That(keyboard.rKey.keyCode, Is.EqualTo(Key.R)); + Assert.That(keyboard.sKey.keyCode, Is.EqualTo(Key.S)); + Assert.That(keyboard.tKey.keyCode, Is.EqualTo(Key.T)); + Assert.That(keyboard.uKey.keyCode, Is.EqualTo(Key.U)); + Assert.That(keyboard.vKey.keyCode, Is.EqualTo(Key.V)); + Assert.That(keyboard.wKey.keyCode, Is.EqualTo(Key.W)); + Assert.That(keyboard.xKey.keyCode, Is.EqualTo(Key.X)); + Assert.That(keyboard.yKey.keyCode, Is.EqualTo(Key.Y)); + Assert.That(keyboard.zKey.keyCode, Is.EqualTo(Key.Z)); + + Assert.That(keyboard.digit1Key.keyCode, Is.EqualTo(Key.Digit1)); + Assert.That(keyboard.digit2Key.keyCode, Is.EqualTo(Key.Digit2)); + Assert.That(keyboard.digit3Key.keyCode, Is.EqualTo(Key.Digit3)); + Assert.That(keyboard.digit4Key.keyCode, Is.EqualTo(Key.Digit4)); + Assert.That(keyboard.digit5Key.keyCode, Is.EqualTo(Key.Digit5)); + Assert.That(keyboard.digit6Key.keyCode, Is.EqualTo(Key.Digit6)); + Assert.That(keyboard.digit7Key.keyCode, Is.EqualTo(Key.Digit7)); + Assert.That(keyboard.digit8Key.keyCode, Is.EqualTo(Key.Digit8)); + Assert.That(keyboard.digit9Key.keyCode, Is.EqualTo(Key.Digit9)); + Assert.That(keyboard.digit0Key.keyCode, Is.EqualTo(Key.Digit0)); + + Assert.That(keyboard.leftShiftKey.keyCode, Is.EqualTo(Key.LeftShift)); + Assert.That(keyboard.rightShiftKey.keyCode, Is.EqualTo(Key.RightShift)); + Assert.That(keyboard.leftAltKey.keyCode, Is.EqualTo(Key.LeftAlt)); + Assert.That(keyboard.rightAltKey.keyCode, Is.EqualTo(Key.RightAlt)); + Assert.That(keyboard.leftCtrlKey.keyCode, Is.EqualTo(Key.LeftCtrl)); + Assert.That(keyboard.rightCtrlKey.keyCode, Is.EqualTo(Key.RightCtrl)); + Assert.That(keyboard.leftMetaKey.keyCode, Is.EqualTo(Key.LeftMeta)); + Assert.That(keyboard.rightMetaKey.keyCode, Is.EqualTo(Key.RightMeta)); + Assert.That(keyboard.leftWindowsKey.keyCode, Is.EqualTo(Key.LeftWindows)); + Assert.That(keyboard.rightWindowsKey.keyCode, Is.EqualTo(Key.RightWindows)); + Assert.That(keyboard.leftAppleKey.keyCode, Is.EqualTo(Key.LeftApple)); + Assert.That(keyboard.rightAppleKey.keyCode, Is.EqualTo(Key.RightApple)); + Assert.That(keyboard.leftCommandKey.keyCode, Is.EqualTo(Key.LeftCommand)); + Assert.That(keyboard.rightCommandKey.keyCode, Is.EqualTo(Key.RightCommand)); + Assert.That(keyboard.contextMenuKey.keyCode, Is.EqualTo(Key.ContextMenu)); + Assert.That(keyboard.escapeKey.keyCode, Is.EqualTo(Key.Escape)); + Assert.That(keyboard.leftArrowKey.keyCode, Is.EqualTo(Key.LeftArrow)); + Assert.That(keyboard.rightArrowKey.keyCode, Is.EqualTo(Key.RightArrow)); + Assert.That(keyboard.upArrowKey.keyCode, Is.EqualTo(Key.UpArrow)); + Assert.That(keyboard.downArrowKey.keyCode, Is.EqualTo(Key.DownArrow)); + Assert.That(keyboard.backspaceKey.keyCode, Is.EqualTo(Key.Backspace)); + Assert.That(keyboard.pageDownKey.keyCode, Is.EqualTo(Key.PageDown)); + Assert.That(keyboard.pageUpKey.keyCode, Is.EqualTo(Key.PageUp)); + Assert.That(keyboard.homeKey.keyCode, Is.EqualTo(Key.Home)); + Assert.That(keyboard.endKey.keyCode, Is.EqualTo(Key.End)); + Assert.That(keyboard.insertKey.keyCode, Is.EqualTo(Key.Insert)); + Assert.That(keyboard.deleteKey.keyCode, Is.EqualTo(Key.Delete)); + Assert.That(keyboard.capsLockKey.keyCode, Is.EqualTo(Key.CapsLock)); + Assert.That(keyboard.scrollLockKey.keyCode, Is.EqualTo(Key.ScrollLock)); + Assert.That(keyboard.numLockKey.keyCode, Is.EqualTo(Key.NumLock)); + Assert.That(keyboard.printScreenKey.keyCode, Is.EqualTo(Key.PrintScreen)); + Assert.That(keyboard.pauseKey.keyCode, Is.EqualTo(Key.Pause)); + Assert.That(keyboard.numpadEnterKey.keyCode, Is.EqualTo(Key.NumpadEnter)); + Assert.That(keyboard.numpadDivideKey.keyCode, Is.EqualTo(Key.NumpadDivide)); + Assert.That(keyboard.numpadMultiplyKey.keyCode, Is.EqualTo(Key.NumpadMultiply)); + Assert.That(keyboard.numpadMinusKey.keyCode, Is.EqualTo(Key.NumpadMinus)); + Assert.That(keyboard.numpadPlusKey.keyCode, Is.EqualTo(Key.NumpadPlus)); + Assert.That(keyboard.numpadPeriodKey.keyCode, Is.EqualTo(Key.NumpadPeriod)); + Assert.That(keyboard.numpadEqualsKey.keyCode, Is.EqualTo(Key.NumpadEquals)); + Assert.That(keyboard.numpad0Key.keyCode, Is.EqualTo(Key.Numpad0)); + Assert.That(keyboard.numpad1Key.keyCode, Is.EqualTo(Key.Numpad1)); + Assert.That(keyboard.numpad2Key.keyCode, Is.EqualTo(Key.Numpad2)); + Assert.That(keyboard.numpad3Key.keyCode, Is.EqualTo(Key.Numpad3)); + Assert.That(keyboard.numpad4Key.keyCode, Is.EqualTo(Key.Numpad4)); + Assert.That(keyboard.numpad5Key.keyCode, Is.EqualTo(Key.Numpad5)); + Assert.That(keyboard.numpad6Key.keyCode, Is.EqualTo(Key.Numpad6)); + Assert.That(keyboard.numpad7Key.keyCode, Is.EqualTo(Key.Numpad7)); + Assert.That(keyboard.numpad8Key.keyCode, Is.EqualTo(Key.Numpad8)); + Assert.That(keyboard.numpad9Key.keyCode, Is.EqualTo(Key.Numpad9)); + Assert.That(keyboard.f1Key.keyCode, Is.EqualTo(Key.F1)); + Assert.That(keyboard.f2Key.keyCode, Is.EqualTo(Key.F2)); + Assert.That(keyboard.f3Key.keyCode, Is.EqualTo(Key.F3)); + Assert.That(keyboard.f4Key.keyCode, Is.EqualTo(Key.F4)); + Assert.That(keyboard.f5Key.keyCode, Is.EqualTo(Key.F5)); + Assert.That(keyboard.f6Key.keyCode, Is.EqualTo(Key.F6)); + Assert.That(keyboard.f7Key.keyCode, Is.EqualTo(Key.F7)); + Assert.That(keyboard.f8Key.keyCode, Is.EqualTo(Key.F8)); + Assert.That(keyboard.f9Key.keyCode, Is.EqualTo(Key.F9)); + Assert.That(keyboard.f10Key.keyCode, Is.EqualTo(Key.F10)); + Assert.That(keyboard.f11Key.keyCode, Is.EqualTo(Key.F11)); + Assert.That(keyboard.f12Key.keyCode, Is.EqualTo(Key.F12)); + Assert.That(keyboard.oem1Key.keyCode, Is.EqualTo(Key.OEM1)); + Assert.That(keyboard.oem2Key.keyCode, Is.EqualTo(Key.OEM2)); + Assert.That(keyboard.oem3Key.keyCode, Is.EqualTo(Key.OEM3)); + Assert.That(keyboard.oem4Key.keyCode, Is.EqualTo(Key.OEM4)); + Assert.That(keyboard.oem5Key.keyCode, Is.EqualTo(Key.OEM5)); + Assert.That(keyboard.f13Key.keyCode, Is.EqualTo(Key.F13)); + Assert.That(keyboard.f14Key.keyCode, Is.EqualTo(Key.F14)); + Assert.That(keyboard.f15Key.keyCode, Is.EqualTo(Key.F15)); + Assert.That(keyboard.f16Key.keyCode, Is.EqualTo(Key.F16)); + Assert.That(keyboard.f17Key.keyCode, Is.EqualTo(Key.F17)); + Assert.That(keyboard.f18Key.keyCode, Is.EqualTo(Key.F18)); + Assert.That(keyboard.f19Key.keyCode, Is.EqualTo(Key.F19)); + Assert.That(keyboard.f20Key.keyCode, Is.EqualTo(Key.F20)); + Assert.That(keyboard.f21Key.keyCode, Is.EqualTo(Key.F21)); + Assert.That(keyboard.f22Key.keyCode, Is.EqualTo(Key.F22)); + Assert.That(keyboard.f23Key.keyCode, Is.EqualTo(Key.F23)); + Assert.That(keyboard.f24Key.keyCode, Is.EqualTo(Key.F24)); + Assert.That(keyboard.mediaPlayPause.keyCode, Is.EqualTo(Key.MediaPlayPause)); + Assert.That(keyboard.mediaRewind.keyCode, Is.EqualTo(Key.MediaRewind)); + Assert.That(keyboard.mediaForward.keyCode, Is.EqualTo(Key.MediaForward)); } - + [Test, Description("https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1541")] [Category("Devices")] public void Devices_Keyboard_AllKeysEnumeratesAllKeyControls() @@ -37,16 +165,21 @@ public void Devices_Keyboard_AllKeysEnumeratesAllKeyControls() Assert.NotNull(key, $"Key at index {index++} was null"); } } - + [Test] [Category("Devices")] public void Devices_Keyboard_AllKeysShouldContainKeyControlsCorrespondingToAllKeys() { var keyboard = InputSystem.AddDevice(); var allKeys = keyboard.allKeys; - foreach (var key in Enum.GetValues(typeof(Key)).Cast()) + foreach (var key in sKeys) { - Assert.That(allKeys.Contains(keyboard.spaceKey), Is.True); + // Key.None is documented as an invalid key so skip it in this test. + // Sub-script operator is documented to through for invalid key. + if (key == Key.None) + continue; + + Assert.That(allKeys.Contains(keyboard[key]), Is.True); } } @@ -55,15 +188,13 @@ public void Devices_Keyboard_AllKeysShouldContainKeyControlsCorrespondingToAllKe public void Devices_Keyboard_SubscriptOperatorCanLookupKeyControlOfCorrespondingKey() { var keyboard = InputSystem.AddDevice(); - foreach (var key in Enum.GetValues(typeof(Key)).Cast()) + foreach (var key in sKeys) { - // Key.None is documented as an invalid key so skip it in this test and instead we verify in test below - // that exception is thrown if attempting to lookup key-control using it. + // Key.None is documented as an invalid key so skip it in this test. + // Sub-script operator is documented to through for invalid key. if (key == Key.None) continue; - if (key == Key.IMESelected) - continue; - + var keyControl = keyboard[key]; Assert.That(keyControl, Is.Not.Null); Assert.That(keyControl.keyCode, Is.EqualTo(key)); @@ -76,9 +207,9 @@ public void Devices_Keyboard_SubscriptOperatorThrowsForInvalidOrOutOfRangeKey() { var keyboard = InputSystem.AddDevice(); var minKey = Enum.GetValues(typeof(Key)).Cast().Min(); - var invalidMin = (Key)((int)minKey)-1; + var invalidMin = (Key)((int)minKey) - 1; var maxKey = Enum.GetValues(typeof(Key)).Cast().Max(); - var invalidMax = (Key)((int)maxKey)+1; + var invalidMax = (Key)((int)maxKey) + 1; Assert.Throws(() => { _ = keyboard[Key.None]; }); Assert.Throws(() => { _ = keyboard[invalidMin]; }); Assert.Throws(() => { _ = keyboard[invalidMax]; }); diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs index f8b91616ad..c311a0df40 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs @@ -156,6 +156,7 @@ public unsafe struct KeyboardState : IInputStateTypeInfo [InputControl(name = "f8", displayName = "F8", layout = "Key", bit = (int)Key.F8)] [InputControl(name = "f9", displayName = "F9", layout = "Key", bit = (int)Key.F9)] [InputControl(name = "f10", displayName = "F10", layout = "Key", bit = (int)Key.F10)] + [InputControl(name = "f11", displayName = "F11", layout = "Key", bit = (int)Key.F11)] [InputControl(name = "f12", displayName = "F12", layout = "Key", bit = (int)Key.F12)] [InputControl(name = "OEM1", layout = "Key", bit = (int)Key.OEM1)] @@ -179,6 +180,10 @@ public unsafe struct KeyboardState : IInputStateTypeInfo [InputControl(name = "mediaRewind", displayName = "MediaRewind", layout = "Key", bit = (int)Key.MediaRewind)] [InputControl(name = "mediaForward", displayName = "MediaForward", layout = "Key", bit = (int)Key.MediaForward)] [InputControl(name = "IMESelected", layout = "Button", bit = (int)KeyEx.RemappedIMESelected, synthetic = true)] // Use the last bit to hold IME selected state. + // Disable deprecation warning to not generate warnings in user project about internal use of deprecated key +#pragma warning disable 0618 + [InputControl(name = "IMESelectedObsoleteKey", layout = "Key", bit = (int)KeyEx.RemappedIMESelected, synthetic = true)] +#pragma warning restore 0618 public fixed byte keys[kSizeInBytes]; // will be the default in new editor [InputControl(name = "IMESelected", layout = "Button", bit = 0, sizeInBits = 1, synthetic = true)] @@ -2370,7 +2375,9 @@ public KeyControl this[Key key] { var index = (int)key - 1; if (index < 0 || index >= m_Keys.Length) - throw new ArgumentOutOfRangeException(nameof(key)); + { + throw new ArgumentOutOfRangeException($"{nameof(key)}: {key}"); + } return m_Keys[index]; } } @@ -2560,7 +2567,7 @@ protected override void FinishSetup() "oem3", "oem4", "oem5", - null, // IMESelected + "IMESelectedObsoleteKey", // IMESelected "f13", "f14", "f15", @@ -2580,8 +2587,6 @@ protected override void FinishSetup() m_Keys = new KeyControl[keyStrings.Length]; for (var i = 0; i < keyStrings.Length; ++i) { - if (string.IsNullOrEmpty(keyStrings[i])) - continue; m_Keys[i] = GetChildControl(keyStrings[i]); ////REVIEW: Ideally, we'd have a way to do this through layouts; this way nested key controls could work, too, diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs index 9803171523..1cb9ff62c4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs @@ -24,10 +24,10 @@ internal partial class FastKeyboard : UnityEngine.InputSystem.Keyboard public const string metadata = ";AnyKey;Button;Axis;Key;DiscreteButton;Keyboard"; public FastKeyboard() { - var builder = this.Setup(130, 15, 7) + var builder = this.Setup(131, 15, 7) .WithName("Keyboard") .WithDisplayName("Keyboard") - .WithChildren(0, 130) + .WithChildren(0, 131) .WithLayout(new InternedString("Keyboard")) .WithStateBlock(new InputStateBlock { format = new FourCC(1262836051), sizeInBits = 128 }); @@ -426,6 +426,9 @@ public FastKeyboard() // /Keyboard/IMESelected var ctrlKeyboardIMESelected = Initialize_ctrlKeyboardIMESelected(kButtonLayout, this); + // /Keyboard/IMESelectedObsoleteKey + var ctrlKeyboardIMESelectedObsoleteKey = Initialize_ctrlKeyboardIMESelectedObsoleteKey(kKeyLayout, this); + // Usages. builder.WithControlUsage(0, new InternedString("Back"), ctrlKeyboardescape); builder.WithControlUsage(1, new InternedString("Cancel"), ctrlKeyboardescape); @@ -564,6 +567,7 @@ public FastKeyboard() this.keys[107] = ctrlKeyboardOEM3; this.keys[108] = ctrlKeyboardOEM4; this.keys[109] = ctrlKeyboardOEM5; + this.keys[110] = ctrlKeyboardIMESelectedObsoleteKey; this.keys[111] = ctrlKeyboardf13; this.keys[112] = ctrlKeyboardf14; this.keys[113] = ctrlKeyboardf15; @@ -601,6 +605,7 @@ public FastKeyboard() , 50857060u, 51381349u, 51905638u, 52429927u, 52954216u, 53478505u, 54002794u, 54527083u, 55051372u, 55575661u , 56099950u, 56624239u, 57148528u, 57672817u, 58721394u, 59245683u, 59769972u, 60294261u, 60818550u, 61342839u , 61867128u, 62391417u, 62915706u, 63439995u, 63964284u, 64488573u, 65012862u, 65537151u, 66061440u, 66585729u + , 66585730u }); builder.WithControlTree(new byte[] @@ -665,7 +670,7 @@ public FastKeyboard() , 127, 0, 247, 0, 0, 0, 0, 121, 0, 243, 0, 0, 0, 0, 123, 0, 245, 0, 0, 0, 0, 120, 0, 255, 255, 125, 0, 1, 121, 0 , 255, 255, 126, 0, 1, 122, 0, 255, 255, 127, 0, 1, 123, 0, 255, 255, 128, 0, 1, 125, 0, 249, 0, 0, 0, 0, 127, 0, 251, 0 , 0, 0, 0, 124, 0, 255, 255, 129, 0, 1, 125, 0, 255, 255, 130, 0, 1, 126, 0, 255, 255, 131, 0, 1, 127, 0, 253, 0, 132, 0 - , 1, 127, 0, 255, 255, 0, 0, 0, 127, 0, 255, 0, 0, 0, 0, 128, 0, 255, 255, 133, 0, 1, 127, 0, 255, 255, 0, 0, 0 + , 1, 127, 0, 255, 255, 0, 0, 0, 127, 0, 255, 0, 0, 0, 0, 128, 0, 255, 255, 133, 0, 2, 127, 0, 255, 255, 0, 0, 0 }, new ushort[] { // Control tree node indicies @@ -674,7 +679,7 @@ public FastKeyboard() , 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 , 58, 59, 60, 61, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85 , 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115 - , 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129 + , 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130 }); builder.Finish(); @@ -3686,5 +3691,29 @@ private UnityEngine.InputSystem.Controls.ButtonControl Initialize_ctrlKeyboardIM .Finish(); return ctrlKeyboardIMESelected; } + + private UnityEngine.InputSystem.Controls.KeyControl Initialize_ctrlKeyboardIMESelectedObsoleteKey(InternedString kKeyLayout, InputControl parent) + { + var ctrlKeyboardIMESelectedObsoleteKey = new UnityEngine.InputSystem.Controls.KeyControl(); + ctrlKeyboardIMESelectedObsoleteKey.Setup() + .At(this, 130) + .WithParent(parent) + .WithName("IMESelectedObsoleteKey") + .WithDisplayName("IMESelectedObsoleteKey") + .WithLayout(kKeyLayout) + .IsSynthetic(true) + .IsButton(true) + .WithStateBlock(new InputStateBlock + { + format = new FourCC(1112101920), + byteOffset = 0, + bitOffset = 127, + sizeInBits = 1 + }) + .WithMinAndMax(0, 1) + .Finish(); + ctrlKeyboardIMESelectedObsoleteKey.keyCode = UnityEngine.InputSystem.Key.IMESelected; + return ctrlKeyboardIMESelectedObsoleteKey; + } } } From 6a35da3a6f74cd91053e991b697bd0862f6cffea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Wed, 3 Sep 2025 08:20:57 +0200 Subject: [PATCH 4/8] Removed debug code --- Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs index a6a45c03ad..e86384e6f7 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs @@ -160,8 +160,6 @@ public void Devices_Keyboard_AllKeysEnumeratesAllKeyControls() var index = 0; foreach (var key in keyboard.allKeys) { - if (index == 109) - Debug.Log("TEMP"); Assert.NotNull(key, $"Key at index {index++} was null"); } } From 77da16ba0f240bdb0984ba19cfe66805c6d8a9cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Wed, 3 Sep 2025 08:24:18 +0200 Subject: [PATCH 5/8] Updated changelog --- Packages/com.unity.inputsystem/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index d6e7542213..5dbb8a9c27 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -24,6 +24,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed an issue in `RebindingUISample` preventing UI navigation without Keyboard and Mouse present. - Fixed an issue in `RebindActionUI` which resulted in active binding not being shown after a scene reload. ISXB-1588. - Fixed an issue in `GamepadIconExample` which resulted in icons for left and right triggers not being displayed after a rebind to the exact same controls. ISXB-1593. +- Fixed an issue in `Keyboard` where the sub-script operator would return a `null` key control for the deprecated key `Key.IMESelected`. Now, an aliased `KeyControl`mapping to the IMESelected bit is returned for compability reasons. It is still strongly advised to not rely on this key since `IMESelected` bit isn't strictly a key and will be removed from the `Key` enumeration type in a future major revision. [ISXB-1541](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1541). ## [1.14.2] - 2025-08-05 From 79465a0a103b6868f5e3d366d0db0ecc7987efe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Wed, 3 Sep 2025 08:39:30 +0200 Subject: [PATCH 6/8] Adding missing functional test --- .../InputSystem/CoreTests_Devices_Keyboard.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs index e86384e6f7..c471236c85 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices_Keyboard.cs @@ -199,6 +199,24 @@ public void Devices_Keyboard_SubscriptOperatorCanLookupKeyControlOfCorresponding } } + [Test(Description = "Verifies documented requirement 'This is equivalent to allKeys[(int)key - 1]'")] + [Category("Devices")] + public void Devices_Keyboard_SubscriptOperatorShouldBeEquivalentToShiftedAllKeysLookup() + { + var keyboard = InputSystem.AddDevice(); + var allKeys = keyboard.allKeys; + foreach (var key in sKeys) + { + // Key.None is documented as an invalid key so skip it in this test. + // Sub-script operator is documented to through for invalid key. + if (key == Key.None) + continue; + + var keyControl = keyboard[key]; + Assert.That(keyControl, Is.EqualTo(allKeys[(int)key - 1])); + } + } + [Test] [Category("Devices")] public void Devices_Keyboard_SubscriptOperatorThrowsForInvalidOrOutOfRangeKey() From b803b1d2e2e5d8df333c953fe839e9258361c804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Fri, 5 Sep 2025 15:00:36 +0200 Subject: [PATCH 7/8] Removed if statement and TODO since IMESelected should either not be in allKeys or if it is present it should not evaluate to a non-null control. --- Assets/Tests/InputSystem/CorePerformanceTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Assets/Tests/InputSystem/CorePerformanceTests.cs b/Assets/Tests/InputSystem/CorePerformanceTests.cs index e092f29bb4..3f4fd03599 100644 --- a/Assets/Tests/InputSystem/CorePerformanceTests.cs +++ b/Assets/Tests/InputSystem/CorePerformanceTests.cs @@ -126,8 +126,6 @@ public void Performance_ReadEveryKey() int keyIndex = 0; foreach (var key in keyboard.allKeys) { - if (++keyIndex == (int)KeyEx.IMESelected) // Skip IMESelected as it's not a real key. TODO This is incorrect, it should not even be there - continue; key.ReadValue(); } }) From 2eff10291de9699abbebfeafe9be896628bfea08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Akan=20Sidenvall?= Date: Fri, 5 Sep 2025 15:07:25 +0200 Subject: [PATCH 8/8] Cleanup from review feedback --- .../com.unity.inputsystem/InputSystem/Devices/Keyboard.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs index c311a0df40..2d3ee2d0c7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Keyboard.cs @@ -156,7 +156,6 @@ public unsafe struct KeyboardState : IInputStateTypeInfo [InputControl(name = "f8", displayName = "F8", layout = "Key", bit = (int)Key.F8)] [InputControl(name = "f9", displayName = "F9", layout = "Key", bit = (int)Key.F9)] [InputControl(name = "f10", displayName = "F10", layout = "Key", bit = (int)Key.F10)] - [InputControl(name = "f11", displayName = "F11", layout = "Key", bit = (int)Key.F11)] [InputControl(name = "f12", displayName = "F12", layout = "Key", bit = (int)Key.F12)] [InputControl(name = "OEM1", layout = "Key", bit = (int)Key.OEM1)] @@ -180,10 +179,7 @@ public unsafe struct KeyboardState : IInputStateTypeInfo [InputControl(name = "mediaRewind", displayName = "MediaRewind", layout = "Key", bit = (int)Key.MediaRewind)] [InputControl(name = "mediaForward", displayName = "MediaForward", layout = "Key", bit = (int)Key.MediaForward)] [InputControl(name = "IMESelected", layout = "Button", bit = (int)KeyEx.RemappedIMESelected, synthetic = true)] // Use the last bit to hold IME selected state. - // Disable deprecation warning to not generate warnings in user project about internal use of deprecated key -#pragma warning disable 0618 [InputControl(name = "IMESelectedObsoleteKey", layout = "Key", bit = (int)KeyEx.RemappedIMESelected, synthetic = true)] -#pragma warning restore 0618 public fixed byte keys[kSizeInBytes]; // will be the default in new editor [InputControl(name = "IMESelected", layout = "Button", bit = 0, sizeInBits = 1, synthetic = true)]