Come rimappare i tasti della tastiera in base alla durata della pressione del tasto


9

Vorrei rimappare i tasti sul mio tastierino numerico in modo che si comportino diversamente a seconda della durata della pressione del tasto. Ecco un esempio:

Se tengo premuto il tasto Numpad 9 per meno di 300 ms, invierà il comando "tab precedente" Ctrl+Tab

Se tengo premuto il tasto Numpad 9 per 300-599 ms, invierà il comando "nuova scheda" Ctrl+T

Se tengo premuto il tasto Numpad 9 per 600-899 ms, invierà il comando "Chiudi scheda / finestra" Ctrl+W

Se si tiene premuto il tasto Numpad 9 per più di 899 ms, non fa nulla nel caso in cui ho perso la finestra temporale desiderata.

Su Windows potrei farlo con AutoHotKey e su OS XI potrei farlo con ControllerMate, ma non riesco a trovare uno strumento su UNIX / Linux che consenta il rimappamento della chiave in base alla durata della chiave.

Se sei a conoscenza di uno strumento in grado di risolvere il mio problema, assicurati di fornire uno script o un esempio di codice che dimostri il comportamento condizionale della durata della chiave che ho descritto sopra. Non è necessario che sia il codice completo per risolvere il mio esempio, ma dovrebbe bastare a riutilizzarlo per il mio esempio.


Questa è una cosa così stravagante da fare. Come hai intenzione di cronometrare la tua pressa da 600 millisecondi? : D +1 per pazza idea.
Carattere jolly

Solo per aggiungere un po 'di pepe alla tua vita, dovresti aggiungere una finestra temporale da 347 a 350 ms che spegnerà forzatamente il tuo computer. ;)
Wildcard il

@Wildcard In realtà uso il tastierino numerico sul mio Razer Naga per questo e quando ho implementato l'idea con AutoHotKey per la prima volta su Windows ho usato finestre temporali da 300-400 ms, ma ora che uso questo sistema da un po ', uso use finestre temporali a circa 200 ms di distanza e posso ottenere la finestra temporale desiderata circa il 99% delle volte. È molto simile al modo in cui comunichi con il codice morse.
Kanoko,

Risposte:


7

Ho appena scritto questo in 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; 

}

Utilizzare showkey -aper ottenere il codice chiave di bind:

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$ 

Inserisci il keycode bind 5 e il suo comando (es. Run /tmp/.a.out) in ~ / .bashrc:

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

Nota che anche il codice chiave pertinente deve cambiare nel codice sorgente (anche il valore esadecimale può ottenere sudo showkey -adall'alto):

int c = 0x35;

Compilare con (output in /tmp/a.outnel mio esempio):

cc filename.c -lcurses

Dimostrazione:

Tastierino numerico 5, pressione breve per aprire una nuova scheda, pressione media per aprire gedit e pressione lunga per aprire gnome-terminal.

inserisci qui la descrizione dell'immagine

Questo non è direttamente applicabile in nessuna finestra su gnome desktop manager, ma penso che dovrebbe darti un'idea di come (difficile) implementarlo. Funziona anche in Virtual Console (Ctrl + Alt + N) e funziona in un emulatore di terminale (es. Konsole, gnome-terminal, xterm).

p / s: non sono un programmatore ac, quindi perdonami se questo codice non è ottimizzato.

[AGGIORNARE]

La risposta precedente funziona solo in shell e richiede focus, quindi penso che analizzare / dev / input / eventX sia la soluzione per funzionare nell'intera sessione X.

Non voglio reinventare la ruota. Gioco con evtestutility e ho modificato la parte inferiore di evtest.c con il mio codice:

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

Si noti che è necessario modificare la parte del nome utente ( xiaobai è il mio nome utente). E anche il if ( (ev[i].code == 76) ) {mio codice tastierino numerico 5, potrebbe essere necessario stampare manualmente il codice ev [i]. Per confermare due volte. E ovviamente dovresti cambiare anche il percorso del video :)

Compilalo e testalo direttamente con (la parte `` è per ottenere il corretto /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}')" ` &

Nota che /by-id/non funziona in Fedora 24, quindi lo cambio in / by-path /. Kali nessun problema del genere.

Il mio desktop manager è gdm3:

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

Quindi, ho inserito questa riga /etc/gdm3/PostLogin/Defaultper eseguire questo comando come root all'avvio di gdm ( /etc/X11/Xsession.d/*non funziona):

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

Per motivi sconosciuti / etc/gdm/PostLogin/Defaultnon funziona su Fedora 24 'gdm che mi dà " Autorizzazione negata " quando si controlla il /tmp/l_gdmEregistro. Non eseguire manualmente alcun problema.

Dimostrazione:

Tastierino numerico 5, la pressione istantanea (<= 0,2 secondi) verrà ignorata, la pressione breve (da 0,2 a 0,5 secondi) aperta nautilus, la pressione media (da 0,5 a 1 secondo) aperta vlcper riprodurre video, la pressione lunga (da 1 a 2 secondi) aperto gnome-terminale timeout-premere (2 secondi) per aprire gedit.

inserisci qui la descrizione dell'immagine

Ho caricato il codice completo (solo un file) qui .

[AGGIORNA di nuovo]

[1] Aggiunto flusso di più chiavi e risolto notify-sendnon riuscito da definisci DBUS_SESSION_BUS_ADDRESS. [2] Aggiunto XDG_CURRENT_DESKTOPe GNOME_DESKTOP_SESSION_IDper assicurarsi che konsole usi la gui del tema di gnome (cambiatela se non state usando gnome).

Ho aggiornato il mio codice qui .

Si noti che questo codice non gestisce il flusso di chiavi combinate, ad es . Ctrl+ t.

AGGIORNARE:

Esistono più interfacce del dispositivo che la sequenza di voci / dev / input / by-path / XXX-eventN è casuale. Quindi cambio il comando /etc/gdm3/PostLogin/Defaultcome di seguito ( Chesenè il nome della mia tastiera, nel tuo caso, dovresti invece cambiarlo in 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 &

Puoi provare l'estratto di eventN da 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 
$ 

In questo esempio, sudo cat /dev/input/event7verrà stampato un output bizzarro solo quando si fa clic sulle 12 cifre del mouse Razer, che ha il modello "sysrq kbd leds event7" da utilizzare in grep -P '^(?=.*sysrq)(?=.*leds)'precedenza (il modello potrebbe variare). sudo cat /dev/input/event6stamperà un output bizzarro solo quando fai clic sul tasto centrale su / giù. Mentre sudo cat /dev/input/event5stamperà un output bizzarro quando si sposta il mouse e si fa scorrere la rotellina.

[Aggiornamento: supporto Sostituire il cavo della tastiera per ricaricare il programma]

Quanto segue dovrebbe essere auto-spiegazione:

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

suppongo che questo metodo richieda che una finestra terminale sia a fuoco mentre si premono i tasti? C'è un modo per aggirare questo?
Kanoko,

@kanoko Ho aggiornato la soluzione.
皞 皞

grazie, apprezzo molto lo sforzo che hai fatto in questo. Lo proverò. pensi che questa soluzione avrà un impatto notevole sull'utilizzo della cpu se la configuro con 12 tasti di scelta rapida diversi?
Kanoko,

@kanoko Ho aggiornato di nuovo il codice per giocare con più tasti. IMHO non credo sia un notevole impatto su CPU perché 10+ if-else è troppo sottile, ed esegue solo il controllo dopo la lettura (fd, ev, sizeof (struct input_event) * 64); dichiarazione, cioè esegue solo if-elseogni tasto premuto, mentre ho anche aggiunto if (currCode >= 59) && (currCode <= 81)per limitare l'intervallo prima if-else.
林果 皞

1
sei fantastico!!! grazie mille per tutto il tuo aiuto. se mai avrai la possibilità di provare questo con un mouse MMO numpad come Razer Naga, giuro che ti cambierà la vita. Posso mostrarti le mie mappature chiave se sei interessato.
Kanoko,

1

Potresti trovare uno strumento che funziona con una particolare serie di programmi, ma non ci sarà uno strumento utilizzabile a livello globale perché il comportamento relativo al tempo viene eseguito nelle applicazioni in X, piuttosto che dal sistema a finestre.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.