Linux 有自己的 input 子系统,可以统一管理鼠标和键盘事件。
基于输入子系统 实现的 uinput 可以方便的在用户空间模拟鼠标和键盘事件。
当然,也可以自己造轮子, 做一个字符设备接收用户输入,根据输入,投递 input 事件。
还有一种方式就是直接 往 evnent 里写入数据, 都可以达到控制鼠标键盘的功能。

本篇文章就是演示直接写入 event 的方法。
linux/input.h中有定义,这个文件还定义了标准按键的编码等

struct input_event {
     struct timeval time;  //按键时间
     __u16 type; //类型,在下面有定义
     __u16 code; //要模拟成什么按键
     __s32 value;//是按下还是释放
 };



code:
事件的代码.如果事件的类型代码是EV_KEY,该代码code为设备键盘代码.代码植0~127为键盘上的按键代码, 0x110~0x116 为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键.其它代码含义请参看include/linux /input.h文件. 如果事件的类型代码是EV_REL,code值表示轨迹的类型.如指示鼠标的X轴方向 REL_X (代码为0x00),指示鼠标的Y轴方向REL_Y(代码为0x01),指示鼠标中轮子方向REL_WHEEL(代码为0x08).

type:
EV_KEY,键盘
EV_REL,相对坐标
EV_ABS,绝对坐标

value:
事件的值.如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;如果事件的类型代码是EV_ REL,value的正数值和负数值分别代表两个不同方向的值.

/*
  * Event types
  */

 #define EV_SYN            0x00
 #define EV_KEY            0x01 //按键
 #define EV_REL            0x02 //相对坐标(轨迹球)
 #define EV_ABS            0x03 //绝对坐标
 #define EV_MSC            0x04 //其他
 #define EV_SW            0x05
 #define EV_LED            0x11 //LED
 #define EV_SND            0x12//声音
 #define EV_REP            0x14//repeat
 #define EV_FF            0x15
 #define EV_PWR            0x16
 #define EV_FF_STATUS        0x17
 #define EV_MAX            0x1f
 #define EV_CNT            (EV_MAX+1)

 下面是一个模拟鼠标和键盘输入的例子:

 #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>

 void simulate_key(int fd,int kval)
 {
     struct input_event event;
     event.type = EV_KEY;
     event.value = 1;
     event.code = kval;

     gettimeofday(&event.time,0);
     write(fd,&event,sizeof(event)) ;

         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));
    
         memset(&event, 0, sizeof(event));
         gettimeofday(&event.time, NULL);
         event.type = EV_KEY;
         event.code = kval;
         event.value = 0;
         write(fd, &event, sizeof(event));
         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));

 }

 void simulate_mouse(int fd)
 {
     struct input_event event;
         memset(&event, 0, sizeof(event));
         gettimeofday(&event.time, NULL);
         event.type = EV_REL;
         event.code = REL_X;
         event.value = 10;
         write(fd, &event, sizeof(event));

         event.type = EV_REL;
         event.code = REL_Y;
         event.value = 10;
         write(fd, &event, sizeof(event));

         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));
 }

 int main()
 {
     int fd_kbd; 
     int fd_mouse; 
     fd_kbd = open("/dev/input/event1",O_RDWR);
     if(fd_kbd<=0){
         printf("error open keyboard:/n");
         return -1;

     }

     fd_mouse = open("/dev/input/event2",O_RDWR); 
     if(fd_mouse<=0){
         printf("error open mouse/n");
         return -2;
     }

     int i = 0;
     for(i=0; i< 10; i++)
     {
         simulate_key(fd_kbd, KEY_A + i);
         simulate_mouse(fd_mouse);
         sleep(1);
     }

     close(fd_kbd);
 }


模拟了鼠标和键盘的输入事件。
关于这里 open 哪个 event , 可以通过 cat /proc/bus/input/devices

I: Bus=0017 Vendor=0001 Product=0001 Version=0100
 N: Name="Macintosh mouse button emulation"
 P: Phys=
 S: Sysfs=/class/input/input0
 U: Uniq=
 H: Handlers=mouse0 event0 
 B: EV=7
 B: KEY=70000 0 0 0 0 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=/class/input/input1
 U: Uniq=
 H: Handlers=kbd event1 
 B: EV=120013
 B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
 B: MSC=10
 B: LED=7

 I: Bus=0019 Vendor=0000 Product=0002 Version=0000
 N: Name="Power Button (FF)"
 P: Phys=LNXPWRBN/button/input0
 S: Sysfs=/class/input/input3
 U: Uniq=
 H: Handlers=kbd event3 
 B: EV=3
 B: KEY=100000 0 0 0

 I: Bus=0019 Vendor=0000 Product=0001 Version=0000
 N: Name="Power Button (CM)"
 P: Phys=PNP0C0C/button/input0
 S: Sysfs=/class/input/input4
 U: Uniq=
 H: Handlers=kbd event4 
 B: EV=3
 B: KEY=100000 0 0 0

 I: Bus=0003 Vendor=046d Product=c018 Version=0111
 N: Name="Logitech USB Optical Mouse"
 P: Phys=usb-0000:00:1d.1-2/input0
 S: Sysfs=/class/input/input24
 U: Uniq=
 H: Handlers=mouse1 event2 
 B: EV=7
 B: KEY=70000 0 0 0 0 0 0 0 0
 B: REL=103



我的鼠标是 罗技 的 Logitech USB Optical Mouse, 所以 鼠标是 event2
下面是一个读取 鼠标和键盘事件的例子:

#include <stdio.h>
 #include <stdlib.h>
 #include <linux/input.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>

 static void show_event(struct input_event* event)
 {
         printf("%d %d %d/n", event->type, event->code, event->value);

         return;
 }

 int main(int argc, char* argv[])
 {
         struct input_event event = {{0}, 0};
         const char* file_name = argc == 2 ? argv[1] : "/dev/input/event2";

         int fd = open(file_name, O_RDWR);


         if(fd > 0)
         {

                 while(1)
                 {
                         int ret = read(fd, &event, sizeof(event));
                         if(ret == sizeof(event))
                         {
                                 show_event(&event);
                         }
                         else
                         {
                                 break;
                         }
                 }
                 close(fd);
         }

         return 0;
 }



很多人对于 如何模拟 CTRL + SPACE 感兴趣, 下面也给个例子,呵呵。

void simulate_ctrl_space(int fd)
 {
         struct input_event event;

      //先发送一个 CTRL 按下去的事件。
         event.type = EV_KEY;
         event.value = 1;
         event.code = KEY_LEFTCTRL;
         gettimeofday(&event.time,0);
         write(fd,&event,sizeof(event)) ;

         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));

      //先发送一个 SPACE 按下去的事件。
         event.type = EV_KEY;
         event.value = 1;
         event.code = KEY_SPACE;
         gettimeofday(&event.time,0);
         write(fd,&event,sizeof(event)) ;

      //发送一个 释放 SPACE 的事件
         memset(&event, 0, sizeof(event));
         gettimeofday(&event.time, NULL);
         event.type = EV_KEY;
         event.code = KEY_SPACE;
         event.value = 0;
         write(fd, &event, sizeof(event));

         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));


      //发送一个 释放 CTRL 的事件
         memset(&event, 0, sizeof(event));
         gettimeofday(&event.time, NULL);
         event.type = EV_KEY;
         event.code = KEY_LEFTCTRL;
         event.value = 0;
         write(fd, &event, sizeof(event));


         event.type = EV_SYN;
         event.code = SYN_REPORT;
         event.value = 0;
         write(fd, &event, sizeof(event));

 }





接下来分析一下 uinput 和 linux 的 input 子系统。