Update before heading home
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 26s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 26s
This commit is contained in:
47
Cargo.lock
generated
47
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -10,6 +10,7 @@ pub trait Memory {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HexEvent {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HexView {
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ mod mem;
|
||||
mod ppu;
|
||||
#[cfg(test)]
|
||||
mod test_roms;
|
||||
pub mod resize_watcher;
|
||||
|
||||
pub use ppu::{Color, PPU, RenderBuffer};
|
||||
|
||||
|
||||
154
src/main.rs
154
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::<Runtime>()
|
||||
.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<Id, WindowType>,
|
||||
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<Message>) {
|
||||
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<Message> {
|
||||
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<Message> {
|
||||
@@ -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<Message> {
|
||||
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())))
|
||||
Some(WindowType::Palette) => container(Canvas::new(DbgImage::Palette(self.nes.ppu())))
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
}
|
||||
.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<Message> for Emulator {
|
||||
_state: &Self::State,
|
||||
renderer: &Renderer,
|
||||
_theme: &Theme,
|
||||
_bounds: iced::Rectangle,
|
||||
bounds: iced::Rectangle,
|
||||
_cursor: mouse::Cursor,
|
||||
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
||||
// 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 {
|
||||
|
||||
105
src/ppu.rs
105
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,8 +295,11 @@ 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
|
||||
@@ -513,17 +591,18 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_palette<R: Renderer>(&self, frame: &mut Frame<R>) {
|
||||
for y in 0..8 {
|
||||
for x in 0..4 {
|
||||
frame.fill_rectangle(
|
||||
Point::new(0., 0.),
|
||||
Point::new(x as f32 * 10., y as f32 * 10.),
|
||||
Size::new(10., 10.),
|
||||
Color {
|
||||
r: todo!(),
|
||||
g: todo!(),
|
||||
b: todo!(),
|
||||
},
|
||||
self.palette.colors[(self.palette.ram[x + y * 4] & 0x3F) as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
340
src/resize_watcher.rs
Normal file
340
src/resize_watcher.rs
Normal file
@@ -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<Element<'a, Message, Theme, Renderer>>,
|
||||
) -> 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<Box<dyn ResizeFn<Message> + 'a>>,
|
||||
}
|
||||
|
||||
pub trait ResizeFn<Message> {
|
||||
fn resized(&self, size: Size) -> Message;
|
||||
}
|
||||
impl<F: Fn(Size) -> M, M> ResizeFn<M> 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<Element<'a, Message, Theme, Renderer>>) -> Self {
|
||||
Self {
|
||||
c: widget::container(content),
|
||||
on_resize: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_resize(mut self, f: impl ResizeFn<Message> + '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<widget::Id>) -> Self {
|
||||
self.c = self.c.id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Padding`] of the [`Container`].
|
||||
pub fn padding<P: Into<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<Length>) -> 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<Length>) -> 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<Pixels>) -> 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<Pixels>) -> 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<Length>) -> 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<Length>) -> 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<Length>) -> 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<Length>) -> 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<Length>) -> 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<Length>) -> 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<Length>) -> 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<alignment::Horizontal>) -> 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<alignment::Vertical>) -> 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<StyleFn<'a, Theme>>,
|
||||
{
|
||||
self.c = self.c.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style class of the [`Container`].
|
||||
#[must_use]
|
||||
pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
|
||||
self.c = self.c.class(class);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
last_size: Size,
|
||||
cur_size: Size,
|
||||
}
|
||||
|
||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||
for ResizeWatcher<'_, Message, Theme, Renderer>
|
||||
where
|
||||
Theme: Catalog,
|
||||
Renderer: iced::advanced::Renderer,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
}
|
||||
|
||||
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<Tree> {
|
||||
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<Length> {
|
||||
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<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||
self.c.overlay(
|
||||
&mut tree.children[0],
|
||||
layout.child(0),
|
||||
renderer,
|
||||
viewport,
|
||||
translation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> From<ResizeWatcher<'a, Message, Theme, Renderer>>
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user