Medius - Rust LibraryOptions

Options

Persistent box settings

Five box settings, each set and read on its own. All persist in NVS and survive a reboot. See the native OPTION command for the wire contract.

OptionSetRead
imperfect cloneallow_imperfect_clonesquery_imperfect
movement ridingset_movement_ridingquery_movement_riding
bearingset_bearingquery_bearing
emit-rate pacingset_emit_pacequery_emit_pace
box nameset_name / clear_nameVersion::name

allow_imperfect_clones

Clone an over-capacity device anyway
fn allow_imperfect_clones(&self, allow: bool) -> Result<()>

Fire-and-forget

By default the box refuses a device it can't clone faithfully. true clones an over-capacity device anyway, the rest faithful and the over-capacity interface dead. Changing the setting while such a device is attached reboots the box to re-clone; a normal device is unaffected.

ParameterTypeDescription
allowboolClone an over-capacity device anyway, or stay faithful-only.
EXAMPLE
use medius::Device;

let device = Device::find()?;
device.allow_imperfect_clones(true)?;   // reboots + re-clones if an over-capacity device is attached

set_movement_riding

Inject motion only on a native move
fn set_movement_riding(&self, window: Option<Duration>) -> Result<()>

Fire-and-forget

Some(window) turns riding on: injected cursor and wheel motion ride a native cursor-motion report seen within window; the box emits no synthetic motion frame. Motion unridden past the window is dropped, not dumped on the next move. None (the default) is off.

The window rounds to whole milliseconds, a non-zero Some is at least 1 ms, and it clamps to 65535 ms.

Pure idle injection, moving the cursor while the user holds still, stops working while riding is on, unless a move opts out with move_rel_now. Button, key and media injection are unaffected.

A change to this setting drops whatever motion was held for a ride, and clears the standing bearing with it, so every With / Against scale stops applying at that instant.

ParameterTypeDescription
windowOption<Duration>Some with the ride window, or None to turn it off.
EXAMPLE
use std::time::Duration;
use medius::Device;

let device = Device::find()?;
device.set_movement_riding(Some(Duration::from_millis(20)))?;  // ride native moves
device.set_movement_riding(None)?;                             // back to gapless fill

set_bearing

What With and Against are measured against
fn set_bearing(&self, window: Option<Duration>, mode: BearingMode) -> Result<()>

Fire-and-forget

Sets the bearing, the direction the box is injecting, which Direction::With and Direction::Against weigh against in scale. Each axis holds the direction of its last injected delta for window past the last one still owed, then has none and both relative directions stop applying.

ParameterTypeDescription
windowOption<Duration>Some with the hold window, or None to turn the bearing off, leaving the relative directions inert whatever their scale.
modeBearingModePerAxis or Vector.

Persisted in NVS, so a box that has been set boots at its own value. BEARING_WINDOW_DEFAULT (20 ms) in PerAxis is the factory one.

Vector weighs a report twice, and the second pass reads whatever the projection left standing on each axis, not what the device reported. Block Y negative while the injection runs diagonally and a purely horizontal flick can come out with its vertical share removed.

A change to either field drops the standing bearing and the box's banked fractions, which is a visible step while a relative scale is live.

EXAMPLE
use std::time::Duration;
use medius::{Axis, BearingMode, Device, Direction};

let device = Device::find()?;
device.set_bearing(Some(Duration::from_millis(20)), BearingMode::PerAxis)?;
device.scale(Axis::X, Direction::Against, 40)?;  // motion opposing the injection, at 40%
device.set_bearing(None, BearingMode::PerAxis)?; // and off again

set_emit_pace

Pick what paces injected motion, and what rate the clone runs at
fn set_emit_pace(&self, pace: EmitPace, force_hz: Option<u16>) -> Result<()>

Fire-and-forget

Picks the emit-rate ceiling for injected motion. EmitPace::Learned (the default) paces injection to the rate the real mouse reports at. EmitPace::Interval paces to the cloned mouse's declared poll rate (itsbInterval). EmitPace::Fixed(hz) paces to a rate you set.

The 1 ms frame clock snaps a fixed rate to 1000/n Hz and caps it at 1 kHz. The pace raises the ceiling only: idle stays idle, and the box emits a frame solely when injection is pending.

force_hz writes one bInterval onto every HID interrupt-IN endpoint of the descriptor the clone serves, and polls the real device at that same interval, so a mouse that declares 125 Hz while able to deliver 1 kHz is not held to what it declared. It snaps to 1000/n Hz and floors at 10 ms on a low-speed clone (100 Hz).

A forced rate applies only with allow_imperfect_clones on, because the descriptor stops matching the real device. Changing the resolved interval re-clones the box, which drops the control port for a few seconds.

ParameterTypeDescription
paceEmitPaceLearned, Interval, or Fixed(hz).
force_hzOption<u16>The rate the clone advertises and the box polls the device at; None leaves the device's own.

The two are independent, and both ride one command, so every call writes both.

EXAMPLE
use medius::{Device, EmitPace};

let device = Device::find()?;
device.set_emit_pace(EmitPace::Fixed(1000), None)?;      // emit at a fixed 1 kHz
device.allow_imperfect_clones(true)?;
device.set_emit_pace(EmitPace::Learned, Some(1000))?;    // a 1 kHz clone, human-paced injection
device.set_emit_pace(EmitPace::Learned, None)?;          // back to the defaults

set_name

Give the box a human-readable name
fn set_name(&self, name: &str) -> Result<()>

Fire-and-forget

Sets the box's name, the readable partner to its MAC. The firmware keeps the leading printable-ASCII run, capped at 32 bytes; an empty string clears it. Read it back off Version::name, not a query.

ParameterTypeDescription
name&strThe new name, 1 to 32 printable ASCII characters.
EXAMPLE
use medius::Device;

let device = Device::find()?;
device.set_name("Loki")?;              // stored in NVS, reported on Version
let name = device.query_version()?.name;  // read it back off Version

clear_name

Back to the synthesized default
fn clear_name(&self) -> Result<()>

Fire-and-forget

Clears the custom name, reverting the box to a firmware-synthesized Medius-XXXX default derived from its MAC.

EXAMPLE
use medius::Device;

let device = Device::find()?;
device.clear_name()?;                  // back to "Medius-XXXX"

query_imperfect

Read the imperfect-clone state
fn query_imperfect(&self) -> Result<ImperfectStatus>

Blocks

Returns an ImperfectStatus: the opt-in toggle, whether the attached device is over-capacity, and whether the live clone went over-capacity anyway with one interface dead.

EXAMPLE
use medius::Device;

let device = Device::find()?;
let status = device.query_imperfect()?;
if status.over_capacity && !status.allowed {
    // the device was refused; opt in to clone it imperfectly
    device.allow_imperfect_clones(true)?;
}

query_movement_riding

Read the ride window
fn query_movement_riding(&self) -> Result<Option<Duration>>

Blocks

Returns the current ride window as a Duration, or None when movement riding is off.

EXAMPLE
use medius::Device;

let device = Device::find()?;
match device.query_movement_riding()? {
    Some(window) => println!("riding, window {window:?}"),
    None => println!("off"),
}

query_bearing

Read the bearing window and geometry
fn query_bearing(&self) -> Result<Bearing>

Blocks

Returns the configured Bearing: the window and how the box reads it.

EXAMPLE
use medius::Device;

let device = Device::find()?;
let bearing = device.query_bearing()?;
if bearing.is_live() {
    println!("{:?} over {:?}", bearing.mode, bearing.window);
}

query_emit_pace

Read the pacing mode and the rate the clone runs at
fn query_emit_pace(&self) -> Result<EmitPaceStatus>

Blocks

Returns an EmitPaceStatus. advertised_hz is what the clone advertises now: the device's own rate while nothing is forced, the forced rate once something is. The reply carries no record of what the device declared before a force was applied.

EXAMPLE
use medius::{Device, EmitPace};

let device = Device::find()?;
let status = device.query_emit_pace()?;
if let EmitPace::Fixed(hz) = status.mode {
    println!("fixed {hz} Hz, emitting at {} Hz", status.resolved_hz);
}
println!("the clone advertises {} Hz", status.advertised_hz);

On AsyncDevice

setters fire, queries await

AsyncDevice keeps the setters fire-and-forget (no await) and makes query_imperfect, query_movement_riding, query_bearing, and query_emit_pace futures, like the other queries.

EXAMPLE
use std::time::Duration;
use medius::Device;

let device = Device::find()?.into_async();
device.set_movement_riding(Some(Duration::from_millis(20)))?;  // sync, no await
let window = device.query_movement_riding().await?;            // awaits