Common APIs - approxeng.input

class approxeng.input.Controller(controls, node_mappings=None, dead_zone=None, hot_zone=None, ff_device=None)[source]

Superclass for controller implementations

Variables:
  • axes (approxeng.input.Axes) – All analogue axes. You can get the individual axis objects from this, but you shouldn’t ever need to do this, use methods on Controller instead!

  • buttons (approxeng.input.Buttons) – All buttons are managed by this object. This can be used to access Button objects representing buttons on the controller, but you will almost never need to do this - use the methods on Controller instead!

__init__(controls, node_mappings=None, dead_zone=None, hot_zone=None, ff_device=None)[source]

Populate the controller name, button set and axis set.

Parameters:
  • controls – A list of Button, CentredAxis, TriggerAxis and BinaryAxis instances

  • node_mappings – A dict from device name to a prefix which will be applied to all events from nodes with a matching name before dispatching the corresponding events. This is used to handle controller types which create multiple nodes in /dev/input by keying on the device names reported to evdev for each node. Nodes are grouped by physical or unique ID first so should, in an ideal world at least, all correspond to the same physical controller. This is necessary to support some controllers on modern kernels, particularly 4.15. If not specified, or none, then no per-node renaming is applied. Device names which do not appear in this map are not assigned a prefix, so it’s legitimate to only assign prefixes for ‘new’ functionality which has magically appeared in a later kernel. Similarly, this is ignored if there is only one device node bound to the controller instance, so the best practice is to leave the older mappings named simply by their code, and only use this to handle secondary device nodes such as motion sensors.

  • dead_zone – If specified, this is applied to all axes

  • hot_zone – If specified, this is applied to all axes

  • ff_device – If specified, this is a force feedback compatible device node, defaults to None

property battery_level: float | None

Read the battery capacity, if available, as a percentage. If not available, return None

check_presses() ButtonPresses[source]

Return the set of Buttons which have been pressed since this call was last made, clearing it as we do. This is a shortcut to doing ‘buttons.get_and_clear_button_press_history’

Returns:

A ButtonPresses instance which contains buttons which were pressed since this call was last made.

property connected: bool
Returns:

True if the controller object is associated correctly with a physical device, False otherwise. Use this to detect a loss of controller pairing.

property controls: dict
Returns:

A dict containing all the names of controls on this controller, this takes the form of a dict with two keys, axes and buttons, the values for each of which are lists of strings containing the names of each type of control.

property has_presses: bool
Returns:

True if there were button presses since the last check.

property has_releases: bool
Returns:

True if any buttons were released since the last check.

property presses: ButtonPresses

The ButtonPresses containing buttons pressed between the two most recent calls to check_presses()

read_led_value(led_name) int | None[source]

Read an existing LED value. This may or may not work depending on the underlying implementation. Requires a bound controller with the specified name present in its LED sys classes. Returns None if this does not apply.

Parameters:

led_name – Name of LED to query

Returns:

Integer value of LED, or None if either unbound or no such LED name

register_button_handler(button_handler, button_sname: str)[source]

Register a handler function which will be called when a button is pressed

Parameters:
  • button_handler – A function which will be called when any of the specified buttons are pressed. The function is called with the Button that was pressed as the sole argument.

  • button_sname – The sname of the button which should trigger the handler function

Returns:

A no-arg function which can be used to remove this registration

property releases: ButtonPresses

The ButtonPresses containing buttons released between the two most recent calls to check_presses()

rumble(milliseconds=1000)[source]

Trigger a force-feedback effect, compiling and sending to the device if necessary first, otherwise using an existing effect that’s already been compiled.

Parameters:

milliseconds – milliseconds of rumbling required

property sys_nodes: dict

Returns a dict of discovered sys nodes representing power and LED status for this controller. If the controller isn’t bound to a physical device, or there aren’t LED or power nodes available this returns an empty dict.

write_led_value(led_name: str, value: int)[source]

Write a value to a named LED. Does nothing if either we’re not bound to a device, or there’s no such LED name.

Parameters:
  • led_name – LED name within this device - use self.sys_nodes[‘leds’] keys to discover LED names.

  • value – Value to write, should be an integer.

class approxeng.input.CentredAxis(name, min_raw_value, max_raw_value, axis_event_code, dead_zone=0.0, hot_zone=0.0, sname=None)[source]

A single analogue axis on a controller where the expected output range is -1.0 to 1.0 and the resting position of the control is at 0.0, at least in principle.

__init__(name, min_raw_value, max_raw_value, axis_event_code, dead_zone=0.0, hot_zone=0.0, sname=None)[source]

Create a new CentredAxis - this will be done internally within the Controller sub-class.

Parameters:
  • name – A friendly name for the axis

  • min_raw_value – The value read from the event system when the axis is at its minimum value

  • max_raw_value – The value read from the event system when the axis is at its maximum value

  • axis_event_code – The evdev code for this axis, used to dispatch events to the axis from the event system

  • dead_zone – Size of the dead zone in the centre of the axis, within which all values will be mapped to 0.0

  • hot_zone – Size of the hot zones at the ends of the axis, where values will be mapped to -1.0 or 1.0

  • sname – The standard name for this axis, if specified

property raw_value: float

Get an uncorrected value for this axis

Returns:

a float value, negative to the left or down, and ranging from -1.0 to 1.0

receive_device_value(raw_value: int)[source]

Set a new value, called from within the joystick implementation class when parsing the event queue.

Parameters:

raw_value – the raw value from the joystick hardware

Internal:

reset()[source]

Reset calibration (max, min and centre values) for this axis specifically. Not generally needed, you can just call the reset method on the SixAxis instance.

Internal:

property value: float

Get a centre-compensated, scaled, value for the axis, taking any dead-zone into account. The value will scale from 0.0 at the edge of the dead-zone to 1.0 (positive) or -1.0 (negative) at the extreme position of the controller or the edge of the hot zone, if defined as other than 1.0. The axis will auto-calibrate for maximum value, initially it will behave as if the highest possible value from the hardware is 0.9 in each direction, and will expand this as higher values are observed. This is scaled by this function and should always return 1.0 or -1.0 at the extreme ends of the axis.

Returns:

a float value, negative to the left or down and ranging from -1.0 to 1.0

class approxeng.input.TriggerAxis(name: str, min_raw_value: int, max_raw_value: int, axis_event_code: int, dead_zone=0.0, hot_zone=0.0, sname: str | None = None, button_sname: str | None = None, button_trigger_value=0.5)[source]

A single analogue axis where the expected output range is 0.0 to 1.0. Typically this is used for triggers, where the resting position is 0.0 and any interaction causes higher values. Whether a particular controller exposes triggers as axes or as buttons depends on the hardware - the PS3 front triggers appear as buttons, the XBox One triggers as axes.

__init__(name: str, min_raw_value: int, max_raw_value: int, axis_event_code: int, dead_zone=0.0, hot_zone=0.0, sname: str | None = None, button_sname: str | None = None, button_trigger_value=0.5)[source]

Create a new TriggerAxis - this will be done internally within the Controller sub-class.

Parameters:
  • name – A friendly name for the axis

  • min_raw_value – The value read from the event system when the trigger is not pressed

  • max_raw_value – The value read from the event system when the trigger is fully pressed

  • axis_event_code – The evdev code for this axis, used when dispatching events to it from an Axes object

  • dead_zone – The proportion of the trigger range which will be treated as equivalent to no press

  • hot_zone – The proportion of the trigger range which will be treated as equivalent to fully depressing the trigger

  • sname – The standard name for this trigger, if specified

  • button_sname – If provided, this creates a new Button internally which will be triggered by changes to the axis value. This is useful for triggers which have axis representations but no corresponding button presses such as the XBox1 controller front triggers. If this is set to None then no button is created

  • button_trigger_value – Defaulting to 0.5, this value determines the point in the trigger axis’ range at which point the button is regarded as being pressed or released.

property raw_value: float

Get an uncorrected value for this trigger

Returns:

a float value, 0.0 when not pressed, to 1.0 when fully pressed

receive_device_value(raw_value: int)[source]

Set a new value, called from within the joystick implementation class when parsing the event queue.

Parameters:

raw_value – the raw value from the joystick hardware

Internal:

reset()[source]

Reset calibration (max, min and centre values) for this axis specifically.

Internal:

property value: float

Get a centre-compensated, scaled, value for the axis, taking any dead-zone into account. The value will scale from 0.0 at the edge of the dead-zone to 1.0 (positive) at the extreme position of the trigger or the edge of the hot zone, if defined as other than 1.0.

Returns:

a float value, 0.0 when not pressed or within the dead zone, to 1.0 when fully pressed or in the hot zone

class approxeng.input.BinaryAxis(name, axis_event_code, b1name=None, b2name=None)[source]

A fake ‘analogue’ axis which actually corresponds to a pair of buttons. Once associated with a Buttons instance it routes events through to the Buttons instance to create button presses corresponding to axis movements. This is necessary as some controllers expose buttons, especially D-pad buttons, as a pair of axes rather than four buttons, but we almost certainly want to treat them as buttons the way most controllers do.

__init__(name, axis_event_code, b1name=None, b2name=None)[source]

Create a new binary axis, used to route axis events through to a pair of buttons, which are created as part of this constructor

Parameters:
  • name – Name for the axis, use this to describe the axis, it’s not used for anything else

  • axis_event_code – The evdev event code for changes to this axis

  • b1name – The sname of the button corresponding to negative values of the axis.

  • b2name – The sname of the button corresponding to positive values of the axis

receive_device_value(raw_value: int)[source]

Receive a value from the underlying operating system code, in our case evdev, and update the internal state of this axis object appropriately.

Parameters:

value – Integer value received from the evdev event handler.

property value

You probably don’t want to actually get the value of this axis, use the generated buttons instead.

Returns int:

The raw value from the evdev events driving this axis.

class approxeng.input.CircularCentredAxis(x: CentredAxis, y: CentredAxis, dead_zone=0.1, hot_zone=0.1)[source]

An aggregation of a pair of CentredAxis instances.

When using a pair of centred axes to model a single joystick there are some unexpected and probably undesirable issues with dead zones. As each axis is treated independently, the dead zones are also applied independently - this means that, for example, with the joystick fully pushed forwards you still have the dead zone behaviour between left and right. You may prefer a behaviour where both axes are zero if the stick is within a certain distance of its centre position in any direction. This class provides that, and is created from a pair of centred axes, i.e. ‘lx’ and ‘ly’. The value is returns is a tuple of (x,y) positions. Use of this class will constrain the overall motion of the paired axes into the unit circle - in many controllers this is true because of the physical layout of the controller, but it may not always be in hardware terms.

__init__(x: CentredAxis, y: CentredAxis, dead_zone=0.1, hot_zone=0.1)[source]

Create a new circular centred axis

Parameters:
  • x (CentredAxis) – Axis to use for x value

  • y (CentredAxis) – Axis to use for y value

  • dead_zone (float) – Specifies the distance from the centre prior to which both x and y will return 0.0, defaults to 0.1

  • hot_zone (float) – Specifies the distance from the 1.0 distance beyond which both x and y will return +-1.0, i.e. if the hot zone is set to 0.1 then all positions where the distance is greater than 0.9 will return magnitude 1 total distances. Defaults to 0.1

class approxeng.input.Axes(axes)[source]

A set of TriggerAxis or CentredAxis instances to which events should be routed based on event code. Contains methods to reset calibration on all axes, or to centre all axes for which this is meaningful.

__init__(axes)[source]

Create a new Axes instance, this will be done within the controller classes, you never have to explicitly instantiate this yourself.

Parameters:

axes – a sequence of approxeng.input.TriggerAxis or approxeng.input.CentredAxis or approxeng.input.BinaryAxis containing all the axes the controller supports.

property active_axes: ['Axis']

Return a sequence of all Axis objects which are not in their resting positions

axis_updated(event: InputEvent, prefix=None)[source]

Called to process an absolute axis event from evdev, this is called internally by the controller implementations

Internal:

Parameters:
  • event – The evdev event to process

  • prefix – If present, a named prefix that should be applied to the event code when searching for the axis

property names: list[str]

The snames of all axis objects

reset_axis_calibration(*args)[source]

Resets any previously defined axis calibration to 0.0 for all axes

set_axis_centres(*args)[source]

Sets the centre points for each axis to the current value for that axis. This centre value is used when computing the value for the axis and is subtracted before applying any scaling. This will only be applied to CentredAxis instances

class approxeng.input.Button(name, key_code=None, sname=None)[source]

A single button on a controller

__init__(name, key_code=None, sname=None)[source]

Create a new Button - this will be done by the controller implementation classes, you shouldn’t create your own unless you’re writing such a class.

Parameters:
  • name – A friendly name for the button

  • key_code – The key code for the button, typically an integer used within the button press and release events. Defaults to None if not used.

  • sname – The standard name for the button, if available.

class approxeng.input.Buttons(buttons_and_axes)[source]

A set of buttons on a controller. This class manages event binding and triggering, as well as monitoring button states and tracking whether buttons are held down, and how long if so. Controller implementations instantiate and configure an instance of this class when they want to provide button information, the controller is responsible for translating button events from the underlying operating system frameworks and updating this object appropriately, user code (i.e. your code if you’re reading this) uses the methods on this object to react to button presses.

class ButtonState(button)[source]

Per-button state, including any handlers registered, whether the button was pressed since the last call to check, whether it is currently pressed, and the timestamp of the last button press. From this we can handle all possible forms of interaction required.

__init__(button)[source]
__init__(buttons_and_axes)[source]

Instantiate a new button manager

Parameters:

buttons_and_axes – a list of approxeng.input.Button instances which will be managed by this class

button_pressed(key_code, prefix=None)[source]

Called from the controller classes to update the state of this button manager when a button is pressed.

Internal:

Parameters:
  • key_code – The code specified when populating Button instances

  • prefix – Applied to key code if present

button_released(key_code, prefix=None)[source]

Called from the controller classes to update the state of this button manager when a button is released.

Internal:

Parameters:
  • key_code – The code specified when populating Button instance

  • prefix – Applied to key code if present

check_presses()[source]

Return the set of Buttons which have been pressed since this call was last made, clearing it as we do.

Returns:

A ButtonPresses instance which contains buttons which were pressed since this call was last made.

held(sname)[source]

Determines whether a button is currently held, identifying it by standard name

Parameters:

sname – The standard name of the button

Returns:

None if the button is not held down, or is not available, otherwise the number of seconds as a floating point value since it was pressed

property names

The snames of all button objects

property presses

Get the ButtonPresses containing buttons pressed between the most recent two calls to check_presses. This will call the check_presses method if it has never been called before, and is therefore always safe even if your code has never called the update function. To make this property actually do something useful, however, you do need to call check_presses, preferably once immediately before you then want to handle any button presses that may have happened.

Returns:

a ButtonPresses object containing information about which buttons were pressed

register_button_handler(button_handler, buttons)[source]

Register a handler function which will be called when a button is pressed

Parameters:
  • button_handler – A function which will be called when any of the specified buttons are pressed. The function is called with the Button that was pressed as the sole argument.

  • buttons ([Button]) – A list or one or more buttons which should trigger the handler when pressed. Buttons are specified as approxeng.input.Button instances, in general controller implementations will expose these as constants such as SixAxis.BUTTON_CIRCLE. A single Button can be specified if only one button binding is required.

Returns:

A no-arg function which can be used to remove this registration

property releases

Analogous to presses, but returns the set of buttons which were released

Returns:

a ButtonPresses object containing information about which buttons were released

class approxeng.input.ButtonPresses(buttons)[source]

Stores the set of buttons pressed within a given time interval

__init__(buttons)[source]