├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── Q&A.md
├── README.md
├── include
└── tools
│ └── tools.h
├── src
├── main.cpp
└── tools
│ └── tools.cpp
└── updateLog.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | cmake-build-debug/
3 | .vscode/
4 | build/
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.21)
2 |
3 | set(CMAKE_CXX_STANDARD 20)
4 | set(NDK_PATH D:/android-ndk-r26b) # 记得修改一下ndk路径
5 |
6 | set(ANDROID_ABI arm64-v8a)
7 | set(ANDROID_NDK ${NDK_PATH})
8 | set(CMAKE_TOOLCHAIN_FILE ${NDK_PATH}/build/cmake/android.toolchain.cmake)
9 | set(ANDROID_SDK_ROOT ${NDK_PATH})
10 |
11 | project(android_virtualTouch-a)
12 |
13 | include_directories(
14 | ./include
15 | ./include/tools
16 | )
17 |
18 | file(GLOB_RECURSE src "./src/*.cpp")
19 |
20 | add_executable(touch ${src})
21 |
22 | find_library(log-lib log PATHS ${ANDROID_NDK}/sources/android)
23 |
24 | target_link_libraries(touch ${log-lib})
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 沐辰
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Q&A.md:
--------------------------------------------------------------------------------
1 | # Q&A
2 | ## Q:第一次执行正常使用,第二次无法使用
3 | ### A:出现这个问题大概率是apatch/kernelsu用户,因为root实现方式的原因(应该是),你直接划掉终端的进程并不会关闭本进程,导致本进程在后台一直运行,然后你第二次执行就出问题了,以正常方法关闭本进程即可
4 | ## Q:这个模拟触摸会不会和真实手指触摸冲突,会不会抢手?
5 | ### A:本项目完全不会和真实手指触摸冲突,也就是说在项目模拟触摸期间你可正常触摸屏幕而不会造成任何影响
6 | ## Q:模拟器虚拟机可以用吗?
7 | ### A:本项目设计之初以及代码中的逻辑均默认为真机使用,不过你可以尝试一下,可能能用,后续有适配虚拟机的想法
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # android_virtualTouch
2 | ## 介绍
3 | 在安卓上纯native层模拟手指触摸
4 |
5 | 不会和正常手指触摸冲突
6 | ## 使用
7 | void touchDown(const int& id,Vector2 pos);//按下,id可以是任何数
8 | void touchUp(const int& id);//释放
9 | void touchMove(const int& id,Vector2 pos);//x轴移动到x,y轴移动到y
10 | void monitorEvent(void (*callBack)(int slot,Vector2 data,int type));//监听触摸并调用回调函数 slot:可以理解为第几根手指 data:坐标 type: 0:touchDown,1:touchUp
11 | ## 关于
12 | 使用多点触控协议A类型
13 | 之前使用b协议的版本放到了另一个分支,有需要可以去下载
14 | -_-
15 | ## Q&A([更多](Q&A.md))
16 | ### Q:第一次执行正常使用,第二次无法使用
17 | #### A:出现这个问题大概率是apatch/kernelsu用户,因为root实现方式的原因(应该是),你直接划掉终端的进程并不会关闭本进程,导致本进程在后台一直运行,然后你第二次执行就出问题了,以正常方法关闭本进程即可
18 | ### Q:这个模拟触摸会不会和真实手指触摸冲突,会不会抢手?
19 | #### A:本项目完全不会和真实手指触摸冲突,也就是说在项目模拟触摸期间你可正常触摸屏幕而不会造成任何影响
20 | ### Q:模拟器虚拟机可以用吗?
21 | #### A:本项目设计之初以及代码中的逻辑均默认为真机使用,不过你可以尝试一下,可能能用,后续有适配虚拟机的想法
22 |
23 |
--------------------------------------------------------------------------------
/include/tools/tools.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | struct screen
5 | {
6 | int width{};
7 | int height{};
8 | int orientation{};
9 | std::vector fd{};
10 | };
11 |
12 | struct touchOBJ
13 | {
14 | int x{0};
15 | int y{0};
16 | int id{0};
17 | int TRACKING_ID{0};
18 | bool isDown{false};
19 | bool isUse{false};
20 | };
21 |
22 | class Vector2
23 | {
24 | public:
25 | Vector2();
26 | Vector2(float x, float y);
27 | Vector2(int x, int y);
28 | Vector2(Vector2 &va);
29 | Vector2& operator=(const Vector2& other);
30 | float x{};
31 | float y{};
32 | };
33 |
34 |
35 | class touch
36 | {
37 | public:
38 | touch();
39 | ~touch();
40 | void touchDown(const int& id,const Vector2 &pos);//按下,id可以是任何数
41 | void touchUp(const int& id);//释放
42 | void touchMove(const int& id,const Vector2 &pos);//x轴移动到x,y轴移动到y
43 | void monitorEvent(void (*callBack)(int slot,Vector2 data,int type));//监听触摸并调用回调函数 slot:可以理解为第几根手指 data:坐标 type: 0:touchDown,1:touchUp
44 | private:
45 | std::vector threads;//储存PTScreenEventToFingerByFd
46 | uinput_user_dev usetup{};//驱动信息
47 | int uinputFd{};//uinput的文件标识符
48 | std::thread GetScreenorientationThread{};//循环获取屏幕方向的线程
49 | float screenToTouchRatio{};//比例
50 | touchOBJ Fingers[2][10]{};//手指,物理触摸屏和模拟触摸
51 | screen screenInfo{};//屏幕信息
52 | screen touchScreenInfo{};//触摸屏信息
53 | void (*monitorCallBack)(int slot,Vector2 data,int type){nullptr};//0:touchDown,1:touchUp
54 | private:
55 | int GetNoUseIndex();//获取一个没有使用过的finger,仅限模拟触摸
56 | int GetindexById(const int& byId);
57 | void GetScrorientation();//循环获取屏幕方向
58 | static std::string exec(const std::string& command);
59 | Vector2 rotatePointx(const Vector2& pos, const Vector2& wh, bool reverse) const;//根据方向来重构坐标,pos是坐标,wh是宽高 --reverse为真代表要反向计算 //举个例子:假如你要在横屏时触摸200,200,就让reverse == ture,假如你要让原始坐标转为屏幕分辨率就让reverse == false
60 | void upLoad();//遍历Finger数组并上报
61 | void PTScreenEventToFinger(int fd=0);//将物理触摸屏的Event转化存到Finger数组
62 | void InitTouchScreenInfo();//初始化物理触摸屏信息
63 | void InitScreenInfo();//初始化屏幕信息
64 | };
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | _ooOoo_
3 | o8888888o
4 | 88" . "88
5 | (| -_- |)
6 | O\ = /O
7 | ____/`---'\____
8 | .' \\| |// `.
9 | / \\||| : |||// \
10 | / _||||| -:- |||||- \
11 | | | \\\ - /// | |
12 | | \_| ''\---/'' | |
13 | \ .-\__ `-` ___/-. /
14 | ___`. .' /--.--\ `. . __
15 | ."" '< `.___\_<|>_/___.' >'"".
16 | | | : `- \`.;`\ _ /`;.`/ - ` : | |
17 | \ \ `-. \_ __\ /__ _/ .-` / /
18 | ======`-.____`-.___\_____/___.-`____.-'======
19 | `=---='
20 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21 | 佛祖保佑 永无BUG
22 | 佛祖镇楼 BUG辟易
23 | 佛曰:
24 | 写字楼里写字间,写字间里程序员;
25 | 程序人员写程序,又拿程序换酒钱。
26 | 酒醒只在网上坐,酒醉还来网下眠;
27 | 酒醉酒醒日复日,网上网下年复年。
28 | 但愿老死电脑间,不愿鞠躬老板前;
29 | 奔驰宝马贵者趣,公交自行程序员。
30 | 别人笑我忒疯癫,我笑自己命太贱;
31 | 不见满街漂亮妹,哪个归得程序员?
32 | !!BUG退散!!
33 | */
34 | #include
35 | #include
36 | #include "tools.h"
37 |
38 | void monitorTest(int slot,Vector2 data,int type)
39 | {
40 | std::cout<<"slot: "<
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #define TAG "muchen"
16 | #define Log __android_log_print
17 |
18 | Vector2::Vector2(int x, int y)
19 | {
20 | this->x = (float) x;
21 | this->y = (float) y;
22 | }
23 |
24 | Vector2::Vector2(float x, float y)
25 | {
26 | this->x = x;
27 | this->y = y;
28 | }
29 |
30 | Vector2::Vector2()
31 | {
32 | x = 0;
33 | y = 0;
34 | }
35 |
36 | Vector2::Vector2(Vector2 &va)
37 | {
38 | this->x = va.x;
39 | this->y = va.y;
40 | }
41 |
42 | Vector2 &Vector2::operator=(const Vector2 &other)
43 | {
44 | // 防止自赋值
45 | if (this != &other)
46 | {
47 | this->x = other.x;
48 | this->y = other.y;
49 | }
50 | return *this;
51 | }
52 |
53 | void touch::InitTouchScreenInfo()
54 | {
55 | for (const auto &entry : std::filesystem::directory_iterator("/dev/input/"))
56 | {
57 | int fd = open(entry.path().c_str(), O_RDWR);
58 | if (fd < 0)
59 | {
60 | Log(ANDROID_LOG_WARN,TAG, "%s", std::string ("打开 "+entry.path().string()+"失败").c_str());
61 | }
62 | input_absinfo absinfo{};
63 | ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &absinfo);
64 | if (absinfo.maximum == 9)
65 | {
66 | Log(ANDROID_LOG_INFO,TAG, "%s", std::string ("找到疑似触摸节点: "+entry.path().string()).c_str());
67 | this->touchScreenInfo.fd.emplace_back(open(entry.path().c_str(), O_RDWR));
68 |
69 | if (touchScreenInfo.width == 0 || touchScreenInfo.height == 0)
70 | {
71 | input_absinfo absX{}, absY{};
72 | ioctl(fd, EVIOCGABS(ABS_MT_POSITION_X), &absX);
73 | ioctl(fd, EVIOCGABS(ABS_MT_POSITION_Y), &absY);
74 | if (absX.maximum != 0 && absY.maximum != 0)
75 | {
76 | this->touchScreenInfo.width = absX.maximum;
77 | this->touchScreenInfo.height = absY.maximum;
78 | }
79 | }
80 | }
81 | close(fd);
82 | } // 遍历/dev/input/下所有eventX,如果ABS_MT_SLOT为9(即最大支持10点触控)就视为物理触摸屏
83 | }
84 |
85 | void touch::InitScreenInfo()
86 | {
87 | std::string ScreenSize = exec("wm size");
88 | std::istringstream ScreenSizeStream(ScreenSize);
89 | std::string line{};
90 |
91 | while (std::getline(ScreenSizeStream, line))
92 | {
93 | if (sscanf(line.c_str(), "Override size: %dx%d",&this->screenInfo.width, &this->screenInfo.height) == 2)
94 | {
95 | break; // 找到后立即退出循环
96 | }
97 | sscanf(line.c_str(), "Physical size: %dx%d",&this->screenInfo.width, &this->screenInfo.height);
98 |
99 | }//有Override size则优先使用,找不到就使用Physical size
100 | }//初始化屏幕分辨率,方向单独放在一个线程了
101 |
102 | touch::touch()
103 | {
104 | InitScreenInfo();
105 | InitTouchScreenInfo();
106 | for (const auto &entry : touchScreenInfo.fd)
107 | {
108 | threads.emplace_back(&touch::PTScreenEventToFinger, this, entry);
109 | }
110 | GetScreenorientationThread = std::thread(&touch::GetScrorientation, this);
111 | sleep(2);
112 |
113 | this->uinputFd = open("/dev/uinput", O_RDWR);
114 | if (uinputFd < 0)
115 | {
116 | Log(ANDROID_LOG_ERROR,TAG, "uinput打开失败");
117 | throw std::runtime_error("uinput打开失败");
118 | }
119 |
120 | ioctl(uinputFd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);//设置为直接输入设备
121 | ioctl(uinputFd, UI_SET_EVBIT, EV_ABS);
122 | ioctl(uinputFd, UI_SET_EVBIT, EV_KEY);
123 | ioctl(uinputFd, UI_SET_EVBIT, EV_SYN);//支持的事件类型
124 |
125 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_TOUCH_MINOR);
126 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_X);
127 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_Y);
128 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
129 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR);
130 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
131 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
132 | ioctl(uinputFd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);//支持的事件
133 |
134 | ioctl(uinputFd, UI_SET_KEYBIT, BTN_TOUCH);
135 | ioctl(uinputFd, UI_SET_KEYBIT, BTN_TOOL_FINGER);//支持的事件
136 |
137 |
138 | usetup.id.bustype = BUS_SPI;
139 | usetup.id.vendor = 0x6c90;
140 | usetup.id.product = 0x8fb0;
141 | strcpy(usetup.name, "Virtual Touch Screen for muchen");//驱动信息
142 |
143 | usetup.absmin[ABS_X] = 0;
144 | usetup.absmax[ABS_X] = 1599;
145 | usetup.absmin[ABS_Y] = 0;
146 | usetup.absmax[ABS_Y] = 2559;
147 | usetup.absmin[ABS_MT_POSITION_X] = 0;
148 | usetup.absmax[ABS_MT_POSITION_X] = touchScreenInfo.width;
149 | usetup.absfuzz[ABS_MT_POSITION_X] = 0;
150 | usetup.absflat[ABS_MT_POSITION_X] = 0;
151 | usetup.absmin[ABS_MT_POSITION_Y] = 0;
152 | usetup.absmax[ABS_MT_POSITION_Y] = touchScreenInfo.height;
153 | usetup.absfuzz[ABS_MT_POSITION_Y] = 0;
154 | usetup.absflat[ABS_MT_POSITION_Y] = 0;
155 | usetup.absmin[ABS_MT_PRESSURE] = 0;
156 | usetup.absmax[ABS_MT_PRESSURE] = 1000;//触摸压力的最大最小值
157 | usetup.absfuzz[ABS_MT_PRESSURE] = 0;
158 | usetup.absflat[ABS_MT_PRESSURE] = 0;
159 | usetup.absmax[ABS_MT_TOUCH_MAJOR] = 255; //与屏接触面的最大值
160 | usetup.absmin[ABS_MT_TRACKING_ID] = 0;
161 | usetup.absmax[ABS_MT_TRACKING_ID] = 65535; //按键码ID累计叠加最大值
162 |
163 | write(uinputFd, &usetup, sizeof(usetup));//将信息写入即将创建的驱动
164 |
165 | ioctl(uinputFd, UI_DEV_CREATE);//创建驱动
166 |
167 | for (const auto &entry : touchScreenInfo.fd)
168 | {
169 | ioctl(entry, EVIOCGRAB, 0x1); // 独占输入,只有此进程才能接收到事件 -_-
170 | }
171 |
172 | std::cout << "触摸屏宽高 " << touchScreenInfo.width << " " << touchScreenInfo.height << std::endl;
173 | std::cout << "屏幕分辨率 " << screenInfo.width << " " << screenInfo.height << std::endl;
174 | Log(ANDROID_LOG_INFO,TAG,"%s",std::string("触摸屏宽高: " + std::to_string(touchScreenInfo.width) + "*" + std::to_string(touchScreenInfo.height)).c_str());
175 | Log(ANDROID_LOG_INFO,TAG,"%s",std::string("屏幕分辨率: " + std::to_string(screenInfo.width) + "*" + std::to_string(screenInfo.height)).c_str());
176 | screenToTouchRatio =(float) (screenInfo.width + screenInfo.height) / (float) (touchScreenInfo.width + touchScreenInfo.height);
177 | if (screenToTouchRatio < 1 && screenToTouchRatio > 0.9)
178 | {
179 | screenToTouchRatio = 1;
180 | }
181 | input_event down{};
182 | down.type = EV_KEY;
183 | down.code = BTN_TOUCH;
184 | down.value = 1;
185 | write(uinputFd, &down, sizeof(down));
186 | sleep(2);
187 | }
188 |
189 | touch::~touch()
190 | {
191 | ioctl(uinputFd, UI_DEV_DESTROY);
192 | close(uinputFd);
193 | GetScreenorientationThread.detach();
194 | for (std::thread &item: threads)
195 | {
196 | item.detach();
197 | }
198 | }
199 |
200 |
201 | void touch::PTScreenEventToFinger(int fd)
202 | {
203 | input_event ie{};
204 | int latestSlot{};
205 | while (true)
206 | {
207 | read(fd, &ie, sizeof(ie));
208 | {
209 | if (ie.type == EV_ABS)
210 | {
211 | if (ie.code == ABS_MT_SLOT)
212 | {
213 | latestSlot = ie.value;
214 | Fingers[0][latestSlot].TRACKING_ID = 114514 + latestSlot;
215 | continue;
216 | }
217 | if (ie.code == ABS_MT_TRACKING_ID)
218 | {
219 | if (ie.value == -1)
220 | {
221 | Fingers[0][latestSlot].isDown = false;
222 | Fingers[0][latestSlot].isUse = false;
223 | Fingers[0][latestSlot].x = 0;
224 | Fingers[0][latestSlot].y = 0;
225 | } else
226 | {
227 | Fingers[0][latestSlot].isUse = true;
228 | Fingers[0][latestSlot].isDown = true;
229 | }
230 | continue;
231 | }
232 | if (ie.code == ABS_MT_POSITION_X)
233 | {
234 | Fingers[0][latestSlot].x = ie.value;
235 | continue;
236 | }
237 | if (ie.code == ABS_MT_POSITION_Y)
238 | {
239 | Fingers[0][latestSlot].y = ie.value;
240 | continue;
241 | }
242 | }
243 | if (ie.type == EV_SYN)
244 | {
245 | if (ie.code == SYN_REPORT)
246 | {
247 | if(monitorCallBack!= nullptr)
248 | {
249 | if(Fingers[0][latestSlot].isDown)
250 | {
251 | Vector2 newPos = rotatePointx({Fingers[0][latestSlot].x,Fingers[0][latestSlot].y},{screenInfo.width, screenInfo.height},false);
252 | newPos.x *= this->screenToTouchRatio;
253 | newPos.y *= this->screenToTouchRatio;
254 | monitorCallBack(latestSlot,newPos,0);
255 | }
256 | if(!Fingers[0][latestSlot].isDown)
257 | {
258 | monitorCallBack(latestSlot, {0,0},1);
259 | }
260 | }
261 | upLoad();
262 | continue;
263 | }
264 | continue;
265 | }
266 | }
267 | }
268 | }
269 |
270 |
271 | void touch::upLoad()
272 | {
273 | std::vector events{};
274 | for (auto &fingers: Fingers)
275 | {
276 | for (auto &finger: fingers)
277 | {
278 | if (finger.isDown)
279 | {
280 | input_event down_events[]
281 | {
282 | {.type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = finger.TRACKING_ID},
283 | {.type = EV_ABS, .code = ABS_MT_POSITION_X, .value = finger.x},
284 | {.type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = finger.y},
285 | {.type = EV_SYN, .code = SYN_MT_REPORT, .value = 0},
286 | };
287 | int arrCount = sizeof(down_events) / sizeof(down_events[0]);
288 | events.insert(events.end(), down_events, down_events + arrCount);
289 | }
290 | }
291 | }
292 | input_event touchEnd{};
293 | touchEnd.type = EV_SYN;
294 | touchEnd.code = SYN_MT_REPORT;
295 | touchEnd.value = 0;
296 | events.push_back(touchEnd);
297 | input_event end{};
298 | end.type = EV_SYN;
299 | end.code = SYN_REPORT;
300 | end.value = 0;
301 | events.push_back(end);
302 | for (const auto &event: events)
303 | {
304 | write(uinputFd, &event, sizeof(event));
305 | }
306 | events.clear();
307 | }
308 |
309 | std::string touch::exec(const std::string &command)
310 | {
311 | char buf[1024];
312 | std::string result{};
313 | FILE* pipe = popen(command.c_str(), "r");
314 |
315 | if (!pipe)
316 | {
317 | Log(ANDROID_LOG_WARN,TAG,"%s",std::string ("命令 "+ command+ "执行失败").c_str());
318 | return "";
319 | }
320 | while (fgets(buf, sizeof(buf), pipe))
321 | {
322 | result += buf;
323 | }
324 | pclose(pipe);
325 | return result;
326 | }
327 |
328 | void touch::GetScrorientation()
329 | {
330 | while (true)
331 | {
332 | this->screenInfo.orientation = atoi(exec("dumpsys display | grep 'mCurrentOrientation' | cut -d'=' -f2").c_str());
333 | std::this_thread::sleep_for(std::chrono::seconds(5));
334 | }
335 | }
336 |
337 |
338 | Vector2 touch::rotatePointx(const Vector2 &pos, const Vector2 &wh, bool reverse) const
339 | {
340 | Vector2 rotated{pos.x,pos.y};
341 | switch (screenInfo.orientation)
342 | {
343 | case 0: // 竖屏
344 | return rotated;
345 | break;
346 | case 1: // 横屏
347 | rotated.y = reverse ? pos.x : wh.y - pos.x;
348 | rotated.x = reverse ? wh.x - pos.y : pos.y;
349 | break;
350 | case 2: // 反向竖屏
351 | rotated.x = wh.x - pos.x;
352 | rotated.y = wh.y - pos.y;
353 | break;
354 | case 3: // 反向横屏
355 | rotated.y = reverse ? wh.y - pos.x : pos.x;
356 | rotated.x = reverse ? pos.y : wh.x - pos.y;
357 | break;
358 | }
359 | return rotated;
360 | }
361 | int touch::GetindexById(const int &byId)
362 | {
363 | for (int i{0}; i < 10; i++)
364 | {
365 | if (Fingers[1][i].id == byId)
366 | {
367 | return i;
368 | }
369 | }
370 | return -1;
371 | }
372 |
373 | int touch::GetNoUseIndex()
374 | {
375 | for (int i{0}; i < 10; i++)
376 | {
377 | if (!Fingers[1][i].isUse)
378 | {
379 | return i;
380 | }
381 | }
382 | return -1;
383 | }
384 |
385 | void touch::touchDown(const int &id, const Vector2 &pos)
386 | {
387 | int index = GetNoUseIndex();
388 | if (Fingers[1][index].isDown && Fingers[1][index].isUse)
389 | {
390 | return;
391 | }
392 | Vector2 newPos = rotatePointx(pos, {screenInfo.width, screenInfo.height}, true);
393 | newPos.x /= this->screenToTouchRatio;
394 | newPos.y /= this->screenToTouchRatio;
395 | Fingers[1][index].isDown = true;
396 | Fingers[1][index].id = id;
397 | Fingers[1][index].TRACKING_ID = 415411 + id;
398 | Fingers[1][index].x = (int) newPos.x;
399 | Fingers[1][index].y = (int) newPos.y;
400 | Fingers[1][index].isUse = true;
401 | this->upLoad();
402 | }
403 |
404 | void touch::touchMove(const int &id, const Vector2 &pos)
405 | {
406 | int index = GetindexById(id);
407 | if (index == -1)
408 | {
409 | return;
410 | }
411 | if (!(Fingers[1][index].isUse && Fingers[1][index].isDown))
412 | {
413 | return;
414 | }
415 | Vector2 newPos = rotatePointx(pos, {screenInfo.width, screenInfo.height}, true);
416 | newPos.x /= this->screenToTouchRatio;
417 | newPos.y /= this->screenToTouchRatio;
418 | Fingers[1][index].x = (int) newPos.x;
419 | Fingers[1][index].y = (int) newPos.y;
420 | this->upLoad();
421 | }
422 |
423 | void touch::touchUp(const int &id)
424 | {
425 | int index = GetindexById(id);
426 | if (!(Fingers[1][index].isDown && Fingers[1][index].isUse))
427 | {
428 | return;
429 | }
430 | Fingers[1][index].isDown = false;
431 | Fingers[1][index].isUse = false;
432 | Fingers[1][index].id = 0;
433 | this->upLoad();
434 | }
435 |
436 | void touch::monitorEvent(void (*callBack)(int, Vector2, int))
437 | {
438 | monitorCallBack = callBack;
439 | }
440 |
441 |
442 |
--------------------------------------------------------------------------------
/updateLog.md:
--------------------------------------------------------------------------------
1 | # V0.5
2 | **240121发布,bug若干,基本不可用**
3 | # V0.9
4 | **修复bug若干,实现与正常触摸不冲突,基本可用**
5 | # V1.0
6 | **修复小bug若干,已经兼容大部分机型,完全可用状态**
--------------------------------------------------------------------------------