前段時間往核,工作中需要完成一個判斷一段時間內(nèi)是否人工操作操作的功能瞧栗,基本思路就是要在后臺監(jiān)聽鍵盤和鼠標(biāo)的輸入事件尝蠕。那么如何能夠捕捉鼠標(biāo)和鍵盤的事件呢列肢?下面分享我們的實現(xiàn)原理·及方法血柳。
捕捉設(shè)備輸入事件的原理
打開/dev/input目錄官册,執(zhí)行l(wèi)s命令,我們可以看到類似下面的文件列表难捌。
$ ls -l
total 0
crw-r-----. 1 root root 13, 64 Jan 29 2018 event0
crw-r-----. 1 root root 13, 65 Jan 29 2018 event1
crw-r-----. 1 root root 13, 66 Jan 29 2018 event2
crw-r-----. 1 root root 13, 67 Jan 29 2018 event3
crw-r-----. 1 root root 13, 68 Jan 29 2018 event4
crw-r-----. 1 root root 13, 63 Jan 29 2018 mice
crw-r-----. 1 root root 13, 32 Jan 29 2018 mouse0
crw-r-----. 1 root root 13, 33 Jan 29 2018 mouse1
crw-r-----. 1 root root 13, 34 Jan 29 2018 mouse2
熟悉Linux的朋友一定已經(jīng)知道這里列出的文件是設(shè)備文件膝宁。在Linux環(huán)境中鸦难,/dev/input目錄下的事件文件(event*)都是在驅(qū)動中調(diào)用input_register_device(struct input_dev *dev)產(chǎn)生的。每個event將上報指定的事件员淫,如觸摸合蔽、Mouse、按鍵等介返。讀取這些事件文件將獲取該事件文件對應(yīng)設(shè)備的輸入信息拴事。
事件數(shù)據(jù)結(jié)構(gòu)
在linux/input.h文件中定義了event事件的輸入數(shù)據(jù)的結(jié)構(gòu)體,該結(jié)構(gòu)體定義如下:
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
time, 指事件發(fā)生的時間圣蝎,具體定義如下:
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
type刃宵,指事件類型,常見的事件類型有:
EV_KEY徘公,按鍵事件牲证,鍵盤的按鍵,鼠標(biāo)的左鍵右鍵等关面;
EV_REL坦袍,相對坐標(biāo),主要用于鼠標(biāo)的移動事件等太;
EV_ABS键闺,絕對坐標(biāo),主要用于觸摸屏的移動事件澈驼。
code 事件代碼,當(dāng)事件類型為EV_KEY時筛武,該代碼為設(shè)備鍵盤代碼缝其,在input.h文件中以KEY_開頭定義;
value 事件的值徘六,當(dāng)事件類型代碼是EV_KEY時内边,按鍵操作值為1,釋放操作值為0待锈,事件類型代碼為EV_REL是漠其,value為正數(shù)值和負(fù)數(shù)值分別代表連個不同方向的值。
如何確定那個事件文件對應(yīng)哪個設(shè)備
/proc/bus/input/device文件中描述了與event對應(yīng)的相關(guān)設(shè)備信息竿音,例如如下信息:
$cat devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: EV=3
B: KEY=10000000000000 0
I: Bus=0017 Vendor=0001 Product=0001 Version=0100
N: Name="Macintosh mouse button emulation"
P: Phys=
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=mouse0 event1
B: EV=7
B: KEY=70000 0 0 0 0
B: REL=3
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/input2
U: Uniq=
H: Handlers=kbd event2
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7
I: Bus=0011 Vendor=0002 Product=0005 Version=0000
N: Name="ImPS/2 Generic Wheel Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input3
U: Uniq=
H: Handlers=mouse1 event3
B: EV=7
B: KEY=70000 0 0 0 0
B: REL=103
在上面的H:中可以看到對應(yīng)的eventxx和屎。
捕捉鍵盤事件的實現(xiàn)方法
通過上節(jié)列出的文件,我們可以獲得鍵盤對應(yīng)的事件文件春瞬。通常鍵盤對應(yīng)的事件文件為event2柴信。監(jiān)聽這個文件,我們就能捕獲到鍵盤的事件宽气。
以下代碼是我實現(xiàn)的監(jiān)聽鍵盤事件的函數(shù)代碼随常,20秒內(nèi)沒有鍵盤事件潜沦,程序退出:
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>
void listen_device(const char *dev, int timeout)
{
int retval;
fd_set readfds;
struct timeval tv;
int fd = open(dev, O_RDONLY);
struct input_event event;
if (fd < 0)
{
perror(dev);
return;
}
while (1)
{
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if((retval = select(fd+1, &readfds, NULL, NULL, &tv)) == 1)
{
if (read(fd, &event, sizeof(event)) == sizeof(event))
{
if (event.type == EV_KEY)
{
if (event.value == 0 || event.value == 1)
{
printf("key %d %s\n", event.code, event.value ? "Pressed" : "Released");
}
}
else
{
printf("type=%x %d %d\n", event.type, event.code, event.value);
}
}
}
else
{
break;
}
}
close(fd);
}
void listen_keyboard(int timeout)
{
listen_device("/dev/input/event2", timeout);
}
int main(int argc, char **argv)
{
listen_keyboard(20);
printf("keyboard timeout\n");
return 0;
}
捕捉鼠標(biāo)事件的實現(xiàn)方法
我們也可以通過上面的方面獲取鼠標(biāo)的event文件,從而實現(xiàn)鼠標(biāo)事件的捕獲绪氛。但事實上鼠標(biāo)有更方便的捕獲方法唆鸡,就是同目錄下的mice文件,通過該文件可以獲取解析后的鼠標(biāo)事件枣察。
具體方法如下:
(1)打開"/dev/input/mice"文件争占。
(2)讀3個字節(jié)。三個字節(jié)的值分別是“Button類型”询件,“X的相對位移”燃乍,“Y的相對位移”。這里先用Button, xRel, yRel表示宛琅。
(3)取Button的低3位(Button & 0x07)刻蟹。0x00 = LeftButtonUp, 0x01 = LeftButtonDown, 0x02 = RightButtonDown.
以下代碼是我實現(xiàn)的監(jiān)鼠標(biāo)事件的函數(shù)代碼:
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h>
void listen_mice(const char *dev, int timeout)
{
char buf[256];
int n_len;
int retval;
fd_set readfds;
struct timeval tv;
int fd = open(dev, O_RDONLY);
if (fd < 0)
{
perror(dev);
return;
}
while (1)
{
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if((retval = select(fd+1, &readfds, NULL, NULL, &tv)) == 1)
{
if ((n_len = read(fd, buf, sizeof(buf))) > 0)
{
if (n_len == 3)
{
printf("Button: %d, xRef=%d, yRef=%d\n", buf[0]&0x07, buf[1], buf[2]);
}
}
}
else
{
break;
}
}
close(fd);
}
void listen_mouse(int timeout)
{
listen_mice("/dev/input/mice", timeout);
}
int main(int argc, char **argv)
{
listen_mouse (20);
printf("mouse timeout\n");
return 0;
}