Input device application programming

In this chapter, we learn the application programming of input devices. We must first know what an input device is? Input devices are actually devices that can generate input events and are called input devices. Common input devices include mice, keyboards, touch screens, buttons, etc. They can all generate input events and generate input data to the computer system.
For input device application programming, it is mainly to obtain the data reported by the input device, the current status of the input device, etc., such as obtaining the X and Y axis position information of the current touch point on the touch screen and whether the touch screen is currently pressed or released.

Introduction to input device programming

What is an input device

Let’s first understand what an input device (also called an input device) is. Common input devices include mouse, keyboard, touch screen, remote control, computer drawing board, etc. Users interact with the system through input devices.

input subsystem

As can be seen from the above introduction, there are many types of input devices, and the data types reported by each device are different. So how does the Linux system manage it? In order to uniformly manage these input devices, the Linux system implements a framework that is compatible with all input devices. This framework is the input subsystem. Driver developers develop input device drivers based on the input subsystem. The input subsystem can shield hardware differences and provide a unified set of interfaces to the application layer.
Input devices that are successfully registered based on the input subsystem will generate corresponding device nodes (device files) in the /dev/input directory. The device node name is usually eventX (X represents a numeric number 0, 1, 2, 3, etc.). For example, /dev/input/event0, /dev/input/event1,
/dev/input/event2, etc., the data reported by the input device can be obtained by reading these device nodes.

The process of reading data

If we want to read the data of the touch screen, assuming that the device node corresponding to the touch screen device is /dev/input/event0, then the data reading process is as follows: ①. The application opens the /dev/input/event0
device file;
②. The application initiates For read operations (such as calling read), if there is no data to read, it will go to sleep (in the case of blocking I/O);
③. When there is data to read, the application will be awakened, and the read operation will return the data;
④. The application parses the read data.
When there is no data to read, the program will enter a dormant state (that is, blocked). For example, if the application reads touch screen data, if the touch screen is not currently touched, there is naturally no data to read; when we touch the touch screen with our fingers or on the screen When you slide up, touch data will be generated and the application will have data to read. The application will wake up and successfully read the data. The same is true for other input devices. When there is no data to read, the application will enter sleep state (in blocking I/O mode) and will be awakened when there is data to read.

How the application parses the data

First of all, we need to know that the application opens the device file corresponding to the input device and initiates a read operation to it. So what kind of data does this read operation obtain? In fact, each read operation obtains a struct input_event structure type data. This structure is defined in the <linux/input.h> header file. Its definition is as follows:

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

The time member variable in the structure is a variable of type struct timeval. This structure was introduced to you before. The kernel will record the time of occurrence of each reported event and return it to the application through the variable time. The time parameter is usually not that important, but the other
three member variables type, code, and value are more important.
⚫ type: type is used to describe which type of event occurred (classification of events). The input event types supported by the Linux system are as follows:

/*
 * Event types
 */
#define EV_SYN 0x00 // 同步类事件,用于同步事件
#define EV_KEY 0x01 // 按键类事件
#define EV_REL 0x02 // 相对位移类事件(譬如鼠标)
#define EV_ABS 0x03 // 绝对位移类事件(譬如触摸屏)
#define EV_MSC 0x04 // 其它杂类事件
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX + 1)

The above macro definitions are also in the <linux/input.h> header file, so the header file needs to be included in the application; an input device can usually generate many different types of events, such as clicking a mouse button (left button, When you press the right button, or other buttons on the mouse), key events will be reported, and when you move the mouse, relative displacement events will be reported.
⚫ code: code represents a specific event in this type of event. Each event type listed above contains a series of specific events. For example, there are usually many keys on a keyboard, such as the letters A, B, C, and D. Or the numbers 1, 2, 3, 4, etc., and the code
variable tells the application which key pressed the input event. Each event type contains a variety of different events, such as key events:

#define KEY_RESERVED 0
#define KEY_ESC 1  // ESC 键
#define KEY_1 2    // 数字1 键
#define KEY_2 3    // 数字2 键
#define KEY_TAB 15 // TAB 键
#define KEY_Q 16   // 字母Q 键
#define KEY_W 17   // 字母W 键
#define KEY_E 18   // 字母E 键
#define KEY_R 19   // 字母R 键
……

relative displacement event

#define REL_X 0x00 // X 轴
#define REL_Y 0x01 // Y 轴
#define REL_Z 0x02 // Z 轴
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
#define REL_CNT (REL_MAX + 1)

Absolute displacement event
The touch screen device is an absolute displacement device that can generate absolute displacement events; for example, for a touch screen, a touch point may contain a variety of information, such as the X-axis coordinate, Y-axis coordinate, and Z-axis coordinate of the touch point. Axis coordinates, pressing force, contact area, etc., so the code
variable tells the application which information of the touch point is currently reported (X coordinate, Y coordinate, or other); the absolute displacement event is as follows:

#define ABS_X 0x00 // X 轴
#define ABS_Y 0x01 // Y 轴
#define ABS_Z 0x02 // Z 轴
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
......

In addition to the ones listed above, there are many more. You can browse the <linux/input.h> header file yourself (these macros are actually defined in the
input-event-codes.h header file, which is included in the <linux/ input.h>), I will introduce these specific events to you later.
⚫ value: Each time the kernel reports an event, it will send a data value to the application layer. The interpretation of the value changes as the code changes. For example, for a key event (type=1), if code=2 (numeric key 1 on the keyboard, that is, KEY_1), then if the value is equal to 1, it means that the KEY_1 key is pressed; if the value is equal to 0, it means that the KEY_1 key is released. , if value equals 2, it means long press of KEY_1 key. For another example, in the absolute displacement event (type=3), if code=0 (touch point X coordinate ABS_X), then the value value is equal to the touch point X-axis coordinate value; similarly, if code=1 (touch point Y coordinate ABS_Y), at this time
the value value is equal to the Y-axis coordinate value of the touch point; so the interpretation of the value value needs to be based on different code values!
Data Synchronization
We mentioned the synchronization event type EV_SYN above. The synchronization event is used to implement synchronization operations and inform the recipient that the data reported in this round is complete. When the application reads the data reported by the input device, a read operation can only read one struct input_event type data. For example, for a touch screen, the information of a touch point includes X coordinates, Y coordinates and other information. In this case, The application needs to perform multiple read operations to read out all the information of a touch point, so that the complete information of the touch point can be obtained.
So how does the application know that the complete data has been read in this round? In fact, this is achieved through synchronization events. After the kernel reports all the data that needs to be reported and sent to the receiver in this round, it will then report a synchronization event to inform the application that the data in this round is complete and can be synchronized.
Synchronization events also include a variety of different events, as shown below:

/*
 * Synchronization events.
 */
#define SYN_REPORT 0
#define SYN_CONFIG 1
#define SYN_MT_REPORT 2
#define SYN_DROPPED 3
#define SYN_MAX 0xf
#define SYN_CNT (SYN_MAX + 1)

All input devices need to report synchronization events. The reported synchronization event is usually SYN_REPORT, and the value is usually 0.

Read struct input_event data

According to the previous introduction, calling read() on the input device will read a struct input_event type data. This section writes a simple application program to print out each element in the read struct input_event type data, and to parse them.
The path corresponding to the source code of this routine is: development board CD->11, Linux C application programming routine source code->17_input->read_input.c.

#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 (;;)
    {
    
    
        /* 循环读取数据*/
        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);
    }
}

When executing the program, you need to pass in parameters. This parameter is the device node (device file) of the corresponding input device. The passed parameters will be verified in the program. In the program, the open() function is first called to open the device file, and then the read() function is called in the for loop to read the file, and the read data is stored in the struct input_event structure object, and then each member of the structure object is The variable is printed out. Note that the program uses blocking I/O to read the device file, so when there is no data to read, the read call will be blocked, and it will not be awakened until there is data to read!
Tips: Device files are different from ordinary files. There is no need to set the read and write position offset before reading and writing device files.
Use cross-compilation tools to compile the above code to obtain the executable file testApp:
Insert image description here

Verify on development board

Both ALPHA and Mini development boards have a user button KEY0, which is a typical input device, as shown in the figure below:
Insert image description here
This button is a GPIO button provided to users. In the factory system, the button driver is based on the input subsystem To achieve this, there is a device node of KEY0 in the /dev/input directory. Which device node it is can be determined using the method introduced in Section 13.1.1, which will not be repeated here! You can also know it by looking at the /proc/bus/input/devices file. Checking this file can get information about all input devices registered in the system, as shown below: Next, we use this button to test and execute the following
Insert image description here
command :
Insert image description here
After the program is running, perform operations such as pressing KEY0 and releasing KEY0, and the terminal will print out the corresponding information, as shown in the figure above.
In the first line, type equals 1, indicating that the key event EV_KEY is reported, code=114. Open the input-event-codes.h header file to search, and you can find that cpde=114 corresponds to the KEY_VOLUMEDOWN key on the keyboard. This is ALPHA /Mini development board factory system has been configured. And value=1 means that the key is pressed, so the entire first line means that the key KEY_VOLUMEDOWN
is pressed.
The second line indicates that the SYN_REPORT event (code=0) in the EV_SYN synchronization event (type=0) is reported, indicating that the data of this round is complete and the report is synchronized.
In the third line, type is equal to 1, indicating a key press event, code is equal to 114, and value is equal to 0, so it indicates that the key KEY_VOLUMEDOWN
is released.
In the fourth line, another synchronization event is reported.
So the entire printed information in the above 4 lines is the process of pressing and releasing the KEY0 button on the development board, the events reported by the kernel and the data value sent to the application layer.
Let's try long-pressing the key KEY0 and holding it down, as shown below:
Insert image description here
You can see that when the key event is reported, the corresponding value is equal to 2, indicating the long-press state.

Button application programming

This section writes an application to obtain the button status and determine whether the button is currently pressed, released, or long pressed. From the information printed above, we can see that for a button, its event reporting process is as follows:

# 以字母A 键为例
KEY_A //上报KEY_A 事件
SYN_REPORT //同步

If it is pressed, when the KEY_A event is reported, value=1; if it is released, value=0; if it is long pressed, value=2.
Next, write the key application program, read the key status and print the result. The code is as follows: The
path corresponding to the source code of this routine is: Development board CD->11, Linux C application programming routine source code->17_input-> read_key.c.

#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)
        {
    
     // 按键事件
            switch (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;
            }
        }
    }
}

In the for loop, read() is called to read the data reported by the input device. When the button is pressed or released (and long pressed), read() will read the data reported by the input
device. First, determine the time Whether the reported event is a key event (EV_KEY), if it is a key event, then use the value to determine whether the current state of the key is released, pressed or long pressed.
Compile the above code:
Insert image description here
Copy the compiled executable file to the /home/root directory of the development board's Linux system.
First we test the KEY0 button of the development board and execute the application:

./testApp /dev/input/event2 # 测试开发板上的KEY0

Figure 17.4.2 Test KEY0

After running the program, press or release KEY0 or long press, the terminal will print out the corresponding information, as shown in the figure above.
code=114 (KEY_VOLUMEDOWN key).

In addition to testing the KEY0 button on the development board, we can also test the buttons on the keyboard. First, find a USB keyboard and connect it to the USB HOST interface of the development board. When the keyboard is inserted, the terminal will print out the corresponding driver loading information. :
Insert image description here
After the driver is successfully loaded, you can check the device node corresponding to the keyboard device and use the command "cat /proc/bus/input/devices" to find the keyboard device information in the print information: For example, the author is using a Logitech
Insert image description here
USB Keyboard "Logitech USB Keyboard", the corresponding device node is /dev/input/event3, run the test program and press and release the keys on the keyboard: you can query the corresponding keys according to the code
Insert image description here
value (through input-event-codes .h header file), for example, code=30 corresponds to the letter A key on the keyboard, and code=48 corresponds to the letter B key.

Touch screen application programming

This section writes a touch screen application to obtain the coordinate information of the touch screen and print it out.

Parse data reported by touch screen devices

The touch screen device is an absolute displacement device and can report absolute displacement events. The absolute displacement events are as follows:

#define ABS_X 0x00 // X 轴坐标
#define ABS_Y 0x01 // Y 轴坐标
#define ABS_Z 0x02 // Z 轴坐标
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18 // 按压力
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_MISC 0x28
#define ABS_MT_SLOT 0x2f                                     /* MT slot being modified */
#define ABS_MT_TOUCH_MAJOR 0x30                              /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31                              /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32                              /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33                              /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34                              /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ // X 轴坐标
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ // Y 轴坐标
#define ABS_MT_TOOL_TYPE 0x37                                /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38                                  /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ ID
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ // 按压力
#define ABS_MT_DISTANCE 0x3b                                /* Contact hover distance */
#define ABS_MT_TOOL_X 0x3c                                  /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d                                  /* Center Y tool position */
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX + 1)

Single-touch and multi-touch
Touch screens are divided into multi-touch devices and single-touch devices. Single-touch devices only support single-touch, and the complete data of one round (the author calls a synchronization event a round) only contains one touch point information; single-touch devices use ABS_XXX events to carry and report touch point information, such as ABS_X (the value value corresponds to the X-axis coordinate value), ABS_Y (the value value corresponds to the Y-axis coordinate value) and other absolute displacement events, and some devices may also support Z-axis coordinates (reported through the ABS_Z event, the value value corresponds to Z-axis coordinate value), pressing force (reported through the ABS_PRESSURE event, the value value corresponds to the pressing force), and contact area and other attributes. Most single-touch devices will report ABS_X and ABS_Y events, while other absolute displacement events depend on the specific device and driver implementation!
Multi-touch devices can support multi-touch. For example, the 4.3-inch, 7-inch and other touch screens used with the ALPHA/Mini development board support multi-touch. For multi-touch devices, a complete round of data may contain multiple touch point information. . Multi-touch devices use ABS_MT_XXX
(MT is Multi-touch, meaning: multi-touch) events to carry and report touch point information, such as ABS_MT_POSITION_X (X-axis coordinates), ABS_MT_POSITION_Y (Y-axis coordinates) and other absolute displacement events.
In addition to reporting absolute displacement events, touch screen devices can also report key events and synchronization events. Synchronization events are easy to understand, because almost every input device will report synchronization events to inform the application layer whether the current round of data is complete; when a finger clicks on the touch screen or leaves the touch screen, a key event will be reported at this time, which is used to describe the press Press and release the touch screen; the specific key event is
BTN_TOUCH (code=0x14a, which is 330). Of course, the BTN_TOUCH event will not be reported when your finger slides on the touch screen.
Tips: The BTN_TOUCH event does not support the long press state, so its value will not be equal to 2. For multi-touch devices, only the BTN_TOUCH event value=1 is reported when the first point is pressed, and the BTN_TOUCH event value=0 is reported when the last point leaves the touch screen.
Single-touch device – sequence of event reporting
From the above introduction, we can see that the process of event reporting for single-touch devices is roughly as follows:

# 点击触摸屏时
BTN_TOUCH
ABS_X
ABS_Y
SYN_REPORT
# 滑动
ABS_X
ABS_Y
SYN_REPORT
# 松开
BTN_TOUCH
SYN_REPORT

The above list is just a rough process. In fact, the amount of information that can be obtained is different for different touch screen devices. For example, a certain device can only read the X and Y coordinates of the touch point, while another device can. Read X, Y coordinates, pressing force, contact area and other information. In short, these data will be reported to the application layer before the SYN_REPORT synchronization event.
When the finger clicks on the touch screen, the BTN_TOUCH event is first reported. At this time, value=1, indicating pressing; then the ABS_X and ABS_Y
events are reported to send the X and Y axis coordinate data to the application layer; after the data reporting is completed, a synchronization event SYN_REPORT is reported, indicating that This time the touch point information is complete.
When the finger slides on the touch screen, the BTN_TOUCH event is not reported because the pressing and releasing actions do not occur during the sliding process.

When released, the BTN_TOUCH event is first reported. At this time, value=0, indicating that the finger has released the touch screen, and then a synchronization event SYN_REPORT is reported.
The above is a general process for reporting events on single-touch devices. Next, let’s look at multi-touch devices.
Multi-touch devices – the order of event reporting.
A complete round of data reported by a multi-touch device may contain information about multiple touch points, such as a 5-point touch device. If 5 fingers slide on the touch screen at the same time, the hardware will be updated. The kernel needs to report the information of the 5 touch points to the application layer.
In the Linux kernel, multi-touch devices use the multi-touch (MT) protocol to report the data of each touch point. The MT protocol is divided into two types: Type A and Type B. The Type A protocol is rarely used in actual use, almost It is on the verge of elimination, so I won’t introduce it to you here. Let’s focus on the Type B protocol.
Type B protocol of MT protocol
Type B protocol is suitable for devices that can track and distinguish touch points. The touch screens used with the development board belong to this type of device. The focus of the Type B
protocol is to report the update of each touch point information through the ABS_MT_SLOT event!
Devices that can track and distinguish touch points usually have hardware that can distinguish different touch points. For example, for a 5-point touch device, the hardware can associate each recognized touch point with a slot. This slot is a number. , touch point 0, touch point 1, touch point 2, etc. The underlying driver reports the ABS_MT_SLOT event to the application layer. This event will tell the receiver which touch point data is currently being updated. The corresponding value data in the ABS_MT_SLOT event stores a slot to inform the application layer that the slot-related data is currently being updated
. Information corresponding to the touch point.
Each identified touch point is assigned a slot, associated with the slot, and uses this slot to transmit changes to the corresponding touch point. In addition to
the ABS_MT_SLOT event, the Type B protocol will also use the ABS_MT_TRACTKING_ID event.
The ABS_MT_TRACTKING_ID event is used for the creation, replacement and destruction of touch points. The data value carried by the ABS_MT_TRACTKING_ID event represents an ID, a non-negative ID (ID>= 0) represents a valid touch point. If the ID is equal to -1, it means that the touch point no longer exists and has been removed; an ID that did not exist before means that this is a new touch point.
The Type B protocol can reduce the data sent to user space. Only changed data will be reported. For example, if a touch point moves, but only the X-axis coordinate is changed, but the Y-axis coordinate is not changed, then the kernel only The changed X coordinate value will
be sent to the application layer through the ABS_MT_POSITION_X event.
This is all about the Type B protocol. To help you understand, the author lists the process of reporting data from multi-touch devices under the Type B protocol as follows:

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

You may not understand this just by looking at it. Next, we will print the data from the touch screen and analyze it one by one.
Tips: You may be a little confused about the two concepts of slot and ID. Here I will tell you my understanding; slot is a concept in hardware, while ID can be considered a concept in software; for a multi-touch For a device, the maximum number of touch points it supports is determined. For example, 5 touch devices can support up to 5 touch points. Each touch point has a distinct number on the hardware, such as touch point 0, touch point 1
, Touch point 2, etc., this number is a slot (usually starting from 0); how to assign a slot to the recognized contact? (
The contact is associated with the slot)? Usually in chronological order, for example, if the first finger touches the touch screen first, then the first finger corresponds to touch point 0 (slot=0), and then the second finger touches the touch screen, which corresponds to touch point 1. (slot=1) and so on! This is usually supported by the hardware.
ID can be considered a concept in software. It is also used to distinguish different touch points. However, it is different from slot and is not a concept at the same level. For example, if a finger touches the touch screen and then removes it, then Touch the touch screen again. During this process, assuming that only this finger performs the touch operation, then the two touches correspond to touch point 0 (slot=0). There is no doubt about this! But from the perspective of the life cycle of the touch point, are they the same touch point? The answer is definitely not, why? After the finger leaves the touch screen, the touch point disappears and is deleted. The life cycle of the touch point ends here, so they are naturally different touch points, so their IDs are different.
Analysis of data reported by the touch screen
First, before testing the touch screen, you need to ensure that the LCD screen has been connected to the development board. The factory system of the ALPHA/Mini I.MX6U development board supports a variety of LCD screens with different resolutions, including 4.3-inch 480 272, 4.3- inch 800 480, 7 inches 800 480, 7 inches 1024600
and 10.1-inch 1280*800. Before starting the development board, you need to connect the LCD screen to the LCD interface of the development board through a flexible cable. After the development board is connected to the LCD screen, power on the development board and run the factory system.
Figure 17.5.1 Connecting the LCD screen (ALPHA board)
Figure 17.5.1 Connecting the LCD screen (ALPHA board)

The touch screen and the LCD screen panel are bonded together, which means the touch screen is directly attached to the LCD screen, and you
can touch and slide directly on the LCD screen. For the convenience of testing, you can exit the GUI application of the factory system. How to exit? Click on the screen to enter the settings page. You can see that there is an exit button option under the page. Just click it!
Use the command "cat /proc/bus/input/devices" to determine the device node corresponding to the touch screen, as shown below:
Figure 17.5.2 Touch screen input device information (here is a 4.3-inch 800*480 screen as an example)
Figure 17.5.2 Touch screen input device information (here is a 4.3-inch 800*480 screen as an example)

The author is using the 4.3-inch 800*480 LCD screen that comes with the development board. If readers are using other screens, the name you see may not be "goodix-ts". Execute the executable file corresponding to sample code 17.2.1. Click the touch screen with one finger without releasing it. The terminal will print the following information:
Insert image description here
First, the first line reports the ABS_MT_TRACKING_ID (code=57) in the absolute displacement event EV_ABS (type=3). ) event, and the value is equal to 78, which is the ID. This ID is a non-negative number, so it means that a new touch point is created, which means that a new touch point (finger press) is generated on the touch screen. .
The second line reports the ABS_MT_POSITION_X (code=53) event in the absolute displacement event EV_ABS (type=3), whose value
corresponds to the X coordinate of the touch point; the third line reports the ABS_MT_POSITION_Y (code=54) event, whose value The value value corresponds to the Y coordinate of the touch point, so it can be seen that the coordinates of the touch point are (372, 381).
The fourth line reports BTN_TOUCH (code=330) in the key event EV_KEY (type=1). The value value is equal to 1, indicating that this is the first touch point generated on the touch screen (slot=0, that is, touch point 0). .
The fifth and sixth lines respectively report ABS_X (code=0) and ABS_Y (code=1) in the absolute displacement event EV_ABS (type=3), whose values ​​correspond to the X coordinate and Y coordinate of the touch point respectively. Multi-touch devices will also report the X and Y coordinates of the touch point through ABS_X and ABS_Y events, but usually only touch point 0 is supported, so the multi-touch device can be used as a single-touch device.
The last line reports the SYN_REPORT (code=0) event in the synchronization event EV_SYN (type=0), indicating that all the information about the touch point has been reported.
On the basis of the first touch point, add a second touch point, and the printed information is as follows:
Insert image description here
Lines 1 to 7 are no longer explained. The eighth line reports the ABS_MT_SLOT event in the absolute displacement event EV_ABS (type=3) ( code=47), indicating that the information corresponding to the touch point associated with slot=1 (that is, touch point 1) is currently being updated.
The ninth line reports the ABS_MT_TRACKING_ID event (code=57) in the absolute displacement event EV_ABS (type=3), ID=79, which is an ID that has not appeared before, indicating that this is a new touch point.
The tenth and eleventh lines report the ABS_MT_POSITION_X and ABS_MT_POSITION_Y events respectively.
The last line reports synchronization events (type=0, code=0) to inform the application layer that the data is complete.
When the finger is released, the touch point will be destroyed, the ABS_MT_TRACKING_ID event will be reported, and the value will be set to -1 (ID), as shown below: I will introduce you
Insert image description here
so much about the analysis of touch screen data, whether it is a keyboard, Or with a mouse or touch screen, you can directly print out the data from the input device as above, and then analyze it yourself to determine the rules and processes for reporting events from the input device. After understanding these, you can write a program to verify the results. Next, we will write single-touch and multi-touch applications ourselves to read the coordinate information of the touch point.

Get touch screen information

This section describes how to obtain information about the touch screen device, such as the maximum number of touch points supported by the touch screen, the range of the X and Y coordinates of the touch screen, etc. This information can be obtained through the ioctl() function. This function was introduced to you in Section 3.10.2. ioctl() is a glove box for file I/O operations. The things it can handle are very complex and inconsistent. It is generally used for To operate special files or device files, for the convenience of explanation,
the prototype of the ioctl() function is listed again:

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

The first parameter fd corresponds to the file descriptor; the second parameter request is related to the specific object to be operated and has no unified value. It means requesting the corresponding operation from the file descriptor, that is, requesting instructions; this function is a variable parameter function , the third parameter needs to be determined according to the request parameter and used in conjunction with the request.
Let’s first look at how to use ioctl() of the input device. There are some macro definitions in the input.h header file, as shown below:

#define EVIOCGVERSION _IOR('E', 0x01, int)             /* get driver version */
#define EVIOCGID _IOR('E', 0x02, struct input_id)      /* get device ID */
#define EVIOCGREP _IOR('E', 0x03, unsigned int[2])     /* get repeat settings */
#define EVIOCSREP _IOW('E', 0x03, unsigned int[2])     /* set repeat settings */
#define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */
#define EVIOCGKEYCODE_V2 _IOR('E', 0x04, struct input_keymap_entry)
#define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) /* set keycode */
#define EVIOCSKEYCODE_V2 _IOW('E', 0x04, struct input_keymap_entry)
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) /* get device properties */
/**
 * EVIOCGMTSLOTS(len) - get MT slot values
 * @len: size of the data buffer in bytes
 *
 * The ioctl buffer argument should be binary equivalent to
 *
 * struct input_mt_request_layout {
 * __u32 code;
 * __s32 values[num_slots];
 * };
 *
 * where num_slots is the (arbitrary) number of MT slots to extract.
 *
 * The ioctl size argument (len) is the size of the buffer, which
 * should satisfy len = (num_slots + 1) * sizeof(__s32). If len is
 * too small to fit all available slots, the first num_slots are
 * returned.
 *
 * Before the call, code is set to the wanted ABS_MT event type. On
 * return, values[] is filled with the slot values for the specified
 * ABS_MT code.
 *
 * If the request code is not an ABS_MT value, -EINVAL is returned.
 */
#define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len)
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len)               /* get global key state */
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len)               /* get all LEDs */
#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len)               /* get all sounds status */
#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len)                /* get all switch states */
#define EVIOCGBIT(ev, len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len)    /* get event bits */
#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo) /* get abs value/limits */
#define EVIOCSABS(abs) _IOW('E', 0xc0 + (abs), struct input_absinfo) /* set abs value/limits */
#define EVIOCSFF _IOW('E', 0x80, struct ff_effect)                   /* send a force effect to a force feedback device */
#define EVIOCRMFF _IOW('E', 0x81, int)                               /* Erase a force effect */
#define EVIOCGEFFECTS _IOR('E', 0x84, int)                           /* Report number of effects playable at the same time */
#define EVIOCGRAB _IOW('E', 0x90, int)                               /* Grab/Release device */
#define EVIOCREVOKE _IOW('E', 0x91, int)                             /* Revoke device access */


There are corresponding comments behind each macro definition. For input input devices, these macros need to be used to perform ioctl() operations on them. Different macros represent different request instructions; for example, use the EVIOCGNAME macro to obtain the device name. The usage is as follows:

char name[100];
ioctl(fd, EVIOCGNAME(sizeof(name)), name);

EVIOCGNAME(len) represents the size of the buffer used to receive string data. At this time, the third parameter of the ioctl() function needs to pass in the address of a buffer, which is used to store the string corresponding to the device name. data.
The one starting with EVIOCG (get) means getting information, and the one starting with EVIOCS (set) means setting; regardless of other macros, let’s focus on the EVIOCGABS (abs) macro. This macro is also the most commonly used, as shown below:

#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo)

Through this macro, you can get the value range of the touch screen slot (slot<0> means touch point 0, slot<1> means touch point 1, slot<2> means touch point 2, and so on!), you can see how to use it This macro needs to pass in an abs parameter, which is expressed as an ABS_XXX absolute displacement event. For example, EVIOCGABS (ABS_MT_SLOT) means to obtain the slot information of the touch screen. At this time, the third parameter of the ioctl() function is a pointer to struct input_absinfo *. Points to a struct input_absinfo object. Calling ioctl() will write the obtained information into
the struct input_absinfo object. The struct input_absinfo structure is as follows:

struct input_absinfo
{
    
    
    __s32 value;   // 最新的报告值
    __s32 minimum; // 最小值
    __s32 maximum; // 最大值
    __s32 fuzz;
    __s32 flat;
    __s32 resolution;
};

Get the maximum number of touch points supported by the touch screen:

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;

Other macro definitions will not be introduced and readers can test them by themselves.
Obtain the maximum number of touch points supported by the touch screen.
The path corresponding to the source code of this routine is: development board CD->11, Linux C application programming routine source code->17_input->read_slot.c.

#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);
}

Compile the sample code, copy it to the user's home directory of the development board's Linux system, and execute the application:
Figure 17.5.6 Operation results

So it can be seen from the printing results that our screen is a 5-point touch screen.

Single touch application

Through the above detailed introduction, everyone should know how to write a touch screen application. In this section, we write a single-touch application to obtain the coordinate information of a touch point and print it out.
The touch screens used with the ALPHA/Mini development board all support multi-touch. Here we use it as a single-touch device and write a program to read a touch point. The sample code is as follows: The path corresponding to the source code of this routine
is : Development board CD->11, Linux C application programming routine source code->17_input->read_ts.c.

#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)
            {
    
    
                down = in_ev.value;
                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)
            {
    
    
                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;
        }
    }
}

In the program, the passed parameters are first verified, and the touch screen device file path is passed into the program through parameter passing. Four variables are defined in the main() function: ⑴, variable x represents the X coordinate of the touch point; ⑵,
variable
y Indicates the Y coordinate of the touch point;
⑶. The variable down indicates whether the finger is pressed, released or slid. down=1 indicates that the finger is pressed, down=0 indicates that the finger is released, and down=
-1 indicates that the finger is sliding;
⑷. Variable Valid indicates whether the data is valid, valid=1 indicates valid, valid=0 indicates invalid; valid means that the information we detect has changed. For example, the program only detects the pressing and releasing actions of the finger and changes in coordinate values.
Then call open() to open the touch screen device file and obtain the file descriptor fd; before the for loop, first initialize
the four variables x, y, down, and valid. Read the data reported by the touch screen in the for loop and store the read data in the struct input_event
data structure. Analyze the read data in the switch...case statement, obtain the value data of the BTN_TOUCH event, determine whether the touch screen is pressed or released, obtain the value variables of the ABS_X and ABS_Y events, and obtain the X-axis coordinates and Y coordinates of the touch point. axis coordinates.
When a synchronization event is reported, it means that the data is complete. Then we analyze the data we obtained and print the coordinate information.
Compile the application:
Figure 17.5.7 Compiling the application

Copy the compiled executable file to the user's home directory of the development board's Linux system and prepare for testing.
The author is using a 4.3-inch 800*480 LCD screen to execute a single-touch application. After the program is executed, press, release and slide the touch screen with one finger. The serial port terminal will print out the corresponding information:
Figure 17.5.8 Single-touch application test results

When the finger taps the touch screen, "Press (X, Y)" will be printed, when released, "Release" will be printed, and when the finger slides on the touch screen, information such as "Move (X, Y)" will be printed
. You can test it yourself. If you don’t understand the code, you can compare it with the test results.

Multi-touch application

After introducing single-touch applications, let's take a look at how to write multi-touch applications. The event reporting process of multi-touch devices has been introduced in detail before.
The path corresponding to the source code of this routine is: development board CD->11, Linux C application programming routine source code->17_input->read_mt.c.

#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)); // 清零
    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)
            {
    
    
                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));
    /* 读数据*/
    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);
}

The struct ts_mt data structure is declared in the sample code, which is used to describe the information of each touch point in the case of multi-touch.
First, let’s look at the main() function, which defines the max_slots variable, which is used to specify the maximum number of touch points supported by the touch screen device. This information of the touch screen is obtained through:
ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot) . Then according to the value of the max_slots variable, apply for memory for the mt pointer:

mt = calloc(max_slots, sizeof(struct ts_mt));

The ts_read() function is called in the for( ; ; ) loop. This function is a custom function used to obtain the data reported by the touch screen. The first parameter represents the file descriptor fd, and the second parameter represents the maximum number of touch points supported by the touch screen. The third parameter is the struct ts_mt array. The ts_read() function will store the obtained data in the array. mt[0] represents the data of slot<0>, mt[1] represents the data of slot<1>, and so on!
In the internal for loop, the obtained data is analyzed to determine whether the data is valid, and the finger movement is judged based on the id. In a single-touch application, we judge the finger movement through the BTN_TOUCH event; while in In multi-touch applications, we need to judge the movements of multiple fingers by ID.
I won’t introduce the custom function ts_read() anymore, the code comments have already described it clearly!
Then compile the application, copy the compiled executable file to the user's home directory of the development board's Linux system, execute the application, and then use multiple fingers to touch the touch screen, release, slide and other operations: each different slot
Insert image description here
represents Different touch points, for example, slot<0> represents touch point 0, slot<1> represents touch point 1, and so on!

Mouse application programming

This section is an assignment left by the author to all readers. I leave it to you to complete. Through the introduction of the content in this chapter, I believe you can complete it independently. The factory system of ALPHA/Mini development board supports USB mouse. Directly insert a USB mouse into the development board. Just use the USB HOST interface, and the driver loading information will be printed on the terminal.

Guess you like

Origin blog.csdn.net/zhuguanlin121/article/details/132571366