The problem#
The Xbox 360 controller has become the defacto standard controller in PC gaming in recent years, likely due to both the popularity of the Xbox and the fact that the controller can easily be used with a computer. One downside of this is that some games assume you have one. If the game supports it and is running through Steam, then Steam's controller settings will let you use any controller, but that doesn't work for all games, and you might not be using Steam. The game that prompted this blog post actually does have Steam controller support promised in the future, but it's in early access and they are busy developing other parts of the game.1
xboxdrv
#
The solution is xboxdrv
, the userspace
Xbox controller driver. In addition to supporting actual Xbox
controllers, it can also simulate Xbox controllers based on inputs from
other devices like a PlayStation controller or some less
common controller.
Just do something like
#!/bin/sh
echo -n "Press a button on the PSOne controller... "
evdev="$($(dirname "$0")/identify_evdev.py)"
echo using device "$evdev".
xboxdrv --evdev "$evdev" \
--evdev-absmap ABS_X=x1,ABS_Y=y1,ABS_RZ=x2,ABS_Z=y2 \
--axismap -Y1=Y1,Y2=X2,-X2=Y2 \
--evdev-keymap BTN_TOP=x,BTN_TRIGGER=y,BTN_THUMB2=a,BTN_THUMB=b,BTN_BASE3=back,BTN_BASE4=start,BTN_BASE=lb,BTN_BASE2=rb,BTN_TOP2=lt,BTN_PINKIE=rt,BTN_BASE5=tl,BTN_BASE6=tr,BTN_DEAD=dl,KEY_#300=du,KEY_#301=dr,KEY_#302=dd \
--mimic-xpad --silent --quiet
and you're done. Easy, right?
Well, no. The first few lines are a pretty straightforward use of identify_evdev.py to discover the right device to use. But the big lists of axis and button mappings are not at all obvious and depend on your actual controller setup. So I made a script for generating them.
Generating button maps#
create_xboxdrv_evdev_map.py
generates that xboxdrv
command by listing the Xbox buttons and axes and asking the user to
touch the corresponding button or axis on their controller, just
like you would expect in any input configuration screen. The code is
implemented in Python using the evdev
library and is well
commented.
To use it, just run the command and follow the directions. When done,
the final line output will be an invocation of xboxdrv
that you
can save and run any time you want to use that controller:
$ git clone https://github.com/dperelman/gamepad-util.git
$ gamepad-util/create_xboxdrv_evdev_map.py
Press any button on only the joystick you are setting up.
Selected event device: /dev/input/event22
Stop pressing any buttons.
Press the corresponding button on your controller. If the button doesn't exist, press the start button again to ignore it.
Press start: BTN_START
Press back (or select): BTN_SELECT
Press guide (large center button): (none)
...
Press trigger lt (left analog trigger (L or L2 button)) all the way: ABS_Z
Press trigger rt (right analog trigger (R or R2 button)) all the way: ABS_RZ
xboxdrv --evdev "/dev/input/event22" --evdev-keymap "BTN_A=a,BTN_B=b,BTN_TL=lb,BTN_THUMBR=tr,BTN_SELECT=back,BTN_START=start,BTN_THUMBL=tl,BTN_TR=rb,BTN_WEST=y,BTN_NORTH=x" --evdev-absmap "ABS_RZ=rt,ABS_RY=y2,ABS_RX=x2,ABS_Z=lt,ABS_Y=y1,ABS_X=x1" --axismap "-y2=y2,-y1=y1" --mimic-xpad --silent
y-axis confusion#
One of the most confusing parts of figuring out how to generate these
mappings was the y-axis. jstest
was showing one thing
and the debug output from xboxdrv
was showing a different value,
which made no sense:
$ sudo xboxdrv --detach-kernel-driver --mimic-xpad
...
X1: -2288 Y1:-32768 X2: -2879 Y2: 887 du:0 dd:0 dl:0 dr:0 back:0 guide:0 start:0 TL:0 TR:0 A:0 B:0 X:0 Y:0 LB:0 RB:0 LT: 0 RT: 0
$ jstest /dev
...
Axes: 0: -2178 1: 32767 2:-32767 3: 0 4: 0 5:-32767 6: 0 7: 0 Buttons: 0:off 1:off 2:off 3:off 4:off 5:off 6:off 7:off 8:off 9:off 10:off
The differences between the X1
on xboxdrv
and axis 0
on
jstest
can be explained by them simply printing at different times
as the joystick was still moving. On the other hand, xboxdrv
shows
Y1
as -32768 while jstest
shows axis 1
as 32767. Even though
the two clearly change together, they are always opposite.
Eventually I solved the mystery by delving into the source code of
xboxdrv
: it inverts the y-axis just before emitting
it to uinput, but after it prints the value to the screen. Which is why
create_xboxdrv_evdev_map.py
asks for the y-axis in reverse order: as a
simple way of inverting it back.
Non-standard key names#
Some of the buttons mapped have names, albeit weird ones like BTN_DEAD
while others just have numbers like KEY_#300
. The names are listed in
input.h
in the Linux kernel, and there are some numbers
that lack corresponding names like 300 which is 0x12c
in hexadecimal.
xboxdrv
works around that by supporting the KEY_#300
format so it can reference unnamed buttons.
--mimic-xpad
#
There is a kernel driver for Xbox controllers called xpad
which has different default mappings. All of the xboxdrv
invocations in this blog post use --mimic-xpad
to make xboxdrv
act like xpad
which some games expect. When using xboxdrv
with an actual Xbox controller, the command to run is
$ sudo xboxdrv --silent --detach-kernel-driver --mimic-xpad
--silent
disables debugging output and --detach-kernel-driver
makes
sure xpad
isn't also handling the controller.
-
The game is Assault Android Cactus, which I highly recommend. It is a twin-stick shooter with some bullet hell elements and support for up to 4 player co-op. ↩
Comments
Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.
There are no comments yet.