input handling
This commit is contained in:
parent
b4d5510bc5
commit
f333723f34
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -1101,6 +1101,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"serde",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2199,6 +2201,12 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
|
@ -2252,6 +2260,7 @@ dependencies = [
|
|||
"baseview",
|
||||
"futures",
|
||||
"gb-emu-lib",
|
||||
"keyboard-types",
|
||||
"nih_plug",
|
||||
"pixels",
|
||||
]
|
||||
|
|
|
@ -17,3 +17,4 @@ baseview = { path = "../vendored/baseview" }
|
|||
pixels = "0.11.0"
|
||||
async-ringbuf = "0.1.2"
|
||||
futures = "0.3.26"
|
||||
keyboard-types = "0.6.2"
|
|
@ -1,7 +1,7 @@
|
|||
use async_ringbuf::AsyncHeapConsumer;
|
||||
use futures::executor;
|
||||
use gb_emu_lib::{
|
||||
connect::{AudioOutput, EmulatorMessage, RomFile},
|
||||
connect::{AudioOutput, EmulatorMessage, JoypadButtons, RomFile},
|
||||
EmulatorCore,
|
||||
};
|
||||
use nih_plug::prelude::*;
|
||||
|
@ -26,9 +26,11 @@ struct EmuVars {
|
|||
pub struct GameboyEmu {
|
||||
vars: Option<EmuVars>,
|
||||
frame_receiver: Arc<FrameReceiver>,
|
||||
key_handler: Arc<JoypadSender>,
|
||||
}
|
||||
|
||||
type FrameReceiver = Mutex<Option<Receiver<Vec<[u8; 4]>>>>;
|
||||
type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
||||
|
||||
const ROM: &[u8; 32768] = include_bytes!("../../test-roms/Tetris.gb");
|
||||
|
||||
|
@ -89,7 +91,10 @@ impl Plugin for GameboyEmu {
|
|||
}
|
||||
|
||||
fn editor(&self, _: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
|
||||
Some(Box::new(Emulator::new(self.frame_receiver.clone())))
|
||||
Some(Box::new(Emulator::new(
|
||||
self.frame_receiver.clone(),
|
||||
self.key_handler.clone(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn initialize(
|
||||
|
@ -111,9 +116,10 @@ impl Plugin for GameboyEmu {
|
|||
|
||||
let (output, rx) = AudioOutput::new_unfilled(buffer_config.sample_rate, false);
|
||||
|
||||
let (renderer, frame_receiver) = EmulatorRenderer::new();
|
||||
let (renderer, frame_receiver, key_handler) = EmulatorRenderer::new();
|
||||
|
||||
*self.frame_receiver.lock().unwrap() = Some(frame_receiver);
|
||||
*self.key_handler.lock().unwrap() = Some(key_handler);
|
||||
|
||||
let mut emulator_core =
|
||||
EmulatorCore::init(receiver, options, Box::new(renderer), output, None);
|
||||
|
|
|
@ -7,21 +7,26 @@ use baseview::{
|
|||
Event, EventStatus, Size, Window, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
||||
};
|
||||
use gb_emu_lib::{
|
||||
connect::{Renderer, HEIGHT, WIDTH},
|
||||
connect::{JoypadButtons, JoypadState, Renderer, HEIGHT, WIDTH},
|
||||
util::scale_buffer,
|
||||
};
|
||||
use keyboard_types::{Code, KeyState};
|
||||
use nih_plug::prelude::*;
|
||||
use pixels::{Pixels, SurfaceTexture};
|
||||
|
||||
use crate::FrameReceiver;
|
||||
use crate::{FrameReceiver, JoypadSender};
|
||||
|
||||
pub struct Emulator {
|
||||
frame_receiver: Arc<FrameReceiver>,
|
||||
joypad_sender: Arc<JoypadSender>,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn new(frame_receiver: Arc<FrameReceiver>) -> Self {
|
||||
Self { frame_receiver }
|
||||
pub fn new(frame_receiver: Arc<FrameReceiver>, joypad_sender: Arc<JoypadSender>) -> Self {
|
||||
Self {
|
||||
frame_receiver,
|
||||
joypad_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +36,8 @@ impl Editor for Emulator {
|
|||
parent: ParentWindowHandle,
|
||||
_context: Arc<dyn GuiContext>,
|
||||
) -> Box<dyn std::any::Any + Send> {
|
||||
let cloned = self.frame_receiver.clone();
|
||||
let fr_cloned = self.frame_receiver.clone();
|
||||
let js_cloned = self.joypad_sender.clone();
|
||||
Window::open_parented(
|
||||
&parent,
|
||||
WindowOpenOptions {
|
||||
|
@ -40,9 +46,12 @@ impl Editor for Emulator {
|
|||
scale: baseview::WindowScalePolicy::SystemScaleFactor,
|
||||
gl_config: None,
|
||||
},
|
||||
|w| EmulatorWindow::new(w, cloned),
|
||||
|w| EmulatorWindow::new(w, fr_cloned, js_cloned),
|
||||
);
|
||||
Box::new(Self::new(self.frame_receiver.clone()))
|
||||
Box::new(Self::new(
|
||||
self.frame_receiver.clone(),
|
||||
self.joypad_sender.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn size(&self) -> (u32, u32) {
|
||||
|
@ -64,10 +73,15 @@ pub struct EmulatorWindow {
|
|||
pix: Pixels,
|
||||
scale: usize,
|
||||
frame_receiver: Arc<FrameReceiver>,
|
||||
joypad_sender: Arc<JoypadSender>,
|
||||
}
|
||||
|
||||
impl EmulatorWindow {
|
||||
fn new(window: &mut Window, frame_receiver: Arc<FrameReceiver>) -> Self {
|
||||
fn new(
|
||||
window: &mut Window,
|
||||
frame_receiver: Arc<FrameReceiver>,
|
||||
joypad_sender: Arc<JoypadSender>,
|
||||
) -> Self {
|
||||
let info = WindowInfo::from_logical_size(Size::new(WIDTH as f64, HEIGHT as f64), 1.);
|
||||
|
||||
let (pix, scale) = init_pixbuf(info, window);
|
||||
|
@ -76,6 +90,7 @@ impl EmulatorWindow {
|
|||
pix,
|
||||
scale,
|
||||
frame_receiver,
|
||||
joypad_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,22 +132,59 @@ impl WindowHandler for EmulatorWindow {
|
|||
}
|
||||
|
||||
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;
|
||||
match event {
|
||||
Event::Window(WindowEvent::Resized(info)) => {
|
||||
(self.pix, self.scale) = init_pixbuf(info, 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,
|
||||
} {
|
||||
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,
|
||||
}
|
||||
EventStatus::Ignored
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmulatorRenderer {
|
||||
tx: Sender<Vec<[u8; 4]>>,
|
||||
joypad: JoypadState,
|
||||
keys: Receiver<(JoypadButtons, bool)>,
|
||||
}
|
||||
|
||||
impl EmulatorRenderer {
|
||||
pub(super) fn new() -> (Self, Receiver<Vec<[u8; 4]>>) {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(super) fn new() -> (Self, Receiver<Vec<[u8; 4]>>, Sender<(JoypadButtons, bool)>) {
|
||||
let (tx, rx) = mpsc::channel::<Vec<[u8; 4]>>();
|
||||
(Self { tx }, rx)
|
||||
let (keys_tx, keys) = mpsc::channel::<(JoypadButtons, bool)>();
|
||||
(
|
||||
Self {
|
||||
tx,
|
||||
joypad: JoypadState::default(),
|
||||
keys,
|
||||
},
|
||||
rx,
|
||||
keys_tx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +196,19 @@ impl Renderer<[u8; 4]> for EmulatorRenderer {
|
|||
self.tx.send(buffer.to_vec());
|
||||
}
|
||||
|
||||
fn latest_joypad_state(&mut self) -> gb_emu_lib::connect::JoypadState {
|
||||
gb_emu_lib::connect::JoypadState::default()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::processor::memory::mmio::gpu::Colour;
|
||||
pub use crate::processor::memory::mmio::joypad::JoypadState;
|
||||
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
|
||||
pub use crate::{HEIGHT, WIDTH};
|
||||
use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb};
|
||||
use futures::executor;
|
||||
|
|
|
@ -19,6 +19,17 @@ pub struct JoypadState {
|
|||
pub a: bool,
|
||||
}
|
||||
|
||||
pub enum JoypadButtons {
|
||||
Down,
|
||||
Up,
|
||||
Left,
|
||||
Right,
|
||||
Start,
|
||||
Select,
|
||||
B,
|
||||
A,
|
||||
}
|
||||
|
||||
impl JoypadState {
|
||||
pub fn reset(&mut self) {
|
||||
self.down = false;
|
||||
|
|
Loading…
Reference in a new issue