Skip to main content

Addon

Trait Addon 

Source
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§

Source

fn id(&self) -> &'static str

Stable identifier, stored in bindings. Never rename one.

Source

fn name(&self) -> &'static str

What to call it.

Source

fn description(&self) -> &'static str

What it integrates, in a sentence.

Source

fn actions(&self) -> &'static [AddonAction]

Everything it can be asked to do.

Source

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.

Source

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§

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Implementors§