Add menu for loading a ROM file
This commit is contained in:
parent
7599fea8c9
commit
cb0e7b0381
153
Cargo.lock
generated
153
Cargo.lock
generated
@ -14,6 +14,12 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
@ -38,6 +44,48 @@ dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialog"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "736bab36d647d14c985725a57a4110a1182c6852104536cd42f1c97e96d29bf0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"rpassword",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.1",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@ -50,6 +98,16 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@ -62,6 +120,16 @@ version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
@ -113,6 +181,28 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
@ -137,7 +227,7 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.1",
|
||||
"libc",
|
||||
"version-compare",
|
||||
]
|
||||
@ -216,6 +306,26 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
@ -254,6 +364,7 @@ name = "ui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"dialog",
|
||||
"sdl2",
|
||||
"simplelog",
|
||||
"uitlk",
|
||||
@ -278,6 +389,40 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
@ -287,6 +432,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
@ -29,6 +29,11 @@ impl CpuMemoryBus {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.ram = [0; RAM_SIZE];
|
||||
self.mapper = None;
|
||||
}
|
||||
|
||||
/// Register a mapper in the memory bus, simulates plugging in a cartridge.
|
||||
pub fn register_mapper(&mut self, mapper: Box<dyn Mapper>) {
|
||||
self.mapper = Some(mapper);
|
||||
|
@ -155,6 +155,19 @@ impl Cpu {
|
||||
nmi_requested: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.registers.pc = 0x8000;
|
||||
self.registers.sp = 0xFD;
|
||||
self.registers.a = 0;
|
||||
self.registers.x = 0;
|
||||
self.registers.y = 0;
|
||||
self.registers.status = 0x04;
|
||||
self.cycle = 0;
|
||||
self.busy_cycle_count = 0;
|
||||
self.oam_dma_triggered = false;
|
||||
self.nmi_requested = false;
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self) {
|
||||
let init_pc = self.memory_bus.borrow().get_word(INITIAL_PC_ADDRESS);
|
||||
|
@ -31,6 +31,13 @@ impl PpuMemoryBus {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.mapper = None;
|
||||
self.nametable_0 = [0u8; NAMETABLE_SIZE];
|
||||
self.nametable_1 = [0u8; NAMETABLE_SIZE];
|
||||
self.palette = [0u8; PALETTE_TABLE_SIZE];
|
||||
}
|
||||
|
||||
fn read_nametable(&self, addr: u16) -> u8 {
|
||||
assert!(addr >= 0x2000);
|
||||
assert!(addr < 0x3000);
|
||||
|
@ -41,6 +41,24 @@ impl Ppu {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.registers.control = 0;
|
||||
self.registers.mask = 0;
|
||||
self.registers.status = 0;
|
||||
self.registers.oam_addr = 0;
|
||||
self.registers.oam_data = 0;
|
||||
self.registers.scroll = 0;
|
||||
self.registers.addr = 0;
|
||||
self.registers.data = 0;
|
||||
self.registers.oam_dma = 0;
|
||||
self.internal_registers.v = 0;
|
||||
self.internal_registers.t = 0;
|
||||
self.internal_registers.x = 0;
|
||||
self.internal_registers.w = false;
|
||||
|
||||
self.memory_bus.reset();
|
||||
}
|
||||
|
||||
pub fn get_background_pattern_table_addr(&self) -> u16 {
|
||||
let control = self.registers.control & PpuControl::BackgroundPatternTableAddress.bits();
|
||||
(control as u16) << 8
|
||||
|
@ -80,7 +80,7 @@ pub trait RomLoader {
|
||||
}
|
||||
|
||||
impl Rom {
|
||||
pub fn read(path: &str) -> Result<Rom, RomReadError> {
|
||||
pub fn read(path: String) -> Result<Rom, RomReadError> {
|
||||
info!("ROM - Reading file from {}", path);
|
||||
|
||||
let content = match fs::read(path) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::cpu::memory::CpuMemoryBus;
|
||||
use crate::cpu::Cpu;
|
||||
use crate::mappers::get_mapper;
|
||||
use crate::ppu::Ppu;
|
||||
use crate::rom::{Rom, RomReadError};
|
||||
use crate::{Clock, Observable};
|
||||
use simplelog::info;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::cpu::Cpu;
|
||||
use crate::mappers::get_mapper;
|
||||
use crate::cpu::memory::CpuMemoryBus;
|
||||
use crate::rom::{Rom, RomReadError};
|
||||
use crate::{Clock, Observable};
|
||||
use crate::ppu::Ppu;
|
||||
|
||||
const CPU_CLOCK_DIVISOR: usize = 12;
|
||||
const CPU_CYCLE_PER_REFRESH: usize = MASTER_CYCLE_PER_REFRESH / CPU_CLOCK_DIVISOR;
|
||||
@ -19,7 +19,8 @@ const REFRESH_RATE: usize = 60; // 60 Hz
|
||||
pub struct System {
|
||||
cpu: Cpu,
|
||||
memory_bus: Rc<RefCell<CpuMemoryBus>>,
|
||||
ppu: Rc<RefCell<Ppu>>
|
||||
ppu: Rc<RefCell<Ppu>>,
|
||||
initialized: bool
|
||||
}
|
||||
|
||||
impl System {
|
||||
@ -27,19 +28,32 @@ impl System {
|
||||
let memory_bus = Rc::new(RefCell::new(CpuMemoryBus::new()));
|
||||
let cpu = Cpu::new(Rc::clone(&memory_bus));
|
||||
let ppu = Rc::new(RefCell::new(Ppu::new()));
|
||||
|
||||
|
||||
let weak_ppu = Rc::downgrade(&ppu);
|
||||
memory_bus.borrow_mut().register_observer(weak_ppu);
|
||||
|
||||
Self { cpu, memory_bus, ppu }
|
||||
|
||||
Self {
|
||||
cpu,
|
||||
memory_bus,
|
||||
ppu,
|
||||
initialized: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_rom(&mut self, path: &str) -> Result<(), RomReadError> {
|
||||
pub fn reset(&mut self) {
|
||||
self.cpu.reset();
|
||||
self.memory_bus.borrow_mut().reset();
|
||||
self.ppu.borrow_mut().reset();
|
||||
self.initialized = false;
|
||||
}
|
||||
|
||||
pub fn insert_rom(&mut self, path: String) -> Result<(), RomReadError> {
|
||||
let rom = Rom::read(path)?;
|
||||
let mapper = get_mapper(rom);
|
||||
|
||||
self.memory_bus.borrow_mut().register_mapper(mapper);
|
||||
self.cpu.initialize();
|
||||
self.initialized = true;
|
||||
|
||||
info!("Successfully inserted ROM into system");
|
||||
Ok(())
|
||||
@ -48,6 +62,10 @@ impl System {
|
||||
|
||||
impl Clock for System {
|
||||
fn cycle(&mut self) {
|
||||
if !self.initialized {
|
||||
return;
|
||||
}
|
||||
|
||||
for _cpu_cycle in 0usize..CPU_CYCLE_PER_REFRESH {
|
||||
self.cpu.cycle();
|
||||
|
||||
|
BIN
nintendo-nes-font.ttf
Normal file
BIN
nintendo-nes-font.ttf
Normal file
Binary file not shown.
@ -8,5 +8,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
uitlk = { path = "../uitlk" }
|
||||
sdl2 = "0.37.0"
|
||||
dialog = "0.3.0"
|
||||
sdl2 = { version = "0.37.0", features = ["ttf"] }
|
||||
simplelog = { version = "^0.12.0", features = ["paris"] }
|
||||
|
162
ui/src/components/menu.rs
Normal file
162
ui/src/components/menu.rs
Normal file
@ -0,0 +1,162 @@
|
||||
use crate::components::{ButtonComponent, MouseListener, Text, UIComponent};
|
||||
use crate::WINDOW_SCALE;
|
||||
use core::system::System;
|
||||
use dialog::{DialogBox, FileSelectionMode};
|
||||
use sdl2::pixels::PixelFormatEnum;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
|
||||
use sdl2::ttf::Font;
|
||||
use sdl2::video::WindowContext;
|
||||
use simplelog::warn;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
const BAR_WIDTH: u32 = 256 * WINDOW_SCALE;
|
||||
const BAR_HEIGHT: u32 = 8 * WINDOW_SCALE;
|
||||
const MENU_MARGIN: u32 = 2 * WINDOW_SCALE;
|
||||
|
||||
const MENU_PIXEL_FORMAT: PixelFormatEnum = PixelFormatEnum::RGB24;
|
||||
const BAR_COLOR: [u8; 3] = [0x35, 0x35, 0x35];
|
||||
const HIGHLIGHT_COLOR: [u8; 3] = [0x4d, 0x4d, 0x4d];
|
||||
|
||||
pub struct MenuBar<'a> {
|
||||
bar_texture: Texture<'a>,
|
||||
load_rom_menu: Menu<'a>,
|
||||
system: Rc<RefCell<System>>,
|
||||
}
|
||||
|
||||
impl MenuBar<'_> {
|
||||
pub fn new<'a>(
|
||||
texture_creator: &'a TextureCreator<WindowContext>,
|
||||
font: &Font,
|
||||
system: Rc<RefCell<System>>,
|
||||
) -> Result<MenuBar<'a>, String> {
|
||||
let mut bar_texture = texture_creator
|
||||
.create_texture_static(MENU_PIXEL_FORMAT, 1, 1)
|
||||
.map_err(|e| e.to_string())?;
|
||||
bar_texture
|
||||
.update(None, &BAR_COLOR, BAR_COLOR.len())
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let load_rom_menu = Menu::new("LOAD ROM", font, texture_creator)?;
|
||||
|
||||
Ok(MenuBar {
|
||||
bar_texture,
|
||||
load_rom_menu,
|
||||
system,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, canvas: &mut WindowCanvas) -> Result<(), String> {
|
||||
self.draw_bar(canvas)?;
|
||||
self.load_rom_menu.render(0, canvas)
|
||||
}
|
||||
|
||||
fn draw_bar(&mut self, canvas: &mut WindowCanvas) -> Result<(), String> {
|
||||
let rect = Rect::new(0, 0, BAR_WIDTH, BAR_HEIGHT);
|
||||
canvas.copy(&self.bar_texture, None, rect)
|
||||
}
|
||||
|
||||
fn select_rom(&self) {
|
||||
let mut system = self.system.borrow_mut();
|
||||
system.reset();
|
||||
|
||||
let choice = dialog::FileSelection::new("Load ROM")
|
||||
.mode(FileSelectionMode::Open)
|
||||
.show()
|
||||
.expect("Could not display file selection");
|
||||
|
||||
match choice {
|
||||
None => {}
|
||||
Some(file_path) => {
|
||||
let insertion_result = system.insert_rom(file_path);
|
||||
if insertion_result.is_err() {
|
||||
warn!("Failed to insert ROM: {}", insertion_result.err().unwrap());
|
||||
dialog::Message::new("Failed to insert ROM")
|
||||
.show()
|
||||
.expect("Could not show message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseListener for MenuBar<'_> {
|
||||
fn mouse_move(&mut self, x: i32, y: i32) {
|
||||
if y > BAR_HEIGHT as i32 {
|
||||
self.load_rom_menu.set_hover(false);
|
||||
}
|
||||
|
||||
let hover = self.load_rom_menu.rect.contains_point((x, y));
|
||||
self.load_rom_menu.set_hover(hover);
|
||||
}
|
||||
|
||||
fn mouse_down(&mut self) {
|
||||
if self.load_rom_menu.hover() {
|
||||
self.select_rom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Menu<'a> {
|
||||
highlight_texture: Texture<'a>,
|
||||
hover: bool,
|
||||
pub rect: Rect,
|
||||
text: Text<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Menu<'a> {
|
||||
pub fn new(
|
||||
label: &'static str,
|
||||
font: &Font,
|
||||
texture_creator: &'a TextureCreator<WindowContext>,
|
||||
) -> Result<Self, String> {
|
||||
let text = Text::new(label, font, texture_creator)?;
|
||||
let rect = Rect::new(0, 0, text.width() + MENU_MARGIN * 2, BAR_HEIGHT);
|
||||
|
||||
let mut highlight_texture = texture_creator
|
||||
.create_texture_static(MENU_PIXEL_FORMAT, 1, 1)
|
||||
.map_err(|e| e.to_string())?;
|
||||
highlight_texture
|
||||
.update(None, &HIGHLIGHT_COLOR, HIGHLIGHT_COLOR.len())
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(Menu {
|
||||
highlight_texture,
|
||||
hover: false,
|
||||
rect,
|
||||
text,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render(&mut self, x: u32, canvas: &mut WindowCanvas) -> Result<(), String> {
|
||||
self.rect.set_x(x as i32);
|
||||
|
||||
if self.hover {
|
||||
// Only draw the highlight texture if the menu is hovered
|
||||
canvas.copy(&self.highlight_texture, None, self.rect)?;
|
||||
}
|
||||
|
||||
self.text.render_center(self.rect, canvas)
|
||||
}
|
||||
}
|
||||
|
||||
impl UIComponent for Menu<'_> {
|
||||
fn width(&self) -> u32 {
|
||||
self.rect.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.rect.height()
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonComponent for Menu<'_> {
|
||||
fn hover(&self) -> bool {
|
||||
self.hover
|
||||
}
|
||||
|
||||
fn set_hover(&mut self, hover: bool) {
|
||||
self.hover = hover;
|
||||
}
|
||||
}
|
92
ui/src/components/mod.rs
Normal file
92
ui/src/components/mod.rs
Normal file
@ -0,0 +1,92 @@
|
||||
pub mod menu;
|
||||
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
|
||||
use sdl2::ttf::Font;
|
||||
use sdl2::video::WindowContext;
|
||||
|
||||
const TEXT_COLOR: Color = Color::WHITE;
|
||||
|
||||
pub trait UIComponent {
|
||||
/// The width of the component in pixels
|
||||
fn width(&self) -> u32;
|
||||
|
||||
/// The height of the component in pixels
|
||||
fn height(&self) -> u32;
|
||||
}
|
||||
|
||||
pub trait ButtonComponent {
|
||||
/// If the component is hovered
|
||||
fn hover(&self) -> bool;
|
||||
|
||||
/// Set the hover status of the component
|
||||
fn set_hover(&mut self, hover: bool);
|
||||
}
|
||||
|
||||
pub trait MouseListener {
|
||||
/// Called when the mouse moves in the window.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `x`: The horizontal position of the mouse (left to right)
|
||||
/// - `y`: The vertical position of the mouse (top to bottom)
|
||||
fn mouse_move(&mut self, x: i32, y: i32);
|
||||
|
||||
/// Called when the left button of the mouse is clicked.
|
||||
fn mouse_down(&mut self);
|
||||
}
|
||||
|
||||
pub struct Text<'a> {
|
||||
rect: Rect,
|
||||
texture: Texture<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Text<'a> {
|
||||
/// Creates a new text
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `str` - The string to be contained by the text
|
||||
/// * `font` - A reference to the font to use
|
||||
/// * `texture_creator` - A reference to a texture creator
|
||||
pub fn new(
|
||||
str: &'static str,
|
||||
font: &Font,
|
||||
texture_creator: &'a TextureCreator<WindowContext>,
|
||||
) -> Result<Self, String> {
|
||||
let surface = font
|
||||
.render(str)
|
||||
.solid(TEXT_COLOR)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let texture = surface
|
||||
.as_texture(texture_creator)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let rect = Rect::new(0, 0, surface.width(), surface.height());
|
||||
Ok(Text { rect, texture })
|
||||
}
|
||||
|
||||
/// Renders the text at center of a rectangle
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `destination` - The destination rectangle to draw the text to
|
||||
/// * `canvas` - The canvas on which the text will be rendered
|
||||
pub fn render_center(
|
||||
&self,
|
||||
destination: Rect,
|
||||
canvas: &mut WindowCanvas,
|
||||
) -> Result<(), String> {
|
||||
let centered_rect = self.rect.centered_on(destination.center());
|
||||
canvas.copy(&self.texture, None, centered_rect)
|
||||
}
|
||||
|
||||
/// The width of the text in pixels
|
||||
pub fn width(&self) -> u32 {
|
||||
self.rect.width()
|
||||
}
|
||||
|
||||
/// The heights of the text in pixels
|
||||
pub fn height(&self) -> u32 {
|
||||
self.rect.height()
|
||||
}
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
extern crate sdl2;
|
||||
mod renderer;
|
||||
mod components;
|
||||
|
||||
use crate::renderer::Renderer;
|
||||
use crate::components::menu::MenuBar;
|
||||
use crate::components::MouseListener;
|
||||
use core::system::System;
|
||||
use core::Clock;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::rect::Point;
|
||||
use sdl2::mouse::MouseButton;
|
||||
use sdl2::pixels::Color;
|
||||
use simplelog::*;
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
const ROM_PATH: &'static str = "./roms/dk.nes";
|
||||
@ -32,7 +38,12 @@ fn main() -> Result<(), String> {
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let system = Rc::new(RefCell::new(System::new()));
|
||||
|
||||
let sdl_context = sdl2::init()?;
|
||||
let ttf_context = sdl2::ttf::init().map_err(|e| e.to_string())?;
|
||||
let font = ttf_context.load_font("./nintendo-nes-font.ttf", 16)?;
|
||||
|
||||
let video_subsystem = sdl_context.video()?;
|
||||
|
||||
let window = video_subsystem
|
||||
@ -42,38 +53,40 @@ fn main() -> Result<(), String> {
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut renderer = Renderer::new(window, WINDOW_SCALE)?;
|
||||
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||
let texture_creator = canvas.texture_creator();
|
||||
|
||||
let mut menu = MenuBar::new(&texture_creator, &font, Rc::clone(&system))?;
|
||||
|
||||
let mut event_pump = sdl_context.event_pump()?;
|
||||
'running: loop {
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::Quit { .. } => break 'running,
|
||||
Event::MouseMotion { x, y, .. } => {
|
||||
menu.mouse_move(x, y);
|
||||
}
|
||||
Event::MouseButtonUp {
|
||||
mouse_btn: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
menu.mouse_down();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
system.borrow_mut().cycle();
|
||||
|
||||
canvas.set_draw_color(Color::BLACK);
|
||||
canvas.clear();
|
||||
|
||||
menu.draw(&mut canvas)?;
|
||||
|
||||
canvas.present();
|
||||
|
||||
renderer.draw(&Point::new(1, 1))?;
|
||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
|
||||
// Main loop
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
// let mut window = UIWindow::new("NesRust", 800, 600, 60);
|
||||
//
|
||||
// // let mut system = System::new();
|
||||
// // system.insert_rom(ROM_PATH).expect("Failed to insert ROM");
|
||||
// loop {
|
||||
// // system.cycle();
|
||||
// if window.poll_events() {
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// // TODO: System loop
|
||||
//
|
||||
// window.render();
|
||||
// window.wait();
|
||||
// }
|
||||
}
|
||||
|
@ -1,33 +0,0 @@
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::{Point, Rect};
|
||||
use sdl2::render::WindowCanvas;
|
||||
use sdl2::video::Window;
|
||||
|
||||
pub struct Renderer {
|
||||
canvas: WindowCanvas,
|
||||
scale: u32,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(window: Window, scale: u32) -> Result<Renderer, String> {
|
||||
let canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||
Ok(Renderer { canvas, scale })
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, point: &Point) -> Result<(), String> {
|
||||
self.canvas.set_draw_color(Color::BLACK);
|
||||
self.canvas.clear();
|
||||
|
||||
self.canvas.set_draw_color(Color::GREEN);
|
||||
self.canvas.fill_rect(Rect::new(
|
||||
point.x() * self.scale as i32,
|
||||
point.y() * self.scale as i32,
|
||||
self.scale,
|
||||
self.scale,
|
||||
))?;
|
||||
|
||||
self.canvas.present();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user