输入设备应用编程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

输入设备应用编程

在这里插入图片描述

输入类设备编程介绍

什么是输入设备

  • 输入设备(input 设备),如鼠标、键盘、触摸屏等,允许用户与系统交互

input 子系统

  • Linux系统通过input子系统管理多种输入设备

  • Input子系统提供统一的框架和接口,屏蔽硬件差异

  • 注册成功的输入设备在/dev/input目录下生成eventX设备节点

  • 通过读取这些设备节点获取输入设备的数据

读取数据的流程

  • 应用程序打开对应的设备文件(如/dev/input/event0)

  • 发起读操作,若无数据则进入休眠状态(阻塞I/O)

  • 有数据时,应用程序被唤醒并读取数据

  • 应用程序对获取的数据进行解析

  • 无数据可读时,应用程序休眠,直到有触摸或其他输入产生数据,才被唤醒继续操作

应用程序如何解析数据

  • 数据结构: 应用程序通过读取操作获取的数据是struct input_event结构体类型,该结构包含时间(time), 类型(type), 具体事件(code), 和值(value)

    • struct input_event {
      struct timeval time;
      __u16 type;
      __u16 code;
      __s32 value;
      };

    • type

        - 数据同步
      
        	- 同步事件的目的
      
        		- 同步事件(EV_SYN)用于告知应用程序,一个输入动作的所有数据已经完整上报
      
        	- 读取输入数据的挑战
      
        		- 由于一次read操作只能读取一个struct input_event,对于包含多个数据点的输入动作(如触摸屏操作),需要多次read才能获得完整信息
      
        	- 同步类事件中包含了多种不同的事件
      
        		-  
      
        	- 完成数据读取的标记
      
        		- 应用程序通过接收到的同步事件(通常是SYN_REPORT,value值为0)来识别一组数据是否完全接收,确保了数据的完整性和准确性
      
        	- 同步事件的普遍性
      
        		- 所有输入设备在上报完一组数据后,都需要上报一个同步事件,以确保数据的完整上报和正确处理
      
      • 在<linux/input.h>头文件中
    • code

      • 按键类事件

      • 相对位移事件

      • 绝对位移事件,触摸屏设备是一种绝对位移设备

      • <linux/input.h>头文件(定义在
        input-event-codes.h 头文件中,该头文件被<linux/input.h>所包含了)

读取 struct input_event 数据

程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
 
int main(int argc, char *argv[])
{
    struct input_event in_ev = {0};
    int fd = -1;

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
        exit(-1);
    }

    /* 打开文件 */
    if (0 > (fd = open(argv[1], O_RDONLY))) {
        perror("open error");
        exit(-1);
    }

    for ( ; ; ) {

        /* 循环读取数据 */
        //计算 struct input_event 结构体的大小(以字节为单位)
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            exit(-1);
        }

        printf("type:%d code:%d value:%d\n",
                in_ev.type, in_ev.code, in_ev.value);
    }
}
  • 1 开始:程序的入口点

  • 2 检查命令行参数数量,如果不等于2,则打印用法信息并退出

  • 3 尝试打开指定的输入设备文件,如果失败,则打印错误信息并退出

  • 4 进入无限循环,尝试读取输入事件数据,如果读取失败,则打印错误信息并退出

    • 如果读取成功,则打印事件的详细信息
  • 5 循环回到步骤4,继续读取下一个事件

注意

  • 阻塞式 I/O 使用:程序采用阻塞式 I/O 方式读取设备文件,意味着在没有数据可读时,read 调用会被暂停(阻塞)

  • 唤醒机制:只有当设备文件有数据可读时,read 调用才会被唤醒并继续执行

  • 设备文件特性:与普通文件不同,读写设备文件时不需要设置读写位置的偏移量

在开发板上验证

ALPHA 和 Mini 开发板上都有一个用户按键 KEY0,作为典型的输入设备

该按键为 GPIO 按键,驱动基于 input 子系统,在 /dev/input 目录下有设备节点

  • 可通过 /proc/bus/input/devices 文件查看系统中所有输入设备的信息

    • 查看/proc/bus/input/devices 文件

程序运行后,终端会打印按键按下和松开的信息

  • 短按

    • 短按按键 KEY0

    • 第一行打印信息:按键 KEY_VOLUMEDOWN 被按下

      • type=1,表示上报的是按键事件 EV_KEY,

      • code=114,打开 input-event-codes.h 头文件进行查找,可以发现 code=114 对应的是键盘上的 KEY_VOLUMEDOWN 按键,这个是 ALPHA/Mini 开发板出厂系统已经配置好的,

      • value=1表示按键按下

    • 第二行打印信息:上报 EV_SYN 同步事件(type=0)中的 SYN_REPORT 事件(code=0),表示本轮数据已经完整、报告同步

    • 第三行打印信息:按键 KEY_VOLUMEDOWN 被松开(type=1, code=114, value=0)

    • 第四行打印信息:再次上报同步事件

  • 长按

    • 长按按键 KEY0

    • 长按按键 KEY0 时,终端会打印 value=2,表示长按状态

按键应用编程

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char *argv[])
{
    struct input_event in_ev = {0};
    int fd = -1;
    int value = -1;

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
        exit(-1);
    }

    /* 打开文件 */
    if (0 > (fd = open(argv[1], O_RDONLY))) {
        perror("open error");
        exit(-1);
    }

    for ( ; ; ) {

        /* 循环读取数据 */
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            exit(-1);
        }

        if (EV_KEY == in_ev.type) { //检查读取的事件是否为按键事件(EV_KEY)
            switch (in_ev.value) {  //根据按键事件的值(in_ev.value),打印按键的状态
            case 0:
                printf("code<%d>: 松开\n", in_ev.code);
                break;
            case 1:
                printf("code<%d>: 按下\n", in_ev.code);
                break;
            case 2:
                printf("code<%d>: 长按\n", in_ev.code);
                break;
            }
        }
    }
}

程序

  • 开始:程序的入口点

  • 校验传参:检查命令行参数是否正确

  • 打开文件:以只读模式打开输入设备文件

  • 无限循环:进入无限循环,持续监听输入设备

  • 读取数据:调用 read() 函数读取输入设备上报的数据

  • 判断事件类型:检查读取到的事件类型

  • 是按键事件?:判断事件类型是否为按键事件(EV_KEY)

  • 判断按键状态:如果是按键事件,根据 value 值判断按键状态(松开、按下、长按)

  • 打印按键状态:根据按键状态打印相应信息

  • 继续循环:继续下一次循环

在开发板测试

  • 测试 KEY0

    • 按下 KEY0 或松开 KEY0 以及长按情况下,终端会打印出相应的信息
  • 测试键盘上的按键

    • 插入 USB 键盘终端打印信息

    • 使用命令"cat /proc/bus/input/devices",在打印信息中找到键盘设备的信息

    • 可以根据 code 值查询到对应的按键(通过 input-event-codes.h 头文件),如 code=30 对应的是键盘上的字母 A 键

触摸屏应用编程

解析触摸屏设备上报的数据

  • 触摸屏设备是一个绝对位移设备,可以上报绝对位移事件,绝对位移事件如下:

  • 单点触摸和多点触摸

    • 触摸屏分为多点触摸设备和单点触摸设备

    • 单点触摸设备只支持一个触摸点,每轮数据包含一个触摸点信息

      • 上报事件为 ABS_XXX,包含 ABS_X(X 轴坐标),ABS_Y(Y 轴坐标),可能还包括 ABS_Z(Z 轴坐标)、ABS_PRESSURE(按压力大小)
    • 多点触摸设备支持多个触摸点,每轮数据可能包含多个触摸点信息

      • 上报事件为 ABS_MT_XXX,包含 ABS_MT_POSITION_X(X 轴坐标)、ABS_MT_POSITION_Y(Y 轴坐标)
    • 触摸屏设备还可以上报按键类事件和同步类事件

      • 同步事件告知应用层数据是否完整

      • 按键类事件如 BTN_TOUCH(code=0x14a,330)用于描述按下和松开触摸屏,不支持长按状态(value 不会等于 2)

      • 多点触摸设备只有第一个点按下时上报 BTN_TOUCH 事件(value=1),最后一个点离开时上报(value=0)

  • 单点触摸设备–事件上报的顺序

    • 单点触摸设备事件上报流程包括点击、滑动和松开三个阶段

      • 点击触摸屏时

BTN_TOUCH
ABS_X
ABS_Y
SYN_REPORT

滑动

ABS_X
ABS_Y
SYN_REPORT

松开

BTN_TOUCH
SYN_REPORT

- 点击触摸屏时,上报顺序为:BTN_TOUCH(value=1),ABS_X,ABS_Y,SYN_REPORT

- 滑动时,上报顺序为:ABS_X,ABS_Y,SYN_REPORT,不包括 BTN_TOUCH 事件

- 松开时,上报顺序为:BTN_TOUCH(value=0),SYN_REPORT

- 不同设备上报的信息量可能不同,但所有数据在 SYN_REPORT 同步事件之前上报给应用层
  • 多点触摸设备–事件上报的顺序

    • 多点触摸设备上报一轮完整数据中可能包含多个触摸点的信息

      • 例如,5点触摸设备会更新5个触摸点的信息,并将其上报给应用层
    • 在Linux内核中,多点触摸设备使用多点触摸(MT)协议上报触摸点数据

    • MT协议分为Type A和Type B两种类型

      • Type A协议使用较少,几乎被淘汰

      • 重点介绍Type B协议

  • MT 协议之 Type B 协议

    • Type B协议适用于能够追踪并区分触摸点的设备,如开发板配套的触摸屏

    • 通过ABS_MT_SLOT事件上报各个触摸点的更新信息,slot是触摸点的编号

      • 每个触摸点分配一个slot,用于传递该触摸点的变化
    • Type B协议还使用ABS_MT_TRACKING_ID事件,用于触摸点的创建、替换和销毁

      • ABS_MT_TRACKING_ID事件的value表示触摸点的ID,ID>=0表示有效触摸点,ID=-1表示触摸点被移除
    • Type B协议减少数据发送,只上报发生变更的数据

    • slot与ID

      • slot是硬件上的概念,ID是软件上的概念,用于区分触摸点

      • slot按时间顺序分配,ID用于区分触摸点的生命周期

    • Type B 协议下多点触摸设备上报数据的流程列举

      • ABS_MT_SLOT 0
        ABS_MT_TRACKING_ID 10
        ABS_MT_POSITION_X
        ABS_MT_POSITION_Y
        ABS_MT_SLOT 1
        ABS_MT_TRACKING_ID 11
        ABS_MT_POSITION_X
        ABS_MT_POSITION_Y
        SYN_REPORT

      • ABS_MT_SLOT 0:选择slot 0,表示当前更新的是触摸点0的数据

      • ABS_MT_TRACKING_ID 10:为slot 0分配一个新的ID为10的触摸点,表示这是一个新创建的触摸点

      • ABS_MT_POSITION_X:上报触摸点0的X轴坐标

      • ABS_MT_POSITION_Y:上报触摸点0的Y轴坐标

      • ABS_MT_SLOT 1:选择slot 1,表示当前更新的是触摸点1的数据

      • 最后,通过SYN_REPORT事件通知应用层,当前这一轮触摸点数据的上报已经完成,所有信息已经同步到应用层

  • 触摸屏上报数据分析

    • 保证开发板上已经连接了 LCD 屏

    • 使用命令"cat /proc/bus/input/devices",确定触摸屏对应的设备节点

    • 一个手指点击触摸屏先不松开

      • 第一行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_TRACKING_ID(code=57)事件,并且 value 值等于 78,也就是 ID,表示创建一个新的触摸点,手指按下

      • 第二行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_POSITION_X(code=53)事件,其 value
        对应的便是触摸点的 X 坐标;第三行上报了 ABS_MT_POSITION_Y(code=54)事件,其 value 值对应的便是触摸点 Y 坐标,所以由此可知该触摸点的坐标为(372, 381)

      • 第四行上报了按键类事件 EV_KEY(type=1)中的 BTN_TOUCH(code=330),value 值等于 1,表示
        这是触摸屏上最先产生的触摸点(slot=0、也就是触摸点 0)

      • 第五行和第六行分别上报了绝对位移事件 EV_ABS(type=3)中的 ABS_X(code=0)和 ABS_Y(code=1),
        其 value 分别对应的是触摸点的 X 坐标和 Y 坐标。多点触摸设备也会通过 ABS_X、ABS_Y 事件上报触摸点的 X、Y 坐标,但通常只有触摸点 0 支持,所以可以把多点触摸设备当成单点触摸设备来使用

      • 最后一行:上报 SYN_REPORT 事件,表示此次触摸点的信息上报完毕

    • 在第一个触摸点的基础上,增加第二个触摸点

      • 1~7 行不再解释,第八行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_SLOT 事件(code=47),
        表示目前要更新 slot=1 所关联的触摸点(也就是触摸点 1)对应的信息

      • 第九行上报了绝对位移事件 EV_ABS(type=3)中的 ABS_MT_TRACKING_ID 事件(code=57),ID=79,
        这是之前没有出现过的 ID,表示这是一个新的触摸点

      • 第十、十一行分别上报了 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 事件

      • 最后一行上报同步事件(type=0、code=0),告知应用层数据完整

    • 当手指松开时,触摸点就会被销毁,上报 ABS_MT_TRACKING_ID 事件,并将 value 设置为-1(ID)

获取触摸屏的信息

  • ioctl()函数

    • 获取触摸屏信息:通过 ioctl() 函数获取触摸屏支持的最大触摸点数、X、Y 坐标范围等信息

      • ioctl()是一个文件 I/O 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或设备文件
    • ioctl() 函数原型:
      #include <sys/ioctl.h>
      int ioctl(int fd, unsigned long request, …);

      • fd:文件描述符

      • request:请求指令,与操作对象相关

        • 在 input.h 头文件有EVIOC 相关的宏定义

          • EVIOCG(get)开头的表示获取信息,EVIOCS(set)开头表示设置

          • EVIOCGABS 宏:用于获取触摸屏 slot 的取值范围

            • #define EVIOCGABS(abs) _IOR(‘E’, 0x40 + (abs), struct input_absinfo)

            • abs:表示 ABS_XXX 绝对位移事件

            • 第三个参数为 struct input_absinfo *,获取的信息写入该结构体

              • struct input_absinfo {
                __s32 value; //最新的报告值
                __s32 minimum; //最小值
                __s32 maximum; //最大值
                __s32 fuzz;
                __s32 flat;
                __s32 resolution;
                };
      • 可变参函数,第三个参数根据 request 决定

        • 譬如使用 EVIOCGNAME 宏获取设备名称

          • char name[100];
            ioctl(fd, EVIOCGNAME(sizeof(name)), name);
    • 获取触摸屏支持的最大触摸点数

      • struct input_absinfo info;
        int max_slots; //最大触摸点数
        if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &info))
        perror(“ioctl error”);
        max_slots = info.maximum + 1 - info.minimum;
  • 程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char *argv[])
{
    struct input_absinfo info;
    int fd = -1;
    int max_slots;

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* 打开文件 */
    if (0 > (fd = open(argv[1], O_RDONLY))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    /* 获取slot信息 */
    if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &info)) {
        perror("ioctl error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    max_slots = info.maximum + 1 - info.minimum;
    printf("max_slots: %d\n", max_slots);

    /* 关闭、退出 */
    close(fd);
    exit(EXIT_SUCCESS);
}
- 开始:程序的入口点

- 校验传参:检查命令行参数是否正确

- 打开文件:以只读模式打开输入设备文件

- 获取触摸屏支持的最大触摸点数

	- 获取slot信息:使用 ioctl 函数获取设备的 ABS_MT_SLOT 信息,并检查是否成功

	- 计算max_slots:计算最大 slot 数量

	- 打印max_slots:打印最大 slot 数量

- 关闭文件:关闭文件描述符

- 退出程序
  • 运行结果

    • 这个屏是一个 5 点触摸屏

单点触摸应用程序

  • 程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char *argv[])
{
    struct input_event in_ev;
    int x, y;   //触摸点x和y坐标
    int down;   //用于记录BTN_TOUCH事件的value,1表示按下,0表示松开,-1表示移动
    int valid;  //用于记录数据是否有效(我们关注的信息发生更新表示有效,1表示有效,0表示无效)
    int fd = -1;

    /* 校验传参 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <input-dev>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* 打开文件 */
    if (0 > (fd = open(argv[1], O_RDONLY))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    x = y = 0;  //初始化x和y坐标值
    down = -1; //初始化<移动>
    valid = 0;//初始化<无效>
    for ( ; ; ) {

        /* 循环读取数据 */
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            exit(EXIT_FAILURE);
        }

        switch (in_ev.type) { // 判断事件类型
        case EV_KEY:    //按键事件
            if (BTN_TOUCH == in_ev.code) {  // 判断按键代码是否为 BTN_TOUCH触摸屏上的触摸事件
                down = in_ev.value;         // 更新 down 值,记录按下 (1) 或松开 (0)
                valid = 1;                  // 标记数据有效
            }
            break;

        case EV_ABS:    //绝对位移事件
            switch (in_ev.code) {   // 判断事件代码
            case ABS_X: //X坐标
                x = in_ev.value;
                valid = 1;
                break;
            case ABS_Y: //Y坐标
                y = in_ev.value;
                valid = 1;
                break;
            }
            break;

        case EV_SYN:    //同步事件
            if (SYN_REPORT == in_ev.code) { // 判断同步事件代码是否为 SYN_REPORT
                                            //SYN_REPORT 是一个特定的同步事件代码,用于表示当前一组输入事件的结束。
                if (valid) {//判断是否有效
                    switch (down) {//判断状态
                    case 1:
                        printf("按下(%d, %d)\n", x, y);
                        break;
                    case 0:
                        printf("松开\n");
                        break;
                    case -1:
                        printf("移动(%d, %d)\n", x, y);
                        break;
                    }

                    valid = 0; //重置valid
                    down = -1; //重置down
                }
            }
            break;
        }
    }
}
- 开始:程序的入口点

- 校验传参:检查命令行参数是否正确

- 打开文件:以只读模式打开输入设备文件

- 初始化变量

	- 初始化 x、y 坐标值为 0

	- 初始化 down 为 -1,表示移动状态

	- 初始化 valid 为 0,表示无效数据

- 无限循环读取输入事件数据

	- 读取触摸屏数据

		- 从输入设备读取一个 input_event 结构体大小的数据

		- 判断读取成功与否

			- 如果读取失败,打印错误信息并退出

	- 解析数据

		- 根据事件类型 EV_KEY、EV_ABS 和 EV_SYN 进行不同处理

		- 按键事件(EV_KEY)

			- 判断按键代码是否为 BTN_TOUCH

			- 更新 down 和 valid

		- 绝对位移事件(EV_ABS)

			- 判断事件代码是否为 ABS_X 或 ABS_Y

			- 更新 x 或 y 坐标,并标记 valid

		- 同步事件(EV_SYN)

			- 判断同步事件代码是否为 SYN_REPORT

			- 如果数据有效,则根据 down 的值打印相应的状态信息,并重置 valid 和 down
  • 运行结果

    • 用一个手指按下触摸屏、松开以及滑动操作

多点触摸应用程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>

/* 用于描述MT多点触摸每一个触摸点的信息 */
struct ts_mt {
    int x;      	//X坐标
    int y;      	//Y坐标
    int id;     	//对应ABS_MT_TRACKING_ID
    int valid;		//数据有效标志位(=1表示触摸点信息发生更新)
};

/* 一个触摸点的x坐标和y坐标 */
struct tp_xy {
    int x;
    int y;
};

//读取触摸屏的数据
static int ts_read(const int fd, const int max_slots,
        struct ts_mt *mt)
{
    struct input_event in_ev;
    static int slot = 0;//用于保存上一个slot
    static struct tp_xy xy[12] = {0};//用于保存上一次的x和y坐标值,假设触摸屏支持的最大触摸点数不会超过12
    int i;

    /* 对缓冲区初始化操作 */
    memset(mt, 0x0, max_slots * sizeof(struct ts_mt));  //清零
    //memset标准库函数,用于将一块内存区域设置为指定的值
    //max_slots 是数组的大小,表示触摸点的最大数量
    
    for (i = 0; i < max_slots; i++)
        mt[i].id = -2;//将id初始化为-2, id=-1表示触摸点删除, id>=0表示创建

    for ( ; ; ) {

        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            return -1;
        }

        switch (in_ev.type) {
        case EV_ABS:    //处理绝对位置事件
            switch (in_ev.code) {
            case ABS_MT_SLOT:
                slot = in_ev.value;
                break;
            case ABS_MT_POSITION_X:
                xy[slot].x = in_ev.value;
                mt[slot].valid = 1;
                break;
            case ABS_MT_POSITION_Y:
                xy[slot].y = in_ev.value;
                mt[slot].valid = 1;
                break;
            case ABS_MT_TRACKING_ID:
                mt[slot].id = in_ev.value;
                mt[slot].valid = 1;
                break;
            }
            break;
        //case EV_KEY://按键事件对单点触摸应用比较有用
        //  break;
        case EV_SYN:    //处理同步事件
            if (SYN_REPORT == in_ev.code) {
                //将所有有效触摸点的 x 和 y 坐标更新到 mt 数组中
                for (i = 0; i < max_slots; i++) {
                    mt[i].x = xy[i].x;
                    mt[i].y = xy[i].y;
                }
            }
            return 0;
        }
    }
}

int main(int argc, char *argv[])
{
    struct input_absinfo slot;
    struct ts_mt *mt = NULL;
    int max_slots;
    int fd;
    int i;

    /* 参数校验 */
    if (2 != argc) {
        fprintf(stderr,"usage: %s <input_dev>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* 打开文件 */
    fd = open(argv[1], O_RDONLY);
    if (0 > fd) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    /* 获取触摸屏支持的最大触摸点数 */
    if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)) {
        perror("ioctl error");
        close(fd);
        exit(EXIT_FAILURE);
    }

    max_slots = slot.maximum + 1 - slot.minimum;
    printf("max_slots: %d\n", max_slots);

    /* 申请内存空间并清零 */
    mt = calloc(max_slots, sizeof(struct ts_mt));
    //calloc 是 C 标准库函数,用于动态分配内存。与 malloc 类似,但不同之处在于 calloc 会将分配的内存初始化为全零
    //计算出总共需要分配的内存字节数,并返回一个指向这块内存的指针

    /* 读数据 */
    for ( ; ; ) {

        if (0 > ts_read(fd, max_slots, mt))
            break;

        for (i = 0; i < max_slots; i++) {
            if (mt[i].valid) {//判断每一个触摸点信息是否发生更新(关注的信息发生更新)

                if (0 <= mt[i].id)
                    printf("slot<%d>, 按下(%d, %d)\n", i, mt[i].x, mt[i].y);
                else if (-1 == mt[i].id)
                    printf("slot<%d>, 松开\n", i);
                else
                    printf("slot<%d>, 移动(%d, %d)\n", i, mt[i].x, mt[i].y);
            }
        }
    }

    /* 关闭设备、退出 */
    close(fd);
    free(mt);
    exit(EXIT_FAILURE);
}
  • 代码

    • 开始:程序的入口点

    • 校验传参:检查命令行参数是否正确

    • 打开文件:以只读模式打开输入设备文件

    • 获取触摸屏支持的最大触摸点数

      • 获取slot信息:使用 ioctl 函数获取设备的 ABS_MT_SLOT 信息,并检查是否成功

      • 计算max_slots:计算最大 slot 数量

      • 打印max_slots:打印最大 slot 数量

    • 申请内存空间:为 mt 指针申请内存,并初始化为零

    • 无限循环读数据:进入无限循环,调用 ts_read 函数读取触摸屏数据

      • ts_read():读取触摸屏数据并存储在 mt 数组中

        • 初始化缓冲区:使用 memset 初始化 mt 数组为零

        • 初始化 mt 数组:将 mt 数组中的每个元素的 id 初始化为 -2

        • 无限循环读取事件:进入无限循环,读取输入事件

          • 读取事件:从文件描述符 fd 读取输入事件

          • 检查读取结果:检查读取是否成功,如果失败则返回 -1

          • 处理事件类型:根据事件类型进行处理

            • 处理 EV_ABS 事件:如果是绝对位置事件,进一步处理

              • 处理 ABS_MT_SLOT:更新当前的 slot

              • 处理 ABS_MT_POSITION_X:更新 xy 数组中的 x 坐标,并设置 mt 数组的 valid 标志

              • 处理 ABS_MT_POSITION_Y:更新 xy 数组中的 y 坐标,并设置 mt 数组的 valid 标志

              • 处理 ABS_MT_TRACKING_ID:更新 mt 数组中的 id,并设置 valid 标志

            • 处理 EV_SYN 事件:如果是同步事件,进一步处理

              • 更新 mt 数组:将 xy 数组中的坐标更新到 mt 数组中
            • 返回 0:返回 0 表示成功

      • 处理触摸点数据:在内部循环中处理每个触摸点的数据

      • 打印触摸点信息:根据触摸点的状态(按下、松开、移动)打印相应的信息

    • 关闭文件描述符,释放内存

    • 退出程序

  • 运行结果

    • 每一个不同的 slot 表示不同的触摸点
  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木不迷茫(˵¯͒¯͒˵)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值