Zugriff auf Schlüssel von einem Linux-Eingabegerät

Lesezeit: 8 Minuten

Benutzeravatar von Senkwich
Senkwich

Was ich versuche zu tun

Also habe ich versucht, unter Linux auf Tastatureingaben zuzugreifen. Insbesondere muss ich in der Lage sein, auf Modifikatortastendrücke zuzugreifen ohne andere Tasten gedrückt werden. Außerdem möchte ich das können ohne ein laufendes X-System.

Also, kurz gesagt, meine Anforderungen sind diese:

  • Funktioniert unter Linux
  • Benötigt kein X11
  • Kann Modifikatortastendruck abrufen ohne alle anderen Tasten gedrückt werden
    • Dazu gehören folgende Schlüssel:
      • Wechsel
      • Kontrolle
      • Alt
    • Alles, was ich brauche, ist ein einfaches 0 = not pressed, 1 = currently pressed um mich wissen zu lassen, ob die Taste gedrückt gehalten wird, wenn die Tastatur überprüft wird

Mein Computer-Setup

Meine normale Linux-Maschine steht auf einem Lastwagen in Richtung meiner neuen Wohnung; Also habe ich im Moment nur ein Macbook Air, mit dem ich arbeiten kann. Daher führe ich Linux in einer VM aus, um dies zu testen.

Virtuelle Maschine in VirtualBox

  • Betriebssystem: Linux Mint 16
  • Desktop-Umgebung: XFCE

Alles unten wurde in dieser Umgebung gemacht. Ich habe es sowohl mit laufendem X als auch in einem der anderen ttys versucht.

Meine Gedanken

Ich werde das ändern, wenn mich jemand korrigieren kann.

Ich habe ziemlich viel gelesen, um festzustellen, dass Bibliotheken auf höherer Ebene diese Art von Funktionalität nicht bieten. Zusatztasten werden zusammen mit anderen Tasten verwendet, um einen alternativen Tastencode bereitzustellen. Der Zugriff auf die Modifikatortasten selbst über eine High-Level-Bibliothek in Linux ist nicht so einfach. Oder besser gesagt, ich habe unter Linux keine High-Level-API dafür gefunden.

ich dachte libtermkey wäre die Antwort, aber es scheint die Shift-Modifikatortaste nicht besser zu unterstützen als das normale Abrufen von Tastenanschlägen. Ich bin mir auch nicht sicher, ob es ohne X funktioniert.

Während ich mit libtermkey arbeitete (bevor ich merkte, dass es in Fällen wie Shift-Return keine Verschiebung bekam), plante ich, einen Daemon zu schreiben, der ausgeführt wird, um Tastaturereignisse zu sammeln. Laufende Kopien des Daemon-Programms würden einfach Anfragen nach Tastaturdaten weiterleiten und als Antwort Tastaturdaten erhalten. Ich könnte dieses Setup verwenden, um etwas immer im Hintergrund laufen zu lassen, falls ich den Schlüsselcodestatus zu bestimmten Zeiten nicht überprüfen kann (muss Schlüsselcodes erhalten, wenn sie passieren).

Unten sind meine zwei Versuche, ein Programm zu schreiben, das vom Linux-Tastaturgerät lesen kann. Ich habe auch meinen kleinen Check beigefügt, um sicherzustellen, dass ich das richtige Gerät habe.

Versuch Nr. 1

Ich habe versucht, direkt auf das Tastaturgerät zuzugreifen, aber es treten Probleme auf. Ich habe den Vorschlag hier ausprobiert, der in einem anderen Stack Overflow-Thread ist. Es gab mir einen Segmentierungsfehler; Also habe ich es von fopen auf open geändert:

// ...

int fd;
fd = open("/dev/input/by-path/platform-i8042-serio-0-event-kbd", O_RDONLY);

char key_map[KEY_MAX/8 + 1];

memset(key_map, 0, sizeof(key_map));
ioctl(fd, EVIOCGKEY(sizeof key_map), key_map);

// ...

Während es keinen Segmentierungsfehler gab, gab es keinen Hinweis auf einen Tastendruck (nicht nur Modifikatortasten). Ich habe dies getestet mit:

./foo && echo "TRUE" || echo "FALSE"

Ich habe das ziemlich häufig verwendet, um auf erfolgreiche Rückgabecodes von Befehlen zu testen. Also, ich weiß, das ist in Ordnung. Ich habe auch den Schlüssel (immer 0) und die Maske (0100) zur Überprüfung ausgegeben. Es scheint einfach nichts zu erkennen.

Versuch Nr. 2

Von hier aus dachte ich, ich würde einen etwas anderen Ansatz versuchen. Ich wollte herausfinden, was ich falsch mache. Folgend Dies Seite mit einem Ausschnitt, der das Ausdrucken von Schlüsselcodes demonstriert, habe ich das in ein Programm gebündelt:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <linux/input.h>

int main(int argc, char** argv) {
    uint8_t keys[128];
    int fd;

    fd = open("/dev/input/by-path/platform-i8042-serio-event-kbd", O_RDONLY);
    for (;;) {
        memset(keys, 0, 128);
        ioctl (fd, EVIOCGKEY(sizeof keys), keys);

        int i, j;
        for (i = 0; i < sizeof keys; i++)
            for (j = 0; j < 8; j++)
                if (keys[i] & (1 << j))
                    printf ("key code %d\n", (i*8) + j);
    }

    return 0;
}

Früher hatte ich die Größe auf 16 Bytes statt 128 Bytes. Ich sollte ehrlich gesagt etwas mehr Zeit damit verbringen, ioctl und EVIOCGKEY zu verstehen. Ich weiß nur, dass es angeblich Bits bestimmten Tasten zuordnet, um Drücken oder ähnliches anzuzeigen (korrigiert mich, wenn ich falsch liege, bitte!).

Ich hatte anfangs auch keine Schleife und hielt einfach verschiedene Tasten gedrückt, um zu sehen, ob ein Tastencode angezeigt wurde. Ich habe nichts erhalten; Also dachte ich, eine Schleife könnte die Überprüfung einfacher machen, falls etwas übersehen wird.

Woher weiß ich, dass das Eingabegerät das richtige ist?

Ich habe es durch Laufen getestet cat auf dem Eingabegerät. Speziell:

$ sudo cat /dev/input/by-path/platform-i8042-serio-0-event-kbd

Garbage ASCII wurde bei Tastendruck und -freigabe an mein Terminal gesendet, beginnend mit der Eingabetaste (Enter), als ich die Ausgabe mit cat begann. Ich weiß auch, dass dies mit Modifikatortasten wie Umschalttaste, Steuerung, Funktion und sogar Apples Befehlstaste auf meinem Macbook, auf dem eine Linux-VM ausgeführt wird, gut zu funktionieren scheint. Die Ausgabe erschien, wenn eine Taste gedrückt wurde, begann schnell von nachfolgenden Signalen zu erscheinen, die durch Gedrückthalten der Taste gesendet wurden, und gab mehr Daten aus, wenn eine Taste losgelassen wurde.

Also, obwohl mein Ansatz vielleicht nicht der richtige ist (ich bin bereit zu hören irgendein Alternative), scheint das Gerät das zu bieten, was ich brauche.

Außerdem weiß ich, dass auf diesem Gerät nur ein Link läuft, der auf /dev/input/event2 zeigt:

$ ls -l /dev/input/by-path/platform-i8042-serio-0-event-kbd

Ich habe beide oben genannten Programme mit /dev/input/event2 ausprobiert und keine Daten erhalten. Das Ausführen von cat auf /dev/input/event2 lieferte die gleiche Ausgabe wie mit dem Link.

  • Kurzer Tipp (ich habe keine wirkliche Antwort): Sie können den Statuscode des vorherigen Befehls in bash mit drucken echo $?. Könnte Tipparbeit sparen. 🙂

    – Luser Drog

    6. Januar 2014 um 5:46 Uhr

  • Wusste das nicht. Vielen Dank!

    – Senkwich

    6. Januar 2014 um 6:38 Uhr

Öffnen Sie das Eingabegerät,

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>

static const char *const evval[3] = {
    "RELEASED",
    "PRESSED ",
    "REPEATED"
};

int main(void)
{
    const char *dev = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
    struct input_event ev;
    ssize_t n;
    int fd;

    fd = open(dev, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
        return EXIT_FAILURE;
    }

und dann lesen Tastaturereignisse vom Gerät:

    while (1) {
        n = read(fd, &ev, sizeof ev);
        if (n == (ssize_t)-1) {
            if (errno == EINTR)
                continue;
            else
                break;
        } else
        if (n != sizeof ev) {
            errno = EIO;
            break;
        }

Das obige Snippet bricht aus der Schleife aus, wenn ein Fehler auftritt oder wenn der Userspace nur eine partielle Ereignisstruktur empfängt (was nicht passieren sollte, aber in einigen zukünftigen/fehlerhaften Kerneln passieren könnte). Vielleicht möchten Sie eine robustere Leseschleife verwenden; Ich persönlich wäre zufrieden, wenn ich den letzten ersetzen würde break mit continuesodass partielle Ereignisstrukturen ignoriert werden.

Sie können die dann untersuchen ev Ereignisstruktur, um zu sehen, was passiert ist, und das Programm zu beenden:

        if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2)
            printf("%s 0x%04x (%d)\n", evval[ev.value], (int)ev.code, (int)ev.code);

    }
    fflush(stdout);
    fprintf(stderr, "%s.\n", strerror(errno));
    return EXIT_FAILURE;
}

Für einen Tastendruck,

  • ev.time: Zeitpunkt des Ereignisses (struct timeval Typ)

  • ev.type: EV_KEY

  • ev.code: KEY_*, Schlüsselkennung; siehe vollständige Liste in /usr/include/linux/input.h

  • ev.value: 0 bei Schlüsselfreigabe, 1 wenn Tastendruck, 2 wenn autorepeat keypress

Sehen Dokumentation/input/input.txt in den Linux-Kernel-Quellen für weitere Details.

Die benannten Konstanten in /usr/include/linux/input.h sind recht stabil, da es sich um eine Kernel-Userspace-Schnittstelle handelt und die Kernel-Entwickler sich sehr bemühen, die Kompatibilität aufrechtzuerhalten. (Das heißt, Sie können damit rechnen, dass es hin und wieder neue Codes gibt, aber bestehende Codes ändern sich selten.)

  • Exzellent! Ich hatte mir die von Ihnen erwähnte Dokumentation angesehen, wollte aber nicht weiter vertiefen, falls ich an der falschen Stelle suchte. Tolle Ausschnitte und Erklärungen! Das Testen des Codes, den Sie mir gegeben haben, funktioniert genau so, wie ich es brauchte!

    – Senkwich

    6. Januar 2014 um 20:31 Uhr


  • Auf meinem System muss ich Superuser sein, um auf das Tastaturgerät zugreifen zu können. Gibt es einen anderen Weg? Auch meine Konsole gibt die Ausgabe auf dem Bildschirm wieder. Welcher Weg wird empfohlen, um das Echo zu vermeiden?

    – Mama

    21. August 2015 um 1:13 Uhr

  • @muman: Nun, Sie können eine udev-Regel hinzufügen, um den Besitz und den Modus des Geräts zu ändern. Sie möchten definitiv NICHT, dass Eingabegeräte für jedermann lesbar sind, denn das würde das Abhören wirklich einfach machen. Normalerweise wird der Input-Handling-Prozess/Daemon mit Superuser-Rechten ausgeführt; mit unprivilegierten Hilfsprogrammen, um mit ihm zu kommunizieren. Grundlegendes Unix-Design, mit anderen Worten.

    – Nominelles Tier

    21. August 2015 um 17:20 Uhr

  • @muman: Du kannst greifen (ioctl(fd, EVIOCGRAB, 1)) das Eingabeereignisgerät, um die Tastendrücke zu konsumieren (sie zu erfassen, anstatt sie nur zu beobachten). Siehe mein Beispiel hier, insbesondere die barcode_open() Funktion. Sie können alle Tastendrücke mit erneut einfügen uinput device (aber auch das benötigt aus offensichtlichen Sicherheitsgründen Privilegien).

    – Nominelles Tier

    21. August 2015 um 17:21 Uhr

  • Wie erkennt man mehrere Tastendruck-Ereignisse?

    – Naveen

    17. November 2020 um 13:40 Uhr

1391220cookie-checkZugriff auf Schlüssel von einem Linux-Eingabegerät

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy