Running OpenSD
OpenSD is intended to be run as a user-level service, but it can be run from a terminal like most other Linux programs. This can be useful for troubleshooting or debugging purposes since every message is logged to stdout.
OpenSD requires access to the uinput and hidraw subsystems. To enable this access, OpenSD provides a udev rule which will be installed when installing OpenSD. In this rule, access is allowed to both subsystem for members of the opensd group, which is also created on install. Any user that intends to run OpenSD must be a member of this group, or otherwise have R/W access to these device nodes. After installing for the first time it will be necessary to log out in order for the user’s group changes to take effect.
For more information see the Troubleshooting section.
Warning
|
Do NOT run the OpenSD daemon as root, either directly or as a system service. |
From a terminal
$ opensdd
Will start an instance of the daemon and log all output to the terminal. The default log level is WARN
.
Or to see debug messages run:
$ opensdd -l debug
A full list of command line options can be shown using:
$ opensdd --help
As a User-Level Service
For systemd users, a service file is provided which can be run as a user-level service that will automatically start on log-in.
To run the OpenSD daemon as a service on user log-in:
$ systemctl --user enable opensd
$ systemctl --user start opensd
Configuration Files
OpenSD uses two types of configuration files. The first type being the Daemon Configuration, and the second type are gamepad Profiles which are loaded by the daemon.
All files use an ini-style format and are easily created or modified with any text editor.
Since OpenSD is a userspace daemon, configuration files are stored in $XDG_CONFIG_HOME/opensd/
which, on most systems, will be ~/.config/opensd/
. Gamepad profiles are stored in the profile
subdirectory, (example: ~/.config/opensd/profiles/
). If the OpenSD user config directory does not exist, a new one will be created and populated with the default configuration files when the daemon is run.
Note
|
OpenSD follows the XDG specification, so environments which set / change $XDG_HOME or $XDG_CONFIG_HOME should behave correctly. For the purposes of this documentation we’ll refer to the default XDG paths for user configuration files (example: ~/.config/opensd/ ).
|
-
Parsing is line-based and space delimited.
-
The files are broken into braced enclosed sections and keys.
-
Names of sections and keys are not case-sensitive, but values can be, such as file names.
-
Sections are enclosed in square brackets (example:
[SectionName]
) -
Keys must be contained within a section.
-
Key assignment must be separated by whitespace. (example:
key = value1 value2 value3
) -
Comments are preceeded by a
#
or;
at the beginning of the line. -
Placing a value in quotes
"
treats the value as a literal string which allows whitespace. (example:key = "A string with spaces"
)
Daemon Configuration File (config.ini)
This single file contains a small group of options that pertain to the daemon itself. The default configuration file for the daemon can be found at ~/.config/opensd/config.ini
. An unmodified copy of this file will be installed to /usr/local/share/opensd/config/config.ini
or /usr/share/opensd/config/config.ini
.
An example config.ini can be found in this documentation if you want to see what a complete file might look like.
[Daemon] Section
Note
|
This section is required. |
Profile
This tells the daemon which profile to load when it starts up. Profiles are only loaded from the ~./config/opensd/profiles/
directory, so only specify the filename here. OpenSD also has other methods for loading profiles once the daemon is running, this setting is only for the default profile you wish to use.
Format:
Profile = <filename>
Example:
[Daemon]
Profile = default.profile
AllowClients
Note
|
This feature is not yet fully implemented. |
This setting enables or disables the use of the CLI and GUI utilities which connect to the daemon. If set to false, the daemon will not listen for clients. This can be useful if you want to "lock down" a configuration so it cannot be changed while it is running. The default is true
.
Format:
AllowClients = <true | false>
Example:
[Daemon]
AllowClients = true
Profile Configuration Files (*.profile)
These files are used to configure the gamepad driver features and bindings. A default profile is configured in the config.ini file to be loaded at startup, but you can also switch between them at any time while the daemon is running using any of several possible methods.
Gamepad profiles can be found in ~/.config/opensd/profiles/
. The file extension is *.profile
.
An example profile can be found in this documentation if you want to see what a complete file might look like.
A default profile (cleverly named default.profile
) is provided which includes documentation in the comments on how to configure it. It is not recommended to modify this file, instead you should make a copy of it, renaming it to whatever_you_want.profile
and edit that file instead. If the default profile is deleted, it will be recreated when the daemon is run, so if you need a clean or updated copy of the default.profile
, simply delete it and restart the OpenSD daemon.
An unmodified copy of this file will be installed to /usr/local/share/opensd/profiles/default.profile
or /usr/share/opensd/profiles/default.profile
.
[Profile] Section
Name
The profile name as it will appear in the GUI and through the CLI query. Should be unique for each profile to avoid confusion.
Format:
Name = <profile_name>
-
profile_name
: Any unique name. Should be enclosed in quotes "" to preserve spaces.
Example:
[Profile]
Name = "My favourite gamepad profile"
Note
|
Prior to version 0.47, quoted literal strings were not supported. It’s recommended, but not necessary to update old profiles to put this value in quotes. |
Description
A breif description of the profile for use in the GUI and CLI query. Does not affect anything else, just intended as a hint for users.
Format:
Description = <description>
-
description
: A brief, helpful description of what your profile does. Should be enclosed in quotes "" to preserve spaces.
Example:
[Profile]
Description = "Just a profile I use for most applications."
Note
|
Prior to version 0.47, quoted literal strings were not supported. It’s recommended, but not necessary to update old profiles to put this value in quotes. |
[Features] Section
ForceFeedback
Enable or disables haptic / force-feedback events for the gamepad device. It’s worth mentioning that only the Gamepad
device can receive force-feedback events; the Motion
or Mouse
devices will not receive these events.
Format:
ForceFeedback = <true | false>
Example:
[Features]
ForceFeedback = true
If unspecified, this value defaults to false
.
Note
|
This feature is not yet fully implemented |
MotionDevice
If this is set to true
, an additional input device will be created which will report motion control data. Motion axes need to have thier ranges and bindings defined. If this is disabled, any Motion
device bindings will be ignored.
Format:
MotionDevice = <true | false>
Example:
[Features]
MotionDevice = true
If unspecified, this value defaults to false
.
Note
|
While it’s possible to combine gamepad and motion input into a single input device, Linux kernel and uinput specifications state that motion control devices should be separate from other gamepad / joystick input. Not separating these can also make it difficult to configure controls in most applications. |
MouseDevice
If this is set to true
, an additional input device will be created which will be used to send mouse / trackpad events. Mouse events still need to have thier bindings defined. If this is disabled, any Mouse
device bindings will be ignored.
Format:
MouseDevice = <true | false>
Example:
[Features]
MouseDevice = true
If unspecified, this value defaults to true
.
LizardMode
The Steam Controller and the Steam Deck both have a kind of fallback "BIOS" mode which emulates some keyboard and mouse input using the gamepad. Valve refers to this as "Lizard Mode". This mode cannot be redefined. It sends events IN ADDITION to the gamepad events created by the OpenSD driver, so it should always be disabled. If you would like mouse or keyboard emulation, they should be configured with bindings. When OpenSD exits, Lizard Mode is re-enabled.
If this is set to false
"Lizard Mode" will be disabled (recommended).
Format:
LizardMode = <true | false>
Example:
[Features]
LizardMode = false
If unspecified, this value defaults to false
.
StickFiltering
The thumbsticks on the Steam Deck have a circular range but return square-ish data, which makes it feel odd and complicated to apply radial deadzones to. Because of this, OpenSD vectorizes the stick position and returns "cleaner", round stick ranges, as well as being able to create clean deadzone rescaling. If you disable this setting, axis ranges are still internally normalized and rescaled to the the uinput device, but no vectorization will be applied and any deadzones will be ignored.
If set to true
thumbsticks will be filtered (recommended).
Format:
StickFiltering = <true | false>
Example:
[Features]
StickFiltering = true
If unspecified this value defaults to true
.
Note
|
This must be enabled for thumbstick deadzones to work. |
TrackpadFiltering
Similar to StickFiltering, but matches the square shape of the trackpad. Filtering is only applied to absolute values. This setting must be enabled to apply deadzones to the trackpad absolute axes. Relative values (*PadRelX
and *PadRelY
) are unaffected, therefore deadzones do not affect mouse movement with the pads.
If set to true
trackpads will be filtered (recommended).
Format:
TrackpadFiltering = <true | false>
Example:
[Features]
TrackpadFiltering = true
If unspecified, this value defaults to true
.
Note
|
This must be enabled for trackpad deadzones to work. |
[DeviceInfo] Section
This section is used to optionally configure the USB identification of each device OpenSD creates. While OpenSD has its own defaults, they can be overridden to make these devices appear as a different hardware. This can be useful when some very poorly written games only look for / support specific gamepads, rather than a universal API. Shame. Often, these games filter out all input devices which don’t match the device name string and/or the USB VID:PID. These settings enable the gamepad to mimic the appearance of a device which the software does recognise.
Format:
<device> = <vid> <pid> <ver> <name>
-
device
: Any OpenSD input device:Gamepad
,Motion
, orMouse
. -
vid
: The USB device VendorID to use. Format must be in hexidecimal. -
pid
: The USB device ProductID to use. Format must be in hexidecimal. -
ver
: The USB device version to use. Format must be in hexidecimal. -
name
: The USB device name string to use. Should be enclosed in quotes "" if it contains any whitespace characters.
Example:
[DeviceInfo] Gamepad = 0xDEAD 0xBEEF 0x0001 "OpenSD Gamepad Device" Motion = 0xDEAD 0xBEEF 0x0001 "OpenSD Motion Control Device" Mouse = 0xC475 0xF00D 0x0101 "OpenSD Mouse / Trackpad Device"
Note
|
Some software may also perform gamepad detection based on button and axis configuration. Axis settings can also be configured in the [GamepadAxes] Section and [MotionAxes] Section for this very purpose. |
[Deadzones] Section
These values are double precision floating point and represent the percentage of the total axis range to ignore. A value of 0.05 would be a 5% deadzone. Deadzones are capped at 0.9 (90%). A value of 0 is considered disabled. If StickFiltering is disabled, LStick
and RStick
deadzones will be ignored. If TrackpadFiltering is disabled, LPad
and RPad
deadzones will be ignored.
Format:
<axis> = <value>
-
axis
: Any of the supported gamepad axes, which are: *LStick
,RStick
,LPad
,RPad
,LTrigg
andRTrigg
. -
value
: A double-precision floating point value between 0 and 0.9.
Example:
[Deadzones]
LStick = 0.1
RStick = 0.1
LPad = 0
RPad = 0
LTrigg = 0
RTrigg = 0
Any undefined axis deadzone will default to 0
(disabled).
Note
|
Because the Steam Deck thumbsticks tend not to return to center completely (at least on current revisions), a small deadzone of around 0.10 (10%) is generally recommended. |
[GamepadAxes] Section
Gamepad absolute axes must have a defined range or they will not be created. Any Gamepad
ABS_*
events which are configured in the Gamepad Bindings section must be defined here first, or they will be ignored.
Internally, the axis values are normalized and rescaled between the actual hardware and the value seen by applications, so no clipping or "dead extremes" will occur. There is no "right" or "wrong" value here that you need to know, but it may be useful to precisely emulate other hardware so it can be detected as such by certain applications which try to guess what kind of device you have.
The Steam Deck hardware uses signed 16-bit integers (-32767 to 32767) for its thumbstick, trackpad, trigger and motion axes, so there’s no reason to use a larger or smaller range for those inputs, unless you are trying to emulate a specific device.
Hat-type axes (ABS_HAT*
) should typically use a range of -1
to 1
because of thier historical purpose, but this is not strictly enforced.
Triggers should typically have minumum value of 0
so that they rest at zero when released.
Format:
<abs_event> = <min> <max> [fuzz] [res]
-
abs_event
: Any absolute axis event code you wish to bind. Absolute event codes begin withABS_
. A full list of input event codes can be found at linux/input-event-codes.h from the Linux kernel. -
min
: A value representing the minimum range of the axis. This is a signed 32-bit integer. -
max
: A value representing the maximum range of the axis. This is a signed 32-bit integer. -
fuzz
: Optional value representing the fuzziness of the axis values. This is a signed 32-bit integer. -
res
: Optional value representing the resolution of the axis in units/mm (units/radian for rotational axes). This is a signed 32-bit integer.
Example:
[GamepadAxes]
ABS_HAT0X = -1 1
ABS_HAT0Y = -1 1
ABS_X = -32767 32767
ABS_Y = -32767 32767
ABS_RX = -32767 32767
ABS_RY = -32767 32767
ABS_Z = 0 32767
ABS_RZ = 0 32767
[MotionAxes] Section
Motion control absolute axes, as with the gamepad device, must have a defined range or they will not be created. Any Motion
ABS_*
events which are configured in the Motion Bindings must be defined here first, or they will be ignored.
Internally, the axis values are normalized and rescaled between the actual hardware and the value seen by applications, so no clipping or "dead extremes" will occur. There is no "right" or "wrong" value here that you need to know, but it may be useful to precisely emulate other hardware so it can be detected as such by certain applications which try to guess what kind of device you have.
The Steam Deck hardware uses signed 16-bit integers (-32767 to 32767) for its thumbstick, trackpad, trigger and motion axes, so there’s no reason to use a larger or smaller range for those inputs, unless you are trying to emulate a specific device.
Format:
<abs_event> = <min> <max>
-
abs_event
: Any absolute axis event code you wish to bind. Absolute event codes begin withABS_
. A full list of input event codes can be found at linux/input-event-codes.h from the Linux kernel. -
min
: An integer representing the minimum range of the axis. This is a signed 32-bit integer. -
max
: An integer representing the maximum range of the axis. This is a signed 32-bit integer.
Example:
[MotionAxes]
ABS_X = -32767 32767
ABS_Y = -32767 32767
ABS_Z = -32767 32767
ABS_RX = -32767 32767
ABS_RY = -32767 32767
ABS_RZ = -32767 32767
Note
|
Motion controls are not yet fully implemented. |
[Bindings] Section
This should be a list of all the physical gamepad buttons/sticks/pads/motion inputs you want to bind to a virtual input event or command. Anything not specified here will be considered "unbound" and not emit any event.
There are currently four basic binding types: device bindings, Command
bindings, Profile
bindings and None
.
- Device bindings
-
Represent input events which are generated by pressing buttons, keys, moving the mouse, thumbsticks, motion control, etc. Event bindings are tied to specific input devices, which include
Gamepad
,Motion
andMouse
. Applications read events from these different device types in different ways so they should generally be separated. Command
bindings-
Executes a given command inside a shell environment.
Profile
bindings-
Used to switch to a different profile when triggered.
None
-
This is used to indicate that a particular input has no binding. (default)
Input binding names which this document will refer to as input
or <input>, are represent physical buttons, triggers, axes, etc. on the physical gamepad portion of the Steam Deck. They can be broken down into a several categories for simplicity:
- Directional Pad
-
DPad{Up|Down|Left|Right}
- Buttons
-
A
B
X
Y
L1
L2
L3
L4
L5
R1
R2
R3
R4
R5
Menu
Options
Steam
QuickAccess
- Triggers
-
{L|R}Trigg
- Thumbsticks
-
{L|R}Stick{Up|Down|Left|Right|Touch|Force}
- Trackpads
-
{L|R}Pad{Up|Down|Left|Right|RelX|RelY|Touch|Press|Force}
- Accelerometers
-
Accel{X|Y}{Plus|Minus}
- Attitude / gyros
-
{Roll|Pitch|Yaw}{Plus|Minus}
Input names prefixed with L
or R
indicate left and right controls (example: LStickLeft
vs RStickLeft
)
Additionally, trackpads are mapped out into several button layouts simultaneously. This means that when pressed, specific areas of the trackpad behave like individual buttons. There are several "maps" which can be used non-exclusively. These are
- Quadrant button maps
-
{L|R}PadPressQuad{Up|Down|Left|Right}
- Orthogonal button maps
-
{L|R}PadPressOrth{Up|Down|Left|Right}
- 2x2 grid maps
-
{L|R}PadPressGrid2x2_{1|2|3|4}
- 3x3 grid maps
-
{L|R}PadPressGrid3x3_{1|2|3|4|5|6|7|8|9}
A full list of available input codes can be seen in the example profile section, as well as in default.profile
file from your installation.
A detailed explanation of each of these inputs can be found in the Input Types section.
Gamepad Bindings
The Gamepad
device binding is used to generate input events for a joystick / gamepad-type device. This generally means buttons (BTN_*
) and absolute axis (ABS_*
) events. KEY_*
events are allowed, but many programs will not read KEY_*
events from a joystick device, instead try binding key events to the Mouse
device.
The syntax for bindings differs slightly depending on the event type. Absolute axis (EV_ABS
) events are prefixed with ABS_
and key / button events (EV_KEY
) are prefixed with KEY_
and BTN_
respectively. OpenSD supports most event codes. For a full list of event codes, see linux/input-event-codes.h from the Linux kernel.
When bound to a button-type input (example: the A
button), the bind is triggered when the button is pressed. When bound to an axis-type input (example: LStickUp
), the event is emitted when the axis is in a non-zero position and leaves the deadzone (if any).
For KEY / BTN events:
Format:
input = Gamepad <event_code>
-
input
: Any one of the input binding names. -
event_code
: Any EV_KEY type event. These events are prefixed withBTN_
orKEY_
. (example:BTN_START
orKEY_ESCAPE
)
Example:
[Bindings]
Menu = Gamepad BTN_START
For ABS events:
Format:
input = Gamepad <event_code> <direction>
-
input
: Any one of the input binding names. -
event_code
: AnyEV_ABS
type event. These events are prefixed withABS_
. (example:ABS_X
) -
direction
: Indicates the direction that the axis is moved in. Values may be+
or-
. For centered axes, like thubsticks,-
represents moving the axis up or left, and+
represents moving the axis down or right. For ramped axes, like triggers and pressure sensors,+
represents applying pressure.
When binding a button-type input like a DPad direction or, say, the B
button to an ABS event, the button will push the axis to its maximum extent in the given direction. When binding an analog axis, like a thumbstick, to an ABS value, the range of motion is mapped to the axis value in the given direction.
Examples:
[Bindings]
# Button mapped to an axis
DPadUp = Gamepad ABS_HAT0Y -
# Analogue stick mapped to an axis
RStickUp = Gamepad ABS_Y -
RStickDown = Gamepad ABS_Y +
# Analogue trigger mapped to an axis
LTrigg = Gamepad ABS_Z +
A full list of gamepad input names can be seen in the example profile.
A detailed explanation of inputs can be found in the Input Types section of this documentation.
Note
|
ABS events must have a defined range in the [GamepadAxes] Section |
Motion Bindings
The Motion
device binding is used to generate input events for a motion control-type device. While OpenSD does not strictly enforce this, the Linux kernel and uinput specify that motion control events should be emitted by separate devices. Not doing so can create a lot of "noise", especially when configuring controls within another application. As per this spec, the Motion
device only supports EV_ABS
type events. These events are prefixed with ABS_
(example: ABS_Z
). For a full list of event codes, see linux/input-event-codes.h from the Linux kernel.
The syntax and behaviour for binding Motion
device events is the same as binding ABS events with the Gamepad
device in the previous section.
Also, the Motion
device is a completely separate context and namespace from the Gamepad
and Mouse
devices, much in the same way that two players with identical controllers will have the same buttons, but very different meanings to the game. For example, pressing A
on controller #1 does not affect player #2. Its up the the end-user’s software to decide the the context and meaning of the individual events.
Format:
input = Motion <event_code> <direction>
-
input
: Any one of the input binding names. -
event_code
: AnyEV_ABS
type event. These events are prefixed withABS_
. (example:ABS_X
) -
direction
: Indicates the direction that the axis is moved in. Values may be+
or-
.
Examples:
[Bindings]
# Bind roll attitude to Motion device
RollPlus = Motion ABS_X +
RollMinus = Motion ABS_X -
A full list of motion contol input names can be seen in the example profile.
A detailed explanation of motion input be found in the Input Types Section of this document.
Note
|
ABS events must have a defined range in the [MotionAxes] Section |
Note
|
This feature is not yet fully implemented. |
Mouse Bindings
The Mouse
device binding is used to generate input events which will be interpreted as events coming from a pointer-type device such as a physical mouse. This binding type supports button / key and relative axis events. The Mouse
device can also function a bit like a keyboard, so it’s advised to bind any key events to this device.
The syntax for bindings differs slightly depending on the event type. Relative axis (EV_REL
) events are prefixed with REL_
and key / button events (EV_KEY
) are prefixed with KEY_
and BTN_
respectively. OpenSD supports most event codes. For a full list of event codes, see linux/input-event-codes.h from the Linux kernel.
For KEY / BTN events:
input = Mouse <event_code>
-
input
: Any one of the input binding names. -
event_code
: Any EV_KEY type event. These events are prefixed withBTN_
orKEY_
. (example:BTN_LEFT
orKEY_ESCAPE
)
Example:
[Bindings]
RPadPress = Mouse BTN_LEFT
QuickAccess = Mouse BTN_RIGHT
For REL events:
[Bindings]
RPadRelX = Mouse REL_X
RPadRelY = Mouse REL_Y
-
input
: Any one of the input binding names. -
event_code
: AnyEV_REL
type event. These events are prefixed withREL_
. (example:REL_X
)
Please see the Trackpad input type section of this documentation for a better explanation of how {L|R}PadRel{X|Y}
relative inputs work.
Command Bindings
The Command
binding allows you to execute external programs or scripts by forking them off as a child process. These processes run concurrently, do not return any usable exit code, and will not interrupt the driver.
Format:
input = Command <wait_for_exit> <repeat_delay_ms> <command_to_execute>
-
input
: Any one of the input binding names. Best suited to button-types. -
wait_for_exit
: atrue
orfalse
value which specifies if the command should complete before the binding can be triggered again. -
repeat_delay_ms
: The amount of time in milliseconds that must elapse before the binding can be triggered again. The timer starts when the binding is successfully triggered. -
command_to_execute
: The name of the command / script you want to run, same as you would from a terminal. The command executes normally inside a shell, so variable expansion should work. Arguments may be specfied by placing them after the command as usual.
Example:
[Bindings]
QuickAccess = Command true 0 rofi -show run
Profile Bindings
The Profile
binding type allows you to switch to a different profile using just the gamepad input. Profiles are loaded from the user profile directory. There is a 2 second delay after profile switching before the profile can be changed again, to prevent undesirable rapid cycling. If a profile fails to load, the process will be aborted and the profile currently in use will remain so.
Format:
input = Profile <profile_name>
-
input
: Any one of the input binding names. Best suited to button-types. -
profile_name
: Filename of the profile ini you want to load. Path is fixed to the user profile directory, so only specify the filename itself.
Example:
[Bindings]
L5 = Profile left_hand_mouse.profile
Input Types
As briefly described in the Bindings section, the gamepad has multiple input components which can be categorized by their interface, but also by a primitive type. For example, the thumbsticks on the Steam Deck have a pair of X / Y axes (example: LStickUp
, LStickLeft
), which, as a primitive type are absolute, but the thumbsticks also have a touch sensor at the top which can be read as a binary button primitive (LStickTouch
) as well as a pressure level (LStickForce
) which is read as a single absolute axis like a trigger would be.
The intent is for each input name to be as simple and intuitive as possible, but that’s always going to be pretty subjective. This section intends to provide a painfully detailed explanation for every individual input that can have a binding ; )
Directional Pad
The directional pad is just a set of four buttons which are diametrically exclusive — you can press up and left simultaneously but you cannot press left and right simultaneously.
- Naming Convention
-
-
DPad{Up|Down|Left|Right}
-
- Descriptions
-
-
DPadUp
: The top button on the directional pad. -
DPadDown
: The botton button on the directional pad. -
DPadLeft
: The left button on the directional pad. -
DPadRight
: The right button on the directional pad.
-
- Use
-
While you can bind them to nearly anything, these buttons are usually bound to pair of Hat axes, which are typically axes with a range of -1 to 1 and 0 when resting / released.
-
Button bindings to
KEY_
andBTN_
events work directly as you might expect. -
Buttons bound to
ABS_
events emit the axis limit in the given direction.
-
A common configuration, as seen in the example profile, might look like this:
[Bindings]
DpadUp = Gamepad ABS_HAT0Y -
DpadDown = Gamepad ABS_HAT0Y +
DpadLeft = Gamepad ABS_HAT0X -
DpadRight = Gamepad ABS_HAT0X +
Buttons
These are pretty straightforward. As you probably expect, buttons have two states. They’re true when pressed and false when released. The Steam Deck borrows common names for most buttons, but also adds a few of it’s own. It’s debatable how to organize or classify these, so I’ll just sorta go by the legacy naming standards.
- Naming conventions
-
-
A
B
X
Y
{L|R}{1|2|3|4|5}
Menu
Options
Steam
QuickAccess
-
- Descriptions
-
-
A
: Same as it appears on the front of the Steam Deck. -
B
: Same as it appears on the front of the Steam Deck. -
X
: Same as it appears on the front of the Steam Deck. -
Y
: Same as it appears on the front of the Steam Deck. -
L1
: The top left bumper / shoulder button. -
R1
: The top right bumper / shoulder button. -
L2
: Button nested inside the pressure sensor of the left trigger. -
R2
: Button nested inside the pressure sensor of the right trigger. -
L3
: Button nested at the bottom of the left stick. Activated by pressing down until it clicks. -
R3
: Button nested at the bottom of the right stick. Activated by pressing down until it clicks. -
L4
: Upper paddle button located on the back-left side of the Steam Deck. -
R4
: Upper paddle button located on the back-right side of the Steam Deck. -
L5
: Lower paddle button located on the back-left side of the Steam Deck. -
R5
: Lower paddle button located on the back-right side of the Steam Deck. -
Menu
: Raised hamburger button (☰) located above the *right *thumbstick. -
Options
: Raised overlapped rectangle button (⮻) located above the left thumbstick. -
Steam
: Flat button of the same name (STEAM), located below the left trackpad. -
QuickAccess
: Flat button with three interpuncts (···), located below the right trackpad.
-
- Use
-
Binding buttons is simple.
-
Button bindings to
KEY_
andBTN_
events work directly. -
Buttons bound to
ABS_
events emit the axis limit in the given direction. For example, if you create a binding likeA = ABS_X +
, then when you press theA
button, it will emit anABS_X
event for whatever the maximum axis limit forABS_X
is, whereasA = ABS_X -
will emit the minimum axis limit. -
A
/B
/X
/Y
have respective input codes forGamepad
devices:BTN_SOUTH
,BTN_EAST
,BTN_WEST
andBTN_NORTH
. -
{L|R}{1|2}
have respective input codes forGamepad
devices:BTN_TL
BTN_TL2
BTN_TR
andBTN_TR2
. -
{L|R}3
have respective input codes forGamepad
devices:BTN_THUMBL
andBTN_THUMBR
-
Menu
has a respective (based on location and common use) input code forGamepad
devices:BTN_START
. -
Options
has a respective (based on location and common use) input code forGamepad
devices:BTN_SELECT
. -
Steam
is probably closest to a PS or HOME button on aGamepad
device; possibly useBTN_MODE
.
-
Thumbsticks
The thumbsticks on the Steam Deck are associated with six different inputs in OpenSD. As you would expect, there is an X/Y axis pair for each stick, but there are also touch and pressure (well, sorta) sensors located on the top of each one. The directional axes are broken into halves such that each direction can emit different events if desired.
Axis values from thumbsticks are normalized internally so they can be rescaled to the defined ABS event ranges. The hardware returns signed 16-bit integer values for axis values in the HID reports. The "pressure sensor" component has a very short numerical range, but is quite sensitive.
If Stick Filtering is enabled the full axis is internally normalized as a unit vector and a radial deadzone may be applied.
- Naming convention
-
-
{L|R}Stick{Up|Down|Left|Right|Touch|Force}
-
- Descriptions
-
-
LStickUp
: Represents the top half of the Y-axis of the left thumbstick. -
LStickDown
: Represents the bottom half of the Y-axis of the left thumbstick. -
LStickLeft
: Represents the left half of the X-axis of the left thumbstick. -
LStickRight
: Represents the right half of the X-axis of the left thumbstick. -
LStickTouch
: This is a binary button that return true when the top of the left thumbstick is touched. -
LStickForce
: Capacitive touch sensor at the top of the left thumbstick. It is very sensitive and can register if your hand is near, without actually touching it. At a hardware level, the sensitivity ranges from 0 to ~112, which is an odd number. This value is returned as an normalized axis (0 to 1.0), just like a trigger. -
RStickUp
: Represents the top half of the Y axis of the right thumbstick. -
RStickDown
: Represents the bottom half of the Y axis of the right thumbstick. -
RStickLeft
: Represents the left half of the X axis of the right thumbstick. -
RStickRight
: Represents the right half of the X axis of the right thumbstick. -
RStickTouch
: This is a binary button that return true when the top of the right thumbstick is touched. -
RStickForce
: Capacitive touch sensor at the top of the right thumbstick. It is very sensitive and can register if your hand is near, without actually touching it. At a hardware level, the sensitivity ranges from 0 to ~112, which is an odd number. This value is returned as an normalized axis (0 to 1.0), just like a trigger.
-
- Use
-
Directional inputs are treated like axis halves. You typically want to map
Up
andDown
onto the same ABS event code, but in opposite directions. Doing so will perfectly map the full range of motion to an event code. An example of this, as demonstrated in the [Bindings] section of the example profile:
# Left Stick
LStickUp = Gamepad ABS_Y -
LStickDown = Gamepad ABS_Y +
LStickLeft = Gamepad ABS_X -
LStickRight = Gamepad ABS_X +
# Right Stick
RStickUp = Gamepad ABS_RY -
RStickDown = Gamepad ABS_RY +
RStickLeft = Gamepad ABS_RX -
RStickRight = Gamepad ABS_RX +
You’re also able to treat each axis half like a button if you bind it to a key or button event code, in which case it will trigger the binding when the stick leaves the center / deadzone. You can use the deadzone in this case to determine how far the stick must be pushed from center before the binding is triggered.
-
LStickLeft
&LStickRight
are typically bound to theABS_X
event code onGamepad
devices. -
LStickUp
&LStickDown
are typically bound to theABS_Y
event code onGamepad
devices. -
RStickLeft
&RStickRight
are typically bound to theABS_RX
event code onGamepad
devices. -
RStickUp
&RStickDown
are typically bound to theABS_RY
event code onGamepad
devices. -
Use
LStickTouch
/RStickTouch
to detect if a players hands are on the controls. -
Use
LStickForce
/RStickForce
if you want to write a tiny electric theremin simulator? If this sensor is bound to an absolute axis, a range of 0 to 100 is recommened.
Triggers
Triggers are pressure sensors that are also treated a bit like a thumbstick’s half-axis, with the difference being there’s no complement half. Triggers have a resting position of the defined axis minimum limit and move toward the maximum limit when actuated. Typically the minimum limit is zero, so the axis does not return non-zero values when released / resting, but you can do any weird thing you want.
Axis values from triggers are normalized internally so they can be rescaled to the defined ABS event ranges. Internally, the HID reports return trigger values as unsigned 16-bit integers.
Triggers also have a binary button component: L2
and R2
. Information about these buttons can be found in the Buttons section.
Linear deadzones can be applied to triggers, if desired in the Deadzones Section of a profile.
- Naming convention
-
-
{L|R}Trigg
-
- Descriptions
-
-
LTrigg
: Represents a pressure sensor value for the left trigger on the top-rear of the device. -
RTrigg
: Represents a pressure sensor value for the right trigger on the top-rear of the device.
-
- Use
-
These inputs are absolute axes and can be mapped to ABS event codes as well as KEY / BTN event codes. One possible reason to use the axis itself as a button-type binding would be to use
L2
/R2
buttons on partial actuation, and use a deadzone to emit another event code on full actuation.-
LTrigg
is typically bound toABS_Z
. -
RTrigg
is typically bound toABS_RZ
.
-
Trackpads
At the core, trackpads are absolute axis devices have with an X / Y pair, as well as a pressure sensor Z-axis and a slightly tactile button. A number of inputs can be extrapolated from the data those basic types provide. That includes touch sensors, press / button sensors, pressure / force sensors, absolute coordinates, relative movement tracking as well as the ability to map out regions of the pad and treat them as individual buttons. OpenSD trackpads have the most input bindings out of all the components.
As with the Thumbsticks, directional axes are broken into halves such that each direction can emit different events if desired. These halves can be mapped on to a whole ABS event code, or use separately.
Trackpads support radial deadzones for absolute axis inputs, and can be configured in the Deadzones Section of a profile.
Relative trackpad input, such as LPadRelX
or LPadRelY
, are not affected by deadzones and return data suitable for pointing devices like mice. These return the difference in positional movement calculated between HID frames. Some filtering is always applied to this process to reduce jitter and a small amount of intertia is also applied. The typical value range returned by these inputs is usually between -5 and +5. Binding RelX
and *RelY
inputs to anything other than REL_
event codes on the Mouse
device is probably not useful.
Touchpads are non-multitouch devices so they only relay a single X / Y coordinate pair. I’ve read that, at a hardware level, they are supposedly multi-touch capable, but I don’t have any information on reading that data yet. If true, it will probably be supported in a future version.
OpenSD provides multiple "button maps", any of which can be used non-exclusively with each other. These "button maps" break the full area of the trackpad into logical sections which, when pressed (as opposed to being merely touched) act as individual buttons. If so desired, trackpads can be used to create "button clusters", which can be used to emulate a Directional Pad, arrow keys, run scripts, launch applications, etc. For the sake of readability, button maps are described separately below the main input descriptions.
- Naming convention
-
-
{L|R}Pad{Up|Down|Left|Right|Touch|Press|Force|RelX|RelY}
-
{L|R}PadPressQuad{Up|Down|Left|Right}
-
{L|R}PadPressOrth{Up|Down|Left|Right}
-
{L|R}PadPressGrid2x2_{1|2|3|4}
-
{L|R}PadPressGrid3x3_{1|2|3|4|5|6|7|8|9}
-
- Descriptions
-
-
LPadUp
: Represents the top half of the Y-axis of the left trackpad. -
LPadDown
: Represents the bottom half of the Y-axis of the left trackpad. -
LPadLeft
: Represents the left half of the X-axis of the left trackpad. -
LPadRight
: Represents the right half of the X-axis of the left trackpad. -
LPadTouch
: This is a button sensor which detects if the left pad is being touched. Quite sensitive. -
LPadPress
: This is also a button which detects if the left pad is being pressed down like a button. Actuation has a slight tactile bump. -
LPadForce
: This is a pressure sensor which returns a normalized value of how much force is being used to press down on the left trackpad. This is an absolute axis value and behaves the same as Triggers. -
LPadRelX
: This is a derived relative axis value that measures the amount of relative X-axis movement between update frames of the left trackpad. This represents the same kind of input data that mice use. This input is unaffected by deadzones. Typical values returned fall inside the range of -5 to +5. -
LPadRelY
: This is a derived relative axis value that measures the amount of relative Y-axis movement between update frames of the left trackpad. This represents the same kind of input data that mice use. This input is unaffected by deadzones. Typical values returned fall inside the range of -5 to +5. -
RPadUp
: Represents the top half of the Y-axis of the right trackpad. -
RPadDown
: Represents the bottom half of the Y-axis of the right trackpad. -
RPadLeft
: Represents the left half of the X-axis of the right trackpad. -
RPadRight
: Represents the right half of the X-axis of the right trackpad. -
RPadTouch
: This is a button sensor which detects if the right pad is being touched. Quite sensitive. -
RPadPress
: This is also a button which detects if the right pad is being pressed down like a button. Actuation has a slight tactile bump. -
RPadForce
: This is a pressure sensor which returns a normalized value of how much force is being used to press down on the right trackpad. This is an absolute axis value and behaves the same as Triggers. -
RPadRelX
: This is a derived relative axis value that measures the amount of relative X-axis movement between update frames of the right trackpad. This represents the same kind of input data that mice use. This input is unaffected by deadzones. Typical values returned fall inside the range of -5 to +5. -
RPadRelY
: This is a derived relative axis value that measures the amount of relative Y-axis movement between update frames of the right trackpad. This represents the same kind of input data that mice use. This input is unaffected by deadzones. Typical values returned fall inside the range of -5 to +5.
-
Quadrant Button Map
The quadrant map (PadPressQuad*
) provides a way to treat each touchpad as being composed of four non-overlapping triagular quadrants, as depicted in the figure 1 below. Each button is inherently exclusive to the others in this map since the X / Y coordinate can only fall inside of one of these regions at a time. This means that this map will not detect any overlapping presses, like a and b when you press in the upper-left region.
Fig. 1
┌─────────────┐ │ \ / │ │ \ a / │ │ \ / │ │ \ / │ │ \ / │ │ b X c │ │ / \ │ │ / \ │ │ / \ │ │ / d \ │ │ / \ │ └─────────────┘
-
LPadPressQuadUp
: The logical button on the left trackpad corresponding to fig. 1, region "a". -
LPadPressQuadDown
: The logical button on the left trackpad corresponding to fig. 1, region "d". -
LPadPressQuadLeft
: The logical button on the left trackpad corresponding to fig. 1, region "b". -
LPadPressQuadRight
: The logical button on the left trackpad corresponding to fig. 1, region "c". -
RPadPressQuadUp
: The logical button on the right trackpad corresponding to fig. 1, region "a". -
RPadPressQuadDown
: The logical button on the right trackpad corresponding to fig. 1, region "d". -
RPadPressQuadLeft
: The logical button on the right trackpad corresponding to fig. 1, region "b". -
RPadPressQuadRight
: The logical button on the right trackpad corresponding to fig. 1, region "c".
Orthogonal Button Map
The orthogonal map (PadPressOrth*
) works similarly to a Directional Pad. As you can see below in figure 2, it demonstrates how the "a", "b", "c" and "d" regions represent orthogonal directions which are not strictly exclusive as they are with the Quadrant Button Map. If a diagonal corner is pressed, it triggers both orthogonally adjacent buttons. For example, pressing the upper-middle of the pad only triggers "a", but pressing the upper-left of the pad will trigger both "a" and "b".
Fig. 2
┌─────┬─────┬─────┐ │ │ │ │ │ ab │ a │ ac │ │ │ │ │ ├─────┼─────┼─────┤ │ │ │ │ │ b │ │ c │ │ │ │ │ ├─────┼─────┼─────┤ │ │ │ │ │ db │ d │ dc │ │ │ │ │ └─────┴─────┴─────┘
-
LPadPressOrthUp
: The logical button on the left trackpad corresponding to fig. 2 regions containing "a". -
LPadPressOrthDown
: The logical button on the left trackpad corresponding to fig. 2 regions containing d". -
LPadPressOrthLeft
: The logical button on the left trackpad corresponding to fig. 2 regions containing "b". -
LPadPressOrthRight
: The logical button on the left trackpad corresponding to fig. 2 regions containing "c". -
RPadPressOrthUp
: The logical button on the right trackpad corresponding to fig. 2 regions containing "a". -
RPadPressOrthDown
: The logical button on the right trackpad corresponding to fig. 2 regions containing d". -
RPadPressOrthLeft
: The logical button on the right trackpad corresponding to fig. 2 regions containing "b". -
RPadPressOrthRight
: The logical button on the right trackpad corresponding to fig. 2 regions containing "c".
2x2 Grid Button Map
This button map divides the pad into regions along the center axes, resulting in four square buttons in each corner, as depicted in figure 3. Buttons are naturally exclusive, so only one can be pressed at a time. Buttons are enumerated left-to-right, top-to-bottom.
Fig. 3
┌────────┬────────┐ │ │ │ │ │ │ │ a │ c │ │ │ │ │ │ │ ├────────┼────────┤ │ │ │ │ │ │ │ c │ d │ │ │ │ │ │ │ └────────┴────────┘
-
LPadPressGrid2x2_1
: The logical button on the left trackpad corresponding to fig. 3, region "a". -
LPadPressGrid2x2_2
: The logical button on the left trackpad corresponding to fig. 3, region "b". -
LPadPressGrid2x2_3
: The logical button on the left trackpad corresponding to fig. 3, region "c". -
LPadPressGrid2x2_4
: The logical button on the left trackpad corresponding to fig. 3, region "d". -
RPadPressGrid2x2_1
: The logical button on the right trackpad corresponding to fig. 3, region "a". -
RPadPressGrid2x2_2
: The logical button on the right trackpad corresponding to fig. 3, region "b". -
RPadPressGrid2x2_3
: The logical button on the right trackpad corresponding to fig. 3, region "c". -
RPadPressGrid2x2_4
: The logical button on the right trackpad corresponding to fig. 3, region "d".
3x3 Grid Button Map
This button map divides the pad into thirds along both axes, resulting in a 3x3 grid of nine square buttons, as depicted in figure 4. These buttons are naturally exclusive to one another, so only one can be pressed at a time. Buttons are enumerated left-to-right, top-to-bottom.
Note
|
The resulting size of each button will be fairly small, so it may require a little practice and small thumbs to manipulate them precisely. |
Fig. 4
┌─────┬─────┬─────┐ │ │ │ │ │ a │ b │ c │ │ │ │ │ ├─────┼─────┼─────┤ │ │ │ │ │ d │ e │ f │ │ │ │ │ ├─────┼─────┼─────┤ │ │ │ │ │ g │ h │ i │ │ │ │ │ └─────┴─────┴─────┘
-
LPadPressGrid3x3_1
: The logical button on the left trackpad corresponding to fig. 4, region "a". -
LPadPressGrid3x3_2
: The logical button on the left trackpad corresponding to fig. 4, region "b". -
LPadPressGrid3x3_3
: The logical button on the left trackpad corresponding to fig. 4, region "c". -
LPadPressGrid3x3_4
: The logical button on the left trackpad corresponding to fig. 4, region "d". -
LPadPressGrid3x3_5
: The logical button on the left trackpad corresponding to fig. 4, region "e". -
LPadPressGrid3x3_6
: The logical button on the left trackpad corresponding to fig. 4, region "f". -
LPadPressGrid3x3_7
: The logical button on the left trackpad corresponding to fig. 4, region "g". -
LPadPressGrid3x3_8
: The logical button on the left trackpad corresponding to fig. 4, region "h". -
LPadPressGrid3x3_9
: The logical button on the left trackpad corresponding to fig. 4, region "i". -
RPadPressGrid3x3_1
: The logical button on the right trackpad corresponding to fig. 4, region "a". -
RPadPressGrid3x3_2
: The logical button on the right trackpad corresponding to fig. 4, region "b". -
RPadPressGrid3x3_3
: The logical button on the right trackpad corresponding to fig. 4, region "c". -
RPadPressGrid3x3_4
: The logical button on the right trackpad corresponding to fig. 4, region "d". -
RPadPressGrid3x3_5
: The logical button on the right trackpad corresponding to fig. 4, region "e". -
RPadPressGrid3x3_6
: The logical button on the right trackpad corresponding to fig. 4, region "f". -
RPadPressGrid3x3_7
: The logical button on the right trackpad corresponding to fig. 4, region "g". -
RPadPressGrid3x3_8
: The logical button on the right trackpad corresponding to fig. 4, region "h". -
RPadPressGrid3x3_9
: The logical button on the right trackpad corresponding to fig. 4, region "i".
Troubleshooting
TODO: This section <<<
Example config.ini
[Daemon]
# The gamepad profile to be loaded on startup
Profile = default.profile
# Allow client connections from CLI and GUI configuration tools
AllowClients = true
Example Profile
[Profile]
Name = Example Profile
Description = Just an example profile to show basic use
[Features]
ForceFeedback = true
MotionDevice = true
MouseDevice = true
LizardMode = false
StickFiltering = true
TrackpadFiltering = true
[Deadzones]
LStick = 0.1
RStick = 0.1
LPad = 0
RPad = 0
LTrigg = 0
RTrigg = 0
[GamepadAxes]
ABS_HAT0X = -1 1
ABS_HAT0Y = -1 1
ABS_X = -32767 32767
ABS_Y = -32767 32767
ABS_RX = -32767 32767
ABS_RY = -32767 32767
ABS_Z = 0 32767
ABS_RZ = 0 32767
[MotionAxes]
ABS_X = -32767 32767
ABS_Y = -32767 32767
ABS_Z = -32767 32767
ABS_RX = -32767 32767
ABS_RY = -32767 32767
ABS_RZ = -32767 32767
[Bindings]
DpadUp = Gamepad ABS_HAT0Y -
DpadDown = Gamepad ABS_HAT0Y +
DpadLeft = Gamepad ABS_HAT0X -
DpadRight = Gamepad ABS_HAT0X +
# Buttons
A = Gamepad BTN_SOUTH
B = Gamepad BTN_EAST
X = Gamepad BTN_WEST
Y = Gamepad BTN_NORTH
L1 = Gamepad BTN_TL
R1 = Gamepad BTN_TR
L2 = Gamepad BTN_TL2
R2 = Gamepad BTN_TR2
L3 = Gamepad BTN_THUMBL
R3 = Gamepad BTN_THUMBR
L4 = None
R4 = None
L5 = None
R5 = None
Menu = Gamepad BTN_START
Options = Gamepad BTN_SELECT
Steam = Gamepad BTN_MODE
QuickAccess = Command true 0 rofi -show drun
# Triggers
LTrigg = Gamepad ABS_Z +
RTrigg = Gamepad ABS_RZ +
# Left Stick
LStickUp = Gamepad ABS_Y -
LStickDown = Gamepad ABS_Y +
LStickLeft = Gamepad ABS_X -
LStickRight = Gamepad ABS_X +
LStickTouch = None
LStickForce = None
# Right Stick
RStickUp = Gamepad ABS_RY -
RStickDown = Gamepad ABS_RY +
RStickLeft = Gamepad ABS_RX -
RStickRight = Gamepad ABS_RX +
RStickTouch = None
RStickForce = None
# Left Trackpad
LPadUp = None
LPadDown = None
LPadLeft = None
LPadRight = None
LPadRelX = None
LPadRelY = None
LPadTouch = None
LPadPress = Mouse BTN_LEFT
LPadForce = None
LPadPressQuadUp = None
LPadPressQuadDown = None
LPadPressQuadLeft = None
LPadPressQuadRight = None
LPadPressOrthUp = None
LPadPressOrthDown = None
LPadPressOrthLeft = None
LPadPressOrthRight = None
LPadPressGrid2x2_1 = None
LPadPressGrid2x2_2 = None
LPadPressGrid2x2_3 = None
LPadPressGrid2x2_4 = None
LPadPressGrid3x3_1 = None
LPadPressGrid3x3_2 = None
LPadPressGrid3x3_3 = None
LPadPressGrid3x3_4 = None
LPadPressGrid3x3_5 = None
LPadPressGrid3x3_6 = None
LPadPressGrid3x3_7 = None
LPadPressGrid3x3_8 = None
LPadPressGrid3x3_9 = None
# Right Trackpad
RPadUp = None
RPadDown = None
RPadLeft = None
RPadRight = None
RPadRelX = Mouse REL_X
RPadRelY = Mouse REL_Y
RPadTouch = None
RPadPress = Mouse BTN_RIGHT
RPadForce = None
RPadPressQuadUp = None
RPadPressQuadDown = None
RPadPressQuadLeft = None
RPadPressQuadRight = None
RPadPressOrthUp = None
RPadPressOrthDown = None
RPadPressOrthLeft = None
RPadPressOrthRight = None
RPadPressGrid2x2_1 = None
RPadPressGrid2x2_2 = None
RPadPressGrid2x2_3 = None
RPadPressGrid2x2_4 = None
RPadPressGrid3x3_1 = None
RPadPressGrid3x3_2 = None
RPadPressGrid3x3_3 = None
RPadPressGrid3x3_4 = None
RPadPressGrid3x3_5 = None
RPadPressGrid3x3_6 = None
RPadPressGrid3x3_7 = None
RPadPressGrid3x3_8 = None
RPadPressGrid3x3_9 = None
# Accelerometers
AccelXPlus = Motion ABS_RX +
AccelXMinus = Motion ABS_RX -
AccelYPlus = Motion ABS_RY +
AccelYMinus = Motion ABS_RY -
AccelZPlus = Motion ABS_RZ +
AccelZMinus = Motion ABS_RZ -
# Gyro / Attitude
RollPlus = Motion ABS_X +
RollMinus = Motion ABS_X -
PitchPlus = Motion ABS_Y +
PitchMinus = Motion ABS_Y -
YawPlus = Motion ABS_Z +
YawMinus = Motion ABS_Z -