controller.hpp¶
IController — the V5 game controller behind the HAL (chunk R1a, absorbing Phase T's T1): the INPUT half of driver control.
This header declares 4 types (26 members).
Extracted from include/shulib/hal/controller.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.
Contents¶
enum class ControllerAxisLeftXLeftYRightXRightYenum class ControllerButtonL1L2R1R2UpDownLeftRightXBYAclass IController~IControllerIControllerIController (overload 2)IController (overload 3)operator=operator= (overload 2)axispressedisConnectedclass ButtonEdgeupdate
enum class ControllerAxis¶
The four analog stick channels, named by physical position.
enum class, declared at include/shulib/hal/controller.hpp:38.
ControllerAxis::LeftX¶
left stick, horizontal (+ = pushed right)
enumerator, declared at include/shulib/hal/controller.hpp:39.
ControllerAxis::LeftY¶
left stick, vertical (+ = pushed up)
enumerator, declared at include/shulib/hal/controller.hpp:40.
ControllerAxis::RightX¶
right stick, horizontal (+ = pushed right)
enumerator, declared at include/shulib/hal/controller.hpp:41.
ControllerAxis::RightY¶
right stick, vertical (+ = pushed up)
enumerator, declared at include/shulib/hal/controller.hpp:42.
enum class ControllerButton¶
The twelve driver-usable buttons. The power button is deliberately absent: pressing it turns the controller off, so no routine may ever bind it.
enum class, declared at include/shulib/hal/controller.hpp:47.
ControllerButton::L1¶
left shoulder, upper trigger (PROS's "first" left trigger)
enumerator, declared at include/shulib/hal/controller.hpp:48.
ControllerButton::L2¶
left shoulder, lower trigger (PROS's "second" left trigger)
enumerator, declared at include/shulib/hal/controller.hpp:49.
ControllerButton::R1¶
right shoulder, upper trigger
enumerator, declared at include/shulib/hal/controller.hpp:50.
ControllerButton::R2¶
right shoulder, lower trigger
enumerator, declared at include/shulib/hal/controller.hpp:51.
ControllerButton::Up¶
left arrow pad, up
enumerator, declared at include/shulib/hal/controller.hpp:52.
ControllerButton::Down¶
left arrow pad, down
enumerator, declared at include/shulib/hal/controller.hpp:53.
ControllerButton::Left¶
left arrow pad, left
enumerator, declared at include/shulib/hal/controller.hpp:54.
ControllerButton::Right¶
left arrow pad, right
enumerator, declared at include/shulib/hal/controller.hpp:55.
ControllerButton::X¶
Right button pad, TOP of the diamond. These four are declared X, B, Y, A — PROS's own order, which is neither alphabetical nor a walk around the pad, so take each button's position from these notes and never from the order.
enumerator, declared at include/shulib/hal/controller.hpp:59.
ControllerButton::B¶
right button pad, RIGHT of the diamond
enumerator, declared at include/shulib/hal/controller.hpp:60.
ControllerButton::Y¶
right button pad, LEFT of the diamond
enumerator, declared at include/shulib/hal/controller.hpp:61.
ControllerButton::A¶
right button pad, BOTTOM of the diamond
enumerator, declared at include/shulib/hal/controller.hpp:62.
class IController¶
The INPUT half of driver control behind the HAL: normalized sticks, LEVEL buttons, and a positive connected/not signal. Canonical at the seam — the V5's raw −127…127 is scaled away in the adapter, and edge detection is refused here on purpose (see ButtonEdge below). One instance per PHYSICAL controller: VEX U's two drivers are a CONSTRUCTION fact (the PROS adapter takes a ControllerId), never an argument here.
class, declared at include/shulib/hal/controller.hpp:70.
IController::~IController¶
Public defaulted special members on a polymorphic base: the virtual destructor makes delete through an IController* well-defined, and copy/move stay available so an adapter deriving from this is free to be value-like. This base carries no state, so copying one copies nothing — hold implementations by reference or pointer, never by value.
function, declared at include/shulib/hal/controller.hpp:77.
IController::IController¶
Covered by the comment on ~IController — one comment documents this run of special members.
function, declared at include/shulib/hal/controller.hpp:78.
IController::IController (overload 2)¶
Covered by the comment on ~IController — one comment documents this run of special members.
function, declared at include/shulib/hal/controller.hpp:79.
IController::IController (overload 3)¶
Covered by the comment on ~IController — one comment documents this run of special members.
function, declared at include/shulib/hal/controller.hpp:80.
IController::operator=¶
Covered by the comment on ~IController — one comment documents this run of special members.
function, declared at include/shulib/hal/controller.hpp:81.
IController::operator= (overload 2)¶
Covered by the comment on ~IController — one comment documents this run of special members.
function, declared at include/shulib/hal/controller.hpp:82.
IController::axis¶
Current deflection of axis, normalized to [-1, 1] (full deflection = ±1). Reads 0.0 while disconnected — check isConnected() to tell a drop from centred sticks.
function, declared at include/shulib/hal/controller.hpp:87.
IController::pressed¶
True while button is held down (a LEVEL, not an edge — see header: per-consumer edge detection lives in ButtonEdge, above the seam).
function, declared at include/shulib/hal/controller.hpp:91.
IController::isConnected¶
True while this controller is connected and its readings are live. POSITIVE polarity by convention (cf. IImu::isReady, IGps::hasFix).
function, declared at include/shulib/hal/controller.hpp:95.
class ButtonEdge¶
Per-consumer rising-edge detector over IController::pressed() levels — the PROS-free replacement for get_digital_new_press(), which consumes the event on read so a second consumer silently misses every press (HA-104). Each consumer owns its own ButtonEdge, so N consumers all see the same press. Stateful by nature (it must remember the previous level); call update() exactly once per tick per consumer.
class, declared at include/shulib/hal/controller.hpp:105.
ButtonEdge::update¶
Feed this tick's level; returns true exactly on a false→true transition. The first call after construction reports a press only if the button is already down (prev starts false) — a button held across a mode switch registers once, deliberately, rather than being swallowed.
function, declared at include/shulib/hal/controller.hpp:111.
Design commentary, from the header¶
The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.
The header’s own reasoning — 32 lines
IController — the V5 game controller behind the HAL (chunk R1a, absorbing
Phase T's T1): the INPUT half of driver control. The frozen F6 facade already
has the OUTPUT half — drive(ChassisSpeeds, Frame) — so this seam is what
makes the library a one-stop shop for teleop without a second library.
Canonical at the seam, like every other HAL edge (§7):
* axis() is normalized to [-1, 1] — the V5's raw −127…127 never reaches the
core (controller_conversion.hpp owns the one scale; HA-103).
* pressed() is a plain bool LEVEL (is it down NOW). Edge detection ("was it
JUST pressed") deliberately lives ABOVE the seam, in ButtonEdge below —
PROS's get_digital_new_press() CONSUMES the event on read, so with two
consumers one silently loses. The adapter binds get_digital() and NEVER
get_digital_new_press() (HA-104); each consumer owns its own ButtonEdge.
* isConnected() is a POSITIVE validity signal (true = usable), matching
IImu::isReady() / IGps::hasFix() polarity (imu.hpp:31-33) — a controller
really does drop mid-match, and the core must be able to tell that from
"sticks centred" (PROS reports 0 on every channel when disconnected, so
without this signal the two are indistinguishable).
PARTNER SUPPORT is structural, not a retrofit: VEX U runs two drivers, so
"which controller" is a CONSTRUCTION question (make two instances — the
hal/pros adapter takes ControllerId::Master or ControllerId::Partner), never
an interface question. Retrofitting a second driver through a single-
controller seam is the reshape a seam exists to prevent.
NOT part of the F4 freeze (that register row locked 2026-06-19 with the ten
runtime interfaces; this seam is R1a's addition, outside it — the same
additive-sibling shape F1 used for IDigitalOut, digital_out.hpp:22-24).
Register row F13 records the NON-freeze out loud (D2's lesson: silence in
that register reads as "frozen"). Freeze trigger: a second real consumer —
T2's driver-control layer is the first.
Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.