AWTK-LINUX-FB实现多点触摸

两周前的一个地图,需要做手势缩放的功能,比较忙就没有发出来,这次抽时间记录一下实现的过程。

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>

最终效果:

img_v3_02gf_c5c91cb1-e975-47b6-b6a7-f3b2be9bd78h

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/9972.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

NRF52832学习笔记(41)——添加串口库libuarte

一、背景 由于板子上不支持硬件流控&#xff0c;在使用 app_uart_fifo 库接收串口大数据时&#xff0c;频繁报 APP_UART_COMMUNICATION_ERROR 错误&#xff0c;多次重新初始化后&#xff0c;串口也不再产生中断了。查看官方论坛后决定使用串口异步库 libuarte。 二、简介 Li…

ORU 的 Open RAN 管理平面 (M 平面)

[TOC](ORU 的 Open RAN 管理平面 (M 平面)) ORU 的 Open RAN 管理平面 (M 平面) https://www.techplayon.com/open-ran-management-plane-m-plane-for-open-radio-unit/ ORU M 平面 在 ORAN 中&#xff0c;设置参数的 O-RU 管理功能是通过 M-Plane 完成的。管理功能包括 O-…

MQ的基础知识

一.什么是MQ 其实就是不同的程序之间的一种的通信方式,通过将消息发送到中间件里面然后中间件就会将这个消息发送给相应的服务进行一个消息的消费,这个时候就会进行一些的业务逻辑的处理,这个方式提高了整个系统的可靠性,拓展性以及灵活性.常见的类型为Aapache Kafaka&#xf…

蓝牙OPP协议详解及Android实现

文章目录 前言一、什么是蓝牙OPP协议&#xff1f;二、OPP协议工作流程1. 设备配对和连接2. 启动 OPP 服务3. 发送对象4. 传输对象5. 传输完成6. 断开连接 三、 Android OPP协议实现1. 启动 OPP 服务器&#xff08;接收方&#xff09;2. 发送文件&#xff08;发送方&#xff09;…

医学可视化之涟漪图

在医学领域&#xff0c;数据可视化能够帮助我们更直观地理解和分析复杂的信息。涟漪图作为一种独特的可视化工具&#xff0c;具有重要的作用、价值和广泛的使用场景。 一、涟漪图的特点 涟漪图是一种基于地理位置的可视化图表&#xff0c;它通过在地图上显示不同大小或颜色的…

定义宏将整数的二进制的奇数位和偶数位互换位置

假设这个数为n00000000 00000000 00000000 00001101——13 1.思路 1.1 奇数位&#xff1a;00000000 00000000 00000000 00000101 但是怎么获得奇数位呢&#xff1f;——进行按位与运算 不懂如何运算的可以看我主页的详解操作符-CSDN博客&#xff0c;该章详细写了各个操作符如何…

快要结束的大学时光

目录 大一 大一上学期 Java HTMLCSS 大一下学期 HTMLCSSJS JAVA python 大二 大二上学期 Java 原型 前端 大二下学期 前端 数据结构 Android BootStrap JavaWeb ios程序设计 软件测试​编辑 大三 大三上学期 小程序 大三下学期 JavaWeb 整理数据库 大…

高效实现MySQL数据集成的具体案例分享

MySQL数据集成案例分享&#xff1a;1–BI秉心-店铺信息表–store_z–>store 在数据驱动的业务环境中&#xff0c;如何高效、可靠地实现数据集成是每个企业面临的重要挑战。本文将聚焦于一个具体的系统对接集成案例&#xff1a;将MySQL中的店铺信息表store_z的数据集成到另一…

[Docker#3] LXC | 详解安装docker | docker的架构与生态

目录 1.LXC容器操作 安装LXC LXC容器操作步骤 2.理论 LXC 是什么&#xff1f; Docker 是什么 Docker 和虚拟机的区别 Docker 和 JVM 虚拟化的区别 Docker 版本 ⭕Docker 官方网站&#xff08;建议收藏&#xff09; Docker 架构 生活案例 Docker 生态 Docker 解决…

CAP相关的分布式技术

目录 一&#xff0c;CAP理论基础 1.1、一致性&#xff08;Consistency&#xff09; 1.2、可用性&#xff08;Availability&#xff09; 1.3、分区容忍性&#xff08;Partition Tolerance&#xff09; 1.4、CAP理论的核心观点 二&#xff0c;如何选C与A 2.1、网络分区情况…

【春秋云镜】CVE-2023-2130

目录 CVE-2023-2130漏洞利用漏洞检测防御措施 靶标介绍&#xff1a;解法一&#xff1a;解法二&#xff1a; CVE-2023-2130 漏洞详细信息 漏洞编号&#xff1a;CVE-2023-2130漏洞名称&#xff1a;SQL注入漏洞受影响的版本&#xff1a;SourceCodester采购订单管理系统1.0影响范…

Code::Blocks 24.10 全中文优化完整版

Code::Blocks&#xff08;或者叫做 CodeBlocks&#xff09;是一款开放源代码、跨平台的集成开发环境&#xff08;IDE&#xff09;&#xff0c;通过配置不同的编程语言编译器&#xff0c;可以用于多种编程语言程序开发。 网上有很多文章介绍 Code::Blocks 的安装&#xff0c;通…

二叉树-哈夫曼树的构造和应用

重点:哈夫曼树的构造和应用(编码) 选取完最小权值的两个节点后新结点的权值是二者之和,新节点可以和选取剩余的结点结合,也可以在剩余的里面选出最小两个结合后形成的新结点与第一个新结点结合(前提他们是最小的两个结点) 哈夫曼编码 哈夫曼编码优化 130为最小的带权路径长度 …

d3坐标轴系数角度变换-位置不对等问题

svg.append(text).attr(x, 100) // 文本 x 坐标.attr(y,200 ) // 文本 y 坐标// .attr(text-anchor, middle) // 文本居中.attr(fill, black) // 文本颜色.attr(transform, rotate(-90, 25, 30)) // 旋转 -90 度.attr(font-size, 9).text(你的文本); 有些老哥…

rosbag数据导出成pcd文件

目录 步骤 1&#xff1a;安装必要的 ROS 包步骤 2&#xff1a;播放 .bag 文件中的点云数据&#xff08;非必须&#xff09;步骤 3&#xff1a;使用 pcl_ros 提取并保存点云数据步骤 4&#xff1a;验证输出 要将 .bag 文件中的点云数据导出为 .pcd 文件&#xff0c;通常需要以…

基于 Spring Boot 和 Vue 的门票销售创新系统

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

推荐一款管道数据检索工具:Pipedata-Pro

Pipedata-Pro是一款专为设计石油、天然气、水和蒸汽管道及管道系统的工程师开发的应用程序。该应用程序提供了设计管道系统所需的工程数据&#xff0c;拥有一个全面的管道类型、配件和材料数据库。 软件特点&#xff1a; 1. 技术参数查询&#xff1a;Pipedata-Pro 提供关于管道…

使用 Umami 部署博客分析工具

Umami 简介 Umami 是一款开源且注重隐私的网站分析工具&#xff0c;可替代 Google Analytics。它提供网站流量和用户行为等见解&#xff0c;但不使用 Cookie 或收集个人数据&#xff0c;符合隐私法规。Umami 轻巧易用&#xff0c;可自行托管。 如果你有自己的博客&#xff0c;…

三菱QD77MS定位模块速度更改功能

速度更改功能” 是以任意时机将控制中的速度更改为新指定的速度的功能。更改后的速度直接设置到缓冲存储器中&#xff0c;并根据速度更改指令([cd.15速度更改请求)或者外部指令信号执行速度更改。 但是&#xff0c;机械原点复位的情况下&#xff0c;检测出近点狗 ON 并开始向蠕…

typescript 补充

文章目录 Pick<T, K> 从 T 中挑选部分属性构成新类型Partial<T>&#xff1a;将类型的所有属性变为可选Required<T>&#xff1a;将类型的属性变为必选。Omit<T, K>&#xff1a;从 T 中移除部分属性构成新类型。Readonly<T>&#xff1a;将类型的属…