키보드 키를 누르고 있는 시간에 따라 키보드 키를 다시 매핑하는 방법

키보드 키를 누르고 있는 시간에 따라 키보드 키를 다시 매핑하는 방법

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

숫자 패드 9 키를 300밀리초 미만 동안 누르고 있으면 "이전 탭" 키 명령 Ctrl+Tab

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

숫자 패드 키 9를 600-899ms 동안 누르고 있으면 "탭/창 닫기" 키 명령 Ctrl+W

숫자 키패드 9를 899밀리초 이상 누르고 있으면 필요한 시간 창을 놓칠 경우를 대비해 아무 작업도 수행되지 않습니다.

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

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

답변1

방금 이걸 썼어요:

#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 데스크탑 관리자의 모든 창에 직접 적용되지는 않지만 이를 구현하는 방법(어려운)에 대한 아이디어를 제공해야 한다고 생각합니다. 또한 가상 콘솔(Ctrl+Alt+N)과 일부 터미널 에뮬레이터(예: konsole, gnome-terminal, xterm)에서도 작동합니다.

p/s: 저는 프로그래머가 아니기 때문에 이 코드가 최적화되지 않은 경우 양해해 주시기 바랍니다.

[고쳐 쓰다]

이전 답변은 셸에서만 작동하고 포커스가 필요하므로 /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;
                    }
                }
            }
        }
    }
}

사용자 이름을 변경해야 합니다(멍청한 놈내 사용자 이름) 부분입니다. 이것은 또한 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/Fedora 24에서는 작동하지 않으므로 /by-path/로 변경했습니다. 칼리에는 그런 문제가 없습니다.

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

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

그래서 저는 다음 줄을 /etc/gdm3/PostLogin/Defaultgdm에 넣고 gdm이 시작될 때 루트로 이 명령을 실행했습니다( /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에서 "허가가 거부되었습니다/tmp/l_gdmE" 로그를 확인할 때 수동으로 실행하는 데 문제가 없습니다.

데모:

숫자 패드 5, 잠깐 누르기(<=0.2초)는 무시됩니다. 짧게 누르면(0.2~0.5초) nautilus, 중간 정도 누르면(0.5~1초) 비디오 재생이 열리고 vlc, 길게 누르면(1~2초) 열립니다 gnome-terminal. 그런 다음 시간 초과(2초)로 열기를 누릅니다 gedit.

여기에 이미지 설명을 입력하세요

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

[다시 업데이트]

[1] 여러 핵심 프로세스를 추가하고 notify-send정의 실패 문제를 해결했습니다 DBUS_SESSION_BUS_ADDRESS. [2] konsole이 gnome 테마 GUI를 사용하는지 추가 XDG_CURRENT_DESKTOP하고 GNOME_DESKTOP_SESSION_ID확인하십시오(gnome을 사용하지 않는 경우 변경하십시오).

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

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 &

답변2

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

답변3

Xmodmap을 확인해 보셨나요?

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

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

관련 정보