detect2.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/input-event-codes.h>
#define MAX_DEVICES 64
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
static inline int test_bit(int bit, const unsigned long *array) {
return (array[LONG(bit)] >> OFF(bit)) & 1;
}
const char* get_device_type(int fd) {
unsigned long evbit[NBITS(EV_MAX)] = {0};
unsigned long keybit[NBITS(KEY_MAX)] = {0};
unsigned long absbit[NBITS(ABS_MAX)] = {0};
unsigned long propbit[NBITS(INPUT_PROP_MAX)] = {0};
if (ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0)
return "unknown";
// 先获取所有需要的位图
if (test_bit(EV_KEY, evbit))
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit);
if (test_bit(EV_ABS, evbit))
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit);
// 获取设备属性(用于区分 touchscreen vs touchpad)
int has_direct = 0, has_pointer = 0;
if (ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit) >= 0) {
has_direct = test_bit(INPUT_PROP_DIRECT, propbit);
has_pointer = test_bit(INPUT_PROP_POINTER, propbit);
}
// ✅ 优先判断:多点触摸 + DIRECT → touchscreen
if (test_bit(EV_ABS, evbit) &&
test_bit(ABS_MT_POSITION_X, absbit) &&
test_bit(ABS_MT_POSITION_Y, absbit)) {
if (has_direct) {
return "touchscreen"; // 明确是触摸屏
} else {
return "multitouch-device"; // 保守判断
}
}
// 单点绝对坐标 + BTN_TOUCH
if (test_bit(EV_ABS, evbit) &&
test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) &&
test_bit(BTN_TOUCH, keybit)) {
if (has_direct) {
return "touchscreen";
} else if (has_pointer) {
return "touchpad";
} else {
// 启发式:分辨率高可能是屏,低可能是板(简化处理)
return "touch-input";
}
}
// 鼠标:相对移动 + 按键
if (test_bit(EV_REL, evbit)) {
unsigned long relbit[NBITS(REL_MAX)] = {0};
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit);
if (test_bit(REL_X, relbit) && test_bit(REL_Y, relbit) &&
test_bit(BTN_MOUSE, keybit)) {
return "mouse";
}
}
// 键盘:包含基本字符或控制键
if (test_bit(EV_KEY, evbit)) {
// 常见键盘按键
if (test_bit(KEY_A, keybit) || test_bit(KEY_Z, keybit) ||
test_bit(KEY_1, keybit) || test_bit(KEY_SPACE, keybit) ||
test_bit(KEY_ESC, keybit) || test_bit(KEY_ENTER, keybit)) {
return "keyboard";
}
// 如果只有特殊功能键(如你的 gt911 的 KEY_SWITCHVIDEOMODE),不视为键盘
}
// 游戏手柄/摇杆
if (test_bit(EV_ABS, evbit) &&
(test_bit(ABS_RX, absbit) || test_bit(ABS_X, absbit)) &&
(test_bit(BTN_A, keybit) || test_bit(BTN_TRIGGER, keybit))) {
return "joystick/gamepad";
}
// 如果有 BTN_TOUCH 但没 ABS_X/Y,可能是虚拟按键?归为 button
if (test_bit(BTN_TOUCH, keybit)) {
return "touch-button"; // 如屏幕上的虚拟键
}
// 仅有一些特殊 KEY_*
if (test_bit(EV_KEY, evbit)) {
return "special-keys"; // 如电源、音量、多媒体键
}
return "unknown";
}
// 获取设备名称
void get_device_name(int fd, char *name, size_t size) {
if (ioctl(fd, EVIOCGNAME(size), name) < 0) {
snprintf(name, size, "unknown");
}
}
int main(void) {
struct pollfd fds[MAX_DEVICES];
char names[MAX_DEVICES][256];
const char* types[MAX_DEVICES];
int fd_count = 0;
char path[256];
DIR *dir;
struct dirent *entry;
dir = opendir("/dev/input");
if (!dir) {
perror("opendir /dev/input");
return EXIT_FAILURE;
}
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, "event", 5) == 0) {
snprintf(path, sizeof(path), "/dev/input/%s", entry->d_name);
int fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "无法打开 %s: %s\n", path, strerror(errno));
continue;
}
char name[256];
get_device_name(fd, name, sizeof(name));
const char* type = get_device_type(fd);
printf("监听 [%s] (%s) -> 类型: %s\n", path, name, type);
fds[fd_count].fd = fd;
fds[fd_count].events = POLLIN;
strncpy(names[fd_count], name, sizeof(names[fd_count]) - 1);
types[fd_count] = type;
fd_count++;
if (fd_count >= MAX_DEVICES) break;
}
}
closedir(dir);
if (fd_count == 0) {
fprintf(stderr, "未找到任何 event 设备。\n");
return EXIT_FAILURE;
}
printf("\n开始监听输入事件...(按 Ctrl+C 退出)\n");
struct input_event ev;
while (1) {
int ret = poll(fds, fd_count, -1);
if (ret < 0) {
perror("poll");
break;
}
for (int i = 0; i < fd_count; i++) {
if (fds[i].revents & POLLIN) {
ssize_t bytes;
while ((bytes = read(fds[i].fd, &ev, sizeof(ev))) == sizeof(ev)) {
printf("[%s] (%s): time=%ld.%06ld, type=%d, code=%d, value=%d\n",
names[i], types[i],
ev.time.tv_sec, ev.time.tv_usec,
ev.type, ev.code, ev.value);
}
}
}
}
for (int i = 0; i < fd_count; i++) {
close(fds[i].fd);
}
return EXIT_SUCCESS;
}测试:
root@TinaLinux:/# chmod +x /tmp/detect2 && /tmp/detect2
监听 [/dev/input/event0] (sunxi_ir_recv) -> 类型: special-keys
监听 [/dev/input/event1] (sunxi-gpadc0/channel0/input0) -> 类型: special-keys
监听 [/dev/input/event2] (audiocodec Headphones) -> 类型: unknown
监听 [/dev/input/event3] (A4Tech USB Keyboard) -> 类型: keyboard
监听 [/dev/input/event4] (A4Tech USB Keyboard) -> 类型: special-keys
监听 [/dev/input/event5] (gt9xxnew_ts) -> 类型: touchscreen
开始监听输入事件...(按 Ctrl+C 退出)
[gt9xxnew_ts] (touchscreen): time=84163.016200, type=1, code=330, value=1
[gt9xxnew_ts] (touchscreen): time=84163.016200, type=3, code=53, value=680
[gt9xxnew_ts] (touchscreen): time=84163.016200, type=3, code=54, value=399
[gt9xxnew_ts] (touchscreen): time=84163.016200, type=3, code=48, value=9
...
[gt9xxnew_ts] (touchscreen): time=84163.036021, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84167.859830, type=4, code=4, value=458756
[A4Tech USB Keyboard] (keyboard): time=84167.859830, type=1, code=30, value=1
[A4Tech USB Keyboard] (keyboard): time=84167.859830, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84167.931824, type=4, code=4, value=458756
[A4Tech USB Keyboard] (keyboard): time=84167.931824, type=1, code=30, value=0
[A4Tech USB Keyboard] (keyboard): time=84167.931824, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.259844, type=4, code=4, value=458979
[A4Tech USB Keyboard] (keyboard): time=84176.259844, type=1, code=125, value=1
[A4Tech USB Keyboard] (keyboard): time=84176.259844, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.451820, type=4, code=4, value=458811
[A4Tech USB Keyboard] (keyboard): time=84176.451820, type=1, code=60, value=1
[A4Tech USB Keyboard] (keyboard): time=84176.451820, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.547841, type=4, code=4, value=458811
[A4Tech USB Keyboard] (keyboard): time=84176.547841, type=1, code=60, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.547841, type=0, code=0, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.627824, type=4, code=4, value=458979
[A4Tech USB Keyboard] (keyboard): time=84176.627824, type=1, code=125, value=0
[A4Tech USB Keyboard] (keyboard): time=84176.627824, type=0, code=0, value=0
[sunxi-gpadc0/channel0/input0] (special-keys): time=84180.164279, type=1, code=103, value=1
[sunxi-gpadc0/channel0/input0] (special-keys): time=84180.164279, type=0, code=0, value=0
[sunxi-gpadc0/channel0/input0] (special-keys): time=84180.301283, type=1, code=103, value=0
[sunxi-gpadc0/channel0/input0] (special-keys): time=84180.301283, type=0, code=0, value=0离线
支持USB鼠标键盘拔插:hotplug.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <sys/inotify.h>
#include <linux/input.h>
#include <linux/input-event-codes.h>
#define MAX_DEVICES 64
#define EVENT_NAME_PREFIX "event"
#define INPUT_DIR "/dev/input"
// --- 工具宏 ---
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
static inline int test_bit(int bit, const unsigned long *array) {
return (array[LONG(bit)] >> OFF(bit)) & 1;
}
// --- 设备类型判断(改进版)---
const char* get_device_type(int fd) {
unsigned long evbit[NBITS(EV_MAX)] = {0};
unsigned long keybit[NBITS(KEY_MAX)] = {0};
unsigned long absbit[NBITS(ABS_MAX)] = {0};
unsigned long propbit[NBITS(INPUT_PROP_MAX)] = {0};
if (ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0)
return "unknown";
if (test_bit(EV_KEY, evbit))
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit);
if (test_bit(EV_ABS, evbit))
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit);
ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit); // 可能失败,但没关系
int has_direct = test_bit(INPUT_PROP_DIRECT, propbit);
// ✅ 优先:多点触摸 + DIRECT → touchscreen
if (test_bit(EV_ABS, evbit) &&
test_bit(ABS_MT_POSITION_X, absbit) &&
test_bit(ABS_MT_POSITION_Y, absbit)) {
return has_direct ? "touchscreen" : "multitouch";
}
if (test_bit(EV_ABS, evbit) &&
test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) &&
test_bit(BTN_TOUCH, keybit)) {
return has_direct ? "touchscreen" : "touchpad";
}
if (test_bit(EV_REL, evbit)) {
unsigned long relbit[NBITS(REL_MAX)] = {0};
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit);
if (test_bit(REL_X, relbit) && test_bit(REL_Y, relbit) &&
test_bit(BTN_MOUSE, keybit))
return "mouse";
}
if (test_bit(EV_KEY, evbit)) {
if (test_bit(KEY_A, keybit) || test_bit(KEY_SPACE, keybit) ||
test_bit(KEY_ENTER, keybit) || test_bit(KEY_ESC, keybit))
return "keyboard";
if (test_bit(BTN_TOUCH, keybit))
return "touch-button";
return "special-keys";
}
return "unknown";
}
void get_device_name(int fd, char *name, size_t size) {
if (ioctl(fd, EVIOCGNAME(size), name) < 0)
snprintf(name, size, "unknown");
}
// --- 动态管理设备 ---
struct device {
int fd;
char path[64];
char name[256];
const char* type;
};
static struct device devices[MAX_DEVICES];
static int dev_count = 0;
int add_device(const char *basename) {
if (dev_count >= MAX_DEVICES) return -1;
char path[256];
snprintf(path, sizeof(path), "%s/%s", INPUT_DIR, basename);
int fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd < 0) return -1;
char name[256];
get_device_name(fd, name, sizeof(name));
const char* type = get_device_type(fd);
printf("🔌 添加设备: %s (%s) -> %s\n", path, name, type);
struct device *dev = &devices[dev_count];
dev->fd = fd;
strncpy(dev->path, path, sizeof(dev->path)-1);
strncpy(dev->name, name, sizeof(dev->name)-1);
dev->type = type;
dev_count++;
return 0;
}
void remove_device_by_fd(int fd) {
for (int i = 0; i < dev_count; i++) {
if (devices[i].fd == fd) {
printf("⏏️ 移除设备: %s (%s)\n", devices[i].path, devices[i].name);
close(fd);
// 移动最后一个覆盖
if (i != dev_count - 1) {
devices[i] = devices[dev_count - 1];
}
dev_count--;
return;
}
}
}
void scan_initial_devices() {
DIR *dir = opendir(INPUT_DIR);
if (!dir) return;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, EVENT_NAME_PREFIX, strlen(EVENT_NAME_PREFIX)) == 0) {
add_device(entry->d_name);
}
}
closedir(dir);
}
// --- 主函数 ---
int main(void) {
int inotify_fd = inotify_init1(IN_NONBLOCK);
if (inotify_fd < 0) {
perror("inotify_init1");
return EXIT_FAILURE;
}
int watch_desc = inotify_add_watch(inotify_fd, INPUT_DIR, IN_CREATE | IN_DELETE);
if (watch_desc < 0) {
perror("inotify_add_watch /dev/input");
close(inotify_fd);
return EXIT_FAILURE;
}
// 初始扫描
scan_initial_devices();
printf("✅ 正在监听输入事件和热插拔...(Ctrl+C 退出)\n");
struct input_event ev;
char inotify_buf[4096];
while (1) {
// 构建 pollfd 数组:所有设备 + inotify_fd
struct pollfd *pfds = malloc((dev_count + 1) * sizeof(struct pollfd));
for (int i = 0; i < dev_count; i++) {
pfds[i].fd = devices[i].fd;
pfds[i].events = POLLIN;
}
pfds[dev_count].fd = inotify_fd;
pfds[dev_count].events = POLLIN;
int ret = poll(pfds, dev_count + 1, -1);
if (ret < 0) {
perror("poll");
free(pfds);
break;
}
// 检查 inotify 事件(热插拔)
if (pfds[dev_count].revents & POLLIN) {
ssize_t len = read(inotify_fd, inotify_buf, sizeof(inotify_buf));
if (len > 0) {
char *ptr = inotify_buf;
while (ptr < inotify_buf + len) {
struct inotify_event *event = (struct inotify_event *)ptr;
if (event->len > 0 && strncmp(event->name, EVENT_NAME_PREFIX, strlen(EVENT_NAME_PREFIX)) == 0) {
if (event->mask & IN_CREATE) {
add_device(event->name);
} else if (event->mask & IN_DELETE) {
// 需要找到对应 fd(通过路径匹配)
char path[256];
snprintf(path, sizeof(path), "%s/%s", INPUT_DIR, event->name);
for (int i = 0; i < dev_count; i++) {
if (strcmp(devices[i].path, path) == 0) {
remove_device_by_fd(devices[i].fd);
break;
}
}
}
}
ptr += sizeof(struct inotify_event) + event->len;
}
}
}
// 检查输入事件
for (int i = 0; i < dev_count; i++) {
if (pfds[i].revents & POLLIN) {
ssize_t bytes;
while ((bytes = read(pfds[i].fd, &ev, sizeof(ev))) == sizeof(ev)) {
printf("[%s] (%s): t=%ld.%06ld, type=%d, code=%d, val=%d\n",
devices[i].name, devices[i].type,
ev.time.tv_sec, ev.time.tv_usec,
ev.type, ev.code, ev.value);
}
}
}
free(pfds);
}
// 清理
for (int i = 0; i < dev_count; i++) {
close(devices[i].fd);
}
inotify_rm_watch(inotify_fd, watch_desc);
close(inotify_fd);
return EXIT_SUCCESS;
}拔插USB鼠标键盘和游戏手柄 测试:
root@TinaLinux:/#
root@TinaLinux:/# chmod +x /tmp/detect2 && /tmp/detect2
🔌 添加设备: /dev/input/event0 (sunxi_ir_recv) -> special-keys
🔌 添加设备: /dev/input/event1 (sunxi-gpadc0/channel0/input0) -> special-keys
🔌 添加设备: /dev/input/event2 (audiocodec Headphones) -> unknown
🔌 添加设备: /dev/input/event3 (A4Tech USB Keyboard) -> keyboard
🔌 添加设备: /dev/input/event4 (A4Tech USB Keyboard) -> special-keys
🔌 添加设备: /dev/input/event5 (gt9xxnew_ts) -> touchscreen
✅ 正在监听输入事件和热插拔...(Ctrl+C 退出)
⏏️ 移除设备: /dev/input/event3 (A4Tech USB Keyboard)
⏏️ 移除设备: /dev/input/event4 (A4Tech USB Keyboard)
🔌 添加设备: /dev/input/event3 ( USB OPTICAL MOUSE) -> mouse
[ USB OPTICAL MOUSE] (mouse): t=86070.380692, type=2, code=0, val=-5
[ USB OPTICAL MOUSE] (mouse): t=86070.380692, type=2, code=1, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86070.380692, type=0, code=0, val=0
[ USB OPTICAL MOUSE] (mouse): t=86070.388716, type=2, code=0, val=-4
[ USB OPTICAL MOUSE] (mouse): t=86070.716714, type=2, code=0, val=1
[ USB OPTICAL MOUSE] (mouse): t=86070.716714, type=2, code=1, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86070.716714, type=0, code=0, val=0
[ USB OPTICAL MOUSE] (mouse): t=86070.724699, type=2, code=0, val=1
[ USB OPTICAL MOUSE] (mouse): t=86070.724699, type=2, code=1, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86070.724699, type=0, code=0, val=0
[ USB OPTICAL MOUSE] (mouse): t=86070.748693, type=2, code=1, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86070.748693, type=0, code=0, val=0
[ USB OPTICAL MOUSE] (mouse): t=86070.772708, type=2, code=0, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86070.772708, type=2, code=1, val=-3
[ USB OPTICAL MOUSE] (mouse): t=86070.772708, type=0, code=0, val=0
⏏️ 移除设备: /dev/input/event3 ( USB OPTICAL MOUSE)
🔌 添加设备: /dev/input/event3 (USB gamepad ) -> special-keys
[USB gamepad ] (special-keys): t=86191.685715, type=3, code=0, val=127
[USB gamepad ] (special-keys): t=86191.685715, type=3, code=1, val=127
[USB gamepad ] (special-keys): t=86191.685715, type=0, code=0, val=0
[USB gamepad ] (special-keys): t=86243.085705, type=3, code=1, val=0
[USB gamepad ] (special-keys): t=86243.085705, type=0, code=0, val=0
[USB gamepad ] (special-keys): t=86243.253702, type=3, code=1, val=127
[USB gamepad ] (special-keys): t=86243.253702, type=0, code=0, val=0
[USB gamepad ] (special-keys): t=86244.533720, type=4, code=4, val=589826
[USB gamepad ] (special-keys): t=86244.533720, type=1, code=289, val=1
[USB gamepad ] (special-keys): t=86244.533720, type=0, code=0, val=0离线
接上HUB,鼠标键盘手柄一起测试:
root@TinaLinux:/# chmod +x /tmp/detect2 && /tmp/detect2
🔌 添加设备: /dev/input/event0 (sunxi_ir_recv) -> special-keys
🔌 添加设备: /dev/input/event1 (sunxi-gpadc0/channel0/input0) -> special-keys
🔌 添加设备: /dev/input/event2 (audiocodec Headphones) -> unknown
🔌 添加设备: /dev/input/event5 (gt9xxnew_ts) -> touchscreen
🔌 添加设备: /dev/input/event6 (A4Tech USB Keyboard) -> keyboard
🔌 添加设备: /dev/input/event7 (A4Tech USB Keyboard) -> special-keys
🔌 添加设备: /dev/input/event4 (USB gamepad ) -> special-keys
🔌 添加设备: /dev/input/event3 ( USB OPTICAL MOUSE) -> mouse
✅ 正在监听输入事件和热插拔...(Ctrl+C 退出)
[USB gamepad ] (special-keys): t=86981.448700, type=4, code=4, val=589827
[USB gamepad ] (special-keys): t=86981.448700, type=1, code=290, val=1
[USB gamepad ] (special-keys): t=86981.448700, type=0, code=0, val=0
[USB gamepad ] (special-keys): t=86981.648719, type=4, code=4, val=589827
[USB gamepad ] (special-keys): t=86981.648719, type=1, code=290, val=0
[USB gamepad ] (special-keys): t=86981.648719, type=0, code=0, val=0
[ USB OPTICAL MOUSE] (mouse): t=86983.889697, type=2, code=0, val=-2
[ USB OPTICAL MOUSE] (mouse): t=86983.889697, type=2, code=1, val=-3
[ USB OPTICAL MOUSE] (mouse): t=86984.145699, type=0, code=0, val=0
[A4Tech USB Keyboard] (keyboard): t=86985.946704, type=4, code=4, val=458831
[A4Tech USB Keyboard] (keyboard): t=86985.946704, type=1, code=106, val=1
[A4Tech USB Keyboard] (keyboard): t=86985.946704, type=0, code=0, val=0
[A4Tech USB Keyboard] (keyboard): t=86986.050719, type=4, code=4, val=458831
[A4Tech USB Keyboard] (keyboard): t=86986.050719, type=1, code=106, val=0
[A4Tech USB Keyboard] (keyboard): t=86986.050719, type=0, code=0, val=0离线