两周前的一个地图,需要做手势缩放的功能,比较忙就没有发出来,这次抽时间记录一下实现的过程。
AWTK官方有对实现多点触摸做过描述,可惜只有STM32的实现例子,跟Linux的差别还是比较大的,好在tslib有多点触摸的测试程序ts_print_mt以及ts_test_mt,根据这些程序,加上awtk-linux-fb本身的tslib_thread的源码,
很快就能弄清怎么实现了。
大致的步骤:
1.确保awtk-linux-fb已经配置TSLIB_INC和TSLIB_LIB,我的例子如下:
awtk_config.py
TSLIB_LIB_DIR='/opt/tslib/lib'
TSLIB_INC_DIR='/opt/tslib/include'
2.参考ts_test_mt写适配层代码,这里需要注意一点不同板子的驱动配置可能不一样,比如有些配置能对压力做0-255的级别识别,有的只能检测按下或者松开(255或者0),有些配置能在检测到弹起事件时清空对应手指slot的坐标和压力值,有的则不会,需要手动检测清空。估计这也是为什么awtk官方的linux触摸没有写死的原因。
3.将awtk的multi_gesture.inc文件移植到和tslib_thread.c同一个目录下:
记录解决思路和遇到的问题:
1.通过中间变量s_point传递tslib的手势参数(slots数组)的数据,能够灵活的检测手指的变化,便于做一些单点到多点再到单点的操作。之前想参考腾讯地图做多点变单点时仍旧不触发单点的逻辑,比较费时没有做成。
2.tslib中对于按下(down)事件,采用水平触发的方式处理,也就是手指压在触摸屏时,只检测一次down事件,后面手指的事件就都是(move)事件,直到手指离开(up),这样就造成一个问题,只有两个手指都移动的时候触摸屏才能检测到多点触摸,因为对于手指压下去没有移动的情况,tslib这端是无法检测的,判定为invalid,想搞一个手指不动另一个手指移动来“缩放”的操作,就得另寻办法。
比如专门用一个变量记录按下的手指数量,和tslib检测到的event数量分开,并且通过中间变量s_point来检测一个手指的上个状态是否为[按下]来决定是否在tslib检测到invaild时清空其压力,避免误算按下的手指个数。
// note: down, up only trigger once(ET), move will be keep trigger(LT)for (int i = 0; i < max_slots; i++) {if (!(info->samp_mt[0][i].valid & TSLIB_MT_VALID)){//for boards that no clear pressure when slot is invaild, need to do it manuallyif(!s_points[i].type == EVT_MULTI_TOUCH_DOWN){info->samp_mt[0][i].pressure = 0;}continue;}event_number++;}for (int i = 0; i < CT_MAX_TOUCH; i++){s_points[i].touch_id = 0;s_points[i].finger_id = i;if(info->samp_mt[0][i].pressure > 0) {down_number++;s_points[i].type = (s_points[i].x == info->samp_mt[0][i].x && s_points[i].y == info->samp_mt[0][i].y) ? EVT_MULTI_TOUCH_DOWN : EVT_MULTI_TOUCH_MOVE;s_points[i].x = info->samp_mt[0][i].x;s_points[i].y = info->samp_mt[0][i].y;// printf("sample %d finger %d %s at (%d %d)\r\n", 0, s_points[i].finger_id, (s_points[i].type == EVT_MULTI_TOUCH_DOWN) ? "down" : "move" , s_points[i].x, s_points[i].y);}else{s_points[i].type = EVT_MULTI_TOUCH_UP;if(0 != s_points[i].x && 0 != s_points[i].y){// printf("sample %d finger %d up at (%d %d)\r\n", 0, s_points[i].finger_id, s_points[i].x, s_points[i].y);}s_points[i].x = 0;s_points[i].y = 0;} }
4.适配层代码完成,接下来就该应用层了,这里以官方的awtk-hello程序为例子,写多点触摸的应用代码:
windows2.c
/*** File: window2.c* Author: AWTK Develop Team* Brief: window2 source** Copyright (c) 2018 - 2018 Guangzhou ZHIYUAN Electronics Co.,Ltd.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* License file for more details.**//*** History:* ================================================================* 2018-08-19 Li XianJing <xianjimli@hotmail.com> created**/#include "window2.h"typedef struct _multi_gesture_info_t {bool_t is_first;float scale;float rotation;float distance;int32_t x;int32_t y;int32_t cnt;
}multi_gesture_info_t;static multi_gesture_info_t* multi_gesture_info_create(void) {return TKMEM_ZALLOC(multi_gesture_info_t);
}static ret_t multi_gesture_info_destroy(multi_gesture_info_t* info) {TKMEM_FREE(info);return RET_OK;
}static ret_t on_window_destroy(void* ctx, event_t* e) {multi_gesture_info_destroy((multi_gesture_info_t*)ctx);return RET_OK;
}static ret_t on_multi_gesture(void* ctx, event_t* e) {multi_gesture_info_t* info = (multi_gesture_info_t*)ctx;multi_gesture_event_t* evt = multi_gesture_event_cast(e);widget_t* win = widget_get_window(WIDGET(e->target));info->x = evt->x;info->y = evt->y;info->rotation += evt->rotation;info->distance += evt->distance;info->cnt++;// log_debug("on multi gesture: x %d y %d rot %f dist %f\r\n", evt->x, evt->y, evt->rotation, evt->distance);if(evt->distance > 0 && info->cnt % 15 == 0){info->scale++;}else if(evt->distance < 0 && info->cnt % 15 == 0){info->scale--;}widget_set_child_text_with_int(win, "x", "x:%d", info->x);widget_set_child_text_with_int(win, "y", "y:%d", info->y);widget_set_child_text_with_double(win, "scale", "%d", (int)info->scale);widget_set_child_text_with_double(win, "rotation", "rotation:%lf", info->rotation);widget_set_child_text_with_double(win, "distance", "distance:%lf", info->distance);// log_debug("on_multi_gesture info dist %.2lf\r\n", info->distance);return RET_OK;
}static ret_t on_pointer_down(void* ctx, event_t* e) {multi_gesture_info_t* info = (multi_gesture_info_t*)ctx;pointer_event_t* evt = pointer_event_cast(e);log_debug("on_pointer_down: %d %d\n", evt->x, evt->y);info->is_first = TRUE;info->rotation = 0;info->distance = 0;info->cnt = 0;return RET_OK;
}static ret_t on_pointer_up(void* ctx, event_t* e) {multi_gesture_info_t* info = (multi_gesture_info_t*)ctx;pointer_event_t* evt = pointer_event_cast(e);log_debug("on_pointer_up: %d %d\n", evt->x, evt->y);info->is_first = TRUE;info->rotation = 0;info->distance = 0;info->cnt = 0;return RET_OK;
}static ret_t on_close_window(void* ctx, event_t* e) {window_close(WIDGET(ctx));return RET_OK;
}ret_t window2_open(void) {widget_t* win = window_open("window2");multi_gesture_info_t* info = multi_gesture_info_create();widget_on(win, EVT_DESTROY, on_window_destroy, info);widget_on(win, EVT_MULTI_GESTURE, on_multi_gesture, info);widget_on(win, EVT_POINTER_DOWN, on_pointer_down, info);widget_on(win, EVT_POINTER_UP, on_pointer_up, info);widget_child_on(win, "close", EVT_CLICK, on_close_window, win);return RET_OK;
}
稍微修改下界面:
<window anim_hint="htranslate" name="window2"><label name="scale" x="224" y="80" w="318" h="209" style:normal:border_color="#000000" style:normal:font_size="50" text="0"/><label name="x" x="23" y="329" w="142" h="40" text="0"/><label name="y" x="170" y="329" w="142" h="40" text="0"/><label name="rotation" x="312" y="329" w="142" h="40" text="0"/><label name="distance" x="454" y="329" w="142" h="40" text="0"/><label name="fingers" x="596" y="329" w="142" h="40" text="0"/><button name="close" x="center" y="bottom:10" w="80%" h="30" text="Close"/>
</window>
最终效果:
tslib_thread.c和multi_gesture.inc的代码附上:
tslib_thread.c
/*** File: tslib_thread.c* Author: AWTK Develop Team* Brief: thread to handle touch screen events** Copyright (c) 2018 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* License file for more details.**//*** History:* ================================================================* 2018-09-07 Li XianJing <xianjimli@hotmail.com> created**/#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include "tslib.h"
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "tkc/thread.h"
#include "base/keys.h"#include "tslib_thread.h"
#include "multi_gesture.inc"#define MT_TOUCH_ENABLE#define RESET "\033[0m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define BLUE "\033[34m"
#define YELLOW "\033[33m"#define CT_MAX_TOUCH 5
static multi_gesture_touch_points_t* touch_points = NULL;
static multi_gesture_touch_fingers_t* touch_fingers = NULL;
static multi_touch_point_event_t s_points[CT_MAX_TOUCH];#ifndef ABS_MT_SLOT
#define ABS_MT_SLOT 0x2f
#endif /*ABS_MT_SLOT*/typedef struct _run_info_t {int32_t max_x;int32_t max_y;struct tsdev* ts;void* dispatch_ctx;char* filename;input_dispatch_t dispatch;event_queue_req_t req;#ifdef MT_TOUCH_ENABLEstruct ts_sample_mt **samp_mt;int read_samples;int max_slots;int user_slots;bool_t in_multi_gesture;int last_event_number;int last_down_number;#endif
} run_info_t;static ret_t tslib_dispatch(run_info_t* info) {ret_t ret = RET_FAIL;char message[MAX_PATH + 1] = {0};// tk_snprintf(message, sizeof(message) - 1, "ts[%s]", info->filename);ret = info->dispatch(info->dispatch_ctx, &(info->req), message);info->req.event.type = EVT_NONE;return ret;
}static ret_t tslib_dispatch_one_event(run_info_t* info) {#ifdef MT_TOUCH_ENABLEint ret = -1;if (info->ts != NULL) {ret = ts_read_mt(info->ts, info->samp_mt, info->max_slots, info->read_samples);}uint8_t event_number = 0, down_number = 0;event_queue_req_t* req = &(info->req);if (ret == 0) {log_warn("%s:%d: get tslib data failed, filename=%s\n", __func__, __LINE__, info->filename);sleep(1);return RET_OK;} else if (ret < 0) {sleep(2);if (access(info->filename, R_OK) == 0) {if (info->ts != NULL) {ts_close(info->ts);}info->ts = ts_open(info->filename, 0);return_value_if_fail(info->ts != NULL, RET_OK);ts_config(info->ts);if (info->ts == NULL) {log_debug("%s:%d: open tslib failed, filename=%s\n", __func__, __LINE__, info->filename);perror("print tslib: ");} else {log_debug("%s:%d: open tslib successful, filename=%s\n", __func__, __LINE__,info->filename);}}return RET_OK;}int max_slots = info->max_slots;// note: down, up only trigger once(ET), move will be keep trigger(LT)for (int i = 0; i < max_slots; i++) {// printf(YELLOW "sample %d - %ld.%06ld -" RESET " (slot %d) %6d %6d %6d\n",// 0,// info->samp_mt[0][i].tv.tv_sec,// info->samp_mt[0][i].tv.tv_usec,// info->samp_mt[0][i].slot,// info->samp_mt[0][i].x,// info->samp_mt[0][i].y,// info->samp_mt[0][i].pressure);if (!(info->samp_mt[0][i].valid & TSLIB_MT_VALID)){//for boards that no clear pressure when slot is invaild, need to do it manuallyif(!s_points[i].type == EVT_MULTI_TOUCH_DOWN){info->samp_mt[0][i].pressure = 0;}continue;}event_number++;}for (int i = 0; i < CT_MAX_TOUCH; i++){s_points[i].touch_id = 0;s_points[i].finger_id = i;if(info->samp_mt[0][i].pressure > 0) {down_number++;s_points[i].type = (s_points[i].x == info->samp_mt[0][i].x && s_points[i].y == info->samp_mt[0][i].y) ? EVT_MULTI_TOUCH_DOWN : EVT_MULTI_TOUCH_MOVE;s_points[i].x = info->samp_mt[0][i].x;s_points[i].y = info->samp_mt[0][i].y;// printf("sample %d finger %d %s at (%d %d)\r\n", 0, s_points[i].finger_id, (s_points[i].type == EVT_MULTI_TOUCH_DOWN) ? "down" : "move" , s_points[i].x, s_points[i].y);}else{s_points[i].type = EVT_MULTI_TOUCH_UP;if(0 != s_points[i].x && 0 != s_points[i].y){// printf("sample %d finger %d up at (%d %d)\r\n", 0, s_points[i].finger_id, s_points[i].x, s_points[i].y);}s_points[i].x = 0;s_points[i].y = 0;} }// printf("evented finger: %d down finger: %d\r\n", event_number, down_number);// printf("-----------------------------------------------------------------\r\n");if(event_number == 1 && (info->last_down_number == 0 || info->last_down_number == 1)){//单点触摸struct ts_sample_mt e = info->samp_mt[0][0];req->event.type = EVT_NONE;req->pointer_event.x = e.x;req->pointer_event.y = e.y;// log_debug("%s%d: e.pressure=%d x=%d y=%d ret=%d\n", __func__, __LINE__, e.pressure, e.x, e.y,// ret);if (e.pressure > 0) {if (req->pointer_event.pressed) {req->event.type = EVT_POINTER_MOVE;} else {req->event.type = EVT_POINTER_DOWN;req->pointer_event.pressed = TRUE;}} else {if (req->pointer_event.pressed) {req->event.type = EVT_POINTER_UP;}req->pointer_event.pressed = FALSE;}tslib_dispatch(info);}else{//多点触摸main_loop_t *loop = (main_loop_t*)info->dispatch_ctx;multi_gesture_post_event_from_points(loop, touch_points, down_number, s_points);}info->last_event_number = event_number;info->last_down_number = down_number;return 0;#else struct ts_sample e = {0};int ret = -1;if (info->ts != NULL) {ret = ts_read(info->ts, &e, 1);}event_queue_req_t* req = &(info->req);if (ret == 0) {log_warn("%s:%d: get tslib data failed, filename=%s\n", __func__, __LINE__, info->filename);sleep(1);return RET_OK;} else if (ret < 0) {sleep(2);if (access(info->filename, R_OK) == 0) {if (info->ts != NULL) {ts_close(info->ts);}info->ts = ts_open(info->filename, 0);return_value_if_fail(info->ts != NULL, RET_OK);ts_config(info->ts);if (info->ts == NULL) {log_debug("%s:%d: open tslib failed, filename=%s\n", __func__, __LINE__, info->filename);perror("print tslib: ");} else {log_debug("%s:%d: open tslib successful, filename=%s\n", __func__, __LINE__,info->filename);}}return RET_OK;}req->event.type = EVT_NONE;req->pointer_event.x = e.x;req->pointer_event.y = e.y;log_debug("%s%d: e.pressure=%d x=%d y=%d ret=%d\n", __func__, __LINE__, e.pressure, e.x, e.y,ret);if (e.pressure > 0) {if (req->pointer_event.pressed) {req->event.type = EVT_POINTER_MOVE;} else {req->event.type = EVT_POINTER_DOWN;req->pointer_event.pressed = TRUE;}} else {if (req->pointer_event.pressed) {req->event.type = EVT_POINTER_UP;}req->pointer_event.pressed = FALSE;}return tslib_dispatch(info);#endif
}void* tslib_run(void* ctx) {run_info_t info = *(run_info_t*)ctx;if (info.ts == NULL) {log_debug("%s:%d: open tslib failed, filename=%s\n", __func__, __LINE__, info.filename);} else {log_debug("%s:%d: open tslib successful, filename=%s\n", __func__, __LINE__, info.filename);}TKMEM_FREE(ctx);while (tslib_dispatch_one_event(&info) == RET_OK) {};#ifdef MT_TOUCH_ENABLEmulti_gesture_gesture_touch_fingers_destroy(touch_fingers);#endif ts_close(info.ts);TKMEM_FREE(info.filename);return NULL;
}static run_info_t* info_dup(run_info_t* info) {run_info_t* new_info = TKMEM_ZALLOC(run_info_t);*new_info = *info;return new_info;
}tk_thread_t* tslib_thread_run(const char* filename, input_dispatch_t dispatch, void* ctx,int32_t max_x, int32_t max_y) {run_info_t info;tk_thread_t* thread = NULL;return_value_if_fail(filename != NULL && dispatch != NULL, NULL);memset(&info, 0x00, sizeof(info));info.max_x = max_x;info.max_y = max_y;info.dispatch_ctx = ctx;info.dispatch = dispatch;info.ts = ts_open(filename, 0);info.filename = tk_strdup(filename);if (info.ts != NULL) {ts_config(info.ts);}#ifdef MT_TOUCH_ENABLEstruct tsdev *ts = info.ts;info.read_samples = 1;info.max_slots = CT_MAX_TOUCH;int read_samples = info.read_samples;int max_slots = info.max_slots;int i;info.samp_mt = malloc(read_samples * sizeof(struct ts_sample_mt *));if (!info.samp_mt) {printf("create samp_mt failed\r\n");ts_close(ts);return NULL;}for (i = 0; i < read_samples; i++) {info.samp_mt[i] = calloc(max_slots, sizeof(struct ts_sample_mt));if (!info.samp_mt[i]) {printf("create samp_mt[%d] failed\r\n", i);for (i--; i >= 0; i--)free(info.samp_mt[i]);free(info.samp_mt);ts_close(ts);return NULL;}}/* 创建不可识别手指类型的多点触控句柄 */touch_points = multi_gesture_touch_points_create(15);/* 创建可识别手指类型的多点触控句柄 */touch_fingers = multi_gesture_touch_fingers_create(CT_MAX_TOUCH);memset(s_points, 0x0, sizeof(multi_touch_point_event_t) * CT_MAX_TOUCH);#endif thread = tk_thread_create(tslib_run, info_dup(&info));if (thread != NULL) {tk_thread_start(thread);} else {ts_close(info.ts);TKMEM_FREE(info.filename);}return thread;
}
multi_gesture.inc
/*** File: multi_gesture.inc* Author: AWTK Develop Team* Brief: multi_gesture** Copyright (c) 2018 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* License file for more details.**//*** History:* ================================================================* 2020-11-03 Lou ZhiMing <louzhiming@zlg.cn> created**/#include <math.h>
#include "tkc/mem.h"
#include "tkc/rect.h"
#include "tkc/utils.h"
#include "tkc/darray.h"
#include "base/events.h"
#include "base/types_def.h"
#include "main_loop/main_loop_simple.h"
/*** @enum multi_event_type_t* @annotation ["scriptable"]* @prefix EVT_* 类型常量定义。*/
typedef enum _multi_event_type_t {/*** @const EVT_MULTI_TOUCH_UP* 多点触摸抬起事件名(multi_touch_event_t)。*/EVT_MULTI_TOUCH_UP = 0x0,/*** @const EVT_MULTI_TOUCH_DOWN* 多点触摸按下事件名(multi_touch_event_t)。*/ EVT_MULTI_TOUCH_DOWN,/*** @const EVT_MULTI_TOUCH_MOVE* 多点触摸点移动事件名(multi_touch_event_t)。*/EVT_MULTI_TOUCH_MOVE,
} multi_event_type_t;/*** @class multi_touch_point_t* @annotation ["scriptable"]* @parent event_t* 多点触控点信息。*/
typedef struct _multi_touch_point_event_t {/*** @property {multi_event_type_t} type* @annotation ["readable", "scriptable"]* 触摸状态。*/ multi_event_type_t type;/*** @property {int64_t} touch_id* @annotation ["readable", "scriptable"]* touch device id。*/int64_t touch_id;/*** @property {xy_t} x* @annotation ["readable", "scriptable"]* x坐标。*/xy_t x;/*** @property {xy_t} y* @annotation ["readable", "scriptable"]* y坐标。*/xy_t y;/*** @property {uint32_t} finger_id* @annotation ["readable", "scriptable"]* 点击手指 ID*/uint32_t finger_id;
} multi_touch_point_event_t;/*** @class multi_gesture_finger_t* @annotation ["scriptable"]* @parent event_t* 点击手指信息*/
typedef struct _multi_gesture_finger_t {/*** @property {int64_t} finger_id* @annotation ["readable", "scriptable"]* 点击手指 ID*/int64_t finger_id;/*** @property {xy_t} x* @annotation ["readable", "scriptable"]* 点击手指 X*/xy_t x;/*** @property {xy_t} y* @annotation ["readable", "scriptable"]* 点击手指 Y*/xy_t y;
} multi_gesture_finger_t;/*** @class multi_gesture_touch_fingers_t* @annotation ["scriptable"]* @parent event_t* 可识别手指类型多点触控对象*/
typedef struct _multi_gesture_touch_fingers_t {/*** @property {int64_t} touch_id* @annotation ["readable", "scriptable"]* touch device id。*/int64_t touch_id;/*** @property {float_t} dtheta* @annotation ["readable", "scriptable"]* 旋转角度(幅度)增量。(单位弧度)*/float_t dtheta;/*** @property {float_t} d_dist* @annotation ["readable", "scriptable"]* 两点间的距离增量。(-1,0)表示缩小,(0-1)表示增加。*/float_t d_dist;/* private */pointf_t centroid;darray_t last_point;
} multi_gesture_touch_fingers_t;/*** @method multi_gesture_gesture_touch_fingers_destroy* 释放可识别手指类型的对象* @param {multi_gesture_touch_fingers_t*} touch 可识别手指类型的对象。** @return {ret_t} 返回RET_OK表示成功,否则表示失败。*/
ret_t multi_gesture_gesture_touch_fingers_destroy(multi_gesture_touch_fingers_t* touch) {return_value_if_fail(touch != NULL, RET_BAD_PARAMS);darray_deinit(&(touch->last_point));TKMEM_FREE(touch);return RET_OK;
}static int multi_gesture_finger_compare(const void* a, const void* b) {int64_t finger_id = *(int64_t*)b;multi_gesture_finger_t* touch = (multi_gesture_finger_t*)a;return finger_id - touch->finger_id;
}/*** @method multi_gesture_touch_fingers_create* 创建可识别手指类型的对象* @param {int32_t} finger_size 可识别手指个数。** @return {multi_gesture_touch_fingers_t*} 成功返回可识别手指类型的对象,失败返回NULL。*/
multi_gesture_touch_fingers_t* multi_gesture_touch_fingers_create(int32_t finger_size) {multi_gesture_touch_fingers_t* touch = TKMEM_ZALLOC(multi_gesture_touch_fingers_t);return_value_if_fail(touch != NULL, NULL);touch->centroid.x = 0;touch->centroid.y = 0;darray_init(&(touch->last_point), finger_size, default_destroy, multi_gesture_finger_compare);return touch;
}static multi_gesture_finger_t* multi_gesture_get_finger_last_point(multi_gesture_touch_fingers_t* touch, int64_t finger_id) {multi_gesture_finger_t* last_point = NULL;return_value_if_fail(touch != NULL, NULL);last_point = (multi_gesture_finger_t*)darray_find(&(touch->last_point), &finger_id);return last_point;
}static ret_t multi_gesture_remove_finger_last_point(multi_gesture_touch_fingers_t* touch, int64_t finger_id) {multi_gesture_finger_t* last_point = NULL;return_value_if_fail(touch != NULL, RET_BAD_PARAMS);last_point = (multi_gesture_finger_t*)darray_find(&(touch->last_point), &finger_id);if (last_point != NULL) {return darray_remove(&(touch->last_point), last_point);}return RET_OK;
}static ret_t multi_gesture_set_finger_last_point(multi_gesture_touch_fingers_t* touch, int64_t finger_id, xy_t x, xy_t y) {multi_gesture_finger_t* last_point = NULL;return_value_if_fail(touch != NULL, RET_BAD_PARAMS);last_point = (multi_gesture_finger_t*)darray_find(&(touch->last_point), &finger_id);if (last_point != NULL) {last_point->x = x;last_point->y = y;} else {last_point = TKMEM_ZALLOC(multi_gesture_finger_t);last_point->x = x;last_point->y = y;last_point->finger_id = finger_id;darray_push(&(touch->last_point), last_point);}return RET_OK;
}static bool_t multi_gesture_get_event_form_fingers(multi_gesture_touch_fingers_t* touch, multi_touch_point_event_t* point,multi_gesture_event_t* out_event) {pointf_t last_p;pointf_t last_centroid;float_t dist;float_t l_dist;float_t dtheta;float_t d_dist;bool_t is_out_event = FALSE;uint32_t type = point->type;if (type == EVT_MULTI_TOUCH_MOVE || type == EVT_MULTI_TOUCH_DOWN || type == EVT_MULTI_TOUCH_UP) {float_t x = point->x;float_t y = point->y;uint32_t finger_id = point->finger_id;uint32_t num_down_fingers = touch->last_point.size;/* Finger Up */if (type == EVT_MULTI_TOUCH_UP && num_down_fingers > 0) {multi_gesture_remove_finger_last_point(touch, finger_id);num_down_fingers = touch->last_point.size;/* touch->gestureLast[j] = touch->gestureLast[touch->num_down_fingers]; */if (num_down_fingers > 0) {touch->centroid.x = (touch->centroid.x * (num_down_fingers + 1) - x) / num_down_fingers;touch->centroid.y = (touch->centroid.y * (num_down_fingers + 1) - y) / num_down_fingers;} else {touch->centroid.x = 0;touch->centroid.y = 0;}} else if (type == EVT_MULTI_TOUCH_MOVE && num_down_fingers > 1) {multi_gesture_finger_t* finger = multi_gesture_get_finger_last_point(touch, finger_id);return_value_if_fail(finger != NULL, FALSE);float_t delta_theta = 0.0f;float_t dx = x - finger->x;float_t dy = y - finger->y;last_p.x = x - dx;last_p.y = y - dy;last_centroid = touch->centroid;touch->centroid.x += dx / num_down_fingers;touch->centroid.y += dy / num_down_fingers;// printf("Centrid : (%f,%f), dx:%f, dy:%f \r\n",touch->centroid.x,touch->centroid.y,dx,dy);if (num_down_fingers > 1) {pointf_t lv; /* Vector from centroid to last x,y position */pointf_t v; /* Vector from centroid to current x,y position *//* lv = touch->gestureLast[j].cv; */lv.x = last_p.x - last_centroid.x;lv.y = last_p.y - last_centroid.y;l_dist = sqrtf(lv.x * lv.x + lv.y * lv.y);/* printf("l_dist = %f\n",l_dist); */v.x = x - touch->centroid.x;v.y = y - touch->centroid.y;/* touch->gestureLast[j].cv = v; */dist = sqrtf(v.x * v.x + v.y * v.y);/* SDL_cos(dTheta) = (v . lv)/(|v| * |lv|) *//* Normalize Vectors to simplify angle calculation */lv.x /= l_dist;lv.y /= l_dist;v.x /= dist;v.y /= dist;dtheta = atan2f(lv.x * v.y - lv.y * v.x, lv.x * v.x + lv.y * v.y);d_dist = (dist - l_dist);if (l_dist == 0) {d_dist = 0;dtheta = 0;} /* To avoid impossible values */delta_theta = tk_abs(touch->dtheta - dtheta);if (delta_theta < 0.01f || delta_theta > M_PI / 2) {dtheta = 0;}/* touch->gestureLast[j].d_dist = d_dist;touch->gestureLast[j].dtheta = dtheta;printf("d_dist = %f, dTheta = %f\n",d_dist,dtheta);gdtheta = gdtheta*.9 + dtheta*.1;gd_dist = gd_dist*.9 + d_dist*.1knob.r += d_dist/num_down_fingers;knob.ang += dtheta;printf("thetaSum = %f, distSum = %f\n",gdtheta,gd_dist);printf("id: %i dTheta = %f, d_dist = %f\n",j,dtheta,d_dist); */// SDL_SendGestureMulti(touch,dtheta,d_dist);--------------------------------------------------------------触发消息touch->dtheta = dtheta;touch->d_dist = d_dist;is_out_event = TRUE;}} else if (type == EVT_MULTI_TOUCH_DOWN) {multi_gesture_finger_t* last_point = (multi_gesture_finger_t*)darray_find(&(touch->last_point), &finger_id);if (last_point == NULL) {num_down_fingers++;touch->centroid.x = (touch->centroid.x * (num_down_fingers - 1) + x) / num_down_fingers;touch->centroid.y = (touch->centroid.y * (num_down_fingers - 1) + y) / num_down_fingers;}}if (type != EVT_MULTI_TOUCH_UP) {multi_gesture_set_finger_last_point(touch, finger_id, x, y);}}return is_out_event;
}/*** @method multi_gesture_post_event_from_fingers* 发送多点触控消息* @param {main_loop_t*} loop 主循环对象* @param {multi_gesture_touch_fingers_t*} touch 可识别手指类型的对象。* @param {uint32_t} point_size 手指个数* @param {multi_touch_point_event_t*} points 手指数据数组** @return {ret_t} 返回RET_OK表示成功,否则表示失败。*/
ret_t multi_gesture_post_event_from_fingers(main_loop_t* loop, multi_gesture_touch_fingers_t* touch, uint32_t point_size, multi_touch_point_event_t* points) {uint32_t i = 0;multi_gesture_event_t multi_gesture_event;return_value_if_fail(loop != NULL && touch != NULL && points != NULL, RET_BAD_PARAMS);// printf("-------------------------------------------- \r\n");for (; i < point_size; i++) {multi_touch_point_event_t* point = &(points[i]);// printf("point:(%d, %d), type:%d \r\n", point->x, point->y, point->type);if (multi_gesture_get_event_form_fingers(touch, point, &multi_gesture_event)) {// printf("centroid:(%f, %f), dtheta:%f, d_dist:%f, num_down_fingers:%d \r\n", touch->centroid.x, touch->centroid.y, touch->dtheta, touch->d_dist, touch->last_point.size);multi_gesture_event_init(&multi_gesture_event, window_manager(), touch->centroid.x, touch->centroid.y,touch->dtheta, touch->d_dist);main_loop_post_multi_gesture_event(loop, &multi_gesture_event);}}if (point_size == 0 || (point_size > 0 && touch->last_point.size == 0)) {multi_gesture_event_init(&multi_gesture_event, window_manager(), 0, 0, 0, 0);main_loop_post_multi_gesture_event(loop, &multi_gesture_event);if (point_size == 0) {touch->centroid.x = 0;touch->centroid.y = 0;darray_clear(&(touch->last_point));}}return RET_OK;
}#ifndef MULTI_GESTURE_DIST_MIN_RANGE
#define MULTI_GESTURE_DIST_MIN_RANGE 1.0f
#endif/*** @class multi_gesture_touch_fingers_t* @annotation ["scriptable"]* @parent event_t* 不可识别手指类型多点触控对象*/
typedef struct _multi_gesture_touch_points_t {/*** @property {int64_t} touch_id* @annotation ["readable", "scriptable"]* touch device id。*/int64_t touch_id;/* private */float_t last_dist;uint32_t last_dist_length;slist_t last_dist_list;uint32_t num_down_fingers;pointf_t last_vector;
} multi_gesture_touch_points_t;/*** @method multi_gesture_gesture_touch_points_destroy* 释放不可识别手指类型的对象* @param {multi_gesture_touch_points_t*} touch 不可识别手指类型的对象。** @return {ret_t} 返回RET_OK表示成功,否则表示失败。*/
ret_t multi_gesture_gesture_touch_points_destroy(multi_gesture_touch_points_t* touch) {return_value_if_fail(touch != NULL, RET_BAD_PARAMS);slist_deinit(&(touch->last_dist_list));TKMEM_FREE(touch);return RET_OK;
}/*** @method multi_gesture_touch_points_create* 创建不可识别手指类型的对象* @param {uint32_t} last_dist_length 缩放比例均值滤波的缓冲长度。** @return {multi_gesture_touch_points_t*} 成功返回不可识别手指类型的对象,失败返回NULL。*/
multi_gesture_touch_points_t* multi_gesture_touch_points_create(uint32_t last_dist_length) {multi_gesture_touch_points_t* touch = TKMEM_ZALLOC(multi_gesture_touch_points_t);return_value_if_fail(touch != NULL, NULL);touch->last_dist_length = last_dist_length;slist_init(&(touch->last_dist_list), default_destroy, NULL);return touch;
}static float_t multi_gesture_touch_get_last_d_dists(multi_gesture_touch_points_t* touch, float_t dists) {uint32_t size = 0;slist_t* slist = NULL;float_t d_dists = 0.0f;slist_node_t* iter = NULL;float_t* tmp = TKMEM_ZALLOC(float_t);return_value_if_fail(tmp != NULL, RET_OOM);return_value_if_fail(touch != NULL, RET_BAD_PARAMS);*tmp = dists;slist = &(touch->last_dist_list);if (touch->last_dist_length <= slist_size(slist)) {void* data = slist_head_pop(slist);TKMEM_FREE(data);}slist_append(slist, tmp);iter = slist->first;while (iter != NULL) {d_dists += *((float_t*)(iter->data));iter = iter->next;size++;}// printf("size:%d \r\n", size);return d_dists / size;
}static float_t multi_gesture_touch_get_dtheta(multi_gesture_touch_points_t* touch, point_t point1, point_t point2, float_t dist) {float_t dtheta = 0.0f;pointf_t v1 = {(point1.x - point2.x) / dist, (point1.y - point2.y) / dist};pointf_t v2 = {(point2.x - point1.x) / dist, (point2.y - point1.y) / dist};return_value_if_fail(touch != NULL, RET_BAD_PARAMS);if (touch->last_vector.x == 0 && touch->last_vector.y == 0 || dist == 0) {touch->last_vector.x = v1.x;touch->last_vector.y = v1.y;} else {float_t dtheta1 = atan2f(touch->last_vector.x * v1.y - touch->last_vector.y * v1.x, touch->last_vector.x * v1.x + touch->last_vector.y * v1.y);float_t dtheta2 = atan2f(touch->last_vector.x * v2.y - touch->last_vector.y * v2.x, touch->last_vector.x * v2.x + touch->last_vector.y * v2.y);if (tk_abs(dtheta1) > tk_abs(dtheta2)) {dtheta = dtheta2;touch->last_vector.x = v2.x;touch->last_vector.y = v2.y;} else {dtheta = dtheta1;touch->last_vector.x = v1.x;touch->last_vector.y = v1.y;}}return dtheta;
}static ret_t multi_gesture_touch_points_reset(multi_gesture_touch_points_t* touch) {return_value_if_fail(touch != NULL, RET_BAD_PARAMS);touch->last_dist = 0;touch->last_vector.x = 0;touch->last_vector.y = 0;slist_remove_all(&(touch->last_dist_list));return RET_OK;
}/*** @method multi_gesture_post_event_from_points* 发送多点触控消息* @param {main_loop_t*} loop 主循环对象* @param {multi_gesture_touch_points_t*} touch 不可识别手指类型的对象。* @param {uint32_t} point_size 手指个数* @param {multi_touch_point_event_t*} points 手指数据数组** @return {ret_t} 返回RET_OK表示成功,否则表示失败。*/
ret_t multi_gesture_post_event_from_points(main_loop_t* loop, multi_gesture_touch_points_t* touch, uint32_t point_size, multi_touch_point_event_t* points) {uint32_t i = 0;float_t t_dist = 0.0f;float_t d_dist = 0.0f;float_t d_theta = 0.0f;point_t* point_list = NULL;uint32_t num_down_fingers = 0;pointf_t centroid = { 0.0f, 0.0f };multi_gesture_event_t multi_gesture_event;return_value_if_fail(loop != NULL && points != NULL, RET_BAD_PARAMS);point_list = TKMEM_ZALLOCN(point_t, point_size);return_value_if_fail(point_list != NULL, RET_OOM);if (point_size < 2) {TKMEM_FREE(point_list);multi_gesture_touch_points_reset(touch);return RET_OK;}// printf("---------------------------------------\r\n");// printf("point_size=%d\r\n", point_size);for (i = 0; i < point_size; i++) {multi_touch_point_event_t* point = &(points[i]);uint32_t type = point->type;// printf("i:%d x:%d, y:%d type: %d\r\n",i, point->x, point->y, type);//这里我做了修改,使其能同时接受手指按下和移动的情况,便于实现一指压住一指缩放的手势if (type == EVT_MULTI_TOUCH_MOVE || type == EVT_MULTI_TOUCH_DOWN) {point_list[num_down_fingers].x = point->x;point_list[num_down_fingers].y = point->y;num_down_fingers++;}}if (num_down_fingers > 1 && touch->num_down_fingers == num_down_fingers) {if (num_down_fingers == 2) {int32_t x = point_list[0].x;int32_t y = point_list[0].y;centroid.x = x;centroid.y = y;for (i = 1; i < num_down_fingers; i++) {x -= point_list[i].x;y -= point_list[i].y;centroid.x = (centroid.x * (i - 1) + point_list[i].x) / i;centroid.y = (centroid.y * (i - 1) + point_list[i].y) / i;}t_dist = sqrtf(x * x + y * y);d_theta = multi_gesture_touch_get_dtheta(touch, point_list[0], point_list[1], t_dist);} else {touch->last_vector.x = 0;touch->last_vector.y = 0;centroid.x = point_list[i].x;centroid.y = point_list[i].y;for (i = 1; i < num_down_fingers; i++) {centroid.x = (centroid.x * (i - 1) + point_list[i].x) / i;centroid.y = (centroid.y * (i - 1) + point_list[i].y) / i;}for (i = 0; i < num_down_fingers; i++) {float_t x = point_list[i].x - centroid.x;float_t y = point_list[i].y - centroid.y;t_dist += sqrtf(x * x + y * y);}}t_dist = multi_gesture_touch_get_last_d_dists(touch, t_dist);if (touch->last_dist != 0) {d_dist = t_dist - touch->last_dist;if (tk_abs(d_dist) <= MULTI_GESTURE_DIST_MIN_RANGE) {d_dist = 0.0f;t_dist = touch->last_dist;}}printf("last_dist:%f, t_dist:%f, d_dist:%f, d_theta:%f, f:%d \r\n", touch->last_dist, t_dist, d_dist, d_theta, num_down_fingers);multi_gesture_event_init(&multi_gesture_event, window_manager(), centroid.x, centroid.y, d_theta, d_dist);main_loop_post_multi_gesture_event(loop, &multi_gesture_event);} else {multi_gesture_touch_points_reset(touch);}touch->num_down_fingers = num_down_fingers;if (num_down_fingers == 0 && touch->last_dist != 0) {multi_gesture_event_init(&multi_gesture_event, window_manager(), 0, 0, 0, 0);main_loop_post_multi_gesture_event(loop, &multi_gesture_event);}touch->last_dist = t_dist;if (t_dist == 0) {slist_remove_all(&(touch->last_dist_list));}TKMEM_FREE(point_list);return RET_OK;
}