RFC-0096 - 用户输入架构

RFC-0096:用户输入架构
状态已接受
领域
  • 人机交互
说明

在具有图形界面和多个运行时的系统上的 Fuchsia 上提交用户输入事件(键盘、鼠标、触摸等)的概要架构。

问题
  • 72908
Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-04-15
审核日期(年-月-日)2021-05-26

总结

此 RFC 描述了在具有图形界面和多个运行时的系统上在 Fuchsia 上传送用户输入事件(键盘、鼠标、触摸等)的目标概要架构。用户通过各种方法/设备为系统提供输入,包括键盘、鼠标、触摸和按钮。此 RFC 说明了 Fuchsia 上的输入事件如何从驱动程序级原始数据到分派到用户空间软件(例如 Flutter 应用)的输入事件。所述组件在发布时仍处于开发阶段。

设计初衷

为了支持 Fuchsia 的包容性理念,Fucsia 输入必须为基于不同运行时(利用不同界面框架(例如 Flutter、Chromium)构建的组件提供平台级支持,并允许产品所有者自定义输入行为。在其他平台上,这些行为通常会纳入特定界面框架的实现中。在将平台输入处理与特定界面框架的细节分离开来时,Fuchsia 存在许多独特的挑战。

在 Fuchsia 代码中,目前存在多个输入路由路径(根 presenter输入流水线),且提供有关首选路径和迁移计划的有限公开指导。此 RFC 概述了 Fuchsia 上用户输入路由的目标架构,并提供了有关如何针对未来用例扩展输入处理的指导。

要求

  • 安全性:借助 Fuchsia 输入堆栈,轻松构建安全产品。
    • 输入事件可能包含密码和付款数据等敏感数据,以及有关用户何时活跃的信息。所有用户输入事件都应被视为个人身份信息,并仅由可信系统组件分派给最终用户软件。
    • 如果路由不正确,用户输入也可能会被滥用,例如,因恶意界面导致用户点击了他们本不想点击的按钮(“点击劫持”)。
    • Fuchsia 平台应尽可能鼓励开发者提供 API Surface,让用户能够轻松了解和审核通过系统的用户输入事件流,从而鼓励开发者构建安全的产品。
  • 正确性:输入事件会根据用户的预期传送。
    • Fuchsia 输入系统负责在上下文中解读输入事件,并将其传递给系统上当前运行的正确目标组件。但“正确”的定义可能因事件和商品类型而异。
    • 即使界面正在添加动画效果或更改大小,输入事件传送也应保持一致。
    • 有时,事件可能会同时分派给多个组件(例如键盘快捷键)。
  • 性能:用户输入速度快(足够快)。
    • 虽然确切的延迟时间要求因输入设备和产品类型而异,但输入源对延迟特别敏感,因此应尽可能快,使用户不会察觉到延迟。
    • 输入架构应避免引入不必要的延迟。尤其要注意进程上下文切换,以避免不必要的阻塞调用。
  • 可定制性:系统应针对在 Fuchsia 平台上构建的不同产品支持不同的用户输入行为。 不过,此 RFC 中提议的初始实现将仅满足自定义的最终要求中的一部分。(请参阅输入流水线自定义。)
    • 一些产品需要支持键盘和鼠标,而另一些产品则主要侧重于通过触摸和按钮进行互动。
    • Fuchsia 平台应提供钩子,以实现特定于产品的输入行为自定义(例如,根据系统上下文对按钮事件进行不同的解释)。
    • Fuchsia 应允许产品根据需要将输入事件映射到不同的类型,例如将触摸事件重新解释为鼠标事件,以与不支持触摸手势的软件兼容。
  • 可扩展性:Fucsia 可以添加对新输入模式的支持。
    • 虽然输入需求截然不同的产品可能需要更改平台,但应该可以增加对新输入法的支持,而无需对现有输入堆栈进行实质性重写。

激励示例

这些用例并不全面,但可以让您深入了解此架构应该支持的行为类型。

  • 当屏幕上有多个框架的界面时,正确解读触摸屏手势。
  • 当屏幕上有多个文本框(由不同的框架提供支持)时,使用适当的布局信息路由键盘输入。
  • 除了这些按钮的正常功能外,还允许按键组合(例如同时向上和向下音量按钮)触发恢复出厂设置。
  • 当设备处于休眠或关闭状态时,禁止在笔记本电脑触摸屏上进行输入。
  • 解读笔记本电脑触控板手势(双指张合缩放、两指滚动)。

背景信息和术语

  • 人机接口设备 (HID) - 一种允许用户通过键盘、鼠标、触摸屏或消费类控制设备(按钮)进行输入和输出的设备。这通常是指使用 USB-HID 规范的设备。
  • 输入事件 - 单个用户输入事件,例如按键、鼠标移动操作或触摸事件。处理时,事件可能会带有额外信息注解,具体取决于当前上下文。
  • 指针事件 - 与屏幕上的某个位置对应的用户输入事件,例如触摸或鼠标事件。指针事件是输入事件的子集。
  • 事件流 - 一组相关的输入事件,通常时间相近。
  • 输入处理程序 - 执行输入处理的单个阶段的软件。输入处理程序将输入事件作为输入,还会发出输入事件。它可以修改事件或与系统的其他部分进行通信。
  • 输入流水线算法 - 一种将一系列输入处理程序链接在一起来处理 Fuchsia 输入的算法。它充当 Fuchsia 输入的政策层。
  • 输入流水线实现 - 输入流水线算法的实现。在实际使用中,这是 fuchsia.git 中的一个组件,负责将驱动程序级用户输入事件路由到系统的其余部分。
  • Scenic - Fuchsia 图形引擎。Views 还负责路由指针事件。
  • 全局场景图 - 由 Scenic 渲染的图形内容树。
  • 视图 - 场景图中的子空间。一个视图通常对应于屏幕上的一个区域,但该区域不一定是矩形。
  • ViewRef - 与特定视图相关联的事件对。这用于标识多个 Fuchsia 组件的视图。

架构概览

本设计简要介绍了 Fuchsia 上用户输入事件的整体流程,但并未涵盖每种输入类型的所有特定详细信息。其中许多详细信息在下文相应部分链接的针对特定行业的设计中进行了说明。

从下到上:dev/class/input-report -> input_pipelines -> {scenic OR a11y manager OR IME manager OR media buttons listener} -> {Chromium OR flutter OR
carnelian} -> {View 1 OR view 2 OR view3 OR view 4}
侧边:Product Session
component with {infocus-fallback-nic-access-nic -> {scenic OR a11y manager OR IME manager OR media buttons listener}

系统会根据一些相互关联的规则分派事件,包括但不限于:

  • 针对相应事件类型的产品政策(例如,所有音量事件均分派给设置)
  • 事件在屏幕上的位置(例如对于触摸或鼠标)
  • 当前聚焦的视图(例如用于文本输入的视图)

输入流水线是一个 Fuchsia 组件,负责管理这些规则及其互动,并将用户输入事件连同处理这些事件所需的信息一起路由到相应的系统服务。它实现输入流水线算法(一个链式输入处理程序系统,每个处理程序在设备输入处理过程中执行离散步骤)。此组件充当 Fuchsia 上输入的政策层。输入流水线通过输入驱动程序直接从驱动程序层提取事件,以及来自测试的合成事件和软件生成的事件(例如来自虚拟键盘的事件)。

当事件在输入流水线中移动时,它们会从全局低级事件(没有语义,但涉及敏感数据)发展到限定了作用域的已处理数据(具有本地含义,可由最终用户软件解读),这些数据可由最终用户软件解读(手势、字符等)。驾驶员级输入事件分为两大类:

  • 与屏幕上的特定位置对应的指针事件。
  • 按钮和切换事件。

未来可能会扩大此功能的适用范围。USB-HID 使用量表非常庞大且包含很多项,其中包括飞行模拟器、健身器材和虚拟现实设备的页面。

驱动程序级输入事件可转换为:

  • 触摸手势。
  • 鼠标事件(包括滚动)。
  • 文本输入事件。
  • 语义无障碍操作。
  • 按钮事件(例如音量改变、摄像头开启/关闭)。

通常,每个事件的分派方式如下:

Driver -> Input Pipeline -> UI System Component -> UI Framework-> UI View
  • 输入流水线组件包含许多称为输入处理程序的内部阶段。输入流水线充当输入的政策层,可能因商品类型而异。(请参阅下文的输入流水线自定义。)
  • 界面系统组件包括 Views、无障碍管理器和 IME 管理器,但随着我们向平台添加功能,这些组件可能会随着时间的推移而扩展。这些组件属于 Fuchsia 平台,位于 Fuchsia.git 中。这些组件与产品无关。
  • 界面系统组件负责决定哪些视图应接收事件,并将该信息传递给界面框架运行程序进行分派。界面系统组件使用 Sense 中的信息(通常是焦点链)来确定哪个视图应该接收事件。
  • 界面系统组件还可以使用事件。
  • 界面框架目前包括 Flutter、Chromium 和 Carnelian。我们希望将来能够支持其他界面框架/运行时。这些产品可用于各种产品类型,但不属于 Fuchsia 平台。
  • 界面视图是拥有相应屏幕区域(称为视图)的图形组件。(请参阅下文的视图和事件路由)。
  • 媒体按钮事件并不总是遵循此模式。媒体按钮的最终目的地可能是设置服务,而不是界面视图。

在某些情况下,事件可能会在最终传送之前经过其他阶段。 例如,触摸事件可能会通过 View 分派给虚拟键盘组件,由虚拟键盘组件生成合成按键,然后通过输入流水线分派该按下事件。

输入流水线将指针事件发送到 Views,后者负责将事件分派到正确的运行时实例。这样,Sensing 就可以保持对内容在屏幕上的位置全局一致的了解,并避免在动画期间出现竞态条件。(请参阅下文的路由图形事件。)

设计

输入事件的来源

输入事件通常通过输入驱动程序或通过充当输入设备控制器的 Fuchsia 组件(例如蓝牙 HID 设备的 bt-host 组件)进入系统。在许多情况下,这些事件最初都是人机接口设备 (HID) 事件,但也有一些例外情况。驱动程序和控制器会以 fuchsia.ui.input/InputReport 的形式发出每个事件。一般来说,输入报告随后会被输入流水线使用和转换。但在恢复模式下,系统会直接将这些文件发送到使用 Carnelian 实现的终端组件,无需进行任何中间处理。

用于测试的输入事件的生成方式有所不同。(请参阅下文的测试部分。)

输入管道

输入流水线组件是 Fuchsia 系统组件。用于实现输入流水线算法。输入流水线组件的作用类似于输入堆栈中的政策层。它负责确定特定设备支持哪些事件类型、应如何调度输入事件,以及管理输入设备。

输入流水线算法由以下部分组成:

绑定阶段通常会用有关设备状态的信息来增强 InputReports。示例:

  • 包含相对鼠标移动的 InputReport 将变为具有相对屏幕坐标的 InputEvent。
  • 按键 InputReports 流变成“按下按键”和“按下按键”InputEvent 的流。(驱动程序通常仅在给定时刻报告哪些键按下,而不是单独的释放事件和按下事件。)可以根据之后的 InputReport 缺少某个键来推断出“key up”。

但是,输入流水线不会在会话领域内运行。输入流水线是比会话具有更高权限的组件,它利用了我们目前不想向会话组件公开的多项功能。

输入流水线实现会作为 Fuchsia 平台的一部分提供给产品所有者。截至本 RFC 发布时,这些实现是用 Rust 编写的,输入处理程序是实现共享 InputHandler Rust 特征的类,但未来可能会更改,以便进行优化和可扩展。

目前,平台提供两种输入流水线实现:一种针对主要依赖触摸屏互动的设备进行了优化,另一种针对使用鼠标和键盘的产品。这种有限程度的自定义在将来很可能会扩展。(请参阅下文的输入流水线自定义部分。)

输入处理程序

输入处理程序是主要机制,可让产品所有者自定义与产品状态相关的输入处理。有时,我们将其称为输入事件的产品政策。输入处理程序可能

  • 通过添加上下文信息(例如,正在使用的键盘布局,或与鼠标点击同时按下的键)来增强事件。
  • 根据事件周围的事件流(例如输入平滑、触控板手势)修改事件。
  • 您可以在处理程序内处理事件,也可以将事件发送到界面系统组件公开的服务,而界面系统组件又负责将该事件分派给一组相应的界面视图。目标视图由 Views 中的当前视图焦点决定。(请参阅下文的焦点部分。)
  • 发送 OutputReports 以控制输入设备状态(例如设置 Capslock LED)。

短期内,我们定义一个 Rust 特征 InputHandler,每个输入处理程序都必须实现它。从长远来看,该接口可以替换为 FIDL 接口。每个处理程序都必须能够处理输入事件,并且应输出输入事件(可能为空)矢量。

#[async_trait]
pub trait InputHandler: Send {
    /// Returns a vector of InputEvents after handling `input_event`.
    ///
    /// # Parameters
    /// `input_event`: The InputEvent to be handled.
    async fn handle_input_event(
        &mut self,
        input_event: input_device::InputEvent,
    ) -> Vec<input_device::InputEvent>;
}

未来基于 Fuchsia 构建的产品可能需要的输入处理程序示例(有些非常奇妙):

  • 无障碍功能输入处理程序:当相关无障碍功能(如开关导航或使用键盘快捷键的屏幕阅读器)启用时,将事件发送到 a11y 管理器。
  • 快捷键处理程序:确定键盘事件是否与有效的键盘快捷键匹配(可调用其他组件)。
  • Locale Handler:应用有关当前视图活跃语言区域的信息。
  • 指针事件调度处理程序:将触摸/鼠标/触控笔事件发送到景观,以便将其分派给视图。
  • 媒体按钮处理程序:将媒体按钮路由到设置服务(或其他位置)。
  • 键盘布局处理程序:使用当前处于活动状态的键盘布局为键盘事件添加注解。
  • 触控板手势处理程序:触控板手势并转发等效的鼠标事件。
  • 输入平滑处理程序:通过对事件取平均值来减少抖动。
  • 焦点处理程序:为当前聚焦的视图使用 ViewRef 为键盘事件添加注解。
  • 睡眠模式处理程序:在设备处于休眠状态时禁止输入。
  • Multitouch Waffle Iron 魔术按钮处理程序:将触摸手势事件解释为媒体按钮。

在许多情况下,输入处理程序会将事件分派给 Fuchsia 系统服务,例如 Schces、无障碍管理器、IME 管理器等。在本例中,该事件将被标记为“已处理”,并在流水线的其余部分中传播。(请参阅下文的事件流一致性)。

输入流水线自定义

您可以通过指定要包含的处理程序及其显示顺序来实例化输入流水线。这使得添加处理程序、重新排序处理程序或实例化修改后的流水线变得非常轻量。Rust 中的示例:

async fn input_handlers(
 ...
 ...
) -> Vec<Box<dyn InputHandler>> {
    let mut handlers: Vec<Box<dyn InputHandler>> = vec![];
    // Shortcut needs to go before IME.
    add_shortcut_handler(&mut handlers).await;
    add_ime(&mut handlers).await;
    add_touch_handler(..., &mut handlers).await;
    add_mouse_handler(..., &mut handlers).await;
    handlers
}

2021 年,该平台提供了两种输入流水线实现,每种实现都作为 fuchsia.git 中的一个组件实现,能够利用 fuchsia SDK 中未发布的特权 API。这些实现共享许多输入处理程序(它们运行相同的代码),主要区别在于支持哪些输入模式。一种实现针对主要依赖触摸屏互动的设备进行了优化,另一种实现围绕具有鼠标和键盘的产品。特定于产品的代码仅限于确定要实例化的处理程序的设置代码。

在短期内,可通过公开一项 capability(可能采用 FIDL API 的形式,用于通过会话框架进行中介的配置)来为会话授予有限的可配置性,以允许会话确定要启用的输入模式。此 API 允许按产品进行配置,并将在会话启动时(即配置产品用户体验时)生效。我们无意支持在正常的产品操作过程中“实时”重新配置流水线。

随着 Fuchsia 扩展以支持更多产品类别,输入处理程序和不同的产品配置可能会扩展,并且可能需要公开一项功能,让会话能够从各种流水线配置中进行选择,或者直接配置应存在哪些输入流水线阶段以及按何种顺序显示。某些输入处理程序可能因产品而异,并且需要位于 fuchsia.git 之外。

每个产品都应该能够使用根据其特定需求量身定制的输入流水线,其中包含该产品支持的输入行为所需的输入处理程序。如何确切实现该过程的决定超出了此 RFC 的范围。理想情况下,任何此类配置解决方案都将利用平台提供的结构化配置机制,而不是发明特定于输入的内容,并且应尽力提供满足产品需求所需的最小可能配置表面。

视图和事件路由

Fuchsia 界面可能会在屏幕上同时包含来自多个运行时的图形。界面被整理成由 Scenic 拥有的全局场景图,其中包含构成界面的图形内容以及渲染界面所需的信息。运行时可以将用户可见内容添加到场景图中称为“视图”的景观资源中,从而向场景贡献内容。通常情况下,一个视图对应于屏幕上的一个区域,但这些区域不一定是矩形,而且并非所有视图在给定时间都可见。由于每个视图都是景观本地资源,不适合在其他组件中引用,因此我们将每个视图都关联了一个称为 ViewRef 的内核对象。

视图以分层方式进行组织,子视图只能影响其父视图边界内的屏幕空间,即严格的包含模型。”当一个视图是图表中另一个视图的父视图时,父视图会保留对其子视图的特定控制权,特别是在它与内容放置和事件路由有关方面。

查看树

下图说明了场景图根目录下的结构。

root -> 无障碍视图 -> sysUI -> {some view, some other view, but
another view}

  • 借助与无障碍功能管理器关联的视图,无障碍功能管理器可以拦截无障碍服务所需的输入事件、更改焦点,以及在必要时在界面其余部分的“上层”绘制。
  • “sysUI”(系统界面)视图是所有其他视图的父视图。该视图通常用于实现“系统手势”(例如在屏幕边缘滑动以更改应用)和系统键盘快捷键。它还可用于界面的许多非输入方面。

路由图形事件

对于与屏幕上的特定位置(例如轻触、鼠标、触控笔)对应的用户事件,输入事件会先通过 Views 进行路由,然后再分派给与每个视图关联的运行时实例。这种方法的优势包括

  • 场景图隔离:Scape 是唯一一个全面了解哪个视图在屏幕上的位置的组件。我们不允许其他组件在给定点查找屏幕上的内容(也称为“命中测试”)。
  • 全局一致性:由于 View 是指定视图在特定时间出现在屏幕上的可信来源,因此设置 View 调度事件可避免视图在输入事件发生与传递期间发生大小、位置或消失的问题。对于在用户与设备互动时可能会频繁更改的产品来说,这一点尤为重要。请注意,Sensing 还拥有焦点链,该链用于确保非指针事件的分派保持一致。
  • 并行调度(手势消除歧义):当视图重叠时,可能会有多个视图对特定事件流感兴趣。例如,产品可能希望实现“系统手势”(如滑动关闭应用),而应用本身可能会以不同的方式解读该手势。景观负责确定哪些视图应接收给定事件,并通过一个称为“手势消除歧义”的过程在它们之间进行中介。在此模式下,触摸事件流会并行分派给多个视图,然后在“手势竞技场”中进行解析,以确定最终将使用事件流的视图。

对焦

为了确定当前正在运行的哪块软件应接收给定的输入事件,我们需要视图焦点的概念。这大致是指当前处于“活动”状态且已准备好接收事件的视图。但实际上,每个事件可能会有多个数据视图都感兴趣。

视图焦点通过焦点链确定(ViewRef 的矢量,对应于聚焦的视图及其在视图树中的祖先实体)。焦点链归 Views 所有(因为 Views 管理视图),但其他组件可能会请求更改当前焦点,例如响应无障碍操作或键盘快捷键。输入流水线负责监控焦点链中的更改,并向输入处理程序提供信息,输入处理程序继而可将事件转发到正确的客户端组件/视图。焦点链的终端元素对应于当前聚焦的视图。

一些处理程序对单个聚焦视图感兴趣,而其他处理程序则对整个焦点链感兴趣。例如:

  • 未触发键盘快捷键的键盘事件通常会路由到当前聚焦的视图。
  • 无障碍功能管理器使用聚焦视图来确定在屏幕阅读器处于启用状态时,屏幕阅读器当前“活跃”的内容。
  • 键盘快捷键管理器关注整个焦点链,其中任何焦点链都可能注册了键盘快捷键和关联的优先级。

焦点事件作为一种特殊的 InputEvent 类型在输入流水线中移动,并与其他输入事件严格排序。

事件流一致性

事件流是指一组相关的 InputEvent,这些输入事件通常在时间上很近发生。例如:

  • 键盘:'a' KEY DOWN -> 'a' KEY UP
  • 鼠标:悬停 -> 悬停 -> BUTTON_DOWN -> BUTTON_UP
  • 触摸:手指向下 -> 移动 -> 移动 -> 移动 -> 向上手指

系统必须确保在流水线的每个阶段和每个视图中的事件流保持一致。这意味着,如果输入处理程序通过将事件路由到系统服务来使用事件,则应将适当的“已处理”事件发送到后续输入处理程序,以便它们可以通知任何客户端。

如果在处理事件流期间焦点发生变化,也是如此。从客户端的角度来看,每个“按下按键”都必须与相应的“按下按键”匹配,如果焦点发生变化或输入设备断开连接,则匹配一个“取消”事件。对于鼠标点击和触摸事件流也是如此。输入流水线负责将事件标记为“已处理”,并通过输入流水线传播它们,以确保数据流一致性。系统服务负责在数据流被取消时通知视图。

性能

可接受的延迟时间

用户输入具有时效性。延迟时间(即从事件发生到界面响应的时间)最好尽可能短,但用户对延迟的容忍度因输入类型而异。在某些情况下,用户可能会遇到性能下降(能否完成某项任务)和满意度低至 10 毫秒的延迟。超过 100 毫秒时,用户体验会开始明显下降,超过 300 毫秒可能会无法接受。直接操纵(例如,使用触控笔在屏幕上绘图)对延迟尤其敏感,并且可能需要预测事件以提供可接受的用户体验。(如需了解背景信息,请参阅这份延迟论文。)

由于此 RFC 中描述的输入系统在运行级别低于运行时及其构建的界面,因此用户会经历系统输入延迟,以及因处理并呈现对输入事件的响应的时间而导致的任何延迟。因此,输入系统应尽可能快地将尽可能多的“延迟时间预算”留给应用和运行时。

此外,持续的时间节奏也很重要。即使平均事件传送时间很短,事件时间的大幅度变化也会降低用户体验,因此查看延迟时间分布情况和平均值非常重要。

提高性能

减少输入架构延迟的最佳方法是最大限度地减少不必要的进程上下文切换。每次进入和退出内核时(通常需要运行调度器),都会给事件时间带来变化。

未来,我们可能会探索将图形和输入堆栈中的多个组件(例如,景观和输入管道)作为单个进程中的单独组件运行,以进一步减少进程的跳数。如果发现选择 Rust 会导致额外的延迟,我们可能还会重新考虑选择 Rust 作为输入流水线实现语言的情况。

针对触摸事件引入手势消除歧义(也称为并行调度)后,在等待感兴趣的组件响应给定事件流时,可能会引入额外的延迟。为了使此算法高效运行,客户端必须与输入事件协同合作并及时响应。系统将需要某种机制来指定和强制执行客户端延迟时间预期 (SLA)。我们会在未来的设计中详细阐述这一点。

国际化和输入上下文

每个视图都有自己的输入上下文,其中包括有效的输入法(“主动键盘”)。输入上下文与 fuchsia.intl.Profile 中的信息不同,后者包含用户首选语言区域并会影响界面呈现。不过,用户的语言区域设置可能会影响哪些输入法/键盘布局可用。如需详细了解 Fuchsia 国际化,请参阅 Fuchsia 国际化文档

系统应允许不同的视图具有不同的有效输入法。例如,用户可能在用一种语言撰写电子邮件,同时用另一种语言聊天。产品可以选择强制执行单个系统级语言区域或活动输入法,但架构必须为每个视图支持不同的输入上下文。将来,可能会决定如何存储这些设置。

除了将事件路由到正确的视图之外,输入管道(以及 IME 管理器等关联的系统组件)在解释输入事件时还会使用该视图的输入上下文。例如,输入流水线会使用与发生该事件的输入上下文中处于活动状态的键盘布局相关的信息为实体键盘事件添加注解。与焦点更改一样,对使用中的键盘布局所做的更改应考虑输入事件并与其他事件依序处理,以避免状态变化时出现竞态条件。

无障碍功能

为了使用户无论能力如何,都可以使用 Fuchsia 设备,Fuchsia 无障碍框架提供了许多无障碍功能,可以改变用户与设备互动的方式。具体而言,这可实现以下功能:

  • 可“放大”部分或全部界面的放大镜。
  • 一种屏幕阅读器,可让盲人或弱视用户通过与当前界面对应的“语义树”,在没有视觉输入的情况下进行探索和互动。

当这两项功能或其中一项功能启用时,无障碍功能管理器需要通过输入流水线拦截输入事件,并将其解释为对当前处于活动状态的无障碍功能发出的命令。这些命令可以使用多种输入模式,具体取决于设备类型。例如,工作站屏幕阅读器主要使用键盘快捷键进行操作,而触摸屏设备上的屏幕阅读器可能会使用一系列点按和滑动操作。无障碍功能管理器可能会决定只使用部分事件(例如放大镜,它会使用一些手势,但允许其他手势传递到界面)或所有事件(例如将事件转换为语义操作的屏幕阅读器)。

无障碍功能管理器会与每个视图保持连接,以便通过语义 API fuchsia.accessibility.semantics 检查、描述该视图中的界面元素并与之互动。例如,启用了屏幕阅读器的“点按两次”通常作为当前所选语义节点上的语义“默认操作”传递给视图。

安全注意事项

输入流水线和关联的系统组件使用一些未在 SDK 中发布特权 API。通过要求会话使用输入流水线进行输入处理,平台能够限制可供外部软件使用的功能。

此外,考虑界面修正攻击(例如点击劫持)也很重要。误导性输入事件可用于在未经用户同意的情况下授予权限(例如,将点击转到恶意网站上的按钮)。虽然在平台级别很难完全防止这种情况发生,但输入架构必须确保将图形事件仅传递给正确的界面组件,并确保产品所有者能够轻松了解输入事件通过系统的流动。

隐私注意事项

对输入流水线的更改应接受隐私权审核,因为攻击者可能会获取用户输入的内容来创建击键记录程序或其他恶意软件。除恢复模式外,为防止出现这种情况,输入流水线应该是允许直接从驱动程序直接执行用户输入事件的唯一组件。

测试

测试输入流水线

您应通过与支持的输入功能(例如触控输入、键盘快捷键)对应的封闭式集成测试来验证平台输入行为,与使用这些功能的产品代码无关。测试应在每个受支持的运行时中使用最少的图形组件来验证相关功能。对于允许开发者在树外构建产品,请务必确保不依赖于特定产品的功能稳定性。

封闭测试带来了许多不在此 RFC 讨论范围之内的挑战。

测试其他一切

端到端测试在很大程度上依赖于合成输入事件,以在测试中以可重现的方式伪造用户互动。虽然大多数界面框架都包含某种注入事件的方法(例如 Flutter 驱动程序),但这不足以测试任何涉及多个运行时的情况。这意味着,Fuchsia 必须提供适当的 API 来创建虚假输入。这是通过 SL4F 和 Fuchsia 输入合成库来实现的,该库通过专用注入 API 将事件插入输入流水线。此 API 应仅在开发者 build 中提供,绝不能出现在正式版 build 中,因为它允许注入任意输入。

输入行业

除了此处介绍的概要架构之外,支持每种不同类型的输入设备会导致极大的复杂性。这些详细信息将在后续 RFC 中得到解决。主要的输入行业包括:

  • 实体键盘(蓝牙和 USB)
  • 虚拟键盘或屏幕键盘
  • 鼠标
  • 触控
  • 触控板

文档

此 RFC 的内容以及实现详情应添加到 Fuchsia 公开文档中。

已考虑的替代方案

本部分包含所讨论的一部分(广泛)替代方案。

将 Root Presenter 演变为流水线

一直以来,SSI 均负责分派所有用户输入事件,包括没有图形/位置组件的键盘事件。虽然键盘事件已被移除,但目前在某些产品配置中会用到它。可以扩展 root presenter 中的现有输入处理代码,以处理其他用例。但是,此代码缺少测试覆盖范围,需要进行大量重写,才能实现所需的一致性和可配置性属性,并移除输入处理与图形 API 之间的不必要耦合。

产品特定的景观

由于输入(尤其是基于指针的输入)与图形密切相关,因此您可以选择通过 Fuchsia 图形引擎 Scenic 路由输入处理。这种架构与当前状态大相径庭。在此版本中,合成器被排除在 View 之外,并以“立即模式”运行,这意味着只要窗口管理器做出更改,它就必须绘制。Views 成为窗口管理器,被视为特定于产品的组件,需要为每个产品类别采用不同的实现。输入通过此组件路由。

虽然这种方法在任何单个产品中都表现良好,但需要在图形引擎中嵌入单个产品的所有输入自定义操作。这意味着,对于每个新的商品类型,都需要专门实现 reCAPTCHA。这种方法在未来可能是一项有价值的优化,但被认为对当前用例来说过于庞大。