linux2.6.x内核提供了uinput驱动,它可以帮助用户将数据(包括用户输入的键盘或者鼠标或者触摸板数据)注入到Linux内核,这对于编写用户自定义的输入设备的应用程序是非常有用的。
驱动程序使用/ dev/ uinput的设备,将数据发送到内核空间,而内核反过来将数据发送到X-Windows或者shell终端。此功能可用于所有涉及的图形用户输入。
uinput驱动在许多linux内核里均被配置为可加载的模块,你可以通过如下的命令加载luinput驱动。
$ modprobe uinput
$ lsmod
“lsmod”命令列出了linux系统已经加载的所有驱动。你应该可以看到“uinput”驱动也在这个列表中。但是如果你是在kernel里把uinput设置成编译进内核,而不是编译成模块的话,那么你通过lsmod是看不到uinput模块的。但你在/dev下会看到uinput的设备结点。后面你可以直接打开此结点来向uinput发送数据。
接下来我们开发一个简单的应用程序,该应用程序将发送键值到kernel,而kernel将键值发送到x-window或shell终端。
1:打开uinput设备:
Uinp_fd = open(“/dev/uinput”,O_WRONLY|O_NDELAY);
If(uinp_fd == NULL)
{
printf(“unable to open /dev/uinput\n”);
Return -1;
}
成功打开设备之后,需要通过驱动的ioctl函数来配置uinput设备的参数,包括鼠标参数、键盘参数等。如下:
ioctl(out_fd,UI_SET_EVBIT,EV_KEY);
ioctl(out_fd,UI_SET_EVBIT,EV_REP);
EV_KEY和EV_REP来告诉uinput驱动是包含键值的键盘事件。
所有来自用户进程的事件都会通过结构体“struct input_event”(在/usr/include/linux/input.h中定义)传送给内核空间。
可以通过下面的代码产生键盘事件:
event.type = EV_KEY;
event.code = KEY_ENTER;
event.value = 1;
write(uinp_fd,&event,sizeof(event));
__u16 type; //类型,在下面有定义
__u16 code; //要模拟成什么按键
__s32 value;//是按下还是释放�
上面的代码将要发送一个enter键给内核。该键值最终会通过内核发送给用户空间的应用程序。所有的按键定义在“/usr/include/linux/input.h”中;
你可以通过下面的例子去测试linux uinput的接口:
// uinput.c
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/* Globals */
static int uinp_fd = -1;
struct uinput_user_dev uinp;
// uInput device structure
struct input_event event; // Input device structure
/* Setup the uinput device */
int setup_uinput_device()
{
// Temporary variable
int i=0;
// Open the input device
uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
if (uinp_fd == NULL)
{
}
printf("Unable to open /dev/uinput\n");
return -1;
memset(&uinp,0,sizeof(uinp)); // Intialize the uInput device to NULL
strncpy(uinp.name, "PolyVision Touch Screen", UINPUT_MAX_NAME_SIZE);
uinp.id.version = 4;
uinp.id.bustype = BUS_USB;
// Setup the uinput device
ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);
ioctl(uinp_fd, UI_SET_RELBIT, REL_X);
ioctl(uinp_fd, UI_SET_RELBIT, REL_Y);
for (i=0; i < 256; i++) {
ioctl(uinp_fd, UI_SET_KEYBIT, i);
}
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_TOUCH);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MIDDLE);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_FORWARD);
ioctl(uinp_fd, UI_SET_KEYBIT, BTN_BACK);
/* Create input device into input sub-system */
write(uinp_fd, &uinp, sizeof(uinp));
}
if (ioctl(uinp_fd, UI_DEV_CREATE))
{
printf("Unable to create UINPUT device.");
return -1;
}
return 1;
void send_click_events( )
{
// Move pointer to (0,0) location
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
event.code = REL_X;
event.value = 100;
write(uinp_fd, &event, sizeof(event));
event.type = EV_REL;
event.code = REL_Y;
event.value = 100;
write(uinp_fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
// Report BUTTON CLICK - PRESS event
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = BTN_LEFT;
event.value = 1;
write(uinp_fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
// Report BUTTON CLICK - RELEASE event
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = BTN_LEFT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
void send_a_button()
{
// Report BUTTON CLICK - PRESS event
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = KEY_A;
event.value = 1;
write(uinp_fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
// Report BUTTON CLICK - RELEASE event
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = KEY_A;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
}
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(uinp_fd, &event, sizeof(event));
/* This function will open the uInput device. Please make
sure that you have inserted the uinput.ko into kernel. */
int main()
{
// Return an error if device not found.
if (setup_uinput_device() < 0)
{
printf("Unable to find uinput device\n");
return -1;
}
send_a_button(); // Send a "A" key
send_click_events(); // Send mouse event
/* Destroy the input device */
ioctl(uinp_fd, UI_DEV_DESTROY);
}
/* Close the UINPUT device */
close(uinp_fd);
//在我们的项目中我们做了一个android悬浮控制,上层java实现用户交互,在接收到虚拟的按键后,调用通过jni封装得uinput 模块,最终通过uinput模块将按键发送给kernel。通过jni将c实现的与uinput通信的模块封装成so,在android上层apk调用so,实现虚拟按键的触发。