diff --git a/gb-emu/src/main.rs b/gb-emu/src/main.rs index b21ca47..8fcd300 100644 --- a/gb-emu/src/main.rs +++ b/gb-emu/src/main.rs @@ -11,8 +11,11 @@ use gb_emu_lib::{ EmulatorCore, }; use gilrs::Gilrs; -use std::sync::mpsc::channel; -use window::WindowRenderer; +use std::{ + sync::mpsc::channel, + time::{Duration, Instant}, +}; +use window::{WindowManager, WindowRenderer}; mod audio; #[cfg(feature = "camera")] @@ -75,77 +78,99 @@ struct Args { fn main() { let args = Args::parse(); - let factor = if let Some(factor) = args.scale_factor { - factor - } else { - 3 - }; - - let tile_window: Option = if args.tile_window { - Some(WindowRenderer::new(factor, None)) - } else { - None - }; - - let (sender, receiver) = channel::(); - let (debug_sender, debug_receiver) = channel::(); - - ctrlc::set_handler(move || { - sender.send(EmulatorMessage::Stop).unwrap(); - debug_sender.send(EmulatorMessage::Stop).unwrap() - }) - .unwrap(); - - let (output, _stream) = audio::create_output(args.mute); - - let window = WindowRenderer::new(factor, Some(Gilrs::new().unwrap())); - let rom = RomFile::Path(args.rom); - - let options = EmulatorOptions::new(window, rom, output) - .with_save_path(args.save) - .with_serial_target(if args.connect_serial { - SerialTarget::Stdout - } else { - SerialTarget::None - }) - .with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom) - .with_no_save(args.no_save) - .with_tile_window(tile_window) - .with_cgb_mode(!args.dmg); - - // let core: Box = if args.camera { - // Box::new(EmulatorCore::init(receiver, options, Webcam::new())) - // } else { - // Box::new(EmulatorCore::init(receiver, options, NoCamera::default())) - // }; - - #[cfg(not(feature = "camera"))] - let core: Box = - Box::new(EmulatorCore::init(receiver, options, NoCamera::default())); - #[cfg(feature = "camera")] - let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new())); - - let mut handler = if args.debug { - EmulatorHandler::Debug(Debugger::new(core, debug_receiver)) - } else { - EmulatorHandler::Normal(core) - }; - - loop { - handler.run(); - } + EmulatorHandler::run(args); } -enum EmulatorHandler { +enum EmulatorTypes { Debug(Debugger), Normal(Box), } +struct EmulatorHandler { + emu: EmulatorTypes, + window_manager: WindowManager, + since: Instant, +} + impl EmulatorHandler { - fn run(&mut self) { - match self { - EmulatorHandler::Debug(ref mut debugger) => debugger.step(), - EmulatorHandler::Normal(ref mut core) => core.run(), + fn run(args: Args) -> ! { + let factor = if let Some(factor) = args.scale_factor { + factor + } else { + 3 + }; + + let (sender, receiver) = channel::(); + let (debug_sender, debug_receiver) = channel::(); + + ctrlc::set_handler(move || { + sender.send(EmulatorMessage::Stop).unwrap(); + debug_sender.send(EmulatorMessage::Stop).unwrap() + }) + .unwrap(); + + let (output, _stream) = audio::create_output(args.mute); + let rom = RomFile::Path(args.rom); + + let mut window_manager = WindowManager::new(); + + let window = window_manager.add(factor, Some(Gilrs::new().unwrap())); + let tile_window: Option = if args.tile_window { + Some(window_manager.add(factor, None)) + } else { + None + }; + + let options = EmulatorOptions::new(window, rom, output) + .with_save_path(args.save) + .with_serial_target(if args.connect_serial { + SerialTarget::Stdout + } else { + SerialTarget::None + }) + .with_bootrom(args.bootrom.map(RomFile::Path), args.show_bootrom) + .with_no_save(args.no_save) + .with_tile_window(tile_window) + .with_cgb_mode(!args.dmg); + + // let core: Box = if args.camera { + // Box::new(EmulatorCore::init(receiver, options, Webcam::new())) + // } else { + // Box::new(EmulatorCore::init(receiver, options, NoCamera::default())) + // }; + + #[cfg(not(feature = "camera"))] + let core: Box = + Box::new(EmulatorCore::init(receiver, options, NoCamera::default())); + #[cfg(feature = "camera")] + let core = Box::new(EmulatorCore::init(receiver, options, Webcam::new())); + + let emu = if args.debug { + EmulatorTypes::Debug(Debugger::new(core, debug_receiver)) + } else { + EmulatorTypes::Normal(core) + }; + + let since = Instant::now(); + + let mut h = Self { + emu, + window_manager, + since, + }; + loop { + h.cycle(); + } + } + + fn cycle(&mut self) { + if self.since.elapsed() >= Duration::from_millis(10) { + self.window_manager.update_events(); + self.since = Instant::now(); + } + match self.emu { + EmulatorTypes::Debug(ref mut debugger) => debugger.step(), + EmulatorTypes::Normal(ref mut core) => core.run(), } } } diff --git a/gb-emu/src/window.rs b/gb-emu/src/window.rs index 923396e..434f7f0 100644 --- a/gb-emu/src/window.rs +++ b/gb-emu/src/window.rs @@ -1,3 +1,8 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + use gb_emu_lib::{ connect::{JoypadState, Renderer}, util::scale_buffer_in_place, @@ -12,16 +17,86 @@ use winit::{ event::{Event, VirtualKeyCode, WindowEvent}, event_loop::EventLoop, platform::run_return::EventLoopExtRunReturn, - window::{Window, WindowBuilder}, + window::{Window, WindowBuilder, WindowId}, }; use winit_input_helper::WinitInputHelper; -pub struct WindowRenderer { - event_loop: EventLoop<()>, - window: Window, - input: WinitInputHelper, - pixels: Pixels, +pub struct WindowInfo { + id: WindowId, + data: Arc>, +} + +pub struct WindowData { scaled_buf: Vec<[u8; 4]>, + pixels: Pixels, +} + +pub struct WindowManager { + event_loop: EventLoop<()>, + windows: HashMap>>, + input: Arc>, +} + +impl WindowManager { + pub(crate) fn new() -> Self { + Self { + event_loop: EventLoop::new(), + windows: HashMap::new(), + input: Arc::new(Mutex::new(WinitInputHelper::new())), + } + } + + pub(crate) fn add(&mut self, factor: usize, gamepad_handler: Option) -> WindowRenderer { + let (r, info) = WindowRenderer::new( + factor, + gamepad_handler, + self.input.clone(), + &self.event_loop, + ); + self.windows.insert(info.id, info.data); + r + } + + pub fn update_events(&mut self) { + self.event_loop.run_return(|event, _, control_flow| { + control_flow.set_wait(); + if let Ok(mut i) = self.input.lock() { + i.update(&event); + } + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + window_id: _, + } => { + // quit = true; + } + Event::MainEventsCleared => { + control_flow.set_exit(); + } + Event::RedrawRequested(window_id) => { + if let Some(w) = self.windows.get(&window_id) { + if let Ok(mut w) = w.lock() { + let scaled = w.scaled_buf.clone(); + for (pixel, source) in + w.pixels.frame_mut().chunks_exact_mut(4).zip(&scaled) + { + pixel.copy_from_slice(source); + } + w.pixels.render().unwrap(); + } + } + } + _ => {} + } + }); + } +} + +pub struct WindowRenderer { + window: Window, + input: Arc>, + data: Arc>, width: usize, height: usize, factor: usize, @@ -32,11 +107,15 @@ pub struct WindowRenderer { } impl WindowRenderer { - pub fn new(factor: usize, gamepad_handler: Option) -> Self { - let event_loop = EventLoop::new(); + pub fn new( + factor: usize, + gamepad_handler: Option, + input: Arc>, + event_loop: &EventLoop<()>, + ) -> (Self, WindowInfo) { let window = WindowBuilder::new() - .with_title("A fantastic window!") - .build(&event_loop) + .with_title("Gameboy") + .build(event_loop) .unwrap(); let real_factor = (window.scale_factor() * factor as f64) as usize; @@ -54,52 +133,30 @@ impl WindowRenderer { .unwrap() }; - let input = WinitInputHelper::new(); - Self { - event_loop, - window, - input, + let data = Arc::new(Mutex::new(WindowData { + scaled_buf: Vec::new(), pixels, - scaled_buf: vec![], - width: 0, - height: 0, - factor, - real_factor, - gamepad_handler, - joypad_state: JoypadState::default(), - current_rumble: false, - } - } + })); + let info = WindowInfo { + id: window.id(), + data: data.clone(), + }; - fn update_events(&mut self) { - self.event_loop.run_return(|event, _, control_flow| { - control_flow.set_wait(); - self.input.update(&event); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - // quit = true; - } - Event::MainEventsCleared => { - control_flow.set_exit(); - } - Event::RedrawRequested(_) => { - for (pixel, source) in self - .pixels - .frame_mut() - .chunks_exact_mut(4) - .zip(&self.scaled_buf) - { - pixel.copy_from_slice(source); - } - self.pixels.render().unwrap(); - } - _ => {} - } - }); + ( + Self { + window, + input, + data, + width: 0, + height: 0, + factor, + real_factor, + gamepad_handler, + joypad_state: JoypadState::default(), + current_rumble: false, + }, + info, + ) } } @@ -113,28 +170,30 @@ impl Renderer<[u8; 4]> for WindowRenderer { let h = (height * self.real_factor) as u32; self.window.set_inner_size(PhysicalSize::new(w, h)); - self.pixels = { - let window_size = self.window.inner_size(); - let surface_texture = - SurfaceTexture::new(window_size.width, window_size.height, &self.window); - Pixels::new(window_size.width, window_size.height, surface_texture).unwrap() - }; - - self.scaled_buf.resize((w * h) as usize, [0; 4]); + if let Ok(mut data) = self.data.lock() { + data.pixels = { + let window_size = self.window.inner_size(); + let surface_texture = + SurfaceTexture::new(window_size.width, window_size.height, &self.window); + Pixels::new(window_size.width, window_size.height, surface_texture).unwrap() + }; + data.scaled_buf.resize((w * h) as usize, [0; 4]); + } self.window.request_redraw(); } fn display(&mut self, buffer: &[[u8; 4]]) { - scale_buffer_in_place( - buffer, - &mut self.scaled_buf, - self.width, - self.height, - self.real_factor, - ); + if let Ok(mut data) = self.data.lock() { + scale_buffer_in_place( + buffer, + &mut data.scaled_buf, + self.width, + self.height, + self.real_factor, + ); + } self.window.request_redraw(); - self.update_events(); } fn set_title(&mut self, title: String) { @@ -142,7 +201,6 @@ impl Renderer<[u8; 4]> for WindowRenderer { } fn latest_joypad_state(&mut self) -> JoypadState { - self.update_events(); self.joypad_state.reset(); if let Some(ref mut gamepad_handler) = self.gamepad_handler { @@ -174,19 +232,20 @@ impl Renderer<[u8; 4]> for WindowRenderer { } } - self.joypad_state.down |= - self.input.key_held(VirtualKeyCode::Down) || self.input.key_held(VirtualKeyCode::S); - self.joypad_state.up |= - self.input.key_held(VirtualKeyCode::Up) || self.input.key_held(VirtualKeyCode::W); - self.joypad_state.left |= - self.input.key_held(VirtualKeyCode::Left) || self.input.key_held(VirtualKeyCode::A); - self.joypad_state.right |= - self.input.key_held(VirtualKeyCode::Right) || self.input.key_held(VirtualKeyCode::D); - self.joypad_state.start |= self.input.key_held(VirtualKeyCode::Equals); - self.joypad_state.select |= self.input.key_held(VirtualKeyCode::Minus); - self.joypad_state.a |= self.input.key_held(VirtualKeyCode::Apostrophe); - self.joypad_state.b |= self.input.key_held(VirtualKeyCode::Semicolon); - + if let Ok(input) = self.input.lock() { + self.joypad_state.down |= + input.key_held(VirtualKeyCode::Down) || input.key_held(VirtualKeyCode::S); + self.joypad_state.up |= + input.key_held(VirtualKeyCode::Up) || input.key_held(VirtualKeyCode::W); + self.joypad_state.left |= + input.key_held(VirtualKeyCode::Left) || input.key_held(VirtualKeyCode::A); + self.joypad_state.right |= + input.key_held(VirtualKeyCode::Right) || input.key_held(VirtualKeyCode::D); + self.joypad_state.start |= input.key_held(VirtualKeyCode::Equals); + self.joypad_state.select |= input.key_held(VirtualKeyCode::Minus); + self.joypad_state.a |= input.key_held(VirtualKeyCode::Apostrophe); + self.joypad_state.b |= input.key_held(VirtualKeyCode::Semicolon); + } self.joypad_state }