Accès aux clés à partir d’un périphérique d’entrée Linux

Ce que j’essaie de faire

J’ai donc essayé d’accéder à la saisie au clavier sous Linux. Plus précisément, je dois pouvoir accéder aux touches de modification sans avoir à appuyer sur d’autres touches. De plus, je veux pouvoir le faire sans un système X en cours d’exécution.

Donc, en bref, mes exigences sont les suivantes:

  • Fonctionne sous Linux
  • N’a pas besoin de X11
  • Peut récupérer la touche de modification sans appuyer sur aucune autre touche
    • Cela inclut les clés suivantes:
      • Décalage
      • Contrôle
      • Alt
    • Tout ce dont j’ai besoin est un simple 0 = not pressed , 1 = currently pressed pour me faire savoir si la touche est maintenue enfoncée lorsque le clavier est coché

Configuration de mon ordinateur

Ma machine Linux normale est sur un camion vers mon nouvel appartement; donc, je n’ai qu’un Macbook Air pour travailler avec maintenant. Par conséquent, je lance Linux sur une machine virtuelle pour le tester.

Machine virtuelle dans VirtualBox

  • OS: Linux Mint 16
  • Environnement de bureau: XFCE

Tout ci-dessous a été fait dans cet environnement. J’ai essayé les deux avec X en cours d’exécution et dans l’un des autres ttys.

Mes pensées

Je vais changer cela si quelqu’un peut me corriger.

J’ai fait pas mal de lecture pour me rendre compte que les bibliothèques de haut niveau ne fournissent pas ce type de fonctionnalité. Les touches de modification sont utilisées avec d’autres touches pour fournir un autre code de clé. L’access aux touches de modification elles-mêmes via une bibliothèque de haut niveau sous Linux n’est pas aussi simple. Ou plutôt, je n’ai pas trouvé d’API de haut niveau pour cela sous Linux.

Je pensais que libtermkey serait la réponse, mais il ne semble pas que la touche de modification Shift soit plus efficace que la récupération de frappe normale. Je ne sais pas non plus si cela fonctionne sans X.

Pendant que je travaillais avec libtermkey (avant que je réalise qu’il n’y avait pas de changement dans les cas comme Shift-Return), j’avais l’intention d’écrire un démon qui fonctionnerait pour rassembler des événements clavier. Exécuter des copies du programme démon transmettrait simplement les demandes de données de clavier et recevrait des données de clavier en réponse. Je pourrais utiliser cette configuration pour avoir quelque chose qui tourne toujours en arrière-plan, au cas où je ne pourrais pas vérifier les statuts de code à des moments précis (les codes de clé doivent être reçus au fur et à mesure).

Voici mes deux tentatives pour écrire un programme capable de lire à partir du clavier Linux. J’ai également inclus mon petit chèque pour m’assurer que j’avais le bon appareil.

Tentative n ° 1

J’ai essayé d’accéder directement au clavier, mais je rencontre des problèmes. J’ai essayé la suggestion ici qui est dans un autre thread de débordement de stack. Cela m’a donné une faille de segmentation; alors, je l’ai changé de fopen pour ouvrir:

 // ... 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); // ... 

Bien qu’il n’y ait pas de faute de segmentation, il n’y avait pas d’indicateur de pression sur une touche (pas seulement des touches de modification). J’ai testé ceci en utilisant:

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

Je l’ai utilisé pour tester les codes de retour des commandes avec succès; alors je sais que ça va. J’ai aussi sorti la clé (toujours 0) et le masque (0100) pour vérifier. Il ne semble tout simplement rien détecter.

Tentative n ° 2

De là, je pensais essayer une approche légèrement différente. Je voulais comprendre ce que je faisais mal. Suite à cette page fournissant un extrait illustrant l’impression des codes clés, je l’ai intégré dans un programme:

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

Auparavant, j’avais la taille de 16 octets au lieu de 128 octets. Je devrais honnêtement passer un peu plus de temps à comprendre ioctl et EVIOCGKEY. Je sais juste que cela mappe des bits à des touches spécifiques pour indiquer des presses, ou quelque chose comme ça ( corrigez-moi si je me trompe, s’il vous plaît! ).

Je n’avais pas non plus de boucle au départ et je maintenais simplement plusieurs touches pour voir si un code de clé apparaissait. Je n’ai rien reçu; alors, je pensais qu’une boucle pourrait rendre le test plus facile à tester en cas de manquement.

Comment je sais que le périphérique d’entrée est le bon

Je l’ai testé en lançant cat sur le périphérique d’entrée. Plus précisément:

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

La corbeille ASCII a été envoyée à mon terminal lors de l’activation et de la libération des clés, en commençant par la touche retour (entrée) lorsque j’ai commencé à utiliser cat. Je sais aussi que cela semble fonctionner correctement avec les touches de modification telles que shift, control, function, et même la touche de commande d’Apple sur mon Macbook exécutant une VM Linux. La sortie est apparue quand une touche a été pressée, a commencé à apparaître rapidement à partir des signaux suivants envoyés en maintenant la touche enfoncée et a produit plus de données quand une touche a été relâchée.

Donc, même si mon approche n’est peut-être pas la bonne (je suis prêt à entendre n’importe quelle alternative), l’appareil semble fournir ce dont j’ai besoin.

En outre, je sais que ce périphérique est juste un lien pointant vers / dev / input / event2 de l’exécution:

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

J’ai essayé les deux programmes ci-dessus avec / dev / input / event2 et je n’ai reçu aucune donnée. L’exécution de cat sur / dev / input / event2 a fourni le même résultat qu’avec le lien.

Ouvrez le périphérique d’entrée,

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

puis lisez les événements de clavier de l’appareil:

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

L’extrait de code ci-dessus se détache de la boucle si une erreur survient, ou si l’espace utilisateur ne reçoit qu’une structure d’événement partielle (ce qui ne devrait pas se produire, mais pourrait se produire dans certains kernelx futurs). Vous pourriez souhaiter utiliser une boucle de lecture plus robuste; Personnellement, je serais satisfait en remplaçant la dernière break par continue , afin que les structures partielles des événements soient ignorées.

Vous pouvez ensuite examiner la structure d’événement ev pour voir ce qui s’est passé et terminer le programme:

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

Pour une frappe de touche,

  • ev.time : heure de l'événement (type struct timeval )

  • ev.type : EV_KEY

  • ev.code : KEY_* , identifiant de clé; voir la liste complète dans /usr/include/linux/input.h

  • ev.value : 0 si la touche est ev.value , 1 si la touche est pressée, 2 si la frappe automatique se répète

Voir Documentation / input / input.txt dans les sources du kernel Linux pour plus de détails.

Les constantes nommées dans /usr/include/linux/input.h sont assez stables, car il s’agit d’une interface kernel-userspace, et les développeurs du kernel s’efforcent de maintenir la compatibilité. (Autrement dit, vous pouvez vous attendre à avoir de nouveaux codes de temps en temps, mais les codes existants changent rarement.)