Recently I solved an issue for setting up my Wireless (via Bluetooth) controller on Steam for Linux, and decided to elaborate a little better about my experience, and share my results and observations with you all.
The controller I have used as a reference for the procedures here below is the ASUS Gamepad TV500BG, whose specs are detailed here. Alternatively, I have verified that the same procedures apply with any other Bluetooth device.
The important thing I would like to highlight is that my approach was to find a procedure that could work in several scenarios, such as for both native games (with or without Steam) and games running via Wine or Proton/Steam Play.
According to the approach mentioned above, I discarded immediately the use of the x360ce driver, which it is largely used for games running on Wine but it represents a limited solution from an architectural point of view.
Using the Userspace driver xboxdrv
The xboxdrv driver is the Xbox/360 gamepad driver for Linux running on userspace. It is widely considered as a replacement of the linux kernel module xpad, and shows significant advantages on handling several types of devices, other than the Xbox-like gamepads.
The additional capability provided by the xboxdrv driver is to remap any of your controller button or axis, and tweak the default configuration for adding new functionalities to it. By using xboxdrv, the device will be recognised then by Steam as an Xbox controller.
The only inconvenience I noticed when using xboxdrv was that the driver was able to lock and consume the hardware resource just exactly 60 seconds after launching the xboxdrv executable from the command line. This is a very well known issue tracked in this bug. You'll also see that there's a workaround noted.
I started with a udev rule for linking the input/event* device of the hardware controller in an arbitrarily named symlink (so that it would be easier to identify). The device has few attributes that make easy to recognise it. They can be read by typing the following statement in a terminal emulator:
$ udevadm info -a /sys/class/input/event*
where you have to replace the * above with the device ID (the highest number you find as soon as you turn the device on might be the right one). The output might look similar to the following:
looking at device '/devices/pci0000:40/0000:40:07.1/0000:42:00.3/usb7/7-1/7-1:1.0/bluetooth/hci0/hci0:11/0005:0B05:4500.000B/input/input30/event26': KERNEL=="event26" SUBSYSTEM=="input" DRIVER=="" looking at parent device '/devices/pci0000:40/0000:40:07.1/0000:42:00.3/usb7/7-1/7-1:1.0/bluetooth/hci0/hci0:11/0005:0B05:4500.000B/input/input30': KERNELS=="input30" SUBSYSTEMS=="input" DRIVERS=="" ATTRS{name}=="ASUS Gamepad" ATTRS{phys}=="5c:f3:70:8f:66:37" ATTRS{properties}=="0" ATTRS{uniq}=="38:2c:4a:8c:2e:87"
The lines above are telling that the device currently identified as "event26" has an attribute name ("ASUS Gamepad") which we can use to easily identify it in the udev rules. We can then open the user-defined rules file, as shown here below:
$ sudo nano /etc/udev/rules.d/75-input-events.rules
and add the following line to it:
KERNEL=="event*" , SUBSYSTEM=="input", MODE="0666" KERNEL=="event*", ATTRS{name}=="ASUS Gamepad", SYMLINK+="input/event-asus-gamepad"
By specifying the last keyword in the rule, the system will automatically create the symlink /dev/input/event-asus-gamepad (named like that in a totally arbitrary way) pointing to the controller device any time it is turned on, regardless of the device current ID in the current session.
Now that we have a way for identifying the controller event device, we can restart the udev rules with the following command:
$ sudo udevadm control --reload-rules && sudo udevadm trigger
and then call the xboxdrv driver with this command:
$ xboxdrv --evdev "/dev/input/event-asus-gamepad" --config xboxdrv.config --debug
The --evdev option enables xboxdrv to read any input coming from the event-asus-gamepad device and convert it into an Xbox controller input. The Xbox-converted input signals are sent by a different char device, which is specified in the command output:
Your Xbox/Xbox360 controller should now be available as: /dev/input/js1 /dev/input/event27
The new devices js1 and event27 above replace the old one, and will be automatically recognised by Steam.
The --config option defines a configuration file where I've specified all the mapping between the Asus Gamepad controller axes and buttons into the conventional Xbox ones:
[xboxdrv] detach-kernel-driver=true silent=true [axismap] -Y1 = Y1 -Y2 = Y2 [evdev-absmap] ABS_X=x1 ABS_Y=y1 ABS_Z=x2 ABS_RZ=y2 ABS_BRAKE=lt ABS_GAS=rt ABS_HAT0X=dpad_x ABS_HAT0Y=dpad_y [evdev-keymap] BTN_SOUTH=a BTN_EAST=b BTN_NORTH=x BTN_WEST=y BTN_TL=lb BTN_TR=rb BTN_THUMBL=tl BTN_THUMBR=tr KEY_BACK=back KEY_HOMEPAGE=start BTN_MODE=guide
The content of the configuration above above might vary according to your local controller.
I can then launch Steam with the following script:
#!/bin/sh if [ -h /dev/input/event-asus-gamepad ] then # remove any previous instance: killall -KILL xboxdrv # start the new driver in userspace mode: xboxdrv --evdev "/dev/input/event-asus-gamepad" \ --config xboxdrv.config & fi /usr/bin/steam %U
In the same way, we could define the additional logic for giving an arbitrary event symlink to the xboxdrv controller device as soon as the system loads the driver. We can maintain again the user-defined rules file, as shown here below:
$ sudo nano /etc/udev/rules.d/75-input-events.rules
and add the following new line at the bottom:
KERNEL=="event*", ATTRS{name}=="Xbox Gamepad (userspace driver)", SYMLINK+="input/event-xboxdrv-gamepad"
With the definition above, the system will automatically create the symlink /dev/input/event-xboxdrv-gamepad regardless of the device current ID in the current user session.
Some people might try to run the xboxdrv driver as a daemon (with the options: --daemon --detach). Unfortunately I didn't get a working controller when I tried to do it.
Alternatively, you could try to start xboxdrv as soon as you turn on (or plug) your controller. You can implement it by adding a RUN keyword on the udev rule above (please refer to the udev documentation for further information on it).
Although this approach looks more direct, I prefer to follow the recommendation to run the driver at the same moment you run your game. This can be particularly useful if you need a different keys mapping for each game.
Some additional consideration would apply according to the specific game category.
Games running natively on Linux
Some best examples of very well-known games belonging to this category are Bioshock Infinite, Tomb Raider (2013) and Borderlands 2 (but this is just a personal opinion).
You have nothing to do in this case. Some specific games might require a value for the SDL environment variable SDL_JOYSTICK_DEVICE as shown in the example here below:
SDL_JOYSTICK_DEVICE=/dev/input/event-xboxdrv-gamepad
Although Steam will automatically identify the virtual Xbox Controller for most of the games.
Additionally, you can configure Steam itself with the Xbox Controller support (configuration is available under the menu: Steam --> Settings --> Controller --> General Controller Settings) as shown in the picture here below. This will enable you to use the controller as a replacement for the mouse pointer in Steam:
Hopefully some of you find this useful.
You are my god.
I collect gamepads that do not come from the three biggest console brands (Xbox, PlayStation or Switch). Connecting some via Bluetooth is fresh hell.
I am sure I will come back to your article multiple times. Great job ^_^
View cookie preferences.
Accept & Show Accept All & Don't show this again Direct Link
View cookie preferences.
Accept & Show Accept All & Don't show this again Direct Link
View cookie preferences.
Accept & Show Accept All & Don't show this again Direct Link
I gave up AmigaOS for Windows over 20 years ago now. Years before that, I couldn't fathom why anyone would willingly use a Windows PC over an Amiga. In retrospect, it's obvious that a desktop computing platform with a non-X86 arch with proprietary bus interfaces, was doomed. Apple somehow made the transition & F'ed over their user base (TWICE 68K ->PPC -->X86), but still retained enough to be an option in the current day. Sadly C= did not (Sorry for the anti-Apple rant but ^%#(@*$#^#).:-)
Since coming to Linux a little over a year ago, I feel the feeling I haven't felt since my Amiga days. However, this time I'm on an underdog that has "open" access to all the CPU and GFX power the industry has to offer, as well as access to every bus interface under the sun.
I hope there comes a day when "JUST WORKS" is a thing with Linux >90% of the time, the way it is with Windows. Until then, we can edit our startup-sequence with appropriate commands to run Enforcer and Segtracker, as well as running SnoopDOS, to help the devs "MAKE IT RIGHT" whenever we can. (Shout out to Amigans!)
Since I got the Steam controller I never looked back.
Last edited by theghost on 25 January 2019 at 6:44 am UTC
There are lots of wireless controllers described here and probably working. I personally use Steam Controller, there is an open source driver available if someone prefers open source to Steam provided driver.
I can actually confirm that was the first thing I tried. It has worked correctly for the games running natively on Linux, but not for those games running on Wine or Steam Play, in particular for those which require the support of either the Microsoft DirectInput or the Xinput API.
For the latter in particular the mapping was always wrong: axes mapped as triggers and vice versa, and changing the controller configuration in Steam hasn't ever worked. The advantage I found with the xboxdrv layer is that remaps the device in a new one, easier to be supported by the in-game Xinput API.
I was going to write a follow-up to the article today that explains what to do for games which support DirectInput or Xinput. I'll keep you all posted of course.
You should get a 8bitdo controller. They are great. 8bitdo.com
Have you already tried with the following udev rule?
KERNEL=="event*", ATTRS{name}=="Your Gamepad Name", SYMLINK+="input/event-gamepad-%n"
The outcome should be a symlink that looks like /dev/input/event-gamepad-* with the kernel number of the attached device.
Last edited by LordDaveTheKind on 25 January 2019 at 12:46 pm UTC