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

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

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에서 키를 보유한 기간에 따라 키를 다시 매핑 할 수있는 도구를 찾을 수 없습니다.

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



답변

방금 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 &


답변

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


답변

Xmodmap을 확인 했습니까?

xmodmap은 Xorg에서 키맵과 포인터 버튼 매핑을 수정하는 유틸리티입니다

https://wiki.archlinux.org/index.php/Xmodmap


답변