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}; pub struct Emulator { frame_receiver: Arc, joypad_sender: Arc, shader_path: Arc>>, size: Size, } impl Emulator { pub fn new( frame_receiver: Arc, joypad_sender: Arc, shader_path: Arc>>, size: Size, ) -> Self { Self { frame_receiver, joypad_sender, shader_path, 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), // ) // }; #[cfg(feature = "vulkan")] let shader_path = self.shader_path.lock().unwrap().clone(); // let shader_path = None; let scale_factor = access_config().vst_config.scale_factor; let size = Size::new( (WIDTH * scale_factor) as f64, (HEIGHT * scale_factor) as f64, ); Window::open_parented( &parent, WindowOpenOptions { title: String::from("gb-emu"), size, scale: baseview::WindowScalePolicy::SystemScaleFactor, gl_config: None, }, move |w| { EmulatorWindow::new( w, fr_cloned, js_cloned, size, #[cfg(feature = "vulkan")] shader_path, ) }, ); Box::new(Self::new( self.frame_receiver.clone(), self.joypad_sender.clone(), self.shader_path.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, size: Size, #[cfg(feature = "vulkan")] shader_path: Option, ) -> Self { 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 (manager, window_options) = { use raw_window_handle::HasRawDisplayHandle; ( Arc::new(RendererBackendManager::new(window.raw_display_handle())), WindowOptions { shader_path }, ) }; #[cfg(feature = "pixels")] let (manager, window_options) = { (Arc::new(RendererBackendManager::new()), WindowOptions {}) }; let renderer = RendererBackend::new(current_resolution, window, window_options, manager.clone()); Self { renderer, manager, frame_receiver, joypad_sender, current_resolution, } } } 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 } }