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