Jak ponownie przypisać klawisze klawiatury na podstawie tego, jak długo trzymasz klawisz

9

Chciałbym ponownie przypisać klawisze na klawiaturze numerycznej, aby zachowywały się inaczej w zależności od czasu naciśnięcia klawisza. Oto przykład:

Jeśli przytrzymam klawisz Numpad 9 przez mniej niż 300 ms, wyśle ​​polecenie „poprzednia karta” Ctrl+Tab

Jeśli przytrzymam klawisz Numpad 9 przez 300-599 ms, wyśle ​​polecenie „nowa karta” Ctrl+T

Jeśli przytrzymam klawisz Numpad 9 przez 600-899 ms, wyśle ​​polecenie „zamknij kartę / okno” Ctrl+W

Jeśli przytrzymam klawisz Numpad 9 dłużej niż 899 ms, nic nie zrobię w przypadku, gdy przegapiłem okno czasowe, które chciałem.

W Windowsie mogłem to zrobić za pomocą AutoHotKey, a w OS XI mogłem to zrobić za pomocą ControllerMate, ale nie mogę znaleźć narzędzia w systemie UNIX / Linux, które pozwala na ponowne mapowanie klucza w oparciu o czas przechowywania klucza.

Jeśli znasz narzędzie, które może rozwiązać mój problem, upewnij się, że podałeś przykładowy skrypt lub kod, który demonstruje zachowanie warunkowego trzymania klucza opisane powyżej. Nie musi to być pełny kod, aby rozwiązać mój przykład, ale powinienem wystarczyć, aby zmienić przeznaczenie na mój przykład.

kanoko
źródło
To jest tak dziwaczne. Jak zmierzysz czas swojej 600 milisekundowej prasy? : D +1 za szalony pomysł.
Wildcard,
Aby dodać odrobinę pikanterii swojemu życiu, należy dodać okno czasowe od 347 do 350 ms, które wymusi zamknięcie komputera. ;)
Wildcard,
@Wildcard Właściwie używam do tego klawiatury numerycznej na Razer Naga, a kiedy po raz pierwszy wdrożyłem ten pomysł z AutoHotKey w systemie Windows, korzystałem z okien czasowych 300-400 ms, ale teraz, gdy już używam tego systemu, używam okna czasowe w odległości około 200 ms od siebie i mogę uzyskać pożądane okno czasowe w około 99% przypadków. Jest bardzo podobny do sposobu komunikacji z kodem Morse'a.
kanoko

Odpowiedzi:

7

Właśnie napisałem to w 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; 

}

Użyj, showkey -aaby uzyskać kod przypisania:

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$ 

Umieść kod powiązania 5 i jego polecenie (np. Uruchom /tmp/.a.out) w ~ / .bashrc:

bind '"5":"/tmp/a.out\n"'

Zauważ, że odpowiedni kod klucza musi również ulec zmianie w kodzie źródłowym (wartość szesnastkową można również uzyskać z sudo showkey -agóry):

int c = 0x35;

Skompiluj z (dane wyjściowe do /tmp/a.outw moim przykładzie):

cc filename.c -lcurses

Demonstracja:

Numpad 5, krótkie naciśnięcie, otwórz nową kartę, średnie naciśnięcie, otwórz gedit i długie wciśnięcie, otwórz gnome-terminal.

wprowadź opis zdjęcia tutaj

Nie ma to bezpośredniego zastosowania w żadnym oknie menedżera pulpitu gnome, ale myślę, że powinno dać ci pewien pomysł, jak (trudno) go wdrożyć. Działa również w konsoli wirtualnej (Ctrl + Alt + N) i działa w niektórych emulatorach terminali (np. Konsole, gnome-terminal, xterm).

p / s: Nie jestem programistą ac, więc wybacz mi, jeśli ten kod nie jest zoptymalizowany.

[AKTUALIZACJA]

Poprzednia odpowiedź działa tylko w powłoce i wymaga skupienia, więc myślę, że przeanalizuj / dev / input / eventX to rozwiązanie do pracy w całej sesji X.

Nie chcę wymyślać koła na nowo. I bawić się z evtestużyteczności i modyfikowane dolną część evtest.c z mojego własnego kodu:

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;
                    }
                }
            }
        }
    }
}

Pamiętaj, że powinieneś zmienić część nazwy użytkownika ( xiaobai to moja nazwa użytkownika). A także if ( (ev[i].code == 76) ) {jest to mój kod numeryczny Numpad 5, może być konieczne ręczne wydrukowanie kodu ev [i] w celu podwójnego potwierdzenia. I oczywiście powinieneś również zmienić ścieżkę wideo :)

Skompiluj i przetestuj go bezpośrednio za pomocą (część `` jest po to, aby uzyskać poprawne /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}')" ` &

Zauważ, że /by-id/nie działa w Fedorze 24, więc zmieniam go na / by-path /. Kali nie ma takiego problemu.

Mój menedżer pulpitu to gdm3:

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

Więc wstawiłem ten wiersz, /etc/gdm3/PostLogin/Defaultaby uruchomić to polecenie jako root podczas uruchamiania gdm ( /etc/X11/Xsession.d/*nie działa):

/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 &

Z nieznanego powodu / etc/gdm/PostLogin/Defaultnie działa na Fedorze 24 'gdm, co daje mi „ Odmowę dostępu ” podczas sprawdzania /tmp/l_gdmEdziennika. Jednak ręcznie nie ma problemu.

Demonstracja:

Numpad 5, natychmiastowe naciśnięcie (<= 0,2 sekundy) zostanie zignorowane, krótkie naciśnięcie (0,2 do 0,5 sekundy) otwarte nautilus, średnie naciśnięcie (0,5 do 1 sekundy) otwarte, vlcaby odtworzyć wideo, długie naciśnięcie (1 do 2 sekund) otwórz gnome-terminali limit czasu naciśnij (2 sekundy) otwórz gedit.

wprowadź opis zdjęcia tutaj

Przesłałem tutaj pełny kod (tylko jeden plik) .

[Ponownie zaktualizuj]

[1] Dodano przepływ wielu kluczy i naprawiono błąd notify-senddefiniowania DBUS_SESSION_BUS_ADDRESS. [2] Dodano XDG_CURRENT_DESKTOPi GNOME_DESKTOP_SESSION_IDaby upewnić się, że konsola używa GUI motywu gnome (Zmień go, jeśli nie używasz gnome).

Zaktualizowałem swój kod tutaj .

Zauważ, że ten kod nie obsługuje przepływu klawiszy kombinacji, np . Ctrl+ t.

AKTUALIZACJA:

Istnieje wiele interfejsów urządzeń, których sekwencja wpisów / dev / input / by-path / XXX-eventN jest losowa. Zmieniam więc polecenie w /etc/gdm3/PostLogin/Defaultnastępujący sposób ( Chesento nazwa mojej klawiatury, w twoim przypadku powinieneś zmienić ją na 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 &

Możesz wypróbować fragment eventN z 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 
$ 

W powyższym przykładzie sudo cat /dev/input/event7wydrukowane zostaną dziwne dane wyjściowe tylko po kliknięciu 12 cyfr myszy Razer, która ma wzorzec „sysrq kbd leds event7” do użycia grep -P '^(?=.*sysrq)(?=.*leds)'powyżej (wzór może się różnić). sudo cat /dev/input/event6wydrukuje dziwne wyniki tylko po kliknięciu środkowego klawisza góra / dół. Podczas gdy sudo cat /dev/input/event5wydrukuje dziwne wyniki, gdy poruszasz myszką i przewijasz kółko.

[Aktualizacja: Wsparcie Wymień kabel klawiatury, aby ponownie załadować program]

Wyjaśnienia powinny być następujące:

$ 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 &
Owoc
źródło
zakładam, że ta metoda wymaga skupienia okna terminala podczas naciskania klawiszy? czy jest na to jakiś sposób?
kanoko
@kanoko Zaktualizowałem rozwiązanie.
Fruit
dzięki, naprawdę doceniam wysiłek włożony w to. Spróbuję tego. czy uważasz, że to rozwiązanie będzie miało zauważalny wpływ na użycie procesora, jeśli skonfiguruję go z 12 różnymi klawiszami skrótu?
kanoko
@kanoko Zaktualizowałem kod ponownie, aby bawić się wieloma klawiszami. IMHO nie sądzę, żeby to miało zauważalny wpływ na procesor, ponieważ 10+ if-else jest zbyt subtelne i uruchamia sprawdzanie dopiero po przeczytaniu (fd, ev, sizeof (struct input_event) * 64); instrukcja, tj. uruchamia tylko if-elsekażde naciśnięcie klawisza, a ja również dodałem, if (currCode >= 59) && (currCode <= 81)aby ograniczyć zakres wcześniej if-else.
Fruit
1
jesteś niesamowity!!! Dziękuje Ci bardzo za całą twoją pomoc. jeśli kiedykolwiek będziesz mieć szansę wypróbować to za pomocą myszy numerycznej MMO, takiej jak Razer Naga, przysięgam, że zmieni to twoje życie. Jeśli jesteś zainteresowany, mogę pokazać Ci moje kluczowe mapowania.
kanoko
1

Możesz znaleźć narzędzie, które działa z określonym zestawem programów, ale nie będzie żadnego narzędzia globalnie użytecznego, ponieważ zachowanie związane z czasem zachodzi w aplikacjach w X, a nie w systemie okienkowym.

Thomas Dickey
źródło