2014-10-12 13 views
15

Ich möchte eine ausführbare Datei ausführen, die auf stdin blockiert, und wenn eine Taste gedrückt wird, wird das gleiche Zeichen sofort gedruckt, ohne Geben Sie ein, drücken Sie.Wie kann ich ein Zeichen von stdin lesen, ohne Enter drücken zu müssen?

Wie kann ich ein Zeichen von der Standardeingabe lesen, ohne einzugeben? Geben Sie ein? Ich begann mit diesem Beispiel:

fn main() { 
    println!("Type something!"); 

    let mut line = String::new(); 
    let input = std::io::stdin().read_line(&mut line).expect("Failed to read line"); 

    println!("{}", input); 
} 

ich über die API sah und versuchte read_line() mit bytes() ersetzen, aber alles, was ich versuche, erfordert mich drücken Sie die Eingabetaste, bevor Lese auftritt.

Diese Frage wurde für C/C++ gefragt, aber es scheint keine Standardmethode zu sein, es zu tun: Capture characters from standard input without waiting for enter to be pressed

Es ist nicht in Rust machbar sein könnte, es ist nicht einfach in C/C++ unter Berücksichtigung.

+3

Dies ist ein Plattformproblem, kein Sprachproblem. Unter Windows gibt es Zeicheneingabefunktionen, aber unter Unix/Linux müssen Sie den Terminal außerhalb der Zeile gepufferten Modus nehmen. –

+0

Sie können die 'Getch'-Funktion von der erwähnten SO-Verknüpfung verwenden. Sie müssen das nur in ein gemeinsames Objekt kompilieren und von Rust aus verwenden: https://gist.github.com/ihrwein/a4558d63d9250ee0bbf6 Sie benötigen einen C-Compiler und es funktioniert nur unter Linux (zumindest habe ich es dort getestet) . –

Antwort

10

Verwenden Sie eine der 'ncurses' Bibliotheken, die jetzt verfügbar sind, zum Beispiel this.

die Abhängigkeit in Fracht hinzufügen

[dependencies] 
ncurses = "5.86.0" 

und umfassen in main.rs:

extern crate ncurses; 
use ncurses::*; // watch for globs 

die Beispiele in der Bibliothek Folgen ncurses zu initialisieren und für einzelne Zeicheneingabe wie folgt warten:

+0

Das funktioniert, aber es scheint unmöglich zu sein, das Löschen des Bildschirms zu verhindern, das von 'initscr()' wie hier diskutiert [http://stackoverflow.com/questions/4772061/curses-library-c-getch- Ohne-Clearing-Bildschirm) und [dort] (http://stackoverflow.com/questions/654471/ncurses-initialization-without-clearing-the-screen). – dojuba

9

Während @ Jons Lösung mit Ncurses funktioniert, löscht Ncurses den Bildschirm von Entwurf. Ich kam mit dieser Lösung, die die termios crate für mein kleines Projekt verwendet, um Rust zu lernen. Die Idee besteht darin, die Flags ECHO und ICANON durch Zugriff auf tcsetattr über Terminologiebindungen zu ändern.

extern crate termios; 
use std::io; 
use std::io::Read; 
use std::io::Write; 
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr}; 

fn main() { 
    let stdin = 0; // couldn't get std::os::unix::io::FromRawFd to work 
        // on /dev/stdin or /dev/tty 
    let termios = Termios::from_fd(stdin).unwrap(); 
    let mut new_termios = termios.clone(); // make a mutable copy of termios 
              // that we will modify 
    new_termios.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode 
    tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap(); 
    let stdout = io::stdout(); 
    let mut reader = io::stdin(); 
    let mut buffer = [0;1]; // read exactly one byte 
    print!("Hit a key! "); 
    stdout.lock().flush().unwrap(); 
    reader.read_exact(&mut buffer).unwrap(); 
    println!("You have hit: {:?}", buffer); 
    tcsetattr(stdin, TCSANOW, & termios).unwrap(); // reset the stdin to 
                // original termios data 
} 

Ein Vorteil der Verwendung eines einzelnen Byte Lesen Pfeiltasten wird die Erfassung, Abfrage usw. Erweiterte F-Tasten werden nicht erfasst (obwohl ncurses kann diese erfassen).

Diese Lösung ist für UNIX-ähnliche Plattformen gedacht. Ich habe keine Erfahrung mit Windows, aber nach dieser forum kann vielleicht etwas ähnliches erreicht werden mit SetConsoleMode in Windows.

Verwandte Themen