Input Messages
HORUS provides input message types for teleoperation and human interface device (HID) control. Both types are fixed-size (72 bytes) with #[repr(C)] layout for zero-copy shared memory transport.
KeyboardInput
Keyboard key press/release events with modifier tracking.
use horus::prelude::*;
// Create a key press event
let key = KeyboardInput::new(
"w".to_string(), // key name
87, // raw key code
vec!["Shift".into()], // active modifiers
true // pressed
);
// Check key state
println!("Key: {}", key.key_name());
println!("Pressed: {}", key.is_pressed());
// Check individual modifiers
if key.is_ctrl() {
println!("Ctrl is held");
}
if key.is_shift() {
println!("Shift is held");
}
// Check any modifier by name
if key.has_modifier("Alt") {
println!("Alt is held");
}
// Get all active modifiers as a list
let mods = key.modifiers(); // Vec<String>
println!("Active modifiers: {:?}", mods);
Fields (72 bytes, #[repr(C)]):
| Field | Type | Description |
|---|---|---|
key_name | [u8; 32] | Key name buffer (null-terminated, max 31 chars) |
code | u32 | Raw key code |
modifier_flags | u32 | Bit flags for active modifiers |
pressed | u8 | 1 = press, 0 = release |
timestamp_ms | u64 | Unix timestamp in milliseconds |
Modifier Flags:
| Constant | Value | Description |
|---|---|---|
MODIFIER_CTRL | 1 << 0 | Control key |
MODIFIER_ALT | 1 << 1 | Alt key |
MODIFIER_SHIFT | 1 << 2 | Shift key |
MODIFIER_SUPER | 1 << 3 | Super/Windows/Cmd key |
MODIFIER_HYPER | 1 << 4 | Hyper key |
MODIFIER_META | 1 << 5 | Meta key |
Methods:
| Method | Returns | Description |
|---|---|---|
new(key, code, modifiers, pressed) | KeyboardInput | Create from key name, code, modifier names, pressed state |
key_name() | String | Get key name from buffer |
is_pressed() | bool | Check if key is pressed |
modifiers() | Vec<String> | Get all active modifiers as string names |
has_modifier(name) | bool | Check modifier by name ("Ctrl", "Alt", "Shift", "Super", "Hyper", "Meta") |
is_ctrl() | bool | Ctrl held |
is_shift() | bool | Shift held |
is_alt() | bool | Alt held |
JoystickInput
Gamepad/joystick events for buttons, axes, hats, and connection state.
use horus::prelude::*;
// Button press
let btn = JoystickInput::new_button(0, 1, "A".to_string(), true);
println!("Button {} pressed: {}", btn.element_name(), btn.is_pressed());
// Axis movement (value: -1.0 to 1.0)
let axis = JoystickInput::new_axis(0, 0, "LeftStickX".to_string(), 0.75);
println!("Axis {}: {:.2}", axis.element_name(), axis.value);
// Hat/D-pad
let hat = JoystickInput::new_hat(0, 0, "DPad".to_string(), 1.0);
// Controller connection event
let conn = JoystickInput::new_connection(0, true); // joystick 0 connected
if conn.is_connection_event() {
println!("Controller {}: connected={}", conn.joystick_id, conn.is_connected());
}
// Check event type
if axis.is_axis() {
println!("Axis event: {}", axis.value);
} else if btn.is_button() {
println!("Button event: pressed={}", btn.is_pressed());
}
Fields (72 bytes, #[repr(C)]):
| Field | Type | Description |
|---|---|---|
joystick_id | u32 | Controller ID (0, 1, 2, ...) |
event_type | [u8; 16] | Event type: "button", "axis", "hat", "connection" |
element_id | u32 | Button/axis/hat number |
element_name | [u8; 32] | Element name (null-terminated, max 31 chars) |
value | f32 | Button: 0.0/1.0, Axis: -1.0 to 1.0, Hat: directional |
pressed | u8 | For buttons: 1 = pressed, 0 = released |
timestamp_ms | u64 | Unix timestamp in milliseconds |
Constructors:
| Constructor | Description |
|---|---|
new_button(joystick_id, button_id, name, pressed) | Button press/release event |
new_axis(joystick_id, axis_id, name, value) | Axis movement event |
new_hat(joystick_id, hat_id, name, value) | Hat/D-pad event |
new_connection(joystick_id, connected) | Controller connect/disconnect |
Methods:
| Method | Returns | Description |
|---|---|---|
event_type() | String | Get event type string |
element_name() | String | Get element name string |
is_button() | bool | Is a button event |
is_axis() | bool | Is an axis event |
is_hat() | bool | Is a hat/D-pad event |
is_connection_event() | bool | Is a connection event |
is_pressed() | bool | Button is pressed |
is_connected() | bool | Controller is connected (connection events) |
Teleoperation Example
use horus::prelude::*;
struct TeleoperationNode {
keyboard_sub: Topic<KeyboardInput>,
joystick_sub: Topic<JoystickInput>,
cmd_vel_pub: Topic<Twist>,
linear_speed: f64,
angular_speed: f64,
}
impl Node for TeleoperationNode {
fn name(&self) -> &str { "Teleop" }
fn tick(&mut self) {
let mut linear = 0.0;
let mut angular = 0.0;
// Process keyboard (WASD)
if let Some(key) = self.keyboard_sub.recv() {
if key.is_pressed() {
match key.key_name().as_str() {
"w" => linear = self.linear_speed,
"s" => linear = -self.linear_speed,
"a" => angular = self.angular_speed,
"d" => angular = -self.angular_speed,
_ => {}
}
// Shift for boost
if key.is_shift() {
linear *= 2.0;
angular *= 2.0;
}
}
}
// Process joystick (overrides keyboard if present)
if let Some(joy) = self.joystick_sub.recv() {
if joy.is_axis() {
match joy.element_name().as_str() {
"LeftStickY" => linear = joy.value as f64 * self.linear_speed,
"RightStickX" => angular = joy.value as f64 * self.angular_speed,
_ => {}
}
}
}
self.cmd_vel_pub.send(Twist::new_2d(linear, angular));
}
}
See Also
- Sensor Messages - Lidar, IMU, GPS sensor data
- Control Messages - Motor and servo commands