use std::{ path::PathBuf, sync::{ mpsc::{self, Receiver, Sender}, Arc, Mutex, }, }; use baseview::{Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowOpenOptions}; use gb_emu_lib::{ connect::{JoypadButtons, JoypadState, Renderer, ResolutionData, HEIGHT, WIDTH}, renderer::{RendererBackend, RendererBackendManager, WindowOptions}, }; use keyboard_types::{Code, KeyState}; use nih_plug::prelude::*; use crate::{access_config, Frame, FrameReceiver, JoypadInfo, JoypadSender, IS_CGB}; pub struct Emulator { frame_receiver: Arc, joypad_sender: Arc, manager: Arc>>>, size: Size, } impl Emulator { pub fn new( frame_receiver: Arc, joypad_sender: Arc, manager: Arc>>>, size: Size, ) -> Self { Self { frame_receiver, joypad_sender, manager, size, } } } impl Editor for Emulator { fn spawn( &self, parent: ParentWindowHandle, _context: Arc, ) -> Box { let fr_cloned = self.frame_receiver.clone(); let js_cloned = self.joypad_sender.clone(); // let (size, scale) = if cfg!(target_os = "macos") { // ( // Size::new((WIDTH * EXTRA_SCALE) as f64, (HEIGHT * EXTRA_SCALE) as f64), // baseview::WindowScalePolicy::SystemScaleFactor, // ) // } else { // ( // Size::new(WIDTH as f64, HEIGHT as f64), // baseview::WindowScalePolicy::ScaleFactor(EXTRA_SCALE as f64), // ) // }; let config = access_config(); #[cfg(feature = "vulkan")] let shader_path = if IS_CGB.get().is_some_and(|v| *v) { config.emu_config.vulkan_config.cgb_shader_path.as_ref() } else { config.emu_config.vulkan_config.dmg_shader_path.as_ref() } .map(|p| config.config_dir.join(p)); let scale_factor = access_config().vst_config.scale_factor; let size = Size::new( (WIDTH * scale_factor) as f64, (HEIGHT * scale_factor) as f64, ); let m = self.manager.clone(); Window::open_parented( &parent, WindowOpenOptions { title: String::from("gb-emu"), size, scale: baseview::WindowScalePolicy::SystemScaleFactor, }, move |w| { EmulatorWindow::new( w, fr_cloned, js_cloned, m, size, #[cfg(feature = "vulkan")] shader_path, ) .0 }, ); Box::new(Self::new( self.frame_receiver.clone(), self.joypad_sender.clone(), self.manager.clone(), size, )) } fn size(&self) -> (u32, u32) { (self.size.width as u32, self.size.height as u32) } fn set_scale_factor(&self, _factor: f32) -> bool { true } fn param_value_changed(&self, _id: &str, _normalized_value: f32) {} fn param_modulation_changed(&self, _id: &str, _modulation_offset: f32) {} fn param_values_changed(&self) {} } pub struct EmulatorWindow { renderer: RendererBackend, manager: Arc, frame_receiver: Arc, joypad_sender: Arc, current_resolution: ResolutionData, } impl EmulatorWindow { fn new( window: &mut Window, frame_receiver: Arc, joypad_sender: Arc, manager: Arc>>>, size: Size, #[cfg(feature = "vulkan")] shader_path: Option, ) -> (Self, Arc) { let current_resolution = ResolutionData { real_width: size.width as u32, real_height: size.height as u32, scaled_width: WIDTH as u32, scaled_height: HEIGHT as u32, }; #[cfg(feature = "vulkan")] let window_options = WindowOptions { shader_path }; #[cfg(feature = "pixels")] let window_options = WindowOptions {}; let mut guard = manager.lock().unwrap(); let m = guard.get_or_insert_with(|| { use raw_window_handle::HasRawDisplayHandle; Arc::new(RendererBackendManager::new(window.raw_display_handle())) }); let renderer = RendererBackend::new(current_resolution, window, window_options, m.clone()); ( Self { renderer, manager: m.clone(), frame_receiver, joypad_sender, current_resolution, }, m.clone(), ) } } impl WindowHandler for EmulatorWindow { fn on_frame(&mut self, _window: &mut Window) { if let Some(ref mut receiver) = *self.frame_receiver.lock().expect("failed to lock mutex") { if let Some(ref buf) = receiver.try_iter().last() { self.renderer.new_frame(buf); } } self.renderer.render(self.current_resolution, &self.manager); } fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus { match event { Event::Window(WindowEvent::Resized(info)) => { let physical_size = info.physical_size(); self.current_resolution = ResolutionData { real_width: physical_size.width, real_height: physical_size.height, scaled_width: WIDTH as u32, scaled_height: HEIGHT as u32, }; self.renderer.resize(self.current_resolution, window); EventStatus::Captured } Event::Keyboard(event) => { let status = event.state == KeyState::Down; if let Some(button) = match event.code { Code::Equal => Some(JoypadButtons::Start), Code::Minus => Some(JoypadButtons::Select), Code::Quote => Some(JoypadButtons::A), Code::Semicolon => Some(JoypadButtons::B), Code::KeyW | Code::ArrowUp => Some(JoypadButtons::Up), Code::KeyA | Code::ArrowLeft => Some(JoypadButtons::Left), Code::KeyS | Code::ArrowDown => Some(JoypadButtons::Down), Code::KeyD | Code::ArrowRight => Some(JoypadButtons::Right), _ => None, } { if let Some(ref mut sender) = *self.joypad_sender.lock().expect("failed to lock mutex") { sender.send((button, status)).unwrap(); } EventStatus::Captured } else { EventStatus::Ignored } } _ => EventStatus::Ignored, } } } pub struct EmulatorRenderer { tx: Sender, joypad: JoypadState, keys: Receiver, } impl EmulatorRenderer { pub(super) fn new() -> (Self, Receiver, Sender) { let (tx, rx) = mpsc::channel::(); let (keys_tx, keys) = mpsc::channel::(); ( Self { tx, joypad: JoypadState::default(), keys, }, rx, keys_tx, ) } } impl Renderer<[u8; 4]> for EmulatorRenderer { fn prepare(&mut self, _width: usize, _height: usize) {} fn display(&mut self, buffer: &[[u8; 4]]) { let _ = self.tx.send(buffer.to_vec()); } fn latest_joypad_state(&mut self) -> JoypadState { while let Ok((key, state)) = self.keys.try_recv() { match key { JoypadButtons::Down => self.joypad.down = state, JoypadButtons::Up => self.joypad.up = state, JoypadButtons::Left => self.joypad.left = state, JoypadButtons::Right => self.joypad.right = state, JoypadButtons::Start => self.joypad.start = state, JoypadButtons::Select => self.joypad.select = state, JoypadButtons::B => self.joypad.b = state, JoypadButtons::A => self.joypad.a = state, } } self.joypad } }