设置和配置 tslib

安装 tslib

tslib 支持多种硬件架构和操作系统,包括 GNU/Linux、FreeBSD 和 Android/Linux。详情请参考构建 tslib 部分。除了从最新发布包构建外,还可以通过运行 ./configure、make 和 make install 安装,或者从如 Arch Linux / Arch Linux ARM、Buildroot、Debian / Ubuntu、Fedora 或 OpenSUSE 这样的发行版及其包管理器获取。

配置 tslib

以下是一个示例 /etc/ts.conf 文件。触点样本自上而下流动。每一行指定一个模块及其参数。模块按顺序处理。使用一个位于顶部的 module_raw 来访问你的设备,然后可以任意组合滤波模块。

module_raw input
    module median depth=3
    module dejitter delta=100
    module linear

raw 未经加工的 英[rɔː] 美[rɔː]
grab 抓住 英[ɡræb] 美[ɡræb]

有关可用滤波器及其参数的详细信息,请参阅下面的部分。在 Linux 上,你的第一条注释行应该是提供一个可选参数 grab_events=1 的 module_raw input,如果你希望它对设备执行 EVIOCGRAB 操作。

有了这个配置文件,我们就得到了如下数据流经库:

驱动程序 --> 原始读取 --> 中值滤波 --> 去抖动 --> 线性 --> 应用(使用 ts_read_mt())
            模块         模块         模块      模块

校准触摸屏

校准由 linear 插件应用,它有自己的配置文件 TSLIB_CALIBFILE。不要手动编辑此文件。它由 ts_calibrate 程序创建:

# ts_calibrate

校准过程简单地要求你尽可能准确地在屏幕上触摸一个十字。

测试过滤后输入行为

你可以快速测试由配置好的过滤器产生的触摸行为,使用 ts_test_mt:

# ts_test_mt


屏幕左下角会显示支持的同时触控点数,以及这是由于驱动程序报告,还是 ts_test_mt 使用 -j 命令行选项覆盖了它。

环境变量(可选)

你可以覆盖默认值。但大多数情况下你可能不需要这样做:

  • TSLIB_TSDEVICE:触摸屏设备文件名。默认:自动检测(在 Linux)
  • TSLIB_CALIBFILE:校准文件。默认:${sysconfdir}/pointercal
  • TSLIB_CONFFILE:配置文件。默认:${sysconfdir}/ts.conf
  • TSLIB_PLUGINDIR:插件目录。默认:${datadir}/plugins
  • TSLIB_CONSOLEDEVICE:控制台设备。(不使用–with-sdl2时不需要)。默认:/dev/tty
  • TSLIB_FBDEVICE:帧缓冲设备。默认:/dev/fb0

在系统中使用过滤结果(X.org 方法)

如果你正在使用 X.org 图形化X服务器,事情应该非常简单。安装 tslib 和 xf86-input-tslib,重新启动,你应该立即拥有你的 ts.conf 过滤器运行,无需自己配置其他内容。

在系统中使用过滤结果(ts_uinput 方法)

简而言之: 启动期间运行 tools/ts_uinput_start.sh 并使用 /dev/input/ts_uinput 作为 evdev 输入设备。
tslib 尝试在 Linux 的 /dev/input/event* 自动找到你的触摸屏设备。现在让 ts_uinput 使用它,而不是直接在你的图形环境:

# ts_uinput -d -v

-d 使程序后台运行并返回。
-v 使其在返回前打印新的 /dev/input/eventX 设备节点。

现在让你的图形环境使用这个新的输入设备,使用 evdev 驱动程序。

  • 对于例如 Qt5,你可能会设置类似这样的东西:
    QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/eventX:rotate=0
  • 对于 X11,你可能需要编辑 xorg.conf 中的 “InputDevice” 部分以使你的触摸屏具有如下设置:
    Option "Device" "/dev/input/eventX"
    请咨询你的输入系统的文档,了解如何使用特定的 evdev 输入设备。

让我们回顾一下这里的数据流程:

驱动程序 --> 原始读取 --> 过滤器(s)... --> ts_uinput --> libevdev 读取 --> GUI 应用/工具包 
          模块          模块(s)...        服务进程       例如在 libinput 中

符号链接 /dev/input/ts_uinput 到新的事件文件

/dev/input/event 编号不是持久的。为了预先知道由 ts_uinput 创建的哪个枚举输入设备文件,你可以使用符号链接:

  • 使用提供的 tools/ts_uinput_start.sh 脚本,它启动 ts_uinput -d -v 并为你创建名为 /dev/input/ts_uinput 的符号链接,或者
  • 如果你正在使用 systemd,创建以下 udev 规则,例如 /etc/udev/rules.d/98-touchscreen.rules:
    SUBSYSTEM=="input", KERNEL=="event[0-9]*", ATTRS{name}=="NAME_OF_THE_TOUCH_CONTROLLER", SYMLINK+="input/ts", TAG+="systemd" ENV{SYSTEMD_WANTS}="ts_uinput.service"
    SUBSYSTEM=="input", KERNEL=="event[0-9]*", ATTRS{name}=="ts_uinput", SYMLINK+="input/ts_uinput"
    其中 NAME_OF_THE_TOUCH_CONTROLLER 是你在 cat /proc/bus/input/devices | grep Name 找到的触摸控制器名称。第一个规则只在 tslib 没有自动为你选择正确的设备时才需要。

作为 systemd 服务运行(可选)

如果你必须使用非默认路径,请创建一个包含 tslib 环境的文件,如 /etc/ts.env

TSLIB_CALIBFILE=/etc/pointercal
TSLIB_CONFFILE=/etc/ts.conf
TSLIB_PLUGINDIR=/usr/lib/ts

然后创建一个 systemd 服务文件,例如 /usr/lib/systemd/system/ts_uinput.service

[Unit]
    Description=触摸屏输入
    BindsTo=dev-input-ts.device
    After=dev-input-ts.device
    RequiresMountsFor=/etc/ts.env

    [Service]
    Type=forking
    EnvironmentFile=/etc/ts.env
    ExecStart=/usr/bin/ts_uinput -d

调整路径,它们也可以位于 /usr/local/ 下。

其他操作系统

我们目前尚未知晓有任何工具能读取tslib采样,并利用诸如Windows触控注入API这样的接口(尽管可能将来会出现)。

滤波器模块

线性模块

线性缩放 - 校准 - 模块,主要用于触摸屏坐标转换至屏幕坐标的任务。
它运用了由ts_calibrate工具记录并保存的压力校正值。

参数(通常无需手动设置):

  • rot
    覆写旋转角度。顺时针:rot=1;倒置:rot=2;逆时针:rot=3。默认值:在ts_calibrate校准时的屏幕旋转状态。
  • xyswap
    交换X和Y轴坐标——如果使用了线性校准实用程序ts_calibrate,则不再需要此项设定。
  • pressure_offset
    设置压力值应用的偏移量。默认值:0
  • pressure_mul
    压力值乘法系数。默认值:1。
  • pressure_div
    分割压力值的数值。默认值:1。

示例:module linear rot=0

反转模块

围绕指定值,在X和/或Y方向上反转值。未设置默认值。若指定了,则需设置具体数值。如仅对单一轴进行设定,则该轴保留原状不受影响。

参数:

  • x0
    在水平(X轴)上的反转中心点值。
  • y0
    在垂直(Y轴)上的反转中心点值。

示例:module invert y0=640 (对于高度为640像素的屏幕,仅反转Y轴,X轴保持不变)

中值滤波器

中值滤波器能够降低样本坐标中的噪声干扰,有效过滤信号中不希望出现的大幅跳变。关于理论,可参考Wikipedia

参数:

  • depth
    应用中值滤波算法的样本数量。默认值:3。

示例:module median depth=5

压力阈值滤波器

压力阈值滤波器用于剔除低于或高于指定压力阈值的样本。由于释放总是显示压力值为0,而按下则是至少等于1。

参数:

  • pmin
    样本有效的最低压力值。默认值:1。
  • pmax
    样本有效的最高压力值。默认值:(INT_MAX)。

示例:module pthres pmin=10

无限脉冲响应滤波器

无限脉冲响应滤波器是一种平滑滤波手段,去除低频噪声,其中存在降噪与反应速度之间的权衡。N和D两个参数以分数形式(N/D)定义平滑程度。

相关理论参见Wikipedia。

参数:

  • N
    平滑系数分子。默认值:0。
  • D
    平滑系数分母。默认值:1。

示例:module iir N=6 D=10

抖动消除模块

此模块通过加权平滑滤波来消除X和Y坐标上的抖动。最新采集的数据具有最大权重,早期数据权重较小。这使得可以实现1:1输入到输出的比例。更多理论请参考Wikipedia。

参数:

  • delta
    定义“快速移动”阈值的距离平方((X2-X1)^2 + (Y2-Y1)^2)。当笔尖快速移动时,无法进行笔迹平滑处理,且快速运动本身即缺乏精度;因此当检测到快速移动时,模块会清除缓存队列并将输入直接复制为输出。默认值:100。

示例:module dejitter delta=100

消弹跳模块

简单的消弹机制,在一个触碰手势停止后的特定时间内舍弃输入事件。更多理论参见Wikipedia。

参数:

  • drop_threshold

在最后释放事件后,最多丢弃此毫秒数内的事件。默认值:0。

示例:module debounce drop_threshold=40

忽略模块

在按下后忽略前nhead个样本,在释放前忽略后ntail个样本。这有助于排除设备在最初或最后样本可能存在的不稳定情况。

参数:

nhead

在压力发生后应被忽略的事件数量。默认值:1。

ntail

在释放前应被忽略的事件数量。默认值:1。

示例:module skip nhead=2 ntail=1

低通滤波器
简单低通指数平均滤波器模块。

参数:

  • factor
    浮点数值介于0和1之间;例如0.2表示较强平滑,0.8表示较弱。默认值:0.4。
  • threshold
    过滤开始应用于两样本间最小距离(横纵坐标),单位为像素。默认值:2。

示例:module lowpass factor=0.5 threshold=1

事件阈值模块

在“笔/手指按下”之后,驱动程序需连续提供N个输入样本才被视为有效并传递给用户(应用程序)。如果在收集完N个样本之前就发生了“笔/手指抬起”,tslib会放弃此次接触。

此滤波器可用于避免因电磁干扰造成的瞬时接触,这些接触通常短于真实用户操作的时间。

不同于skip滤波器,evthres滤波器不会切割真实触控输入的一部分,只会完全删除整个“轻按”,如果其持续时间足够短暂的话。

相较于debounce滤波器,此滤波器会对每次“笔/手指按下”的情况生效,包括首次,而不只从第二次开始。此外,“debounce”基于时间间隔,而非事件数目。

参数:

  • N
    设备驱动程序在“按下”与“抬起”之间需提供的事件数目,以此判断触控是否真实并转发。

示例:module evthres N=5

裁剪模块

裁剪模块旨在排除那些落在x/y事件代码的Min/Max值范围之外的输入触控事件,换句话说,过滤掉那些可能因为已施加的滤波效果而位于可见帧缓冲区外的触点。

当指定坐标超出屏幕框架时,它会发送“笔/手指抬起”命令给相应的槽位。

它会从TSLIB_CALIBFILE中读取框架尺寸信息,该文件是由ts_calibrate工具生成并保存的。

示例:module crop

方差滤波器

方差滤波器致力于尽可能地过滤来自触摸屏ADC的随机噪声。这是通过限制样本移动速度至一定阈值下完成的,比如笔尖不应该比某个阈值更快地移动。

当前此滤波器暂无多点触控支持(未来可能会有)。当使用此滤波器时,ts_read_mt()将您的输入限定在一个槽位内,建议尝试使用中值滤波器替代。

参数:

  • delta

设置触摸屏单位下的前后笔位置间的距离平方((X2-X1)^2 + (Y2-Y1)^2),此值决定了判断两个样本是否“接近”或“远离”的标准。

如果前后样本之间的距离被认定为“远离”,那么这个样本会被标记为“潜在噪声”。但这并不意味着它一定会被丢弃;如果后续读数接近它,会被视作正常的“快速移动”事件,得以进入下一阶段。另外,如果紧跟在“潜在噪声”样本后的那个样本也与前述样本“远离”,这种情况同样被视为“快速移动”事件,让该样本流入输出流中。

以上示例配置:

       |--------|       |-----|      |--------------|
x ---> | median | ----> | IIR | ---> |              | ---> x'
       |--------|    -> |-----|      |    屏幕      |
                    |                |    转换      |
                    |                |   (校准)     |
       |--------|   |   |-----|      |              |
y ---> | median | ----> | IIR | ---> |              | ---> y'
       |--------|   |-> |-----|      |--------------|
                    |
                    |
             |----------|
p ---------> | debounce | -------------------------------> p'
             |----------|

可通过以下ts.conf配置达到:

module_raw input
module debounce drop_threshold=40
module median depth=5
module iir N=6 D=10
module linear

您自由调整上述参数值。

屏幕旋转

图形工具支持屏幕旋转功能,详情可查阅 ts_calibrate --help或相关手册页。 虽然ts_calibrate在后台会忽略屏幕旋转状态,但它会在“校准文件” TSLIB_CALIBFILE中保存当前的旋转状况。linear过滤模块能够读取这些信息,并根据给定的旋转角度进行调整。这一设置可以通过使用rot模块参数进行覆写:例如,module linear rot=0(当你的工具包或高级应用程序已处理旋转时)。

libts 库

libts 的 API 接口
我们的API文档可以在doc目录下的手册页中找到。想要了解如何使用它,不妨参考我们tests目录中的示例代码。

tslib_version()
ts_libversion()
ts_open()
ts_config()
ts_setup()
ts_close()
ts_reconfig()
ts_option()
ts_fd()
ts_load_module()
ts_read()
ts_read_raw()
ts_read_mt()
ts_read_raw_mt()
int (*ts_error_fn)(const char *fmt, va_list ap)
int (*ts_open_restricted)(const char *path, int flags, void *user_data)
void (*ts_close_restricted)(int fd, void *user_data)
ts_get_eventpath()
ts_print_ascii_logo()
ts_conf_get()
ts_conf_set()

使用 libts

若要在C或C++程序中使用此库,在源代码文件中需包含以下预处理器指令:

#include <tslib.h>

链接该库时,向连接器传递参数-lts即可完成链接操作。

利用autoconf和pkg-config编译

在UNIX系统上,可以借助pkg-config来自动选择适合libts的编译器与连接器开关。你可以通过PKG_CHECK_MODULES宏实现自动设定Makefile变量:

PKG_CHECK_MODULES([TSLIB], [tslib >= 1.10],,
      AC_MSG_ERROR([未发现版本不低于1.10的libts库。])
    )

如果你希望同时兼容版本低于1.2的tslib,但又不放弃对多点触控以及新版本的支持,则可以采取如下方式:

#include <tslib.h>

#ifndef TSLIB_VERSION_MT
/* 这里按旧版tslib的方式调用ts_read() */
#else
/* 调用新版ts_setup()并使用ret = ts_read_mt() */
if(ret == -ENOSYS)
    /* 当用户配置导致问题时按旧法调用ts_read() */
#endif

这是一个完整的示例程序,类似于ts_print_mt.c的例子:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>

#include <tslib.h>

#define SLOTS 5
#define SAMPLES 1

int main(int argc, char **argv)
{
    struct tsdev *ts;
    char *tsdevice = NULL;
    struct ts_sample_mt **samp_mt = NULL;
    int ret, i, j;

    ts = ts_setup(tsdevice, 0);
    if(!ts) {
        perror("ts_setup失败");
        return -1;
    }

    samp_mt = malloc(SAMPLES * sizeof(struct ts_sample_mt *));
    if(!samp_mt) {
        ts_close(ts);
        return -ENOMEM;
    }
    // ...后续代码...
}

以上示例代码省略了部分细节以减少篇幅。

如果知道设备能处理的插槽数,可以直接声明而避免使用malloc:

struct ts_sample_mt TouchScreenSamples[SAMPLES][SLOTS];

struct ts_sample_mt (*pTouchScreenSamples)[SLOTS] = TouchScreenSamples;
struct ts_sample_mt *ts_samp[SAMPLES];
for(i = 0; i < SAMPLES; i++)
    ts_samp[i] = pTouchScreenSamples[i];

然后像下面这样调用ts_read_mt()函数:

ts_read_mt(ts, ts_samp, SLOTS, SAMPLES);

ABI — 应用二进制接口

如Wikipedia所介绍的那样。

libts 儿子名版本号

通常情况下,libts在升级后不会破坏ABI,因此您的应用可以继续使用升级后的libts库。这具体体现在libts库版本号中的主版本号不变;按照我们的版本命名规则,只有在破坏了向后兼容性的情况下才会增加这个数字。次要及修订版本号则随发布更新而递增。举例说明,

libts.so -> libts.so.0.7.0
libts.so.0 -> libts.so.0.7.0
libts.so.0.7.0

使用libts.so无条件引用tslib;使用libts.so.0确保现有应用在升级后仍能正常运行。

若新增功能,次级版本号会递增,修订版本设回零值;主要修正漏洞的话,仅修订版本递增。

tslib 包版本

tslib的tarball版本号无法反映其向后兼容性情况。

相关依赖

libc(动态构建时需要libdl)
libsdl2-dev(使用–with-sdl2构建针对SDL2图形应用时所需)

相关库

libevdev - 用于访问事件设备和uinput(“Linux API”的封装)
libinput - 为Wayland处理输入设备(使用libevdev)
xf86-input-evdev - X使用的evdev插件(使用libevdev)

libts用户

以下列出了tslib日常应用中与外界交互的程序。为了测试目的,还有如ts_test_mt这样的工具。

包含在tslib中的组件

  • ts_calibrate - 图形化校准工具。配置linear和crop过滤模块。
  • ts_uinput - 用户空间evdev驱动,用于tslib过滤后的样本。

第三方应用程序

  • xf86-input-tslib - X11直接使用tslib的输入驱动
  • qtslib - Qt5直接使用的tslib输入插件
  • Enlightenment - 窗口管理器(直接支持帧缓冲模式,X11通过xf86-input-tslib)
  • DirectFB - 基于帧缓冲的图形库

tslib模块API

  • struct tslib_module_info
  • struct tslib_vars
  • struct tslib_ops
  • tslib_parse_vars(struct tslib_module_info *,const struct tslib_vars *, int, const char *);

位于plugins目录下的tslib模块(过滤器或驱动/原始模块)需要实现mod_init()。如果该模块接受参数,它需要声明一个const struct tslib_vars结构体,并在mod_init()期间将其、长度和传递给mod_init的参数字符串传递给tslib_parse_vars()。

此外,还需要声明一个const struct tslib_ops,其成员指向模块实现的模块操作,如read_mt,这些操作会在过滤器链中被调用。

版本符号

名称 引入版本
TSLIB_VERSION_MT 1.10
TSLIB_VERSION_OPEN_RESTRICTED 1.13
TSLIB_VERSION_EVENTPATH 1.15
TSLIB_VERSION_VERSION 1.16
TSLIB_MT_VALID 1.13
TSLIB_MT_VALID_TOOL 1.13
tslib_version 1.16
ts_print_ascii_logo 1.16
ts_libversion 1.10
ts_close 1.0
ts_config 1.0
ts_reconfig 1.3
ts_setup 1.4
ts_error_fn 1.0
ts_open_restricted 1.13
ts_close_restricted 1.13
ts_fd 1.0
ts_load_module 1.0
ts_open 1.0
ts_option 1.1
ts_read 1.0
ts_read_mt 1.3
ts_read_raw 1.0
ts_read_raw_mt 1.3
tslib_parse_vars 1.0
ts_get_eventpath 1.15
ts_conf_get 1.18
ts_conf_set 1.18

编译tslib

动态库与静态库构建

libts可以根据需求进行构建。使用配置脚本来启用你需要的模块。默认情况下,libts作为共享库构建,每个模块本身也是一个共享库对象。不过你可以配置tslib以静态链接方式构建,将所需模块编译到libts内部。如下是一个示例:

./configure --enable-static --disable-shared --enable-input=static --enable-linear=static --enable-iir=static

这将产生一个大约50kb的libts.a库,适用于使用校准(线性过滤器)和ts.conf中的无限脉冲响应滤波器。

使用CMake

也可以使用CMake来构建项目。

mkdir build && cd build
cmake ../

# 或添加配置选项:cmake -Denable-input-evdev=ON ../
cmake --build .
cmake -P cmake_install.cmake

默认情况下,核心tslib作为共享库构建。要构建为静态库,在配置行中添加 -DBUILD_SHARED_LIBS=OFF

同样,默认情况下插件作为共享库构建。要在配置阶段构建静态插件,向配置步骤中添加-Dstatic-<module>=ON。禁用和启用模块,使用标志-Denable-<module>=ON/OFF

在客户端应用中使用tslib

以下是使用CMake构建的tslib在客户端应用中的最小使用示例。将tslib::tslib作为链接目标会添加必要的依赖项和由生成的构建文件提供的包含目录。

cmake_minimum_required(VERSION 3.10)
project(tslib_client)
find_package(tslib 1.16)
add_executable(tslib_client main.c)
target_link_libraries(tslib_client PUBLIC tslib::tslib)

使用SDL2构建可移植的ts_calibrate和ts_test_mt

如果你无法直接在帧缓冲上绘图,可以尝试使用SDL2的实验性实现所需的图形工具。它们更便携,但运行时需要更多资源。确保安装了SDL2及其开发头文件后,使用./configure --with-sdl2

兼容性

tslib跨平台;你应该能在多种操作系统上构建它。

libts和过滤器插件(module)

这是硬件无关的核心部分:libts和所有过滤器模块作为共享库,在以下操作系统上构建,可能还适用于更多系统:

  • GNU/Linux
  • Android/Linux
  • FreeBSD
  • GNU/Hurd
  • Haiku
  • Windows
  • Mac OS X

输入插件(module_raw)

这使事情在现实世界中可用,因为它访问你的设备。有关当前可能的平台配置,参见touchscreen硬件支持。

libts的默认配置当前禁用了以下输入模块:

  • cy8mrln-palmpre

  • dmc_dus3000

  • galax

  • arctic2

  • corgi

  • collie

  • dmc

  • h3600

  • mk712

  • ucb1x00

  • tatung
    请注意,这个列表可能会随着时间的推移而增加。如果你依赖特定的输入插件,应明确启用它。在Linux上,通常只需要input。

  • GNU/Linux - 所有(最重要的是input)
    ./configure

  • Android/Linux - 所有(最重要的是input)
    ./configure

  • FreeBSD - 几乎所有(最重要的是input)
    ./configure –disable-waveshare

  • GNU/Hurd - 部分,参考touchscreen硬件支持
    ./configure –disable-input –disable-waveshare

  • Haiku - 部分,参考touchscreen硬件支持
    ./configure –disable-input –disable-touchkit –disable-waveshare

  • Windows - 尚无针对Windows触摸屏API的tslib模块
    ./configure –with-sdl2 –disable-input –disable-touchkit –disable-waveshare
    如果现有的插件不适用,编写自己的插件相当简单。

测试程序和工具

  • GNU/Linux - 所有
  • Android/Linux - 所有(?)
  • FreeBSD - 所有
  • GNU/Hurd - ts_print_mt, ts_print, ts_print_raw, ts_finddev
  • Haiku - ts_print_mt, ts_print, ts_print_raw, ts_finddev
  • Windows - ts_print.exe, ts_print_raw.exe, ts_print_mt.exe, ts_test_mt.exe, ts_calibrate.exe

下载二进制文件?

对于GNU/Linux的所有架构,由于Debian, Arch Linux和其他发行版,覆盖率非常广。

请帮助完成缺失程序的移植!

触摸屏硬件支持

快速摘要: 在Linux上,使用module_raw input

出于历史原因,tslib包含了特定于设备的module_raw用户空间驱动程序。ts.conf手册页提供了可用的module_raw驱动程序的详细信息;并非所有这些驱动程序都列在默认的etc/ts.conf配置文件中。这些应被视为变通方法,并可能在未来默认配置中被禁用。

强烈建议为您的系统使用真正的设备驱动程序,并利用tslib的通用访问module_raw。对于Linux(evdev),这称为input。还有一个需要安装libevdev的实验性模块:module_raw input_evdev。


来源:
https://github.com/libts/tslib