[F] Fix async, peacefully exit

This commit is contained in:
Azalea Gui
2023-03-08 19:54:42 -05:00
parent e400a223af
commit 393e76fa87
+48 -28
View File
@@ -1,24 +1,27 @@
#![feature(let_chains)] #![feature(let_chains)]
use std::{mem, thread}; use std::{mem};
use std::io::{Read, stdin, stdout, Stdout, Write}; use std::io::Write;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::process::exit;
use std::string::ToString; use std::string::ToString;
use std::sync::{Arc, Mutex}; use std::sync::{Arc};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use rand::Rng; use rand::Rng;
use termion::color::{Fg, Rgb};
use termion::cursor::Goto; use termion::cursor::Goto;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
use tokio::io::{AsyncReadExt, stdin, stdout, AsyncWriteExt};
use tokio::sync::Mutex;
use crate::cowsay::gen_bubble_ascii; use crate::cowsay::gen_bubble_ascii;
mod cowsay; mod cowsay;
const RESET: &str = "\x1b[0m"; const RESET: &str = "\x1b[0m";
const CLEAR: &str = "\x1b[2J";
const HIDE_CURSOR: &str = "\x1b[?25l";
const SHOW_CURSOR: &str = "\x1b[?25h";
/// Constants /// Constants
const SNOW_DENSITY: f32 = 0.04; // Snow particles per pixel on screen const SNOW_DENSITY: f32 = 0.04; // Snow particles per pixel on screen
@@ -113,6 +116,7 @@ struct Mutes {
last_update: Instant, last_update: Instant,
snow: Vec<SnowParticle>, snow: Vec<SnowParticle>,
should_exit: bool,
} }
struct Main { struct Main {
@@ -188,6 +192,7 @@ impl Mutes {
buf, last_buf, buf, last_buf,
last_update: Instant::now(), last_update: Instant::now(),
snow, snow,
should_exit: false,
} }
} }
@@ -330,7 +335,7 @@ fn draw_ascii_frame(mt: &mut Mutes, cn: &Consts) {
} }
async fn start_update_loop(stdout: &mut RawTerminal<Stdout>, mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> { async fn start_update_loop(mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> {
// Start the loop // Start the loop
loop { loop {
@@ -339,7 +344,8 @@ async fn start_update_loop(stdout: &mut RawTerminal<Stdout>, mt: Arc<Mutex<Mutes
let mut txt: String; let mut txt: String;
{ {
let mut mt = mt.lock().unwrap(); let mut mt = mt.lock().await;
if mt.should_exit { break; }
// Calculate the delta time // Calculate the delta time
let dt = (now - mt.last_update).as_secs_f32(); let dt = (now - mt.last_update).as_secs_f32();
@@ -357,21 +363,23 @@ async fn start_update_loop(stdout: &mut RawTerminal<Stdout>, mt: Arc<Mutex<Mutes
let draw_time = (end - now).as_secs_f32(); let draw_time = (end - now).as_secs_f32();
txt.push_str(&*format!("\rDraw time: {:.2}ms", draw_time * 1000.0)); txt.push_str(&*format!("\rDraw time: {:.2}ms", draw_time * 1000.0));
write!(stdout, "{}", txt)?; stdout().write_all(txt.as_bytes()).await?;
// print!("{}", txt);
// Use tokio to sleep for 1/20th of a second // Use tokio to sleep for 1/20th of a second
tokio::time::sleep(Duration::from_millis(1000 / 20)).await; tokio::time::sleep(Duration::from_millis(1000 / 20)).await;
} }
Ok(())
} }
async fn pull_input(mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> { async fn pull_input(mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> {
// Read keyboard input in a loop // Read keyboard input in a loop
let stdin = stdin(); let mut stdin = stdin();
let mut stdin = stdin.lock();
let mut buf = [0; 3]; let mut buf = [0; 3];
loop { loop {
// Read a byte from stdin // Read a byte from stdin
let n = stdin.read(&mut buf).unwrap(); let n = stdin.read(&mut buf).await?;
if n == 0 { break; } if n == 0 { break; }
let str = String::from_utf8_lossy(&buf[..n]).to_string(); let str = String::from_utf8_lossy(&buf[..n]).to_string();
@@ -384,18 +392,23 @@ async fn pull_input(mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> {
} }
// print!("\r\n"); // print!("\r\n");
let move_x = |amount: i32| { {
let mut mt = mt.lock().unwrap(); let mut mt = mt.lock().await;
let mut move_x = |amount: i32| {
mt.x = (mt.x as i32 + amount).max(0).min((mt.w - 1) as i32) as u16; mt.x = (mt.x as i32 + amount).max(0).min((mt.w - 1) as i32) as u16;
}; };
// Switch on the key // Switch on the key
match str.as_str() { match str.as_str() {
"q" => break, "q" => {
mt.should_exit = true;
break;
},
"a" => move_x(-1), "a" => move_x(-1),
"d" => move_x(1), "d" => move_x(1),
_ => (), _ => (),
} }
}
// Sleep for 1/100th of a second // Sleep for 1/100th of a second
tokio::time::sleep(Duration::from_millis(30)).await; tokio::time::sleep(Duration::from_millis(30)).await;
@@ -404,33 +417,40 @@ async fn pull_input(mt: Arc<Mutex<Mutes>>, cn: &Consts) -> Result<()> {
Ok(()) Ok(())
} }
fn main() { fn run() -> Result<()> {
pretty_env_logger::init(); pretty_env_logger::init();
let cn: &Consts = Box::leak(Box::new(Consts::new())); let cn: &Consts = Box::leak(Box::new(Consts::new()));
let mut mt = Arc::new(Mutex::new(Mutes::new(&cn))); let mt = Arc::new(Mutex::new(Mutes::new(&cn)));
// Set terminal to raw mode // Set terminal to raw mode
let mut stdout = stdout().into_raw_mode().unwrap(); let mut out = std::io::stdout().into_raw_mode().unwrap();
// Clear the screen // Clear the screen
write!(stdout, "{}", termion::clear::All).unwrap(); out.write(CLEAR.as_ref())?;
write!(stdout, "{}", termion::cursor::Hide).unwrap(); out.write(HIDE_CURSOR.as_ref())?;
stdout.flush().unwrap(); out.flush()?;
// Start update_loop and pull_input concurrently and wait for them to finish // Start update_loop and pull_input concurrently and wait for them to finish
let rt = tokio::runtime::Runtime::new().unwrap(); let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { rt.block_on(async {
let update_loop = start_update_loop(&mut stdout, mt.clone(), cn); let update_loop = start_update_loop( mt.clone(), cn);
let pull_input = pull_input(mt.clone(), cn); let pull_input = pull_input(mt.clone(), cn);
tokio::try_join!(update_loop, pull_input)?; tokio::try_join!(update_loop, pull_input)?;
// tokio::try_join!(pull_input)?; // tokio::try_join!(pull_input)?;
Ok::<(), Error>(()) Ok::<(), Error>(())
}).unwrap(); })?;
// Finish execution, set terminal back to normal // Reset the terminal
stdout.suspend_raw_mode().unwrap(); out.suspend_raw_mode().unwrap();
print!("{}", termion::cursor::Show); out.write(SHOW_CURSOR.as_ref())?;
print!("{}", termion::clear::All); out.write(CLEAR.as_ref())?;
stdout.flush().unwrap(); out.write("Exiting...".as_ref())?;
out.flush()?;
Ok(())
}
fn main() {
run().expect("Error running program");
} }