在嵌入式开发中,按键和触摸板等输入系统很常见,其驱动文件位于/dev/input
下,以event开头,后接数字。epoll是2.6内核引入的I/O多路复用的新的高效的接口,我在以前的博客中,使用其实现了一个聊天室服务器程序。这里我们使用其来监控input子系统事件。
在嵌入式软件开发中,小的程序,我们可以直接使用I/O接口来获得外部的时间输入,而如果对于一个大型的嵌入式软件系统,其会被很多地方用到,那么就需要进行相应的封装处理,对外提供统一的接口。
本篇我们就来实现我们自己的Dispatcher,开始的时候我只监控KeyBoard的事件,所以叫KeyDispatcher
,后来加入了鼠标,所以不要奇怪类型。
使用cat /pro/bus/input/devices
命令,我得到了我的键盘和鼠标的驱动文件名。
I: Bus=0003 Vendor=24ae Product=2000 Version=0101
N: Name="RAPOO RAPOO 2.4G Wireless Device"
P: Phys=usb-0000:00:1d.0-1.3/input1
S: Sysfs=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input15
U: Uniq=
H: Handlers=kbd mouse0 event7
B: PROP=0
B: EV=1f
B: KEY=4837fff072ff32d bf54444600000000 1f0001 20f908b17c000 677bfad941dfed 9ed68000004400 10000002
B: REL=1c3
我使用的是RAPOO的无线鼠标,H: Handlers=kbd mouse0 event7
mouse0,为/dev/input
目录下的event7文件。
I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input4
U: Uniq=
H: Handlers=sysrq kbd event4
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7
keyboard为/dev/input
下的event4。
在这个工程中,我使用了我前面写的signal机制。我们使用的事件触发方式时ET,边沿触发,其在状态转变时只触发一次信号,所以对于读写,一定彻底。
keydispatcher.h
#ifndef KEYDISPATCHER_H
#define KEYDISPATCHER_H
#include "signal.h"
struct epoll_event;
class KeyDispatcher {
public:
KeyDispatcher();
~KeyDispatcher();
bool init();
void run();
Signal<void (*)(int, int, int)> m_key;
private:
int m_epfd;
int m_kdfd;
int m_msfd;
void doInput(const epoll_event* ev);
};
#endif // KEYDISPATCHER_H
keydispatcher.cpp
#include "keydispatcher.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <sys/epoll.h>
#include <string.h>
#define MAX_EVENTS 10
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];
char buffer[sizeof(input_event)];
KeyDispatcher::KeyDispatcher()
{
}
KeyDispatcher::~KeyDispatcher()
{
}
bool KeyDispatcher::init()
{
bool re = false;
m_epfd = epoll_create(1);
if (m_epfd == -1) {
printf("epoll create fail!\n");
}
else {
m_kdfd = open("/dev/input/event4", O_RDONLY | O_NONBLOCK);
m_msfd = open("/dev/input/event7", O_RDONLY | O_NONBLOCK);
if (m_kdfd == -1 || m_msfd == -1) {
printf("open kd device or mouse fail!\n");
}
else {
ev.data.fd = m_kdfd;
ev.events = EPOLLIN | EPOLLET;
int i = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_kdfd, &ev);
ev.data.fd = m_msfd;
ev.events = EPOLLIN | EPOLLET;
int j = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_msfd, &ev);
if (i == -1 || j == -1) {
printf("add kd device in epoll fail!\n");
}
else {
re = true;
}
}
}
return re;
}
void KeyDispatcher::doInput(const epoll_event* ev)
{
ssize_t resize = 0;
ssize_t n = 0;
struct input_event input_ev;
while ((resize = read(ev->data.fd, buffer + n, sizeof(struct input_event) - n)) > 0) {
n += resize;
if (n == sizeof(input_event)) {
memcpy((void*)(&input_ev), buffer, sizeof(input_event));
m_key.eemit((int)input_ev.type, (int)input_ev.code, (int)input_ev.value);
n = 0;
}
}
}
void KeyDispatcher::run()
{
//struct input_event input_ev;
int re = 0;
while (true) {
re = epoll_wait(m_epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < re; ++i) {
if (events[i].events & EPOLLIN) {
doInput(events + i);
// while (read(events[i].data.fd, &input_ev, sizeof(struct input_event)) == sizeof(struct input_event)) {
// printf("type:%d, code:%d, value:%d \n", input_ev.type, input_ev.code, input_ev.value);
// m_key.eemit((int)input_ev.type, (int)input_ev.code, (int)input_ev.value);
// }
}
}
re = 0;
}
}
关于epoll和input子系统,请各位找些资料看下。
main.cpp
#include "keydispatcher.h"
#include <stdio.h>
#include <pthread.h>
#define KEY 0x01
#define REL 0x02
#define ABS 0x03
class Log
{
public:
void log(int type, int code, int value) {
do {
if (type == KEY) {
printf("KEY\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
if (type == REL) {
printf("REL\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
if (type == ABS) {
printf("ABS\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
} while(0);
fflush(stdout);
}
};
void* pt_callback(void * arg)
{
KeyDispatcher *keydispatcher = (KeyDispatcher*)(arg);
keydispatcher->run();
return NULL;
}
int main()
{
pthread_t ptid;
KeyDispatcher dispatcher;
Log logger;
dispatcher.m_key.connect(&logger, &Log::log);
if (dispatcher.init()) {
int ptre = pthread_create(&ptid, NULL, pt_callback, (void*)(&dispatcher));
if (ptre != 0) {
printf("dispatcher thread create fail!\n");
}
else {
pthread_join(ptid, NULL);
}
//dispatcher.run();
}
else {
printf("init fail!\n");
}
return 0;
}
mouse和keyboard都是输入设备,所以我们只监听了可读信号,不写,所以简单很多。
基于这个,通过为上层应用提供统一接口添加一个编码解析层,上层来实现处理接口,通过调用这些接口我们就可以实现输入设备的事件分发到各上层应用中。
项目使用qmake管理,在当前的目录下,执行qmake,生成Makefile文件,项目中的signal使用了c++0x的特性,所以请在Makefile的CXXFLAGS一项中,追加-std=c++0x
。
本篇的代码:https://git.oschina.net/zhouX/dispatcher.git