use std::sync::{ mpsc::{self, Receiver, Sender}, Arc, Mutex, }; use baseview::{ Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, }; use gb_emu_lib::{ connect::{Renderer, HEIGHT, WIDTH}, util::scale_buffer, }; use nih_plug::prelude::*; use pixels::{Pixels, SurfaceTexture}; pub struct Emulator { frame_receiver: Arc>>>>, } impl Emulator { pub fn new(frame_receiver: Arc>>>>) -> Self { Self { frame_receiver } } } impl Editor for Emulator { fn spawn( &self, parent: ParentWindowHandle, _context: Arc, ) -> Box { let cloned = self.frame_receiver.clone(); Window::open_parented( &parent, WindowOpenOptions { title: String::from("gb-emu"), size: Size::new(WIDTH as f64, HEIGHT as f64), scale: baseview::WindowScalePolicy::SystemScaleFactor, gl_config: None, }, |w| EmulatorWindow::new(w, cloned), ); Box::new(Self::new(self.frame_receiver.clone())) } fn size(&self) -> (u32, u32) { (WIDTH as u32, 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 { pix: Pixels, scale: usize, frame_receiver: Arc>>>>, } impl EmulatorWindow { fn new( window: &mut Window, frame_receiver: Arc>>>>, ) -> Self { let info = WindowInfo::from_logical_size(Size::new(WIDTH as f64, HEIGHT as f64), 1.); let (pix, scale) = init_pixbuf(info, window); Self { pix, scale, frame_receiver, } } } fn init_pixbuf(info: WindowInfo, window: &mut Window) -> (Pixels, usize) { let physical_size = info.physical_size(); let scale = (physical_size.width as usize / WIDTH).min(physical_size.height as usize / HEIGHT); ( pixels::Pixels::new( physical_size.width, physical_size.height, SurfaceTexture::new(physical_size.width, physical_size.height, window), ) .unwrap(), scale, ) } 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") { match receiver.recv() { Ok(buf) => { let scaled_buf = if self.scale != 1 { scale_buffer(&buf, WIDTH, HEIGHT, self.scale) } else { buf }; for (pixel, source) in self.pix.get_frame_mut().chunks_exact_mut(4).zip(scaled_buf) { pixel.copy_from_slice(&source); } self.pix.render().unwrap(); } Err(e) => nih_log!("recv error: {e}"), } } } fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus { if let Event::Window(WindowEvent::Resized(info)) = event { (self.pix, self.scale) = init_pixbuf(info, window); return EventStatus::Captured; } EventStatus::Ignored } } pub struct EmulatorRenderer { tx: Sender>, } impl EmulatorRenderer { pub(super) fn new() -> (Self, Receiver>) { let (tx, rx) = mpsc::channel::>(); (Self { tx }, rx) } } impl Renderer<[u8; 4]> for EmulatorRenderer { fn prepare(&mut self, _width: usize, _height: usize) {} #[allow(unused_must_use)] fn display(&mut self, buffer: &[[u8; 4]]) { self.tx.send(buffer.to_vec()); } fn latest_joypad_state(&mut self) -> gb_emu_lib::connect::JoypadState { gb_emu_lib::connect::JoypadState::default() } }