Initial commit: NES emulator with GTK4 desktop frontend
Some checks failed
CI / rust (push) Has been cancelled
Some checks failed
CI / rust (push) Has been cancelled
Full NES emulation: CPU, PPU, APU, 47 mappers, iNES/NES 2.0 parsing. GTK4 desktop client with HeaderBar, pixel-perfect Cairo rendering, drag-and-drop ROM loading, and keyboard shortcuts. 187 tests covering core emulation, mappers, and runtime.
This commit is contained in:
118
src/runtime/adapters.rs
Normal file
118
src/runtime/adapters.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
#[cfg(feature = "adapter-api")]
|
||||
use nesemu_adapter_api::{
|
||||
AudioSink, BUTTONS_COUNT, ButtonState, InputSource, TimeSource, VideoSink,
|
||||
};
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
use crate::runtime::{FrameClock, JoypadButtons};
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
fn to_joypad_buttons(buttons: ButtonState) -> JoypadButtons {
|
||||
let mut out = [false; crate::runtime::JOYPAD_BUTTONS_COUNT];
|
||||
out.copy_from_slice(&buttons[..BUTTONS_COUNT]);
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
pub struct InputAdapter<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> InputAdapter<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> crate::runtime::InputProvider for InputAdapter<T>
|
||||
where
|
||||
T: InputSource,
|
||||
{
|
||||
fn poll_buttons(&mut self) -> JoypadButtons {
|
||||
to_joypad_buttons(self.inner.poll_buttons())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
pub struct VideoAdapter<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> VideoAdapter<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> crate::runtime::VideoOutput for VideoAdapter<T>
|
||||
where
|
||||
T: VideoSink,
|
||||
{
|
||||
fn present_rgba(&mut self, frame: &[u8], width: usize, height: usize) {
|
||||
self.inner.present_rgba(frame, width as u32, height as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
pub struct AudioAdapter<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> AudioAdapter<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> crate::runtime::AudioOutput for AudioAdapter<T>
|
||||
where
|
||||
T: AudioSink,
|
||||
{
|
||||
fn push_samples(&mut self, samples: &[f32]) {
|
||||
self.inner.push_samples(samples);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
pub struct ClockAdapter<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> ClockAdapter<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "adapter-api")]
|
||||
impl<T> FrameClock for ClockAdapter<T>
|
||||
where
|
||||
T: TimeSource,
|
||||
{
|
||||
fn wait_next_frame(&mut self) {
|
||||
self.inner.wait_next_frame();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user