2014-10-12 10 views
15

Chcę uruchomić plik wykonywalny, który blokuje się na standardowym wejściu, a po naciśnięciu klawisza ten sam znak jest drukowany natychmiast, bez konieczności ponownej próby wpisania .Jak mogę odczytać jedną postać ze stdin bez konieczności wciskania enter?

Jak mogę odczytać jedną postać ze stdin bez konieczności uderzania Wpisz? Zacząłem od tego przykładu:

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

Spojrzałem za pośrednictwem interfejsu API i próbował zastępując read_line() z bytes(), ale wszystko staram wymaga mnie uderzyć Wprowadź zanim dojdzie do odczytu.

To pytanie został poproszony o C/C++, ale nie wydaje się być nie standardowy sposób to zrobić: Capture characters from standard input without waiting for enter to be pressed

To może nie być wykonalne w Rust rozważa to nie jest proste w C/C++.

+3

Jest to problem dotyczący platformy, a nie języka. W oknach dostępne są funkcje wprowadzania znaków, ale na systemie unix/linux trzeba wyjść z trybu buforowania linii. –

+0

Możesz użyć funkcji 'getch' ze wspomnianego łącza SO. Musisz tylko skompilować to w obiekt współdzielony i użyć go z Rust: https://gist.github.com/ihrwein/a4558d63d9250ee0bbf6 Będziesz potrzebował kompilatora C i działa on tylko na Linuksie (przynajmniej testowałem go tam) . –

Odpowiedz

10

Użyj jednej z dostępnych bibliotek "ncurses", na przykład this.

Dodaj zależność w Cargo

[dependencies] 
ncurses = "5.86.0" 

i obejmują w main.rs:

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

Wykonaj przykłady w bibliotece zainicjować ncurses i czekać na jedno wejście znaków takich jak to:

initscr(); 
/* Print to the back buffer. */ 
printw("Hello, world!"); 

/* Update the screen. */ 
refresh(); 

/* Wait for a key press. */ 
getch(); 

/* Terminate ncurses. */ 
endwin(); 
+0

To działa, ale wygląda na to, że niemożliwe jest uniknięcie wyczyszczenia ekranu wymuszonego przez 'initscr()', jak to omówiono [tutaj] (http://stackoverflow.com/questions/4772061/curses-library-c-getch- bez ekranu czyszczącego) i [tam] (http://stackoverflow.com/questions/654471/ncurses-initialization- without-clearing-the-screen). – dojuba

9

Podczas gdy rozwiązanie @ Jona używa ncurses działa, ncurses czyści ekran po projekcie. Wpadłem na to rozwiązanie, które używa termios crate do mojego małego projektu, aby nauczyć się Rusta. Chodzi o to, aby zmodyfikować flagi ECHO i uzyskując dostęp do tcsetattr poprzez wiązania termios.

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 
} 

Zaletą czyta jeden bajt jest przechwytywanie klawiszy strzałek, ctrl itp Rozszerzone klawisze funkcyjne nie są ujęte (choć ncurses może uchwycić te).

To rozwiązanie jest przeznaczone dla platform typu UNIX. Nie mam doświadczenia z Windows, ale według tegomożna osiągnąć coś podobnego za pomocą SetConsoleMode w systemie Windows.

Powiązane problemy