use super::keyboard::Keyboard;
use super::terminal::Window;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::{panic, thread, time};
pub struct Config {
pub fps: u32,
}
impl Config {
pub fn new() -> Config {
Config { fps: 30 }
}
pub fn fps(mut self, fps: u32) -> Config {
self.fps = fps;
self
}
}
impl Default for Config {
fn default() -> Self {
Self { fps: 30 }
}
}
#[derive(Default)]
pub struct State {
running: Arc<AtomicBool>,
keyboard: Keyboard,
pub(self) dt: time::Duration,
pub(self) step: usize,
}
impl State {
pub fn run(&self) {
self.running.store(true, Ordering::SeqCst);
}
pub fn stop(&self) {
self.running.store(false, Ordering::SeqCst);
}
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
pub fn keyboard(&self) -> &Keyboard {
&self.keyboard
}
pub fn dt(&self) -> &time::Duration {
&self.dt
}
pub fn step(&self) -> usize {
self.step
}
}
#[derive(Default)]
pub struct App {
config: Config,
state: State,
window: Window,
}
impl App {
pub fn config(config: Config) -> App {
App {
config,
state: State::default(),
window: Window::default(),
}
}
pub fn window(&self) -> &Window {
&self.window
}
pub fn run<F>(&mut self, mut frame_action: F)
where
F: FnMut(&mut State, &mut Window),
{
let expected_duration = time::Duration::from_nanos(1_000_000_000 / self.config.fps as u64);
self.state.run();
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
self.window.open();
while self.state.is_running() {
let now = time::Instant::now();
self.window.clear();
self.state.keyboard.consume_key_events();
frame_action(&mut self.state, &mut self.window);
self.window.draw();
self.state.dt = now.elapsed();
self.state.step += 1;
if let Some(time) = expected_duration.checked_sub(self.state.dt) {
thread::sleep(time);
}
}
self.window.close();
}));
if result.is_err() {
self.window.close();
}
}
}