Linux Joystick Issue

Hi all, I realise ED under Linux is not supported but was wondering if anyone else has had the same issue and, hopefully, found a fix.

I have a pair of Virpil sticks that I can not get to work properly in ED under Linux. I have tried both the Steam version and the standalone version using Lutris. Under Steam, I can get my Joysticks to detect as Controllers which doesn't allow me to use all of the available buttons or axis.

Under Lutris, using the wine control panel, I can again get them to show up as Xinput control pads, but as soon as I override to set them as direct input devices (Something I have had to do in other space games to get them to detect) they just don't work at all in game. I can use the inbuilt test in the control panel and see they are connected and working.

I've followed every guide I can find. Tried editing the games registry. I am out of ideas. Anyone able to help at all?
 
I'm using plain Wine (9.22) and a VKB Gladiator. Works like a charm as a DInput device with both the
[X] Enable SDL
[X] Disable hidraw
boxes ticked in "wine control joy.cpl".
(Sorry, not familiar with Proton/Lutris, but maybe it helps.)
 
I've had issues with older Proton versions not passing controllers through to the game. Using a different Proton version fixed that.

I use a Winwing Ursa Minor on the right hand and a 3DConnexion SpacePilot Pro on the left.

Another way would be to abstract the devices before they are passed through to the client. I use Input Remapper for this. It has the benefit of being able to map the buttons on the controller to key presses as well as Xinput button presses, which sidesteps the issue you describe. You can also set deadzones and there is a simple response curve setting, which might be useful if you find the stick to be twitchy near the centre. I'm still tweaking mine.

One drawback to Input Remapper is that by default it only spawns one virtual gamepad, so you have to edit the source code to have it spawn additional ones. I find it works fine this way on Kubuntu 24.10/Wayland/GE-Proton9-22, passing through all the analogue axes and remapped buttons to the game.

I really wish there was a Linux app as rounded as Joystick Gremlin for input devices - it would solve so many problems.
 
Last edited:
I've also seen the following suggestion for a launch argument in Steam:

Code:
PROTON_ENABLE_HIDRAW=0x3344/0x01f8,0x3344/0x025b,0x03eb/0x2054,0x03eb/0x2046 SDL_JOYSTICK_HIDAPI=0 gamemoderun %command% /autorun /edo
 
AFAIK: Virpil devices require an initial configuration before they are fully functional. Their configuration software only works on Windows. I've never tried mine under Linux (haven't had time).

Edit: I found a post on Reddit about it: Source: https://www.reddit.com/r/hotas/comments/cs275l/vkb_joystick_and_virpil_throttle_on_linux/


Unfortunately, it doesn't look like Virpil software will work under Wine: https://appdb.winehq.org/objectManager.php?sClass=version&iId=40672&iTestingId=112046

Maybe run Windows under a temporary VM, just long enough to configure it?
 
Last edited:
For what it's worth, I tried to pass my joystick directly to the game instead of remapping it to a couple of Xinput virtual controllers and ran into exactly the same issue as the OP. Proton seems to have issues with Dinput devices that don't have drivers in the kernel.

Virtualising the stick and mapping it to a couple of Xinput gamepads is the only way I've been able to get it to work. This also introduces a mysterious deadzone, whose source I'm having trouble pinpointing - the Game Controller app in KDE doesn't show it for either the physical or virtual device; control joy.cpl in Proton doesn't show it. The game has no deadzone set. Yet still there's a deadzone.
 
^^ this :) I run EndeavourOS and play using a couple of Thrustmaster 1600s and launch ED(O) using Proton Experimental.

Kernel support for those is fine, all the buttons, axis and hats map through to ED, and show up in joy.cpl.
And yes, the deadzone on Linux (evidenced by tools like jstest-gtk) completely vanishes when moving to WINE/Proton. I need to go through WINE 10's release notes and see if that is a thing now, though.
For me, the deadzone is in Wine/Proton somewhere, although no deadzone shows at any layer: Game Controller in Plasma 6.3, jstest-GTK, control joy.cpl, but there's definitely a deadzone in the game, despite there not being a deadzone set. It's weird.
For driver support, you can try different kernels. If you have a joystick that is only programmable by direct interaction with the USB device, you might be out of luck.
It's not quite in Virpil territory - at heart, it's a standard 3-axis Dinput joystick with Hall effect sensors. The Hall effect Z axis is what sold me on it. The plethora of hats (4!) is an added bonus. With my left-hand device, I don't need to touch the base buttons. Input remapper allows me to map the hats to keypresses as needed. The Windows software for the stick is functional, although the UI was clearly designed by engineers. The axis mapping feature is a basic Vjoy instantiation. Joystick Gremlin is superior.
 
For anyone searching deadzone issues on joysticks in Linux, I fixed my issue by examining the joystick in evdev. I ran evtest to find the event ID of the stick:

Code:
$ evtest
No device specified, trying to scan all of /dev/input/event*
Not running as root, no devices may be available.
Available devices:
/dev/input/event10:     Razer Razer Tartarus V2 Keyboard
/dev/input/event11:     Razer Razer Tartarus V2
/dev/input/event12:     Razer Razer Tartarus V2
/dev/input/event13:     Winwing WINWING URSA MINOR FIGHTER FLIGHT STICK R
/dev/input/event14:     Logitech G604
/dev/input/event15:     8BitDo Pro 2 Wired Controller
/dev/input/event2:      Smart JC-U3912T
/dev/input/event27:     input-remapper gamepad2
/dev/input/event30:     input-remapper gamepad
/dev/input/event4:      MSI MYSTIC LIGHT
/dev/input/event9:      Razer Razer Tartarus V2
Select the device event number [0-30]: 13

On my system, this revealed that the stick was defaulting to an unacceptably wide deadzone:
Code:
 Event code 0 (ABS_X)
      Value  32768
      Min        0
      Max    65535
      Fuzz     255
      Flat    4095
    Event code 1 (ABS_Y)
      Value  32768
      Min        0
      Max    65535
      Fuzz     255
      Flat    4095
    Event code 2 (ABS_Z)
      Value  32768
      Min        0
      Max    65535
      Fuzz     255
      Flat    4095

The sensors on the stick are really excellent, so neither the Flat (deadzone) nor the Fuzz (event filtering) values were necessary. These values can be manipulated through the evdev-joystick command:

Code:
sudo evdev-joystick --e /dev/input/event13 --f 0 --d 0

I'm still working out how to have this apply on reboot, as the event IDs can - and do - change. It's very quick to do this process anyway - I just need the event ID. My joystick is now 100% more responsive in and near the centre, which makes FA-off much less of a pain.
 
Last edited:
I have updated my solution to the deadzone issues by creating a udev rule that points to a bash script. This applies my deadzone and noise filtering settings on boot or when the joystick is plugged in. It combines the rule from this site with a (very helpful!) suggestion from ChatGPT to have the rule trigger a script that invokes evdev-joystick, rather than calling evdev-joystick directly (which didn't work). I've put the code in spoilers to avoid a wall of text.
Create a file in the text editor of your choice and enter:
Code:
ACTION=="add", SUBSYSTEM=="input", ENV{ID_VENDOR_ID}=="4098", ENV{ID_MODEL_ID}=="bc2a", RUN+="/usr/local/bin/joystick-init.sh"
Save this file as 99-joystick.rules in the /etc/udev/rules.d folder
Notes:
  • You will need to replace the VENDOR ID and MODEL ID strings with the values reported by your device. VID 4098 = Winwing. PID bc2a = Ursa Minor Fighter. You can identify these values for your device using lsusb.
  • The script name (in this case, joystick-init.sh) and location must match.
BetaToaster's suggestion above to use the symlink in /dev/event/by-id simplifies the joystick-init script as this filename never changes. This allows the script to work without needing to identify the stick's eventID, meaning it just works when you attach the stick.
Bash:
#!/bin/bash

# Set this to your joystick's symlink name (no path)
JOYSTICK_SYMLINK="usb-Winwing_WINWING_URSA_MINOR_FIGHTER_FLIGHT_STICK_R_DCE860721456622163E650B2-event-joystick"

DEVICE="/dev/input/by-id/$JOYSTICK_SYMLINK"
LOGFILE="/tmp/joystick.log"

{
  echo
  echo "=== JOYSTICK INIT (by symlink) ==="
  echo "Timestamp: $(date)"
  echo "DEVICE: $DEVICE"
  echo "Whoami: $(whoami)"

  if [ -e "$DEVICE" ]; then
    echo "Device found. Applying settings..."
    /usr/bin/evdev-joystick --e "$DEVICE" --d 0 --f 0
    echo "evdev-joystick exit code: $?"
  else
    echo "Device not found at $DEVICE"
  fi
  echo "==============================="
} >> "$LOGFILE" 2>&1
Notes:
  • save the script in /usr/local/bin/
  • You can name the script as you prefer, but it must match the filename specified in the udev rule.
  • I don't think this will work permanently on an immutable distro (e.g. Bazzite, SteamOS) as the files will be deleted on an update.
Credits:
  1. BetaToaster for the idea to use the by-id symlinks
  2. mitu on the RetroPie forums thread linked above
  3. ChatGPT (and all the people whose work was hoovered up in its training) for the idea to use a Bash script and for helping me to write it
If anyone knows a more efficient way to achieve the same outcome - please let me know. I'm new to this and want to learn.
 
Last edited:
Bash:
#!/bin/bash
{
  ...
} >> "$LOGFILE" 2>&1
🚯Minor nit: Logging to a file in /tmp is littering. (And it used to be a security issue until Linux grew extra checks to prevent exploitation.)
🚮You should be logging to syslog instead:
Code:
{
  ...
} 2>&1 | logger --skip-empty

If anyone knows a more efficient way to achieve the same outcome - please let me know. I'm new to this and want to learn.
No, that's how you do it.
 
🚯Minor nit: Logging to a file in /tmp is littering. (And it used to be a security issue until Linux grew extra checks to prevent exploitation.)
🚮You should be logging to syslog instead:
Code:
{
  ...
} 2>&1 | logger --skip-empty
Oof! I am scrupulously tidy in public - I really hate littering and cannot abide people who just dump their trolleys at the supermarket exit.

Suitably chastened, I have changed it (it was just for troubleshooting when I was still using evdev-by-event and the script was more complex, in my defence!)
No, that's how you do it.
That's good to know :)
 
Back
Top Bottom