在获取系统触摸后,剩下的就是解决数据传递和画框子的问题了.
数据传递
由于 jni 是没有 root 权限的,所以只能由 java 端运行一个有 root 权限的进程,然后在 root 进程里将读取到的数据传回 imgui 进程.
匿名共享内存
这是我比较推荐的一个做法,首先用 open "/dev/ashmem" 打开匿名共享内存,设置大小,然后 mmap 函数映射到本地内存.
1 | fd = open("/dev/ashmem", O_RDWR); |
之后通过 tcp 或 udp unix socket 将 fd 传给 imgui 进程里.
1 | send_asheme_fd(fd); |
imgui 进程同样使用 mmap 来映射匿名共享内存
1 | mmap(NULL, sizeof(AshmemData), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); |
映射建立后,双方就可以使用匿名共享内存通信了.
共享内存结构
一般来说,共享内存包括了需要绘制的人物坐标,载具坐标,物品坐标,还需要额外传递一个 fps 值,这个 fps 值用来读取内存,画面同步.
1 | struct AshmemData{ |
读取数据
如果是用外部读写的方案,那么可以选择如下方法
- 读取 /proc/pid/mem 文件.
- 读取 /proc/pid/pagemap 文件.
- ptrace 读内存
- syscall(SYS_process_vm_readv, pid, local, 1, remote, 1, 0);
如果用 Magisk 注入的 imgui, 那就比较简单,直接读取即可.
这里建议有几类数据就开几个线程,比如这里有 3 类数据 (人物,载具,物品), 就开三个线程.
数据读取速率与画面刷新速率同步
屏幕刷新速率通常是固定的,比如 120hz 的屏幕,每更新 1 帧的时间大约为 0.0083 秒,但是你并不能保证读取数据的速率也是 0.0083 秒。如果不加限制,就会造成性能的浪费,
比如屏幕刷新率比数据读取率快,那么就会导致几帧刷新同一画面.
如果屏幕刷新率比数据读取率慢,就会导致损失几帧的数据.
通常的做法是计算出每次数据读取的时间.
1 | clock_gettime(CLOCK_MONOTONIC,&old); |
clock_gettime (CLOCK_MONOTONIC,&now); 可以读取到当前的时间.
在所有线程开始读取前先调用这个函数,读取数据后调用。然后相减即可得到读取一次数据的时间.
同样的,在 imgui 渲染线程里同样需要调用这个函数来计算渲染一次所需要的时间
如果谁的时间少,就需要调用 usleep 函数来等待到 fps 值.
1 | usleep(time) |
imgui 画框子,文字
1.imgui 画框子
1 | void DrawTool::DrawRect(int x, int y, int w, int h, RGBA *color, int thickness) |
2.imgui 画文字
1 | void DrawTool::DrawNewText(int x, int y, RGBA *color, const char *str) |