blob: 2435f564a02c7c995eb9a7b2716056710ccfd437 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// # Fuchsia keyboard shortcut library, version 2.
///
/// The `fuchsia.ui.shortcut2` API is a library for keyboard shortcut
/// notifications on Fuchsia. A program can register using the [Registry] protocol
/// as a [Listener] for a keyboard shortcut (for example, Ctrl+Shift+A).
/// When that key chord is detected, `Listener.OnShortcut` will be called by
/// the platform to notify the program of the shortcut event.
///
/// ## Protocol roles
///
/// The protocols require two roles:
///
/// * The **shortcut subsystem** is the part of the Fuchsia platform which
/// processes shortcuts.
/// * The **consumer** is the program that is interested in receiving shortcut
/// notifications
///
/// ## Shortcuts and focus
///
/// A shortcut listener registration is always paired with a [ViewRef], ensuring
/// that only `Listeners` that are associated with a particular view get
/// notified. For a `Listener` to receive a notification it must either be in
/// focus, or it must be in the focus chain of the view in focus.
///
/// ## Shortcut lifecycle
///
/// A typical sequence of events when registering and receiving a shortcut
/// is the following:
///
/// 1. Component Manager routes `fuchsia.ui.shortcut2/Registry` from the shortcut
/// subsystem to the consumer (hereafter: [Registry]).
/// 2. Consumer opens `Registry` connection.
/// 3. Consumer calls `Registry.SetView`, passing `ViewRef` and [Listener] to
/// the shortcut subsystem.
/// 4. Consumer calls `Registry.RegisterShortcut` to express interest in
/// a specific [Shortcut].
/// a. The shortcut subsystem MAY respond with an [Error] if the call is
/// malformed. In that case, the malformed call to
/// `Registry.RegisterShortcut` MUST be disregarded. The call MAY be
/// corrected and repeated.
/// b. Make sure that `RegisterShortcut` calls are issued strictly after
/// `SetView` to ensure that `Listener` gets timely notifications of
/// shortcuts. Doing otherwise MAY cause the `Listener` to miss legitimate
/// shortcut activations.
/// 5. Consumer MAY close the connection to `Registry` once no more shortcut
/// registrations are needed.
/// 6. The shortcut subsystem calls `Listener.OnShortcut` when it detects an
/// actuation of a shortcut of interest.
/// a. The consumer MUST respond promptly with an acknowledgment, and MUST
/// denote in the response whether it has `HANDLED` or `NOT_HANDLED`
/// the request.
/// b. Tardy responses MAY cause the shortcut subsystem to close the
/// `Listener` connection.
/// 7. Consumer closes the `Listener` channel, thus unregistering all registered
/// shortcuts. The consumer MAY restart the workflow to register new
/// interests.
@available(added=9)
library fuchsia.ui.shortcut2;
using fuchsia.ui.views;
using fuchsia.ui.input3;
/// The maximum number of keys that can be used to trigger a shortcut. This
/// value MAY grow in the future based on how needs evolve.
const MAX_REQUIRED_KEYS uint64 = 4;
/// A locally-unique identifier. The identifier is unique in the scope of a
/// single connection to Registry.
alias ID = uint32;
/// The type that encodes any errors in using Shortcut library protocols.
type Error = flexible enum {
/// Returned for a call that accepts user arguments. Denotes that user given
/// arguments are somehow not well-formed. Implementors should leave a log
/// message in syslog at least at level `DEBUG` which explains what is wrong.
ILLEGAL_ARGUMENT = 1;
};
/// Registry is the entry protocol into shortcut processing.
///
/// This protocol allows a consumer to register an interest in being notified
/// when a particular key chord has been detected by the platform.
///
/// Consumers MUST open a connection to [Registry] first, to start interacting
/// with the shortcut subsystem.
///
/// A consumer SHOULD then call [SetView] next to establish a [Listener], before
/// making any [RegisterShortcut] calls. This minimizes the races that could
/// occur if a shortcut arrives before the consumer registers an interest for it.
/// While in production races between shortcut registration and the input events
/// are always possible, issuing a fake input event strictly after
/// `RegisterShortcut` has returned should be race-free.
///
/// Once `SetView` and `RegisterShortcut` calls have completed, the caller MAY
/// close the connection to `Registry`.
///
/// There are currently no affordances for changing shortcuts once they have
/// been registered. But, closing `Registry` and `Listener` channels will cause
/// all associated shortcut registrations to be removed. A consumer MAY close
/// the connections and re-register shortcuts if this is desirable.
@discoverable
protocol Registry {
/// `SetView` opens a new `Listener` connection for a shortcut consumer.
SetView(resource struct {
/// A [ViewRef] that this [Listener] will be associated with. A `Listener`
/// will only receive shortcut notifications if it is in focus, or in
/// the focus chain of the currently focused view.
view_ref fuchsia.ui.views.ViewRef;
/// The channel for receiving shortcut notifications. The message flow
/// on `Listener` is from the shortcut subsystem to the consumer.
listener client_end:Listener;
});
/// `RegisterShortcut` is used to register a single shortcut for the `Listener`
/// to be notified about. Repeat the call to register more shortcuts.
RegisterShortcut(struct {
/// The shortcut to register.
///
/// See [Shortcut] for a detailed specification.
///
/// Returns [ILLEGAL_ARGUMENT] if the specific shortcut is not
/// well-formed for any reason. See details on [ILLEGAL_ARGUMENT]
/// for recommendations.
shortcut Shortcut;
}) -> () error Error;
};
/// Specifies a single shortcut definition.
type Shortcut = struct {
/// The shortcut identifier. The identifier MUST be unique per a connection
/// to the Registry protocol.
id ID;
/// The list of keys that triggers this shortcut.
///
/// A shortcut will be triggered when all of the required keys are
/// actuated. The order of key actuation does not matter.
key_meanings vector<fuchsia.ui.input3.KeyMeaning>:MAX_REQUIRED_KEYS;
/// Optional settings for the shortcut.
options table {
/// If set, [modifier_lock_keys] will trigger a mode in which every key
/// press will be prefixed by [modifier_lock_keys] until the same combination
/// has been pressed again.
///
/// This allows invoking a sequence of shortcut with long chords using only
/// a singular trigger key. For example, setting:
///
/// ```
/// modifier_lock_keys = [Shift, Control]
/// ```
///
/// Allows one to invoke `Shift+Control+A` by pressing only `a`. This
/// mode is deactivated by pressing `Shift+Control` again.
1: modifier_lock_keys vector<fuchsia.ui.input3.KeyMeaning>:MAX_REQUIRED_KEYS;
};
};
/// Reports from [Listener] whether the shortcut has been handled or not.
///
/// When the shortcut has been reported as [HANDLED], the shortcut subsystem
/// will not deliver it to any other [Listener]. When the shortcut has been
/// reported as [NOT_HANDLED], it will be offered to the next listener in line.
type Handled = flexible enum {
/// The shortcut has been handled.
HANDLED = 1;
/// The shortcut has not been handled.
@unknown
NOT_HANDLED = 2;
};
/// A [Listener] is served by the shortcut service *consumer*.
///
/// When a shortcut with the identifier ([ID]) matching that of a registered
/// [Shortcut] is detected, `Listener.OnShortcut` is called with this `ID`.
///
/// The [Listener] MUST respond with an acknowledgment as soon as the message
/// is received. Repeated tardiness in response MAY cause the client end of
/// the `Listener` to be closed.
///
/// Closing the `Listener` channel on either end causes all shortcuts registered
/// for this `Listener` to be unregistered.
protocol Listener {
OnShortcut(struct {
/// The locally unique identifier of the triggered shortcut.
id ID;
}) -> (struct {
/// Whether the shortcut has been handled or not. See [Listener] above
/// for protocol details.
handled Handled;
});
};