从tslib源码看触摸屏滤波:手把手实现一个自定义的‘filter’插件
从tslib源码看触摸屏滤波手把手实现一个自定义的‘filter’插件触摸屏设备在现代交互系统中扮演着关键角色但原始触摸数据往往包含噪声、抖动和不准确性。tslib作为Linux生态中广泛使用的触摸屏支持库其核心价值在于提供了一套可扩展的滤波处理框架。本文将带您深入tslib的模块化架构理解其数据处理流程并最终实现一个能够记录原始坐标的自定义filter插件。1. tslib架构深度解析tslib采用插件化设计所有滤波模块都以动态库形式存在plugins目录下。这种设计使得开发者可以灵活组合不同的处理模块构建适合特定硬件的处理流水线。1.1 模块链工作原理在ts.conf配置文件中模块按照处理顺序排列形成处理链。例如一个典型配置可能如下module_raw input module median module dejitter module linear数据流从底层驱动读取后依次经过每个模块的read/read_mt函数处理。每个模块都接收前一个模块的输出作为输入并将处理结果传递给下一个模块。1.2 核心数据结构tslib使用以下关键结构体传递触摸数据struct ts_sample { int x; // 归一化后的X坐标 int y; // 归一化后的Y坐标 unsigned int pressure; // 压力值 struct timeval tv; // 时间戳 }; struct ts_sample_mt { int x; int y; unsigned int pressure; int slot; // 触点ID int tracking_id; // 触点跟踪ID int tool_type; // 工具类型(手指/笔等) int tool_x; // 工具原始X坐标 int tool_y; // 工具原始Y坐标 struct timeval tv; };2. 开发自定义filter插件2.1 创建插件基础结构每个tslib插件需要实现以下基本结构#include tslib.h static int myfilter_read(struct tsdev *ts, struct ts_sample *samp, int nr) { // 处理逻辑 return nr; } static int myfilter_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr) { // 多点触摸处理逻辑 return nr; } static const struct tslib_ops myfilter_ops { .read myfilter_read, .read_mt myfilter_read_mt, }; TSAPI struct tslib_module_info *myfilter_mod_init(struct tsdev *dev, const char *params) { struct tslib_module_info *m; m malloc(sizeof(struct tslib_module_info)); if (m NULL) return NULL; m-ops myfilter_ops; return m; } #ifndef TSLIB_STATIC_MYFILTER_MODULE TSLIB_MODULE_INIT(myfilter_mod_init); #endif2.2 实现坐标记录插件下面是一个完整的坐标记录插件实现它会将原始触摸数据写入系统日志#include tslib.h #include syslog.h #include stdio.h static int coordlog_read(struct tsdev *ts, struct ts_sample *samp, int nr) { int i; for (i 0; i nr; i) { syslog(LOG_INFO, RAW COORD: x%d y%d pressure%d, samp[i].x, samp[i].y, samp[i].pressure); } return nr; // 继续传递原始数据 } static int coordlog_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr) { int i, j; for (i 0; i nr; i) { for (j 0; j max_slots; j) { if (samp[i][j].valid) { syslog(LOG_INFO, MT[%d]: x%d y%d slot%d id%d, j, samp[i][j].x, samp[i][j].y, samp[i][j].slot, samp[i][j].tracking_id); } } } return nr; } static const struct tslib_ops coordlog_ops { .read coordlog_read, .read_mt coordlog_read_mt, }; TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { struct tslib_module_info *m; openlog(tslib_coordlog, LOG_PID|LOG_CONS, LOG_USER); m malloc(sizeof(struct tslib_module_info)); if (m NULL) return NULL; m-ops coordlog_ops; return m; } #ifndef TSLIB_STATIC_COORDLOG_MODULE TSLIB_MODULE_INIT(coordlog_mod_init); #endif3. 编译与集成插件3.1 编译动态库创建Makefile文件CC gcc CFLAGS -fPIC -Wall -Wextra LDFLAGS -shared TARGET coordlog.so all: $(TARGET) $(TARGET): coordlog.c $(CC) $(CFLAGS) $(LDFLAGS) -o $ $^ clean: rm -f $(TARGET)执行编译make3.2 配置tslib使用新插件将生成的coordlog.so复制到tslib插件目录然后在ts.conf中添加配置module_raw input module coordlog module median module dejitter module linear4. 高级插件开发技巧4.1 处理模块参数tslib支持向模块传递参数格式为key1value1:key2value2。在mod_init函数中可以解析这些参数TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { // 参数解析示例 if (params) { char *p strdup(params); char *tok strtok(p, :); while (tok) { char *eq strchr(tok, ); if (eq) { *eq \0; if (strcmp(tok, log_level) 0) { // 处理log_level参数 } } tok strtok(NULL, :); } free(p); } // ... 其余初始化代码 }4.2 维护模块状态复杂的滤波算法可能需要维护状态信息。可以通过扩展tslib_module_info结构来实现struct coordlog_data { struct tslib_module_info module; int log_level; FILE *logfile; }; TSAPI struct tslib_module_info *coordlog_mod_init(struct tsdev *dev, const char *params) { struct coordlog_data *d; d calloc(1, sizeof(struct coordlog_data)); if (d NULL) return NULL; d-module.ops coordlog_ops; d-log_level LOG_INFO; // 初始化自定义字段 d-logfile fopen(/var/log/ts_coord.log, a); return d-module; }5. 调试与性能优化5.1 调试技巧使用ts_print和ts_print_mt工具验证数据流通过strace跟踪系统调用在模块中添加调试日志#define DEBUG(fmt, ...) fprintf(stderr, DEBUG: fmt \n, ##__VA_ARGS__) static int myfilter_read(struct tsdev *ts, struct ts_sample *samp, int nr) { DEBUG(Processing %d samples, nr); // ... }5.2 性能考量触摸屏数据处理对实时性要求较高应注意避免在滤波模块中进行内存分配限制日志输出频率使用查表法替代复杂计算针对ARM平台进行编译优化CFLAGS -O2 -mcpucortex-a7 -mfpuneon-vfpv4 -mfloat-abihard