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

news2024/12/23 18:49:35

输入设备应用编程

在这里插入图片描述

输入类设备编程介绍

什么是输入设备

  • 输入设备(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 表示不同的触摸点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1940715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络编程之LINUX信号

注意发送信号是给进程&#xff0c;不是线程&#xff0c;调用的是KILL函数&#xff0c;SIG是信号种类。pid0是本进程的其他的进程。 可以通过设置ERRNO来查看返回的错误&#xff0c;如下&#xff1a; 当目标进程收到信号后&#xff0c;要对信号进行一些执行操作&#xff1a; 定义…

[每周一更]-(第106期):DNS和SSL协作模式

文章目录 什么是DNS&#xff1f;DNS解析过程DNS解析的底层逻辑 什么是SSL&#xff1f;SSL证书SSL握手过程SSL的底层逻辑 DNS与SSL的协同工作过程 什么是DNS&#xff1f; DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;是互联网的重要组成部分&#xff0c…

黑马程序员MySQL基础学习,精细点复习【持续更新】

文章目录 数据库Mysql基础一、数据库1.数据库2.数据库管理系统3.SQL4.Mysql目录结构5.关系型数据库6.SQL基础概念 mysql高级一、数据库备份和还原1.图形化界面备份与还原 二、约束1.分类&#xff1a;2.主键约束3.唯一约束4.非空约束5.默认值约束6.外键约束 三、表关系1.概述2.一…

《Windows API每日一练》13.1 打印基础

在Windows中使用打印机时&#xff0c;在调用一系列与打印相关的GDI绘图函数的背后&#xff0c;实际上启动了一系列模块之间复杂的交互过程&#xff0c;包括 GDI32库模块、打印机设备驱动程序库模块&#xff08;带.DRV后缀的文件&#xff09;、Windows后台打印处理程序&#xff…

5. harbor镜像仓库

harbor镜像仓库 一、镜像仓库1、类型2、构建私有仓库的方案 二、部署harbor仓库(单机版)1、安装docker(略)2、安装docker-compose工具3、安装harbor4、生成harbor需要的证书、密钥(V3版本证书)4.1 创建CA4.2 创建harbor仓库需要的证书 5、编辑harbor配置文件6、启动harbor 三、…

【C++】17.AVL树

一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种…

Linux实用操作二

文章目录 Linux实用操作二日期、时区&#xff1a;date命令查看日期时间作用&#xff1a;语法&#xff1a;字段解释&#xff1a;操作&#xff1a; 修改Linux系统时区作用&#xff1a;操作&#xff1a; 使用ntp进行时间同步和校准作用&#xff1a;操作&#xff1a; IP地址、主机名…

buuctf web 第五到八题

[ACTF2020 新生赛]Exec 这里属实有点没想到了&#xff0c;以为要弹shell&#xff0c;结果不用 127.0.0.1;ls /PING 127.0.0.1 (127.0.0.1): 56 data bytes bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var127.0.0.1;tac /f*[GXYCTF2019]Pin…

【数学建模】多波束测线问题(持续更新)

多波束测线问题 问题 1建立模型覆盖宽度海水深度重叠长度重叠率 问题二问题三问题四 问题 1 与测线方向垂直的平面和海底坡面的交线构成一条与水平面夹角为 α \alpha α的斜线&#xff08;如下图&#xff09;&#xff0c;称 α \alpha α为坡度。请建立多波束测深的覆盖宽度及…

世界华人国学泰斗级人物颜廷利:人类史上全球公认最伟大的思想家

世界华人国学泰斗级人物颜廷利:人类史上全球公认最伟大的思想家 颜廷利教授,一位在21世纪东方哲学、科学界具有深远影响力的人物,同时也是当代中国教育界的知名教授、周易起名与易经姓名学的专家,以及现代国学的杰出代表。他在其著作《升命学说》中提出了一系列独到的理论,包括…

Mybatis--分页查询

一、分页查询 分页查询则是在页面上将本来很多的数据分段显示&#xff0c;每页显示用户自定义的行数。可提高用户体验度&#xff0c;同时减少一次性加载&#xff0c;内存溢出风险。 1、真假分页 分页分为&#xff1a;真分页和假分页。 假分页&#xff1a;一次性查询所有数据存入…

笔记小结:卷积神经网络之多输入多输出通道

本文为李沐老师《动手学深度学习》笔记小结&#xff0c;用于个人复习并记录学习历程&#xff0c;适用于初学者 彩色图像具有标准的RGB通道来代表红、绿和蓝&#xff0c;需要三个通道表示&#xff0c;故而只有单输入单输出是不够的。 对于单个输入和单个输出通道的简化例子&…

Yolo-World网络模型结构及原理分析(一)——YOLO检测器

文章目录 概要一、整体架构分析二、详细结构分析YOLO检测器1. Backbone2. Head3.各模块的过程和作用Conv卷积模块C2F模块BottleNeck模块SPPF模块Upsampling模块Concat模块 概要 尽管YOLO&#xff08;You Only Look Once&#xff09;系列的对象检测器在效率和实用性方面表现出色…

【引领未来智造新纪元:量化机器人的革命性应用】

在日新月异的科技浪潮中&#xff0c;量化机器人正以其超凡的智慧与精准的操作&#xff0c;悄然改变着各行各业的生产面貌&#xff0c;成为推动产业升级、提升竞争力的关键力量。今天&#xff0c;让我们一同探索量化机器人在不同领域的广泛应用价值&#xff0c;见证它如何以科技…

CSA笔记4-包/源管理命令以及本地光盘仓库搭建

包/源管理命令 1.rpm是最基础的rmp包的安装命令&#xff0c;需要提前下载相关安装包和依赖包 2.yum/dnf是基于rpm包的自动安装命令&#xff0c;可以自动在仓库中匹配安装软件和依赖包 注意:以上是安装命令&#xff0c;以下是安装源 3.光盘源&#xff1a;是指安装系统时后的…

Air780EP- AT开发-阿里云应用指南

简介 使用AT方式连接阿里云分为一机一密和一型一密两种方式&#xff0c;其中一机一密又包括HTTP认证二次连接和MQTT直连两种方式 关联文档和使用工具&#xff1a; AT固件获取在线加/解密工具阿里云平台 准备工作 Air780EP_全IO开发板一套&#xff0c;包括天线SIM卡&#xff0…

华为云技术精髓笔记(四)-CES基础入门实战

华为云技术精髓笔记(四) CES基础入门实战 一、监控ECS性能 1、 远程登录ECS 步骤一 双击实验桌面的“Xfce终端”打开Terminal&#xff0c;输入以下命令登录云服务器。注意&#xff1a;请使用云服务器的公网IP替换命令中的【EIP】。 LANGen_us.UTF-8 ssh rootEIP说明&#x…

中国自然灾害影响及损失数据

自然灾害往往会导致大量的人员伤亡和财产损失&#xff0c;数据集详细记载了2014-2020年中国自然灾害影响以及灾害造成的损失情况。其中包括地震、台风、雨雪、阵雨、雪灾、暴雨、旱灾、龙卷风、泥石流、山崩、泥石流、滑坡、洪涝等灾害事件。 数据集主要以excel的格式存储。属性…

【学术会议征稿】第二届人工智能与自动化控制国际学术会议(AIAC 2024)

第二届人工智能与自动化控制国际学术会议&#xff08;AIAC 2024&#xff09; The 2nd International Conference on Artificial Intelligence and Automation Control 随着技术的迅猛发展&#xff0c;人工智能与自动化控制已经深入到工业、交通、医疗、教育等各个领域&#x…

【Linux】-----权限详解

目录 一、Linux下的权限概念 Ⅰ、是什么&#xff1f; Ⅱ、Linux下的两种角色 角色 如何添加普通用户 身份的转化方式 身份的提权 添加普通用户至白名单 二、Linux下的权限管理 Ⅰ、文件访问者的分类(Linux下的“人”) Ⅱ、文件类型和访问权限(事物属性) 1.文件类型 …