安卓纯native层模拟手指触摸

前言: 写此项目主要是因为市面上好像并没有一个像样的模拟触摸工具(不与手指触摸冲突.有人维护并且开源)

其实这个项目去年就写完了,但是还是突然想记录一下…

思路

本来想的很简单,用uinput模块创建驱动并提交事件,但是写完了才发现并没有这么简单,一但手指触摸和虚拟触摸同时存在就会冲突

然后又想着可否把真实的触摸给屏蔽掉,与虚拟触摸进行融合,然后在虚拟设备中统一提交?

说干就淦!

实现

初始化屏幕信息

void touch::InitScreenInfo()
{
std::string window_size = exec("wm size");
sscanf(window_size.c_str(), "Physical size: %dx%d", &this->screenInfo.width, &this->screenInfo.height);
}//初始化屏幕分辨率,方向单独放在一个线程了

初始化触摸屏信息

void touch::InitTouchScreenInfo()
{
bool isFound{false};//是否已经找到了一个疑似触摸屏的设备
for (const auto &entry: std::filesystem::directory_iterator("/dev/input/"))
{
int fd = open(entry.path().c_str(), O_RDWR);
if(fd < 0)
{
std::cout<<"打开"<<entry.path()<<"失败"<<std::endl;
}
input_absinfo absinfo{};
ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &absinfo);
if (absinfo.maximum == 9)
{
if(!isFound)
{
isFound = true;
this->touchScreenInfo.fd = open(entry.path().c_str(), O_RDWR);
close(fd);
if(touchScreenInfo.width == 0||touchScreenInfo.height == 0)
{
input_absinfo absX{}, absY{};
ioctl(touchScreenInfo.fd, EVIOCGABS(ABS_MT_POSITION_X), &absX);
ioctl(touchScreenInfo.fd, EVIOCGABS(ABS_MT_POSITION_Y), &absY);
if(absX.maximum!=0&&absY.maximum!=0)
{
this->touchScreenInfo.width = absX.maximum;
this->touchScreenInfo.height = absY.maximum;
}
}
}
else
{
if(touchScreenInfo.width == 0||touchScreenInfo.height == 0)
{
input_absinfo absX{}, absY{};
ioctl(fd, EVIOCGABS(ABS_MT_POSITION_X), &absX);
ioctl(fd, EVIOCGABS(ABS_MT_POSITION_Y), &absY);
if(absX.maximum!=0 && absY.maximum!=0)
{
this->touchScreenInfo.width = absX.maximum;
this->touchScreenInfo.height = absY.maximum;
}
}
ioctl(fd, EVIOCGRAB, 0x1);//独占输入,只有此进程才能接收到事件 -_-
threads.emplace_back(&touch::PTScreenEventToFingerByFd,this,fd);
}
}
}//遍历/dev/input/下所有eventX,如果ABS_MT_SLOT为9(即最大支持10点触控)就视为物理触摸屏
}

这样写是因为ABS_MT_SLOT为9的设备在部分设备上好像不止一个……

转换

把物理触摸信息转换为touchOBJ

void touch::PTScreenEventToFinger()
{
input_event ie{};
int latestSlot{};
while (true)
{
read(touchScreenInfo.fd, &ie, sizeof(ie));
{
if (ie.type == EV_ABS)
{
if (ie.code == ABS_MT_SLOT)
{
latestSlot = ie.value;
Fingers[0][latestSlot].TRACKING_ID = 114514 + latestSlot;
continue;
}
if (ie.code == ABS_MT_TRACKING_ID)
{
if (ie.value == -1)
{
Fingers[0][latestSlot].isDown = false;
Fingers[0][latestSlot].isUse = false;
} else
{
Fingers[0][latestSlot].isUse = true;
Fingers[0][latestSlot].isDown = true;
}
continue;
}
if (ie.code == ABS_MT_POSITION_X)
{
Fingers[0][latestSlot].x = ie.value;
continue;
}
if (ie.code == ABS_MT_POSITION_Y)
{
Fingers[0][latestSlot].y = ie.value;
continue;
}
}
if (ie.type == EV_SYN)
{
if (ie.code == SYN_REPORT)
{
upLoad();
continue;
}
continue;
}
}
}
}

把touchOBJ转为触摸信息并提交

void touch::upLoad()
{
std::vector<input_event> events{};
for (auto &fingers: Fingers)
{
for (auto &finger: fingers)
{
if (finger.isDown)
{
input_event down_events[]
{
{.type = EV_ABS, .code = ABS_MT_TRACKING_ID, .value = finger.TRACKING_ID},
{.type = EV_ABS, .code = ABS_MT_POSITION_X, .value = finger.x},
{.type = EV_ABS, .code = ABS_MT_POSITION_Y, .value = finger.y},
{.type = EV_SYN, .code = SYN_MT_REPORT, .value = 0},
};
int arrCount = sizeof(down_events) / sizeof(down_events[0]);
events.insert(events.end(), down_events, down_events + arrCount);
}
}
}
input_event touchEnd{};
touchEnd.type = EV_SYN;
touchEnd.code = SYN_MT_REPORT;
touchEnd.value = 0;
events.push_back(touchEnd);
input_event end{};
end.type = EV_SYN;
end.code = SYN_REPORT;
end.value = 0;
events.push_back(end);
for (const auto &event: events)
{
write(uinputFd, &event, sizeof(event));
}
events.clear();
}

BY

有几点可能要说一下

1.最后提交的时候是使用的多点触控a协议,因为b协议我写出来有bug,没找出哪里的原因,发现linux内核同时支持ab,索性就用a了

2.效率略低,有很大优化空间

3.开源地址