Skip to content

Commit 7da428c

Browse files
committed
Set different Xbox button bit mapping based on PID/VID
Only for macOS since Windows uses XInput. So far it only does this for a particular PID VID group and can likely be expanded and improved.
1 parent 7924b18 commit 7da428c

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

Assets/Tests/InputSystem/APIVerificationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ internal static bool IgnoreTypeForDocsByName(string fullName)
183183
#if UNITY_EDITOR_OSX
184184
fullName == typeof(UnityEngine.InputSystem.XInput.XboxGamepadMacOS).FullName ||
185185
fullName == typeof(UnityEngine.InputSystem.XInput.XboxOneGampadMacOSWireless).FullName ||
186+
fullName == typeof(UnityEngine.InputSystem.XInput.XboxGamepadMacOSWireless).FullName ||
186187
#endif
187188
#if UNITY_EDITOR_WIN
188189
fullName == typeof(UnityEngine.InputSystem.XInput.XInputControllerWindows).FullName ||

Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XInputSupport.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ public static void Initialize()
2828
InputSystem.RegisterLayout<XboxGamepadMacOS>(
2929
matches: new InputDeviceMatcher().WithInterface("HID")
3030
.WithProduct("Xbox.*Wired Controller"));
31+
32+
// Matching older controllers that have different View and Share buttons than the newer Xbox Series
33+
// controllers.
34+
// Reported inhttps://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1264
3135
InputSystem.RegisterLayout<XboxOneGampadMacOSWireless>(
36+
matches: new InputDeviceMatcher().WithInterface("HID")
37+
.WithCapability("vendorId", 0x045E)
38+
.WithCapability("productId", 0x02E0));
39+
40+
// This layout is for all the other Xbox One or Series controllers that have the same View and Share buttons.
41+
// Reported in https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-385
42+
InputSystem.RegisterLayout<XboxGamepadMacOSWireless>(
3243
matches: new InputDeviceMatcher().WithInterface("HID")
3344
.WithProduct("Xbox.*Wireless Controller"));
3445
#endif

Packages/com.unity.inputsystem/InputSystem/Plugins/XInput/XboxGamepadMacOS.cs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ internal struct XInputControllerWirelessOSXState : IInputStateTypeInfo
111111
public enum Button
112112
{
113113
Start = 11,
114-
Select = 10,
114+
Select = 16,
115115
LeftThumbstickPress = 13,
116116
RightThumbstickPress = 14,
117117
LeftShoulder = 6,
@@ -194,6 +194,98 @@ public XInputControllerWirelessOSXState WithDpad(byte value)
194194
leftStickY = 32767
195195
};
196196
}
197+
198+
[StructLayout(LayoutKind.Explicit)]
199+
internal struct XInputControllerWirelessOSXStateV2 : IInputStateTypeInfo
200+
{
201+
public static FourCC kFormat => new FourCC('H', 'I', 'D');
202+
203+
public enum Button
204+
{
205+
Start = 11,
206+
Select = 10,
207+
LeftThumbstickPress = 13,
208+
RightThumbstickPress = 14,
209+
LeftShoulder = 6,
210+
RightShoulder = 7,
211+
A = 0,
212+
B = 1,
213+
X = 3,
214+
Y = 4,
215+
}
216+
[FieldOffset(0)]
217+
private byte padding;
218+
219+
[InputControl(name = "leftStick", layout = "Stick", format = "VC2S")]
220+
[InputControl(name = "leftStick/x", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
221+
[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")]
222+
[InputControl(name = "leftStick/right", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1")]
223+
[InputControl(name = "leftStick/y", offset = 2, format = "USHT", parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
224+
[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")]
225+
[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")]
226+
[FieldOffset(1)] public ushort leftStickX;
227+
[FieldOffset(3)] public ushort leftStickY;
228+
229+
[InputControl(name = "rightStick", layout = "Stick", format = "VC2S")]
230+
[InputControl(name = "rightStick/x", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
231+
[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")]
232+
[InputControl(name = "rightStick/right", offset = 0, format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=1,clampMin=0.5,clampMax=1")]
233+
[InputControl(name = "rightStick/y", offset = 2, format = "USHT", parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
234+
[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")]
235+
[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")]
236+
[FieldOffset(5)] public ushort rightStickX;
237+
[FieldOffset(7)] public ushort rightStickY;
238+
239+
[InputControl(name = "leftTrigger", format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=0.01560998")]
240+
[FieldOffset(9)] public ushort leftTrigger;
241+
[InputControl(name = "rightTrigger", format = "USHT", parameters = "normalize,normalizeMin=0,normalizeMax=0.01560998")]
242+
[FieldOffset(11)] public ushort rightTrigger;
243+
244+
[InputControl(name = "dpad", format = "BIT", layout = "Dpad", sizeInBits = 4, defaultState = 8)]
245+
[InputControl(name = "dpad/up", format = "BIT", layout = "DiscreteButton", parameters = "minValue=8,maxValue=2,nullValue=0,wrapAtValue=9", bit = 0, sizeInBits = 4)]
246+
[InputControl(name = "dpad/right", format = "BIT", layout = "DiscreteButton", parameters = "minValue=2,maxValue=4", bit = 0, sizeInBits = 4)]
247+
[InputControl(name = "dpad/down", format = "BIT", layout = "DiscreteButton", parameters = "minValue=4,maxValue=6", bit = 0, sizeInBits = 4)]
248+
[InputControl(name = "dpad/left", format = "BIT", layout = "DiscreteButton", parameters = "minValue=6, maxValue=8", bit = 0, sizeInBits = 4)]
249+
[FieldOffset(13)]
250+
public byte dpad;
251+
252+
[InputControl(name = "start", bit = (uint)Button.Start, displayName = "Start")]
253+
[InputControl(name = "select", bit = (uint)Button.Select, displayName = "Select")]
254+
[InputControl(name = "leftStickPress", bit = (uint)Button.LeftThumbstickPress)]
255+
[InputControl(name = "rightStickPress", bit = (uint)Button.RightThumbstickPress)]
256+
[InputControl(name = "leftShoulder", bit = (uint)Button.LeftShoulder)]
257+
[InputControl(name = "rightShoulder", bit = (uint)Button.RightShoulder)]
258+
[InputControl(name = "buttonSouth", bit = (uint)Button.A, displayName = "A")]
259+
[InputControl(name = "buttonEast", bit = (uint)Button.B, displayName = "B")]
260+
[InputControl(name = "buttonWest", bit = (uint)Button.X, displayName = "X")]
261+
[InputControl(name = "buttonNorth", bit = (uint)Button.Y, displayName = "Y")]
262+
263+
[FieldOffset(14)]
264+
public uint buttons;
265+
266+
public FourCC format => kFormat;
267+
268+
public XInputControllerWirelessOSXStateV2 WithButton(Button button)
269+
{
270+
Debug.Assert((int)button < 32, $"Expected button < 32, so we fit into the 32 bit wide bitmask");
271+
buttons |= 1U << (int)button;
272+
return this;
273+
}
274+
275+
public XInputControllerWirelessOSXStateV2 WithDpad(byte value)
276+
{
277+
dpad = value;
278+
return this;
279+
}
280+
281+
public static XInputControllerWirelessOSXStateV2 defaultState => new XInputControllerWirelessOSXStateV2
282+
{
283+
rightStickX = 32767,
284+
rightStickY = 32767,
285+
leftStickX = 32767,
286+
leftStickY = 32767
287+
};
288+
}
197289
}
198290
namespace UnityEngine.InputSystem.XInput
199291
{
@@ -223,5 +315,22 @@ public class XboxGamepadMacOS : XInputController
223315
public class XboxOneGampadMacOSWireless : XInputController
224316
{
225317
}
318+
319+
/// <summary>
320+
/// A wireless Xbox One or Xbox Series Gamepad connected to a macOS computer.
321+
/// </summary>
322+
/// <remarks>
323+
/// An Xbox One/Series wireless gamepad connected to a mac using Bluetooth.
324+
/// The reason this is different from <see cref="XboxOneGampadMacOSWireless"/> is that some Xbox Controllers have
325+
/// different View and Share button bit mapping. So we need to use a different layout for those controllers. It seems
326+
/// that some Xbox One and Xbox Series controller share the same mappings so this combines them all.
327+
/// Note: only the latest version of Xbox One wireless gamepads support Bluetooth. Older models only work
328+
/// with a proprietary Xbox wireless protocol, and cannot be used on a Mac.
329+
/// Unlike wired controllers, bluetooth-cabable Xbox One controllers do not need a custom driver to work on macOS.
330+
/// </remarks>
331+
[InputControlLayout(displayName = "Wireless Xbox Controller", stateType = typeof(XInputControllerWirelessOSXStateV2), hideInUI = true)]
332+
public class XboxGamepadMacOSWireless : XInputController
333+
{
334+
}
226335
}
227336
#endif // UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX

0 commit comments

Comments
 (0)