简体   繁体   English

如何使用输入子系统生成击键事件

[英]How to generate key strokes events with the Input Subsystem

I am writing a Key board emulator program in Linux, As a start I was able to render key strokes into X11 window but this is not working in virtual terminals and try out a different way.我正在 Linux 中编写键盘仿真器程序,作为开始,我能够将击键渲染到 X11 window 中,但这不适用于虚拟终端并尝试不同的方式。 I referred to http://thiemonge.org/getting-started-with-uinput and tried with uinput kernel module.我参考了http://thiemonge.org/getting-started-with-uinput并尝试使用 uinput kernel 模块。 According to the tutorial key strokes can be injected as a uinput event and I wrote below code accordingly.根据教程,击键可以作为 uinput 事件注入,我相应地编写了下面的代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <iostream>

#include <time.h>
#include <string>
#define die(str, args...) do { \
perror(str); \
exit(EXIT_FAILURE); \
} while(0)

int main(void)
{
    int                    fd_keyEmulator;
    struct uinput_user_dev uidev;
    struct input_event     ev;
    int                    dx, dy;
    int                    i;

    fd_keyEmulator = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if(fd_keyEmulator < 0)
    {
        std::cout << "error: open : " <<  strerror(errno) << std::endl;
    }

    int ret;
    //ret = ioctl(fd_keyEmulator, UI_SET_EVBIT, EV_KEY);
    //ret = ioctl(fd_keyEmulator, UI_SET_KEYBIT, KEY_D);
    //ret = ioctl(fd_keyEmulator, UI_SET_EVBIT, EV_SYN);
    sleep(5);
    if (ioctl(fd_keyEmulator, UI_SET_EVBIT, EV_KEY) < 0)
    {
        std::cout << "test 1 ..." << std::endl;
        die("error: ioctl");
    }
    if (ioctl(fd_keyEmulator, UI_SET_KEYBIT, KEY_D) < 0)
    {
        std::cout << "test 2 ..." << std::endl;
        die("error: ioctl");
    }
    if (ioctl(fd_keyEmulator, UI_SET_EVBIT, EV_REL) < 0)
    {
        std::cout << "test 3 ..." << std::endl;
        die("error: ioctl");
    }


    memset(&uidev, 0, sizeof(uidev));
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "keyboard-emulator");
    uidev.id.bustype = BUS_USB;
    uidev.id.vendor  = 0x1;
    uidev.id.product = 0x1;
    uidev.id.version = 1;

    std::cout << "Writing key press..." << std::endl;
    if(write(fd_keyEmulator, &uidev, sizeof(uidev)) < 0)
        std::cout << "error: write" <<  strerror(errno) << std::endl;

    if(ioctl(fd_keyEmulator, UI_DEV_CREATE) < 0)
        std::cout << "error: ioctl" <<  strerror(errno) << std::endl;


    memset(&ev, 0, sizeof(ev));
    ev.type = EV_REL;
    ev.code = KEY_D;
    ev.value = 1;

    //ret = write(fd_keyEmulator, &ev, sizeof(ev));
    if (write(fd_keyEmulator, &ev, sizeof (struct input_event)) < 0)
                die("error: write");
    if (write(fd_keyEmulator, &ev, sizeof (struct input_event)) < 0)
                die("error: write");
    if (write(fd_keyEmulator, &ev, sizeof (struct input_event)) < 0)
                die("error: write");
    if (write(fd_keyEmulator, &ev, sizeof (struct input_event)) < 0)
                die("error: write");


    if(ioctl(fd_keyEmulator, UI_DEV_DESTROY) < 0)
        std::cout << "error: ioctl" <<  strerror(errno) << std::endl;

    close(fd_keyEmulator);

}

in this case, what I am trying is to generate a uinput events for key stroke 'd'.在这种情况下,我正在尝试为击键“d”生成一个 uinput 事件。 but with the program execution I cannot see anything.但是随着程序的执行,我什么也看不到。 can somebody help me to verify this program.有人可以帮我验证这个程序。 It is not clear how to inject a key stroke with uinput subsystem in the tutorial as well.在本教程中也不清楚如何使用 uinput 子系统注入击键。

EDIT: I wrote a different program, but I cannot see any output.编辑:我写了一个不同的程序,但我看不到任何 output。 I got lost and any help appreciate.我迷路了,任何帮助表示赞赏。

#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <iostream>

#include <time.h>
#include <string>

using namespace std;

/*
 * 
 */
int main(int argc, char** argv) {

    // create uinput file descriptor
    int fd_key_emulator;                                                                    

    // open file descriptor
    fd_key_emulator = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if(fd_key_emulator < 0)
    {
        std::cout << "error in open : " << strerror(errno) << std::endl;
    }

    // uinput_user_dev struct for fake keyboard
    struct uinput_user_dev dev_fake_keyboard;
    memset(&dev_fake_keyboard, 0, sizeof(uinput_user_dev));
    snprintf(dev_fake_keyboard.name, UINPUT_MAX_NAME_SIZE, "kb-emulator");
    dev_fake_keyboard.id.bustype = BUS_USB;
    dev_fake_keyboard.id.vendor = 0x01;
    dev_fake_keyboard.id.product = 0x01;
    dev_fake_keyboard.id.version = 1;



    /**configure the input device to send type of events, inform to subsystem which 
     * type of input events we are using via ioctl calls. 
     * UI_SET_EVBIT ioctl request is used to applied on uinput descriptor to enable a type of event.
     **/
    // enable key press/release event
    if(ioctl(fd_key_emulator, UI_SET_EVBIT, EV_KEY))
    {
        std::cout << "Error in ioctl : UI_SET_EVBIT : EV_KEY : " << strerror(errno) << std::endl;
    }

    // enable set of KEY events here
    if(ioctl(fd_key_emulator, UI_SET_KEYBIT, KEY_A))
    {
        std::cout << "Error in ioctl : UI_SET_KEYBIT : KEY_A : " << strerror(errno) << std::endl;
    }

    // enable synchronization event
    if(ioctl(fd_key_emulator, UI_SET_EVBIT, EV_SYN))
    {
        std::cout << "Error in ioctl : UI_SET_EVBIT : EV_SYN : " << strerror(errno) << std::endl;
    }

    // now write the uinput_user_dev structure into uinput file descriptor
    if(write(fd_key_emulator, &dev_fake_keyboard, sizeof(uinput_user_dev)) < 0)
    {
        std::cout << "Error in write(): uinput_user_dev struct into uinput file descriptor: " << strerror(errno) << std::endl;
    }

    // create the device via an IOCTL call 
    if(ioctl(fd_key_emulator, UI_DEV_CREATE))
    {
        std::cout << "Error in ioctl : UI_DEV_CREATE : " << strerror(errno) << std::endl;
    }
    // now fd_key_emulator represents the end-point file descriptor of the new input device. 


    // struct member for input events
    struct input_event key_input_event;
    memset(&key_input_event, 0, sizeof(input_event));

    // key press event for 'a'
    key_input_event.type = EV_KEY;
    key_input_event.code = KEY_A;
    key_input_event.value = 1;

    // now write to the file descriptor
    if(write(fd_key_emulator, &key_input_event, sizeof(input_event)) < 0)
    {
        std::cout << "Error write : KEY_A press : " << strerror(errno) << std::endl;
    }

    memset(&key_input_event, 0, sizeof(input_event));
    // EV_SYN for key press event
    key_input_event.type = EV_SYN;
    key_input_event.code = SYN_REPORT;
    key_input_event.value = 0;

    // now write to the file descriptor
    if(write(fd_key_emulator, &key_input_event, sizeof(input_event)) < 0)
    {
        std::cout << "Error write : EV_SYN for key press : " << strerror(errno) << std::endl;
    }

    memset(&key_input_event, 0, sizeof(input_event));

    // key release event for 'a'
    key_input_event.type = EV_KEY;
    key_input_event.code = KEY_A;
    key_input_event.value = 0;

    // now write to the file descriptor
    if(write(fd_key_emulator, &key_input_event, sizeof(input_event)) < 0)
    {
        std::cout << "Error write : KEY_A release : " << strerror(errno) << std::endl;
    }

    memset(&key_input_event, 0, sizeof(input_event));
    // EV_SYN for key press event
    key_input_event.type = EV_SYN;
    key_input_event.code = SYN_REPORT;
    key_input_event.value = 0;

    // now write to the file descriptor
    if(write(fd_key_emulator, &key_input_event, sizeof(input_event)) < 0)
    {
        std::cout << "Error write : EV_SYN for key release : " << strerror(errno) << std::endl;
    }

    return 0;
}

There is nothing wrong with above 2 programs.以上2个程序没有错。 The problem here is, that this program creates a new input device (probably somewhere in "/dev/input") and that X cannot register the input device fast enough so that it can fetch the input.这里的问题是,这个程序创建了一个新的输入设备(可能在“/dev/input”中的某个地方),并且 X 无法足够快地注册输入设备,以便它可以获取输入。 A simple "sleep(1);"一个简单的“sleep(1);” (with the appropriate "#include <unistd.h>") after the creation of the input device solves the problem. (使用适当的“#include <unistd.h>”)创建输入设备后解决了问题。 To be (probably unnecessary) specific, after changing the code the program will behave as expected because now X had ample time to realize there's a new input device.具体来说(可能是不必要的),在更改代码后,程序将按预期运行,因为现在 X 有足够的时间来实现新的输入设备。 Add the sleep() statement after below code segment.在下面的代码段之后添加 sleep() 语句。

    // create the device via an IOCTL call 
    if(ioctl(fd_key_emulator, UI_DEV_CREATE))
    {
        std::cout << "Error in ioctl : UI_DEV_CREATE : " << strerror(errno) << std::endl;
    }
    // add 1 second sleep.
    sleep (1);

    // now fd_key_emulator represents the end-point file descriptor of the new input device.

now program is working as expected.现在程序按预期工作。

[Update]: Reference link is removed due to it is generated error 404, then later it is redirecting to a malicious web site. [更新]:参考链接因生成错误 404 而被删除,然后它被重定向到恶意 web 站点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM