Transform
A transform moves one field the clone's descriptor declares into another. Unlike the advanced control layer, it needs no imperfect-clone opt-in.
Transforms run before rendering, so injection, riding and rendering act on the transformed field. They are session state on the same lifecycle as a lock.
native report the box's semantic path the wire
X Y wheel pan --> parse --> lock --> [ field transform ] --> render --> emit
buttons/keys weigh swap
remap (X->Y, btn->btn, same report)
|
+-- btn->key / btn->media --> that interface's report| Transform a... | Swap | Remap | Weigh or invert |
|---|---|---|---|
| relative axis (X / Y / wheel / pan) | transform_swap | transform_remap | scale, at a signed percent |
| button | axes only | transform_remap, into a button, key, or media | one bit: lock or unlock |
| key or media usage | not a source | destination only, from a button | one bit, as a button |
All are fire-and-forget: one frame, no reply. transform takes any Transform built from parts. query_transforms reads the active table.
transformInstall or overwrite one field transform
fn transform(&self, t: &Transform) -> Result<()>
Fire-and-forget
| Parameter | Type | Description |
|---|---|---|
t | Transform | The operation, source and destination fields. If the op can't address the pair, or one field is both ends, the call returns Error::TransformOpFields. Installing one past Transforms::CAPACITY returns Error::TransformTableFull. A transform the box refuses is absent from query_transforms. |
Entries are keyed by (source, dest). Setting an existing key overwrites that entry in place, keeping its position. Entries apply in installation order, so the order of two entries writing the same field matters.
use medius::{Device, Axis, Transform};
let device = Device::find()?;
device.transform(&Transform::swap(Axis::X, Axis::Y))?; // X and Y exchanged
device.transform(&Transform::remap(Axis::Wheel, Axis::Y))?; // the wheel drives vertical motiontransform_swap / transform_remap Two transforms, one call each
fn transform_swap(&self, a: Axis, b: Axis) -> Result<()>
fn transform_remap(&self, source: impl Into<LockTarget>, dest: impl Into<LockTarget>) -> Result<()>
Fire-and-forget
Each installs the Transform its matching constructor builds. Both refuse on the same terms as transform.
use medius::{Device, Axis, Button, Direction, Key};
let device = Device::find()?;
device.transform_swap(Axis::X, Axis::Y)?; // exchange the two axes
device.transform_remap(Axis::Wheel, Axis::Y)?; // the wheel drives vertical motion
device.transform_remap(Button::new(4), Key::A)?; // fifth button emits 'A' on the keyboard interface
device.scale(Axis::Y, Direction::Both, -100)?; // Y inverteduntransform / clear_transforms Drop one entry or the whole table
fn untransform(&self, t: &Transform) -> Result<()>
fn clear_transforms(&self) -> Result<()>
Fire-and-forget
untransform drops the entry with this transform's (source, dest) key and ignores its op. clear_transforms drops the whole table.
let swap = Transform::swap(Axis::X, Axis::Y);
device.transform(&swap)?;
device.untransform(&swap)?; // the same key, dropped
device.clear_transforms()?; // or drop everythingquery_transforms Active table
fn query_transforms(&self) -> Result<Transforms>
Blocks
Returns a Transforms: the held entries in the order the box applies them, and a full flag. query_health reports a non-empty table in its transform_on flag.
let table = device.query_transforms()?;
println!("{} transforms{}", table.entries.len(), if table.table_full { " (full)" } else { "" });
for t in &table.entries {
println!(" {:?} {:?} -> {:?}", t.op, t.source, t.dest);
}On AsyncDevice Transforms fire; query_transforms awaits
Transforms carry no opt-in check, so AsyncDevice keeps every setter synchronous. Only query_transforms is a future.
use futures::executor::block_on;
use medius::{AsyncDevice, Axis};
let device = AsyncDevice::open("/dev/ttyACM0")?;
device.transform_swap(Axis::X, Axis::Y)?; // sync
let table = block_on(device.query_transforms())?; // query awaits