pub trait Addon: Send {
Show 19 methods
// Required methods
fn id(&self) -> &'static str;
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn actions(&self) -> &'static [AddonAction];
fn availability(&self) -> Availability;
fn perform(
&mut self,
action: &str,
invocation: &Invocation<'_>,
) -> Result<(), AddonError>;
// Provided methods
fn device_actions(&self) -> &'static [DeviceAction] { ... }
fn applies(&self, _action: &str) -> bool { ... }
fn status(&self) -> Option<String> { ... }
fn signals(&self) -> &'static [AddonSignal] { ... }
fn settings(&self) -> &'static [AddonSetting] { ... }
fn bound_inputs(&mut self, action: &str, inputs: &[String]) { ... }
fn held_inputs(&mut self, _action: &str, _held: &[String]) { ... }
fn choices(&self) -> &'static [AddonChoices] { ... }
fn live_choices(&mut self, _id: &str) -> Vec<Choice> { ... }
fn permissions(&self) -> &'static [Permission] { ... }
fn configure(
&mut self,
_values: &BTreeMap<String, String>,
_credentials: CredentialHandle,
) { ... }
fn attach_store(&mut self, _store: StoreHandle) { ... }
fn read_signals(&mut self) -> Reading { ... }
}Expand description
Something that can act on another application’s behalf.
Implementations are expected to be cheap to construct and to discover their target lazily: the daemon builds every addon at startup whether or not the application it integrates is installed, and a constructor that blocked on a network call would delay login for all of them.
Required Methods§
Sourcefn description(&self) -> &'static str
fn description(&self) -> &'static str
What it integrates, in a sentence.
Sourcefn actions(&self) -> &'static [AddonAction]
fn actions(&self) -> &'static [AddonAction]
Everything it can be asked to do.
Sourcefn availability(&self) -> Availability
fn availability(&self) -> Availability
Whether it could act right now.
Asked whenever the interface draws the addon, so it must be quick and
must not block. It is allowed to be wrong a moment later — the target
application can close between this answer and the next keypress, which
is why Self::perform returns a result of its own rather than
trusting this.
Sourcefn perform(
&mut self,
action: &str,
invocation: &Invocation<'_>,
) -> Result<(), AddonError>
fn perform( &mut self, action: &str, invocation: &Invocation<'_>, ) -> Result<(), AddonError>
Do it.
invocation carries the binding’s parameters and, for a
Trigger::Continuous action, the position the input reported.
§Errors
If the action is unknown, a required parameter is missing, the target is unavailable, or it failed.
Provided Methods§
Sourcefn device_actions(&self) -> &'static [DeviceAction]
fn device_actions(&self) -> &'static [DeviceAction]
Everything it names that the device does on its own (ADR-0021).
Defaulted to nothing, like Self::signals and Self::settings, and
for a stronger reason than either: almost every addon has none, and the
whole point of the declaration is that it is exceptional. An addon
listing one is saying this is better as a keystroke than as a call to
me, and here is what to call it.
Self::perform is never called for one of these by a daemon that
understands the declaration — it resolved the action to a keystroke when
the key was bound, and the press never reaches the host at all. An author
implements nothing for them and must not try. A daemon built against an
older SDK dropped this field on the way in and will call perform
anyway; run answers that itself with a failure naming the
cause, so an author still implements nothing.
Ids share one namespace with Self::actions, because a binding stores
one string and cannot say which list it came from.
Sourcefn applies(&self, _action: &str) -> bool
fn applies(&self, _action: &str) -> bool
Whether an action is worth offering at the moment.
For the pair that undo each other — sign in and sign out — where offering both at once means one of them is always the wrong half of a question nobody asked.
§This is relevance, not permission
Self::perform deliberately does not consult it, and neither
does Self::actions, which stays complete. Three consequences, all
intended:
- A key bound to a hidden action still works. Bindings are authored once and pressed later, and a key that stopped existing because of something that happened after it was bound is the failure US9 calls out.
- The action can still be bound, because the editor is about what a key could ever do rather than what is useful this second.
- An addon must still handle being asked. Signing out twice succeeds; this only stops the interface suggesting it.
Defaulted to true, which is the ordinary case: an action that is
worth having is worth having now.
Sourcefn status(&self) -> Option<String>
fn status(&self) -> Option<String>
One line about what it is currently connected to, when that is a question a user can have.
Shown wherever the addon is drawn, next to its name.
§Why this is not part of Availability
They answer different questions, and only one of them has an answer
when things are fine. Availability is can it act, and the useful
case is the negative one — an addon that cannot act owes the user a
reason. Ready carries no words because “it works” needs none.
This is what is it working as, and it is only interesting when the
answer is ready. “Signed in as Philipp” tells someone which of two
Spotify accounts their Save key is filling up, which is invisible from
anywhere else in the interface and is exactly the thing they need before
pressing Sign out. Folding it into Ready(String) would have made every
addon that has nothing to say construct an empty one.
Defaulted to None, which is the ordinary case: an addon that talks to
a local application is connected to the only thing it could be.
Called whenever the interface draws the addon, so it must be quick and
must not block — the same contract as Self::availability, and the
same reason. Read it from what the addon already holds; do not go and
ask the network.
Sourcefn signals(&self) -> &'static [AddonSignal]
fn signals(&self) -> &'static [AddonSignal]
Every signal it publishes. FR-062.
Defaulted, because most addons only act. An addon that publishes none is not a lesser addon — it is the ordinary case, and the interface should not make it write an empty slice to say so.
Sourcefn settings(&self) -> &'static [AddonSetting]
fn settings(&self) -> &'static [AddonSetting]
Everything it needs configuring once (ADR-0013).
Defaulted for the same reason as signals: most addons need nothing, and the ones that do are the exception.
Sourcefn bound_inputs(&mut self, action: &str, inputs: &[String])
fn bound_inputs(&mut self, action: &str, inputs: &[String])
The inputs bound to one of this addon’s actions, in device order.
Sent when it changes: a binding edited, a profile switched, a module attached or unplugged. Defaulted to ignoring it, because almost no addon cares which key called it.
§Why the daemon sends an order rather than positions
006-FR-014a defines fader order as “ascending slot position, then
input index within the module” — and the only side that knows where a
module physically sits is the daemon, which owns the inventory. Sending
coordinates would make every addon that cares reimplement the sort, and
the second implementation would be the one that was wrong about a
module attached at a negative offset.
So this is already sorted. Match Invocation::input against it to
learn which fader moved and where it sits among the others.
Sourcefn held_inputs(&mut self, _action: &str, _held: &[String])
fn held_inputs(&mut self, _action: &str, _held: &[String])
Which of this action’s inputs the user currently has hold of.
Sent when the set changes, not on a timer. Defaulted to ignoring it, because almost no addon cares — it matters only where the host moves an input on its own, which today means a motorised fader.
§Why an addon is told rather than asked
Held is defined against something the device reports, and only the host sees the device. An addon deriving it from the values it receives would be doing it from the wrong side of a pipe and against a different set of reports, and two definitions of held would disagree exactly when it mattered.
§What it is for
A host-driven control moving under somebody’s hand is the case where automation stops feeling like automation. An addon that reassigns controls should leave a held one alone and apply the change when it is released — and apply the latest one, not the one that was pending when it was grabbed. The user let go into the present.
Sourcefn choices(&self) -> &'static [AddonChoices]
fn choices(&self) -> &'static [AddonChoices]
Named lists its parameters can draw options from (ADR-0022).
Defaulted to nothing, because most parameters are free text and always were. Declared here rather than on the parameter so that two parameters can share one list — a pin and a priority pointing at the same people must not be able to show two different rosters.
A source marked AddonChoices::live carries no values here; the
daemon asks for them when somebody opens the picker.
Sourcefn live_choices(&mut self, _id: &str) -> Vec<Choice>
fn live_choices(&mut self, _id: &str) -> Vec<Choice>
The current options for a live source.
Asked when somebody opens the picker, and at no other time. Not polled: a timer would cost idle CPU for a picker nobody has open. So this is allowed to be the slow one — but it still must not block for long, because an interface is waiting on it with nothing to draw.
Answer from whatever the addon already knows rather than by asking the far side. An addon that holds a cached picture answers from memory; one that must fetch should keep the request small and give up quickly rather than leave a menu spinning.
The order is the addon’s to decide, and that is the point of a named source rather than a per-parameter one: an addon that wants who is here now, then who I have seen before, then nothing, returns one flat ordered list and the interface renders it in that order. No interface support is needed for a concept only the addon has.
An unknown id answers empty rather than panicking — the daemon and the addon can disagree across a version, and a menu with nothing in it is a better outcome than a dead child process.
Sourcefn permissions(&self) -> &'static [Permission]
fn permissions(&self) -> &'static [Permission]
Everything it needs to be allowed to do (FR-046).
Defaulted to nothing, and that default is the honest one for more addons than it looks: an addon that drives a local API — the media session, the audio mixer — reaches no host, opens no file and keeps no account, so it has nothing to ask for and the user is never asked.
An addon declaring anything here does not run until the user grants it. So the list is what the addon needs, not what it might one day like: every entry is a question somebody has to answer before the addon works at all, and an addon that asks for more than it uses is training people to grant without reading.
Sourcefn configure(
&mut self,
_values: &BTreeMap<String, String>,
_credentials: CredentialHandle,
)
fn configure( &mut self, _values: &BTreeMap<String, String>, _credentials: CredentialHandle, )
Take its settings, and a place to keep its own credentials.
Called at startup and whenever the settings change, so an addon holds
what it needs rather than asking. Credentials arrives as a handle
rather than a value because an addon needs it later too — a refreshed
token has to go back, and that happens mid-action rather than at
configuration time.
Sourcefn attach_store(&mut self, _store: StoreHandle)
fn attach_store(&mut self, _store: StoreHandle)
Take somewhere to remember things that are not secrets (ADR-0027).
Called once, before the first Self::configure, so an addon that
wants to load what it remembered can do so before it is asked anything.
A separate method rather than a third argument to configure, because
this arrived after the trait was published and widening a signature
would break every addon written against the older one — including ones
whose authors cannot be contacted. Defaulted to ignoring it, which is
the right behaviour for the many addons that remember nothing.
Sourcefn read_signals(&mut self) -> Reading
fn read_signals(&mut self) -> Reading
Read every signal it publishes, now.
Called on the signal poll, never on the input path — the same rule
as Self::perform, and for a stronger reason: this runs on a timer,
so an implementation that blocked for a second would do it forever
rather than once per keypress.
See Reading for why this is a question the daemon asks rather than
something the addon announces.