Surface error notifications

This commit is contained in:
Simon Gellis 2024-12-09 23:18:42 -05:00
parent 25b88c622c
commit ae04f9f73b
4 changed files with 76 additions and 19 deletions

20
Cargo.lock generated
View File

@ -908,6 +908,15 @@ dependencies = [
"nohash-hasher", "nohash-hasher",
] ]
[[package]]
name = "egui-toast"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95d58a44bf48161fe6a3f12386722e99bf86b2c16d23c2d04f6a48a95a5923d2"
dependencies = [
"egui",
]
[[package]] [[package]]
name = "egui-wgpu" name = "egui-wgpu"
version = "0.29.1" version = "0.29.1"
@ -1711,6 +1720,7 @@ dependencies = [
"clap", "clap",
"cpal", "cpal",
"egui", "egui",
"egui-toast",
"egui-wgpu", "egui-wgpu",
"egui-winit", "egui-winit",
"egui_extras", "egui_extras",
@ -1732,9 +1742,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.167" version = "0.2.168"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -2690,15 +2700,15 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.41" version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]

View File

@ -11,6 +11,7 @@ clap = { version = "4", features = ["derive"] }
cpal = { git = "https://github.com/sidit77/cpal.git", rev = "66ed6be" } cpal = { git = "https://github.com/sidit77/cpal.git", rev = "66ed6be" }
egui = "0.29" egui = "0.29"
egui_extras = "0.29" egui_extras = "0.29"
egui-toast = "0.15"
egui-winit = "0.29" egui-winit = "0.29"
egui-wgpu = { version = "0.29", features = ["winit"] } egui-wgpu = { version = "0.29", features = ["winit"] }
gilrs = "0.11" gilrs = "0.11"

View File

@ -11,6 +11,7 @@ use std::{
}; };
use anyhow::Result; use anyhow::Result;
use egui_toast::{Toast, ToastKind, ToastOptions};
use crate::{audio::Audio, graphics::TextureSink}; use crate::{audio::Audio, graphics::TextureSink};
pub use shrooms_vb_core::VBKey; pub use shrooms_vb_core::VBKey;
@ -139,6 +140,7 @@ pub struct Emulator {
audio_on: Arc<[AtomicBool; 2]>, audio_on: Arc<[AtomicBool; 2]>,
linked: Arc<AtomicBool>, linked: Arc<AtomicBool>,
renderers: HashMap<SimId, TextureSink>, renderers: HashMap<SimId, TextureSink>,
messages: HashMap<SimId, mpsc::Sender<Toast>>,
} }
impl Emulator { impl Emulator {
@ -161,6 +163,7 @@ impl Emulator {
audio_on, audio_on,
linked, linked,
renderers: HashMap::new(), renderers: HashMap::new(),
messages: HashMap::new(),
}) })
} }
@ -330,28 +333,35 @@ impl Emulator {
fn handle_command(&mut self, command: EmulatorCommand) { fn handle_command(&mut self, command: EmulatorCommand) {
match command { match command {
EmulatorCommand::SetRenderer(sim_id, renderer) => { EmulatorCommand::ConnectToSim(sim_id, renderer, messages) => {
self.renderers.insert(sim_id, renderer); self.renderers.insert(sim_id, renderer);
self.messages.insert(sim_id, messages);
} }
EmulatorCommand::LoadGame(sim_id, path) => { EmulatorCommand::LoadGame(sim_id, path) => {
if let Err(error) = self.load_cart(sim_id, &path) { if let Err(error) = self.load_cart(sim_id, &path) {
eprintln!("error loading rom: {}", error); self.report_error(sim_id, format!("Error loading rom: {error}"));
} }
} }
EmulatorCommand::StartSecondSim(path) => { EmulatorCommand::StartSecondSim(path) => {
if let Err(error) = self.start_second_sim(path) { if let Err(error) = self.start_second_sim(path) {
eprintln!("error starting second sim: {}", error); self.report_error(
SimId::Player2,
format!("Error starting second sim: {error}"),
);
} }
} }
EmulatorCommand::StopSecondSim => { EmulatorCommand::StopSecondSim => {
if let Err(error) = self.stop_second_sim() { if let Err(error) = self.stop_second_sim() {
eprintln!("error stopping second sim: {}", error); self.report_error(
SimId::Player2,
format!("Error stopping second sim: {error}"),
);
} }
} }
EmulatorCommand::Pause => { EmulatorCommand::Pause => {
for sim_id in SimId::values() { for sim_id in SimId::values() {
if let Err(error) = self.pause_sim(sim_id) { if let Err(error) = self.pause_sim(sim_id) {
eprintln!("error pausing: {}", error); self.report_error(sim_id, format!("Error pausing: {error}"));
} }
} }
} }
@ -375,7 +385,7 @@ impl Emulator {
} }
EmulatorCommand::Reset(sim_id) => { EmulatorCommand::Reset(sim_id) => {
if let Err(error) = self.reset_sim(sim_id, None) { if let Err(error) = self.reset_sim(sim_id, None) {
eprintln!("error resetting sim: {}", error); self.report_error(sim_id, format!("Error resetting sim: {error}"));
} }
} }
EmulatorCommand::SetKeys(sim_id, keys) => { EmulatorCommand::SetKeys(sim_id, keys) => {
@ -386,18 +396,35 @@ impl Emulator {
EmulatorCommand::Exit(done) => { EmulatorCommand::Exit(done) => {
for sim_id in SimId::values() { for sim_id in SimId::values() {
if let Err(error) = self.save_sram(sim_id) { if let Err(error) = self.save_sram(sim_id) {
eprintln!("error saving sram on exit: {}", error); self.report_error(sim_id, format!("Error saving sram on exit: {error}"));
} }
} }
let _ = done.send(()); let _ = done.send(());
} }
} }
} }
fn report_error(&self, sim_id: SimId, message: String) {
let messages = self
.messages
.get(&sim_id)
.or_else(|| self.messages.get(&SimId::Player1));
if let Some(msg) = messages {
let toast = Toast::new()
.kind(ToastKind::Error)
.options(ToastOptions::default().duration_in_seconds(5.0))
.text(&message);
if msg.send(toast).is_ok() {
return;
}
}
eprintln!("{}", message);
}
} }
#[derive(Debug)] #[derive(Debug)]
pub enum EmulatorCommand { pub enum EmulatorCommand {
SetRenderer(SimId, TextureSink), ConnectToSim(SimId, TextureSink, mpsc::Sender<Toast>),
LoadGame(SimId, PathBuf), LoadGame(SimId, PathBuf),
StartSecondSim(Option<PathBuf>), StartSecondSim(Option<PathBuf>),
StopSecondSim, StopSecondSim,

View File

@ -1,12 +1,15 @@
use std::sync::mpsc;
use crate::{ use crate::{
app::UserEvent, app::UserEvent,
emulator::{EmulatorClient, EmulatorCommand, SimId}, emulator::{EmulatorClient, EmulatorCommand, SimId},
}; };
use egui::{ use egui::{
ecolor::HexColor, menu, Align2, Button, CentralPanel, Color32, Context, Frame, Layout, ecolor::HexColor, menu, Align2, Button, CentralPanel, Color32, Context, Direction, Frame,
Response, Sense, TopBottomPanel, Ui, Vec2, ViewportBuilder, ViewportCommand, ViewportId, Layout, Response, Sense, TopBottomPanel, Ui, Vec2, ViewportBuilder, ViewportCommand,
WidgetText, Window, ViewportId, WidgetText, Window,
}; };
use egui_toast::{Toast, Toasts};
use winit::event_loop::EventLoopProxy; use winit::event_loop::EventLoopProxy;
use super::{ use super::{
@ -36,6 +39,7 @@ pub struct GameWindow {
display_mode: DisplayMode, display_mode: DisplayMode,
colors: [Color32; 2], colors: [Color32; 2],
screen: Option<GameScreen>, screen: Option<GameScreen>,
messages: Option<mpsc::Receiver<Toast>>,
color_picker: Option<ColorPickerState>, color_picker: Option<ColorPickerState>,
} }
@ -48,6 +52,7 @@ impl GameWindow {
display_mode: DisplayMode::Anaglyph, display_mode: DisplayMode::Anaglyph,
colors: COLOR_PRESETS[0], colors: COLOR_PRESETS[0],
screen: None, screen: None,
messages: None,
color_picker: None, color_picker: None,
} }
} }
@ -257,6 +262,14 @@ impl AppWindow for GameWindow {
} }
fn show(&mut self, ctx: &Context) { fn show(&mut self, ctx: &Context) {
let mut toasts = Toasts::new()
.anchor(Align2::LEFT_BOTTOM, (10.0, 10.0))
.direction(Direction::BottomUp);
if let Some(messages) = self.messages.as_mut() {
while let Ok(toast) = messages.try_recv() {
toasts.add(toast);
}
}
TopBottomPanel::top("menubar") TopBottomPanel::top("menubar")
.exact_height(22.0) .exact_height(22.0)
.show(ctx, |ui| { .show(ctx, |ui| {
@ -280,13 +293,19 @@ impl AppWindow for GameWindow {
ui.add(screen); ui.add(screen);
} }
}); });
toasts.show(ctx);
} }
fn on_init(&mut self, render_state: &egui_wgpu::RenderState) { fn on_init(&mut self, render_state: &egui_wgpu::RenderState) {
let (screen, sink) = GameScreen::init(render_state); let (screen, sink) = GameScreen::init(render_state);
self.client let (message_sink, message_source) = mpsc::channel();
.send_command(EmulatorCommand::SetRenderer(self.sim_id, sink)); self.client.send_command(EmulatorCommand::ConnectToSim(
self.screen = Some(screen) self.sim_id,
sink,
message_sink,
));
self.screen = Some(screen);
self.messages = Some(message_source);
} }
fn on_destroy(&mut self) { fn on_destroy(&mut self) {