simvx.core.input.devices¶
The devices under the player, and the pads among them as objects.
Input.devices is a mapping of the engine’s id to every device the machine
has, each an :class:InputDevice, and Input.controllers is the pads among
them, each a :class:ControllerDevice. A ControllerDevice answers the questions
a game asks of one pad: what it is called, which family
of controller it belongs to (so a prompt can say Cross rather than A), whether
its buttons carry the engine’s standard meanings, what is held on it, where its
sticks are, and whether it can be made to vibrate.
A device is a live view, not a snapshot. It holds the store and its own id and reads through on every query, so the object a game stored at connection never goes stale and a per-frame poll allocates nothing beyond the
- class:
~simvx.core.math.types.Vec2a stick has always returned. A snapshot would have to be rebuilt every frame by everyone who kept one, and the cost of getting that wrong is a game reading last frame’s stick forever.
A pad that disconnects answers, it does not raise. It leaves
Input.controllers the moment the poll that lost it closes, so a game
enumerating pads never sees one mid-teardown; but a ControllerDevice a game is still
holding keeps working and reports a pad with nothing held: :attr:connected is
False, every button is up, every axis is zero, and :meth:rumble does nothing
and returns False. The release edges the disconnect produced are readable for
the rest of that frame, which is what lets a game stop what the player was
doing rather than run on the last reading.
The device record is process-wide, held once in :data:store and read
through every tree’s Input: the held keys and mouse buttons, the fingers on
the glass, the pads and the input method. A key is down on the machine and a
controller is plugged into the machine, not into a scene tree, so an editor and
the game it is running agree about both. Action state, contexts and focus are
the other half and stay per tree, resolved from the tree’s own delivered
record of what actually reached it (see
- class:
~simvx.core.input.state._Input), which is why a key the editor never forwarded presses nothing in the game whileInput.is_key_pressedthere still says the machine is holding it.
A test isolates devices in time, not in space. There is no second store to
run a test against, in the same way that there is no second machine; what a
test has instead is :func:reset, which puts the pads and the input method
back as they were at process start so an injected pad cannot leak into the next
test. Input._reset() calls it, so a test that already resets input needs
nothing further, and a test that only wants the hardware facts cleared calls it
directly. Its sibling is
- func:
simvx.core.input.capabilities.reset, which does the same for the capability flags and is deliberately separate: a capability is declared once by a backend and a pad arrives and leaves many times.
Module Contents¶
Classes¶
What an axis’s hardware reports: where it is, how far it moved, or both. |
|
One of a device’s buttons, as a rebind screen lists it. |
|
One of a device’s axes, as a rebind screen lists it. |
|
One device: the keyboard, the mouse, a touchscreen, a pen, a tablet. |
|
One connected pad. Read it through |
Functions¶
Which controller family a pad belongs to, from the name its driver reports. |
|
The change from before to now on a scale that wraps at 1.0, the short way round. |
|
Validate what a backend announced for a device’s axes. |
|
Put the pads and the input method back as they were at process start. |
|
Run a guest tree’s frame without spending the devices’ frame. |
Data¶
API¶
- simvx.core.input.devices.__all__¶
[‘AxisInfo’, ‘AxisReport’, ‘ButtonInfo’, ‘ControllerDevice’, ‘InputDevice’, ‘family_for’, ‘hold_fram…
- simvx.core.input.devices.STICK_DEADZONE¶
0.15
- simvx.core.input.devices.family_for(name: str, guid: str = '', *, recognised: bool = True) simvx.core.input.enums.ControllerFamily[source]¶
Which controller family a pad belongs to, from the name its driver reports.
The family decides what a prompt draws, and nothing else: the engine’s controls are positional either way, so
Controller.Ais the bottom face button on every pad and only its printed label moves.A pad the engine cannot lay out as a standard controller (
recognisedFalse) is- Attr:
~simvx.core.input.enums.ControllerFamily.JOYSTICKwhatever it is called, because naming its buttons after a controller’s would be a guess. One that is laid out but whose name reveals no family is- Attr:
~simvx.core.input.enums.ControllerFamily.GENERIC, which prompts with the engine’s own names.
- class simvx.core.input.devices.AxisReport[source]¶
Bases:
enum.StrEnumWhat an axis’s hardware reports: where it is, how far it moved, or both.
A ring reports an absolute angle on some drivers and deltas on others, so this is declared per axis by whatever connects the device.
Initialization
Initialize self. See help(type(self)) for accurate signature.
- ABSOLUTE¶
‘absolute’
- RELATIVE¶
‘relative’
- BOTH¶
‘both’
- __new__(*values)¶
- __add__()¶
- __contains__()¶
- __delattr__()¶
- __dir__()¶
- __eq__()¶
- __format__()¶
- __ge__()¶
- __getattribute__()¶
- __getitem__()¶
- __getnewargs__()¶
- __getstate__()¶
- __gt__()¶
- __hash__()¶
- __iter__()¶
- __le__()¶
- __len__()¶
- __lt__()¶
- __mod__()¶
- __mul__()¶
- __ne__()¶
- __reduce__()¶
- __reduce_ex__()¶
- __repr__()¶
- __rmod__()¶
- __rmul__()¶
- __setattr__()¶
- __sizeof__()¶
- __str__()¶
- __subclasshook__()¶
- capitalize()¶
- casefold()¶
- center()¶
- count()¶
- encode()¶
- endswith()¶
- expandtabs()¶
- find()¶
- format()¶
- format_map()¶
- index()¶
- isalnum()¶
- isalpha()¶
- isascii()¶
- isdecimal()¶
- isdigit()¶
- isidentifier()¶
- islower()¶
- isnumeric()¶
- isprintable()¶
- isspace()¶
- istitle()¶
- isupper()¶
- join()¶
- ljust()¶
- lower()¶
- lstrip()¶
- partition()¶
- removeprefix()¶
- removesuffix()¶
- replace()¶
- rfind()¶
- rindex()¶
- rjust()¶
- rpartition()¶
- rsplit()¶
- rstrip()¶
- split()¶
- splitlines()¶
- startswith()¶
- strip()¶
- swapcase()¶
- title()¶
- translate()¶
- upper()¶
- zfill()¶
- __deepcopy__(memo)¶
- __copy__()¶
- name()¶
- value()¶
- class simvx.core.input.devices.ButtonInfo[source]¶
One of a device’s buttons, as a rebind screen lists it.
Attributes: index: The device’s own index. control: What the layout in force calls it: a name, or the index form. label: What to print for it.
- index: int¶
None
- control: simvx.core.input.controls.DeviceButton¶
None
- label: str¶
None
- class simvx.core.input.devices.AxisInfo[source]¶
One of a device’s axes, as a rebind screen lists it.
Attributes: index: The device’s own index. control: What the layout in force calls it. label: What to print for it. report: What the hardware reports for it. wraps: Whether its position wraps at 1.0, as a ring’s does, which is what makes a delta across the wrap the short way round.
- index: int¶
None
- control: simvx.core.input.controls.DeviceAxis¶
None
- label: str¶
None
- report: simvx.core.input.devices.AxisReport¶
None
- wraps: bool¶
None
- simvx.core.input.devices.wrapped_delta(now: float, before: float) float[source]¶
The change from before to now on a scale that wraps at 1.0, the short way round.
- simvx.core.input.devices.check_axes(reports: collections.abc.Sequence[simvx.core.input.devices.AxisReport | str], wraps: collections.abc.Collection[int]) tuple[tuple[simvx.core.input.devices.AxisReport, ...], frozenset[int]][source]¶
Validate what a backend announced for a device’s axes.
- class simvx.core.input.devices.InputDevice(store: simvx.core.input.devices._DeviceStore, device_id: int)[source]¶
One device: the keyboard, the mouse, a touchscreen, a pen, a tablet.
A live view, like :class:
Controller: it holds the store and its own id and reads through on every query, so a device a game kept keeps answering, and one that has gone reports nothing held.Read them through
Input.devices. A rebind screen lists :attr:buttonsand :attr:axesand prints :meth:label.Initialization
- __slots__¶
(‘_id’, ‘_store’)
- property id: int[source]¶
The engine’s id for the device, which every event it produces carries as
device_id.
- property type: simvx.core.input.controls.DeviceType[source]¶
What type of device this is.
- property guid: str[source]¶
The model’s stable id, the same for every unit of it, or
""where the backend has none.
- property buttons: tuple[simvx.core.input.devices.ButtonInfo, ...][source]¶
Every button the device reported, with the control it is and its label.
As many as the backend announced, or as many as the layout names where it announced none.
- property axes: tuple[simvx.core.input.devices.AxisInfo, ...][source]¶
Every axis the device reported, with the control it is, its label and what it reports.
- label(control: simvx.core.input.controls.DeviceControl) str[source]¶
What this device prints on control, for a rebind screen or a prompt.
""for a control this device’s layout does not have.
- pressed(button: simvx.core.input.controls.DeviceButton) bool[source]¶
Whether button is held on this device right now.
- axis(axis: simvx.core.input.controls.DeviceAxis) float | None[source]¶
Where axis is, or None for an axis that reports only motion, or that nothing names.
- axis_delta(axis: simvx.core.input.controls.DeviceAxis) float[source]¶
How far axis moved this frame, in the same units as its position.
- class simvx.core.input.devices.ControllerDevice(store: simvx.core.input.devices._DeviceStore, device_id: int)[source]¶
Bases:
simvx.core.input.devices.InputDeviceOne connected pad. Read it through
Input.controllers[device_id].A game normally binds actions and never touches this; it is here for what a binding cannot express: which pad a local-multiplayer player is holding, what to draw as a prompt, and rumble. It is a device like any other in
Input.devices, with what only a pad has besides.Initialization
- __slots__¶
()
- property source: simvx.core.input.enums.ControllerSource[source]¶
Where the pad comes from: real hardware, a test, or the on-screen pad.
- property family: simvx.core.input.enums.ControllerFamily[source]¶
Which controller family it belongs to, which is what a prompt draws.
- property recognised: bool[source]¶
Whether a controller database recognised this device and laid it out in the standard positions.
Recognised,
Controller.Ais its bottom face button. Not, it has only the driver’s numbered controls,Controller.button(i)andController.axis(i), until the player maps names onto them:InputMap.rebind(Controller.A, Controller.button(3).on(device))makesController.Athat device’s button 3, for actions, handlers and these queries alike, and is saved with the player’s rebinds. Until then a named query on it raises, naming what to do, and an action bound to a name reads at rest on it.
- property connected: bool[source]¶
Whether the pad is still there.
False for a pad that has gone: every query below then answers as an untouched pad rather than raising, so a game holding this object across the disconnect keeps running.
- pressed(button: simvx.core.input.controls.DeviceButton) bool[source]¶
Whether button is held on this controller right now.
Raises
ValueErrorfor a named control (Controller.A) on a controller that is not :attr:recognisedand that the player has not mapped it on.
- axis_delta(axis: simvx.core.input.controls.DeviceAxis) float[source]¶
How far axis moved this frame; a named axis on an unrecognised controller raises, as :meth:
pressed.
- just_pressed(button: simvx.core.input.controls.DeviceButton) bool[source]¶
Did button go down on this pad during this frame?
Inside a physics tick, during the frame that tick opens: the frame’s edges belong to its first tick and to no other, the same rule every other edge query follows, so a press fires once whatever the display and the tick rate are doing.
- just_released(button: simvx.core.input.controls.DeviceButton) bool[source]¶
Did button come up on this pad during this frame?
True for every button a pad was holding when it was unplugged, for the frame the disconnect happened on. Tick-scoped inside a tick, as
- Meth:
just_pressed.
- axis(axis: simvx.core.input.controls.DeviceAxis) float[source]¶
One axis in the engine’s convention, with no deadzone applied.
Sticks run -1.0 to 1.0 with +y down the screen; triggers run 0.0 released to 1.0 fully pulled. Raw because a deadzone is a gameplay decision: :meth:
stickapplies the radial one a stick wants, and an action binding applies its own. A named axis on a controller that is not :attr:recognisedraises, as :meth:presseddoes.
- stick(stick: simvx.core.input.controls.DeviceStick = Controller.LEFT_STICK, *, deadzone: float = STICK_DEADZONE) simvx.core.math.types.Vec2[source]¶
One stick as a vector, zeroed inside a radial deadzone.
Radial rather than per-axis, so a stick pushed diagonally is not clipped into a square: the whole vector is discarded when it is shorter than deadzone, and passed through unchanged when it is not.
- rumble(strong: float = 1.0, weak: float = 0.0, duration: float = 0.2) bool[source]¶
Vibrate the pad, returning whether anything happened.
Args: strong: The heavy low-frequency motor, 0.0 to 1.0. weak: The light high-frequency motor, 0.0 to 1.0. duration: Seconds to run for.
Returns: False when this pad cannot vibrate, when the backend supplies no rumble at all (
Input.capabilities.rumbleis then False), or when the pad has disconnected. A game may call it unconditionally.
- label(control: simvx.core.input.controls.DeviceControl, *, positive: bool | None = None) str[source]¶
What this pad prints on a button or an axis.
"A"on an Xbox pad,"Cross"on a PlayStation one and"B"on a Nintendo one, all forController.A: the controls are positional and only the printing moves.positivenames one half of a stick axis, which is what a binding splits it into. A control the pad’s layout does not name prints as its number.
- property id: int¶
- property type: simvx.core.input.controls.DeviceType¶
- property name: str¶
- property guid: str¶
- property buttons: tuple[simvx.core.input.devices.ButtonInfo, ...]¶
- property axes: tuple[simvx.core.input.devices.AxisInfo, ...]¶
- simvx.core.input.devices.store¶
‘_DeviceStore(…)’