use std::{
error::Error,
num::NonZeroU32,
rc::Rc,
sync::{
Arc,
atomic::{AtomicU8, Ordering},
mpsc::{self, Receiver, Sender},
},
time::Duration,
};
use chip_eight::{Draw, Emulator, QuirksMode, ReadInputState, SuperChipBehaviour};
use softbuffer::{Context, Surface};
use winit::{
application::ApplicationHandler,
event::{ElementState, StartCause, WindowEvent},
event_loop::{EventLoop, OwnedDisplayHandle},
keyboard::KeyCode,
window::Window,
};
const SCALE_FACTOR: usize = 10;
impl Draw for DisplayOutput {
fn draw_buffer(&mut self, screen_buf: &[u8], screen_width: usize, screen_height: usize) {
let _ = self
.buffer_sender
.send((screen_buf.to_vec(), screen_width, screen_height));
}
}
impl ReadInputState for KeyboardInput {
fn read_keys_state(&self) -> Result<[u8; 16], String> {
let x = std::array::from_fn(|i| self.keys[i].load(Ordering::Relaxed));
Ok(x)
}
fn reset_keys_state(&mut self) {
for key in self.keys.iter() {
key.store(0, Ordering::Relaxed);
}
}
}
struct KeyboardInput {
keys: Arc<[AtomicU8; 16]>,
}
struct DisplayOutput {
buffer_sender: Sender<(Vec<u8>, usize, usize)>,
}
struct App {
context: Context<OwnedDisplayHandle>,
app_state: AppState,
keys: Arc<[AtomicU8; 16]>,
buffer_receiver: Receiver<(Vec<u8>, usize, usize)>,
}
enum AppState {
Initial,
Suspended {
window: Rc<Window>,
},
Running {
surface: Surface<OwnedDisplayHandle, Rc<Window>>,
},
}
impl ApplicationHandler for App {
fn new_events(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
cause: winit::event::StartCause,
) {
if let StartCause::Init = cause {
let window_attrs = Window::default_attributes();
let window = event_loop
.create_window(window_attrs)
.expect("failed creating window");
let window = Rc::new(window);
self.app_state = AppState::Suspended { window };
}
}
fn resumed(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
let AppState::Suspended { window } = &mut self.app_state else {
eprintln!("Invalid state, resumed when not suspended");
return;
};
let mut surface =
Surface::new(&self.context, window.clone()).expect("failed creating surface");
let size = window.inner_size();
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
if let Err(e) = surface.resize(width, height) {
dbg!(e);
};
}
self.app_state = AppState::Running { surface };
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
let AppState::Running { surface } = &mut self.app_state else {
eprintln!("Invalid state, app not running");
return;
};
if surface.window().id() != window_id {
eprintln!("Invalid window");
return;
}
match event {
WindowEvent::Resized(size) => {
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
if let Err(e) = surface.resize(width, height) {
dbg!(e);
};
}
}
WindowEvent::CloseRequested => {
eprintln!("Close requested");
event_loop.exit();
}
WindowEvent::KeyboardInput {
device_id: _,
event,
is_synthetic: _,
} => match event.physical_key {
winit::keyboard::PhysicalKey::Code(key_code) => {
let key_state = match event.state {
ElementState::Pressed => 1,
ElementState::Released => 0,
};
match key_code {
KeyCode::KeyX => {
self.keys[0].store(key_state, Ordering::Relaxed);
}
KeyCode::Digit1 => {
self.keys[1].store(key_state, Ordering::Relaxed);
}
KeyCode::Digit2 => {
self.keys[2].store(key_state, Ordering::Relaxed);
}
KeyCode::Digit3 => {
self.keys[3].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyQ => {
self.keys[4].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyW => {
self.keys[5].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyE => {
self.keys[6].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyA => {
self.keys[7].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyS => {
self.keys[8].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyD => {
self.keys[9].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyZ => {
self.keys[10].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyC => {
self.keys[11].store(key_state, Ordering::Relaxed);
}
KeyCode::Digit4 => {
self.keys[12].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyR => {
self.keys[13].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyF => {
self.keys[14].store(key_state, Ordering::Relaxed);
}
KeyCode::KeyV => {
self.keys[15].store(key_state, Ordering::Relaxed);
}
_ => {}
}
}
winit::keyboard::PhysicalKey::Unidentified(_native_key_code) => {}
},
WindowEvent::RedrawRequested => {
if let Ok((draw_buffer, screen_width, screen_height)) =
self.buffer_receiver.recv_timeout(Duration::from_millis(6))
{
let _ = surface.resize(
NonZeroU32::new((screen_width * SCALE_FACTOR) as u32)
.expect("Definitely non zero"),
NonZeroU32::new((screen_height * SCALE_FACTOR) as u32)
.expect("Definitely non zero"),
);
let mut buffer = surface.buffer_mut().expect("Failed to get screen buffer");
let expanded_buf =
expand_buffer(draw_buffer.as_slice(), SCALE_FACTOR as u8, screen_width);
if expanded_buf.len() < buffer.len() {
eprintln!("Buffer too big");
return;
}
for (i, x) in buffer.iter_mut().enumerate() {
if let Some(j) = expanded_buf.get(i) {
*x = *j;
}
}
buffer.present().expect("Failed to present buffer");
}
}
_ => {}
}
}
fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
let AppState::Running { surface } = &self.app_state else {
return;
};
surface.window().request_redraw();
}
}
fn main() -> Result<(), Box<dyn Error>> {
let args = std::env::args();
let program_name = if args.len() > 1
&& let Some(arg) = args.last()
{
arg
} else {
eprintln!(
"Chip 8 Program needs to be passed as final argument
[USAGE]
cargo run --example winit_example /path/to/chip8/program.c8
"
);
std::process::exit(1);
};
let Ok(program) = std::fs::read(program_name) else {
eprintln!("Chip 8 program not found");
std::process::exit(2);
};
let (buffer_sender, buffer_receiver) = mpsc::channel();
let display_output = DisplayOutput { buffer_sender };
let keyboard_input = KeyboardInput {
keys: Arc::new(std::array::from_fn(|_| AtomicU8::new(0))),
};
let keys = keyboard_input.keys.clone();
std::thread::spawn(move || {
Emulator::init(program, display_output, keyboard_input)
.expect("Program too large")
.set_max_draw_delay(Duration::from_millis(7))
.set_quirks_mode(QuirksMode::SuperChip(SuperChipBehaviour::Modern))
.run_blocking();
});
let event_loop = EventLoop::new()?;
let context = Context::new(event_loop.owned_display_handle())?;
let mut app = App {
app_state: AppState::Initial,
context,
keys,
buffer_receiver,
};
event_loop.run_app(&mut app)?;
Ok(())
}
fn expand_buffer(buf: &[u8], factor: u8, screen_width: usize) -> Vec<u32> {
let mut output = vec![];
for chunk in buf.chunks(screen_width) {
let mut acc = vec![];
for c in chunk {
for _ in 0..factor {
acc.push(if *c > 0 { 0xBBBBBB } else { 0x111111 });
}
}
for _ in 0..factor {
output.extend(acc.as_slice());
}
}
output
}