Как переназначить клавиши клавиатуры в зависимости от длительности удержания клавиши

Как переназначить клавиши клавиатуры в зависимости от длительности удержания клавиши

Я хотел бы переназначить клавиши на моей цифровой клавиатуре так, чтобы они вели себя по-разному в зависимости от того, как долго удерживается клавиша. Вот пример:

Если я буду удерживать клавишу Numpad 9 менее 300 мс, она отправит команду «предыдущая вкладка» Ctrl+Tab

Если я буду удерживать клавишу Numpad 9 в течение 300–599 мс, то будет отправлена ​​команда «новая вкладка» Ctrl+T

Если я буду удерживать клавишу Numpad 9 в течение 600–899 мс, то будет отправлена ​​команда «закрыть вкладку/окно» Ctrl+W

Если я буду удерживать клавишу 9 на цифровой клавиатуре более 899 мс, ничего не произойдет, если я пропущу нужное мне временное окно.

В Windows я мог бы сделать это с помощью AutoHotKey, а в OS X — с помощью 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"'

Обратите внимание, что соответствующий код клавиши необходимо изменить и в исходном коде (шестнадцатеричное значение sudo showkey -aтакже можно получить из вышеприведенного источника):

int c = 0x35;

Скомпилируйте с помощью (вывод /tmp/a.outв моем примере):

cc filename.c -lcurses

Демонстрация:

Numpad 5, короткое нажатие открывает новую вкладку, среднее нажатие открывает gedit и длинное нажатие открывает gnome-terminal.

введите описание изображения здесь

Это не применимо напрямую ни в одном окне менеджера рабочего стола gnome, но я думаю, что это должно дать вам некоторое представление о том, как (сложно) это реализовать. Это работает также в виртуальной консоли (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;
                    }
                }
            }
        }
    }
}

Обратите внимание, что вам следует изменить имя пользователя (сяобайэто мое имя пользователя). А также 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/. В Kali такой проблемы нет.

Мой менеджер рабочего стола — gdm3:

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

Итак, я вставил эту строку, /etc/gdm3/PostLogin/Defaultчтобы запустить эту команду от имени root при запуске 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/Defaultне работает на Fedora 24' gdm, который дает мне "Доступ запрещен" при проверке /tmp/l_gdmEжурнала. Хотя вручную запустить проблем не составит.

Демонстрация:

Numpad 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] Добавлено XDG_CURRENT_DESKTOPи GNOME_DESKTOP_SESSION_IDдля обеспечения использования konsole темы gnome (измените ее, если вы не используете 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будет выведен только странный вывод при нажатии 12 цифр на мыши Razer, которая имеет шаблон "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

Связанный контент