Medius - Rust LibraryCalls & input

Three kinds of call

Fire-and-forget, blocking query, no round-trip

Every Device method is one of three kinds, tagged with a badge on the API pages.

Fire-and-forget

Writes one frame, returns once the bytes are out, no reply.

EXAMPLE
device.move_rel(100, -50)?; // one frame out, no reply

Blocks

Sends a QUERY and waits for the correlated RESP.

EXAMPLE
let v = device.query_version()?; // waits for the box to reply

No round-trip

Reads state the library already holds; can't fail on the link.

EXAMPLE
let c = device.counters(); // local snapshot, no network

Why the queries are async

Queries await a reply, everything else fires and forgets

With the async feature, the QUERY methods are the async fns, because a query blocks for its correlated RESP. Every other method is fire-and-forget, so it stays synchronous.

METHOD SPLIT
Async (you .await it)Stays sync (no .await)
query_version, query_health, device_info, caps, query_rate, query_stats, query_locks, query_catch, the option query_* methods, and the clip status querymove_rel, wheel, inject, press, release, force_release, reset, reboot, led, lock, unlock, catch_events, and the clip append / start / stop

Driving futures without a runtime

futures::executor::block_on

block_on runs one future to completion on the current thread, so you can await a query with no async runtime at all.

EXAMPLE
use futures::executor::block_on;

let device = Device::find()?.into_async();
let v = block_on(device.query_version())?;
println!("{v}");

Inside an async main, .await the same future instead; it runs unchanged under tokio, async-std, or smol.

When the box is silent

Default timeout and QueryTimeout

A query waits DEFAULT_QUERY_TIMEOUT (1 second), then returns Err(Error::QueryTimeout). This applies to both the sync and async queries.

EXAMPLE
match device.query_health() {
    Ok(h) => println!("{h:?}"),
    Err(medius::Error::QueryTimeout) => eprintln!("no reply in time"),
    Err(e) => return Err(e),
}

QueryTimeout is a query hitting its deadline; NoReply is the handshake's own silence. Both are on the Errors page.

Smooth motion

Subdivide the delta, pace the steps

move_rel applies one delta at once. To spread it over time, subdivide the move and pace the steps yourself, roughly one per millisecond. There's no move_smooth.

EXAMPLE
use std::thread::sleep;
use std::time::Duration;

// Glide ~400 counts to the right over 200 steps (~200 ms at 1 kHz).
for _ in 0..200 {
    device.move_rel(2, 0)?;
    sleep(Duration::from_millis(1));
}

The library applies no rate limit. A no-sleep loop queues frames faster than 4 Mbaud drains; pace your own steps.

Making a click

Press, wait, release

There's no one-shot click: press, wait, then release with release which clears only the box's own override.

EXAMPLE
use std::{thread, time::Duration};
use medius::Button;

device.press(Button::Left)?;
thread::sleep(Duration::from_millis(20));
device.release(Button::Left)?;

reset drops every override at once; a held press is re-asserted on reconnect via reapply.