The camera in a strategy game slides in one direction forever. A menu scrolls on its own. Your character strafes without you touching anything. You unplug your controller and it keeps happening. On Linux there is a specific, unglamorous cause that gets misdiagnosed as a game bug constantly. A device that is not a gamepad is being enumerated as one, and it reports its axes jammed at full deflection. Motherboard RGB controllers are the classic offender. Here is how to prove it in about two minutes, and the one-line udev rule that ends it.
USB devices describe themselves with an HID report descriptor, a list of the inputs and outputs they have. A gamepad declares absolute axes, because a stick has a position rather than a direction of travel. So does a mouse wheel. So does a graphics tablet, a VR tracker, a fan controller reporting RPM. And so do a lot of motherboard RGB and LED controllers, which pipe their channel values through the same generic HID plumbing. That last one is what bites people.
Linux does not judge intent. The kernel's joydev driver attaches to anything that looks
joystick-shaped and creates a /dev/input/js* node. udev then tags the device
ID_INPUT_JOYSTICK=1. From that point on, every game library that asks "what gamepads are
attached?" is told about your light controller. SDL, Unity's libudev backend, Unreal, Steam Input.
That alone would be harmless if the device reported neutral values. It usually does not. An LED controller
has no reason to centre anything, so its "axes" sit wherever its firmware leaves them. Frequently the numeric
extreme. A game reading a stick pegged at -32767 does exactly what it would if you were holding
a real stick all the way over. It moves, forever.
Unity makes this especially likely. Its default input mappings feed joystick axes straight into the same
Horizontal and Vertical axes as your keyboard. You do not have to have configured a
controller. You do not have to own a controller.
List every input device the kernel knows about and look at what carries a js handler:
cat /proc/bus/input/devices
You want a stanza whose Name is obviously not a game controller, but which has a
js* handler and an ABS= line. A real example from an ASRock board:
I: Bus=0003 Vendor=26ce Product=01a2 Version=0110
N: Name="ASRock LED Controller"
H: Handlers=kbd event2 js0
B: ABS=100000301ff
An RGB controller with twelve absolute axes and a joystick node. For a quick shortlist:
ls -l /dev/input/js* /dev/input/by-id/*joystick* 2>/dev/null
If the only js0 on your machine belongs to something that lights up your case, you have found
it. Confirm what udev is advertising:
udevadm info /dev/input/event2 | grep -E 'ID_INPUT|ID_VENDOR_ID|ID_MODEL'
Do not stop at "it is enumerated". Read what it reports. That is the difference between a harmless phantom device and the thing ruining your game. The joystick API dumps the full current state the moment you open the node, so a few seconds of reading is enough:
python3 - <<'PY'
import struct, os, time
f = os.open("/dev/input/js0", os.O_RDONLY | os.O_NONBLOCK)
fmt, axes = "IhBB", {}
sz = struct.calcsize(fmt)
t0 = time.time()
while time.time() - t0 < 3:
try:
data = os.read(f, sz)
except BlockingIOError:
time.sleep(0.02); continue
if not data or len(data) < sz: continue
_, val, typ, num = struct.unpack(fmt, data)
if typ & 0x02: axes[num] = val
os.close(f)
for n, v in sorted(axes.items()):
print(f"axis {n}: {v}{' <-- OFF-CENTRE' if v else ''}")
PY
A genuinely idle device reports 0 across the board. The ASRock controller above reported
ten of its twelve axes pinned at -32767. Hard against the stop, on every one.
That is a game being told you are shoving a stick into the corner and holding it there.
evtest does the same job interactively if you would rather watch events scroll by. It needs
root and a device number.
Using libudev for joystick management and Importing game controller configs in the
game's Player.log, usually under
~/.config/unity3d/<Company>/<Game>/. That confirms the game enumerates joysticks at
all, which is the link between "a phantom device exists" and "my camera will not stop moving".
The fix is to stop udev advertising the device as a joystick. Every consumer discovers gamepads by that tag. SDL, Unity, Steam Input. Remove the tag and the device goes invisible to games while staying completely functional for whatever it actually does.
Create /etc/udev/rules.d/99-not-a-joystick.rules, substituting your own vendor and product
IDs from step 1:
SUBSYSTEM=="input", ATTRS{idVendor}=="26ce", ATTRS{idProduct}=="01a2", \
ENV{ID_INPUT_JOYSTICK}="", ENV{ID_INPUT}=""
Reload and re-apply without rebooting:
sudo udevadm control --reload-rules
sudo udevadm trigger --subsystem-match=input
Verify the tag is gone:
udevadm info /dev/input/event2 | grep ID_INPUT # should print nothing
/dev/input/js0 node may still exist. That is fine. The kernel's
joydev driver creates it from the device's capabilities, and a udev rule does not change what
the hardware claims to be. The rule changes discovery. SDL and Unity enumerate tagged devices, so an
untagged one never gets opened. Want the node gone too and you have to unbind or blacklist
joydev, which kills your real controllers as well. Not worth it. Judge success by the missing
ID_INPUT_JOYSTICK tag and by the game behaving, not by the node disappearing.
This does not affect RGB control. Lighting software talks to the device over USB HID directly, which is how OpenRGB works. The input-subsystem tag is a separate concern.
On Bazzite, Silverblue, Aurora or another rpm-ostree system this needs no special handling.
/etc is writable and persists across updates, so drop the rule in
/etc/udev/rules.d/ exactly as above. No layered packages. No rpm-ostree. No
reboot.
SDL_GAMECONTROLLER_IGNORE_DEVICES=0x26ce/0x01a2 as a
launch option. Handy for a one-off. It only works for games that discover pads through SDL. Unity's own
libudev backend ignores SDL environment variables entirely, which is exactly when you most need the fix.
The udev rule covers everything.Be honest with the evidence before you go rule-writing. Two checks:
This one is worth knowing because it is invisible from inside the game and survives every obvious troubleshooting step. Reinstall, verify files, swap drivers, unplug every peripheral you own. A light controller bolted to your motherboard keeps quietly holding the stick down.