use async_std::task; use clap::{App, Arg, ArgMatches, SubCommand}; use crossterm::{ cursor, event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, QueueableCommand, }; use domasi::pomodoro::{Alert, Clock, Config, Status}; use domasi::Pomodoro; use std::io::{stdout, Write}; use std::path::{Path, PathBuf}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread; fn main() { let matches = App::new("Domasi") .version("0.1.0") .author("paoda ") .about("Yet another pomodoro timer.") .subcommand( SubCommand::with_name("start") .about("Start the Pomodoro Timer") .arg( Arg::with_name("alert") .short("a") .long("alert") .value_name("FILE") .takes_value(true) .help("Aloud Sound. (Supports WAV, MP3, Vorbis, FLAC)"), ), ) .get_matches(); // match matches.subcommand() { // ("start", Some(sub_matches)) => start(sub_matches), // _ => {} // } if let ("start", Some(sub_matches)) = matches.subcommand() { start(sub_matches); } } pub fn start(args: &ArgMatches) { let config = Config::default(); let audio_path: PathBuf; if let Some(path) = args.value_of("alert") { audio_path = Path::new(path).to_path_buf(); } else { audio_path = config.data_directory.join("sound/alert.ogg"); } let default_device = rodio::default_output_device().unwrap(); let alert = Alert::new(&audio_path, &default_device); let mut pomodoro = Pomodoro::new(&alert); let (tx, rx): (Sender, Receiver) = channel(); // UI Thread thread::spawn(move || loop { if let Ok(status) = rx.recv() { loop { let (remaining, string) = Clock::get_formatted_string(status); print_overwrite(&string); // Super fun race condition that you gotta handle better :) if remaining < 1 { break; } thread::sleep(Clock::get_polling_interval()); } } }); // User Input Thread thread::spawn(|| { setup_user_input().unwrap(); }); task::block_on(async { pomodoro.start(config, tx).await; }); } fn setup_user_input() -> crossterm::Result<()> { enable_raw_mode()?; get_user_input()?; disable_raw_mode() } fn print_overwrite(text: &str) { let mut stdout = stdout(); stdout.queue(Clear(ClearType::CurrentLine)).unwrap(); stdout.queue(cursor::SavePosition).unwrap(); stdout.write_all(text.as_bytes()).unwrap(); stdout.queue(cursor::RestorePosition).unwrap(); stdout.flush().unwrap(); } fn get_user_input() -> crossterm::Result<()> { loop { if poll(Clock::get_polling_interval())? { if let Event::Key(event) = read()? { handle_key_event(event); } } } } fn handle_key_event(event: KeyEvent) { match event.code { KeyCode::Char('c') => { if let KeyModifiers::CONTROL = event.modifiers { exit_domasi(1) } } KeyCode::Esc | KeyCode::Char('q') => exit_domasi(0), _ => {} } } fn exit_domasi(code: i32) { disable_raw_mode().unwrap(); std::process::exit(code); }