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,可以是硬件提供的,也可以是从原始数据计算出来的 [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

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

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 轴对齐时,应返回范围 max;当与负方向的 X 轴对齐时,应返回范围 -max。

触摸椭圆默认是对称的。 对于能够进行真正 360 度方向的设备,报告的方向必须超过范围 max 才能指示超过四分之一圈。 对于倒置的手指,应返回范围 max * 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. 手指跟踪

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

3.9. 手势

在创建手势事件的具体应用中,TOUCH 和 WIDTH 参数可以用来近似手指压力或区分食指和拇指。添加 MINOR 参数后,还可以区分扫动的手指和指向的手指,而通过 ORIENTATION,可以检测手指的扭动。

3.10. 注释

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

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