2026-07-28 09:33:50 -05:00
|
|
|
mod app;
|
|
|
|
|
mod audio;
|
|
|
|
|
mod events;
|
|
|
|
|
mod msg;
|
|
|
|
|
mod notification;
|
|
|
|
|
mod terminal;
|
|
|
|
|
mod ui;
|
|
|
|
|
|
2026-07-22 19:57:29 -05:00
|
|
|
use std::{
|
2026-07-27 17:08:31 -05:00
|
|
|
io::{self, stdout},
|
2026-07-28 09:33:50 -05:00
|
|
|
sync::mpsc,
|
2026-07-27 17:08:31 -05:00
|
|
|
thread,
|
|
|
|
|
time::Duration,
|
2026-07-22 19:57:29 -05:00
|
|
|
};
|
|
|
|
|
|
2026-07-28 09:33:50 -05:00
|
|
|
use crossterm::event;
|
2026-07-22 22:15:59 -05:00
|
|
|
use figlet_rs::Toilet;
|
2026-07-28 09:33:50 -05:00
|
|
|
use ratatui::{Terminal, backend::CrosstermBackend};
|
2026-07-22 19:57:29 -05:00
|
|
|
|
2026-07-28 09:33:50 -05:00
|
|
|
use app::Termato;
|
|
|
|
|
use audio::AudioVisualizer;
|
|
|
|
|
use events::handle_event;
|
|
|
|
|
use msg::Message;
|
|
|
|
|
use terminal::TerminalGuard;
|
|
|
|
|
use ui::render_app;
|
2026-07-22 19:32:28 -05:00
|
|
|
|
2026-07-22 19:57:29 -05:00
|
|
|
fn main() -> Result<(), io::Error> {
|
2026-07-22 22:48:27 -05:00
|
|
|
let args: Vec<String> = std::env::args().collect();
|
2026-07-27 17:08:31 -05:00
|
|
|
|
2026-07-22 22:48:27 -05:00
|
|
|
if args.iter().any(|arg| arg == "-h" || arg == "--help") {
|
2026-07-22 23:16:44 -05:00
|
|
|
println!("Usage: termato [options] [work_minutes] [break_minutes]");
|
|
|
|
|
println!();
|
|
|
|
|
println!("Options:");
|
|
|
|
|
println!(" -n, --notify Enable desktop notifications");
|
|
|
|
|
println!(" -h, --help Print this help message");
|
|
|
|
|
println!(" -v, --version Print the version number");
|
2026-07-27 15:00:07 -05:00
|
|
|
println!(" -z, --visualizer Display visualizer of playing audio");
|
2026-07-22 23:16:44 -05:00
|
|
|
println!();
|
2026-07-22 22:48:27 -05:00
|
|
|
println!("Defaults: work_minutes = 25, break_minutes = 5");
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if args.iter().any(|arg| arg == "-v" || arg == "--version") {
|
|
|
|
|
println!("termato version {}", env!("CARGO_PKG_VERSION"));
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-22 22:44:17 -05:00
|
|
|
let _guard = TerminalGuard::new()?;
|
2026-07-22 22:15:59 -05:00
|
|
|
|
2026-07-22 19:57:29 -05:00
|
|
|
let backend = CrosstermBackend::new(stdout());
|
|
|
|
|
let mut terminal = Terminal::new(backend)?;
|
2026-07-22 22:15:59 -05:00
|
|
|
let font = Toilet::smblock().unwrap();
|
2026-07-22 22:48:27 -05:00
|
|
|
|
2026-07-27 15:00:07 -05:00
|
|
|
let enable_visualizer = args.iter().any(|arg| arg == "-z" || arg == "--visualizer");
|
2026-07-22 23:16:44 -05:00
|
|
|
let enable_notifications = args.iter().any(|arg| arg == "-n" || arg == "--notify");
|
2026-07-27 15:00:07 -05:00
|
|
|
|
2026-07-27 17:08:31 -05:00
|
|
|
let visualizer = if enable_visualizer {
|
|
|
|
|
Some(AudioVisualizer::new(64))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2026-07-27 17:38:41 -05:00
|
|
|
|
2026-07-27 17:36:02 -05:00
|
|
|
let is_flag = |arg: &str| {
|
|
|
|
|
arg == "-n"
|
|
|
|
|
|| arg == "--notify"
|
|
|
|
|
|| arg == "-z"
|
|
|
|
|
|| arg == "--visualizer"
|
|
|
|
|
|| arg == "-h"
|
|
|
|
|
|| arg == "--help"
|
|
|
|
|
|| arg == "-v"
|
|
|
|
|
|| arg == "--version"
|
|
|
|
|
};
|
2026-07-27 17:38:41 -05:00
|
|
|
|
2026-07-27 17:36:02 -05:00
|
|
|
let positional_args: Vec<&String> = args.iter().skip(1).filter(|arg| !is_flag(arg)).collect();
|
2026-07-22 23:16:44 -05:00
|
|
|
|
2026-07-27 17:08:31 -05:00
|
|
|
let work_mins = positional_args
|
|
|
|
|
.first()
|
|
|
|
|
.and_then(|s| s.parse().ok())
|
|
|
|
|
.unwrap_or(25);
|
|
|
|
|
|
|
|
|
|
let break_mins = positional_args
|
|
|
|
|
.get(1)
|
|
|
|
|
.and_then(|s| s.parse().ok())
|
|
|
|
|
.unwrap_or(5);
|
|
|
|
|
|
2026-07-27 15:00:07 -05:00
|
|
|
let mut termato = Termato::new(work_mins, break_mins)
|
2026-07-27 17:08:31 -05:00
|
|
|
.with_notifications(enable_notifications)
|
|
|
|
|
.with_visualizer(enable_visualizer);
|
2026-07-22 19:57:29 -05:00
|
|
|
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
2026-07-22 22:15:59 -05:00
|
|
|
|
2026-07-22 19:57:29 -05:00
|
|
|
thread::spawn(move || {
|
2026-07-22 19:32:28 -05:00
|
|
|
loop {
|
2026-07-22 19:57:29 -05:00
|
|
|
thread::sleep(Duration::from_secs(1));
|
|
|
|
|
if tx.send(()).is_err() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-07-22 19:32:28 -05:00
|
|
|
}
|
2026-07-22 19:57:29 -05:00
|
|
|
});
|
|
|
|
|
|
2026-07-28 09:33:50 -05:00
|
|
|
while !termato.should_quit {
|
|
|
|
|
terminal.draw(|f| render_app(&termato, f, &font, visualizer.as_ref()))?;
|
2026-07-22 22:24:48 -05:00
|
|
|
|
2026-07-28 09:33:50 -05:00
|
|
|
if event::poll(Duration::from_millis(16))? {
|
|
|
|
|
let raw_event = event::read()?;
|
|
|
|
|
if let Some(msg) = handle_event(raw_event) {
|
|
|
|
|
termato.update(msg);
|
2026-07-22 19:57:29 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rx.try_recv().is_ok() {
|
2026-07-28 09:33:50 -05:00
|
|
|
termato.update(Message::Tick);
|
2026-07-22 19:57:29 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2026-07-22 19:32:28 -05:00
|
|
|
}
|