use crate::{display::SimulatorDisplay, framebuffer::Framebuffer, output_settings::OutputSettings};
use embedded_graphics::{
geometry::{Point, Size},
pixelcolor::{PixelColor, Rgb888},
DrawTarget,
};
use sdl2::{
event::Event,
keyboard::{Keycode, Mod},
mouse::{MouseButton, MouseWheelDirection},
render,
};
use std::{thread, time::Duration};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SimulatorEvent {
KeyUp {
keycode: Keycode,
keymod: Mod,
repeat: bool,
},
KeyDown {
keycode: Keycode,
keymod: Mod,
repeat: bool,
},
MouseButtonUp {
mouse_btn: MouseButton,
point: Point,
},
MouseButtonDown {
mouse_btn: MouseButton,
point: Point,
},
MouseWheel {
scroll_delta: Point,
direction: MouseWheelDirection,
},
Quit,
}
#[allow(dead_code)]
pub struct Window {
framebuffer: Option<Framebuffer>,
sdl_window: Option<SdlWindow>,
title: String,
output_settings: OutputSettings,
}
impl Window {
pub fn new(title: &str, output_settings: &OutputSettings) -> Self {
Self {
framebuffer: None,
sdl_window: None,
title: String::from(title),
output_settings: output_settings.clone(),
}
}
pub fn update<C>(&mut self, display: &SimulatorDisplay<C>)
where
C: PixelColor + Into<Rgb888>,
{
if let Ok(path) = std::env::var("EG_SIMULATOR_DUMP") {
display
.to_image_buffer(&self.output_settings)
.save(path)
.unwrap();
std::process::exit(0);
}
if self.framebuffer.is_none() {
self.framebuffer = Some(Framebuffer::new(display, &self.output_settings));
}
if self.sdl_window.is_none() {
self.sdl_window = Some(SdlWindow::new(display, &self.title, &self.output_settings));
}
let framebuffer = self.framebuffer.as_mut().unwrap();
let sdl_window = self.sdl_window.as_mut().unwrap();
framebuffer.update(display);
sdl_window.update(&framebuffer);
}
pub fn show_static<C>(&mut self, display: &SimulatorDisplay<C>)
where
C: PixelColor + Into<Rgb888>,
{
self.update(&display);
'running: loop {
if self.events().any(|e| e == SimulatorEvent::Quit) {
break 'running;
}
thread::sleep(Duration::from_millis(20));
}
}
pub fn events(&mut self) -> impl Iterator<Item = SimulatorEvent> + '_ {
self.sdl_window
.as_mut()
.unwrap()
.events(&self.output_settings)
}
}
#[allow(dead_code)]
struct SdlWindow {
canvas: render::Canvas<sdl2::video::Window>,
event_pump: sdl2::EventPump,
}
impl SdlWindow {
#[allow(dead_code)]
pub fn new<C>(
display: &SimulatorDisplay<C>,
title: &str,
output_settings: &OutputSettings,
) -> Self
where
C: PixelColor + Into<Rgb888>,
{
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let size = output_settings.framebuffer_size(display);
let window = video_subsystem
.window(title, size.width, size.height)
.position_centered()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let event_pump = sdl_context.event_pump().unwrap();
Self { canvas, event_pump }
}
#[allow(dead_code)]
pub fn update(&mut self, framebuffer: &Framebuffer) {
let Size { width, height } = framebuffer.size();
let texture_creator = self.canvas.texture_creator();
let mut texture = texture_creator
.create_texture_streaming(sdl2::pixels::PixelFormatEnum::RGB24, width, height)
.unwrap();
texture
.update(None, framebuffer.data.as_ref(), width as usize * 3)
.unwrap();
self.canvas.copy(&texture, None, None).unwrap();
self.canvas.present();
}
pub fn events(
&mut self,
output_settings: &OutputSettings,
) -> impl Iterator<Item = SimulatorEvent> + '_ {
let output_settings = output_settings.clone();
self.event_pump
.poll_iter()
.filter_map(move |event| match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => Some(SimulatorEvent::Quit),
Event::KeyDown {
keycode,
keymod,
repeat,
..
} => {
if let Some(valid_keycode) = keycode {
Some(SimulatorEvent::KeyDown {
keycode: valid_keycode,
keymod,
repeat,
})
} else {
None
}
}
Event::KeyUp {
keycode,
keymod,
repeat,
..
} => {
if let Some(valid_keycode) = keycode {
Some(SimulatorEvent::KeyUp {
keycode: valid_keycode,
keymod,
repeat,
})
} else {
None
}
}
Event::MouseButtonUp {
x, y, mouse_btn, ..
} => {
let point = output_settings.output_to_display(Point::new(x, y));
Some(SimulatorEvent::MouseButtonUp { point, mouse_btn })
}
Event::MouseButtonDown {
x, y, mouse_btn, ..
} => {
let point = output_settings.output_to_display(Point::new(x, y));
Some(SimulatorEvent::MouseButtonDown { point, mouse_btn })
}
Event::MouseWheel {
x, y, direction, ..
} => Some(SimulatorEvent::MouseWheel {
scroll_delta: Point::new(x, y),
direction,
}),
_ => None,
})
}
}