2010-11-09 15 views
10

è possibile avere un modo multipiattaforma per gestire le chiavi backspace e frecce all'interno di un programma C o OCaml?Riconoscimento dei tasti freccia con stdin

In realtà una soluzione OCaml sarebbe apprezzata, ma molte funzioni standard UNIX sono incluse direttamente nelle chiamate API corrispondenti, quindi non ci dovrebbero essere problemi nel porting di una soluzione C.

Quello che otterrò è catturare i tasti freccia per sovrascrivere il suo comportamento all'interno della shell (riproponendo l'ultima riga o operazioni come queste). Penso che questa cosa cada prima del programma vero e non sia gestita dal codice stesso, quindi non so se sia possibile.

Il programma viene compilato sia su Linux, OS X e Windows (su Cygwin) quindi mi piacerebbe farlo per tutte le piattaforme ..

risposta

8

Ho fatto qualcosa di molto simile recentemente (sebbene il mio codice sia solo Linux). È necessario impostare lo stdin in modalità non canonica per leggere le pressioni dei tasti freccia. Questo dovrebbe funzionare su OS X e Linux e probabilmente funzionerà su Cygwin anche se non posso dirlo con certezza.

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; 

quando la modalità canonica è spento, non è necessario attendere per una nuova riga per leggere da stdin. c_vmin rappresenta il numero minimo di caratteri da leggere prima di tornare (probabilmente vorrai essere in grado di leggere un singolo carattere alla volta) e c_vtime è il tempo di attesa massimo di lettura (in unità di 0.1s).

si potrebbe anche voler impostare c_echo su false in modo che la freccia tasti sono stampate al terminale (ma poi si dovrà stampare manualmente tutto il resto.

maggior parte dei terminali rappresentano sulla pressione dei tasti utilizzando ANSI escape sequences. Se si esegue cat senza argomenti e iniziare a colpire i tasti freccia è possibile vedere le sequenze di escape utilizzate. Essi sono in genere

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

Dove '\ 027' è il valore ASCII per esc

+0

Sono quasi riuscito a farlo funzionare, ma come dovrei gestire l'eco manuale del personaggio? Lo chiedo perché lo stdin viene quindi inoltrato a un parser che analizza riga per riga e lo interpreta. Dovrei mettere nel mezzo e stampare ogni carattere? Ma poi, quando l'utente preme il tasto, dovrebbe sostituire il carattere già stampato con qualcosa di diverso. – Jack

+0

Dopo aver riletto le tue domande penso che usare readline/ledit sia probabilmente la soluzione migliore. Ma se vuoi fare le cose manualmente, dovrai controllare manualmente lo stdin del parser. Leggi da stdin, controlla una sequenza di escape up-char, direttamente sullo stdin del tuo parser. Sto facendo qualcosa di simile qui: https://github.com/aplusbi/reflow/blob/master/reflow.ml nella funzione 'main', dove sto leggendo stdin in una stringa e poi scriverlo in un file_descr . Probabilmente finirai per fare lo stesso, ma prima controllerai le sequenze di escape. –

+0

questi caratteri freccia sono in realtà dall'ECMA 48 qui di seguito http://man7.org/linux/man-pages/man4/console_codes.4.html puoi vedere quali caratteri di controllo di ECMA 48 supporta Linux: sembrerebbe che sposta il cursore a sinistra 5 caratteri che puoi fare \ 027 [5D – aeroson

5

Utilizzare ncurses per estrarre le sequenze per le capacità, freccia e poi guardare per loro quando leggi stdin. Probabilmente è più semplice usare qualcosa come libedit o readline, e lasciare che si occupino del keyhandling.

+0

Un paio di ulteriori note sulla soluzione ncurses: su una singola piattaforma, è cross-terminal, prendendosi cura delle differenze terminali pelose per voi. Inoltre, ci sono versioni disponibili per Windows in modo da poter avere le funzionalità di modifica anche nelle console di Windows. –

2

Il modo standard di supportare l'input da tastiera oltre le righe di caratteri stampabili è attraverso la libreria ncurses, che ha un Ocaml binding. Un'altra possibilità comune è la libreria readline (la più usata da Bash).

Se tutto ciò che state facendo è leggere l'input riga per riga, ma volete che i vostri utenti abbiano un editor di riga decente, non è necessario includere alcun supporto nel vostro programma. Invece, basta dire agli utenti di utilizzare un programma wrapper come rlwrap (che si basa su readline) o ledit. Questi wrapper forniscono cronologia e cronologia delle linee, le due funzionalità elencate come requisiti. Ti consiglio di creare l'elaborazione di input nel tuo programma solo se desideri qualcosa di più elaborato, come il completamento specifico del programma quando l'utente preme la scheda Tab.

1

Backspace è un carattere ASCII e verrà inserito in stdin come qualsiasi altro carattere. La sequenza di escape dei caratteri '\b' è il carattere di backspace.

Per i tasti cursore, questi non sono associati a nessun carattere di controllo, quindi non generare dati nel flusso stdin. L'accesso a basso livello è necessariamente specifico per la piattaforma, sebbene esistano librerie multipiattaforma che astraggono le differenze di piattaforma. Credo che ncurses sia disponibile per tutte le piattaforme che hai menzionato.

+1

Sono sicuro che qualsiasi terminale in modalità canonica non inserirà un carattere backspace nello stdin. Il testo è memorizzato nel buffer, quindi se hai digitato "abcd \ b \ n" avresti ottenuto "abc \ n" nel tuo programma. –

+0

@Niki: Oops, trascorro la maggior parte del mio tempo a codificare sistemi embedded collegati al software di emulatore di terminale dumb. – Clifford

Problemi correlati