android 按键精灵。C++编写。

可以记录屏幕键盘等传感器对系统的输入。

上一篇文章做的那个稍微有点复杂了,还需要把板子的输出拿回电脑处理再倒回去。这个就简单多了用法如下

usage:
event record /path/file1
event replay /path/file1

给我女友写的程序直接搬过来了,所以注释有些冗余。

"stdafx.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <asm/types.h>
#include <unistd.h>
#include <iostream>
#include <vector>
struct input_event {
    timeval time;
    __u16 type;
    __u16 code;
    __s32 value;
};
struct myinputevent:public input_event
//继承了16字节的数据结构,添加了一个deviceID
{
    int deviceID;

};

main.cpp

#include "stdafx.h"

void record(char* filename);
void replay(char* filename);
int main(int argc, char *argv[])
{
    if(argc!=3 || (strcmp(argv[1],"record") & strcmp(argv[1],"replay")))
    {
        std::cout<<

                "usage:"<<std::endl<<
                "event record /path/file1"<<std::endl<<
                "event replay /path/file1"<<std::endl;
        return 0;
    }
    if(!strcmp(argv[1],"record"))
        record(argv[2]);
    else
        replay(argv[2]);
    return 0;
}

 

replay.cpp

#include "stdafx.h"
//#define DEBUG
int timedif(timeval& a,timeval& b)
//这是个计算时间差的函数,对于一个timeval的数据结构,前面4字节秒,后面4字节微秒
//这个函数接受两个这样的结构,返回一个微秒单位的时间差
{
    return (b.tv_sec-a.tv_sec)*1000000+(b.tv_usec-a.tv_usec);
}


void replay(char* filename)
{

    int fi = open(filename,O_RDONLY);
    if(fi==-1)
    {
        std::cout<<filename<<" cannot be opened"<<std::endl;
        exit(0);
    }


    char dev[40]="/dev/input/eventX";


    int DeviceFile[10];
    for(char i='0';i<='9';++i)
    {
        dev[16]=i;
        int f=open(dev,O_RDWR);
        //把所有的event都打开准备写入
        DeviceFile[i-'0']=f;
        //把句柄保存下来,如果打不开的话f值是-1
#ifdef DEBUG
        if(f!=-1)
            std::cout<<"Device opened:"<<i<<std::endl;
#endif

    }
    myinputevent event0,event1;
    read(fi,&event0,sizeof(myinputevent));
    //先读取第一个event到event0中
    timeval timetemp=event0.time;
    //临时存储event中的时间,因为往设备写的时候,时间要清空
    memset(&event0.time, 0, sizeof(struct timeval));
    //清空时间
    write(DeviceFile[event0.deviceID], &event0.time, sizeof(input_event));
    //写回设备
    event0.time=timetemp;
    //把刚刚保存的时间拿回来
    while(read(fi,&event1,sizeof(myinputevent)))
    //只要文件还能读,就读取下一个event到event1中
    {

        int sleeptime=timedif(event0.time,event1.time);
        //计算event0和event1的时间差
#ifdef DEBUG
        std::cout<<"sleep"<<sleeptime<<std::endl;
#endif
        usleep(sleeptime);
        //进行时间间隔
        event0.time=event1.time;
        //把event1的时间赋值给event0,这样下次循环的时候继续对比下一个时间差
        memset(&event1.time, 0, sizeof(struct timeval));
        //清空event1的时间
        write(DeviceFile[event1.deviceID], &event1.time, sizeof(input_event));
        //把event1写回设备
    }
    for(int i=0;i<=9;++i)
    {
        if(DeviceFile[i]!=-1)
            close(DeviceFile[i]);
        //关闭所有设备

    }
    std::cout<<"Replay completed"<<std::endl;
}

record.cpp

 

#include "stdafx.h"

#define DEBUG
inline bool eventcmp(const myinputevent& a,const myinputevent& b)
//这个比较函数为了之后排序用,因为两个自定义的数据结构,你用标准排序函数,排序肯定不知道你这俩结构应该怎么比大小
//定义了我的数据结构之间如果做小于比较时,时间小就算小。
//这样就可以采用标准排序函数对事件进行排序了。
{
    if(a.time.tv_sec<b.time.tv_sec)
        return 1;
    if(a.time.tv_sec>b.time.tv_sec)
        return 0;
    if(a.time.tv_usec<b.time.tv_usec)
        return 1;
    else
        return 0;
}



std::vector<myinputevent> AllRecordedEvent;
//这里有个数组,用来添加所有录制到的event
char fn[200];
void afterstop(int x)
//按下ctrl+c和回车之后,录制停止,做数据整理和保存工作
//主要是按时间顺序对事件排序,然后保存
{
#ifdef DEBUG
    std::cout<<"Recorded events:"<<AllRecordedEvent.size()<<std::endl;
#endif
    std::cout<<"Sorting & saving..."<<std::endl;
    std::stable_sort(AllRecordedEvent.begin(),AllRecordedEvent.end(),eventcmp);
    std::cout<<"Sort completed"<<std::endl;
    //这是排序语句,对所有的录制的event排序,排序时大小比较用eventcmp函数,也就是根据时间先后排序啦
    //因为有的时候,设备的输出并不按照时间顺序,比如你按开机键,它涉及的不止一个event,有时会有微微的时间上的错序
    //任何错序都会严重影响replay,所以这里必须排序啦,这里一定要stable_sort,也就是排序必须是稳定的
    //对于不稳定的排序,有时两个事件的时间完全相等,可能会使原来在后面的跑前面去
    int f=open(fn,O_WRONLY | O_CREAT | O_TRUNC);
    if(f==-1)
    {
        std::cout<<fn<<" cannot be opened!";
        exit(0);
    }
    //打开一开始event record file中那个file文件进行写入
    for(unsigned int i=0;i<AllRecordedEvent.size();++i)
    {
        write(f,&AllRecordedEvent[i],sizeof(myinputevent));
    }
    close(f);
    std::cout<<"Record completed. Filename:"<<fn<<std::endl;
    exit(0);
}
void record(char* filename)
{
    std::cout<<"If using windows adb, you have to \"busybox stty intr ^X\" to change the INTR from Ctrl-C to Ctrl-X"<<std::endl;
    std::cout<<"And the stop recording command will be ctrl-X."<<std::endl
            <<"If using linux adb, you are fine."<<std::endl;
    //注意如果用windows中的adb,按下ctrl+c之后windows的shell也会收到命令从而终止adb shell,这样达不到终止录制的功能
    //所以如果非要在windows中使用adb,就需要用这个命令busybox stty intr ^X,把板子的终止组合键注册到ctrl-X上去
    //这样按ctrl-X再回车,就可以终止录制
    strcpy(fn,filename);
    int f=open(fn,O_WRONLY | O_CREAT);
    //先尝试打开一下参数中的文件,若失败就不继续了
    if(f==-1)
    {
        std::cout<<fn<<" cannot be opened!";
        exit(0);
    }
    close(f);
    struct sigaction act;
    act.sa_handler=afterstop;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGINT,&act, NULL);
    //这里注册了ctrl+c事件,这样录制过程中按下ctrl+c然后回车,可以执行afterstop函数。

    char dev[40]="/dev/input/eventX";

    int DeviceFile[10];
    for(char i='0';i<='9';++i)
    {
        dev[16]=i;
        int f=open(dev,O_RDONLY | O_NONBLOCK);
        //把所有的event都打开,这样保证能包括键盘和屏幕
        //这里O_RDONLY是只读,O_NONBLOCK是非阻塞。非阻塞的意思就是读取的时候,如果设备里没有,就返回-1。
        //如果是阻塞的话,设备里没有,read函数是不返回的,要等到有了才返回。
        //所以阻塞肯定不行,因为我有一堆设备要循环读取的,如果第一个设备没数据就会卡住,后面的设备都读不到。
        DeviceFile[i-'0']=f;
        //把句柄保存下来,如果打不开的话f值是-1
#ifdef DEBUG
        if(f!=-1)
            std::cout<<"Device opened:"<<i<<std::endl;
#endif

    }
    char buffer[2048];


    std::cout<<"Recording started, press Ctrl+C then press ENTER to stop recording"<<std::endl;

    while(1)
    {
        for(int i=0;i<10;++i)
        {
            if(DeviceFile[i]==-1)
                continue;
            int ReadSize=read(DeviceFile[i],buffer,2048);
            //不断的循环,把打开了的文件反复读取,这里缓存区必须是16的倍数,因为生成的数据是16字节的数据结构,这个基本结构不能破坏了
            //16字节的数据结构中,前4字节是秒,然后4字节微秒,最后8字节是实际的设备发送的信息
            //这里read会返回读取到的字节数量,如果啥也没读到会返回-1
            if(ReadSize!=-1)
            {
#ifdef DEBUG
                std::cout<<"device"<<i<<"read"<<ReadSize<<std::endl;
#endif
                for(input_event *p=(input_event*)buffer;(char*)p<buffer+ReadSize;++p)
                {
                    myinputevent my;

                    my.code=p->code;
                    my.type=p->type;
                    my.value=p->value;
                    my.time=p->time;
                    my.deviceID=i;
                    AllRecordedEvent.push_back(my);
                    //这里可能难理解,buffer里存储了大量16字节的event,所以让指针p是数据结构指针,然后16字节16字节的挪动
                    //我有一个自己的数据结构叫myinputevent,比这个16字节来说,尾部添加了一个4字节的int,存储设备的event号
                    //你可以在stdafx.h中看到这些数据结构
                    //把那16字节的原封不动的放到我的数据结构中,再把我自己的设备号赋值,就完成了这一个事件的录制。
                    //然后就存储到AllRecordedEvent里面去。
                }
            }
        }
    }

}

 

posted @ 2013-08-16 03:27  张拯  阅读(1458)  评论(2编辑  收藏  举报