2010-11-09 25 views
10

est-il possible d'avoir une manière multi-plateforme de gérer les touches de retour arrière et de flèches dans un programme C ou OCaml?Reconnaissance des touches fléchées avec stdin

En fait, une solution OCaml serait appréciée, mais de nombreuses fonctions unix standard sont directement intégrées aux appels API correspondants, donc il ne devrait pas y avoir de problème à porter une solution C. Ce que je vais faire est d'attraper les touches fléchées pour remplacer son comportement à l'intérieur du shell (en repropostant la dernière ligne ou des opérations comme celles-ci). Je pense que cette chose tombe avant le programme et n'est pas traitée par le code lui-même, donc je ne sais pas si c'est possible.

Le programme est compilé soit sous Linux, OS X et Windows (Cygwin) donc je voudrais le faire pour toutes les plateformes ..

Répondre

8

J'ai fait quelque chose de très similaire récemment (bien que mon code soit Linux uniquement). Vous devez définir stdin en mode non-canonique pour lire les pressions sur les touches fléchées. Cela devrait fonctionner sur OS X et Linux et fonctionnera probablement sur Cygwin bien que je ne puisse pas le dire avec certitude.

open Unix 
let terminfo = tcgetattr stdin in 
    let newterminfo = {terminfo with c_icanon = false; c_vmin = 0; c_vtime = 0} in 
    at_exit (fun _ -> tcsetattr tsdin TCSAFLUSH terminfo); (* reset stdin when you quit*) 
    tcsetattr stdin TCSAFLUSH newterminfo; 

lorsque le mode canonique est désactivé, vous n'avez pas besoin d'attendre une nouvelle ligne afin de lire stdin. c_vmin représente le nombre minimum de caractères à lire avant de retourner (vous voulez probablement être capable de lire un seul caractère à la fois) et c_vtime est le temps d'attente de lecture maximum (en unités de 0,1s).

Vous pouvez également définir c_echo false afin que les touches fléchées sont imprimées sur le terminal (mais vous devrez imprimer manuellement tout le reste.

La plupart des terminaux représentent flèche touches à l'aide ANSI escape sequences. Si vous exécutez cat sans arguments et commencez à frapper les touches fléchées, vous pouvez voir les séquences d'échappement utilisées. Ils sont généralement

up - "\027[A" 
down - "\027[B" 
left - "\027[D" 
right - "\027[C" 

Où \ 027 'est la valeur ascii pour esc

+0

J'ai presque réussi à faire ce travail, mais comment dois-je gérer l'écho manuel du personnage? Je demande cela parce que le stdin est ensuite transmis à un analyseur qui analyse ligne par ligne et l'interprète. Je devrais placer au milieu et imprimer chaque char? Mais lorsque l'utilisateur appuie sur la touche haut, il doit remplacer le caractère déjà imprimé par quelque chose de différent. – Jack

+0

Après avoir relu vos questions, je pense que l'utilisation de readline/ledit est probablement votre meilleur pari. Mais si vous voulez faire les choses manuellement, alors vous devrez contrôler manuellement le stdin de votre analyseur. Lire à partir de stdin, vérifier une séquence d'échappement vers le haut-char, directement à stdin de votre analyseur. Je fais quelque chose de similaire ici: https://github.com/aplusbi/reflow/blob/master/reflow.ml dans la fonction 'main', où je suis en train de lire stdin dans une chaîne puis de l'écrire dans un fichier_descr . Vous finirez probablement par faire la même chose, mais vérifiez d'abord les séquences d'échappement. –

+0

ces caractères fléchés sont en fait de ECMA 48 standartd ici http://man7.org/linux/man-pages/man4/console_codes.4.html vous pouvez voir quels caractères de contrôle de ECMA 48 Linux prend en charge: il semblerait que déplacer le curseur à gauche 5 caractères que vous pouvez faire \ 027 [5D – aeroson

5

Utilisez ncurses pour extraire les séquences pour les fonctionnalités clés de flèche puis regarder pour eux quand tu lis stdin. Il est probablement plus facile d'utiliser quelque chose comme libedit ou readline à la place, et laissez-le faire avec le keyhandling.

+0

Quelques autres remarques sur la solution ncurses: sur une seule plate-forme, elle est cross-terminal, en prenant soin des différences de terminaux velues pour vous. En outre, il existe des versions disponibles pour Windows afin que vous puissiez également avoir les fonctionnalités d'édition dans les consoles Windows. –

2

La prise en charge standard de la saisie au clavier au-delà des lignes de caractères imprimables s'effectue via la bibliothèque ncurses, qui a un Ocaml binding. Une autre possibilité courante est la bibliothèque readline (la plus utilisée par Bash).

Si tout ce que vous faites est la lecture ligne d'entrée par ligne, mais que vos utilisateurs d'avoir un éditeur de ligne décent, il n'y a pas besoin d'inclure un soutien dans votre programme. Au lieu de cela, il suffit de dire à vos utilisateurs d'utiliser un programme d'emballage tels que rlwrap (qui est basé sur readline) ou ledit. Ces enveloppes fournissent l'édition et l'historique des lignes, les deux fonctions que vous répertoriez en tant que vos besoins. Je recommanderais que vous construisiez le traitement d'entrée dans votre programme seulement si vous voulez quelque chose d'exaltant, tel que l'accomplissement spécifique au programme quand l'utilisateur presse Tab.

1

Retour arrière est un caractère ASCII et sera placé dans stdin comme tout autre caractère. La séquence d'échappement de caractères '\b' est le caractère de retour arrière.

Pour les touches de curseur, elles ne sont associées à aucun caractère de contrôle, donc ne générez pas de données dans le flux stdin. L'accès de bas niveau est nécessairement spécifique à la plate-forme, bien qu'il existe des bibliothèques multi-plateformes qui éliminent les différences de plate-forme. Je crois que ncurses est disponible pour toutes les plateformes que vous avez mentionnées.

+1

Je suis à peu près sûr que n'importe quel terminal en mode canonique ne placera pas un caractère de retour arrière dans stdin. Le texte est tamponné, donc si vous avez tapé "abcd \ b \ n" vous auriez "abc \ n" dans votre programme. –

+0

@Niki: Oops, je passe le plus clair de mon temps à coder des systèmes embarqués connectés à un logiciel d'émulation de terminal binaire. – Clifford