2013-06-14 23:58:33 dayong419 阅读数 1766
  • 一学就会——电脑安装linux虚拟

    在Linux环境下学习编程开发,运维管理,都需要用到Linux系统。 可以安装Linux、Windows双系统,也可以将电脑重装为Linux系统,最简单的方法就是安装一个Linux虚拟机。 用的时候打开虚拟机,不用的时候关闭虚拟机。 跟QQ一样,用的时候开启QQ,不用就关掉QQ。 不会装Linux虚拟机? 没关系,本课程详细介绍了如何Windows 10安装Linux虚拟机。其他的Windows 8, Windows 7也是一样的,都可以安装。

    1432 人正在学习 去看看 谢昆明

    最近看了些linux模拟鼠标按键的方法,但是都要从/proc/bus/input/devices中去查看现有的鼠标设备,然后重用该设备,比较麻烦,而且不是很好——可能对应不同硬件要作一定改动。后来学习发现可以让用户态程序利用uinput的输入子系统来创建一个自己的虚拟设备,然后再向我们的这个虚拟设备写入事件,最后证明也能够实现功能。

    我对于Linux算是个入门汉,有些地方理解不够,有什么错误欢迎大家指正。~~


下面是我实现虚拟设备和模拟鼠标移动的代码:

#include <sys/ioctl.h>
#include <linux/uinput.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>

#define ALOGE(...) \
        printf(__VA_ARGS__); \
        printf("\n")


#define UINPUT_NAME      "/dev/uinput"
#define VIR_MOUSE_NAME   "DFC Virtual Mouse"

static int device_handler = -1;
static struct uinput_user_dev vir_mouse;

static int install_uinput_mouse_device()
{
    if (device_handler > 0)
    {
        ALOGE("virtual mouse has been installed already.");
        return device_handler;
    }

    //open uinput device
    if ((device_handler = open(UINPUT_NAME, O_WRONLY | O_NDELAY)) <= 0)
    {
        ALOGE("open uinput(%s) failed.", UINPUT_NAME);
        device_handler = -1;
        return device_handler;
    }

    //setup our uinput device of virtual mouse
    memset(&vir_mouse, 0, sizeof(struct uinput_user_dev));
    strncpy(vir_mouse.name, VIR_MOUSE_NAME, UINPUT_MAX_NAME_SIZE);
    vir_mouse.id.version = 4;
    vir_mouse.id.bustype = BUS_USB;

    //if we don't do these register, it seems like not to work
    ioctl(device_handler, UI_SET_EVBIT, EV_KEY);
    for (int i = 0; i < 256; i++)
    {
        ioctl(device_handler, UI_SET_KEYBIT, i);
    }

    //setup mouse coordinate event
    ioctl(device_handler, UI_SET_EVBIT,  EV_REL);
    ioctl(device_handler, UI_SET_RELBIT, REL_X);
    ioctl(device_handler, UI_SET_RELBIT, REL_Y);

    //setup mouse button event
    ioctl(device_handler, UI_SET_KEYBIT, BTN_MOUSE);
    ioctl(device_handler, UI_SET_KEYBIT, BTN_TOUCH);
    ioctl(device_handler, UI_SET_KEYBIT, BTN_LEFT);
    ioctl(device_handler, UI_SET_KEYBIT, BTN_RIGHT);
    ioctl(device_handler, UI_SET_KEYBIT, BTN_MIDDLE);

    //create input device into input subsystem
    write(device_handler, &vir_mouse, sizeof(vir_mouse));
    if (ioctl(device_handler, UI_DEV_CREATE))
    {
        ALOGE("install mouse uinput device failed with error(%s).", strerror(errno));
        close(device_handler);
        device_handler = -1;
    }

    return device_handler;
}

static void uninstall_uinput_mouse_device()
{
    if (device_handler > 0)
    {
        ioctl(device_handler, UI_DEV_DESTROY);
        close(device_handler);
        device_handler = ERROR_DEVICE_HANDLER;
    }
}

void mouse_move(struct data* data)
{
    if (device_handler <= 0)
    {
        install_uinput_mouse_device();
    }

    if (device_handler > 0)
    {
        struct input_event event;
        memset(&event, 0, sizeof(event));

        //x coordinate
        gettimeofday(&event.time, 0);
        event.type = EV_REL;
        event.code = REL_X;
        event.value = data->x;
        write(device_handler, &event, sizeof(event));

        //y coordinate
        event.type  = EV_REL;
        event.code  = REL_Y;
        event.value = data->y;
        write(device_handler, &event, sizeof(event));

        //execute move event
        event.type  = EV_SYN;
        event.code  = SYN_REPORT;
        event.value = 0;
        write(device_handler, &event, sizeof(event));
    }
    else
    {
        ALOGE("invalid device file handler.");
    }
}


下面的代码是测试鼠标移动的代码:

#include <unistd.h>

int main(int argc, char** argv)
{
    struct data data;
    data.x = 2;
    data.y = 2;
    while (1)
    {
        mouse_move(&data);
        sleep(1);
    }

    return 0;
}

然后执行:gcc -o uinput-deamo main.c my_virtual_mouse.c

sudo ./uinput-deamo

我们可以看到鼠标会在每秒移动以下鼠标。查看/proc/bus/input/devices可以看到我们添加的设备。

2009-12-18 12:13:00 pottichu 阅读数 6490
  • 一学就会——电脑安装linux虚拟

    在Linux环境下学习编程开发,运维管理,都需要用到Linux系统。 可以安装Linux、Windows双系统,也可以将电脑重装为Linux系统,最简单的方法就是安装一个Linux虚拟机。 用的时候打开虚拟机,不用的时候关闭虚拟机。 跟QQ一样,用的时候开启QQ,不用就关掉QQ。 不会装Linux虚拟机? 没关系,本课程详细介绍了如何Windows 10安装Linux虚拟机。其他的Windows 8, Windows 7也是一样的,都可以安装。

    1432 人正在学习 去看看 谢昆明

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 子系统。

2017-05-21 12:11:42 jklinux 阅读数 1823
  • 一学就会——电脑安装linux虚拟

    在Linux环境下学习编程开发,运维管理,都需要用到Linux系统。 可以安装Linux、Windows双系统,也可以将电脑重装为Linux系统,最简单的方法就是安装一个Linux虚拟机。 用的时候打开虚拟机,不用的时候关闭虚拟机。 跟QQ一样,用的时候开启QQ,不用就关掉QQ。 不会装Linux虚拟机? 没关系,本课程详细介绍了如何Windows 10安装Linux虚拟机。其他的Windows 8, Windows 7也是一样的,都可以安装。

    1432 人正在学习 去看看 谢昆明

通过http://blog.csdn.net/jklinux/article/details/72570663博文可以得知:

一个输入设备获取到的数据是以struct input_event为单位的。
而且鼠标的相对坐标事件中的type值为EV_REL时, code的值为REL_X时, value的正值表示右移,负值表示左移, 移动量为value
code的值为REL_Y时, value的正值表示下移,负值表示上移,移动量为value

鼠标的按键事件中的type为EV_KEY时, code的值表示左(BTN_LEFT),中(BTN_MIDDLE), 右键(BTN_RIGHT)
value的值表示相应的键是按下或松手

输入事件的数据处理流程:

硬件设备 --> 设备驱动检测硬件并提交数据  <--> linux内核 <---> 系统调用 <--> 用户程序

而且linux里的输入设备驱动除了检测硬件产生相应的事件数据以外,还可以接收用户提交的事件数据(也就是用户程序里填好数据的input_event).
虚拟的输入设备就可以使用这种接口来实现。

如用键盘的方向代替鼠标的移动, 用’l’键代替鼠标的左键, ‘m’代替中键, ‘r’代替右键, ‘q’键退出
代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/input.h>

void handle_rel(int dev_fd); //处理坐标事件
int main(int argc, char *argv[])
{
    int fd, ch; 
    struct input_event evt, evt_syn = {EV_SYN};

    if (argc < 2)
    {
        printf("usage: %s  mouse_device_file \n", argv[0]);
        return 1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
        printf("open %s failed\n", argv[1]);
        return 1;
    }

    system("stty raw -echo"); //用于无须确认键,直接接收用户的按键

    while (1)
    {
        ch = getchar();
        if (('q' == ch) || ('Q' == ch)) 
            break;

        memset(&evt, 0, sizeof(evt));
        evt.type = EV_KEY;
        switch (ch)
        {
        case 'l':
            evt.code = BTN_LEFT;
            break;
        case 'r':
            evt.code = BTN_RIGHT;
            break;
        case 'm':
            evt.code = BTN_MIDDLE;
            break;
        case '\033':  //方向键
            evt.type = EV_REL;
            handle_rel(fd);
            break;  
        }
        if ((EV_KEY == evt.type) && evt.code) //按键事件处理
        {
            evt.value = 1; //按下
            write(fd, &evt, sizeof(evt)); //键按下的事件
            write(fd, &evt_syn, sizeof(evt_syn)); //提交事件

            evt.value = 0;
            write(fd, &evt, sizeof(evt)); //键松手的事件
            write(fd, &evt_syn, sizeof(evt_syn)); //提交事件
        }   
    }
    system("stty -raw echo"); //恢复终端属性
    return 0;
}

#define  VAL   10   //移动量的单位大小
void handle_rel(int dev_fd)
{
    int ch;
    struct input_event evt;

    evt.type = EV_REL;

    ch = getchar();
    if ('[' != ch)
        return;

    ch = getchar();
    switch (ch)
    {
    case 'A':  // up
        evt.code = REL_Y;
        evt.value = -VAL;
        break;
    case 'B': // down
        evt.code = REL_Y;
        evt.value = VAL;
        break;
    case 'C':  // right
        evt.code = REL_X;
        evt.value = VAL;
        break;
    case 'D': // left
        evt.code = REL_X;
        evt.value = -VAL;
        break;
    default:
        return;
    }

    write(dev_fd, &evt, sizeof(evt)); //提交相对坐标事件
    memset(&evt, 0, sizeof(evt));
    evt.type = EV_SYN; 
    write(dev_fd, &evt, sizeof(evt)); //提交事件

}

基于上例可以扩展为接收网络传输过来的数据再产生相应的输入事件,即可实现网络输入设备的功能。
另如果系统里没有相应的输入设备, 可以自己写个输入设备驱动用于支持虚拟输入设备功能。

2018-11-11 21:48:24 imred 阅读数 1265
  • 一学就会——电脑安装linux虚拟

    在Linux环境下学习编程开发,运维管理,都需要用到Linux系统。 可以安装Linux、Windows双系统,也可以将电脑重装为Linux系统,最简单的方法就是安装一个Linux虚拟机。 用的时候打开虚拟机,不用的时候关闭虚拟机。 跟QQ一样,用的时候开启QQ,不用就关掉QQ。 不会装Linux虚拟机? 没关系,本课程详细介绍了如何Windows 10安装Linux虚拟机。其他的Windows 8, Windows 7也是一样的,都可以安装。

    1432 人正在学习 去看看 谢昆明

在Linux下创建一个虚拟鼠标设备还是比较简单的,使用内核uinput模块提供的函数即可。创建出虚拟鼠标以后,在主线程监听键盘的事件,当特定的键(此处使用了小键盘的数字键8、2、4、6)被按下或弹起后,进行记录。在另一个线程根据主线程记录的flag创建输入事件,然后将输入事件写入虚拟鼠标设备即可。
在实现程序时一个让我思考时间比较长的问题是:是否需要另外创建一个线程来写虚拟鼠标设备。当一个键被按下后,我们需要不断的写向相应方向移动的事件,这里是需要定时循环写的。如果不另开线程的话,我们需要在这个循环中先使用select或poll读键盘事件,可能等到了事件,也可能等不到事件;然后对相应的键进行记录;然后写虚拟鼠标设备;最后睡眠一定的时间,睡眠的时间是受前面等待事件的时间影响的,这样才能使得每个循环花费时间是相同的。这样做应该也行得通,但我觉得不如另开一个线程,在两个循环里分别读写事件实现起来比较方便。不过另开线程需要为线程安全做一点小工作,原因是有些flag可能需要在两个线程里写,使用标准库提供的atomic模板就能做到。
原本以为没几行代码,没想到写着写着就长了,功能也不太全,没有实现左右按键的功能,不过好歹能直接跑起来,有需要的时候进行扩展就是了。实现代码时主要参考了这些资料:
https://www.kernel.org/doc/html/v4.12/input/uinput.html
https://cgit.freedesktop.org/evtest
代码地址为:https://github.com/im-red/virtualmouse
也把代码贴在这里:

#include <err.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/uinput.h>
#include <string.h>
#include <dirent.h>
#include <vector>
#include <string>
#include <iostream>
#include <poll.h>
#include <signal.h>
#include <pthread.h>
#include <atomic>

#define STR(X) STR2(X)
#define STR2(X) #X

#define ENSURE(CONDITION, ...) if (!(CONDITION)) { err(EXIT_FAILURE, __FILE__ ":" STR(__LINE__) " " __VA_ARGS__); }

static const char *EVENT_DEV_NAME = "event";
static const char *EVENT_DEV_PREFIX = "/dev/input/";
static const char *UINPUT_NAME = "/dev/uinput";

static const int BITS_PER_LONG = sizeof(long) * 8;

inline int nbytes(int x)
{
    return (x - 1) / BITS_PER_LONG + 1;
}

inline bool testBits(int bit, const unsigned long array[])
{
    unsigned long result = array[bit / BITS_PER_LONG] >> bit % BITS_PER_LONG & 1;
    return (result == 1);
}

static int isEventDevice(const struct dirent *dir) 
{
	return strncmp(EVENT_DEV_NAME, dir->d_name, strlen(EVENT_DEV_NAME)) == 0;
}

static void createVirtualMouse(int fd)
{
    // enable mouse button left and relative events
    ioctl(fd, UI_SET_EVBIT, EV_KEY);
    ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);

    ioctl(fd, UI_SET_EVBIT, EV_REL);
    ioctl(fd, UI_SET_RELBIT, REL_X);
    ioctl(fd, UI_SET_RELBIT, REL_Y);

    struct uinput_user_dev uud;
    memset(&uud, 0, sizeof(uud));

    snprintf(uud.name, UINPUT_MAX_NAME_SIZE, "Virtual Mouse");

    int ret = write(fd, &uud, sizeof(uud));
    ENSURE(ret == sizeof(uud));

    ioctl(fd, UI_DEV_CREATE);
}

static std::vector<std::string> getAllEventDevicePath()
{
    struct dirent **direntList;

    int ndev = scandir(EVENT_DEV_PREFIX, &direntList, isEventDevice, versionsort);
    ENSURE(ndev > 0, "No event device found");

    std::vector<std::string> result(ndev);

    for (int i = 0; i < ndev; i++)
    {
        result[i] = std::string(EVENT_DEV_PREFIX) + std::string(direntList[i]->d_name);
    }
    return std::move(result);
}

// A simple RAII class
class FileOpener
{
public:
    explicit FileOpener(const char *pathname, int flags)
    {
        fd = open(pathname, flags);
    }

    explicit FileOpener(const char *pathname, int flags, mode_t mode)
    {
        fd = open(pathname, flags, mode);
    }

    ~FileOpener()
    {
        if (fd >= 0)
        {
            close(fd);
        }
    }

    int getFd()
    {
        return fd;
    }

private:
    int fd;
};

static bool isSupportType(const std::string &path, int type)
{
    FileOpener opener(path.c_str(), O_RDONLY);
    int fd = opener.getFd();
    if (fd < 0)
    {
        return false;
    }

    unsigned long supportType[nbytes(EV_MAX)];
    ioctl(fd, EVIOCGBIT(0, EV_MAX), supportType);
    if (!testBits(type, supportType))
    {
        return false;
    }

    return true;
}

static bool isSupportTypeCode(const std::string &path, int type, int code)
{
    if (!isSupportType(path, type))
    {
        return false;
    }

    FileOpener opener(path.c_str(), O_RDONLY);
    int fd = opener.getFd();
    if (fd < 0)
    {
        return false;
    }

    unsigned long supportCode[nbytes(KEY_MAX)];
    ioctl(fd, EVIOCGBIT(type, KEY_MAX), supportCode);
    if (!testBits(code, supportCode))
    {
        return false;
    }
    return true;
}

static const int NEEDED_KEY[] = { KEY_KP8, KEY_KP2, KEY_KP4, KEY_KP6 };
// device reports KEY_KP8 KEY_KP2 KEY_KP4 KEY_KP6 is valid keyboard
static bool isValidKeyboard(const std::string &path)
{
    for (unsigned int i = 0; i < (sizeof(NEEDED_KEY) / sizeof(int)); i++)
    {
        if (!isSupportTypeCode(path, EV_KEY, NEEDED_KEY[i]))
        {
            return false;
        }
    }
    return true;
}

static std::vector<std::string> getAllValidKeyboard()
{
    std::vector<std::string> validDevice;
    std::vector<std::string> allDevice = getAllEventDevicePath();

    for (unsigned int i = 0; i < allDevice.size(); i++)
    {
        if (isValidKeyboard(allDevice[i]))
        {
            validDevice.push_back(allDevice[i]);
        }
    }

    return validDevice;
}

static std::string getOneNumLockDevice()
{
    std::vector<std::string> allDevice = getAllEventDevicePath();

    for (unsigned int i = 0; i < allDevice.size(); i++)
    {
        if (isSupportTypeCode(allDevice[i], EV_LED, LED_CAPSL))
        {
            return allDevice[i];
        }
    }
    return std::string("");
}

static int uinputFd = 0;

// program should exit
static volatile bool shouldStop = false;

// if numlock is on, we don't move mouse
static bool numlockOn = false;

const static int UP = 0;
const static int DOWN = 1;
const static int LEFT = 2;
const static int RIGHT = 3;
const static int DIRECTION_NUM = 4;

// move status
// change to true/false when corresponding key is pressed/released
static bool isMoving[DIRECTION_NUM] = { false };

// move times from the point that corresponding key is pressed
// reset to 0 when key is released
static std::atomic_int moveTimes[DIRECTION_NUM];

inline void resetMoveTimes()
{
    for (int i = 0; i < DIRECTION_NUM; i++)
    {
        moveTimes[i] = 0;
    }
}

// move step is affected by move times
// so we can apply some acceleration strategy
static int moveStep(int times)
{
    const int MIN_STEP = 1;
    const int MAX_STEP = 10;
    const int MIN_POINT = 50;
    const int MAX_POINT = 200;

    if (times <= MIN_POINT)
    {
        return MIN_STEP;
    }
    else if (times <= MAX_POINT)
    {
        return MIN_STEP + (times - MIN_POINT) * 1.0 / (MAX_POINT - MIN_POINT) * (MAX_STEP - MIN_STEP);
    }
    else
    {
        return MAX_STEP;
    }
}

// write mouse move event every timeInterval ms
static int timeInterval = 10;

static void setIsMoving(int index, int value)
{
    isMoving[index] = value;
    if (!value)
    {
        moveTimes[index] = 0;
    }
}

static void keyAction(int index, int value)
{
    ENSURE(index >= 0 && index < DIRECTION_NUM);
    if (value == 1)
    {
        setIsMoving(index, true);
    }
    else if (value == 0)
    {
        setIsMoving(index, false);
    }
    else
    {
        // do nothing
    }
}

static void handleKeyEvent(const struct input_event &ev)
{
    if (numlockOn)
    {
        return;
    }

    int index = -1;
    switch (ev.code)
    {
    case KEY_KP8: index = UP; break;
    case KEY_KP2: index = DOWN; break;
    case KEY_KP4: index = LEFT; break;
    case KEY_KP6: index = RIGHT; break;
    default: index = -1; break;
    }
    if (index != -1)
    {
        keyAction(index, ev.value);
    }
}

static void handleLEDEvent(const struct input_event &ev)
{
    if (ev.code == LED_NUML)
    {
        if (ev.value == 0)
        {
            numlockOn = false;
        }
        else if (ev.value == 1)
        {
            numlockOn = true;
            for (int i = 0; i < DIRECTION_NUM; i++)
            {
                setIsMoving(i, false);
            }
        }
    }
}

static void handleEvent(const struct input_event &ev)
{
    if (ev.type == EV_KEY)
    {
        handleKeyEvent(ev);
    }
    else if (ev.type == EV_LED)
    {
        handleLEDEvent(ev);
    }
}

static void handleDevice(int fd)
{
    struct input_event ev[64];
    int rd = read(fd, ev, sizeof(ev));
    ENSURE(rd % sizeof(struct input_event) == 0, "expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd % (int) sizeof(struct input_event));
    int n = rd / sizeof(struct input_event);
    for (int j = 0; j < n; j++)
    {
        handleEvent(ev[j]);
    }
}

static void interrupt_handler(int sig)
{
    (void) sig;
	shouldStop = true;
}

static bool queryNumlock()
{
    std::string ledDevice = getOneNumLockDevice();

    // if there is no led device, we suppose that numlock is always off
    if (ledDevice == std::string(""))
    {
        return false;
    }

    FileOpener opener(ledDevice.c_str(), O_RDONLY);

    int fd = opener.getFd();
    ENSURE(fd >= 0);

    unsigned long supportLED[nbytes(LED_MAX)];
    ioctl(fd, EVIOCGLED(sizeof(supportLED)), supportLED);
    if (testBits(LED_NUML, supportLED))
    {
        return true;
    }
    else
    {
        return false;
    }
}

static void incMoveTimes()
{
    for (int i = 0; i < DIRECTION_NUM; i++)
    {
        if (isMoving[i])
        {
            moveTimes[i]++;
        }
    }
}

static void calcMoveSteps(int *steps)
{
    for (int i = 0; i < DIRECTION_NUM; i++)
    {
        if (isMoving[i])
        {
            steps[i] = moveStep(moveTimes[i]);
        }
    }

    // if opposite key is pressed, we set the steps all to 0
    if (isMoving[UP] && isMoving[DOWN])
    {
        steps[UP] = 0;
        steps[DOWN] = 0;
    }
    if (isMoving[LEFT] && isMoving[RIGHT])
    {
        steps[LEFT] = 0;
        steps[RIGHT] = 0;
    }
}

// ok, let's write device to move the mouse
static void writeDevice(int *steps)
{
    int x = steps[RIGHT] - steps[LEFT];
    int y = steps[DOWN] - steps[UP];

    struct input_event ev[3];
    memset(ev, 0, sizeof(ev));

    ev[0].type = EV_REL;
    ev[0].code = REL_X;
    ev[0].value = x;

    ev[1].type = EV_REL;
    ev[1].code = REL_Y;
    ev[1].value = y;

    ev[2].type = EV_SYN;
    ev[2].code = SYN_REPORT;

    int ret = write(uinputFd, ev, sizeof(ev));
    ENSURE(ret == sizeof(ev));
}

static void moveMouse()
{
    incMoveTimes();
    int steps[DIRECTION_NUM] = { 0 };
    calcMoveSteps(steps);
    writeDevice(steps);
}

static void *moveMouseThread(void *arg)
{
    (void) arg;

    while(true)
    {
        moveMouse();
        usleep(timeInterval * 1000);
    }

    return nullptr;
}

int main()
{
    uinputFd  = open(UINPUT_NAME, O_WRONLY);
    ENSURE(uinputFd >= 0);

    createVirtualMouse(uinputFd);

    numlockOn = queryNumlock();

    std::vector<std::string> kbds = getAllValidKeyboard();

    ENSURE(kbds.size() >= 1, "There should be at least one keyboard");

    struct pollfd fds[kbds.size()];
    memset(fds, 0, sizeof(fds));

    for (unsigned int i = 0; i < kbds.size(); i++)
    {
        fds[i].fd = open(kbds[i].c_str(), O_RDONLY);
        ENSURE(fds[i].fd >= 0, "Open keyboard %s failed", kbds[i].c_str());
        fds[i].events = POLLIN;
    }

    signal(SIGINT, interrupt_handler);
	signal(SIGTERM, interrupt_handler);

    pthread_t tid;
    int ret = pthread_create(&tid, nullptr, moveMouseThread, nullptr);
    ENSURE(ret == 0);

    resetMoveTimes();

    while (!shouldStop)
    {
        for (unsigned int i = 0; i < kbds.size(); i++)
        {
            fds[i].revents = 0;
        }
        poll(fds, kbds.size(), -1);
        for (unsigned int i = 0; i < kbds.size(); i++)
        {
            if (fds[i].revents & POLLIN)
            {
                handleDevice(fds[i].fd);
            }
        }
    }

    for (unsigned int i = 0; i < kbds.size(); i++)
    {
        close(fds[i].fd);
    }
    return 0;
}
2010-03-30 16:19:00 saturnbj 阅读数 1431
  • 一学就会——电脑安装linux虚拟

    在Linux环境下学习编程开发,运维管理,都需要用到Linux系统。 可以安装Linux、Windows双系统,也可以将电脑重装为Linux系统,最简单的方法就是安装一个Linux虚拟机。 用的时候打开虚拟机,不用的时候关闭虚拟机。 跟QQ一样,用的时候开启QQ,不用就关掉QQ。 不会装Linux虚拟机? 没关系,本课程详细介绍了如何Windows 10安装Linux虚拟机。其他的Windows 8, Windows 7也是一样的,都可以安装。

    1432 人正在学习 去看看 谢昆明

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));

}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/pottichu/archive/2009/12/18/5030860.aspx

没有更多推荐了,返回首页