Module dmc::game_input_device [] [src]

Get input from gamepads, joysticks, steering wheels, and others.

Within this module's doc, a "device" will refer to any input device specific to video-games, such as gamepads, joysticks and steering wheels.

Background

Cross-platform input with game input devices is a much more of a big deal than it should be. Especially on desktop, there's such a wide variety of devices, each with their own button sets, capabilities, OS-and-driver-and-backend specific quirks, that it's seldom possible to properly categorize one when the OS exposes it.
This is particularly the case on Linux, where the situation is a complete mess to the point most users have to configure everything from scratch (and let's not get started on BSD and friends).
On the opposite side (such as Windows with XInput), things are much smoother.

With this in mind, it's near impossible to provide wrappers that cover accurately every possible device - most multimedia libraries (such as SDL2 and SFML) afford to lose some information in favor of a simpler interface, and some engines (eg. Unity) "stringly-type" parts of their related APIs.

This module strives to achieve professional-quality completeness, and as such, attempts to provide every useful piece of information the backend exposes and which could be of reasonable interest to developers, players, and testers. As such, it is not as small or simple, but it represents well the current situation.

This is also why it is not named "gamepad", "game_controller", "joystick", or anything else, because these names carry too many assumptions.

Overview

This module's main entry point is the GameInputDevice structure, which is a handle to a connected device. You acquire these by first creating a Monitor once. At this time, you also get a list of the already connected devices, which you have to take ownership of (not necessarily the list itself, but rather, each device).

Then, you should poll the Monitor regularly to know when new devices are connected.

Device handles which the Monitor gives are yours to manage (unlike some libraries which hand back IDs or indices to internal collections). Again: this module does not keep track of device handles. If you miss the opportunities to store them, you won't be able to get them back later.

You should also poll your own list of GameInputDevices regularly to receive state change events from each one of them. One of these events is Disconnected, which tells you that the device isn't available anymore, in which case you should drop the GameInputDevice in order to free the remaining resources, and because you won't be able to do much with it anymore.

Regardless of a device's actual classification and abilities, you should handle all possible events, even when some of them may actually never be generated. For instance, an actual gamepad will never report joystick-specific events, and vice versa, but sometimes the backend just doesn't know and reports whatever event it deems appropriate.

You can do a bunch of other things with a GameInputDevice, some of which are:

  • Query the immediate state of its buttons and axes;
  • Query its battery's immediate state, if relevant and supported;
  • Retrieve associated metadata, such as its name, vendor, capabilities, etc;
  • Send a list of event remapping "passes" (if needed);
  • Send and play a variety of haptic (force feedback) effects;

Some things that aren't supported yet, but are put in a potential TODO:

  • Capture from integrated microphones;
  • Output audio from integrated speakers;

The model

TODO: Picture of the virtual gamepad model ?

In this module:

  • X axes go from left (negative) to right (positive);
  • Y and Z axes go from down (negative) to up (positive);
  • Integer axis values are normalized to the [min, max] inclusive range, where min and max are the minimum and maximum values of the integer type.

This module also does not implicitly remap actions (except for cases when the backend is known not to be sane).
Right now, it's up to your game to provide proper input remapping. This module might be extended later to ease this task.

Advice for input handling

Dead zones

Often, gamepad axes are very sensitive, such that what should be a complete absence of motion for the end user is actually reported as very tiny motion events. Since this causes poor gameplay experience, there's that concept of "dead zones" within which your game should ignore motion events.
Some drivers claim to cull such events automatically (even though sometimes they actually don't), some have no idea about a device's appropriate dead zones (while some do, or think they do)...

Long story short, it's a complete mess.
So, you should decide of your own dead zone values (or better, let the user configure them) and manually ignore events which do not comply. When the driver exposes dead zone values, this module provides them to you, but I don't know whether you should actually trust them or not - it's up to you and your game.

Proper handling of dead zones is not self-evident: you're encouraged to use the "Scaled Radial Dead Zone" technique as described in Doing Thumbstick Dead Zones Right.

dpad vs. hat[0]

On some (most ?) Linux drivers, D-pad motions are actually reported as joystick_hats[0]. It is probably wise to handle dpad and joystick_hats[0] as being mutually exclusive and meaning the same thing.

L2/R2 vs. LTrigger/RTrigger

What you know as the L2 and R2 buttons on gamepads are actually triggers on Xbox360 gamepads (as such, they are axes, not buttons).
You should handle both *_shoulder_2 and *_trigger as being mutually exclusive and meaning the same thing, knowing that *_trigger carries more precision.

Primary stick

Joystick devices report joystick motion through l_stick (which in this case means "the first stick" instead of actual "left stick").

l_stick.z/r_stick.z vs l_thumb/r_thumb

*_stick.z is for joysticks which genuinely support vertical motion as an axis.
*_thumb is for gamepad joysticks which can be "clicked" (as for Dualshock and Xbox 360 gamepads, and most others actually).

joystick_thumb1 vs l_thumb

You probably want to handle both as if they were the same, but it depends.

Implementation notes

Linux

This module uses libudev to monitor and manage devices. When a device is connected, it normally exposes both /dev/input/jsXX (joydev node) and /dev/input/eventYY (evdev node), where XX and YY are integers not known in advance.
Normally, user-space is granted read-write access to these nodes (other devices, such as mouses and keyboards, are provided to user-space by the X server).

It then attempts to open the evdev node. If it succeeds, further interactions with the device are performed through libevdev.

Otherwise, it attempts to open the joydev node. If it succeeds, further interactions with the device are performed though standard read() and write() calls, guided by struct definitions from the linux/joystick.h system header file.

joydev is a bit too simple, in that the meaning of axes is fairly arbitrary, and buttons are only identified as a number from 0 to 10.
evdev is the modern and more powerful alternative, which is why it is preferred when available.

Reexports

pub use self::monitor::Monitor;
pub use self::mapping::Mapping;
pub use self::haptic::UploadedEffect;
pub use self::haptic::PlayingEffect;
pub use self::haptic::Effect;
pub use self::haptic::RumbleEffect;
pub use self::haptic::Envelope;
pub use self::haptic::ConstantEffect;
pub use self::haptic::Waveform;
pub use self::haptic::PeriodicEffect;
pub use self::haptic::RampEffect;
pub use self::haptic::ConditionEffect;

Modules

haptic

Haptic (force feedback)-related data structures.

mapping

Remappings for game input devices.

monitor

Monitor-related submodule.

Structs

AxisInfo

Information for an axis, such as dead zone values, resolution and fuzz.

Capabilities

Which buttons and axes the device supports, as claimed by the backend.

Dpad

The well-known D-pad is a cross of 4 direction buttons.

GameInputDevice

A Gamepad, joystick, steering wheel, or whatever device that's reported as a game input device by the backend.

GenericIAxis
GenericUAxis
Info

Everything device-specific that is not the device's current state.

Minmax

A simple min-max value pair.

PollIter

A polling iterator over a device's event queue.

State

State of all of a device's buttons and axes.

Vec2
Vec3
WaitIter

A waiting iterator over a device's event queue.

Enums

AbsoluteAxis
AbsoluteAxisMotion
Bus
ButtonPressed
ButtonReleased
Error
Event

Events sent by a GameInputDevice.

GamepadModel
JoystickModel
Model
RelativeAxis
RelativeAxisMotion

Relative axis motion events. You'll seldom see them, but they exist.

SteeringWheelModel

Constants

MAX_JOYSTICK_BASE_BUTTONS

The maximum number of base buttons (whatever that means) for joystick devices.

MAX_JOYSTICK_HATS

The maximum number of hats for joystick devices.

MAX_NUMBERED_BUTTONS

The maximum number of numbered buttons.

Traits

SignedAxis

A type that's suitable for representing a signed axis.

UnsignedAxis

A type that's suitable for representing an unsigned axis.

Type Definitions

Button
IAxis

The common type for signed axes.

UAxis

The common type for unsigned axes.