Async streams and raw terminal

Greetings,

I am attempting to write a program that incorporates a timer and async stdin.

There is a nice example at:

I've modified it a little:

use std::io::stdout;
use std::mem::drop;
use std::process::exit;
use std::time::Duration;

use futures::{future::Either, StreamExt};
use tokio::{io::stdin, time::interval};

use termion::raw::{
    IntoRawMode,
    RawTerminal,
};
use termion_input_tokio::TermReadAsync;
use tokio_stream::wrappers::IntervalStream;

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    run().await;
    Ok(())
}

async fn run(
) {
    let raw_term = stdout().into_raw_mode().unwrap();

    let input = stdin().keys_stream().map(Either::Right);
    let ticks = IntervalStream::new(interval(Duration::from_secs(1))).map(Either::Left);

    let events = futures::stream::select(ticks, input);

    events
        .fold(
            raw_term, |raw_term, it| {
                println!("Event: {:?}\r", it);
                match it {
                    // FIXME.. The following does not properly drop the raw_term...
                    Either::Right(_) => process(&raw_term),
                    _ => (),
                }
                async {
                    raw_term
                }
            }
        )
        .await
    ;
}

fn process(
    raw_term: &RawTerminal<std::io::Stdout>,
) {
    drop(raw_term);
    exit(0);
}
// Here are my Cargo dependencies...
tokio               = { version="1", features=["full"] }
termion             = "1.5.4"
futures             = "0.3.21"
termion-input-tokio = "0.3"
tokio-stream        = "0.1.8"

If I replace the branch arm in the above "match" to the original block of code, the raw_term is properly dropped and my terminal isn't "screwed up":

                    Either::Right(_) => {
                        drop(raw_term);
                        exit(0);
                    }

Any ideas about why the function call doesn't work, but the code block in the match arm does work?

Thanks for any help!

-m

1 Like

Sorry the code is malformatted. I should have previewed first. Mea culpa.

Can you edit the post to fix the formatting? There should be a :pencil2: icon somewhere.

1 Like

Thanks @Michael-F-Bryan for the hint. Appreciated!

1 Like

If you use drop on a reference, then that doesn't do anything because references don't have destructors. Remove the ampersand.

Thanks for the help @alice!

Since I don't want to move the raw_term, I just return a bool from the "process" function and exit based on that bool.

async fn run(
) {
    let raw_term = stdout().into_raw_mode().unwrap();

    let input = stdin().keys_stream().map(Either::Right);
    let ticks = IntervalStream::new(interval(Duration::from_secs(1))).map(Either::Left);

    let events = futures::stream::select(ticks, input);

    events
        .fold(
            raw_term, |raw_term, it| {
                println!("Event: {:?}\r", it);
                let quit: bool = match it {
                    Either::Right(_) => process(&raw_term),
                    _ => false,
                };
                if quit {
                    drop(raw_term);
                    exit(0);
                }   
                async {
                    raw_term
                }
            }       
        )           
        .await  
    ;           
}                   
                
fn process( 
    raw_term: &RawTerminal<std::io::Stdout>,
) -> bool {
    true
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.