From 1f85132b0151b5a39295cd1db03d4259012cb12b Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Mon, 14 Apr 2025 22:28:44 +0200 Subject: [PATCH 1/6] Implement config module --- Cargo.toml | 3 +++ src/config/consts.rs | 2 ++ src/config/mod.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/ui/state.rs | 12 ++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 src/config/consts.rs create mode 100644 src/config/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f5a428b..57129c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,6 @@ rfd = "0.15.0" pollster = "0.4.0" async-mutex = "1.4.0" clap = { version = "4.5.21", features = ["derive"] } +serde = { version = "1.0.218", features = ["derive"] } +toml = "0.8.20" +dirs = "6.0.0" diff --git a/src/config/consts.rs b/src/config/consts.rs new file mode 100644 index 0000000..05aa4df --- /dev/null +++ b/src/config/consts.rs @@ -0,0 +1,2 @@ +pub const CONFIG_PATH: &str = ".hachi"; +pub const CONFIG_FILE: &str = "config.toml"; diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..adf6866 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,46 @@ +mod consts; + +use std::fs; +use std::path::{Path, PathBuf}; +use serde::{Deserialize, Serialize}; +use crate::config::consts::{CONFIG_FILE, CONFIG_PATH}; + +#[derive(Deserialize, Serialize)] +pub struct Configuration { + pub cycle_per_frame: u32, + pub default_color: String +} + +impl Configuration { + pub fn new() -> Self { + Self { + cycle_per_frame: 10, + default_color: String::from("#0D822C") + } + } + + fn get_file_path() -> PathBuf { + dirs::home_dir().unwrap() + .join(CONFIG_PATH) + .join(CONFIG_FILE) + } + + pub fn read() -> Result { + let file_path = Self::get_file_path(); + let file_contents = match fs::read_to_string(file_path) { + Ok(c) => c, + Err(e) => { + return Err(format!("Error reading config file: {}", e)); + } + }; + + toml::from_str(&file_contents).unwrap_or_else(|e| Err(format!("Error parsing config file: {}", e))) + } + + pub fn update(&self) { + let config_path = Self::get_file_path(); + let config_to_str = toml::to_string(self).unwrap(); + + fs::write(config_path, config_to_str).unwrap(); + } +} diff --git a/src/main.rs b/src/main.rs index 812257d..c5c3b8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod ui; mod vm; +mod config; use notan::draw::DrawConfig; use notan::egui::EguiConfig; diff --git a/src/ui/state.rs b/src/ui/state.rs index aa58af9..e2696f5 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -8,10 +8,12 @@ use std::collections::HashMap; use std::ffi::OsString; use std::path::PathBuf; use std::sync::Arc; +use crate::config::Configuration; #[derive(AppState)] pub struct State { pub last_dir: PathBuf, + pub configuration: Configuration, pub file_path_option: Arc>>, pub cycles_per_frame: u32, pub memory_debug_page: usize, @@ -32,11 +34,13 @@ struct Args { pub fn setup(gfx: &mut Graphics) -> State { let args = Args::parse(); + let file_path = if args.file.is_empty() { None } else { Some(PathBuf::from(args.file).canonicalize().unwrap()) }; + let last_dir = if let Some(path) = &file_path { path.parent().unwrap().to_path_buf() } else { @@ -75,8 +79,16 @@ pub fn setup(gfx: &mut Graphics) -> State { (KeyCode::V, 0xF), ]; + let config = Configuration::read().unwrap_or_else(|_| { + let c = Configuration::new(); + c.update(); + + c + }); + State { last_dir, + configuration: config, file_path_option: Arc::new(AsyncMutex::new(file_path)), cycles_per_frame: 10, memory_debug_page: 0, From a6064218f1db5543d7d1561a6c95fdc015b1a65e Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Sat, 19 Apr 2025 11:33:06 +0200 Subject: [PATCH 2/6] Redesign configuration layer and implement options window --- src/config/errors.rs | 18 +++++++++++ src/config/mod.rs | 71 ++++++++++++++++++++++++++++++++----------- src/ui/egui_plugin.rs | 15 ++++++++- src/ui/mod.rs | 1 + src/ui/options.rs | 39 ++++++++++++++++++++++++ src/ui/state.rs | 13 +++----- 6 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 src/config/errors.rs create mode 100644 src/ui/options.rs diff --git a/src/config/errors.rs b/src/config/errors.rs new file mode 100644 index 0000000..b96ec6f --- /dev/null +++ b/src/config/errors.rs @@ -0,0 +1,18 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum ConfigError { + InvalidFileFormat, + NotReadable, + NotWritable, + CannotCreateDirectory +} + +impl Display for ConfigError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Error for ConfigError {} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs index adf6866..5c23f30 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,46 +1,83 @@ mod consts; +mod errors; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use serde::{Deserialize, Serialize}; use crate::config::consts::{CONFIG_FILE, CONFIG_PATH}; +use crate::config::errors::ConfigError; #[derive(Deserialize, Serialize)] pub struct Configuration { - pub cycle_per_frame: u32, - pub default_color: String + pub vm: VmOptions, + pub debug: DebugOptions, } -impl Configuration { - pub fn new() -> Self { +#[derive(Deserialize, Serialize)] +pub struct VmOptions { + pub cycles_per_frame: u32, +} + +#[derive(Deserialize, Serialize)] +pub struct DebugOptions { + pub enable_debug_menu: bool, +} + +impl Default for Configuration { + fn default() -> Self { Self { - cycle_per_frame: 10, - default_color: String::from("#0D822C") + vm: VmOptions { + cycles_per_frame: 10, + }, + debug: DebugOptions { + enable_debug_menu: false, + } } } +} +impl Configuration { fn get_file_path() -> PathBuf { dirs::home_dir().unwrap() .join(CONFIG_PATH) .join(CONFIG_FILE) } - pub fn read() -> Result { + pub fn load() -> Result { let file_path = Self::get_file_path(); - let file_contents = match fs::read_to_string(file_path) { - Ok(c) => c, - Err(e) => { - return Err(format!("Error reading config file: {}", e)); - } - }; - toml::from_str(&file_contents).unwrap_or_else(|e| Err(format!("Error parsing config file: {}", e))) + if !file_path.exists() { + let config = Configuration::default(); + config.update()?; + + return Ok(config); + } + + Ok(Self::read_from_file(&file_path)?) + } + + fn read_from_file(path: &PathBuf) -> Result { + let file_contents = fs::read_to_string(path) + .map_err(|_| ConfigError::NotReadable)?; + + match toml::from_str(&file_contents) { + Ok(config) => Ok(config), + Err(_) => Err(ConfigError::InvalidFileFormat), + } } - pub fn update(&self) { + pub fn update(&self) -> Result<(), ConfigError> { let config_path = Self::get_file_path(); + + if !config_path.exists() { + fs::create_dir_all(config_path.parent().unwrap()) + .map_err(|_| ConfigError::CannotCreateDirectory)?; + } + let config_to_str = toml::to_string(self).unwrap(); + fs::write(config_path, config_to_str) + .map_err(|_| ConfigError::NotWritable)?; - fs::write(config_path, config_to_str).unwrap(); + Ok(()) } } diff --git a/src/ui/egui_plugin.rs b/src/ui/egui_plugin.rs index d298913..1434bbb 100644 --- a/src/ui/egui_plugin.rs +++ b/src/ui/egui_plugin.rs @@ -8,6 +8,7 @@ use pollster::FutureExt; use rfd::AsyncFileDialog; use std::sync::Arc; use std::thread; +use crate::ui::options::option_dialog; pub fn init<'a>( _app: &'a mut App, @@ -21,6 +22,10 @@ pub fn init<'a>( }); }); + if state.show_configuration_window { + option_dialog(state, ctx); + } + CentralPanel::default().show(ctx, |ui| { if let Err(e) = state.vm.last_cycle_result.clone() { state.vm.pause(); @@ -122,11 +127,19 @@ fn file_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { }); } } + + if ui.button("Options").clicked() { + ui.close_menu(); + + state.show_configuration_window = true; + } } } fn view_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { |ui| { - ui.checkbox(&mut state.debug_mode_enabled, "Debug mode"); + ui.add_enabled_ui(state.configuration.debug.enable_debug_menu, |ui| { + ui.checkbox(&mut state.debug_mode_enabled, "Debug mode"); + }); } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index ddb8247..8b6f736 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,6 @@ mod consts; mod debug; +mod options; pub(super) mod egui_plugin; pub mod state; diff --git a/src/ui/options.rs b/src/ui/options.rs new file mode 100644 index 0000000..79c567b --- /dev/null +++ b/src/ui/options.rs @@ -0,0 +1,39 @@ +use notan::egui; +use notan::egui::Window; +use crate::ui::state::State; + +pub fn option_dialog( + state: &mut State, + ctx: &egui::Context, +) { + Window::new("Options") + .max_height(100.0) + .max_width(200.0) + .resizable(false) + .resizable(true) + .show(ctx,|ui| { + let mut cycles_per_frame_raw_text = state.configuration.vm.cycles_per_frame.to_string(); + + ui.horizontal(|ui| { + ui.label("Cycles per frame"); + if ui.text_edit_singleline(&mut cycles_per_frame_raw_text).changed() { + if let Ok(value) = cycles_per_frame_raw_text.parse::() { + state.configuration.vm.cycles_per_frame = value; + } + } + }); + + ui.horizontal(|ui| { + ui.checkbox(&mut state.configuration.debug.enable_debug_menu, "Debug mode"); + }); + + ui.add_space(20.0); + + ui.centered_and_justified(|ui| { + if ui.button("Close").clicked() { + state.configuration.update().unwrap(); + state.show_configuration_window = false; + } + }); + }); +} \ No newline at end of file diff --git a/src/ui/state.rs b/src/ui/state.rs index e2696f5..6504c4c 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -15,7 +15,6 @@ pub struct State { pub last_dir: PathBuf, pub configuration: Configuration, pub file_path_option: Arc>>, - pub cycles_per_frame: u32, pub memory_debug_page: usize, pub memory_address_search: String, pub vm: VirtualMachine, @@ -24,6 +23,7 @@ pub struct State { pub vm_display: SizedTexture, pub keypad_bindigs: HashMap, pub timer: f32, + pub show_configuration_window: bool } #[derive(Parser)] @@ -79,18 +79,12 @@ pub fn setup(gfx: &mut Graphics) -> State { (KeyCode::V, 0xF), ]; - let config = Configuration::read().unwrap_or_else(|_| { - let c = Configuration::new(); - c.update(); - - c - }); + let config = Configuration::load().unwrap(); State { last_dir, configuration: config, file_path_option: Arc::new(AsyncMutex::new(file_path)), - cycles_per_frame: 10, memory_debug_page: 0, memory_address_search: String::new(), vm: VirtualMachine::new(), @@ -99,6 +93,7 @@ pub fn setup(gfx: &mut Graphics) -> State { vm_display: vm_display_texture, keypad_bindigs: default_bindings.into(), timer: 0.0, + show_configuration_window: false } } @@ -151,7 +146,7 @@ pub fn update(app: &mut App, state: &mut State) { } if state.vm.is_running { - for _ in 0..state.cycles_per_frame { + for _ in 0..state.configuration.vm.cycles_per_frame { state.vm.fde_cycle(); } } From e558ea06fb03c1743ef695ec97ca70daac10e70a Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Sat, 19 Apr 2025 11:35:13 +0200 Subject: [PATCH 3/6] Reformat code --- src/config/errors.rs | 4 ++-- src/config/mod.rs | 26 +++++++++++--------------- src/main.rs | 2 +- src/ui/egui_plugin.rs | 2 +- src/ui/mod.rs | 2 +- src/ui/options.rs | 27 ++++++++++++++++----------- src/ui/state.rs | 6 +++--- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/config/errors.rs b/src/config/errors.rs index b96ec6f..97fd351 100644 --- a/src/config/errors.rs +++ b/src/config/errors.rs @@ -6,7 +6,7 @@ pub enum ConfigError { InvalidFileFormat, NotReadable, NotWritable, - CannotCreateDirectory + CannotCreateDirectory, } impl Display for ConfigError { @@ -15,4 +15,4 @@ impl Display for ConfigError { } } -impl Error for ConfigError {} \ No newline at end of file +impl Error for ConfigError {} diff --git a/src/config/mod.rs b/src/config/mod.rs index 5c23f30..5a70e7c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,11 +1,11 @@ mod consts; mod errors; -use std::fs; -use std::path::PathBuf; -use serde::{Deserialize, Serialize}; use crate::config::consts::{CONFIG_FILE, CONFIG_PATH}; use crate::config::errors::ConfigError; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; #[derive(Deserialize, Serialize)] pub struct Configuration { @@ -26,21 +26,15 @@ pub struct DebugOptions { impl Default for Configuration { fn default() -> Self { Self { - vm: VmOptions { - cycles_per_frame: 10, - }, - debug: DebugOptions { - enable_debug_menu: false, - } + vm: VmOptions { cycles_per_frame: 10 }, + debug: DebugOptions { enable_debug_menu: false }, } } } impl Configuration { fn get_file_path() -> PathBuf { - dirs::home_dir().unwrap() - .join(CONFIG_PATH) - .join(CONFIG_FILE) + dirs::home_dir().unwrap().join(CONFIG_PATH).join(CONFIG_FILE) } pub fn load() -> Result { @@ -53,12 +47,14 @@ impl Configuration { return Ok(config); } - Ok(Self::read_from_file(&file_path)?) + let configuration = Self::read_from_file(&file_path)?; + + Ok(configuration) } fn read_from_file(path: &PathBuf) -> Result { - let file_contents = fs::read_to_string(path) - .map_err(|_| ConfigError::NotReadable)?; + let file_contents = + fs::read_to_string(path).map_err(|_| ConfigError::NotReadable)?; match toml::from_str(&file_contents) { Ok(config) => Ok(config), diff --git a/src/main.rs b/src/main.rs index c5c3b8e..beef8d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ +mod config; mod ui; mod vm; -mod config; use notan::draw::DrawConfig; use notan::egui::EguiConfig; diff --git a/src/ui/egui_plugin.rs b/src/ui/egui_plugin.rs index 1434bbb..699cdbe 100644 --- a/src/ui/egui_plugin.rs +++ b/src/ui/egui_plugin.rs @@ -1,5 +1,6 @@ use super::state::State; use crate::ui::debug::{display_memory_contents, vm_display}; +use crate::ui::options::option_dialog; use notan::app::App; use notan::egui::{ self, CentralPanel, Context, Grid, Id, Modal, SidePanel, TopBottomPanel, Ui, @@ -8,7 +9,6 @@ use pollster::FutureExt; use rfd::AsyncFileDialog; use std::sync::Arc; use std::thread; -use crate::ui::options::option_dialog; pub fn init<'a>( _app: &'a mut App, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8b6f736..bd42ff7 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,7 +1,7 @@ mod consts; mod debug; -mod options; pub(super) mod egui_plugin; +mod options; pub mod state; use crate::vm::consts::DISPLAY_WIDTH; diff --git a/src/ui/options.rs b/src/ui/options.rs index 79c567b..0368475 100644 --- a/src/ui/options.rs +++ b/src/ui/options.rs @@ -1,30 +1,35 @@ +use crate::ui::state::State; use notan::egui; use notan::egui::Window; -use crate::ui::state::State; -pub fn option_dialog( - state: &mut State, - ctx: &egui::Context, -) { +pub fn option_dialog(state: &mut State, ctx: &egui::Context) { Window::new("Options") .max_height(100.0) .max_width(200.0) .resizable(false) .resizable(true) - .show(ctx,|ui| { - let mut cycles_per_frame_raw_text = state.configuration.vm.cycles_per_frame.to_string(); + .show(ctx, |ui| { + let mut cycles_per_frame_raw_text = + state.configuration.vm.cycles_per_frame.to_string(); ui.horizontal(|ui| { ui.label("Cycles per frame"); - if ui.text_edit_singleline(&mut cycles_per_frame_raw_text).changed() { - if let Ok(value) = cycles_per_frame_raw_text.parse::() { + if ui + .text_edit_singleline(&mut cycles_per_frame_raw_text) + .changed() + { + if let Ok(value) = cycles_per_frame_raw_text.parse::() + { state.configuration.vm.cycles_per_frame = value; } } }); ui.horizontal(|ui| { - ui.checkbox(&mut state.configuration.debug.enable_debug_menu, "Debug mode"); + ui.checkbox( + &mut state.configuration.debug.enable_debug_menu, + "Debug mode", + ); }); ui.add_space(20.0); @@ -36,4 +41,4 @@ pub fn option_dialog( } }); }); -} \ No newline at end of file +} diff --git a/src/ui/state.rs b/src/ui/state.rs index 6504c4c..d93d043 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -1,3 +1,4 @@ +use crate::config::Configuration; use crate::vm::consts::{DEBUG_DISPLAY_HEIGHT, DEBUG_DISPLAY_WIDTH}; use crate::vm::VirtualMachine; use async_mutex::Mutex as AsyncMutex; @@ -8,7 +9,6 @@ use std::collections::HashMap; use std::ffi::OsString; use std::path::PathBuf; use std::sync::Arc; -use crate::config::Configuration; #[derive(AppState)] pub struct State { @@ -23,7 +23,7 @@ pub struct State { pub vm_display: SizedTexture, pub keypad_bindigs: HashMap, pub timer: f32, - pub show_configuration_window: bool + pub show_configuration_window: bool, } #[derive(Parser)] @@ -93,7 +93,7 @@ pub fn setup(gfx: &mut Graphics) -> State { vm_display: vm_display_texture, keypad_bindigs: default_bindings.into(), timer: 0.0, - show_configuration_window: false + show_configuration_window: false, } } From 0ec38bb6496bce14cb3a5925b3badc2fad094f9c Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Mon, 21 Apr 2025 22:42:19 +0200 Subject: [PATCH 4/6] Fix overlap options window to above vm display, apply configuration to current state after closing options window --- src/config/mod.rs | 12 ++++++------ src/ui/debug.rs | 8 ++------ src/ui/egui_plugin.rs | 42 +++++++++++++++++++++++++++++++++++++----- src/ui/mod.rs | 7 +------ src/ui/options.rs | 9 ++++++++- src/ui/state.rs | 12 +++++------- 6 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 5a70e7c..22ff475 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -7,13 +7,13 @@ use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; -#[derive(Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize)] pub struct Configuration { pub vm: VmOptions, pub debug: DebugOptions, } -#[derive(Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize)] pub struct VmOptions { pub cycles_per_frame: u32, } @@ -56,10 +56,10 @@ impl Configuration { let file_contents = fs::read_to_string(path).map_err(|_| ConfigError::NotReadable)?; - match toml::from_str(&file_contents) { - Ok(config) => Ok(config), - Err(_) => Err(ConfigError::InvalidFileFormat), - } + let configuration = toml::from_str(&file_contents) + .map_err(|_| ConfigError::InvalidFileFormat)?; + + Ok(configuration) } pub fn update(&self) -> Result<(), ConfigError> { diff --git a/src/ui/debug.rs b/src/ui/debug.rs index 3cb68fd..8d5a8ae 100644 --- a/src/ui/debug.rs +++ b/src/ui/debug.rs @@ -1,6 +1,5 @@ use crate::ui::consts::MEMORY_MAX_DISPLAY_ITEMS; use crate::ui::state::State; -use crate::vm::consts::{DEBUG_DISPLAY_HEIGHT, DEBUG_DISPLAY_WIDTH}; use notan::egui; use notan::egui::{ vec2, Align, Button, Color32, Frame, Grid, Layout, Mesh, Rect, ScrollArea, @@ -18,11 +17,10 @@ fn filter_by_memory_address(index: &usize, state: &State) -> bool { *index == search_address } -pub fn vm_display(state: &mut State, ui: &mut Ui) { +pub fn vm_display(state: &mut State, ui: &mut Ui, height: f32, width: f32) { Frame::canvas(ui.style()).show(ui, |ui| { let rect = ui.max_rect(); - let desired_size = - Vec2::new(DEBUG_DISPLAY_WIDTH as f32, DEBUG_DISPLAY_HEIGHT as f32); + let desired_size = Vec2::new(width, height); let mut vm_display_mesh = Mesh::with_texture(state.vm_display.id); vm_display_mesh.add_rect_with_uv( @@ -33,8 +31,6 @@ pub fn vm_display(state: &mut State, ui: &mut Ui) { ui.painter().add(Shape::mesh(vm_display_mesh)); }); - - ui.add_space(270.0); } pub fn display_memory_contents(ui: &mut Ui, state: &mut State) { diff --git a/src/ui/egui_plugin.rs b/src/ui/egui_plugin.rs index 699cdbe..021ad66 100644 --- a/src/ui/egui_plugin.rs +++ b/src/ui/egui_plugin.rs @@ -1,9 +1,11 @@ use super::state::State; use crate::ui::debug::{display_memory_contents, vm_display}; use crate::ui::options::option_dialog; +use crate::vm::consts::{DEBUG_DISPLAY_HEIGHT, DEBUG_DISPLAY_WIDTH}; use notan::app::App; use notan::egui::{ - self, CentralPanel, Context, Grid, Id, Modal, SidePanel, TopBottomPanel, Ui, + self, CentralPanel, Color32, Context, Grid, Id, Mesh, Modal, Rect, Shape, + SidePanel, TopBottomPanel, Ui, Vec2, }; use pollster::FutureExt; use rfd::AsyncFileDialog; @@ -52,9 +54,18 @@ pub fn init<'a>( } }); - if !state.vm.is_running && !state.debug_mode_enabled { + if !state.debug_mode_enabled { CentralPanel::default().show(ctx, |ui| { - ui.label("Welcome to Hachi!"); + if !state.vm.is_running { + ui.label("Welcome to Hachi!"); + } else { + vm_display_fullscreen( + state, + ui, + _app.window().width() as f32, + _app.window().width() as f32, + ); + } }); } @@ -85,7 +96,14 @@ pub fn init<'a>( }); ui.vertical(|ui| { - vm_display(state, ui); + vm_display( + state, + ui, + DEBUG_DISPLAY_HEIGHT as f32, + DEBUG_DISPLAY_WIDTH as f32, + ); + + ui.add_space(270.0); ui.separator(); @@ -139,7 +157,21 @@ fn file_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { fn view_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { |ui| { ui.add_enabled_ui(state.configuration.debug.enable_debug_menu, |ui| { - ui.checkbox(&mut state.debug_mode_enabled, "Debug mode"); + ui.checkbox(&mut state.debug_mode_enabled, "Debug mode").clicked(); }); } } + +fn vm_display_fullscreen(state: &State, ui: &mut Ui, height: f32, width: f32) { + let rect = ui.available_rect_before_wrap(); + let desired_size = Vec2::new(width, height / 1.5); + + let mut vm_display_mesh = Mesh::with_texture(state.vm_display.id); + vm_display_mesh.add_rect_with_uv( + Rect::from_min_size(egui::pos2(0.0, rect.min.y), desired_size), + Rect::from_min_max(egui::pos2(0.0, 1.0), egui::pos2(1.0, 0.0)), + Color32::WHITE, + ); + + ui.painter().add(Shape::mesh(vm_display_mesh)); +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index bd42ff7..2baa735 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -53,10 +53,5 @@ pub fn draw( } gfx.render(&ui_renderer); - - if state.debug_mode_enabled { - gfx.render_to(&state.display_renderer, &vm_display_renderer); - } else { - gfx.render(&vm_display_renderer); - } + gfx.render_to(&state.display_renderer, &vm_display_renderer); } diff --git a/src/ui/options.rs b/src/ui/options.rs index 0368475..e90e509 100644 --- a/src/ui/options.rs +++ b/src/ui/options.rs @@ -7,7 +7,6 @@ pub fn option_dialog(state: &mut State, ctx: &egui::Context) { .max_height(100.0) .max_width(200.0) .resizable(false) - .resizable(true) .show(ctx, |ui| { let mut cycles_per_frame_raw_text = state.configuration.vm.cycles_per_frame.to_string(); @@ -37,6 +36,14 @@ pub fn option_dialog(state: &mut State, ctx: &egui::Context) { ui.centered_and_justified(|ui| { if ui.button("Close").clicked() { state.configuration.update().unwrap(); + + if state.debug_mode_enabled { + state.debug_mode_enabled = + state.configuration.debug.enable_debug_menu; + } + + state.cycles_per_frame = + state.configuration.vm.cycles_per_frame; state.show_configuration_window = false; } }); diff --git a/src/ui/state.rs b/src/ui/state.rs index d93d043..a23b080 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -1,5 +1,4 @@ use crate::config::Configuration; -use crate::vm::consts::{DEBUG_DISPLAY_HEIGHT, DEBUG_DISPLAY_WIDTH}; use crate::vm::VirtualMachine; use async_mutex::Mutex as AsyncMutex; use clap::Parser; @@ -24,6 +23,7 @@ pub struct State { pub keypad_bindigs: HashMap, pub timer: f32, pub show_configuration_window: bool, + pub cycles_per_frame: u32, } #[derive(Parser)] @@ -47,10 +47,7 @@ pub fn setup(gfx: &mut Graphics) -> State { std::env::current_dir().unwrap() }; - let display_renderer = gfx - .create_render_texture(DEBUG_DISPLAY_WIDTH, DEBUG_DISPLAY_HEIGHT) - .build() - .unwrap(); + let display_renderer = gfx.create_render_texture(800, 600).build().unwrap(); let vm_display_texture = gfx.egui_register_texture(&display_renderer); @@ -83,7 +80,7 @@ pub fn setup(gfx: &mut Graphics) -> State { State { last_dir, - configuration: config, + configuration: config.clone(), file_path_option: Arc::new(AsyncMutex::new(file_path)), memory_debug_page: 0, memory_address_search: String::new(), @@ -94,6 +91,7 @@ pub fn setup(gfx: &mut Graphics) -> State { keypad_bindigs: default_bindings.into(), timer: 0.0, show_configuration_window: false, + cycles_per_frame: config.vm.cycles_per_frame, } } @@ -146,7 +144,7 @@ pub fn update(app: &mut App, state: &mut State) { } if state.vm.is_running { - for _ in 0..state.configuration.vm.cycles_per_frame { + for _ in 0..state.cycles_per_frame { state.vm.fde_cycle(); } } From df69ca20b365ec5ff5fc6981964fa57ff9ee3f0a Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Tue, 22 Apr 2025 22:36:29 +0200 Subject: [PATCH 5/6] Add Clone attribute to DebugOptions struct --- src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 22ff475..fd9eabb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -18,7 +18,7 @@ pub struct VmOptions { pub cycles_per_frame: u32, } -#[derive(Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize)] pub struct DebugOptions { pub enable_debug_menu: bool, } From 5c37b759a8fb29c943b3a3be85f1d4dee9f0e5a0 Mon Sep 17 00:00:00 2001 From: Daniel Olczyk Date: Tue, 22 Apr 2025 23:10:57 +0200 Subject: [PATCH 6/6] Change behaviour of debug mode button in configuration window --- src/ui/egui_plugin.rs | 4 +--- src/ui/options.rs | 7 +------ src/ui/state.rs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ui/egui_plugin.rs b/src/ui/egui_plugin.rs index 021ad66..7ea3ca4 100644 --- a/src/ui/egui_plugin.rs +++ b/src/ui/egui_plugin.rs @@ -156,9 +156,7 @@ fn file_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { fn view_menu_handler(state: &mut State) -> impl FnOnce(&mut Ui) + '_ { |ui| { - ui.add_enabled_ui(state.configuration.debug.enable_debug_menu, |ui| { - ui.checkbox(&mut state.debug_mode_enabled, "Debug mode").clicked(); - }); + ui.checkbox(&mut state.debug_mode_enabled, "Debug mode").clicked(); } } diff --git a/src/ui/options.rs b/src/ui/options.rs index e90e509..b672da5 100644 --- a/src/ui/options.rs +++ b/src/ui/options.rs @@ -27,7 +27,7 @@ pub fn option_dialog(state: &mut State, ctx: &egui::Context) { ui.horizontal(|ui| { ui.checkbox( &mut state.configuration.debug.enable_debug_menu, - "Debug mode", + "Run \"Debug mode\" on start", ); }); @@ -37,11 +37,6 @@ pub fn option_dialog(state: &mut State, ctx: &egui::Context) { if ui.button("Close").clicked() { state.configuration.update().unwrap(); - if state.debug_mode_enabled { - state.debug_mode_enabled = - state.configuration.debug.enable_debug_menu; - } - state.cycles_per_frame = state.configuration.vm.cycles_per_frame; state.show_configuration_window = false; diff --git a/src/ui/state.rs b/src/ui/state.rs index a23b080..1578e34 100644 --- a/src/ui/state.rs +++ b/src/ui/state.rs @@ -85,7 +85,7 @@ pub fn setup(gfx: &mut Graphics) -> State { memory_debug_page: 0, memory_address_search: String::new(), vm: VirtualMachine::new(), - debug_mode_enabled: false, + debug_mode_enabled: config.debug.enable_debug_menu, display_renderer, vm_display: vm_display_texture, keypad_bindigs: default_bindings.into(),