For context, I am using the libraries bevy and print_typewriter.

I noticed that before the program even starts, I am able to type in characters. This is bad, since when I ask for the user’s input, the previous characters are included inside of it.

How do I make sure that only the user’s input after the program starts, and after I print a question gets in?

  • BinetteOP
    link
    fedilink
    English
    arrow-up
    1
    ·
    4 months ago

    Took me a while to get it to work because of bevy stuff, but it works a lot better! Is there a way to flush stdin without requiring the user to press a key?

    Thanks a lot!

    • ssokolow
      link
      fedilink
      English
      arrow-up
      2
      ·
      4 months ago

      The answer depends on what’s actually going on. I’ve yet to do this sort of thing in Rust but, when I was doing it in Python and initializing curses for a TUI (i.e. like a GUI but made using text), I remember the curses wrapper’s initialization stuff handling that for me.

      Because of the way terminal emulation works, there are actually two buffers: the one inside the fake terminal hardware that lets the user accumulate and backspace characters before sending the data over the fake wire when you press Enter and the one on the receiving end of the wire.

      Paste your initialization code for your curses wrapper and I’ll take a look at it after I’ve had breakfast.

      • BinetteOP
        link
        fedilink
        English
        arrow-up
        1
        ·
        edit-2
        4 months ago

        Here’s the main function

        fn main() {
            let mut main_terminal = terminal::new_terminal(caps::Capabilities::new_from_env().unwrap()).unwrap();
        
            terminal::Terminal::set_raw_mode(&mut main_terminal);
        
            App::new()
                .insert_non_send_resource(main_terminal)
                .init_resource::<TextDuration>()
                .add_systems(Startup, enter_name)
                .run();
        }
        

        And here are the function enter name and flush_sdin

        fn enter_name(duration: Res<TextDuration>, main_terminal: NonSendMut<terminal::SystemTerminal>){
            print_text(&duration, Speeds::Default, String::from("Please enter your name: "));
            flush_stdin();
            terminal::Terminal::set_cooked_mode(main_terminal.into_inner());
            let mut name = String::new();
            io::stdin().read_line(&mut name);
            print_text(&duration, Speeds::Default, String::from(format!("Hello, {}!\n", name.trim().green())));
        
        }
        
        fn flush_stdin(){
            let mut clean_buffer = [0; 4];
            let _ =io::stdin().read(&mut clean_buffer[..]);
        }
        

        When I use flush_stdin, I have to press a key before submitting the actual input

        Edit: I forgot to mention, the print_text function is just something I used to make print_typed! use different speeds and stuff. I haven’t finished the different speeds for now, but that’s not important

        
        fn print_text(duration: &Res<TextDuration>, speed: Speeds, text: String){
            match speed {
                Speeds::Default => print_typed!((duration.default), "{}", text),
                
            }
        }
        
        
        • ssokolow
          link
          fedilink
          English
          arrow-up
          1
          ·
          edit-2
          4 months ago

          What you’re running into is that read() does blocking I/O by default and, while you can change that, both approaches (checking for pending data before reading or setting stdin into non-blocking mode so it’ll return immediately either way) require different code for Windows and for POSIX, so it’s best to let your platform abstraction (i.e. termwiz) handle that.

          I have no experience with Bevy or Termwiz, but see if this does what you want once you’ve fixed any “I wrote this but never tried to compile it” bugs:

          use std::time::Duration;
          
          fn flush_stdin(main_terminal: terminal::SystemTerminal) {
              while let Ok(Some(_)) = main_terminal.poll_input(Some(Duration::ZERO)) { }
          }
          

          If I’ve understood the termwiz docs correctly, that’ll pull and discard keypresses off the input buffer until none are left and then return.

          Note that I’m not sure how it’ll behave if you call enter_name a second time and you’re still in cooked mode from a previous enter_name. My experience is with raw mode. (And it’s generally bad form to have a function change global state as a hidden side-effect and not restore what it was before.)

          • BinetteOP
            link
            fedilink
            arrow-up
            1
            ·
            4 months ago

            Thanks a lot man! After debuggin for a while it worked!

            I was also wondering, where do you learn that kind of stuff? I’m currently learning and would like to be as resourceful as possible.

            • ssokolow
              link
              fedilink
              English
              arrow-up
              2
              ·
              edit-2
              4 months ago

              I’m sure other people have a more teachable way of learning these things but I’m just one of those nerdy guys who’s been reading technical materials for pleasure since he was in elementary school and gathered the core “this will tell you how the system is designed so you know what to ask about” knowledge along the way.

              For example, I just ran across The TTY Demystified, Things Every Hacker Once Knew, The Art of UNIX Programming, and A Digital Media Primer for Geeks on my own. (Sort of the more general version of “It showed up in the YouTube sidebar one day” or “I landed on it while wandering Wikipedia for fun”.)

              Beyond that, it’s mostly “exposing yourself to things the professionals experience”, like running a Linux distro like Archlinux or Gentoo Linux which expect you to tinker under the hood and give you documentation to do so, maybe working through LinuxFromScratch to get exposed to how the pieces of Linux fit together, reading periodicals like LWN (articles become un-paywalled after a week, if you’re tight on money or need time to convince yourself it’s worthwhile), and watching conference talks on YouTube like code::dive conference 2014 - Scott Meyers: Cpu Caches and Why You Care or “NTFS really isn’t that bad” - Robert Collins (LCA 2020).

              (I switched to Linux permanently while I was still in high school, several years before YouTube even existed, and I’m only getting back into Windows now that I’m buying used books to start learning hobby-coding for MS-DOS beyond QBasic 1.1, Windows 3.1, Windows 95 beyond Visual Basic 6, and classic Mac OS, so I haven’t really picked up much deep knowledge for Windows.)

              The best I can suggest for directed learning is to read up on how the relevant system (eg. the terminal, UNIX I/O) works until you start to get a sense for which are the right questions to ask.