linux系统下,在用户空间应用程序中模拟发送系统键盘事件
Linux 有自己的 input 子系统,可以统一管理鼠标和键盘事件。
如果想模拟键盘事件,但是系统没有键盘设备该如何是好?
基于输入子系统实现的 input 可以方便的在用户空间模拟鼠标和键盘事件。
也可以自己做一个字符设备接收用户输入,根据输入,投递 input 事件。
还有一种方式就是直接往evnent 里写入数据, 都可以达到控制鼠标键盘的功能。---没有键盘设备的话,向哪一个event里面写?
好文:
http://blog.chinaunix.net/uid-23381466-id-3883164.html
https://blog.csdn.net/neiloid/article/details/7893732
相关书籍:
https://blog.csdn.net/huaweimember/article/details/51001672
http://baijiahao.baidu.com/s?id=1601404998850288910&wfr=spider&for=pc
weekend 探索成功, 使用uinput驱动来实现 linux系统用户空间模拟按键事件:
这几篇真是太棒了!:
http://sipgreen.iteye.com/blog/1774676
http://sipgreen.iteye.com/blog/1774676
http://blog.sina.com.cn/s/blog_602f87700100llew.html
https://www.cnblogs.com/myblesh/articles/2367648.html
https://www.cnblogs.com/myblesh/articles/2367648.html
https://www.kernel.org/doc/html/v4.12/input/uinput.html ----linux官网上关于uinput模块的介绍
首先在内核中添加uinput模块(也可以将这个模块直接编译进内核里面),然后使用wriet系统函数向文件中写入键盘事件!
linux2.6.x内核提供了uinput驱动,它可以帮助用户将数据(包括用户输入的键盘或者鼠标或者触摸板数据)注入到Linux内核,这对于编写用户自定义的输入设备的应用程序是非常有用。
驱动程序使用/dev/uinput的设备,将数据发送到内核空间,内核将数据发送到X-Windows或者shell终端。此功能可用于所有涉及图形用户的输入。
uinput驱动在许多linux内核里均被配置为可加载的模块,你可以通过$ modprobe uinput 命令加载luinput驱动。
$ lsmod 命令列出linux系统已经加载的所有驱动。你应该可以看到“uinput”驱动也在这个列表中。但是如果你是在kernel里把uinput设置成编译进内核,而不是编译成模块的话,那么你通过lsmod是看不到uinput模块的。但你在/dev下会看到uinput的设备结点。后面你可以直接打开此结点来向uinput发送数据。
相关头文件:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <linux/uinput.h> #include <sys/time.h> #include <unistd.h> #include <errno.h>
step1:打开uinput设备并设置设备参数:
uinp_fd = open(“/dev/uinput”,O_WRONLY|O_NDELAY);
If(uinp_fd < 0){
printf(“unable to open /dev/uinput\n”);//也有可能在/dev/input/uniput
Return -1;
}
成功打开/dev/uinput设备之后,需要通过驱动的ioctl函数(点击打开链接)来配置uinput设备的参数,包括鼠标参数、键盘参数等,即设置Input Device关心或者说会产生的消息:
ioctl(uinp_fd ,UI_SET_EVBIT,EV_KEY); //设置设备所支持的动作,#defineEV_KEY 0x01 // 按下键
ioctl(uinp_fd ,UI_SET_EVBIT,EV_REP); //设置设备所支持的动作,#defineEV_KEY 0x02 // 释放
(//EV_KEY和EV_REP来告诉uinput驱动是包含键值的键盘事件。)
ioctl(uinp_fd , UI_SET_RELBIT, REL_X); //包含鼠标事件
ioctl(uinp_fd , UI_SET_RELBIT, REL_Y);
ioctl(uinp_fd , UI_SET_EVBIT, EV_ABS);
ioctl(uinp_fd , UI_SET_ABSBIT, ABS_X);
ioctl(uinp_fd , UI_SET_ABSBIT, ABS_Y);
ioctl(uinp_fd , UI_SET_ABSBIT, ABS_PRESSURE);
for(i = 0; i < 256; i++){// ---------------------???????
ioctl(uinp_fd , UI_SET_KEYBIT, i);
}
setp2:创建设备并写入至input子系统
struct uinput_user_dev uinput;
uinput.id.version = 4;
uinput.id.bustype = BUS_USB;
uinput.absmin[ABS_X] = 0;
uinput.absmax[ABS_X] = 65535; //sam 把屏幕设为0-65535
uinput.absmin[ABS_Y] = 0;
uinput.absmax[ABS_Y] = 65535;
uinput.absmin[ABS_PRESSURE] = 0;
uinput.absmax[ABS_PRESSURE] = 0xfff;
ret = write( uinp_fd , &uinput, sizeof(uinput) );
ioctl(uinput_fd, UI_DEV_CREATE)
如果在代码中,上面的执行完成之后,马上执行下面的step3的话,会导致没有任何效果,我加掩饰100ms,好了!原因还未知!
step3: 自己构造input事件并写入
所有来自用户进程的事件都会通过结构体“struct input_event”(在/usr/include/linux/input.h中定义)传送给内核空间。
可以通过下面的代码产生键盘事件:
event.type = EV_KEY;
event.code = KEY_ENTER;
event.value = 1;//1表示按下,0表示抬起
gettimeofday(&event.time, NULL);
__u16 type; //类型,在下面有定义
__u16 code; //要模拟成什么按键
__s32 value;//是按下还是释放
write(uinp_fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
gettimeofday(&event.time, NULL);
write(uinput_fd, &event, sizeof(event));//发送同步信号
上面的代码将要发送一个enter键给内核。该键值最终会通过内核发送给用户空间的应用程序。所有的按键定义在“/usr/include/linux/input.h”中;注意,当发送按键事件之后,要马上发送一个同步事件,否则内核不会马上处理你的按键事件!
my code as follows: 注意需要修改/dev/uinput文件的权限
void testevent()
{
//int uinp_fd = open("/dev/uinput",O_WRONLY|O_NDELAY);O_RDWR
int uinp_fd = open("/dev/uinput",O_RDWR);
if (uinp_fd < 0){
qDebug()<<"unable to open /dev/uinput";
return;
}
if(ioctl(uinp_fd ,UI_SET_EVBIT,EV_KEY)<0) //设置设备所支持的动作,#defineEV_KEY 0x01 // 按下键
{
qDebug()<<"unable to set EV_KEY";
return;
}
if(ioctl(uinp_fd ,UI_SET_EVBIT,EV_REP)<0) //设置设备所支持的动作,#defineEV_KEY 0x02 // 释放
{
qDebug()<<"unable to set EV_REP";
return;
}
for(int i = 0; i < 256; i++){ //---------------------???????
ioctl(uinp_fd , UI_SET_KEYBIT, i);
}
//创建设备并写入至input子系统
struct uinput_user_dev uinput;
memset(&uinput,0,sizeof(uinput));
uinput.id.version = 4;
uinput.id.bustype = BUS_USB;
strncpy(uinput.name,"virtual device a",UINPUT_MAX_NAME_SIZE);
int ret = write( uinp_fd , &uinput, sizeof(uinput) );
if(ret<0)
{
qDebug()<<"unable to write(uinp_fd , &uinput, sizeof(uinput)";
return;
}
int err = ioctl(uinp_fd, UI_DEV_CREATE);
if(err < 0)
{
qDebug()<<"unable to creat device";
return;
}
//construct input_event and send it
struct input_event event;
unsigned int key_code = KEY_TAB;
usleep(100000);//important!!!!!!!!!!!!!!!!!!!!!!!!!! don't know why
//unsigned int key_code = KEY_CAPSLOCK;
event.type = EV_KEY;
event.code = key_code;
event.value = 1; //1 means pressed signal
gettimeofday(&event.time, NULL);
if (write(uinp_fd, &event, sizeof(event)) < 0) {
qDebug()<<"unable to write key event";
return;
}
gettimeofday(&event.time, NULL);
event.value = 0;
////////////////send sync signal///////////////////////////////
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
gettimeofday(&event.time, NULL);
if( write(uinp_fd, &event, sizeof(event))<0 )//发送同步信号
{
qDebug()<<"unable to write sync event";
return;
}
close(uinp_fd);
qDebug()<<"UmProg::testQevent() end:";
return;
}
linux udev学习: