Як перевпорядкувати клавіші клавіатури залежно від того, як довго ви тримаєте клавішу


9

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

Якщо я затримаю клавішу Numpad 9 менше 300 мс, вона надішле клавішу Ctrl+ "попередня вкладка" +Tab

Якщо я утримую клавішу Numpad 9 протягом 300-599 мс, вона надішле клавішу Ctrl+ "нова вкладка" +T

Якщо я затримаю клавішу Numpad 9 протягом 600-899 мс, вона надішле клавішу "закрити вкладку / вікно" Ctrl+W

Якщо я утримую клавішу Numpad 9 більше 899 м, вона нічого не робить, якщо я пропустила потрібне вікно часу.

У Windows я міг би це зробити з AutoHotKey, а в OS XI міг би це зробити з ControllerMate, але я не можу знайти інструмент на UNIX / Linux, який дозволяє переспівати ключі залежно від того, скільки часу утримується ключ.

Якщо вам відомий інструмент, який може вирішити мою проблему, будь ласка, переконайтеся, що вкажіть зразок сценарію чи коду, який демонструє поведінку тривалості утримання умовного ключа, яку я описав вище. Щоб вирішити мій приклад, це не повинен бути повним кодом, але мені повинно бути достатньо, щоб я змінив його на свій приклад.


Це така чудова справа. Як ви збираєтеся виконати 600-мілісекундний прес? : D +1 для шаленої ідеї.
Wildcard

Для того, щоб додати певної спеції у ваше життя, вам слід додати часове вікно від 347 до 350 мс, що призведе до примусового вимкнення вашого комп’ютера. ;)
Wildcard

@Wildcard Я фактично використовую для цього цифровий планшет на моїй Razer Naga, і коли я вперше реалізував ідею з AutoHotKey в Windows, я використовував часові вікна 300-400 мс, але тепер, коли я деякий час використовував цю систему, я використовую тимчасові вікна приблизно 200 м один від одного, і я можу отримати бажане вікно часу приблизно в 99% часу. Це дуже схоже на те, як ви спілкувались з кодом Морзе.
kanoko

Відповіді:


7

Я щойно написав це на С :

#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 та його команду (наприклад, запустити /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-терміналом.

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

Це безпосередньо не застосовується в будь-якому вікні менеджера робочого столу 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;
                    }
                }
            }
        }
    }
}

Зауважте, що ви повинні змінити ім'я користувача ( xiaobai - це моє ім'я користувача). А також if ( (ev[i].code == 76) ) {мій код ключа Numpad 5, можливо, вам потрібно буде вручну надрукувати код ev [i]. І звичайно, ви також повинні змінити шлях відео :)

Скомпілюйте і протестуйте його безпосередньо (з частиною `` для отримання правильності /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, тому я змінюю його на / по шляху /. У Калі такої проблеми немає.

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

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

Отже, я поклав цей рядок, /etc/gdm3/PostLogin/Defaultщоб запустити цю команду як корінь при запуску 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журналу. Вручну запустити не проблема.

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

Цифрова цифра 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 використовуйте тему gui 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 &

я припускаю, що цей метод вимагає, щоб термінальне вікно було у фокусі, роблячи натискання клавіш? чи є шлях до цього?
каноко

@kanoko Я оновив рішення.
фрукти

THX, я дуже ціную зусилля, які ви доклали до цього. Я спробую це. чи вважаєте ви, що це рішення матиме помітний вплив на використання процесора, якщо я встановлю його за допомогою 12 різних гарячих клавіш?
каноко

@kanoko Я знову оновив код, щоб пограти кількома клавішами. IMHO я не думаю, що це помітний вплив на процесор, тому що 10+ if-else є занадто тонким, і він запускає перевірку лише після читання (fd, ev, sizeof (struct input_event) * 64); оператор, тобто він виконує лише if-elseкожне натискання клавіші, тоді як я також додав if (currCode >= 59) && (currCode <= 81)для обмеження діапазону раніше if-else.
фрукти

1
ти дивовижний!!! дуже дякую за всю вашу допомогу. якщо ви коли-небудь отримаєте можливість спробувати це з мишею MMO з нумерацією, як Razer Naga, я клянусь, це змінить ваше життя. Я можу показати вам свої ключові карти, якщо вам цікаво.
каноко

1

Можливо, ви знайдете інструмент, який працює з певним набором програм, але глобально застосований інструмент не буде, оскільки поведінка, пов'язана з часом, виконується в додатках в X, а не в системі вікон.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.