키를 누른 시간을 기준으로 키보드 키를 다시 매핑하는 방법


9

숫자 키패드의 키를 다시 매핑하여 키를 누른 시간에 따라 다르게 동작하도록하고 싶습니다. 예를 들면 다음과 같습니다.

Numpad 9 키를 300ms 미만으로 누르고 있으면 "이전 탭"키 명령 Ctrl+Tab

Numpad 9 키를 300-599ms 동안 누르고 있으면 "새 탭"키 명령 Ctrl+T

내가 600-899ms의 숫자 패드 9 키 아래를 누르고 있으면 그것은 "탭 닫기 / 창"키 명령을 보내드립니다 Ctrl+를W

Numpad 9 키를 899ms 이상 누르고 있으면 원하는 시간 창을 놓친 경우 아무것도하지 않습니다.

Windows에서는 AutoHotKey를 사용하여이 작업을 수행 할 수 있고 OS XI에서는 ControllerMate를 사용하여이 작업을 수행 할 수 있지만 UNIX / Linux에서 키를 보유한 기간에 따라 키를 다시 매핑 할 수있는 도구를 찾을 수 없습니다.

내 문제를 해결할 수있는 도구를 알고 있다면 위에서 설명한 조건부 키 홀드 지속 시간 동작을 보여주는 스크립트 또는 코드 샘플을 제공하십시오. 내 예제를 해결하기 위해 전체 코드 일 필요는 없지만 내 예제를 위해 용도를 변경하기에 충분해야합니다.


이것은 그렇게 끔찍한 일입니다. 600 밀리 초의 프레스 시간은 어떻게 되나요? 미친 아이디어에 대한 : D +1.
와일드 카드

삶에 향신료를 더하기 위해서는 컴퓨터를 강제 종료시키는 347 ~ 350 ms의 시간 범위를 추가해야합니다. ;)
와일드 카드

@Wildcard 나는 실제로 이것을 위해 Razer Naga의 숫자 패드를 사용하고 Windows에서 AutoHotKey로 아이디어를 처음 구현했을 때 300-400ms 시간 창을 사용했지만이 시스템을 잠시 동안 사용 했으므로 사용합니다. 시간 창은 약 200ms 떨어져 있고 원하는 시간 창을 약 99 % 얻을 수 있습니다. 모스 코드와 통신하는 방식과 매우 유사합니다.
kanoko

답변:


7

방금 C로 이것을 썼습니다 :

#include <stdio.h>
#include <curses.h>
#include <time.h> //time(0)
#include <sys/time.h>                // gettimeofday()
#include <stdlib.h>

void waitFor (unsigned int secs) {
    //credit: http://stackoverflow.com/a/3930477/1074998
    unsigned int retTime = time(0) + secs;   // Get finishing time.
    while (time(0) < retTime);               // Loop until it arrives.
}

int
main(void) {

    struct timeval t0, t1, t2, t3;
    double elapsedTime;

    clock_t elapsed_t = 0;
    int c = 0x35;

    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);

    halfdelay(5); //increae the number if not working //adjust below `if (elapsedTime <= 0.n)` if this changed
    printf("\nSTART again\n");

    elapsed_t = 0;
    gettimeofday(&t0, NULL);

    float diff;

    int first = 1;
    int atleast_one = 0;

      while( getch() == c) { //while repeating same char, else(ffff ffff in my system) break

            int atleast_one = 1;

            if (first == 1) {
                gettimeofday(&t1, NULL);
                first = 0;
            }

            //printf("DEBUG 1 %x!\n", c);
            gettimeofday(&t2, NULL);
            elapsedTime = (t2.tv_sec - t1.tv_sec) + ((t2.tv_usec - t1.tv_usec)/1000000.0); 

            if (elapsedTime > 1) { //hit max time

                printf("Hit Max, quit now. %f\n", elapsedTime);
                system("gnome-terminal");
                //waitFor(4);

                int cdd;
                while ((cdd = getch()) != '\n' && cdd != EOF);
                endwin();

                exit(0);
            }

            if(halfdelay(1) == ERR) { //increae the number if not working
                //printf("DEBUG 2\n");
                //waitFor(4);
                break; 
                }
            else {
                //printf("DEBUG 3\n");
                }
        }

    if (atleast_one == 0) {
            //gettimeofday(&t1, NULL);
            t1 = t0;
    }

    gettimeofday(&t3, NULL);
    elapsedTime = (t3.tv_sec - t1.tv_sec) + ((t3.tv_usec - t1.tv_usec)/1000000.0); 
    printf("Normal quit %f\n", elapsedTime);
    if (elapsedTime > 0.6) { //this number based on halfdelay above
        system("gedit &");
        //system("xdotool key shift+left &");
        //system("mplayer -vo caca -quiet 'video.mp4' &");
        //waitFor(4);
    }
    else if (elapsedTime <= 0.6) {
        system("xdotool key ctrl+shift+t &");
        //waitFor(4);
    }

    int cdd;
    while ( (cdd = getch() ) != '\n' && cdd != EOF);
    endwin();
    return 0; 

}

showkey -a바인드 키 코드를 얻는 데 사용하십시오 .

xb@dnxb:/tmp$ sudo showkey -a

Press any keys - Ctrl-D will terminate this program

^[[24~   27 0033 0x1b #pressed F12
         91 0133 0x5b
         50 0062 0x32
         52 0064 0x34
        126 0176 0x7e
5        53 0065 0x35 #pressed Numpad 5, 5 is the keycode used in `bind`
^C        3 0003 0x03
^D        4 0004 0x04
xb@dnxb:/tmp$ 

바인드 키 코드 5와 그 명령 (예 : run /tmp/.a.out)을 ~ / .bashrc에 넣으십시오 .

bind '"5":"/tmp/a.out\n"'

관련 키 코드도 소스 코드에서 변경해야합니다 (16 진수 값 sudo showkey -a도 위에서 가져올 수 있음 ).

int c = 0x35;

( /tmp/a.out내 예제에서 출력)로 컴파일하십시오 .

cc filename.c -lcurses

데모:

숫자 키패드 5, 짧게 누르면 새 탭 열기, 중간 누르면 열려있는 gedit 및 길게 누르면 열린 그놈 터미널.

여기에 이미지 설명을 입력하십시오

이것은 gnome desktop manager의 모든 창에서 직접 적용 할 수는 없지만 구현하는 방법 (어려운)에 대한 아이디어를 제공해야한다고 생각합니다. 가상 콘솔 (Ctrl + Alt + N)에서도 작동하며 일부 터미널 에뮬레이터 (예 : konsole, gnome-terminal, xterm)에서 작동합니다.

p / s : 나는 AC 프로그래머가 아니므 로이 코드가 최적화되어 있지 않으면 용서하십시오.

[최신 정보]

이전 답변은 셸에서만 작동하고 필요한 포커스가 필요하므로 / dev / input / eventX 구문 분석은 전체 X 세션에서 작동하는 솔루션이라고 생각합니다.

바퀴를 재발 명하고 싶지 않습니다. evtest유틸리티 를 가지고 놀고 evtest.c 의 하단 부분을 내 코드로 수정했습니다 .

int onHold = 0;
struct timeval t0;
double elapsedTime;
int hitMax = 0;

while (1) {
    rd = read(fd, ev, sizeof(struct input_event) * 64);

    if (rd < (int) sizeof(struct input_event)) {
        perror("\nevtest: error reading");
        return 1;
    }

    system("echo 'running' >/tmp/l_is_running 2>/tmp/l_isrunning_E &");
    for (i = 0; i < rd / sizeof(struct input_event); i++) {

        //system("date >/tmp/l_date 2>/tmp/l_dateE &");

        if (ev[i].type == EV_KEY) {
            if ( (ev[i].code == 76) ) {

                if (!onHold) {
                    onHold = 1;
                    t0 = ev[i].time;
                    hitMax = 0;
                }
                if (!hitMax) { //to avoid hitMax still do the time checking instruction, you can remove hitMax checking if you think it's overkill, but still hitMax itself is necessary to avoid every (max) 2 seconds will repeatly system();
                    elapsedTime = (ev[i].time.tv_sec - t0.tv_sec) + ((ev[i].time.tv_usec - t0.tv_usec)/1000000.0);
                    printf("elapsedTime: %f\n", elapsedTime);
                    if (elapsedTime > 2) {
                        hitMax = 1;
                        printf("perform max time action\n");
                        system("su - xiaobai -c 'export DISPLAY=:0; gedit &'");
                    }
                }

                if (ev[i].value == 0)  {
                    printf("reseted ...... %d\n", ev[i].value);
                    onHold = 0;
                    if (!hitMax) {
                        if (elapsedTime > 1) { //just ensure lower than max 2 seconds
                            system("su - xiaobai -c 'export DISPLAY=:0; gnome-terminal &'");
                        } else if (elapsedTime > 0.5) { 
                            system("su - xiaobai -c \"export DISPLAY=:0; vlc '/home/xiaobai/Downloads/videos/test/Pokémon Red_Blue_Yellow Gym Leader Battle Theme Remix-CbJTkx7QUJU.mp4' &\"");
                        } else if  (elapsedTime > 0.2) {
                            system("su - xiaobai -c 'export DISPLAY=:0; nautilus &'");
                        }
                    } else { //else's max system() already perform
                        hitMax = 0;
                    }
                }
            }
        }
    }
}

사용자 이름 ( xiaobai 는 내 사용자 이름) 부분을 변경해야합니다 . 또한 if ( (ev[i].code == 76) ) {내 Numpad 5 키 코드입니다. ev [i] .code를 수동으로 인쇄하여 이중 확인해야 할 수도 있습니다. 물론 비디오 경로도 변경해야합니다. :)

직접 컴파일하고 테스트하십시오 (``부분은 올바른 결과를 얻습니다 /dev/input/eventN).

$ gcc /home/put_your_path/my_long_press.c -o /home/put_your_path/my_long_press; sudo /home/put_your_path/my_long_press `ls -la /dev/input/by-path/* | grep kbd |  echo "/dev/input/""$(awk -F'/' '{print $NF}')" ` &

참고 /by-id/난에 /로 경로를 변경하므로, 페도라 24 일을하지 않는 /. 칼리는 그런 문제가 없습니다.

내 데스크탑 관리자는 gdm3입니다.

$ cat /etc/X11/default-display-manager 
/usr/sbin/gdm3

따라서 /etc/gdm3/PostLogin/Defaultgdm 시작 시이 명령을 루트로 실행하기 위해이 줄을 넣었습니다 ( /etc/X11/Xsession.d/*작동하지 않음).

/home/put_your_path/my_long_press `ls -la /dev/input/by-id/* | grep kbd |  echo "/dev/input/""$(awk -F'/' '{print $NF}')" 2>/tmp/l_gdm` 2>/tmp/l_gdmE &

알 수없는 이유로 / etc/gdm/PostLogin/DefaultFedora 24 'gdm에서 작동하지 않아 로그를 확인할 때 " Permission denied "가 표시됩니다 /tmp/l_gdmE. 그래도 수동으로 아무런 문제가 없습니다.

데모:

숫자 키패드 5, 순간 누르기 (<= 0.2 초)는 무시되고, 짧게 누르기 (0.2 ~ 0.5 초) 열림 nautilus, 중간 누르기 (0.5 ~ 1 초) 열려 vlc비디오 재생, 길게 누르기 (1 ~ 2 초) 을 열고 gnome-terminal타임 아웃 (2 초)을 엽니 다 gedit.

여기에 이미지 설명을 입력하십시오

여기에 전체 코드 (하나의 파일 만)를 업로드했습니다 .

[다시 업데이트]

[1] 여러 키 흐름을 추가 notify-send하고 define에 의해 수정이 실패했습니다 DBUS_SESSION_BUS_ADDRESS. [2] konsole 테마 gui 사용 (guignome을 사용하지 않는 경우 변경)을 추가 XDG_CURRENT_DESKTOP하고 GNOME_DESKTOP_SESSION_ID보장합니다.

내 코드를 여기에서 업데이트했습니다 .

이 코드는 조합 키 흐름 (예 : Ctrl+)을 처리하지 않습니다 t.

최신 정보:

/ dev / input / by-path / XXX-eventN 항목 순서가 무작위 인 여러 장치 인터페이스가 있습니다. 따라서 다음과 같이 명령을 변경합니다 /etc/gdm3/PostLogin/Default( Chesen내 키보드 이름입니다. grep Razer대신 키보드 이름을 대신 변경해야합니다 ).

/your_path/my_long_press "$(cat /proc/bus/input/devices | grep -i Chesen -A 4 | grep -P '^(?=.*sysrq)(?=.*leds)' |  tr ' ' '\n' | ls /dev/input/`grep event`)" 2>/tmp/l_gdmE &

다음에서 eventN 추출을 시도 할 수 있습니다 cat /proc/bus/input/devices | grep -i Razer -A 4.

$ cat /proc/bus/input/devices | grep -i Razer -A 4
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.0/0003:1532:0053.0003/input/input6
U: Uniq=
H: Handlers=mouse2 event5 
--
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input1
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.1/0003:1532:0053.0004/input/input7
U: Uniq=
H: Handlers=sysrq kbd event6 
--
N: Name="Razer Razer Naga Chroma"
P: Phys=usb-0000:00:14.0-1.3/input2
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.3/3-1.3:1.2/0003:1532:0053.0005/input/input8
U: Uniq=
H: Handlers=sysrq kbd leds event7 
$ 

위의 예에서, 위에서 sudo cat /dev/input/event7"sysrq kbd leds event7"패턴을 가진 Razer 마우스의 12 자리를 클릭하면 기괴한 출력 만 인쇄됩니다 grep -P '^(?=.*sysrq)(?=.*leds)'(패턴이 다를 수 있음). sudo cat /dev/input/event6중간 위 / 아래 키를 클릭 할 때만 기괴한 출력을 인쇄합니다. 동안은 sudo cat /dev/input/event5마우스 휠 스크롤을 이동할 때 기괴한 출력을 인쇄합니다.

[업데이트 : 키보드 케이블을 다시 연결하여 프로그램을 다시로드하도록 지원]

다음은 자기 설명이어야합니다.

$ lsusb #to know my keyboard is idVendor 0a81 and idProduct 0101
...
Bus 001 Device 003: ID 0a81:0101 Chesen Electronics Corp. Keyboard

$ cat /etc/udev/rules.d/52-hole-keyboard.rules #add this line with your idVendor and idProduct above in custom udev rules file
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0a81", ATTR{idProduct}=="0101", MODE="0666", GROUP="plugdev", RUN+="/bin/bash -c 'echo 1 > /tmp/chesen_plugged'"

$ cat /usr/local/bin/inotifyChesenPlugged #A long run listener script to listen for modification of /tmp/chesen_plugged #Ensures `inotifywait` has been installed first.
touch /tmp/chesen_plugged
while inotifywait -q -e modify /tmp/chesen_plugged >/dev/null; do
        killall -9 my_long_press
        /usr/local/bin/startLongPress &
done

$ cat /usr/local/bin/startLongPress #the executable script run the long press executable #Change with your pattern as explained above.
#!/bin/bash
<YOUR_DIR>/my_long_press "$(cat /proc/bus/input/devices | grep -i Chesen -A 4 | grep -P '^(?=.*sysrq)(?=.*leds)' |  tr ' ' '\n' | ls /dev/input/`grep event`)" 2>/tmp/l_gdmE) & disown

$ cat /etc/gdm3/PostLogin/Default #the executable startup script run listener and long press script
/usr/local/bin/inotifyChesenPlugged &
/usr/local/bin/startLongPress &

이 방법을 사용하려면 키를 누르는 동안 터미널 창에 포커스가 있어야한다고 가정합니까? 이 주위에 방법이 있습니까?
kanoko

@kanoko 솔루션을 업데이트했습니다.
과일

thx, 나는 당신이 이것에 넣은 노력에 정말로 감사합니다. 나는 이것을 시도 할 것이다. 이 솔루션이 12 가지 핫키로 설정하면 CPU 사용량에 눈에 띄는 영향을 줄 것이라고 생각하십니까?
kanoko

@kanoko 나는 여러 키를 가지고 놀기 위해 코드를 다시 업데이트했습니다. IMHO 나는 10 + if-else가 너무 미묘하기 때문에 CPU에 눈에 띄는 영향이 없다고 생각하고 읽은 후에 만 ​​검사를 실행합니다 (fd, ev, sizeof (struct input_event) * 64); 문, 즉 if-else매 키를 누를 때마다 실행되며 if (currCode >= 59) && (currCode <= 81)이전에도 범위를 제한하기 위해 추가 했습니다 if-else.
과일

1
진짜 대단한데!!! 모든 도움을 주셔서 감사합니다. Razer Naga와 같은 MMO numpad 마우스로 이것을 시도 할 수 있다면 인생이 바뀔 것이라고 맹세합니다. 관심이 있다면 내 키 매핑을 보여줄 수 있습니다.
kanoko

1

특정 프로그램 세트에서 작동하는 도구를 찾을 수 있지만 시간 관련 동작이 윈도우 시스템이 아닌 X의 응용 프로그램에서 수행되므로 전역 적으로 사용할 수있는 도구가 없습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.