flux-tui 0.4.5

Fast and lightweight Terminal UI drawing library
Documentation
use std::{
	rc::Rc,
	sync::atomic::{AtomicU64, Ordering},
	time::*,
};

/// This handles rendering stable `n` frames per second
/// It will try to approximately render a frame each '1 / framerate' secs
pub struct FramerateStabilizer {
	framerate: u64,
	start: Instant,
	last_render: Instant,
	rendered_frames: u64,
	fps: Rc<AtomicU64>,
}

impl FramerateStabilizer {
	/// Initializes a new instance of [FramerateStabilizer] based on the given static framerate
	pub fn new(framerate: u64) -> Self {
		Self {
			framerate,
			start: Instant::now(),
			last_render: Instant::now(),
			rendered_frames: u64::MIN,
			fps: Rc::new(AtomicU64::new(u64::MIN)),
		}
	}

	/// This will call the given rendering callback when its due
	pub fn render(&mut self, mut render_callback: impl FnMut()) -> bool {
		let next_due = Duration::from_secs(1) / self.framerate as u32;
		if self.last_render.elapsed() >= next_due {
			let start = Instant::now();
			render_callback();
			self.last_render = start;
			self.rendered_frames += 1;
			self.fps.store(
				Self::calc_fps(self.rendered_frames, &self.start),
				Ordering::Relaxed,
			);
			true
		}
		else {
			false
		}
	}

	// Returns the fps counter
	pub fn fps(&self) -> Rc<AtomicU64> {
		self.fps.clone()
	}

	/// Returns the currently rendered frames per second
	/// May differ from the actual framerate due to longer rendering processes
	fn calc_fps(frames: u64, start_time: &Instant) -> u64 {
		let mut elapsed = start_time.elapsed().as_secs();
		if elapsed == 0 {
			elapsed = 1;
		}
		frames / elapsed
	}
}