use std::sync::{Arc, Mutex}; use baseview::{ Event, EventStatus, Size, Window, WindowEvent, WindowHandle, WindowHandler, WindowOpenOptions, }; use gb_emu_lib::{ connect::{JoypadButtons, JoypadState, RendererMessage, ResolutionData, HEIGHT, WIDTH}, renderer::{RendererBackend, RendererBackendManager, WindowOptions}, }; use keyboard_types::{Code, KeyState}; use nih_plug::prelude::*; use crate::{access_config, EmuComms}; pub struct TwincEditor { emu_comms: Arc>>, size: Size, } impl TwincEditor { pub fn new(emu_comms: Arc>>, size: Size) -> Self { Self { emu_comms, size } } } impl Editor for TwincEditor { fn spawn( &self, parent: ParentWindowHandle, _context: Arc, ) -> Box { let rr_cloned = self.emu_comms.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 crate::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 = config.vst_config.scale_factor; let size = Size::new( (WIDTH * scale_factor) as f64, (HEIGHT * scale_factor) as f64, ); let window = Window::open_parented( &parent, WindowOpenOptions { title: String::from("gb-emu"), size, scale: baseview::WindowScalePolicy::SystemScaleFactor, }, move |window| { let manager = { #[cfg(feature = "vulkan")] { use raw_window_handle::HasRawDisplayHandle; Arc::new(RendererBackendManager::new(window.raw_display_handle())) } #[cfg(feature = "pixels")] Arc::new(RendererBackendManager::new()) }; TwincEditorWindow::new( window, rr_cloned, manager, size, #[cfg(feature = "vulkan")] shader_path, ) }, ); Box::new(TwincEditorWindowHandle { window }) } 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) {} } struct TwincEditorWindowHandle { window: WindowHandle, } unsafe impl Send for TwincEditorWindowHandle {} impl Drop for TwincEditorWindowHandle { fn drop(&mut self) { self.window.close(); } } pub struct TwincEditorWindow { renderer: RendererBackend, manager: Arc, emu_comms: Arc>>, joypad_state: JoypadState, latest_buf: Vec<[u8; 4]>, current_resolution: ResolutionData, } impl TwincEditorWindow { fn new( window: &mut Window, emu_comms: Arc>>, manager: 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 window_options = WindowOptions { shader_path }; #[cfg(feature = "pixels")] let window_options = WindowOptions {}; let renderer = RendererBackend::new(current_resolution, window, window_options, manager.clone()); Self { renderer, manager, emu_comms, joypad_state: Default::default(), latest_buf: Vec::new(), current_resolution, } } fn process_events(&mut self) { if let Ok(comms) = self.emu_comms.lock() { if let Some(ref comms) = *comms { while let Ok(e) = comms.receiver.try_recv() { match e { RendererMessage::Prepare { width: _, height: _, } => {} RendererMessage::Resize { width: _, height: _, } => {} RendererMessage::Display { buffer } => self.latest_buf = buffer, RendererMessage::SetTitle { title: _ } => {} RendererMessage::Rumble { rumble: _ } => {} } } } } } } impl WindowHandler for TwincEditorWindow { fn on_frame(&mut self, _window: &mut Window) { self.process_events(); if self.latest_buf.len() == (self.current_resolution.scaled_height * self.current_resolution.scaled_width) as usize { self.renderer.new_frame(&self.latest_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, } { self.joypad_state.set(button, status); if let Ok(comms) = self.emu_comms.lock() { if let Some(ref comms) = *comms { match comms.sender.send( gb_emu_lib::connect::EmulatorMessage::JoypadUpdate( self.joypad_state, ), ) { Ok(_) => {} Err(e) => nih_error!("error sending joypad update: {e:#?}"), } } } EventStatus::Captured } else { EventStatus::Ignored } } _ => EventStatus::Ignored, } } }