2023-03-07 21:45:11 +11:00
|
|
|
use std::sync::{
|
|
|
|
mpsc::{self, Receiver, Sender},
|
2023-03-08 11:50:19 +11:00
|
|
|
Arc,
|
2023-03-07 21:45:11 +11:00
|
|
|
};
|
2023-03-05 20:18:06 +11:00
|
|
|
|
|
|
|
use baseview::{
|
2023-03-07 21:45:11 +11:00
|
|
|
Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
|
|
|
};
|
|
|
|
use gb_emu_lib::{
|
2023-10-04 11:39:27 +11:00
|
|
|
connect::{JoypadButtons, JoypadState, Renderer, ResolutionData, HEIGHT, WIDTH},
|
|
|
|
renderer::{RendererBackend, RendererBackendManager, WindowOptions},
|
2023-03-05 20:18:06 +11:00
|
|
|
};
|
2023-03-08 15:19:10 +11:00
|
|
|
use keyboard_types::{Code, KeyState};
|
2023-03-05 20:18:06 +11:00
|
|
|
use nih_plug::prelude::*;
|
2023-10-04 11:39:27 +11:00
|
|
|
use raw_window_handle::HasRawDisplayHandle;
|
2023-03-05 20:18:06 +11:00
|
|
|
|
2023-09-10 10:56:21 +10:00
|
|
|
use crate::{Frame, FrameReceiver, JoypadInfo, JoypadSender};
|
2023-03-08 11:50:19 +11:00
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
pub struct Emulator {
|
2023-03-08 11:50:19 +11:00
|
|
|
frame_receiver: Arc<FrameReceiver>,
|
2023-03-08 15:19:10 +11:00
|
|
|
joypad_sender: Arc<JoypadSender>,
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
2023-03-05 20:18:06 +11:00
|
|
|
|
|
|
|
impl Emulator {
|
2023-03-08 15:19:10 +11:00
|
|
|
pub fn new(frame_receiver: Arc<FrameReceiver>, joypad_sender: Arc<JoypadSender>) -> Self {
|
|
|
|
Self {
|
|
|
|
frame_receiver,
|
|
|
|
joypad_sender,
|
|
|
|
}
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-04 11:39:27 +11:00
|
|
|
const EXTRA_SCALE: usize = 4;
|
2023-03-08 15:26:32 +11:00
|
|
|
|
2023-03-05 20:18:06 +11:00
|
|
|
impl Editor for Emulator {
|
|
|
|
fn spawn(
|
|
|
|
&self,
|
|
|
|
parent: ParentWindowHandle,
|
|
|
|
_context: Arc<dyn GuiContext>,
|
|
|
|
) -> Box<dyn std::any::Any + Send> {
|
2023-03-08 15:19:10 +11:00
|
|
|
let fr_cloned = self.frame_receiver.clone();
|
|
|
|
let js_cloned = self.joypad_sender.clone();
|
2023-03-24 13:01:39 +11:00
|
|
|
|
2023-10-04 11:39:27 +11:00
|
|
|
// 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),
|
|
|
|
// )
|
|
|
|
// };
|
2023-03-24 13:01:39 +11:00
|
|
|
|
2023-03-05 20:18:06 +11:00
|
|
|
Window::open_parented(
|
|
|
|
&parent,
|
|
|
|
WindowOpenOptions {
|
|
|
|
title: String::from("gb-emu"),
|
2023-10-04 11:39:27 +11:00
|
|
|
size: Size::new((WIDTH * EXTRA_SCALE) as f64, (HEIGHT * EXTRA_SCALE) as f64),
|
|
|
|
scale: baseview::WindowScalePolicy::SystemScaleFactor,
|
2023-03-07 08:51:12 +11:00
|
|
|
gl_config: None,
|
2023-03-05 20:18:06 +11:00
|
|
|
},
|
2023-03-08 15:19:10 +11:00
|
|
|
|w| EmulatorWindow::new(w, fr_cloned, js_cloned),
|
2023-03-05 20:18:06 +11:00
|
|
|
);
|
2023-03-08 15:19:10 +11:00
|
|
|
Box::new(Self::new(
|
|
|
|
self.frame_receiver.clone(),
|
|
|
|
self.joypad_sender.clone(),
|
|
|
|
))
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
fn size(&self) -> (u32, u32) {
|
2023-03-08 15:26:32 +11:00
|
|
|
((WIDTH * EXTRA_SCALE) as u32, (HEIGHT * EXTRA_SCALE) as u32)
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2023-10-04 11:39:27 +11:00
|
|
|
renderer: RendererBackend,
|
|
|
|
manager: Arc<RendererBackendManager>,
|
2023-03-08 11:50:19 +11:00
|
|
|
frame_receiver: Arc<FrameReceiver>,
|
2023-03-08 15:19:10 +11:00
|
|
|
joypad_sender: Arc<JoypadSender>,
|
2023-10-04 11:39:27 +11:00
|
|
|
current_resolution: ResolutionData,
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EmulatorWindow {
|
2023-03-08 15:19:10 +11:00
|
|
|
fn new(
|
|
|
|
window: &mut Window,
|
|
|
|
frame_receiver: Arc<FrameReceiver>,
|
|
|
|
joypad_sender: Arc<JoypadSender>,
|
|
|
|
) -> Self {
|
2023-03-24 13:01:39 +11:00
|
|
|
let info = WindowInfo::from_logical_size(
|
|
|
|
Size::new(WIDTH as f64, HEIGHT as f64),
|
|
|
|
EXTRA_SCALE as f64,
|
|
|
|
);
|
2023-10-04 11:39:27 +11:00
|
|
|
let physical_size = info.physical_size();
|
|
|
|
|
|
|
|
let current_resolution = ResolutionData {
|
|
|
|
real_width: physical_size.width,
|
|
|
|
real_height: physical_size.height,
|
|
|
|
scaled_width: WIDTH as u32,
|
|
|
|
scaled_height: HEIGHT as u32,
|
|
|
|
};
|
|
|
|
|
|
|
|
let manager = Arc::new(RendererBackendManager::new(window.raw_display_handle()));
|
2023-03-05 20:18:06 +11:00
|
|
|
|
2023-10-04 11:39:27 +11:00
|
|
|
let renderer = RendererBackend::new(current_resolution, window, WindowOptions {
|
|
|
|
shader_path: Some(std::path::PathBuf::from(
|
|
|
|
"./test-roms/shaders/slang-shaders/handheld/console-border/gbc-lcd-grid-v2.slangp",
|
|
|
|
)),
|
|
|
|
}, manager.clone());
|
2023-03-05 20:18:06 +11:00
|
|
|
|
|
|
|
Self {
|
2023-10-04 11:39:27 +11:00
|
|
|
renderer,
|
|
|
|
manager,
|
2023-03-07 21:45:11 +11:00
|
|
|
frame_receiver,
|
2023-03-08 15:19:10 +11:00
|
|
|
joypad_sender,
|
2023-10-04 11:39:27 +11:00
|
|
|
current_resolution,
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl WindowHandler for EmulatorWindow {
|
|
|
|
fn on_frame(&mut self, _window: &mut Window) {
|
2023-03-07 21:45:11 +11:00
|
|
|
if let Some(ref mut receiver) = *self.frame_receiver.lock().expect("failed to lock mutex") {
|
2023-03-09 11:52:24 +11:00
|
|
|
if let Some(ref buf) = receiver.try_iter().last() {
|
2023-10-04 11:39:27 +11:00
|
|
|
self.renderer.new_frame(buf);
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
|
|
|
}
|
2023-10-04 11:39:27 +11:00
|
|
|
|
|
|
|
self.renderer.render(self.current_resolution, &self.manager);
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus {
|
2023-03-08 15:19:10 +11:00
|
|
|
match event {
|
|
|
|
Event::Window(WindowEvent::Resized(info)) => {
|
2023-10-04 11:39:27 +11:00
|
|
|
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);
|
2023-03-08 15:19:10 +11:00
|
|
|
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,
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-07 08:51:12 +11:00
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
pub struct EmulatorRenderer {
|
2023-09-10 10:56:21 +10:00
|
|
|
tx: Sender<Frame>,
|
2023-03-08 15:19:10 +11:00
|
|
|
joypad: JoypadState,
|
2023-09-10 10:56:21 +10:00
|
|
|
keys: Receiver<JoypadInfo>,
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EmulatorRenderer {
|
2023-09-10 10:56:21 +10:00
|
|
|
pub(super) fn new() -> (Self, Receiver<Frame>, Sender<JoypadInfo>) {
|
|
|
|
let (tx, rx) = mpsc::channel::<Frame>();
|
|
|
|
let (keys_tx, keys) = mpsc::channel::<JoypadInfo>();
|
2023-03-08 15:19:10 +11:00
|
|
|
(
|
|
|
|
Self {
|
|
|
|
tx,
|
|
|
|
joypad: JoypadState::default(),
|
|
|
|
keys,
|
|
|
|
},
|
|
|
|
rx,
|
|
|
|
keys_tx,
|
|
|
|
)
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
|
|
|
}
|
2023-03-07 08:51:12 +11:00
|
|
|
|
2023-03-08 11:01:18 +11:00
|
|
|
impl Renderer<[u8; 4]> for EmulatorRenderer {
|
2023-03-07 08:51:12 +11:00
|
|
|
fn prepare(&mut self, _width: usize, _height: usize) {}
|
|
|
|
|
2023-03-08 11:01:18 +11:00
|
|
|
fn display(&mut self, buffer: &[[u8; 4]]) {
|
2023-04-06 13:15:48 +10:00
|
|
|
let _ = self.tx.send(buffer.to_vec());
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
2023-03-07 08:51:12 +11:00
|
|
|
|
2023-03-08 15:19:10 +11:00
|
|
|
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
|
2023-03-07 08:51:12 +11:00
|
|
|
}
|
|
|
|
}
|