如何根據按住按鍵的時間重新映射鍵盤按鍵

如何根據按住按鍵的時間重新映射鍵盤按鍵

我想重新映射數字鍵盤上的按鍵,以便它們根據按下按鍵的時間長短而表現不同。這是一個例子:

如果我按住 Numpad 9 鍵少於 300 毫秒,它將發送「上一個選項卡」鍵命令Ctrl+Tab

如果我按住數字鍵盤 9 鍵 300-599 毫秒,它將發送“新選項卡”鍵命令Ctrl+T

如果我按住數字鍵盤 9 鍵 600-899 毫秒,它將發送“關閉選項卡/視窗”鍵命令Ctrl+W

如果我按住數字鍵盤 9 鍵超過 899 毫秒,它不會執行任何操作,以防我錯過了所需的時間視窗。

在 Windows 上,我可以使用 AutoHotKey 來完成此操作,在 OS XI 上可以使用 ControllerMate 來完成此操作,但我在 UNIX/Linux 上找不到允許根據按鍵保持時間進行按鍵重新映射的工具。

如果您知道可以解決我的問題的工具,請確保提供一個腳本或程式碼範例來示範我上面描述的條件按鍵持續時間行為。它不需要是解決我的範例的完整程式碼,但它應該足以讓我將其重新用於我的範例。

答案1

我剛剛寫了這個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"'

請注意,原始程式碼中相關的鍵碼也需要更改(十六進位值sudo showkey -a也可以從上面取得):

int c = 0x35;

編譯(在我的範例中輸出為/tmp/a.out):

cc filename.c -lcurses

示範:

小鍵盤 5,短按開啟新分頁卡,中按開啟 gedit,長按開啟 gnome-terminal。

在此輸入影像描述

這並不直接適用於 gnome 桌面管理器上的任何窗口,但我認為它應該讓您了解如何(困難)實現它。它也可以在虛擬控制台(Ctrl+Alt+N)中運作,並且可以在某些終端模擬器(例如 konsole、gnome-terminal、xterm)中運作。

p/s:我不是程式設計師,所以如果這段程式碼沒有優化,請原諒我。

[更新]

前面的答案只能在 shell 中工作並且需要焦點,所以我認為解析 /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 啟動時以 root 身份運行此命令(/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/Default在 Fedora 24' gdm 上不起作用,這給了我“沒有權限「檢查/tmp/l_gdmE日誌時。手動運行沒有問題。

示範:

小鍵盤5,瞬間按下(<=0.2秒)將被忽略,短按(0.2至0.5秒)打開nautilus,中按(0.5至1秒)打開vlc播放視頻,長按(1至2秒) open gnome-terminal,然後超時按(2秒) open gedit

在此輸入影像描述

我在這裡上傳了完整的程式碼(只有一個檔案)

[再次更新]

[1] 新增了多個按鍵流程並修復了notify-send定義失敗的問題DBUS_SESSION_BUS_ADDRESS。 [2] 新增XDG_CURRENT_DESKTOPGNOME_DESKTOP_SESSION_ID確保 konsole 使用 gnome 主題 gui(如果您不使用 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當單擊 Razer 滑鼠上的 12 位數字時才會列印奇怪的輸出,該滑鼠具有上面使用的模式“sysrq kbd leds event7” 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

相關內容