BeamNG.drive 0.39 native lost G29 FFB because SDL3's HIDAPI lg4ff driver advertises an effect-status query it cannot answer (12,201 failures per session in beamng.log). DOCS-g29-ffb.md records the diagnosis, the narrow SDL_JOYSTICK_HIDAPI_LG4FF=0 hint vs the broad one, and the launch-option %command% bug that had gamemoderun passed as a game argument. install_new_lg4ff.sh swaps the in-tree hid-logitech for new-lg4ff via DKMS (dkms.conf installs it under the same name in updates/dkms/), regenerates initramfs presets that bundle the module, and offers --status/--revert. Passed install/status/revert in vmbox; host run pending. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SS8M5wk88nJTXxDWz61KKk
5.1 KiB
Logitech G29: no force feedback in BeamNG.drive (native Linux)
Files: fixes/install_new_lg4ff.sh (optional driver upgrade),
~/src/steam-backlog-enforcer/steam_backlog_enforcer/_desktop_env.py (the
host-level env var). Shifter companion: DOCS-g29-shifter.md.
Symptom
BeamNG.drive 0.39.x, native Linux build (not Proton): the G29 steers, pedals and buttons work, but the wheel is limp — no resistance, no self-centring. Other games with FFB are unaffected.
Cause (measured 2026-09-14, BeamNG 0.39.4.0, SDL 3.4.12 static)
~/.local/share/BeamNG/BeamNG.drive/current/beamng.log:
D|logHapticFeatures| Device joystick3 supports the following ffb features:
Constant Sine Triangle SawToothUp SawToothDown Spring Damper Friction Status Query
I|inputRegistry| Created ffbid 0, ... on device joystick3 with axis id=0
E|updateFrame| Failed to get joystick3 FFB effect status: (x12,201 per session)
That feature list is SDL3's HIDAPI lg4ff driver (SDL_hidapi_lg4ff.c),
which talks to the wheel over hidraw and bypasses the kernel hid-logitech
driver — the kernel driver advertises only constant/gain/autocenter
(capabilities/ff = 300040000 0). The hidapi haptic backend advertises a
status query it cannot answer; BeamNG polls it every frame, gets an error, and
treats the effect as dead. Known regression:
https://www.beamng.com/threads/ffb-not-working-on-linux.110807/.
Not involved: the shifter HID-BPF (input direction only), hidraw permissions (SDL had already opened the device), Proton (not used).
Fix: make SDL use the kernel driver
Two env vars exist; they are not interchangeable:
| var | scope | use |
|---|---|---|
SDL_JOYSTICK_HIDAPI_LG4FF=0 |
only the Logitech-wheel HIDAPI driver | host-level: set for every game, gamepads keep HIDAPI (BT rumble, LEDs) |
SDL_JOYSTICK_HIDAPI=0 |
all of SDL's HIDAPI joystick drivers | forum-proven fallback, per-game only |
Both names verified against strings in BinLinux/BeamNG.drive.x64.
-
Per game — Steam → BeamNG.drive → Properties → Launch Options:
SDL_JOYSTICK_HIDAPI_LG4FF=0 LSFG_PROCESS=BeamNG.Drive gamemoderun %command%The previous value had no
%command%, so Steam appendedLSFG_PROCESS=... gamemoderun ./BinLinux/BeamNG.drive.x64as arguments to the game (visible inpgrep -a BeamNG.drive.x64) and neither gamemode nor LSFG was ever applied. -
Host level — Steam on this machine is started by
steam-backlog-enforcerthroughsudo -u kuhy env <allowlist> steam ….sudo'senv_resetmakes that list the entire environment Steam and every game sees, so/etc/environment,~/.profileor~/.pam_environmentnever reach it. The variable lives indesktop_env_args()in the enforcer's_desktop_env.py. Check it landed:tr '\0' '\n' < /proc/$(pgrep -x steam)/environ | grep LG4FFA missing line means Steam predates the enforcer change — it takes effect on the next Steam launch (
systemctl restart steam-backlog-enforcerkills a running game; checkpgrep -f BeamNG.drive.x64first).
Verify
- Game-independent motor check, BeamNG closed:
fftest /dev/input/by-id/usb-Logitech_G29_Driving_Force_Racing_Wheel-event-joystick— effects 0 (constant) and 4 (autocenter) must move the wheel. - In a fresh
beamng.logthe haptic feature line becomes the kernel-style list andgrep -c 'FFB effect status' beamng.logdrops to 0. A non-zero count with working force is fine; a zero count with no force is not. - The gate is physical: drive a car, feel resistance and centring.
Optional: new-lg4ff instead of the in-tree driver
fixes/install_new_lg4ff.sh installs new-lg4ff-dkms-git (AUR). Its
dkms.conf sets DEST_MODULE_NAME="hid-logitech", so DKMS places it in
updates/dkms/ and it shadows the in-tree module by name — no blacklist,
no modules-load.d. Once loaded it registers as hid_logitech_new. Adds
spring/damper/friction/periodic effects and the spring_level /
damper_level / friction_level / gain / autocenter sysfs knobs.
Traps the script handles:
mkinitcpio'sautodetecthook bundleshid-logitechinto the initramfs because the wheel is plugged in at build time; a stale in-tree copy there would load first at boot. The script regenerates the presets when an image carries the module.dkms status <name>is a prefix filter that prints everything on a miss; always grepdkms statusoutput.- USB interface 1 of the G29 (
0003:046D:C24F.0007) is unbound by design.
~/src/testsAndMisc/linux_configuration/fixes/install_new_lg4ff.sh # install (runs yay; needs a TTY)
~/src/testsAndMisc/linux_configuration/fixes/install_new_lg4ff.sh --status # which module owns the wheel + ff caps
~/src/testsAndMisc/linux_configuration/fixes/install_new_lg4ff.sh --revert # pacman -Rns + reload, no reboot