stickup 0.2.1

A modular input device abstraction layer with HID and virtual device support.
Documentation

StickUp

๐Ÿš€ Update: v0.2.1 is here!
StickUp v0.2.1 adds per-frame input snapshots, intuitive binding resolution ("joy0.axis1"), and cleaner device control with DeviceManager.
Built to scale with sim rigs, overlays, game engines, and beyond.

๐Ÿ“ˆ Already over 1,000 downloads in under 48 hours โ€” huge thanks to everyone testing, sharing, and supporting the launch!

Crates.io Downloads Join the Discord Buy Me a Coffee Follow on X


๐ŸŽฎ What is StickUp?

StickUp is a modular, high-performance input framework for Rust.
It supports both real HID devices and virtual inputs with clarity, precision, and stability.

Part of the CelerisTech stack by Belegrade Studio


๐Ÿ” Security Note

The name stickup was previously used in 2023 for a malicious crate that has since been removed from crates.io.

This version โ€” authored by Belegrade Studio โ€” is a clean and fully rewritten project, unrelated to the original.

โœ… No build.rs
โœ… No network activity
โœ… 100% open and auditable

Transparency matters. Feel free to inspect the source or reach out directly.


โœจ Features

  • ๐Ÿ”Œ Plug-and-play device management via DeviceManager
  • ๐ŸŽฎ Unified Device trait for axis + button input
  • ๐Ÿง  Persistent device identity (hardware fingerprinting)
  • ๐Ÿงฐ Binding resolution like "joy0.axis1" โ†’ Option<f32>
  • ๐Ÿ” Snapshot-based polling and input state tracking
  • ๐Ÿ”ง Hotplug-friendly and fully extendable
  • ๐Ÿ›  Supports hid and virtual backends via optional features
  • ๐Ÿ’ก Zero magic โ€” minimal, intentional design

๐Ÿงญ Philosophy

StickUp is about presence, clarity, and persistence.
It doesnโ€™t guess. It doesnโ€™t simulate. It reflects exactly what your device is doing โ€” no more, no less.


๐Ÿ“ฆ Installation

stickup = { version = "0.2.1", features = ["hid", "virtual"] }

use stickup::DeviceManager;

fn main() {
    let mut input = DeviceManager::new();
    input.snapshot(); // poll + build snapshot

    if let Some(throttle) = input.get_axis("joy0.throttle") {
        println!("Throttle: {:.2}", throttle);
    }

    if input.is_pressed("joy1.trigger") {
        println!("Trigger is pressed!");
    }

    // Full snapshot usage
    let state = input.snapshot();

    for (id, device_state) in state.iter() {
        println!("Device: {id}");

        for (axis, value) in &device_state.axes {
            println!("  Axis {axis}: {value:.2}");
        }

        for (button, pressed) in &device_state.buttons {
            println!("  Button {button}: {}", if *pressed { "Pressed" } else { "Released" });
        }
    }
}

๐Ÿงฌ Device Identity

StickUp assigns a stable, persistent ID to each device:

vendor_id:product_id:serial_number
# Example: 044f:0402:ABCD1234

This allows for consistent bindings across reboots and USB port changes.


๐Ÿ” Examples

Run any with:

cargo run --example <name>
  • poll โ€“ Print a full snapshot of all input state
  • virtual_demo โ€“ Feed input into a simulated virtual device

๐Ÿ› ๏ธ Optional Features

Feature Description
hid (default) Enables HID device support via hidapi
virtual Enables manually fed virtual devices

๐Ÿ”ฎ Coming Next: Event Listeners

StickUp currently uses snapshot-based polling for simplicity and determinism.

Next release will introduce event listener support:
Low-latency, per-device events that let you respond to input as it happens โ€” perfect for overlays, sim triggers, and reactive UIs.

Example:

device.add_listener(Arc::new(MyEventHandler));

This will coexist with polling โ€” giving you full control and flexibility.


๐Ÿ“œ License

This project is licensed under the Pact of the Amaranth Rite.
See LICENSE for details.

Third-Party Dependencies

  • hidapi โ€” MIT/Apache-2.0 (HID support)
  • serde โ€” MIT/Apache-2.0 (serialization)
  • serde_json โ€” MIT/Apache-2.0 (layout/config IO)
  • toml โ€” MIT/Apache-2.0 (if config parsing used)

๐Ÿ’ฌ Connect