pub enum DeviceKeystroke {
Tap {
key: u8,
ctrl: bool,
shift: bool,
alt: bool,
gui: bool,
},
Consumer {
usage: u16,
},
}Expand description
One keystroke, exactly as the device will send it.
§Why this crate spells the HID vocabulary again
It has no choice. Nothing here may depend on another Nobble crate — the rule
at the top of this module, and FR-044 behind it — so nobble_core::HidAction
is unreachable from an addon author’s build, and Constitution VI’s preferred
answer, generated bindings, does not cross a repository boundary that exists
on purpose (FR-043, FR-049). The alternative to restating the shape is not
restating it somewhere better; it is being unable to declare a keystroke at
all, and then FR-024 has no answer.
What is a choice is how much gets restated and how the copy is held in
step. These variants, their field names and their serde spelling in
KeystrokeDecl match
nobble_rpc::ActionDto::HidTap and HidConsumer one for one, so the object
crossing this pipe is the same JSON text a binding is saved as, and the
daemon’s conversion is a rename-free match a reader can check by eye. A
test in nobble-service — the only crate that can see both — pins that, and
stands in for the generated binding.
That is weaker than one definition and is recorded as such rather than
argued away. Nothing makes a new HidAction variant a compile error here,
because Cargo cannot see across the boundary. Two things bound the damage:
the drift is asymmetric — a variant added there narrows what this can say, a
variant added here breaks the daemon’s exhaustive bridge — and what is copied
is USB HID’s vocabulary rather than Nobble’s, so it is not a format anyone
here is free to change.
§Why exactly these two, and why not MIDI
The cut is made by an existing function rather than by judgement.
ActionDto::from_binding has arms for HidTap and HidConsumer, and its
_ => return None covers key sequences and mouse movement, which have no
on-disk form — so an addon declaring one would declare a binding
check_supported refuses to save, and it fails the whole file rather
than the one key.
The narrower reading matters as much. One chord cannot type a string, and a sequence is precisely the mechanism the contract rules out when it says an addon able to ask the device to send anything at any time “would be a keylogger with extra steps”. Excluding it in the type leaves no check for anyone to forget.
MIDI is left out on different grounds, and the omission is not a claim
that device-resolved means keyboard. Binding::Midi is device-resolved too
and a control change is inherently continuous, so resolution and trigger are
genuinely independent axes. But midi_note and midi_cc are already
first-class binding kinds with their own editors, so there is no
discoverability gap for an addon to close. Adding a variant later is
additive; adding one now is speculative.
Deliberately not #[non_exhaustive] — the opposite choice from
ParamKind, for the reason that separates them: an unfamiliar parameter
kind has a useful fallback, a text box, and an unfamiliar keystroke has none.
A wildcard arm here is how a device silently sends nothing. Adding a variant
should fail to compile in the daemon that has to translate it, which is
what Request says for itself.
Variants§
Tap
Press and release one key, with modifiers held.
What is declared here is a suggestion the user is expected to change,
and an addon that treats it as a contract has misunderstood the field.
Discord ships no keybind for Toggle Mute — the user invents one — so
whatever is declared has to be transcribed into Discord by hand anyway.
Self::Consumer is the opposite case: there the addon knows the answer.
Fields
key: u8A HID usage code, not a character — the same distinction
HidKey makes on the daemon side, for the same reason: a keystroke
authored on a QWERTZ layout and pressed on a QWERTY host produces a
different character, and hiding that makes it invisible until
somebody complains. Usage 0x10 is the key labelled M on ANSI
and the key labelled , on AZERTY, so an addon’s choice of chord
can collide with something on a layout its author never saw. That is
what the editor is for.
Consumer
A Consumer Control usage — the media keys.
Here because ADR-0021 intends media to gain a device-resolved
play/pause as a new action, and calls its case stronger than Discord’s:
a consumer usage is layout-free and needs nothing arranged elsewhere, so
the addon genuinely knows the number and the declaration is a fact rather
than a suggestion. A type that could not say so would have made the
adoption impossible.
Trait Implementations§
Source§impl Clone for DeviceKeystroke
impl Clone for DeviceKeystroke
Source§fn clone(&self) -> DeviceKeystroke
fn clone(&self) -> DeviceKeystroke
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DeviceKeystroke
impl Debug for DeviceKeystroke
Source§impl PartialEq for DeviceKeystroke
impl PartialEq for DeviceKeystroke
Source§fn eq(&self, other: &DeviceKeystroke) -> bool
fn eq(&self, other: &DeviceKeystroke) -> bool
self and other values to be equal, and is used by ==.