专栏文章目录传送门:返回专栏目录
目录
本文实操环境:
开发环境 | EVK Board | Soc | kernel |
---|---|---|---|
Ubuntu 18.04 | i.MX 8M QUAD EVK | i.MX8MQ | kernel 5.10 |
从上一个章节已经大概了解Linux Input子系统的概念以及一些实现的原理,本章节主要讲解Linux Input子系统在应用上该如何使用,以及在Android上的一些使用。
1. Linux 应用层获取Input数据
Linux Input子系统已经在内核给用户提供了统一的接口,在用户层我们可以通过总线上的input设备:
-
命令:cat /proc/bus/input/devices
可以在Linux 系统去查看
在Linux 应用层获取某个input 设备的事件,大概流程是打开/dev/input/eventX设备,再通过read读取,进行解析既可。代码如下:
/*************************************************************************
> File Name : input_event_reader.c
> Author : Hywel
> Mail : huizh2009@126.com
> Created Time: Wed 14 Jul 2021 05:48:05 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <linux/input.h>
#define test_bit(bit) (mask[(bit)/8] & (1 << ((bit)%8)))
#define DEV_MAXNUM_LIST 32
struct input_event event;
const char* help_message = "Usage: ./keycode_test <argument>\n"
"Arguments:\n"
" <argument> : /dev/input/eventX\n"
" --help : Display this help message.\n";
void list_input_dev(void)
{
char name[64]; /* RATS: Use ok, but could be better */
char buf[256] = { 0, }; /* RATS: Use ok */
unsigned char mask[EV_MAX/8 + 1]; /* RATS: Use ok */
int version;
int i, j;
int fd = 0;
printf("list dev input...\n");
printf("====================================================\n");
for (i = 0; i < DEV_MAXNUM_LIST; i++) {
sprintf(name, "/dev/input/event%d", i);
if ((fd = open(name, O_RDONLY, 0)) >= 0) {
ioctl(fd, EVIOCGVERSION, &version);
ioctl(fd, EVIOCGNAME(sizeof(buf)), buf);
ioctl(fd, EVIOCGBIT(0, sizeof(mask)), mask);
printf("%s\n", name);
printf(" evdev version: %d.%d.%d\n",
version >> 16, (version >> 8) & 0xff, version & 0xff);
printf(" name: %s\n", buf);
printf(" features:");
for (j = 0; j < EV_MAX; j++) {
if (test_bit(j)) {
const char *type = "unknown";
switch(j) {
case EV_KEY:
type = "keys/buttons";
break;
case EV_REL:
type = "relative";
break;
case EV_ABS:
type = "absolute";
break;
case EV_MSC:
type = "reserved";
break;
case EV_LED:
type = "leds";
break;
case EV_SND:
type = "sound";
break;
case EV_REP:
type = "repeat";
break;
case EV_FF:
type = "feedback";
break;
}
printf(" %s", type);
}
}
printf("\n");
close(fd);
}
}
printf("====================================================\n");
}
int main(int argc, char **argv)
{
char name[64];
int fd = 0, rc = 0, i = 0;
list_input_dev();
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
printf("Invalid number of arguments.\n");
printf("%s", help_message);
return 1;
}
if (strncmp(argv[1], "/dev/input/event", 16) != 0) {
printf("Invalid argument format. Please use /dev/input/eventX.\n");
printf("%s", help_message);
return 1;
}
sprintf(name, "%s", argv[1]);
if ((fd = open(name, O_RDWR, 0)) < 0) {
perror("Failed to open input device");
return 1;
}
printf("%s: open, fd = %d\n", name, fd);
while ((rc = read(fd, &event, sizeof(event))) > 0) {
printf("%-24.24s.%06lu type 0x%04x; code 0x%04x;"
" value 0x%08x; ",
ctime(&event.time.tv_sec),
event.time.tv_usec,
event.type, event.code, event.value);
printf("\n");
// add your process code
}
if (rc < 0) {
perror("Failed to read input event");
}
close(fd);
return 0;
}
根据代码中,先list 当前设备存在的input 设备,然后通过传入需要打开的input 设备,当在打开的input输入就可以看到相关打印信息。
gcc与交叉编译:
// 如果在本地运行编译,
gcc -static keycode_test.c -o keycode_test
// i.MX8MQ 下使用,则采用交叉编译器编译
aarch64-linux-gnu-gcc -static keycode_test.c -o keycode_test
放入i.MX8MQ中运行:
这里通过USB接入了一个键盘,可以看到input 设备是
/dev/input/event3
在键盘输入的是时候,可以看到相关的数据,type , code 等等数据。
2. Android 上使用Input相关
2.1 input命令输入
input keyevent <keycode>
这条命令是在android 11 下可以使用的,也是在Linux 上得到支持,对于keycode这里是指在
frameworks/base/core/java/android/view/KeyEvent.java
截取部分代码:
比如
# homeinput keycode 3
# backinput keycode 4
# power input keycode 26
#...
在android 终端可以输入看到效果,其实input 这个命令包含很多输入的类型,具体查看
evk_8mq:/ # input --help
Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]
The sources are:
dpad keyboard mouse touchpad gamepad touchnavigation joystick touchscreen stylus trackball
-d: specify the display ID. (Default: -1 for key event, 0 for motion event if not specified.)The commands and default sources are: text <string> (Default: touchscreen) keyevent [--longpress] <key code number or name> ... (Default: keyboard) tap <x> <y> (Default: touchscreen) swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen) draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen) press (Default: trackball) roll <dx> <dy> (Default: trackball) motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)
Error: Error: Unknown command: --help
支持多种sources,这里再举例常用的几个命令如下:
# 通过坐标模拟点击
evk_8mq:/ # input tap 600 600#
滑动动作,从(300 400)划动到(600,600)时间100ms
evk_8mq:/ # input swipe 300 400 600 600 100
# 拖动动作 从(300 400)托动到(600,600)时间100ms
evk_8mq:/ # input draganddrop 300 400 600 600 1000
# 文本输入 abc
evk_8mq:/ # input text abc#...
2.2 代码模拟输入
目前在系统中,需要通过代码进行模拟输入一些keyevent, 那么该如何操作,因为在系统调用过程中发现采用input 输入命令的方式,会造成系统卡顿延时的情况,为了解决这个问题采用创建虚拟input的方式解决:
/*************************************************************************
> File Name : simulate_input.c
> Author : Hywel
> Mail : huizh2009@126.com
> Created Time: Wed 14 Jul 2021 05:48:05 PM CST
************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/uinput.h>
#include <linux/input.h>
#include "simulate_input.h"
#define DEBUG_PRINTF printf
static int uinput_fd;
static struct uinput_user_dev uinput_dev;
int creat_user_uinput(void)
{
int i;
int ret = 0;
uinput_fd = open("/dev/uinput", O_RDWR | O_NDELAY);
if(uinput_fd < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
return -1;//error process.
}
//to set uinput dev
memset(&uinput_dev, 0, sizeof(struct uinput_user_dev));
//snprintf(uinput_dev.name, UINPUT_MAX_NAME_SIZE, "uinput-custom-dev");
snprintf(uinput_dev.name, UINPUT_MAX_NAME_SIZE, "virtual-uinput-dev");
uinput_dev.id.version = 1;
uinput_dev.id.bustype = BUS_VIRTUAL;
ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN);
ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY);
ioctl(uinput_fd, UI_SET_EVBIT, EV_MSC);
for(i = 0; i < 256; i++) {
ioctl(uinput_fd, UI_SET_KEYBIT, i);
}
ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_UP);
ioctl(uinput_fd, UI_SET_MSCBIT, KEY_CUSTOM_DOWN);
ret = write(uinput_fd, &uinput_dev, sizeof(struct uinput_user_dev));
if(ret < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
return ret;//error process.
}
ret = ioctl(uinput_fd, UI_DEV_CREATE);
if(ret < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
close(uinput_fd);
return ret;//error process.
}
return ret;
}
int report_key(unsigned int type, unsigned int keycode, unsigned int value)
{
struct input_event key_event;
int ret;
memset(&key_event, 0, sizeof(struct input_event));
gettimeofday(&key_event.time, NULL);
key_event.type = type;
key_event.code = keycode;
key_event.value = value;
ret = write(uinput_fd, &key_event, sizeof(struct input_event));
if(ret < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
return ret;//error process.
}
gettimeofday(&key_event.time, NULL);
key_event.type = EV_SYN;
key_event.code = SYN_REPORT;
key_event.value = 0;//event status sync
ret = write(uinput_fd, &key_event, sizeof(struct input_event));
if(ret < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
return ret;//error process.
}
return 0;
}
void send_simulate_keycode( int keycode)
{
report_key(EV_KEY, keycode, 1);
usleep(100);
report_key(EV_KEY, keycode, 0);
DEBUG_PRINTF("simulate input keycode %d\n", keycode);
}
int keycode_event_init(void)
{
DEBUG_PRINTF("keycode event init...\n");
int ret = creat_user_uinput();
if(ret < 0) {
DEBUG_PRINTF("%s:%d\n", __func__, __LINE__);
return -1;//error process.
}
return ret;
}
int main(int argc, char **argv)
{
int ret = 0;
int event_code = 0;
#if 1 //for debug input code
if (argc != 2) {
printf("Invalid number of arguments.\n");
printf("Usage: %s <event_code>\n", argv[0]);
return 1;
}
event_code = atoi(argv[1]);
#endif
ret = keycode_event_init();
if(ret) {
DEBUG_PRINTF("keycode event failed...\n");
return ret;
}
//debug.
DEBUG_PRINTF("start debug %d...\n", event_code );
//send_simulate_keycode(KEY_CUSTOM_UP);
send_simulate_keycode(event_code);
//send_simulate_keycode(KEY_CUSTOM_DOWN);
close(uinput_fd);
return 0;
}
根据代码编译放入i.MX8MQ中运行。
编译:
aarch64-linux-gnu-gcc -static simulate_input.c -o simulate_input
在开发板运行就可以看到,会有相应的选项选择下一个。
3. 总结
本章节主要讲述关于Input子系统的应用,获取外部输入和模拟一个虚拟的input 设备,这些都使用Linux 系统,Android 系统也适用,Android同样进行交叉静态编译也可以运行。