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)]):

FieldTypeDescription
key_name[u8; 32]Key name buffer (null-terminated, max 31 chars)
codeu32Raw key code
modifier_flagsu32Bit flags for active modifiers
pressedu81 = press, 0 = release
timestamp_msu64Unix timestamp in milliseconds

Modifier Flags:

ConstantValueDescription
MODIFIER_CTRL1 << 0Control key
MODIFIER_ALT1 << 1Alt key
MODIFIER_SHIFT1 << 2Shift key
MODIFIER_SUPER1 << 3Super/Windows/Cmd key
MODIFIER_HYPER1 << 4Hyper key
MODIFIER_META1 << 5Meta key

Methods:

MethodReturnsDescription
new(key, code, modifiers, pressed)KeyboardInputCreate from key name, code, modifier names, pressed state
key_name()StringGet key name from buffer
is_pressed()boolCheck if key is pressed
modifiers()Vec<String>Get all active modifiers as string names
has_modifier(name)boolCheck modifier by name ("Ctrl", "Alt", "Shift", "Super", "Hyper", "Meta")
is_ctrl()boolCtrl held
is_shift()boolShift held
is_alt()boolAlt 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)]):

FieldTypeDescription
joystick_idu32Controller ID (0, 1, 2, ...)
event_type[u8; 16]Event type: "button", "axis", "hat", "connection"
element_idu32Button/axis/hat number
element_name[u8; 32]Element name (null-terminated, max 31 chars)
valuef32Button: 0.0/1.0, Axis: -1.0 to 1.0, Hat: directional
pressedu8For buttons: 1 = pressed, 0 = released
timestamp_msu64Unix timestamp in milliseconds

Constructors:

ConstructorDescription
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:

MethodReturnsDescription
event_type()StringGet event type string
element_name()StringGet element name string
is_button()boolIs a button event
is_axis()boolIs an axis event
is_hat()boolIs a hat/D-pad event
is_connection_event()boolIs a connection event
is_pressed()boolButton is pressed
is_connected()boolController 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