From e1d08672fb19371ec8b6897b7efa3bbbc99f85ce Mon Sep 17 00:00:00 2001 From: Simon Gellis Date: Sun, 23 Nov 2025 13:37:35 -0500 Subject: [PATCH] Remember whether game is muted --- src/app.rs | 2 +- src/config.rs | 58 ++++++++++++++++++++++++++++++++++++++ src/emulator.rs | 18 ++++++------ src/main.rs | 9 ++++++ src/window.rs | 1 + src/window/game.rs | 70 ++++++++++------------------------------------ 6 files changed, 92 insertions(+), 66 deletions(-) create mode 100644 src/config.rs diff --git a/src/app.rs b/src/app.rs index a6ff2cb..85101b3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -61,12 +61,12 @@ impl Application { pub fn new( client: EmulatorClient, proxy: EventLoopProxy, + persistence: Persistence, debug_port: Option, profiling: bool, ) -> Self { let wgpu = WgpuState::new(); let icon = load_icon().ok().map(Arc::new); - let persistence = Persistence::new(); let mappings = MappingProvider::new(persistence.clone()); let shortcuts = ShortcutProvider::new(persistence.clone()); let controllers = ControllerManager::new(client.clone(), &mappings); diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4b29570 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,58 @@ +use anyhow::Result; +use egui::{Color32, Vec2}; +use serde::{Deserialize, Serialize}; + +use crate::{emulator::SimId, persistence::Persistence, window::DisplayMode}; + +pub const COLOR_PRESETS: [[Color32; 2]; 3] = [ + [ + Color32::from_rgb(0xff, 0x00, 0x00), + Color32::from_rgb(0x00, 0xc6, 0xf0), + ], + [ + Color32::from_rgb(0x00, 0xb4, 0x00), + Color32::from_rgb(0xc8, 0x00, 0xff), + ], + [ + Color32::from_rgb(0xb4, 0x9b, 0x00), + Color32::from_rgb(0x00, 0x00, 0xff), + ], +]; + +const fn default_audio_enabled() -> bool { + true +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct SimConfig { + pub display_mode: DisplayMode, + pub colors: [Color32; 2], + pub dimensions: Vec2, + #[serde(default = "default_audio_enabled")] + pub audio_enabled: bool, +} + +impl SimConfig { + pub fn load(persistence: &Persistence, sim_id: SimId) -> Self { + if let Ok(config) = persistence.load_config(config_filename(sim_id)) { + return config; + } + Self { + display_mode: DisplayMode::Anaglyph, + colors: COLOR_PRESETS[0], + dimensions: DisplayMode::Anaglyph.proportions() + Vec2::new(0.0, 22.0), + audio_enabled: true, + } + } + + pub fn save(&self, persistence: &Persistence, sim_id: SimId) -> Result<()> { + persistence.save_config(config_filename(sim_id), self) + } +} + +fn config_filename(sim_id: SimId) -> &'static str { + match sim_id { + SimId::Player1 => "config_p1", + SimId::Player2 => "config_p2", + } +} diff --git a/src/emulator.rs b/src/emulator.rs index 072040e..4dc4a88 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -96,7 +96,6 @@ impl EmulatorBuilder { queue, sim_state: builder.sim_state.clone(), state: builder.state.clone(), - audio_on: builder.audio_on.clone(), linked: builder.linked.clone(), }; (builder, client) @@ -116,6 +115,12 @@ impl EmulatorBuilder { } } + pub fn with_audio_on(self, p1: bool, p2: bool) -> Self { + self.audio_on[0].store(p1, Ordering::Relaxed); + self.audio_on[1].store(p2, Ordering::Relaxed); + self + } + pub fn build(self) -> Result { let mut emulator = Emulator::new( self.commands, @@ -715,9 +720,8 @@ impl Emulator { }; sim.watch_stdout(true); } - EmulatorCommand::SetAudioEnabled(p1, p2) => { - self.audio_on[SimId::Player1.to_index()].store(p1, Ordering::Release); - self.audio_on[SimId::Player2.to_index()].store(p2, Ordering::Release); + EmulatorCommand::SetAudioEnabled(sim_id, enabled) => { + self.audio_on[sim_id.to_index()].store(enabled, Ordering::Release); } EmulatorCommand::Link => { self.link_sims(); @@ -792,7 +796,7 @@ pub enum EmulatorCommand { AddWatchpoint(SimId, u32, usize, VBWatchpointType), RemoveWatchpoint(SimId, u32, usize, VBWatchpointType), WatchStdout(SimId, mpsc::Sender), - SetAudioEnabled(bool, bool), + SetAudioEnabled(SimId, bool), Link, Unlink, Reset(SimId), @@ -858,7 +862,6 @@ pub struct EmulatorClient { queue: mpsc::Sender, sim_state: Arc<[Atomic; 2]>, state: Arc>, - audio_on: Arc<[AtomicBool; 2]>, linked: Arc, } @@ -869,9 +872,6 @@ impl EmulatorClient { pub fn emulator_state(&self) -> EmulatorState { self.state.load(Ordering::Acquire) } - pub fn is_audio_enabled(&self, sim_id: SimId) -> bool { - self.audio_on[sim_id.to_index()].load(Ordering::Acquire) - } pub fn are_sims_linked(&self) -> bool { self.linked.load(Ordering::Acquire) } diff --git a/src/main.rs b/src/main.rs index 04f823c..cf7cb50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,11 @@ use tracing::error; use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt}; use winit::event_loop::{ControlFlow, EventLoop}; +use crate::{config::SimConfig, emulator::SimId, persistence::Persistence}; + mod app; mod audio; +mod config; mod controller; mod emulator; mod gdbserver; @@ -100,6 +103,8 @@ fn main() -> Result<()> { let args = Args::parse(); + let persistence = Persistence::new(); + let (mut builder, client) = EmulatorBuilder::new(); if let Some(path) = &args.rom { builder = builder.with_rom(path); @@ -113,6 +118,9 @@ fn main() -> Result<()> { if args.profile { builder = builder.start_paused(true) } + let p1_audio_on = SimConfig::load(&persistence, SimId::Player1).audio_enabled; + let p2_audio_on = SimConfig::load(&persistence, SimId::Player2).audio_enabled; + builder = builder.with_audio_on(p1_audio_on, p2_audio_on); ThreadBuilder::default() .name("Emulator".to_owned()) @@ -134,6 +142,7 @@ fn main() -> Result<()> { event_loop.run_app(&mut Application::new( client, proxy, + persistence, args.debug_port, args.profile, ))?; diff --git a/src/window.rs b/src/window.rs index 36012e1..3c48d43 100644 --- a/src/window.rs +++ b/src/window.rs @@ -3,6 +3,7 @@ use std::sync::Arc; pub use about::AboutWindow; use egui::{Context, ViewportBuilder, ViewportId}; pub use game::GameWindow; +pub use game_screen::DisplayMode; pub use gdb::GdbServerWindow; pub use hotkeys::HotkeysWindow; pub use input::InputWindow; diff --git a/src/window/game.rs b/src/window/game.rs index 8415846..b4892e4 100644 --- a/src/window/game.rs +++ b/src/window/game.rs @@ -5,6 +5,7 @@ use std::{ use crate::{ app::UserEvent, + config::{COLOR_PRESETS, SimConfig}, emulator::{EmulatorClient, EmulatorCommand, EmulatorState, SimId, SimState}, input::{Command, ShortcutProvider}, persistence::Persistence, @@ -16,7 +17,6 @@ use egui::{ ViewportBuilder, ViewportCommand, ViewportId, Window, }; use egui_notify::{Anchor, Toast, Toasts}; -use serde::{Deserialize, Serialize}; use winit::event_loop::EventLoopProxy; use super::{ @@ -25,28 +25,13 @@ use super::{ utils::UiExt as _, }; -const COLOR_PRESETS: [[Color32; 2]; 3] = [ - [ - Color32::from_rgb(0xff, 0x00, 0x00), - Color32::from_rgb(0x00, 0xc6, 0xf0), - ], - [ - Color32::from_rgb(0x00, 0xb4, 0x00), - Color32::from_rgb(0xc8, 0x00, 0xff), - ], - [ - Color32::from_rgb(0xb4, 0x9b, 0x00), - Color32::from_rgb(0x00, 0x00, 0xff), - ], -]; - pub struct GameWindow { client: EmulatorClient, proxy: EventLoopProxy, persistence: Persistence, shortcuts: ShortcutProvider, sim_id: SimId, - config: GameConfig, + config: SimConfig, toasts: Toasts, screen: Option, messages: Option>, @@ -62,7 +47,7 @@ impl GameWindow { shortcuts: ShortcutProvider, sim_id: SimId, ) -> Self { - let config = load_config(&persistence, sim_id); + let config = SimConfig::load(&persistence, sim_id); let toasts = Toasts::new() .with_anchor(Anchor::BottomLeft) .with_margin((10.0, 10.0).into()) @@ -410,15 +395,15 @@ impl GameWindow { }); }); ui.menu_button("Audio", |ui| { - let p1_enabled = self.client.is_audio_enabled(SimId::Player1); - let p2_enabled = self.client.is_audio_enabled(SimId::Player2); - if ui.selectable_button(p1_enabled, "Player 1").clicked() { - self.client - .send_command(EmulatorCommand::SetAudioEnabled(!p1_enabled, p2_enabled)); - } - if ui.selectable_button(p2_enabled, "Player 2").clicked() { - self.client - .send_command(EmulatorCommand::SetAudioEnabled(p1_enabled, !p2_enabled)); + if ui + .selectable_button(self.config.audio_enabled, "Enabled") + .clicked() + { + self.update_config(|c| c.audio_enabled = !c.audio_enabled); + self.client.send_command(EmulatorCommand::SetAudioEnabled( + self.sim_id, + self.config.audio_enabled, + )); } }); ui.menu_button("Input", |ui| { @@ -460,13 +445,11 @@ impl GameWindow { } } - fn update_config(&mut self, update: impl FnOnce(&mut GameConfig)) { + fn update_config(&mut self, update: impl FnOnce(&mut SimConfig)) { let mut new_config = self.config.clone(); update(&mut new_config); if self.config != new_config { - let _ = self - .persistence - .save_config(config_filename(self.sim_id), &new_config); + let _ = new_config.save(&self.persistence, self.sim_id); } self.config = new_config; } @@ -480,24 +463,6 @@ impl GameWindow { } } -fn config_filename(sim_id: SimId) -> &'static str { - match sim_id { - SimId::Player1 => "config_p1", - SimId::Player2 => "config_p2", - } -} - -fn load_config(persistence: &Persistence, sim_id: SimId) -> GameConfig { - if let Ok(config) = persistence.load_config(config_filename(sim_id)) { - return config; - } - GameConfig { - display_mode: DisplayMode::Anaglyph, - colors: COLOR_PRESETS[0], - dimensions: DisplayMode::Anaglyph.proportions() + Vec2::new(0.0, 22.0), - } -} - impl AppWindow for GameWindow { fn viewport_id(&self) -> ViewportId { match self.sim_id { @@ -580,10 +545,3 @@ struct ColorPickerState { just_opened: bool, unpause_on_close: bool, } - -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] -struct GameConfig { - display_mode: DisplayMode, - colors: [Color32; 2], - dimensions: Vec2, -}