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 GameInputDevice
s 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, wheremin
andmax
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 |
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. |