frameworks 之getEvent指令


通过 getEvent 可以识别按键基本命令和里面的关键信息
涉及到的类如下

  • system/core/toolbox/toolbox.c
  • system/core/toolbox/tools.h
  • system/core/toolbox/getevent.c

指令解析

通过 getEvent -h 可打印出相关的指令帮助,对应的指令注释如下。输出的数字都为16进制

houji:/ $ getevent -help
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]
    -t: show time stamps  // 打印时间戳
    -n: don't print newlines // 不换行
    -s: print switch states for given bits // 打印指定位的开关状态
    -S: print all switch states // 打印所有开关状态
    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64) // 根据 mask 值显示相关信息,执行后会一直显示上报数据,详细掩码(错误=1,dev=2,名称=4,信息=8,错误=16,位置事件=32,道具=64)默认显示 dev| name| info| vers = 30
    -d: show HID descriptor, if available // 如果设备可用,显示设备隐藏的描述信息
    -p: show possible events (errs, dev, name, pos. events) // 显示设备支持的事件类型和编码方式
    -i: show all device info and possible events // 显示设备的所有信息和支持的事件,比 -p 显示更多信息
    -l: label event types and names in plain text // 以文本形式输出事件类型和名称,比 -t 更清楚直观
    -q: quiet (clear verbosity mask) // 以文本形式输出事件类型和名称,比 -t 更清楚直观
    -c: print given number of events then exit // 打印固定数量的事件并退出
    -r: print rate events are received // 显示事件上报速率

其中 -v 打印输入设备的详细信息,默认为 默认显示 dev| name| info| vers = 30

emulator_x86_64:/ # getevent -v
add device 1: /dev/input/event0
  bus:      0019
  vendor    0000
  product   0001
  version   0000
  name:     "Power Button"
  location: "LNXPWRBN/button/input0"
  id:       ""
  version:  1.0.1

-l 打印的信息 分别对应 type code value

emulator_x86_64:/ # getevent -l                                               
/dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000000            
/dev/input/event3: EV_ABS       ABS_MT_POSITION_X    000031af            
/dev/input/event3: EV_ABS       ABS_MT_POSITION_Y    00004447 

type 值分为 3种类型

  1. EV_SYN 表示同步事件 有开始(不必要)和结束,对应的 code为 分为以下几点
    对应的code:。
    0004:代表一个事件开始(不必要)
    0005:代表一个事件开始(不必要)
    SYN_REPORT:代表一个事件的结束 (必要)
    看到 SYN_REPORT 代表一个事件系列的结束
  2. EV_ABS 表示绝对坐标类型事件
    对应的coe 如下
    ABS_MT_SLOT:代表不同手指,它的value代表手指id,当有多指触碰的时候会出现
    ABS_MT_TRACKING_ID: 代表对应的ID,每次都会在上一次基础+1,一个非负数的表示一次接触。当为-1时候,表示这是一个无用的slot(可表示为这一次结束的结束,抬起, value 为 ffffffff)。
    ABS_MT_POSITION_X,ABS_MT_POSITION_Y: 相对于屏幕中心的x,y坐标
    ABS_MT_TOUCH_MAJORL: 接触部分的长轴长度。相当于椭圆的长轴,按压的接触长度
    ABS_MT_TOUCH_MINOR:接触部分的短轴长度。相当于椭圆的短轴
    ABS_MT_PRESSURE:代表按下压力,有的设备不一定有
  3. EV_KEY 表示按键类型事件(包含手指触摸)
    对应的coe 如下
    BTN_TOUCH: 触碰按键。value 是DOWN或者UP
    TN_TOOL_FINGER:按键的是finger,并且其值也是DOWN或者UP
    KEY_POWER…: 各种硬按键名称

示例以及解释如下

[ 1959159.438822] /dev/input/event7: EV_ABS       ABS_MT_SLOT          00000000            //第1个手指
[ 1959159.438822] /dev/input/event7: EV_ABS       ABS_MT_TRACKING_ID   0005cfc3            //第1个手指对应的TRACKING_ID 
[ 1959159.438822] /dev/input/event7: EV_KEY       BTN_TOUCH            DOWN                //触摸按下事件
[ 1959159.438822] /dev/input/event7: EV_KEY       BTN_TOOL_FINGER      DOWN                //触摸按下
[ 1959159.438822] /dev/input/event7: EV_ABS       ABS_MT_POSITION_X    00007346            //对应x坐标
[ 1959159.438822] /dev/input/event7: EV_ABS       ABS_MT_POSITION_Y    00034396            //对应y坐标
[ 1959159.438822] /dev/input/event7: EV_SYN       SYN_REPORT           00000000            //事件结束
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_POSITION_X    00007382            //第1个手指的坐标
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_POSITION_Y    000343c8            //第1个手指的坐标
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_SLOT          00000001            //第2个手指
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_TRACKING_ID   0005cfc4            //第2个手指对应的TRACKING_ID 
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_POSITION_X    00014ae6            //第2个手指的坐标
[ 1959160.905443] /dev/input/event7: EV_ABS       ABS_MT_POSITION_Y    00030962            //事件结束
[ 1959160.905443] /dev/input/event7: EV_SYN       SYN_REPORT           00000000             rate 123
[ 1959160.913862] /dev/input/event7: EV_ABS       ABS_MT_SLOT          00000000            //切换到第1个手指的坐标
[ 1959160.913862] /dev/input/event7: EV_ABS       ABS_MT_POSITION_X    00007396            
[ 1959160.913862] /dev/input/event7: EV_ABS       ABS_MT_POSITION_Y    000343d2            
[ 1959160.913862] /dev/input/event7: EV_SYN       SYN_REPORT           00000000             rate 118
[ 1959160.922049] /dev/input/event7: EV_ABS       ABS_MT_POSITION_X    000073aa            //第1个手指的坐标束
[ 1959160.922049] /dev/input/event7: EV_ABS       ABS_MT_POSITION_Y    000343e6            
[ 1959160.922049] /dev/input/event7: EV_SYN       SYN_REPORT           00000000             rate 122
[ 1959162.928588] /dev/input/event7: EV_ABS       ABS_MT_TRACKING_ID   ffffffff            //为负数,表示第1个手指结束抬起
[ 1959162.928588] /dev/input/event7: EV_SYN       SYN_REPORT           00000000             rate 0 //事件结束
[ 1959164.133957] /dev/input/event7: EV_ABS       ABS_MT_SLOT          00000001           //第二个手指 
[ 1959164.133957] /dev/input/event7: EV_ABS       ABS_MT_TRACKING_ID   ffffffff           // 为负数表示第二个手指抬起 
[ 1959164.133957] /dev/input/event7: EV_KEY       BTN_TOUCH            UP                  
[ 1959164.133957] /dev/input/event7: EV_KEY       BTN_TOOL_FINGER      UP                  
[ 1959164.133957] /dev/input/event7: EV_SYN       SYN_REPORT           00000000             rate 0 //事件结




参数可以组合使用,一次性查看需要的触摸屏信息,常用的命令组合为 getevent -ltr

emulator_x86_64:/ # getevent -ltr                                         
[    1384.965200] /dev/input/event3: EV_ABS       ABS_MT_TRACKING_ID   00000000  

源码追溯

可以执行, 则表示 system/bin 有对应的脚本。所以可以通过执行对应的打印 查看文件。通过命令 ls -l | grep event 过滤对应的关键字可以看到 该事件通过 软链接(前面开头为l开头即为软链接)。链接到 toolbox

emulator_x86_64:/system/bin # ls -l | grep event 
lrwxr-xr-x 1 root shell       7 2024-08-17 22:51 getevent -> toolbox
-rwxr-xr-x 1 root shell    3925 2024-03-18 00:26 mm_events
lrwxr-xr-x 1 root shell       6 2024-08-17 22:51 sendevent -> toybox
lrwxr-xr-x 1 root shell       4 2024-08-17 22:51 ueventd -> init

回到源码文件,一般工具类 在 system 目录下,可以先通过该目录查找。执行 find -name toolbox。可以看到在该**./core/toolbox** 文件夹下。
在这里插入图片描述
跳转到该文件夹下,查看对应的文件打印,查看对应的Bp文件。包含以下文件,可以看出入口为
toolbox.c 文件夹。

cc_defaults {
    ...
    srcs: [
        "toolbox.c",
        "getevent.c",
        "getprop.cpp",
        "modprobe.cpp",
        "setprop.cpp",
        "start.cpp",
    ],
    ...
}

对应的 main 入口如下,该方法通过读取参数,遍历数组 ,然后判断名称和命令是否相等,相等就执行对应的 func 方法。对应 tools[] 数组 在文件前面定义,定义了名词和执行的方法。
会根据 name 拼接对应的执行方法。如 getEvent 拼接完为 getevent_main。相关数组定义在 tools.h 头文件里面。

// system/core/toolbox/tools.c
#define TOOL(name) int name##_main(int, char**); // 拼接对应的执行方法名称
#include "tools.h"
#undef TOOL
// 定义对应的数组,数组内容来自 tools.h定义
static struct {
    const char* name;
    int (*func)(int, char**);
} tools[] = {
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL
    { 0, 0 },
};

int main(int argc, char** argv) {
    // Let's assume that none of this code handles broken pipes. At least ls,
    // ps, and top were broken (though I'd previously added this fix locally
    // to top). We exit rather than use SIG_IGN because tools like top will
    // just keep on writing to nowhere forever if we don't stop them.
    signal(SIGPIPE, SIGPIPE_handler);
	// 获取第一个参数,遍历数组,判断是否和名称相等,相等执行对应的方法。
    char* cmd = strrchr(argv[0], '/');
    char* name = cmd ? (cmd + 1) : argv[0];

    for (size_t i = 0; tools[i].name; i++) {
        if (!strcmp(tools[i].name, name)) {
            return tools[i].func(argc, argv);
        }
    }

    printf("%s: no such tool\n", argv[0]);
    return 127;
}

头文件定义

// system/core/toolbox/tools.h
TOOL(getevent)
TOOL(getprop)
TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)
TOOL(toolbox)

所以运行脚本 执行的方法 在 getevent.c 下的 getevent_main方法下。

源码解析

1.解析参数

查看对应的方法 main 方法,可以看到 第一步 通过 getopt 解析对应的参数
部分参数通过 或 记录到 print_flags 中。

opterr = 0;
    do {
        c = getopt(argc, argv, "tns:Sv::dpilqc:rh"); // 解析对应的参数
        if (c == EOF)
            break;
        switch (c) {
        case 't':
            get_time = 1;
            break;
        case 'n':
            newline = "";
            break;
        case 's':
            get_switch = strtoul(optarg, NULL, 0);
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'S':
            get_switch = ~0;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'v':
            if(optarg)
                print_flags |= strtoul(optarg, NULL, 0);
            else
                print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
            print_flags_set = 1;
            break;
        case 'd':
            print_flags |= PRINT_HID_DESCRIPTOR;
            break;
        case 'p':
            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE
                    | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;
            print_flags_set = 1;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'i':
            print_flags |= PRINT_ALL_INFO;
            print_flags_set = 1;
            if(dont_block == -1)
                dont_block = 1;
            break;
        case 'l':
            print_flags |= PRINT_LABELS;
            break;
        case 'q':
            print_flags_set = 1;
            break;
        case 'c':
            event_count = atoi(optarg); //解析对应参数值
            dont_block = 0;
            break;
        case 'r':
            sync_rate = 1;
            break;
        case '?':
            fprintf(stderr, "%s: invalid option -%c\n",
                argv[0], optopt);
        case 'h':
            usage(argv[0]); // 打印帮助
            exit(1);
        }
    } while (1);
    if(dont_block == -1)
        dont_block = 0;

    if (optind + 1 == argc) {
        device = argv[optind];
        optind++;
    }
    if (optind != argc) {
        usage(argv[0]);
        exit(1);
    }

2.初始化ufds数组

第二步,会通过 device 判断是否 观察单个或者全部 。如如果我们 执行的命令后面加了对应的设备,则只会观察对应的设备 如 getevent -ltr /dev/input/event1。其中 inotify_init 获取了 dev/input 文件夹对应的监听。 然后通过 inotify_add_watch。进行删除和添加的监听。

	nfds = 1;
    ufds = calloc(1, sizeof(ufds[0]));
    ufds[0].fd = inotify_init();
    ufds[0].events = POLLIN;
	// 打印单个设备对应信息
    if(device) {
        if(!print_flags_set)
            print_flags |= PRINT_DEVICE_ERRORS;
        // 通过 open 方法打开对应的驱动,并通过 ioctl 获取对应的信息,并放到对应的 ufds 数组
        res = open_device(device, print_flags);
        if(res < 0) {
            return 1;
        }
    } else { // 打印全部
        if(!print_flags_set)
            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
        print_device = 1;
        // 添加删除和添加对应文件夹的监听
		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
        if(res < 0) {
            fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
            return 1;
        }
        // 遍历该文件夹 dev/input 文件夹下所有的
        res = scan_dir(device_path, print_flags);
        if(res < 0) {
            fprintf(stderr, "scan dir failed for %s\n", device_path);
            return 1;
        }
    }

没指定对应的设备的会 将所有添加到数组中,通过 scan_dir 方法。遍历文件夹下。先通过 opendir,打开文件夹,然后 通过 readdir进行遍历。遍历到符合的 在通过 open_device 将fd 添加到 数组中。

static int scan_dir(const char *dirname, int print_flags)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    // // 打开对应的文件夹
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        // 将fd 添加到 数组
        open_device(devname, print_flags);
    }
    closedir(dir);
    return 0;
}

最终都是通过 open_device 添加到数组,查看对应的方法,可以看到该方法通过 open 打开对应的节点,在通过 ioctl 获取对应的信息,并根据 设置的 print_flags 按需打印对应信息。最终在存放到 ufds 数组中

static int open_device(const char *device, int print_flags)
{
    int version;
    int fd;
    int clkid = CLOCK_MONOTONIC;
    struct pollfd *new_ufds;
    char **new_device_names;
    char name[80];
    char location[80];
    char idstr[80];
    struct input_id id;
    // 打开这个节点
    fd = open(device, O_RDONLY | O_CLOEXEC);
    if(fd < 0) {
        if(print_flags & PRINT_DEVICE_ERRORS)
            fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
        return -1;
    }
    // 获取相关的信息
    if(ioctl(fd, EVIOCGVERSION, &version)) {
        if(print_flags & PRINT_DEVICE_ERRORS)
            fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
        return -1;
    }
    if(ioctl(fd, EVIOCGID, &id)) {
        if(print_flags & PRINT_DEVICE_ERRORS)
            fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
        return -1;
    }
    name[sizeof(name) - 1] = '\0';
    location[sizeof(location) - 1] = '\0';
    idstr[sizeof(idstr) - 1] = '\0';
    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
        //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
        name[0] = '\0';
    }
    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
        //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
        location[0] = '\0';
    }
    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
        //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
        idstr[0] = '\0';
    }

    if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) {
        fprintf(stderr, "Can't enable monotonic clock reporting: %s\n", strerror(errno));
        // a non-fatal error
    }

    new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
    if(new_ufds == NULL) {
        fprintf(stderr, "out of memory\n");
        return -1;
    }
    ufds = new_ufds;
    new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
    if(new_device_names == NULL) {
        fprintf(stderr, "out of memory\n");
        return -1;
    }
    device_names = new_device_names;

    if(print_flags & PRINT_DEVICE)
        printf("add device %d: %s\n", nfds, device);
    if(print_flags & PRINT_DEVICE_INFO)
        printf("  bus:      %04x\n"
               "  vendor    %04x\n"
               "  product   %04x\n"
               "  version   %04x\n",
               id.bustype, id.vendor, id.product, id.version);
    if(print_flags & PRINT_DEVICE_NAME)
        printf("  name:     \"%s\"\n", name);
    if(print_flags & PRINT_DEVICE_INFO)
        printf("  location: \"%s\"\n"
               "  id:       \"%s\"\n", location, idstr);
    if(print_flags & PRINT_VERSION)
        printf("  version:  %d.%d.%d\n",
               version >> 16, (version >> 8) & 0xff, version & 0xff);

    if(print_flags & PRINT_POSSIBLE_EVENTS) {
        print_possible_events(fd, print_flags);
    }

    if(print_flags & PRINT_INPUT_PROPS) {
        print_input_props(fd);
    }
    if(print_flags & PRINT_HID_DESCRIPTOR) {
        print_hid_descriptor(id.bustype, id.vendor, id.product);
    }
    // 将 fd 存放到ufds 数组中 
    ufds[nfds].fd = fd;
    ufds[nfds].events = POLLIN;
    // 放到 device_names 数组中
    device_names[nfds] = strdup(device);
    nfds++;

    return 0;
}

3.添加到poll 并做对应处理

添加完数组后,通过 poll 等待消息的监听。poll 和 epoll 是一样的。但是 epoll 更加高效。因为 epoll 是监听到消息 塞到 events 数组并返回 数量。后续遍历变化的即可。但是 poll 是无论如何都要遍历全部数组 找到对应的变化,没 epoll 高效。
当有新消息来后,则做对应的处理。数组第一个为文件夹 ,所以对文件夹做单独的处理。
通过 read_notify 方法。添加对应添加和删除,添加则调用 open_device,删除则调用 close_device 移除对应的监听
因为第0个已处理,所以下面的遍历从下标1开始。遍历到变化时候, 通过 read 读取对应信息到 变量 event 中(input_event),最后根据设置的 falg 打印相对于的信息格式。

while(1) {
        //int pollres =
        // 通过 poll 等待消息的监听
        poll(ufds, nfds, -1);
        //printf("poll %d, returned %d\n", nfds, pollres);
        // 第1个是文件夹的变化,对文件夹内容的监听做单独的处理
        if(ufds[0].revents & POLLIN) {
            read_notify(device_path, ufds[0].fd, print_flags);
        }
        // 因为第0个已处理,所以下面的遍历从下标1开始
        for(i = 1; i < nfds; i++) {
            if(ufds[i].revents) {
                // 判断到有变化
                if(ufds[i].revents & POLLIN) {
                    // 读取对应的内容到变量 event中,为 input_event 结构体
                    res = read(ufds[i].fd, &event, sizeof(event));
                    if(res < (int)sizeof(event)) {
                        fprintf(stderr, "could not get event\n");
                        return 1;
                    }
                    if(get_time) {
                        printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
                    }
                    if(print_device)
                        printf("%s: ", device_names[i]);
                    // 传进去 值,根据 对应的 print_flags 做打印
                    print_event(event.type, event.code, event.value, print_flags);
                    if(sync_rate && event.type == 0 && event.code == 0) {
                        int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
                        if(last_sync_time)
                            printf(" rate %lld", 1000000LL / (now - last_sync_time));
                        last_sync_time = now;
                    }
                    printf("%s", newline);
                    if(event_count && --event_count == 0)
                        return 0;
                }
            }
        }
    }
  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值