Don't treat composite HID keyboards as game controllers (#1839)
* Don't treat composite HID keyboards as game controllers isGameController() calls getMotionRange() without a source class, so any device that claims SOURCE_JOYSTICK in its source mask counts as a controller as soon as it reports AXIS_X/AXIS_Y through *any* source. Bluetooth keyboards with a built-in touchpad hit exactly that: the touchpad reports those axes under SOURCE_MOUSE, while the composite HID descriptor also advertises SOURCE_JOYSTICK. The keyboard then gets bound as a virtual XInput pad, which takes over library navigation and leaves keyboard+mouse unusable in game. Query the motion ranges under SOURCE_JOYSTICK explicitly, in both copies of the check, matching what PhysicalControllerHandler.kt already does. Real controllers are unaffected: they either report joystick axes under SOURCE_JOYSTICK or match on the (isGamepad && hasGamepadKeys) branch, which this does not touch. * Also accept controller axes reported under SOURCE_GAMEPAD Review feedback: the joystick-only query is narrower than the helper in PhysicalControllerHandler.kt, which tries SOURCE_JOYSTICK, then SOURCE_GAMEPAD, then the source-agnostic overload. Query SOURCE_GAMEPAD as well, so a driver that attaches the sticks to the gamepad source instead of the joystick source keeps being classified. The source-agnostic third arm is deliberately left out: that is the one that matches a touchpad's SOURCE_MOUSE axes, i.e. the misclassification this change exists to fix. It is safe in PhysicalControllerHandler because hasMotionRange is only reached from deviceHasTriggerAxis for a device that already has a controller profile bound, never to decide whether a device is a controller. * Keep a single copy of the axis-source check Review feedback: the new helper was pasted into both classes. Define it once in ExternalController (package-private) and have ControllerManager call it, so this change adds no new duplication. Merging the two isGameController() copies outright is left out on purpose: they have drifted, ExternalController skips virtual devices and ControllerManager does not, so unifying them means either starting to skip virtual devices when assigning controller slots or stopping to skip them in the xserver path. That is a behaviour change beyond this bug fix and wants its own PR and a device to test on.
M
Mario J. Barchéin committed
d4e6dabd04bbf6d276d89da1e82b13f293b65cde
Parent: 036c135
Committed by GitHub <noreply@github.com>
on 8/30/2026, 11:40:28 AM