gb-emu/gb-vst/src/ui.rs

241 lines
7.4 KiB
Rust
Raw Normal View History

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-03-08 15:19:10 +11:00
connect::{JoypadButtons, JoypadState, Renderer, HEIGHT, WIDTH},
2023-03-09 11:52:24 +11:00
util::scale_buffer_in_place,
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::*;
use pixels::{wgpu::PowerPreference, Pixels, SurfaceTexture};
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-03-08 15:26:32 +11:00
const EXTRA_SCALE: usize = 3;
2023-03-24 13:01:39 +11:00
const S_WIDTH: usize = WIDTH * EXTRA_SCALE;
const S_HEIGHT: usize = HEIGHT * EXTRA_SCALE;
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
let (size, scale) = if cfg!(target_os = "macos") {
(
Size::new(S_WIDTH as f64, S_HEIGHT as f64),
baseview::WindowScalePolicy::SystemScaleFactor,
)
} else {
(
Size::new(WIDTH as f64, HEIGHT as f64),
baseview::WindowScalePolicy::ScaleFactor(EXTRA_SCALE as f64),
)
};
2023-03-05 20:18:06 +11:00
Window::open_parented(
&parent,
WindowOpenOptions {
title: String::from("gb-emu"),
2023-03-24 13:01:39 +11:00
size,
scale,
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 {
pix: Pixels,
2023-03-07 21:45:11 +11:00
scale: usize,
2023-09-10 10:56:21 +10:00
scaled_buf: Frame,
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-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-03-05 20:18:06 +11:00
2023-03-09 11:52:24 +11:00
let (pix, scale, scaled_buf) = init_pixbuf(info, window);
2023-03-05 20:18:06 +11:00
Self {
pix,
2023-03-07 21:45:11 +11:00
scale,
2023-03-09 11:52:24 +11:00
scaled_buf,
2023-03-07 21:45:11 +11:00
frame_receiver,
2023-03-08 15:19:10 +11:00
joypad_sender,
2023-03-05 20:18:06 +11:00
}
}
}
2023-09-10 10:56:21 +10:00
fn init_pixbuf(info: WindowInfo, window: &mut Window) -> (Pixels, usize, Frame) {
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);
2023-03-09 11:52:24 +11:00
let scaled_buf = vec![[0, 0, 0, 0xFF]; WIDTH * scale * HEIGHT * scale];
2023-03-07 21:45:11 +11:00
(
pixels::PixelsBuilder::new(
2023-03-07 21:45:11 +11:00
physical_size.width,
physical_size.height,
SurfaceTexture::new(physical_size.width, physical_size.height, window),
)
.request_adapter_options(pixels::wgpu::RequestAdapterOptionsBase {
power_preference: PowerPreference::HighPerformance,
..pixels::wgpu::RequestAdapterOptionsBase::default()
})
.build()
2023-03-07 21:45:11 +11:00
.unwrap(),
scale,
2023-03-09 11:52:24 +11:00
scaled_buf,
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() {
if self.scale != 1 {
scale_buffer_in_place(buf, &mut self.scaled_buf, WIDTH, HEIGHT, self.scale);
}
for (pixel, source) in self
.pix
2023-05-18 10:36:52 +10:00
.frame_mut()
2023-03-09 11:52:24 +11:00
.chunks_exact_mut(4)
.zip(&self.scaled_buf)
2023-03-08 15:34:31 +11:00
{
2023-03-09 11:52:24 +11:00
pixel.copy_from_slice(source);
2023-03-07 21:45:11 +11:00
}
2023-03-08 15:34:31 +11:00
self.pix.render().unwrap();
2023-03-07 21:45:11 +11:00
}
}
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-03-09 11:52:24 +11:00
(self.pix, self.scale, self.scaled_buf) = init_pixbuf(info, 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
}
}