From 5c3d537cfd4fd0ea430f9af51ce48de7c1f179df Mon Sep 17 00:00:00 2001 From: Matthew Pomes Date: Fri, 19 Dec 2025 20:38:47 -0600 Subject: [PATCH] Update before heading home --- Cargo.lock | 47 +++--- Cargo.toml | 1 + src/header_menu.rs | 3 + src/hex_view.rs | 1 + src/lib.rs | 1 + src/main.rs | 158 +++++++++++--------- src/ppu.rs | 113 +++++++++++--- src/resize_watcher.rs | 340 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 555 insertions(+), 109 deletions(-) create mode 100644 src/resize_watcher.rs diff --git a/Cargo.lock b/Cargo.lock index 2eb1fe9..b029a7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -774,8 +774,7 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737" +source = "git+https://github.com/pop-os/cosmic-text.git?rev=a07a6190548c8e40a55f6b7761387047ff1bf6ff#a07a6190548c8e40a55f6b7761387047ff1bf6ff" dependencies = [ "bitflags 2.10.0", "fontdb", @@ -838,8 +837,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc795bdbccdbd461736fb163930a009da6597b226d6f6fce33e7a8eb6ec519" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=89883bcf38b5bed0d7bade788ef738d9facc857c#89883bcf38b5bed0d7bade788ef738d9facc857c" dependencies = [ "cosmic-text", "etagere", @@ -902,9 +900,8 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +version = "0.1.1" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" [[package]] name = "either" @@ -1491,7 +1488,7 @@ dependencies = [ [[package]] name = "iced" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_core", "iced_debug", @@ -1507,7 +1504,7 @@ dependencies = [ [[package]] name = "iced_beacon" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bincode", "futures", @@ -1521,7 +1518,7 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bitflags 2.10.0", "bytes", @@ -1538,7 +1535,7 @@ dependencies = [ [[package]] name = "iced_debug" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_beacon", "iced_core", @@ -1548,7 +1545,7 @@ dependencies = [ [[package]] name = "iced_devtools" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_debug", "iced_program", @@ -1558,7 +1555,7 @@ dependencies = [ [[package]] name = "iced_futures" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "futures", "iced_core", @@ -1571,7 +1568,7 @@ dependencies = [ [[package]] name = "iced_graphics" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -1591,7 +1588,7 @@ dependencies = [ [[package]] name = "iced_program" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_graphics", "iced_runtime", @@ -1599,7 +1596,7 @@ dependencies = [ [[package]] name = "iced_renderer" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1610,7 +1607,7 @@ dependencies = [ [[package]] name = "iced_runtime" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bytes", "iced_core", @@ -1621,7 +1618,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bytemuck", "cosmic-text", @@ -1636,7 +1633,7 @@ dependencies = [ [[package]] name = "iced_wgpu" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -1655,7 +1652,7 @@ dependencies = [ [[package]] name = "iced_widget" -version = "0.14.2" +version = "0.15.0-dev" dependencies = [ "iced_renderer", "log", @@ -1668,7 +1665,7 @@ dependencies = [ [[package]] name = "iced_winit" -version = "0.14.0" +version = "0.15.0-dev" dependencies = [ "iced_debug", "iced_program", @@ -2199,6 +2196,7 @@ dependencies = [ "bitfield", "iced", "thiserror 2.0.17", + "tokio", "tracing", "tracing-subscriber", ] @@ -3734,7 +3732,9 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.61.2", @@ -4822,9 +4822,8 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" -version = "0.30.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" +version = "0.30.8" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" dependencies = [ "ahash", "android-activity", diff --git a/Cargo.toml b/Cargo.toml index dd16a74..3c30140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,6 @@ iced = { path = "../iced", features = ["debug", "canvas", "tokio", "lazy", "imag # iced_graphics = { version = "0.14.0", features = ["geometry", "image"] } # iced_widget = { version = "0.13.4", features = ["canvas", "image"] } thiserror = "2.0.17" +tokio = { version = "1.48.0", features = ["full"] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.20", features = ["ansi", "chrono", "env-filter", "json", "serde"] } diff --git a/src/header_menu.rs b/src/header_menu.rs index d408108..929021e 100644 --- a/src/header_menu.rs +++ b/src/header_menu.rs @@ -237,6 +237,7 @@ where align_y: alignment::Vertical::Center, shaping: self.text_shaping, wrapping: text::Wrapping::default(), + hint_factor: renderer.scale_factor(), }; for (option, paragraph) in options.iter().zip(state.options.iter_mut()) { @@ -474,6 +475,7 @@ where align_y: alignment::Vertical::Center, shaping, wrapping: text::Wrapping::default(), + hint_factor: renderer.scale_factor(), }, Point::new( bounds.x + bounds.width - self.padding.right, @@ -500,6 +502,7 @@ where align_y: alignment::Vertical::Center, shaping: self.text_shaping, wrapping: text::Wrapping::default(), + hint_factor: renderer.scale_factor(), }, Point::new(bounds.x + self.padding.left, bounds.center_y()), style.text_color, diff --git a/src/hex_view.rs b/src/hex_view.rs index 6f5088d..c39ef37 100644 --- a/src/hex_view.rs +++ b/src/hex_view.rs @@ -10,6 +10,7 @@ pub trait Memory { #[derive(Debug, Clone)] pub enum HexEvent {} +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HexView { } diff --git a/src/lib.rs b/src/lib.rs index c2d9696..2770480 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ mod mem; mod ppu; #[cfg(test)] mod test_roms; +pub mod resize_watcher; pub use ppu::{Color, PPU, RenderBuffer}; diff --git a/src/main.rs b/src/main.rs index 1a7f538..0d17e5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ -use std::{collections::HashMap, fmt}; +use std::{collections::HashMap, fmt, time::Duration}; use iced::{ Color, Element, Font, - Length::Fill, + Length::{Fill, Shrink}, Point, Renderer, Size, Subscription, Task, Theme, mouse, widget::{ Canvas, button, @@ -16,7 +16,9 @@ use nes_emu::{ debugger::{DebuggerMessage, DebuggerState}, header_menu::header_menu, hex_view::{HexEvent, HexView}, + resize_watcher::resize_watcher, }; +use tokio::runtime::Runtime; use tracing_subscriber::EnvFilter; const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "even_odd.nes"); @@ -33,13 +35,16 @@ fn main() -> Result<(), iced::Error> { .subscription(Emulator::subscriptions) .theme(Theme::Dark) .title(Emulator::title) + .executor::() .run() } +#[derive(Debug, Clone, PartialEq, Eq)] enum MemoryTy { Cpu, } +#[derive(Debug, Clone, PartialEq, Eq)] enum WindowType { Main, Memory(MemoryTy, HexView), @@ -51,10 +56,11 @@ enum WindowType { #[derive(Debug, Clone, PartialEq, Eq)] enum HeaderButton { - OpenMemory, - OpenTileMap, - OpenTileViewer, - OpenDebugger, + // OpenMemory, + // OpenTileMap, + // OpenTileViewer, + // OpenDebugger, + Open(WindowType), Reset, PowerCycle, } @@ -62,10 +68,12 @@ enum HeaderButton { impl fmt::Display for HeaderButton { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::OpenMemory => write!(f, "Open Memory Viewer"), - Self::OpenTileMap => write!(f, "Open TileMap Viewer"), - Self::OpenTileViewer => write!(f, "Open Tile Viewer"), - Self::OpenDebugger => write!(f, "Open Debugger"), + Self::Open(WindowType::Memory(MemoryTy::Cpu, _)) => write!(f, "Open Memory Viewer"), + Self::Open(WindowType::TileMap) => write!(f, "Open TileMap Viewer"), + Self::Open(WindowType::TileViewer) => write!(f, "Open Tile Viewer"), + Self::Open(WindowType::Debugger) => write!(f, "Open Debugger"), + Self::Open(WindowType::Palette) => write!(f, "Open Palette"), + Self::Open(WindowType::Main) => write!(f, "Create new Main window"), Self::Reset => write!(f, "Reset"), Self::PowerCycle => write!(f, "Power Cycle"), } @@ -76,6 +84,7 @@ struct Emulator { nes: NES, windows: HashMap, debugger: DebuggerState, + main_win_size: Size, } #[derive(Debug, Clone)] @@ -85,25 +94,29 @@ enum Message { DMA, CPU, DebugInt, - WindowOpened(Id), WindowClosed(Id), Header(HeaderButton), Hex(Id, HexEvent), Debugger(DebuggerMessage), + SetSize(window::Id, Size), } impl Emulator { fn new() -> (Self, Task) { let mut nes = nes_emu::NES::load_nes_file(ROM_FILE).expect("Failed to load nes file"); nes.reset(); - let (win, task) = iced::window::open(Settings::default()); + let (win, task) = iced::window::open(Settings { + min_size: None, + ..Settings::default() + }); ( Self { nes, windows: HashMap::from_iter([(win, WindowType::Main)]), debugger: DebuggerState::new(), + main_win_size: Size::new(0., 0.), }, - task.map(Message::WindowOpened), + task.discard(), ) } fn title(&self, win: Id) -> String { @@ -121,7 +134,7 @@ impl Emulator { fn open(&mut self, ty: WindowType) -> Task { let (win, task) = iced::window::open(Settings::default()); self.windows.insert(win, ty); - return task.map(Message::WindowOpened); + return task.discard(); } fn update(&mut self, message: Message) -> Task { @@ -135,25 +148,13 @@ impl Emulator { Message::DMA => while !self.nes.run_one_clock_cycle().dma {}, Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {}, Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {}, - Message::WindowOpened(_id) => { - // Window - } Message::WindowClosed(id) => { if let Some(WindowType::Main) = self.windows.remove(&id) { return iced::exit(); } } - Message::Header(HeaderButton::OpenMemory) => { - return self.open(WindowType::Memory(MemoryTy::Cpu, HexView::new())); - } - Message::Header(HeaderButton::OpenTileMap) => { - return self.open(WindowType::TileMap); - } - Message::Header(HeaderButton::OpenTileViewer) => { - return self.open(WindowType::TileViewer); - } - Message::Header(HeaderButton::OpenDebugger) => { - return self.open(WindowType::Debugger); + Message::Header(HeaderButton::Open(w)) => { + return self.open(w); } Message::Hex(id, val) => { if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) { @@ -169,13 +170,31 @@ impl Emulator { Message::Debugger(debugger_message) => { self.debugger.update(debugger_message, &mut self.nes) } + Message::SetSize(id, size) => { + if let Some(WindowType::Main) = self.windows.get(&id) { + self.main_win_size = size; + } + println!("New size for {:?}, {:?}", id, size); + // return iced::window::set_min_size(id, size.into()) + // .then(move |_: ()| iced::window::resize(id, size)); + return Task::future(async { + tokio::time::sleep(Duration::from_millis(50)).await; + }) + .then(move |_| { + iced::window::resize(id, size) + }); + } } // self.image.0.clone_from(self.nes.image()); Task::none() } fn subscriptions(&self) -> Subscription { - window::close_events().map(Message::WindowClosed) + Subscription::batch([ + window::close_events().map(Message::WindowClosed), + // window::events().map(Message::Window), + // window::resize_events().map(Message::WindowResized), + ]) } fn view(&self, win: Id) -> Element<'_, Message> { @@ -183,12 +202,16 @@ impl Emulator { Some(WindowType::Main) => { let content = column![ self.dropdowns(), - Element::from(Canvas::new(self).width(Fill).height(255. * 2.)), - self.cpu_state(), - self.controls(), + Element::from(Canvas::new(self).width(256. * 2.).height(240. * 2.)), + // self.cpu_state(), + // self.controls(), ] - .height(Fill); - container(content).width(Fill).height(Fill).into() + .height(Shrink); + resize_watcher(content) + .on_resize(move |s| Message::SetSize(win, s)) + .width(Shrink) + .height(Shrink) + .into() } Some(WindowType::Memory(ty, view)) => { let hex = match ty { @@ -211,12 +234,10 @@ impl Emulator { .height(Fill) .into() } - Some(WindowType::Palette) => { - container(Canvas::new(DbgImage::Palette(self.nes.ppu()))) - .width(Fill) - .height(Fill) - .into() - } + Some(WindowType::Palette) => container(Canvas::new(DbgImage::Palette(self.nes.ppu()))) + .width(Fill) + .height(Fill) + .into(), Some(WindowType::Debugger) => { container(self.debugger.view(&self.nes).map(Message::Debugger)) .width(Fill) @@ -228,26 +249,26 @@ impl Emulator { } } - fn cpu_state(&self) -> Element<'_, Message> { - row![column![ - // text!("Registers").font(Font::MONOSPACE), - text!("{:?}", self.nes).font(Font::MONOSPACE), - ],] - .width(Fill) - .into() - } + // fn cpu_state(&self) -> Element<'_, Message> { + // row![column![ + // // text!("Registers").font(Font::MONOSPACE), + // text!("{:?}", self.nes).font(Font::MONOSPACE), + // ],] + // .width(Fill) + // .into() + // } - fn controls(&self) -> Element<'_, Message> { - row![ - button("Clock tick").on_press(Message::Tick(1)), - button("CPU tick").on_press(Message::CPU), - button("Next Frame").on_press(Message::Frame), - button("Next DMA").on_press(Message::DMA), - button("Next DBG").on_press(Message::DebugInt), - ] - .width(Fill) - .into() - } + // fn controls(&self) -> Element<'_, Message> { + // row![ + // button("Clock tick").on_press(Message::Tick(1)), + // button("CPU tick").on_press(Message::CPU), + // button("Next Frame").on_press(Message::Frame), + // button("Next DMA").on_press(Message::DMA), + // button("Next DBG").on_press(Message::DebugInt), + // ] + // .width(Fill) + // .into() + // } fn dropdowns(&self) -> Element<'_, Message> { row![ @@ -259,10 +280,11 @@ impl Emulator { header_menu( "Debugging", [ - HeaderButton::OpenDebugger, - HeaderButton::OpenMemory, - HeaderButton::OpenTileMap, - HeaderButton::OpenTileViewer, + HeaderButton::Open(WindowType::Debugger), + HeaderButton::Open(WindowType::Memory(MemoryTy::Cpu, HexView {})), + HeaderButton::Open(WindowType::TileMap), + HeaderButton::Open(WindowType::TileViewer), + HeaderButton::Open(WindowType::Palette), ], Message::Header ) @@ -280,16 +302,16 @@ impl Program for Emulator { _state: &Self::State, renderer: &Renderer, _theme: &Theme, - _bounds: iced::Rectangle, + bounds: iced::Rectangle, _cursor: mouse::Cursor, ) -> Vec> { // const SIZE: f32 = 2.; let mut frame = Frame::new( renderer, - iced::Size { - width: 256. * 2., - height: 240. * 2., - }, + bounds.size(), // iced::Size { + // width: 256. * 2., + // height: 240. * 2., + // }, ); frame.scale(2.); for y in 0..240 { diff --git a/src/ppu.rs b/src/ppu.rs index defb357..43f9bb1 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -98,13 +98,76 @@ pub struct Mask { em_blue: bool, } -const COLORS: &'static [Color; 0b11_1111] = &[ - Color { r: 0, g: 0, b: 0}; 0b11_1111 +const COLORS: &'static [Color; 0b100_0000] = &[ + Color { r: 0x66, g: 0x66, b: 0x66 }, // 00 + Color { r: 0x00, g: 0x2A, b: 0x88 }, // 01 + Color { r: 0x14, g: 0x12, b: 0xA7 }, // 02 + Color { r: 0x3B, g: 0x00, b: 0xA4 }, // 03 + Color { r: 0x5C, g: 0x00, b: 0x7E }, // 04 + Color { r: 0x6E, g: 0x00, b: 0x40 }, // 05 + Color { r: 0x6C, g: 0x06, b: 0x00 }, // 06 + Color { r: 0x56, g: 0x1D, b: 0x00 }, // 07 + Color { r: 0x33, g: 0x35, b: 0x00 }, // 08 + Color { r: 0x0B, g: 0x48, b: 0x00 }, // 09 + Color { r: 0x00, g: 0x52, b: 0x00 }, // 0A + Color { r: 0x00, g: 0x4F, b: 0x08 }, // 0B + Color { r: 0x00, g: 0x40, b: 0x4D }, // 0C + Color { r: 0x00, g: 0x00, b: 0x00 }, // 0D + Color { r: 0x00, g: 0x00, b: 0x00 }, // 0E + Color { r: 0x00, g: 0x00, b: 0x00 }, // 0F + Color { r: 0xAD, g: 0xAD, b: 0xAD }, // 10 + Color { r: 0x15, g: 0x5F, b: 0xD9 }, // 11 + Color { r: 0x42, g: 0x40, b: 0xFF }, // 12 + Color { r: 0x75, g: 0x27, b: 0xFE }, // 13 + Color { r: 0xA0, g: 0x1A, b: 0xCC }, // 14 + Color { r: 0xB7, g: 0x1E, b: 0x7B }, // 15 + Color { r: 0xB5, g: 0x31, b: 0x20 }, // 16 + Color { r: 0x99, g: 0x4E, b: 0x00 }, // 17 + Color { r: 0x6B, g: 0x6D, b: 0x00 }, // 18 + Color { r: 0x38, g: 0x87, b: 0x00 }, // 19 + Color { r: 0x0C, g: 0x93, b: 0x00 }, // 1A + Color { r: 0x00, g: 0x8F, b: 0x32 }, // 1B + Color { r: 0x00, g: 0x7C, b: 0x8D }, // 1C + Color { r: 0x00, g: 0x00, b: 0x00 }, // 1D + Color { r: 0x00, g: 0x00, b: 0x00 }, // 1E + Color { r: 0x00, g: 0x00, b: 0x00 }, // 1F + Color { r: 0xFF, g: 0xFE, b: 0xFF }, // 20 + Color { r: 0x64, g: 0xB0, b: 0xFF }, // 21 + Color { r: 0x92, g: 0x90, b: 0xFF }, // 22 + Color { r: 0xC6, g: 0x76, b: 0xFF }, // 23 + Color { r: 0xF3, g: 0x6A, b: 0xFF }, // 24 + Color { r: 0xFE, g: 0x6E, b: 0xCC }, // 25 + Color { r: 0xFE, g: 0x81, b: 0x70 }, // 26 + Color { r: 0xEA, g: 0x9E, b: 0x22 }, // 27 + Color { r: 0xBC, g: 0xBE, b: 0x00 }, // 28 + Color { r: 0x88, g: 0xD8, b: 0x00 }, // 29 + Color { r: 0x5C, g: 0xE4, b: 0x30 }, // 2A + Color { r: 0x45, g: 0xE0, b: 0x82 }, // 2B + Color { r: 0x48, g: 0xCD, b: 0xDE }, // 2C + Color { r: 0x4F, g: 0x4F, b: 0x4F }, // 2D + Color { r: 0x00, g: 0x00, b: 0x00 }, // 2E + Color { r: 0x00, g: 0x00, b: 0x00 }, // 2F + Color { r: 0xFF, g: 0xFE, b: 0xFF }, // 30 + Color { r: 0xC0, g: 0xDF, b: 0xFF }, // 31 + Color { r: 0xD3, g: 0xD2, b: 0xFF }, // 32 + Color { r: 0xE8, g: 0xC8, b: 0xFF }, // 33 + Color { r: 0xFB, g: 0xC2, b: 0xFF }, // 34 + Color { r: 0xFE, g: 0xC4, b: 0xEA }, // 35 + Color { r: 0xFE, g: 0xCC, b: 0xC5 }, // 36 + Color { r: 0xF7, g: 0xD8, b: 0xA5 }, // 37 + Color { r: 0xE4, g: 0xE5, b: 0x94 }, // 38 + Color { r: 0xCF, g: 0xEF, b: 0x96 }, // 39 + Color { r: 0xBD, g: 0xF4, b: 0xAB }, // 3A + Color { r: 0xB3, g: 0xF3, b: 0xCC }, // 3B + Color { r: 0xB5, g: 0xEB, b: 0xF2 }, // 3C + Color { r: 0xB8, g: 0xB8, b: 0xB8 }, // 3D + Color { r: 0x00, g: 0x00, b: 0x00 }, // 3E + Color { r: 0x00, g: 0x00, b: 0x00 }, // 3F ]; pub struct Palette { - colors: &'static [Color; 0b11_1111], - ram: [u8; 0x20] + colors: &'static [Color; 0x40], + ram: [u8; 0x20], } pub struct PPU { @@ -162,7 +225,19 @@ impl PPU { em_green: false, em_blue: false, }, - palette: Palette { colors: COLORS, ram: [0; _] }, + palette: Palette { + colors: COLORS, + ram: [ + 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, + 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, + ], + }, vblank: false, frame_count: 0, nmi_enabled: false, @@ -220,9 +295,12 @@ impl PPU { 5 => panic!("ppuscroll is write-only"), 6 => panic!("ppuaddr is write-only"), 7 => { - let val = self.memory.read(self.background.v).reg_map(|a, _| match a { - PPUMMRegisters::Palette => todo!(), - }); + let val = self + .memory + .read(self.background.v) + .reg_map(|a, off| match a { + PPUMMRegisters::Palette => self.palette.ram[off as usize], + }); // if self.background self.background.v = self .background @@ -513,16 +591,17 @@ impl PPU { } } } + pub fn render_palette(&self, frame: &mut Frame) { - frame.fill_rectangle( - Point::new(0., 0.), - Size::new(10., 10.), - Color { - r: todo!(), - g: todo!(), - b: todo!(), - }, - ); + for y in 0..8 { + for x in 0..4 { + frame.fill_rectangle( + Point::new(x as f32 * 10., y as f32 * 10.), + Size::new(10., 10.), + self.palette.colors[(self.palette.ram[x + y * 4] & 0x3F) as usize] + ); + } + } } } diff --git a/src/resize_watcher.rs b/src/resize_watcher.rs new file mode 100644 index 0000000..504746f --- /dev/null +++ b/src/resize_watcher.rs @@ -0,0 +1,340 @@ +use iced::{ + Element, Event, Length, Padding, Pixels, Rectangle, Size, Vector, + advanced::{ + Clipboard, Layout, Shell, Widget, layout, mouse, overlay, renderer, + widget::{Operation, Tree, tree}, + }, + alignment, + widget::{ + self, + container::{self, Catalog, Style, StyleFn}, + }, +}; + +pub fn resize_watcher<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> ResizeWatcher<'a, Message, Theme, Renderer> +where + Theme: container::Catalog + 'a, + Renderer: iced::advanced::Renderer, +{ + ResizeWatcher::new(content) +} + +pub struct ResizeWatcher<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer> +where + Theme: container::Catalog + 'a, + Renderer: iced::advanced::Renderer, +{ + c: container::Container<'a, Message, Theme, Renderer>, + on_resize: Option + 'a>>, +} + +pub trait ResizeFn { + fn resized(&self, size: Size) -> Message; +} +impl M, M> ResizeFn for F { + fn resized(&self, size: Size) -> M { + self(size) + } +} + +impl<'a, Message, Theme, Renderer> ResizeWatcher<'a, Message, Theme, Renderer> +where + Theme: container::Catalog + 'a, + Renderer: iced::advanced::Renderer, +{ + pub fn new(content: impl Into>) -> Self { + Self { + c: widget::container(content), + on_resize: None, + } + } + + pub fn on_resize(mut self, f: impl ResizeFn + 'a) -> Self { + self.on_resize = Some(Box::new(f)); + self + } + + /// Sets the [`widget::Id`] of the [`Container`]. + pub fn id(mut self, id: impl Into) -> Self { + self.c = self.c.id(id); + self + } + + /// Sets the [`Padding`] of the [`Container`]. + pub fn padding>(mut self, padding: P) -> Self { + self.c = self.c.padding(padding); + self + } + + /// Sets the width of the [`Container`]. + pub fn width(mut self, width: impl Into) -> Self { + self.c = self.c.width(width); + // self.width = width.into(); + self + } + + /// Sets the height of the [`Container`]. + pub fn height(mut self, height: impl Into) -> Self { + self.c = self.c.height(height); + self + } + + /// Sets the maximum width of the [`Container`]. + pub fn max_width(mut self, max_width: impl Into) -> Self { + self.c = self.c.max_width(max_width); + self + } + + /// Sets the maximum height of the [`Container`]. + pub fn max_height(mut self, max_height: impl Into) -> Self { + self.c = self.c.max_height(max_height); + self + } + + /// Sets the width of the [`Container`] and centers its contents horizontally. + pub fn center_x(mut self, width: impl Into) -> Self { + self.c = self.c.center_x(width); + self + } + + /// Sets the height of the [`Container`] and centers its contents vertically. + pub fn center_y(mut self, height: impl Into) -> Self { + self.c = self.c.center_y(height); + self + } + + /// Sets the width and height of the [`Container`] and centers its contents in + /// both the horizontal and vertical axes. + /// + /// This is equivalent to chaining [`center_x`] and [`center_y`]. + /// + /// [`center_x`]: Self::center_x + /// [`center_y`]: Self::center_y + pub fn center(mut self, length: impl Into) -> Self { + self.c = self.c.center(length); + self + } + + /// Sets the width of the [`Container`] and aligns its contents to the left. + pub fn align_left(mut self, width: impl Into) -> Self { + self.c = self.c.align_left(width); + self + } + + /// Sets the width of the [`Container`] and aligns its contents to the right. + pub fn align_right(mut self, width: impl Into) -> Self { + self.c = self.c.align_right(width); + self + } + + /// Sets the height of the [`Container`] and aligns its contents to the top. + pub fn align_top(mut self, height: impl Into) -> Self { + self.c = self.c.align_top(height); + self + } + + /// Sets the height of the [`Container`] and aligns its contents to the bottom. + pub fn align_bottom(mut self, height: impl Into) -> Self { + self.c = self.c.align_bottom(height); + self + } + + /// Sets the content alignment for the horizontal axis of the [`Container`]. + pub fn align_x(mut self, alignment: impl Into) -> Self { + self.c = self.c.align_x(alignment); + self + } + + /// Sets the content alignment for the vertical axis of the [`Container`]. + pub fn align_y(mut self, alignment: impl Into) -> Self { + self.c = self.c.align_y(alignment); + self + } + + /// Sets whether the contents of the [`Container`] should be clipped on + /// overflow. + pub fn clip(mut self, clip: bool) -> Self { + self.c = self.c.clip(clip); + self + } + + /// Sets the style of the [`Container`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.c = self.c.style(style); + self + } + + /// Sets the style class of the [`Container`]. + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.c = self.c.class(class); + self + } +} + +struct State { + last_size: Size, + cur_size: Size, +} + +impl Widget + for ResizeWatcher<'_, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: iced::advanced::Renderer, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State { + last_size: Size::new(0., 0.), + cur_size: Size::new(0., 0.), + }) + } + + fn children(&self) -> Vec { + vec![Tree::new(&self.c as &dyn Widget<_, _, _>)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.c as &dyn Widget<_, _, _>]); + } + + fn size(&self) -> Size { + self.c.size() + } + + fn layout( + &mut self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let tmp = layout::Node::container( + self.c.layout(&mut tree.children[0], renderer, limits), + Padding::new(0.), + ); + let s: &mut State = tree.state.downcast_mut(); + s.cur_size = tmp.size(); + tmp + } + + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + operation.container(None, layout.bounds()); + self.c + .operate(&mut tree.children[0], layout.child(0), renderer, operation); + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + if let Some(m) = &self.on_resize { + let s: &mut State = tree.state.downcast_mut(); + if s.cur_size != s.last_size { + shell.publish(m.resized(s.cur_size)); + } + s.last_size = s.cur_size; + } + self.c.update( + &mut tree.children[0], + event, + layout.child(0), + cursor, + renderer, + clipboard, + shell, + viewport, + ); + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.c.mouse_interaction( + &tree.children[0], + layout.child(0), + cursor, + viewport, + renderer, + ) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.c.draw( + &tree.children[0], + renderer, + theme, + style, + layout.child(0), + cursor, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'b>, + renderer: &Renderer, + viewport: &Rectangle, + translation: Vector, + ) -> Option> { + self.c.overlay( + &mut tree.children[0], + layout.child(0), + renderer, + viewport, + translation, + ) + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: iced::advanced::Renderer + 'a, +{ + fn from( + container: ResizeWatcher<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(container) + } +}