From 2d7874568ac915bf98b36cfc13177aa36442f90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Sidenvall?= Date: Fri, 20 Dec 2024 13:13:01 +0100 Subject: [PATCH 1/6] Set different Xbox button mapping based on VID/PID Only for macOS since Windows uses XInput. Uses some PIDs sourced from SDL2 database. Adds automated test to establish layout mapping. We will need to update this based on reports from users in the future. --- .../Tests/InputSystem/APIVerificationTests.cs | 1 + .../Tests/InputSystem/Plugins/XInputTests.cs | 61 +++++++++- .../Plugins/XInput/XInputSupport.cs | 28 ++++- .../Plugins/XInput/XboxGamepadMacOS.cs | 111 +++++++++++++++++- 4 files changed, 198 insertions(+), 3 deletions(-) diff --git a/Assets/Tests/InputSystem/APIVerificationTests.cs b/Assets/Tests/InputSystem/APIVerificationTests.cs index 63aef7d580..c96b435404 100644 --- a/Assets/Tests/InputSystem/APIVerificationTests.cs +++ b/Assets/Tests/InputSystem/APIVerificationTests.cs @@ -183,6 +183,7 @@ internal static bool IgnoreTypeForDocsByName(string fullName) #if UNITY_EDITOR_OSX fullName == typeof(UnityEngine.InputSystem.XInput.XboxGamepadMacOS).FullName || fullName == typeof(UnityEngine.InputSystem.XInput.XboxOneGampadMacOSWireless).FullName || + fullName == typeof(UnityEngine.InputSystem.XInput.XboxGamepadMacOSWireless).FullName || #endif #if UNITY_EDITOR_WIN fullName == typeof(UnityEngine.InputSystem.XInput.XInputControllerWindows).FullName || diff --git a/Assets/Tests/InputSystem/Plugins/XInputTests.cs b/Assets/Tests/InputSystem/Plugins/XInputTests.cs index 1532f44aa6..742cbffdce 100644 --- a/Assets/Tests/InputSystem/Plugins/XInputTests.cs +++ b/Assets/Tests/InputSystem/Plugins/XInputTests.cs @@ -5,6 +5,7 @@ using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Utilities; using System.Runtime.InteropServices; +using UnityEngine.InputSystem.HID; using UnityEngine.InputSystem.Processors; #if UNITY_EDITOR_WIN || UNITY_EDITOR_OSX || UNITY_XBOXONE || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN @@ -23,7 +24,7 @@ internal class XInputTests : CoreTestsFixture [Category("Devices")] #if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX [TestCase("Xbox One Wired Controller", "Microsoft", "HID", "XboxGamepadMacOS")] - [TestCase("Xbox One Wireless Controller", "Microsoft", "HID", "XboxOneGampadMacOSWireless")] + [TestCase("Xbox Series Wireless Controller", "Microsoft", "HID", "XboxGamepadMacOSWireless")] #endif #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN || UNITY_WSA [TestCase(null, null, "XInput", "XInputControllerWindows")] @@ -148,6 +149,64 @@ public void Devices_SupportXboxControllerOnOSX() AssertButtonPress(gamepad, new XInputControllerOSXState().WithButton(XInputControllerOSXState.Button.Select), gamepad.selectButton); } + [TestCase(0x045E, 0x02E0, 16, 11)] // Xbox One Wireless Controller + [TestCase(0x045E, 0x0B20, 10, 11)] // Xbox Series X|S Wireless Controller + // This test is used to establish the correct button map layout based on the PID and VIDs. The usual difference + // is around the select and start button bits. + // If the layout is changed this test will fail and will need to be adapted either with a new device/layout or + // a new button map. + public void Devices_SupportWirelessXboxOneAndSeriesControllerOnOSX(int vendorId, int productId, int selectBit, int startBit) + { + // Fake a real Xbox Wireless Controller + var xboxGamepad = InputSystem.AddDevice(new InputDeviceDescription + { + interfaceName = "HID", + product = "Xbox Wireless Controller", + manufacturer = "Microsoft", + capabilities = new HID.HIDDeviceDescriptor + { + vendorId = vendorId, + productId = productId, + }.ToJson() + }); + + + Assert.That(xboxGamepad, Is.AssignableTo()); + + var gamepad = (XInputController)xboxGamepad; + Assert.That(gamepad.selectButton.isPressed, Is.False); + + // Check if the controller is an Xbox One from a particular type where we know the select and start buttons are + // different + if (productId == 0x02e0) + { + Assert.That(xboxGamepad, Is.AssignableTo()); + + InputSystem.QueueStateEvent(gamepad, + new XInputControllerWirelessOSXState + { + buttons = (uint)(1 << selectBit | + 1 << startBit) + }); + InputSystem.Update(); + } + else + { + Assert.That(xboxGamepad, Is.AssignableTo()); + + InputSystem.QueueStateEvent(gamepad, + new XInputControllerWirelessOSXState + { + buttons = (uint)(1 << selectBit | + 1 << startBit) + }); + InputSystem.Update(); + } + + Assert.That(gamepad.selectButton.isPressed); + Assert.That(gamepad.startButton.isPressed); + } + // Disable tests in standalone builds from 2022.1+ see UUM-19622 #if !UNITY_STANDALONE_OSX || !TEMP_DISABLE_STANDALONE_OSX_XINPUT_TEST [Test] diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs index 6e760a2393..d377154280 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs @@ -16,6 +16,15 @@ static class XInputSupport { public static void Initialize() { + void RegisterXboxOneWirelessFromProductAndVendorID(int vendorId, int productId) + { + InputSystem.RegisterLayout( + matches: new InputDeviceMatcher().WithInterface("HID") + .WithProduct("Xbox.*Wireless Controller") + .WithCapability("vendorId", vendorId) + .WithCapability("productId", productId)); + } + // Base layout for Xbox-style gamepad. InputSystem.RegisterLayout(); @@ -28,7 +37,24 @@ public static void Initialize() InputSystem.RegisterLayout( matches: new InputDeviceMatcher().WithInterface("HID") .WithProduct("Xbox.*Wired Controller")); - InputSystem.RegisterLayout( + + // Matching older Xbox One controllers that have different View and Share buttons than the newer Xbox Series + // controllers. + // Reported inhttps://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1264 + // Based on devices from this list + // https://github.com/mdqinc/SDL_GameControllerDB/blob/a453871de2e0e2484544514c6c080e1e916d620c/gamecontrollerdb.txt#L798C1-L806C1 + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02B0); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02D1); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02DD); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02E0); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02E3); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02EA); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02FD); + RegisterXboxOneWirelessFromProductAndVendorID(0x045E, 0x02FF); + + // This layout is for all the other Xbox One or Series controllers that have the same View and Share buttons. + // Reported in https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-385 + InputSystem.RegisterLayout( matches: new InputDeviceMatcher().WithInterface("HID") .WithProduct("Xbox.*Wireless Controller")); #endif diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs index ab43ddbaec..e83aa099e1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs @@ -111,7 +111,7 @@ internal struct XInputControllerWirelessOSXState : IInputStateTypeInfo public enum Button { Start = 11, - Select = 10, + Select = 16, LeftThumbstickPress = 13, RightThumbstickPress = 14, LeftShoulder = 6, @@ -194,6 +194,98 @@ public XInputControllerWirelessOSXState WithDpad(byte value) leftStickY = 32767 }; } + + [StructLayout(LayoutKind.Explicit)] + internal struct XInputControllerWirelessOSXStateV2 : IInputStateTypeInfo + { + public static FourCC kFormat => new FourCC('H', 'I', 'D'); + + public enum Button + { + Start = 11, + Select = 10, + LeftThumbstickPress = 13, + RightThumbstickPress = 14, + LeftShoulder = 6, + RightShoulder = 7, + A = 0, + B = 1, + X = 3, + Y = 4, + } + [FieldOffset(0)] + private byte padding; + + [InputControl(name = "leftStick", layout = "Stick", format = "VC2S")] + [InputControl(name = "leftStick/x", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")] + [InputControl(name = "leftStick/left", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0,clampMax=0.5,invert")] + [InputControl(name = "leftStick/right", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1")] + [InputControl(name = "leftStick/y", offset = 2, format = "USHT", parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")] + [InputControl(name = "leftStick/up", offset = 2, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0,clampMax=0.5,invert")] + [InputControl(name = "leftStick/down", offset = 2, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1,invert=false")] + [FieldOffset(1)] public ushort leftStickX; + [FieldOffset(3)] public ushort leftStickY; + + [InputControl(name = "rightStick", layout = "Stick", format = "VC2S")] + [InputControl(name = "rightStick/x", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")] + [InputControl(name = "rightStick/left", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0,clampMax=0.5,invert")] + [InputControl(name = "rightStick/right", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1")] + [InputControl(name = "rightStick/y", offset = 2, format = "USHT", parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")] + [InputControl(name = "rightStick/up", offset = 2, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0,clampMax=0.5,invert")] + [InputControl(name = "rightStick/down", offset = 2, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1,invert=false")] + [FieldOffset(5)] public ushort rightStickX; + [FieldOffset(7)] public ushort rightStickY; + + [InputControl(name = "leftTrigger", format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=0.01560998")] + [FieldOffset(9)] public ushort leftTrigger; + [InputControl(name = "rightTrigger", format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=0.01560998")] + [FieldOffset(11)] public ushort rightTrigger; + + [InputControl(name = "dpad", format = "BIT", layout = "Dpad", sizeInBits = 4, defaultState = 8)] + [InputControl(name = "dpad/up", format = "BIT", layout = "DiscreteButton", parameters = "minValue=8,maxValue=2,nullValue=0,wrapAtValue=9", bit = 0, sizeInBits = 4)] + [InputControl(name = "dpad/right", format = "BIT", layout = "DiscreteButton", parameters = "minValue=2,maxValue=4", bit = 0, sizeInBits = 4)] + [InputControl(name = "dpad/down", format = "BIT", layout = "DiscreteButton", parameters = "minValue=4,maxValue=6", bit = 0, sizeInBits = 4)] + [InputControl(name = "dpad/left", format = "BIT", layout = "DiscreteButton", parameters = "minValue=6, maxValue=8", bit = 0, sizeInBits = 4)] + [FieldOffset(13)] + public byte dpad; + + [InputControl(name = "start", bit = (uint)Button.Start, displayName = "Start")] + [InputControl(name = "select", bit = (uint)Button.Select, displayName = "Select")] + [InputControl(name = "leftStickPress", bit = (uint)Button.LeftThumbstickPress)] + [InputControl(name = "rightStickPress", bit = (uint)Button.RightThumbstickPress)] + [InputControl(name = "leftShoulder", bit = (uint)Button.LeftShoulder)] + [InputControl(name = "rightShoulder", bit = (uint)Button.RightShoulder)] + [InputControl(name = "buttonSouth", bit = (uint)Button.A, displayName = "A")] + [InputControl(name = "buttonEast", bit = (uint)Button.B, displayName = "B")] + [InputControl(name = "buttonWest", bit = (uint)Button.X, displayName = "X")] + [InputControl(name = "buttonNorth", bit = (uint)Button.Y, displayName = "Y")] + + [FieldOffset(14)] + public uint buttons; + + public FourCC format => kFormat; + + public XInputControllerWirelessOSXStateV2 WithButton(Button button) + { + Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask"); + buttons |= 1U << (int)button; + return this; + } + + public XInputControllerWirelessOSXStateV2 WithDpad(byte value) + { + dpad = value; + return this; + } + + public static XInputControllerWirelessOSXStateV2 defaultState => new XInputControllerWirelessOSXStateV2 + { + rightStickX = 32767, + rightStickY = 32767, + leftStickX = 32767, + leftStickY = 32767 + }; + } } namespace UnityEngine.InputSystem.XInput { @@ -223,5 +315,22 @@ public class XboxGamepadMacOS : XInputController public class XboxOneGampadMacOSWireless : XInputController { } + + /// + /// A wireless Xbox One or Xbox Series Gamepad connected to a macOS computer. + /// + /// + /// An Xbox One/Series wireless gamepad connected to a mac using Bluetooth. + /// The reason this is different from is that some Xbox Controllers have + /// different View and Share button bit mapping. So we need to use a different layout for those controllers. It seems + /// that some Xbox One and Xbox Series controller share the same mappings so this combines them all. + /// Note: only the latest version of Xbox One wireless gamepads support Bluetooth. Older models only work + /// with a proprietary Xbox wireless protocol, and cannot be used on a Mac. + /// Unlike wired controllers, bluetooth-cabable Xbox One controllers do not need a custom driver to work on macOS. + /// + [InputControlLayout(displayName = "Wireless Xbox Controller", stateType = typeof(XInputControllerWirelessOSXStateV2), hideInUI = true)] + public class XboxGamepadMacOSWireless : XInputController + { + } } #endif // UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX From 17b94317c8d0e9b699b528c2e2e2adc696fcb270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Freire?= Date: Tue, 14 Jan 2025 16:32:45 +0100 Subject: [PATCH 2/6] Show HID version in Input Debugger device window Will help track down devices that might have a firmware version update that changes the button bit mapping structure while maintaining the same PID. --- .../InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs index bda67adbf8..2658ec1922 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs @@ -132,6 +132,8 @@ internal void OnGUI() EditorGUILayout.LabelField("Product", m_Device.description.product); if (!string.IsNullOrEmpty(m_Device.description.manufacturer)) EditorGUILayout.LabelField("Manufacturer", m_Device.description.manufacturer); + if (!string.IsNullOrEmpty(m_Device.description.version)) + EditorGUILayout.LabelField("Version", m_Device.description.version); if (!string.IsNullOrEmpty(m_Device.description.serial)) EditorGUILayout.LabelField("Serial Number", m_Device.description.serial); EditorGUILayout.LabelField("Device ID", m_DeviceIdString); From 6ae6742e5930fde8bb8754f9e5d25094c1df8802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Freire?= Date: Tue, 14 Jan 2025 16:36:21 +0100 Subject: [PATCH 3/6] Update 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 f43554ee84..9c7f08597f 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -46,6 +46,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed multiple `OnScreenStick` Components that does not work together when using them simultaneously in isolation mode. [ISXB-813](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-813) - Fixed an issue in input actions editor window that caused certain fields in custom input composite bindings to require multiple clicks to action / focus. [ISXB-1171](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1171) - Fixed an editor/player hang in `InputSystemUIInputModule` due to an infinite loop. This was caused by the assumption that `RemovePointerAtIndex` would _always_ successfully remove the pointer, which is not the case with touch based pointers. [ISXB-1258](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1258) +- Fixed wrong Xbox Series S|X and Xbox One wireless controllers "View" button mapping on macOS by expanding device PID and VID matching. [ISXB-1264](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1264) ### Changed - Changed location of the link xml file (code stripping rules), from a temporary directory to the project Library folder (ISX-2140). From fd28fa2061d0284b9183388c67e7b46cf3eb5bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Freire?= Date: Tue, 14 Jan 2025 17:17:51 +0100 Subject: [PATCH 4/6] Fix test --- Assets/Tests/InputSystem/Plugins/XInputTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Assets/Tests/InputSystem/Plugins/XInputTests.cs b/Assets/Tests/InputSystem/Plugins/XInputTests.cs index 742cbffdce..051a48791f 100644 --- a/Assets/Tests/InputSystem/Plugins/XInputTests.cs +++ b/Assets/Tests/InputSystem/Plugins/XInputTests.cs @@ -217,7 +217,12 @@ public void Devices_SupportXboxWirelessControllerOnOSX() { interfaceName = "HID", product = "Xbox One Wireless Controller", - manufacturer = "Microsoft" + manufacturer = "Microsoft", + capabilities = new HID.HIDDeviceDescriptor + { + vendorId = 0x045E, + productId = 0x02E0, + }.ToJson() }); Assert.That(device, Is.AssignableTo()); From 96a8d1b626c4d9ca5fcc996cb5bb0b37eb75d397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Freire?= Date: Tue, 14 Jan 2025 17:28:38 +0100 Subject: [PATCH 5/6] Guard method with macOS platform define --- .../Plugins/XInput/XInputSupport.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs index d377154280..06a3e37d94 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs @@ -16,15 +16,6 @@ static class XInputSupport { public static void Initialize() { - void RegisterXboxOneWirelessFromProductAndVendorID(int vendorId, int productId) - { - InputSystem.RegisterLayout( - matches: new InputDeviceMatcher().WithInterface("HID") - .WithProduct("Xbox.*Wireless Controller") - .WithCapability("vendorId", vendorId) - .WithCapability("productId", productId)); - } - // Base layout for Xbox-style gamepad. InputSystem.RegisterLayout(); @@ -57,6 +48,16 @@ void RegisterXboxOneWirelessFromProductAndVendorID(int vendorId, int productId) InputSystem.RegisterLayout( matches: new InputDeviceMatcher().WithInterface("HID") .WithProduct("Xbox.*Wireless Controller")); + + void RegisterXboxOneWirelessFromProductAndVendorID(int vendorId, int productId) + { + InputSystem.RegisterLayout( + matches: new InputDeviceMatcher().WithInterface("HID") + .WithProduct("Xbox.*Wireless Controller") + .WithCapability("vendorId", vendorId) + .WithCapability("productId", productId)); + } + #endif } } From 2e6814582c5d9b4f4f3c14e77903e87607fa7f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Freire?= Date: Mon, 20 Jan 2025 14:51:19 +0100 Subject: [PATCH 6/6] Changed Debug.Assert message --- .../InputSystem/Plugins/XInput/XboxGamepadMacOS.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs index e83aa099e1..c57bfe3e8b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs @@ -97,7 +97,7 @@ public enum Button public XInputControllerOSXState WithButton(Button button) { - Debug.Assert((int)button < 16, $"Expected button < 16, so we fit into the 16 bit wide bitmask"); + Debug.Assert((int)button < 16, $"A maximum of 16 buttons is supported for this layout."); buttons |= (ushort)(1U << (int)button); return this; } @@ -175,7 +175,7 @@ public enum Button public XInputControllerWirelessOSXState WithButton(Button button) { - Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask"); + Debug.Assert((int)button < 32, $"A maximum of 32 buttons is supported for this layout."); buttons |= 1U << (int)button; return this; } @@ -267,7 +267,7 @@ public enum Button public XInputControllerWirelessOSXStateV2 WithButton(Button button) { - Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask"); + Debug.Assert((int)button < 32, $"A maximum of 32 buttons is supported for this layout."); buttons |= 1U << (int)button; return this; }