2023-03-07 21:45:11 +11:00
|
|
|
use std::sync::{
|
|
|
|
mpsc::{self, Receiver, Sender},
|
|
|
|
Arc, Mutex,
|
|
|
|
};
|
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::{
|
|
|
|
connect::{Renderer, HEIGHT, WIDTH},
|
|
|
|
util::scale_buffer,
|
2023-03-05 20:18:06 +11:00
|
|
|
};
|
|
|
|
use nih_plug::prelude::*;
|
|
|
|
use pixels::{Pixels, SurfaceTexture};
|
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
pub struct Emulator {
|
2023-03-08 11:01:18 +11:00
|
|
|
frame_receiver: Arc<Mutex<Option<Receiver<Vec<[u8; 4]>>>>>,
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
2023-03-05 20:18:06 +11:00
|
|
|
|
|
|
|
impl Emulator {
|
2023-03-08 11:01:18 +11:00
|
|
|
pub fn new(frame_receiver: Arc<Mutex<Option<Receiver<Vec<[u8; 4]>>>>>) -> Self {
|
2023-03-07 21:45:11 +11:00
|
|
|
Self { frame_receiver }
|
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-07 21:45:11 +11:00
|
|
|
let cloned = self.frame_receiver.clone();
|
2023-03-05 20:18:06 +11:00
|
|
|
Window::open_parented(
|
|
|
|
&parent,
|
|
|
|
WindowOpenOptions {
|
|
|
|
title: String::from("gb-emu"),
|
|
|
|
size: Size::new(WIDTH as f64, HEIGHT 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-07 21:45:11 +11:00
|
|
|
|w| EmulatorWindow::new(w, cloned),
|
2023-03-05 20:18:06 +11:00
|
|
|
);
|
2023-03-07 21:45:11 +11:00
|
|
|
Box::new(Self::new(self.frame_receiver.clone()))
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
fn size(&self) -> (u32, u32) {
|
2023-03-07 21:45:11 +11:00
|
|
|
(WIDTH as u32, HEIGHT 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 {
|
|
|
|
pix: Pixels,
|
2023-03-07 21:45:11 +11:00
|
|
|
scale: usize,
|
2023-03-08 11:01:18 +11:00
|
|
|
frame_receiver: Arc<Mutex<Option<Receiver<Vec<[u8; 4]>>>>>,
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EmulatorWindow {
|
2023-03-08 11:01:18 +11:00
|
|
|
fn new(
|
|
|
|
window: &mut Window,
|
|
|
|
frame_receiver: Arc<Mutex<Option<Receiver<Vec<[u8; 4]>>>>>,
|
|
|
|
) -> Self {
|
2023-03-05 20:18:06 +11:00
|
|
|
let info = WindowInfo::from_logical_size(Size::new(WIDTH as f64, HEIGHT as f64), 1.);
|
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
let (pix, scale) = init_pixbuf(info, window);
|
2023-03-05 20:18:06 +11:00
|
|
|
|
|
|
|
Self {
|
|
|
|
pix,
|
2023-03-07 21:45:11 +11:00
|
|
|
scale,
|
|
|
|
frame_receiver,
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
fn init_pixbuf(info: WindowInfo, window: &mut Window) -> (Pixels, usize) {
|
2023-03-05 20:18:06 +11:00
|
|
|
let physical_size = info.physical_size();
|
2023-03-07 21:45:11 +11:00
|
|
|
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,
|
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") {
|
|
|
|
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)
|
|
|
|
{
|
2023-03-08 11:01:18 +11:00
|
|
|
pixel.copy_from_slice(&source);
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
|
|
|
self.pix.render().unwrap();
|
|
|
|
}
|
|
|
|
Err(e) => nih_log!("recv error: {e}"),
|
|
|
|
}
|
|
|
|
}
|
2023-03-05 20:18:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus {
|
|
|
|
if let Event::Window(WindowEvent::Resized(info)) = event {
|
2023-03-07 21:45:11 +11:00
|
|
|
(self.pix, self.scale) = init_pixbuf(info, window);
|
2023-03-05 20:18:06 +11:00
|
|
|
return EventStatus::Captured;
|
|
|
|
}
|
|
|
|
EventStatus::Ignored
|
|
|
|
}
|
|
|
|
}
|
2023-03-07 08:51:12 +11:00
|
|
|
|
2023-03-07 21:45:11 +11:00
|
|
|
pub struct EmulatorRenderer {
|
2023-03-08 11:01:18 +11:00
|
|
|
tx: Sender<Vec<[u8; 4]>>,
|
2023-03-07 21:45:11 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EmulatorRenderer {
|
2023-03-08 11:01:18 +11:00
|
|
|
pub(super) fn new() -> (Self, Receiver<Vec<[u8; 4]>>) {
|
|
|
|
let (tx, rx) = mpsc::channel::<Vec<[u8; 4]>>();
|
2023-03-07 21:45:11 +11:00
|
|
|
(Self { tx }, rx)
|
|
|
|
}
|
|
|
|
}
|
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-07 21:45:11 +11:00
|
|
|
#[allow(unused_must_use)]
|
2023-03-08 11:01:18 +11:00
|
|
|
fn display(&mut self, buffer: &[[u8; 4]]) {
|
2023-03-07 21:45:11 +11:00
|
|
|
self.tx.send(buffer.to_vec());
|
|
|
|
}
|
2023-03-07 08:51:12 +11:00
|
|
|
|
|
|
|
fn latest_joypad_state(&mut self) -> gb_emu_lib::connect::JoypadState {
|
|
|
|
gb_emu_lib::connect::JoypadState::default()
|
|
|
|
}
|
|
|
|
}
|