cl gamepad
3.0.0Portability library for gamepad and joystick access.
About cl-gamepad
This is a library to provide cross-platform access to gamepads, joysticks, and other such HID devices.
How To
For this tutorial we assume the nickname gamepad
exists for org.shirakumo.fraf.gamepad
. Before you can start, you must initialise the library.
(gamepad:init)
If successful, it will return a list of currently connected gamepads. You can query the device properties with name
, vendor
, product
, version
, and driver
. Once you are ready to process device input, you can do so with poll-events
.
(gamepad:poll-events device
(lambda (event)
(format T "~& ~a ~a~%"
(etypecase event
(gamepad:button-down "Pressed")
(gamepad:button-up "Released")
(gamepad:axis-move "Moved"))
(or (gamepad:event-label event) (gamepad:event-code event)))))
You can access the event properties with event-device
, event-time
, event-code
, event-label
, and for axis events event-value
. Do note that the event instance is only valid within the dynamic extent of your supplied function. You cannot store the event elsewhere or process it out of band.
If the device gets disconnected or some other problem occurs during event polling, an error of type gamepad-error
will be signalled. You should handle this error and unhook the device from your own system, then either invoke the restart drop-device
, or call poll-devices
to automatically refresh the current device list.
If the label in the event is NIL
, the feature may not be properly mapped to a standardised label. Please see the following section.
Whenever you want to listen for new device connections or ensure current devices still exist, you should run poll-devices
. Both poll-events
and poll-devices
accept a timeout argument to allow you to efficiently poll if you need a dedicated thread. The default behaviour is to return immediately if there are no pending changes, allowing you to use the calls synchronously.
Most gamepads also support haptic or force feedback. You can activate the rumble feature and modulate it over time using the rumble
function. Do note that sadly the capability of the devices, and especially the capability of the underlying drivers varies wildly and the feature may thus sometimes just not be available.
You can query the last known state of a gamepad feature through button
and axis
. Only standardised labels can be given for these functions however, so unmapped features cannot be queried.
Finally you can configure common axis features such as the dead-zone
and the ramp
per device. Do note however that these settings are not persistent, so you must apply them whenever the device is connected anew. The axis values are rezoned after the dead-zone clamp has applied, meaning that if the value exceeds the dead-zone, it will be remapped to fit into the [0,1] range again. Also note that if you apply the dead-zone to the two axes of a single joystick separately, the zone you set will be square, rather than the expected circular dead zone. You can set a circular dead zone by setting the dead zone on the virtual labels :L
for the :L-H
and :L-V
axes, or :R
for the :R-H
and :R-V
axes. The ramp
function is applied after the dead zone. By default no dead zone or ramp is applied.
Standardised Button Maps
In order to make portable interaction with gamepads possible, their features (buttons and axes) must be mapped to standardised purposes (see +labels+
). Cl-gamepad will try to determine such a mapping automatically if possible. On Evdev and XInput this is typically the case. On DirectInput and IOKit not so much. In any case, when the automated mapping fails, manual mappings from the device's internal feature IDs to the standardised labels must be provided.
To this end cl-gamepad includes a database of mappings in default-device-mappings.lisp
. These mappings are automatically loaded for any matching controller and should thus apply immediately, making the listed controllers compatible. If your controller is not listed and auto-detection fails, please use configure-device
from a terminal. It will guide you through the process of mapping the device features to the proper labels, and will then save the mapping to the aforementioned file to make it persistent.
If you do configure a new device, please submit the updated mapping file to the upstream repository so that it can be included in the main distribution.
Supported Platforms
Currently the following platforms are supported:
Linux evdev (via libevdev)
macOS/iOS IOKit
Windows DirectInput
Windows XInput
On Windows DirectInput and XInput are used at the same time.
System Information
Definition Index
-
ORG.SHIRAKUMO.FRAF.GAMEPAD
No documentation provided.-
EXTERNAL SPECIAL-VARIABLE *DEFAULT-MAPPINGS-FILE*
No documentation provided. -
EXTERNAL GLOBAL +LABELS+
Vector of standardised button and axis labels. Modifying this value results in undefined behaviour. The labels are intended to have the following semantic meanings: A --- The A B C buttons are laid out on the bottom diagonal B named button row, with A being the bottom-most. C This follows the "Xbox" convention. X --- The X Y Z buttons are laid out on the top diagonal Y named button row, with X being the bottom-most. Z This follows the "Xbox" convention. L1 --- The left hand upper trigger button. L2 --- The left hand lower trigger button/axis. L3 --- The left hand stick press-in button. R1 --- The right hand upper trigger button. R2 --- The right hand lower trigger button/axis. R3 --- The right hand stick press-in button. DPAD-L --- The left dpad button. DPAD-R --- The right dpad button. DPAD-U --- The upper dpad button. DPAD-D --- The lower dpad button. SELECT --- The left central button. Often named select, back, or share. HOME --- The middle central button. Usually a logo. START --- The right central button. Often named start, or options. L-H --- The left stick horizontal axis. L-V --- The left stick vertical axis. R-H --- The right stick horizontal axis. R-V --- The right stick vertical axis. DPAD-H --- The dpad horizontal axis. DPAD-V --- The dpad vertical axis. TILT-X --- The tilt degree around the X (along the length) axis. TILT-Y --- The tilt degree around the Y (along the height) axis. TILT-Z --- The tilt degree around the Z (along the depth) axis. MOVE-X --- The relative motion over the X axis. MOVE-Y --- The relative motion over the Y axis. MOVE-Z --- The relative motion over the Z axis. WHEEL --- The steering wheel direction. GAS --- The gas pedal. BRAKE --- The brake pedal. THROTTLE --- The throttle strength. RUDDER --- The rudder direction.
-
EXTERNAL CLASS DEVICE
-
EXTERNAL CONDITION GAMEPAD-ERROR
Error signalled when an underlying operating system, driver, or device problem occurs. Depending on the driver used the actual type of error signalled may be a subtype of this condition with more information available about the error specifics.
-
EXTERNAL STRUCTURE AXIS-MOVE
Event issued when an axis moves. This event may be signalled many times with very similar values. It will NOT be issued if the combined axis (horizontal and vertical) has a dead zone set and the axis falls within the dead zone. If a ramp is defined on the axis, the value is already ramp-adjusted. See EVENT-VALUE See EVENT
-
EXTERNAL STRUCTURE BUTTON-DOWN
Event issued when a button is pressed down. This event is only signalled once on the rising edge. See EVENT
-
EXTERNAL STRUCTURE BUTTON-UP
Event issued when a button is released. This event is only signalled once on the falling edge. See EVENT
-
EXTERNAL STRUCTURE EVENT
Base type for device events. An event denotes a change in the device's buttons or axes. See POLL-EVENTS See BUTTON-DOWN See BUTTON-UP See AXIS-MOVE See EVENT-DEVICE See EVENT-TIME See EVENT-CODE See EVENT-LABEL See EVENT-VALUE
-
EXTERNAL FUNCTION AXIS
- AXIS
- DEVICE
Returns the last known value of the axis. Returns a single-float representing the axis state. This state is automatically updated when POLL-EVENTS is called. The axis name must be a known and mapped keyword from +LABELS+. See AXIS-MOVE See POLL-EVENTS See DEVICE See +LABELS+
-
EXTERNAL FUNCTION BUTTON
- BUTTON
- DEVICE
Returns the last known value of the button. Returns T if the button is pressed, NIL if it is released. This state is automatically updated when POLL-EVENTS is called. The button name must be a known and mapped keyword from +LABELS+. See BUTTON-DOWN See BUTTON-UP See POLL-EVENTS See DEVICE See +LABELS+
-
EXTERNAL FUNCTION CONFIGURE-DEVICE
- DEVICE
- &KEY
- BUTTON-LABELS
- AXIS-LABELS
Run an interactive configuration wizard to determine the proper button mappings. This will interactively ask you to press the correct buttons and axes corresponding to the prompted labels. You can use this to configure a device's button and axis mappings if the default mapping is not correct. See +COMMON-BUTTONS+ See +COMMON-AXES+ See SAVE-DEVICE-MAPPINGS
-
EXTERNAL FUNCTION DEAD-ZONE
- DEVICE
- AXIS
-
EXTERNAL FUNCTION (SETF DEAD-ZONE)
- VALUE
- DEVICE
- AXIS
No documentation provided. -
EXTERNAL FUNCTION DEVICE-MAPPING
- ID
A place accessing the device mapping associated with the given ID. An ID can either be a DEVICE instance, or a list with the following members: DRIVER --- The name of the underlying driver for the device. One of :DINPUT :XINPUT :EVDEV :IOKIT VENDOR --- The vendor ID of the device. PRODUCT --- The product ID of the device. Returns NIL if no mapping is known, or a plist with the following members: :NAME --- A string identifying the device in a hopefully human-readable way. :BUTTONS --- A hash table mapping backend-specific button IDs to the standardised button labels from +LABELS+ :AXES --- A hash table mapping backend-specific axis IDs to the standardised axis labels from +LABELS+ Setting a device mapping will automatically update all known devices that match the mapping. See REMOVE-DEVICE-MAPPING See CONFIGURE-DEVICE
-
EXTERNAL FUNCTION (SETF DEVICE-MAPPING)
- MAPPING
- ID
No documentation provided. -
EXTERNAL FUNCTION EVENT-CODE
- INSTANCE
Returns the underlying code of the feature on the device that caused the event. The code is a positive integer that has a device and driver specific meaning. This field is always set. See EVENT
-
EXTERNAL FUNCTION (SETF EVENT-CODE)
- VALUE
- INSTANCE
No documentation provided. -
EXTERNAL FUNCTION EVENT-DEVICE
- INSTANCE
-
EXTERNAL FUNCTION (SETF EVENT-DEVICE)
- VALUE
- INSTANCE
No documentation provided. -
EXTERNAL FUNCTION EVENT-LABEL
- INSTANCE
-
EXTERNAL FUNCTION (SETF EVENT-LABEL)
- VALUE
- INSTANCE
No documentation provided. -
EXTERNAL FUNCTION EVENT-TIME
- INSTANCE
Returns some form of time identifier for the event. The actual time resolution is dependent on the driver. This is mostly useful to gauge whether events happened simultaneously or not. This field is always set. See EVENT
-
EXTERNAL FUNCTION (SETF EVENT-TIME)
- VALUE
- INSTANCE
No documentation provided. -
EXTERNAL FUNCTION EVENT-VALUE
- INSTANCE
Returns the axis value as a single-float in [-1,+1]. For a horizontal axis, -1 means left, +1 means right. For a vertical axis, -1 means down, +1 means up. This field is only accessible for AXIS-MOVEs. See AXIS-MOVE
-
EXTERNAL FUNCTION (SETF EVENT-VALUE)
- VALUE
- INSTANCE
No documentation provided. -
EXTERNAL FUNCTION INIT
Initialises the library for use. This will cause system shared objects to be loaded and will cause certain support structures to be allocated and initialised. Only call this function on a target system where the gamepads should be queried. You MUST call this function before calling any other device functions. It is safe to call this function multiple times. Returns the list of currently known devices. See SHUTDOWN
-
EXTERNAL FUNCTION LIST-DEVICES
Returns a fresh list of all known gamepad devices. This will not query for device updates, and only represents the currently known state of the system. On an uninitialised system this will return NIL. See POLL-DEVICES
-
EXTERNAL FUNCTION POLL-DEVICES
- &KEY
- TIMEOUT
Queries for device changes. TIMEOUT can be a one of the following: T --- Continuously poll for device changes indefinitely. NIL --- Immediately return if no changes are pending. REAL --- Wait up to the given number of seconds until a change is noticed. Returns as soon as a change happened, or some time until the timeout runs out. You should call this function whenever you want to allow changing the connected devices, or whenever an existing device is disconnected. You must have called INIT prior to calling this function. See INIT See LIST-DEVICES
-
EXTERNAL FUNCTION POLL-EVENTS
- DEVICE
- FUNCTION
- &KEY
- TIMEOUT
Queries the device for events. The FUNCTION is called with any new device events that arrive during querying. The events are not guaranteed to be fresh and you MUST NOT store them anywhere. You should instead translate the event into a format more suitable for your use case or consume it on the spot. TIMEOUT can be of one of the following: T --- Continuously poll for device events indefinitely. NIL --- Immediately return if no events are pending. REAL --- Wait up to the given number of seconds until an event is noticed. Returns as soon as an event happened, or some time until the timeout runs out. You should call this function whenever you wish to query the gamepad for new changes in its buttons or axes. This function may signal an error of type GAMEPAD-ERROR. When this happens the device has become unavailable for some reason. You should remove the device from your own references and invoke the restart DROP-DEVICE which will remove the device from the internal library and abort the query. You must have called INIT prior to calling this function. See INIT See DEVICE See EVENT
-
EXTERNAL FUNCTION RAMP
- DEVICE
- AXIS
-
EXTERNAL FUNCTION (SETF RAMP)
- RAMP
- DEVICE
- AXIS
No documentation provided. -
EXTERNAL FUNCTION REMOVE-DEVICE-MAPPING
- ID
Removes the device mapping associated with the given ID. See DEVICE-MAPPING
-
EXTERNAL FUNCTION RUMBLE
- DEVICE
- STRENGTH
- &KEY
- PAN
Causes haptic feedback on the controller. STRENGTH should be a number in [0,1] denoting the strength of the haptic feedback. PAN should be a number in [-1,+1] denoting where (from left to right) to rumble. Note that haptic feedback is wildly inconsistent across devices and drivers, and may be completely unsupported, or not behave exactly as described here. If the haptic feedback is unsupported, this function returns :UNSUPPORTED. The rumbling will last at least 0.1 seconds, and may go on indefinitely. You must modulate the rumbling manually, or stop it completely by setting the strength to zero. You must have called INIT prior to calling this function. See INIT See DEVICE
-
EXTERNAL FUNCTION SAVE-DEVICE-MAPPINGS
- &OPTIONAL
- FILE
Saves all known device mappings to a lisp source file. The source file can simply be LOADed in to restore the mappings. See *DEFAULT-MAPPINGS-FILE*
-
EXTERNAL FUNCTION SHUTDOWN
Uninitialises the library and closes all used resources. This will not unload system shared objects, but it will close all used devices and other services and deallocate them. You should call this function when shutting down on a target system. After calling this function you MUST NOT call any other device functions without first calling INIT again. It is safe to call this function multiple times. See INIT
-
EXTERNAL GENERIC-FUNCTION DRIVER
- OBJECT
Returns a symbol identifying the driver underlying the device. May be one of: :EVDEV --- (Linux evdev) :DINPUT --- (Windows DirectInput) :XINPUT --- (Windows XInput) :IOKIT --- (Apple IOKit) See DEVICE
-
EXTERNAL GENERIC-FUNCTION NAME
- OBJECT
Returns a human-readable name for the device. No guarantees can be made about the quality or descriptiveness of the name. See DEVICE
-
EXTERNAL GENERIC-FUNCTION PRODUCT
- OBJECT
Returns an integer ID identifying the device product. See DEVICE
-
EXTERNAL GENERIC-FUNCTION VENDOR
- OBJECT
Returns an integer ID identifying the vendor of the device. See DEVICE
-
EXTERNAL GENERIC-FUNCTION VERSION
- OBJECT
Returns an integer denoting the version of the device. See DEVICE
-
EXTERNAL MACRO DEFINE-DEVICE-MAPPING
- DRIVER
- VENDOR
- PRODUCT
- &BODY
- PLIST
Defines a new device mapping for the named device type. This is a shorthand around (SETF DEVICE-MAPPING). The BODY should be a plist with :NAME :BUTTONS and :AXES keys. For buttons and axes, the value should be a plist from device IDs to labels. See DEVICE-MAPPING
-