3. 多点触控 (MT) 协议

版权:

© 2009-2010 Henrik Rydberg <rydberg@euromail.se>

3.1. 简介

为了充分利用新型多点触控和多用户设备的强大功能,需要一种方法来报告来自多个接触点的详细数据,即与设备表面直接接触的物体。本文档描述了多点触控 (MT) 协议,该协议允许内核驱动程序报告任意数量的接触点的详细信息。

该协议根据硬件的功能分为两种类型。对于处理匿名接触点的设备(A 型),该协议描述了如何将所有接触点的原始数据发送给接收器。对于能够跟踪可识别接触点的设备(B 型),该协议描述了如何通过事件槽发送单个接触点的更新。

注意

MT 协议 A 型已过时,所有内核驱动程序都已转换为使用 B 型。

3.2. 协议用法

接触点详细信息作为单独的 ABS_MT 事件数据包按顺序发送。只有 ABS_MT 事件才被识别为接触点数据包的一部分。由于这些事件会被当前单点触控 (ST) 应用程序忽略,因此 MT 协议可以在现有驱动程序中的 ST 协议之上实现。

A 型设备的驱动程序通过在每个数据包末尾调用 input_mt_sync() 来分隔接触点数据包。这将生成一个 SYN_MT_REPORT 事件,该事件指示接收器接受当前接触点的数据并准备接收另一个接触点的数据。

B 型设备的驱动程序通过在每个数据包的开头调用 input_mt_slot() (以槽位作为参数) 来分隔接触点数据包。这将生成一个 ABS_MT_SLOT 事件,该事件指示接收器准备接收给定槽位的更新。

所有驱动程序都通过调用常用的 input_sync() 函数来标记多点触控传输的结束。这将指示接收器对自上次 EV_SYN/SYN_REPORT 以来累积的事件采取行动,并准备接收一组新的事件/数据包。

无状态 A 型协议和有状态 B 型槽位协议之间的主要区别在于使用可识别的接触点来减少发送到用户空间的数据量。槽位协议需要使用 ABS_MT_TRACKING_ID,该 ID 可以由硬件提供,也可以从原始数据计算得出 [5]

对于 A 型设备,内核驱动程序应生成当前在表面上的全套匿名接触点的任意枚举。数据包在事件流中出现的顺序并不重要。事件过滤和手指跟踪留给用户空间 [3]

对于 B 型设备,内核驱动程序应将每个已识别的接触点与一个槽位相关联,并使用该槽位来传播该接触点的更改。接触点的创建、替换和销毁通过修改关联槽位的 ABS_MT_TRACKING_ID 来实现。非负跟踪 ID 被解释为接触点,值 -1 表示未使用的槽位。以前不存在的跟踪 ID 被认为是新的,而不再存在的跟踪 ID 被认为是已删除的。由于只传播更改,因此每个已启动接触点的完整状态必须驻留在接收端。接收到 MT 事件后,只需更新当前槽位的相应属性即可。

某些设备识别和/或跟踪的接触点比它们可以报告给驱动程序的接触点多。此类设备的驱动程序应将一个 B 型槽位与硬件报告的每个接触点相关联。每当与槽位关联的接触点的身份更改时,驱动程序应通过更改其 ABS_MT_TRACKING_ID 来使该槽位无效。如果硬件指示它正在跟踪比当前报告更多的接触点,则驱动程序应使用 BTN_TOOL_*TAP 事件来通知用户空间此时硬件正在跟踪的总接触点数。驱动程序应通过显式发送相应的 BTN_TOOL_*TAP 事件并在调用 input_mt_report_pointer_emulation() 时将 use_count 设置为 false 来实现此目的。驱动程序应仅通告硬件可以报告的槽位数。用户空间可以通过注意最大的支持的 BTN_TOOL_*TAP 事件大于 ABS_MT_SLOT 轴的 absinfo 中报告的 B 型槽位总数,来检测驱动程序可以报告的总接触点数多于槽位数。

ABS_MT_SLOT 轴的最小值必须为 0。

3.3. 协议示例 A

以下是 A 型设备两次接触的最小事件序列

ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
SYN_MT_REPORT
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT

移动其中一个接触点后的序列看起来完全相同;在每次与 SYN_REPORT 同步之间,都会发送所有当前接触点的原始数据。

以下是抬起第一个接触点后的序列

ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_MT_REPORT
SYN_REPORT

以下是抬起第二个接触点后的序列

SYN_MT_REPORT
SYN_REPORT

如果驱动程序除了 ABS_MT 事件之外还报告 BTN_TOUCH 或 ABS_PRESSURE 中的一个,则可以省略最后一个 SYN_MT_REPORT 事件。否则,最后一个 SYN_REPORT 将被输入核心丢弃,导致没有零接触事件到达用户空间。

3.4. 协议示例 B

以下是 B 型设备两次接触的最小事件序列

ABS_MT_SLOT 0
ABS_MT_TRACKING_ID 45
ABS_MT_POSITION_X x[0]
ABS_MT_POSITION_Y y[0]
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT

以下是接触点 45 在 x 方向移动后的序列

ABS_MT_SLOT 0
ABS_MT_POSITION_X x[0]
SYN_REPORT

以下是抬起槽位 0 中的接触点后的序列

ABS_MT_TRACKING_ID -1
SYN_REPORT

正在修改的槽位已经是 0,因此省略了 ABS_MT_SLOT。此消息删除了槽位 0 与接触点 45 的关联,从而销毁了接触点 45 并释放了槽位 0 以供另一个接触点重复使用。

最后,以下是抬起第二个接触点后的序列

ABS_MT_SLOT 1
ABS_MT_TRACKING_ID -1
SYN_REPORT

3.5. 事件用法

定义了一组具有所需属性的 ABS_MT 事件。这些事件分为几类,以允许部分实现。最小集合包括 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y,它们允许跟踪多个接触点。如果设备支持,可以使用 ABS_MT_TOUCH_MAJOR 和 ABS_MT_WIDTH_MAJOR 分别提供接触区域和接近工具的大小。

TOUCH 和 WIDTH 参数具有几何解释;想象一下通过窗户看着某人轻轻地将手指按在玻璃上。您将看到两个区域,一个内部区域由实际接触玻璃的手指部分组成,一个外部区域由手指的周长形成。接触区域 (a) 的中心是 ABS_MT_POSITION_X/Y,接近的手指 (b) 的中心是 ABS_MT_TOOL_X/Y。触摸直径是 ABS_MT_TOUCH_MAJOR,手指直径是 ABS_MT_WIDTH_MAJOR。现在想象一下,这个人更用力地将手指按在玻璃上。接触区域将增加,并且通常,始终小于 1 的比率 ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR 与接触压力有关。对于基于压力的设备,可以使用 ABS_MT_PRESSURE 来提供接触区域上的压力。能够接触悬停的设备可以使用 ABS_MT_DISTANCE 来指示接触点与表面之间的距离。

     Linux MT                               Win8
    __________                     _______________________
   /          \                   |                       |
  /            \                  |                       |
 /     ____     \                 |                       |
/     /    \     \                |                       |
\     \  a  \     \               |       a               |
 \     \____/      \              |                       |
  \                 \             |                       |
   \        b        \            |           b           |
    \                 \           |                       |
     \                 \          |                       |
      \                 \         |                       |
       \                /         |                       |
        \              /          |                       |
         \            /           |                       |
          \__________/            |_______________________|

除了 MAJOR 参数之外,还可以通过添加 MINOR 参数来描述触摸和手指区域的椭圆形形状,使得 MAJOR 和 MINOR 是椭圆的长轴和短轴。触摸椭圆的方向可以用 ORIENTATION 参数描述,手指椭圆的方向由向量 (a - b) 给出。

对于 A 型设备,可以通过 ABS_MT_BLOB_ID 进一步指定触摸形状。

ABS_MT_TOOL_TYPE 可以用来指定触摸工具是手指还是笔或其他东西。最后,ABS_MT_TRACKING_ID 事件可以用来随着时间的推移跟踪已识别的接触点 [5]

在 B 型协议中,ABS_MT_TOOL_TYPE 和 ABS_MT_TRACKING_ID 由输入核心隐式处理;驱动程序应改为调用 input_mt_report_slot_state()

3.6. 事件语义

ABS_MT_TOUCH_MAJOR

接触点长轴的长度。长度应以表面单位给出。如果表面具有 X 乘以 Y 的分辨率,则 ABS_MT_TOUCH_MAJOR 的最大可能值为 sqrt(X^2 + Y^2),即对角线 [4]

ABS_MT_TOUCH_MINOR

接触面的短轴长度,以表面单位计。如果接触面是圆形的,则可以省略此事件[4]

ABS_MT_WIDTH_MAJOR

接近工具的长轴长度,以表面单位计。这应理解为工具本身的大小。接触面和接近工具的方向被假定为相同[4]

ABS_MT_WIDTH_MINOR

接近工具的短轴长度,以表面单位计。如果为圆形则省略[4]

以上四个值可用于推导有关接触面的其他信息。ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR 的比率近似于压力的概念。手指和手掌都有不同的特征宽度。

ABS_MT_PRESSURE

接触区域上的压力,以任意单位表示。可以用于基于压力的设备或任何具有空间信号强度分布的设备,以替代 TOUCH 和 WIDTH。

如果分辨率为零,则压力数据为任意单位。如果分辨率非零,则压力数据的单位为单位/克。有关详细信息,请参见输入事件代码

ABS_MT_DISTANCE

接触面与表面之间的距离,以表面单位表示。零距离表示接触面正在接触表面。正数表示接触面悬停在表面之上。

ABS_MT_ORIENTATION

接触椭圆的方向。该值应描述围绕触摸中心顺时针旋转的四分之一圈的有符号值。有符号值的范围是任意的,但是当椭圆与表面的 Y 轴(北)对齐时应返回零,当椭圆向左转动时应返回负值,当椭圆向右转动时应返回正值。当与正方向的 X 轴对齐时,应返回范围最大值;当与负方向的 X 轴对齐时,应返回范围最小值。

默认情况下,触摸椭圆是对称的。对于能够实现真正 360 度方向的设备,报告的方向必须超出范围最大值,以指示超过四分之一圈。对于倒置的手指,应返回范围最大值 * 2。

如果触摸区域是圆形的,或者内核驱动程序中没有该信息,则可以省略方向。如果设备可以区分两个轴,但不能(唯一地)区分两者之间的任何值,则可以使用部分方向支持。在这种情况下,ABS_MT_ORIENTATION 的范围应为 [0, 1] [4]

ABS_MT_POSITION_X

触摸椭圆中心的表面 X 坐标。

ABS_MT_POSITION_Y

触摸椭圆中心的表面 Y 坐标。

ABS_MT_TOOL_X

接近工具中心的表面 X 坐标。如果设备无法区分预期的触摸点和工具本身,则省略。

ABS_MT_TOOL_Y

接近工具中心的表面 Y 坐标。如果设备无法区分预期的触摸点和工具本身,则省略。

四个位置值可用于将触摸的位置与工具的位置分开。如果两个位置都存在,则工具的主轴指向触摸点[1]。否则,工具轴与触摸轴对齐。

ABS_MT_TOOL_TYPE

接近工具的类型。许多内核驱动程序无法区分不同的工具类型,例如手指或笔。在这种情况下,应省略该事件。该协议当前主要支持 MT_TOOL_FINGER、MT_TOOL_PEN 和 MT_TOOL_PALM [2]。对于 B 型设备,此事件由输入核心处理;驱动程序应改为使用 input_mt_report_slot_state()。接触点的 ABS_MT_TOOL_TYPE 可能会在接触设备时随着时间而改变,因为固件可能无法确定首次出现时正在使用哪个工具。

ABS_MT_BLOB_ID

BLOB_ID 将多个数据包分组到一个任意形状的接触面中。点的序列形成一个定义接触面形状的多边形。这是 A 型设备的低级匿名分组,不应与高级跟踪 ID 混淆[5]。大多数 A 型设备不具有 Blob 功能,因此驱动程序可以安全地省略此事件。

ABS_MT_TRACKING_ID

TRACKING_ID 在其整个生命周期中标识一个已启动的接触点 [5]。TRACKING_ID 的值范围应足够大,以确保在较长时间内保持接触点的唯一标识。对于 B 型设备,此事件由输入核心处理;驱动程序应改为使用 input_mt_report_slot_state()

3.7. 事件计算

不同的硬件不可避免地导致某些设备更适合 MT 协议。为了简化和统一映射,本节提供了有关如何计算某些事件的配方。

对于将接触面报告为矩形的设备,无法获得有符号的方向。假设 X 和 Y 是接触矩形边的长度,则以下是一个保留尽可能多信息的简单公式

ABS_MT_TOUCH_MAJOR := max(X, Y)
ABS_MT_TOUCH_MINOR := min(X, Y)
ABS_MT_ORIENTATION := bool(X > Y)

ABS_MT_ORIENTATION 的范围应设置为 [0, 1],以指示设备可以区分沿 Y 轴 (0) 的手指和沿 X 轴 (1) 的手指。

对于具有 T 和 C 坐标的 Win8 设备,位置映射为

ABS_MT_POSITION_X := T_X
ABS_MT_POSITION_Y := T_Y
ABS_MT_TOOL_X := C_X
ABS_MT_TOOL_Y := C_Y

不幸的是,没有足够的信息来同时指定接触椭圆和工具椭圆,因此必须求助于近似值。一种与早期使用兼容的简单方案是

ABS_MT_TOUCH_MAJOR := min(X, Y)
ABS_MT_TOUCH_MINOR := <not used>
ABS_MT_ORIENTATION := <not used>
ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C)
ABS_MT_WIDTH_MINOR := min(X, Y)

原理:我们没有关于接触椭圆方向的信息,因此请使用内切圆来近似它。工具椭圆应与矢量 (T - C) 对齐,因此直径必须随着距离 (T, C) 的增加而增加。最后,假设触摸直径等于工具厚度,我们就得到了上面的公式。

3.8. 手指跟踪

手指跟踪的过程,即为表面上每个启动的接触面分配唯一的跟踪 ID,是一个欧几里得二分匹配问题。在每次事件同步时,将来自先前同步的实际接触点集与接触点集匹配。完整的实现可以在[3]中找到。

3.9. 手势

在创建手势事件的特定应用中,TOUCH 和 WIDTH 参数可用于例如近似手指压力或区分食指和大拇指。通过添加 MINOR 参数,还可以区分扫动手势和指向手势,并且通过 ORIENTATION 可以检测到手指的扭曲。

3.10. 注释

为了与现有应用程序保持兼容,手指数据包中报告的数据不得被识别为单点触摸事件。

对于 A 型设备,所有手指数据都会绕过输入筛选,因为相同类型的后续事件指的是不同的手指。