# multi-touch-protocol

> 原文：[Documentation/input/multi-touch-protocol.txt](https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt)\
> &#x20;翻译：[@wengpingbo](https://github.com/wengpingbo)\
> &#x20;校订：[@lzufalcon](https://github.com/lzufalcon)<br>

### 目录

* [介绍](#toc_27205_4037_1)
* [协议用法](#toc_27205_4037_2)
* [Type A 协议示例](#toc_27205_4037_3)
* [Type B 协议示例](#toc_27205_4037_4)
* [事件的用法](#toc_27205_4037_5)
* [事件的含义](#toc_27205_4037_6)
* [事件计算](#toc_27205_4037_7)
* [手指跟踪](#toc_27205_4037_8)
* [手势](#toc_27205_4037_9)
* [笔记](#toc_27205_4037_10)

## 多点触控协议

> Copyright (C) 2009-2010 Henrik Rydberg <rydberg@euromail.se>

### 介绍

为了充分利用多指触控和多用户设备，我们需要一种能够上报多点接触的数据的方法，例如物体直接和设备表面接触。这个文档描述了一种多点触控协议，它允许内核驱动上报任意数量的触摸点数据。

这个协议根据硬件的能力，可以分为两种。对于不区分触摸点的设备（Type A），该协议描述了怎样把所有触点的原始数据传送给接收者。对于能够跟踪可辨别的触点的设备（Type B），该协议描述了怎样把独立的触点更新数据通过事件通道上报上去。

### 协议用法

触点的数据是通过独立的 `ABS_MT` 事件顺序送出。只有 `ABS_MT` 事件才会被识别为触点数据的一部分。目前，这些事件会被单点触控应用忽略掉，所以多点触控协议可以在已存驱动中得单点触控协议之上实现。

对于 TYPE A 的设备驱动来说，触摸数据是通过在数据包最后调用 `input_mt_sync()` 来分割的。这会生成一个 `SYN_MT_REPORT` 事件，从而通知接收者接受当前触摸数据，并准备下一次接收。

对于 TYPE B 的设备驱动来说，触摸数据的分割是通过在每一个数据包之前调用 `input_mt_slot()`，该函数带有一个 slot 参数。这会生成一个 `ABS_MT_SLOT` 事件，通知接收者准备接受指定通道的更新。

所有驱动都是通过调用 `input_sync()` 函数来标记多点触摸传输的结束。这会通知接收者处理在上一次 `EV_SYN`/`SYN_REPORT` 事件之前累计的事件，并准备接收一批新的事件 / 数据包。

无状态的 TYPE A 协议和有状态的 TYPE B 协议之间主要的差别在于对可分辨的触摸点的使用，来减少传送给用户空间的数据总量。TYPE B 协议要求使用 `ABS_MT_TRACKING_ID`，通过硬件提供，或者通过原始数据计算出来 \[5]。

对于 TYPE A 的设备，内核驱动应该为当前还在设备上的所有触摸点生成一个随机的枚举（**注：**&#x7F16;号？？）。数据包在事件流上出现的顺序并不重要。事件过滤和手指跟踪是留给用户空间去做 \[3]。

对于 TYPE B 的设备，内核驱动需要把每一个可分辨的触摸点和一个通道联系在一起，并且使用该通道来传送该触摸点的变动。触摸点的创建，替换和消除可以通过修改相应通道的 `ABS_MT_TRACKING_ID` 来实现。一个非负数的 TRACKING\_ID 代表一个触摸点，-1 代表一个不使用的通道。一个新的 TRACKING\_ID 代表一个新的触摸点，而一个不出现的 TRACKING\_ID 代表触摸点已经移除了。由于是增量传递，接收端会保留每一个触控点的全部状态属性。当接受到一个 MT 事件后，只需要更新当前通道特定的属性。

有一些设备能够分辨亦或跟踪多个可以上报给驱动的触摸点（**注：**&#x8FD9;里原文是 more contacts than they can report to the driver，但语义不通。这里根据上下文把 than 改为 that 来翻译）。该类型设备的驱动应该把硬件上报的每一个触摸点都和一个 Type B 的通道相关联。当区分出和一个通道相关联的触摸点改变时（**原文：**&#x69;dentity of the contact associated with a slot changes），驱动应该通过改变它的 `ABS_MT_TRACKING_ID` 来关闭该通道。若硬件上报有新增的触摸点（**注：**？？），驱动应该使用 `BTN_TOOL_*TAP` 事件来通知用户空间当前硬件上跟踪的触摸点总数。当调用 `input_mt_report_pointer_emulation()` 时，驱动应该明确的发送 `BTN_TOOL_*TAP` 事件，并且把 `use_count` 置为 false。驱动最多只能创建硬件支持的最大触摸点数的通道。用户态程序可以通过发现最大支持的 `BTN_TOOL_*TAP` 事件比 `ABS_MT_SLOT` 轴上报的 Type B 通道的总数大，来检测出这种情况。（**注：**？？）

`ABS_MT_SLOT` 轴的最小值必须为 0。

### Type A 协议示例

这是 Type 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` 事件也会被 INPUT 核心系统丢掉，导致触点清除事件（**注：**&#x7A;ero-contact event）无法到达上层。

### Type B 协议示例

这是 Type 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
```

这是 ID 为 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
```

### 事件的用法

`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` 指定。现在想象一下这个人用力按压玻璃面板，通常，真实触摸的区域将会增加，`ABS_MT_TOUCH_MAJOR` / `ABS_MT_WIDTH_MAJOR` 的比例也会随着压力的增大而增大，但总是比 1 小。对于能感知压力的设备，`ABS_MT_PRESSURE` 可以用于上报设备上的压力值。支持悬浮操作的设备可以用 `ABS_MT_DISTANCE` 来表示当前触摸距离面板表面的距离。

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

除了 `MAJOR` 参数外，触摸和手指的椭圆形状也可以通过添加 `MINOR` 参数来表示，这样 `MAJOR` 和 `MINOR` 就分别代表椭圆的长短轴。椭圆形触摸的方向可以通过 `ORIENTATION` 参数来表示，手指形成的椭圆形朝向是由向量 （a - b）决定。

对于 Type A 的设备来说，未来的标准中，触摸形状可能用 `ABS_MT_BLOB_ID`。

`ABS_MT_TOOL_TYPE` 用于指定触摸工具是手指，触摸笔或者其他工具。最后，`ABS_MT_TRACKING_ID` 事件可以用来跟踪不同事件下相同的触点 \[5]。

在 Type B 协议中，`ABS_MT_TOOL_TYPE` 和 `ABS_MT_TRACKING_ID` 事件是在 INPUT 核心中处理的；驱动应该使用 `input_mt_report_slot_state()`。

### 事件的含义

* `ABS_MT_TOUCH_MAJOR`

  触点长轴的长度。该长度应该和屏幕尺寸单位一致。若屏幕有 X \* Y 的分辨率，则 `ABS_MT_TOUCH_MAJOR` 最大的长度为对角线 - `sqrt(X^2 + Y^2)`。
* `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`

  触点和屏幕表面之间的距离，屏幕尺寸单位。0 距离意味着触点和屏幕是接触的。一个正数意味着触点是悬浮在屏幕之上的。
* `ABS_MT_ORIENTATION`

  触点椭圆外形的方向。该值应该描述触点中心顺时针一周中的 1/4 的方位。带符号数值的范围是随意的。但是，当触点椭圆外形和表面 Y 轴对齐时，应该返回 0 值。当椭圆外形向左转变时，应该返回负值，向右转变时，应该返回正值。当完全和 X 轴对齐时，应该返回范围最大值。

  触点椭圆外形默认是对称的。对于那些能够检测 360 度方向的设备，上报的值一定要超过范围最大值，来显示大于一周的 1/4。对于一个颠倒的手指，应该返回 `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 轴坐标值。若设备无法分辨触摸点和触摸工具自身时，该事件可以忽略。

  这 4 个位置值可以用于分割触点位置和触摸工具位置。若两者都有，工具轴是指向触点的。否则，工具轴和触点轴是对齐的。
* `ABS_MT_TOOL_TYPE`

  触摸工具的类型。许多内核驱动无法分辨触摸工具的类型，例如是手指或者触摸笔。在这种情况下，这个事件应该被忽略掉。这个协议当前支持 `MT_TOOL_FINGER` 和 `MT_TOOL_PEN` 两者类型 \[2]。对于类型 B 的设备，这个事件是由 INPUT 子系统核心处理。驱动应该使用 `input_mt_report_slot_state()` 函数。
* `ABS_MT_BLOB_ID`

  `BLOB_ID` 把多个包组合成一个任意形状的触点。顺序的坐标点形成一个多边形，定义了触点的形状。这是一个类型 A 设备上的匿名分组，应该和 trackingID 区分开。大部分类型 A 设备没有 ‘BLOB’ 能力，所以驱动可以安全的忽略这个事件。
* `ABS_MT_TRACKING_ID`

  `TRACKING_ID` 标示一个触点的整个生命周期 \[5]。`TRACKING_ID` 的数值范围应该足够大，从而保证一段时间类的每一个触点标示都是唯一的。对于类型 B 设备来说，这个事件是由 INPUT 子系统核心处理，驱动应该使用 `input_mt_report_slot_state()` 函数。

### 事件计算

由于不同硬件的差异，不可避免的会导致有一些设备比其他设备更适合多指触控协议。为了简化和统一，这个章节列举了一些特定事件的计算。

对于上报长方形触点的设备，带符号的方向是获取不到的。假定 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_X := 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) 向量的长度。最后，假定触点直径是等于工具的厚度的，这样我们就有了以上公式。

### 手指跟踪

手指的跟踪流程，例如给每一个表面上的触点分配一个独立 trackingID，是一个欧几里德二分图匹配问题。在每一个事件同步时，实际的触点集合和前一个同步时的触点集合是匹配的。完整的实现可以在 \[3] 找到。

### 手势

在一个实际的创建手势事件的应用中， TOUCH 和 WIDTH 参数可以用于估算手指触摸的压力或者区分拇指和其他手指。加上额外的 MINOR 参数，我们也可以区分滑动的手指和点击的手指，若再加上 ORIENTATION，我们还可以检测手指的转动。

### 笔记

为了和现有的应用保持兼容，在一个手指数据包里，上报的数据一定不能识别为单一触摸事件。

对于类型 A 设备来说，由于随后相同事件类型的事件指向不同的手指，所有的触摸数据都会跳过 INPUT 过滤。

类型 A 协议的使用范例见 bcm5974 驱动。对于类型 B 协议的使用范例，见 hid-egalax 驱动。

* \[1] 同样, (`TOOL_X` - `POSITION_X`) 的差值可以用于模型倾斜
* \[2] 该列表可以被扩展
* \[3] mtdev 项目主页： <http://bitmath.org/code/mtdev/>
* \[4] 看 \[事件计算] 小节
* \[5] 看 \[手指跟踪] 小节


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tinylab-1.gitbook.io/linux-doc/zh-cn/input/multi-touch-protocol.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
