2016-11-18 8 views
0

리눅스에서 키 보드 에뮬레이터 프로그램을 작성 중입니다. 처음에는 키 스트로크를 X11 창에 렌더링 할 수 있었지만 가상 터미널에서는 작동하지 않고 다른 방법. 나는 http://thiemonge.org/getting-started-with-uinput을 참조하고 uinput 커널 모듈을 사용해 보았습니다. 튜토리얼에 따르면 키 스트로크는 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); 

} 

은, 내가 무엇을 시도하고 키 스트로크 'D'에 대한 uinput 이벤트를 생성하는 것입니다. 그러나 프로그램 실행으로 나는 아무것도 볼 수 없다. 누군가이 프로그램을 확인할 수 있도록 도와 줄 수 있습니까? 튜토리얼에서도 uinput 서브 시스템을 사용하여 키 스트로크를 주입하는 방법은 명확하지 않습니다.

편집 : 다른 프로그램을 작성했지만 결과가 표시되지 않습니다. 나는 길을 잃었고 어떤 도움을 주셔서 감사합니다.

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

답변

0

위의 2 가지 프로그램에는 아무런 문제가 없습니다. 여기서 문제는이 프로그램이 (아마도 "/ dev/input"의 어딘가에) 새로운 입력 장치를 만들고 X가 입력을 가져올 수 있도록 입력 장치를 충분히 빠르게 등록 할 수 없다는 것입니다. 간단한 "잠 (1);" (적절한 "#include"와 함께) 입력 장치를 만든 후에 문제가 해결됩니다. 코드를 변경 한 후에는 X가 새로운 입력 장치가 있음을 알기에 충분한 시간이 있었기 때문에 프로그램이 예상대로 작동합니다 (아마 불필요합니다). 아래 코드 세그먼트 뒤에 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. 

이제 프로그램이 예상대로 작동합니다. 동일한 현상이 http://www.linuxforums.org/forum/ubuntu-linux/161718-its-no-effect-when-using-uinput.html에서 발견되었습니다.