broken GUI impl + Broken PPU scrolling
This commit is contained in:
+69
-2
@@ -1,15 +1,17 @@
|
||||
use eframe::egui;
|
||||
use crate::input::{key_to_button, Controller, Key};
|
||||
use crate::palette::index_to_rgb;
|
||||
|
||||
pub trait Renderer {
|
||||
fn present(&mut self, framebuffer: &[u8], width: u16, height: u16);
|
||||
fn should_close(&self) -> bool { false } // headless never closes
|
||||
fn poll_input(&mut self, _controller: &mut Controller) {} // headless: no input
|
||||
}
|
||||
|
||||
pub struct PpmRenderer {
|
||||
frame: u32,
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl PpmRenderer {
|
||||
pub fn new() -> Self {
|
||||
Self { frame: 0 }
|
||||
@@ -26,6 +28,71 @@ impl Renderer for PpmRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
// The GUI renderer: egui window, texture upload, keyboard -> controller.
|
||||
pub struct WindowRenderer {
|
||||
ctx: egui::Context,
|
||||
texture: Option<egui::TextureHandle>,
|
||||
rgb: Vec<u8>, // reusable RGB buffer (palette indices -> RGB)
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl WindowRenderer {
|
||||
pub fn new(cc: &eframe::CreationContext<'_>, width: u16, height: u16) -> Self {
|
||||
Self {
|
||||
ctx: cc.egui_ctx.clone(),
|
||||
texture: None,
|
||||
rgb: vec![0; width as usize * height as usize * 3],
|
||||
width: width as usize,
|
||||
height: height as usize,
|
||||
}
|
||||
}
|
||||
|
||||
// Called from eframe::App::ui: draws the texture filling the window.
|
||||
pub fn draw(&mut self, ui: &mut egui::Ui) {
|
||||
if let Some(tex) = &self.texture {
|
||||
let avail = ui.available_size();
|
||||
ui.add(egui::Image::new(tex).fit_to_exact_size(avail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for WindowRenderer {
|
||||
fn present(&mut self, framebuffer: &[u8], width: u16, height: u16) {
|
||||
// palette indices -> RGB
|
||||
for (i, &idx) in framebuffer.iter().enumerate() {
|
||||
let (r, g, b) = index_to_rgb(idx);
|
||||
self.rgb[i * 3] = r;
|
||||
self.rgb[i * 3 + 1] = g;
|
||||
self.rgb[i * 3 + 2] = b;
|
||||
}
|
||||
let image = egui::ColorImage::from_rgb([self.width, self.height], &self.rgb);
|
||||
match &mut self.texture {
|
||||
Some(t) => t.set(image, egui::TextureOptions::NEAREST),
|
||||
None => {
|
||||
let t = self.ctx.load_texture("nes-screen", image, egui::TextureOptions::NEAREST);
|
||||
self.texture = Some(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_input(&mut self, controller: &mut Controller) {
|
||||
let buttons = self.ctx.input(|i| {
|
||||
let mut b = 0u8;
|
||||
if i.key_down(egui::Key::Z) { b |= key_to_button(Key::A); }
|
||||
if i.key_down(egui::Key::X) { b |= key_to_button(Key::B); }
|
||||
if i.modifiers.shift { b |= key_to_button(Key::Select); }
|
||||
if i.key_down(egui::Key::Enter) { b |= key_to_button(Key::Start); }
|
||||
if i.key_down(egui::Key::ArrowUp) { b |= key_to_button(Key::Up); }
|
||||
if i.key_down(egui::Key::ArrowDown) { b |= key_to_button(Key::Down); }
|
||||
if i.key_down(egui::Key::ArrowLeft) { b |= key_to_button(Key::Left); }
|
||||
if i.key_down(egui::Key::ArrowRight) { b |= key_to_button(Key::Right); }
|
||||
b
|
||||
});
|
||||
controller.set_buttons(buttons);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ppm_bytes(framebuffer: &[u8], width: u16, height: u16) -> Vec<u8> {
|
||||
let mut out = format!("P6\n{width} {height}\n255\n").into_bytes();
|
||||
for &idx in framebuffer {
|
||||
|
||||
Reference in New Issue
Block a user