So ordnen Sie Tastaturtasten neu zu, je nachdem, wie lange Sie die Taste gedrückt halten

9

Ich möchte die Tasten auf meinem Nummernblock neu zuordnen, damit sie sich je nach gedrückter Taste unterschiedlich verhalten. Hier ist ein Beispiel:

Wenn ich die Numpad 9-Taste weniger als 300 ms lang gedrückt halte, wird der Tastenbefehl "Vorherige Registerkarte" Ctrl+ gesendetTab

Wenn ich die Numpad 9-Taste 300-599 ms lang gedrückt halte, wird der Tastenbefehl "Neue Registerkarte" Ctrl+ gesendetT

Wenn ich die Numpad 9-Taste 600-899 ms lang gedrückt halte, wird der Tastenbefehl "Tab / Fenster schließen" Ctrl+ gesendetW

Wenn ich die Numpad 9-Taste länger als 899 ms gedrückt halte, geschieht nichts, falls ich das gewünschte Zeitfenster verpasst habe.

Unter Windows könnte ich dies mit AutoHotKey und unter OS XI mit ControllerMate tun, aber unter UNIX / Linux kann ich kein Tool finden, das eine Neuzuordnung von Schlüsseln basierend auf der Haltedauer eines Schlüssels ermöglicht.

Wenn Ihnen ein Tool bekannt ist, mit dem mein Problem gelöst werden kann, stellen Sie sicher, dass Sie ein Skript oder ein Codebeispiel bereitstellen, das das oben beschriebene Verhalten der bedingten Haltedauer von Schlüsseln demonstriert. Es muss nicht der vollständige Code sein, um mein Beispiel zu lösen, aber es sollte ausreichen, um es für mein Beispiel zu verwenden.

Kanoko
quelle
Das ist so eine ausgefallene Sache. Wie werden Sie Ihre 600-Millisekunden-Presse zeitlich festlegen? : D +1 für verrückte Idee.
Wildcard
Um Ihrem Leben etwas Würze zu verleihen, sollten Sie ein Zeitfenster von 347 bis 350 ms hinzufügen, das das Herunterfahren Ihres Computers erzwingt. ;)
Wildcard
@Wildcard Ich benutze dafür tatsächlich den Nummernblock auf meinem Razer Naga und als ich die Idee zum ersten Mal mit AutoHotKey unter Windows umsetzte, verwendete ich Zeitfenster von 300 bis 400 ms, aber jetzt, wo ich dieses System für eine Weile benutze, benutze ich use Zeitfenster im Abstand von ca. 200 ms, und ich kann das gewünschte Zeitfenster in ca. 99% der Fälle erhalten. Es ist sehr ähnlich zu der Art und Weise, wie Sie mit Morsecode kommunizieren würden.
Kanoko

Antworten:

7

Ich habe das gerade in C geschrieben :

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

}

Verwenden Sie showkey -adiese Option, um den Bindungsschlüsselcode abzurufen:

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$ 

/tmp/.a.outGeben Sie den Bindungsschlüsselcode 5 und seinen Befehl (z. B. Ausführen ) in ~ / .bashrc ein:

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

Beachten Sie, dass sich der relevante Schlüsselcode auch im Quellcode ändern muss (der Hex-Wert kann auch von sudo showkey -aoben kommen):

int c = 0x35;

Kompilieren mit (Ausgabe an /tmp/a.outin meinem Beispiel):

cc filename.c -lcurses

Demonstration:

Numpad 5, kurz drücken, neue Registerkarte öffnen, mittel drücken, gedit öffnen und lange drücken, Gnome-Terminal öffnen.

Geben Sie hier die Bildbeschreibung ein

Dies ist in keinem Fenster des Gnome-Desktop-Managers direkt anwendbar, aber ich denke, es sollte Ihnen eine Vorstellung davon geben, wie (schwer) es zu implementieren ist. Es funktioniert auch in der virtuellen Konsole (Strg + Alt + N) und in einigen Terminalemulatoren (z. B. Konsole, Gnome-Terminal, xterm).

p / s: Ich bin kein AC-Programmierer. Verzeihen Sie mir, wenn dieser Code nicht optimiert ist.

[AKTUALISIEREN]

Die vorherige Antwort funktioniert nur in der Shell und erfordert den Fokus. Daher denke ich, dass das Parsen von / dev / input / eventX die Lösung ist, um in der gesamten X-Sitzung zu arbeiten.

Ich möchte das Rad nicht neu erfinden. Ich spiele mit dem evtestDienstprogramm herum und habe den unteren Teil von evtest.c mit meinem eigenen Code geändert :

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

Beachten Sie, dass Sie den Teil Benutzername ( Xiaobai ist mein Benutzername) ändern sollten . Außerdem if ( (ev[i].code == 76) ) {ist dies mein Numpad 5-Schlüsselcode. Möglicherweise müssen Sie den ev [i] .code manuell ausdrucken, um ihn doppelt zu bestätigen. Und natürlich solltest du auch den Videopfad ändern :)

Kompilieren und testen Sie es direkt mit (der `` Teil dient dazu, das richtige zu erhalten /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}')" ` &

Beachten Sie, dass /by-id/dies in Fedora 24 nicht funktioniert, daher ändere ich es in / by-path /. Kali kein solches Problem.

Mein Desktop Manager ist gdm3:

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

Also habe ich diese Zeile /etc/gdm3/PostLogin/Defaulteingefügt, um diesen Befehl als Root beim Start von gdm auszuführen ( /etc/X11/Xsession.d/*funktioniert nicht):

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

Aus unbekannten Gründen / etc/gdm/PostLogin/Defaultfunktioniert nicht mit Fedora 24 'gdm, die mir beim Überprüfen des Protokolls " Berechtigung verweigert " geben /tmp/l_gdmE. Manuell kein Problem ausführen.

Demonstration:

Numpad 5, Sofortdruck (<= 0,2 Sekunden) wird ignoriert, Kurzdruck (0,2 bis 0,5 Sekunden) geöffnet nautilus, Mitteldruck (0,5 bis 1 Sekunde) geöffnet vlc, um Video abzuspielen, Langdruck (1 bis 2 Sekunden) Öffnen gnome-terminalund Timeout-Drücken (2 Sekunden) öffnen gedit.

Geben Sie hier die Bildbeschreibung ein

Ich habe hier den vollständigen Code (nur eine Datei) hochgeladen .

[Erneut aktualisieren]

[1] Mehrere Schlüssel wurden hinzugefügt und notify-senddurch Definieren fehlgeschlagen behoben DBUS_SESSION_BUS_ADDRESS. [2] Hinzugefügt XDG_CURRENT_DESKTOPund GNOME_DESKTOP_SESSION_IDum sicherzustellen, dass die Konsole Gnome Theme GUI verwendet (Ändern Sie es, wenn Sie Gnome nicht verwenden).

Ich habe meinen Code hier aktualisiert .

Beachten Sie, dass dieser Code nicht für den Fluss von Kombinationsschlüsseln geeignet ist, z . B. Ctrl+ t.

AKTUALISIEREN:

Es gibt mehrere Geräteschnittstellen, bei denen die Eingabesequenz / dev / input / by-path / XXX-eventN zufällig ist. Also ändere ich den Befehl /etc/gdm3/PostLogin/Defaultwie Chesenfolgt ( ist mein Tastaturname, für Ihren Fall sollten Sie ihn grep Razerstattdessen ändern ):

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

Sie können den eventN-Extrakt ausprobieren aus 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 diesem Beispiel oben sudo cat /dev/input/event7wird eine bizarre Ausgabe nur gedruckt, wenn Sie auf die 12 Stellen der Razer-Maus klicken, die das oben verwendete Muster "sysrq kbd leds event7" enthält grep -P '^(?=.*sysrq)(?=.*leds)'(Ihr Muster kann variieren). sudo cat /dev/input/event6druckt bizarre Ausgaben nur, wenn Sie auf die mittlere Auf- / Ab-Taste klicken. While sudo cat /dev/input/event5druckt bizarre Ausgaben, wenn Sie die Maus bewegen und das Rad bewegen.

[Update: Support Replug-Tastaturkabel zum erneuten Laden des Programms unterstützen]

Folgendes sollte selbsterklärend sein:

$ 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 &
林果 皞
quelle
Ich gehe davon aus, dass diese Methode erfordert, dass ein Terminalfenster beim Drücken von Tasten scharfgestellt ist. Gibt es einen Weg, dies zu umgehen?
Kanoko
@kanoko Ich habe die Lösung aktualisiert.
皞 皞
Danke, ich schätze die Mühe, die Sie in diese Sache gesteckt haben, sehr. Ich werde das ausprobieren. Glauben Sie, dass diese Lösung einen spürbaren Einfluss auf die CPU-Nutzung hat, wenn ich sie mit 12 verschiedenen Hotkeys einrichte?
Kanoko
@kanoko Ich habe den Code erneut aktualisiert, um mit mehreren Tasten herumzuspielen. IMHO glaube ich nicht, dass es spürbare Auswirkungen auf die CPU hat, weil 10+ if-else zu subtil ist und die Prüfung erst nach dem Lesen ausgeführt wird (fd, ev, sizeof (struct input_event) * 64); Anweisung, dh es wird nur bei if-elsejedem Tastendruck ausgeführt, während ich auch hinzugefügt habe if (currCode >= 59) && (currCode <= 81), um den Bereich vorher zu begrenzen if-else.
6 皞
1
du bist unglaublich !!! Vielen Dank für all Ihre Hilfe. Wenn Sie jemals die Möglichkeit haben, dies mit einer MMO-Numpad-Maus wie Razer Naga zu versuchen, wird dies Ihr Leben verändern. Ich kann Ihnen meine Schlüsselzuordnungen zeigen, wenn Sie interessiert sind.
Kanoko
1

Möglicherweise finden Sie ein Tool, das mit einem bestimmten Satz von Programmen funktioniert. Es gibt jedoch kein global verwendbares Tool, da zeitbezogenes Verhalten in Anwendungen in X und nicht vom Fenstersystem ausgeführt wird.

Thomas Dickey
quelle
0

Hast du die Xmodmap überprüft?

xmodmap ist ein Dienstprogramm zum Ändern von Keymaps und Zeigerschaltflächenzuordnungen in Xorg

https://wiki.archlinux.org/index.php/Xmodmap

Kamaraj
quelle
2
Aber es weiß nicht über Zeitverzögerungen :-)
Thomas Dickey