extern crate sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::render::Canvas;
use sdl2::video::Window;
use sdl2::EventPump;
use sdl2::Sdl;
use std::thread::sleep;
use std::time::{Duration, Instant};
use drawable::{DrawSettings, DSETTINGS_MAIN, DSETTINGS_NOTES};
use latex::render::render_all_equations;
use scene::{Action, Scene};
const FPS_PRINT_RATE: Duration = Duration::from_millis(1000);
pub enum YEvent {
Step,
StepSlide,
Other(Event),
}
pub struct WindowSettings {
pub draw_settings: DrawSettings,
pub window_size: (u32, u32),
}
pub struct WindowManagerSettings {
pub windows: Vec<(String, WindowSettings)>,
pub event_step_rule: Box<dyn Fn(Event) -> bool>,
pub event_step_slide_rule: Box<dyn Fn(Event) -> bool>,
pub quit_rule: Box<dyn Fn(Event) -> bool>,
}
pub fn default_settings(title: &str) -> WindowManagerSettings {
let title = title.to_string();
let mut notes_title = title.clone();
notes_title.push_str(" - Notes");
WindowManagerSettings {
windows: vec![(title, WSETTINGS_MAIN), (notes_title, WSETTINGS_NOTES)],
event_step_rule: Box::new(|event| match event {
Event::KeyDown {
keycode: Some(Keycode::Space),
..
} => true,
Event::MouseButtonDown { .. } => true,
_ => false,
}),
event_step_slide_rule: Box::new(|event| match event {
Event::KeyDown {
keycode: Some(Keycode::Return),
..
} => true,
_ => false,
}),
quit_rule: Box::new(|event| match event {
Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => true,
Event::Quit { .. } => true,
_ => false,
}),
}
}
pub const WSETTINGS_MAIN: WindowSettings = WindowSettings {
draw_settings: DSETTINGS_MAIN,
window_size: (1200, 800),
};
pub const WSETTINGS_NOTES: WindowSettings = WindowSettings {
draw_settings: DSETTINGS_NOTES,
window_size: (600, 400),
};
pub struct WindowManager<T: Scene> {
pub context: Sdl,
pub canvases: Vec<(WindowSettings, Canvas<Window>)>,
pub event_step_rule: Box<dyn Fn(Event) -> bool>,
pub event_step_slide_rule: Box<dyn Fn(Event) -> bool>,
pub quit_rule: Box<dyn Fn(Event) -> bool>,
pub event_pump: EventPump,
pub scene: T,
time_manager: Option<TimeManager>,
tick: usize,
}
struct TimeManager {
last_time: Instant,
last_fps_print: Instant,
durs: Vec<Duration>,
}
impl<T: Scene> WindowManager<T> {
pub fn init_window(mut scene: T, settings: WindowManagerSettings) -> WindowManager<T> {
scene.register();
let start = Instant::now();
eprintln!("Rendering LaTeX...");
render_all_equations().expect("Can't render!");
eprintln!("Loading scenes");
scene.load();
let delta = Instant::now() - start;
eprintln!(
"Done! Took {:.2}s",
delta.as_secs() as f64 + delta.subsec_millis() as f64 / 1000.
);
let mut canvases = Vec::with_capacity(settings.windows.len());
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
for (title, settings) in settings.windows {
let window = video_subsystem
.window(&title, settings.window_size.0, settings.window_size.1)
.position_centered()
.resizable()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
canvases.push((settings, canvas));
}
let event_pump = sdl_context.event_pump().unwrap();
WindowManager {
context: sdl_context,
canvases,
event_step_rule: settings.event_step_rule,
event_step_slide_rule: settings.event_step_slide_rule,
quit_rule: settings.quit_rule,
event_pump,
scene,
time_manager: None,
tick: 0,
}
}
fn process_events(&mut self) -> bool {
if let Some(ref mut tm) = self.time_manager {
let dt = tm.dt();
self.scene.update(dt);
match self.scene.action() {
Action::Done => {
return false;
}
_ => {}
}
for event in self.event_pump.poll_iter() {
if (*self.quit_rule)(event.clone()) {
return false;
} else if (*self.event_step_slide_rule)(event.clone()) {
self.scene.event(YEvent::StepSlide)
} else if (*self.event_step_rule)(event.clone()) {
self.scene.event(YEvent::Step)
} else {
self.scene.event(YEvent::Other(event))
}
}
} else {
self.time_manager = Some(TimeManager::new());
}
true
}
fn draw(&mut self) {
for (ref mut settings, ref mut canvas) in &mut self.canvases {
canvas.set_draw_color(Color::RGBA(
settings.draw_settings.background_color.0,
settings.draw_settings.background_color.1,
settings.draw_settings.background_color.2,
255,
));
canvas.clear();
settings.window_size = canvas.window().size();
self.scene.draw(canvas, settings.draw_settings);
canvas.present();
}
}
pub fn start(&mut self) {
loop {
self.tick += 1;
self.draw();
if !self.process_events() {
break;
}
sleep(Duration::from_millis(5));
}
}
}
impl TimeManager {
fn new() -> TimeManager {
TimeManager {
last_time: Instant::now(),
last_fps_print: Instant::now(),
durs: Vec::new(),
}
}
fn dt(&mut self) -> f64 {
let now = Instant::now();
let diff = now - self.last_time;
self.last_time = now;
self.durs.push(diff);
if now - self.last_fps_print > FPS_PRINT_RATE {
let num_dur = self.durs.len() as u32;
let avg_dur: Duration = self.durs.drain(..).sum::<Duration>() / num_dur;
let fps = 1. / (avg_dur.as_secs() as f64 + avg_dur.subsec_millis() as f64 / 1000.);
eprintln!("\x1b[KFPS: {:.2}", fps);
self.last_fps_print = now;
}
diff.as_secs() as f64 + diff.subsec_millis() as f64 / 1000.
}
}