From 4c4fba3acc4a1a2f4a284e3ba6ac896b7ca9b894 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee <ultimaweapon@outlook.com> Date: Sun, 15 Dec 2024 22:48:20 +0700 Subject: [PATCH] Moves error display to App trait (#1183) --- .github/workflows/ci-windows.yml | 12 +++++++ gui/src/main.rs | 61 +++++++++++++++++--------------- gui/src/rt/app.rs | 6 ++++ gui/src/rt/mod.rs | 26 +++++++++++--- 4 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 gui/src/rt/app.rs diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 1c7b5ae08..36b6c31dd 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -10,6 +10,7 @@ jobs: uses: actions/checkout@v4 - name: Generate cache keys run: | + echo "target=${{ runner.os }}-target" >> $env:GITHUB_OUTPUT echo "vulkan=${{ runner.os }}-vulkan-1.3.290.0" >> $env:GITHUB_OUTPUT id: cache-keys - name: Restore Vulkan SDK @@ -27,6 +28,11 @@ jobs: if: ${{ steps.restore-vulkan.outputs.cache-hit != 'true' }} - name: Set Vulkan SDK path run: echo "VULKAN_SDK=C:\VulkanSDK" >> $env:GITHUB_ENV + - name: Restore target directory + uses: actions/cache/restore@v4 + with: + path: target + key: ${{ steps.cache-keys.outputs.target }} - name: Update Rust run: rustup update stable - name: Add additional Rust targets @@ -40,6 +46,12 @@ jobs: with: name: obliteration-win-x64 path: dist + - name: Cache target directory + uses: actions/cache/save@v4 + with: + path: target + key: ${{ steps.cache-keys.outputs.target }}-${{ github.run_id }} + if: startsWith(github.ref, 'refs/heads/') - name: Cache Vulkan SDK uses: actions/cache/save@v4 with: diff --git a/gui/src/main.rs b/gui/src/main.rs index 5c83736b3..1d42522ee 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -10,6 +10,7 @@ use debug::DebugServer; use erdp::ErrorDisplay; use slint::{ComponentHandle, ModelRc, SharedString, VecModel}; use std::cell::Cell; +use std::error::Error; use std::net::SocketAddrV4; use std::path::PathBuf; use std::process::ExitCode; @@ -74,30 +75,9 @@ fn main() -> ExitCode { } // Run. - let run = async move { - // Setup Slint back-end. This need to be done before using any Slint API. - slint::platform::set_platform(Box::new(SlintBackend::new())).unwrap(); - - // Run. - let e = match run(args, exe).await { - Ok(_) => return, - Err(e) => e, - }; - - // Show error window. - let win = ErrorWindow::new().unwrap(); - - win.set_message(format!("An unexpected error has occurred: {}.", e.display()).into()); - win.on_close({ - let win = win.as_weak(); + let app = Rc::new(App { args, exe }); - move || win.unwrap().hide().unwrap() - }); - - win.exec().await.unwrap(); - }; - - match self::rt::block_on(run) { + match self::rt::run(app.clone(), run(app)) { Ok(_) => ExitCode::SUCCESS, Err(e) => { error(format!( @@ -110,7 +90,10 @@ fn main() -> ExitCode { } } -async fn run(args: ProgramArgs, exe: PathBuf) -> Result<(), ProgramError> { +async fn run(app: Rc<App>) -> Result<(), ProgramError> { + // Setup Slint back-end. This need to be done before using any Slint API. + slint::platform::set_platform(Box::new(SlintBackend::new())).unwrap(); + #[cfg(unix)] rlim::set_rlimit_nofile().map_err(ProgramError::FdLimit)?; @@ -125,9 +108,9 @@ async fn run(args: ProgramArgs, exe: PathBuf) -> Result<(), ProgramError> { }; // Get kernel path. - let kernel = args.kernel.as_ref().cloned().unwrap_or_else(|| { + let kernel = app.args.kernel.as_ref().cloned().unwrap_or_else(|| { // Get kernel directory. - let mut path = exe.parent().unwrap().to_owned(); + let mut path = app.exe.parent().unwrap().to_owned(); #[cfg(target_os = "windows")] path.push("share"); @@ -175,7 +158,7 @@ async fn run(args: ProgramArgs, exe: PathBuf) -> Result<(), ProgramError> { } // Get profile to use. - let (profile, debug) = if let Some(v) = args.debug { + let (profile, debug) = if let Some(v) = app.args.debug { // TODO: Select last used profile. (profiles.pop().unwrap(), Some(v)) } else { @@ -352,6 +335,28 @@ enum ProgramMode { PanicHandler, } +/// Implementation of [`self::rt::App`] for main program mode. +struct App { + args: ProgramArgs, + exe: PathBuf, +} + +impl self::rt::App for App { + async fn error(&self, e: impl Error) { + // Show error window. + let win = ErrorWindow::new().unwrap(); + + win.set_message(format!("An unexpected error has occurred: {}.", e.display()).into()); + win.on_close({ + let win = win.as_weak(); + + move || win.unwrap().hide().unwrap() + }); + + win.exec().await.unwrap(); + } +} + /// Represents an error when our program fails. #[derive(Debug, Error)] enum ProgramError { @@ -393,5 +398,5 @@ enum ProgramError { RunMainWindow(#[source] slint::PlatformError), #[error("couldn't create VMM screen")] - CreateScreen(#[source] Box<dyn std::error::Error>), + CreateScreen(#[source] Box<dyn Error>), } diff --git a/gui/src/rt/app.rs b/gui/src/rt/app.rs new file mode 100644 index 000000000..75a4cf4a0 --- /dev/null +++ b/gui/src/rt/app.rs @@ -0,0 +1,6 @@ +use std::error::Error; + +/// Provides application-specific methods for runtime to use. +pub trait App: 'static { + async fn error(&self, e: impl Error); +} diff --git a/gui/src/rt/mod.rs b/gui/src/rt/mod.rs index bbd64ccfb..88c2b40df 100644 --- a/gui/src/rt/mod.rs +++ b/gui/src/rt/mod.rs @@ -1,3 +1,4 @@ +pub use self::app::*; pub use self::context::*; pub use self::window::*; @@ -7,7 +8,7 @@ use self::waker::Waker; use std::collections::HashMap; use std::error::Error; use std::future::Future; -use std::rc::Weak; +use std::rc::{Rc, Weak}; use std::sync::Arc; use thiserror::Error; use winit::application::ApplicationHandler; @@ -15,19 +16,34 @@ use winit::error::{EventLoopError, OsError}; use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy}; use winit::window::WindowId; +mod app; mod context; mod event; mod task; mod waker; mod window; -pub fn block_on(main: impl Future<Output = ()> + 'static) -> Result<(), RuntimeError> { +pub fn run<E, A>( + app: Rc<A>, + main: impl Future<Output = Result<(), E>> + 'static, +) -> Result<(), RuntimeError> +where + E: Error, + A: App, +{ // Setup winit event loop. let mut el = EventLoop::<Event>::with_user_event(); let el = el.build().map_err(RuntimeError::CreateEventLoop)?; - let main = async move { - main.await; - RuntimeContext::with(|cx| cx.el.exit()); + let main = { + let app = app.clone(); + + async move { + if let Err(e) = main.await { + app.error(e).await; + } + + RuntimeContext::with(|cx| cx.el.exit()); + } }; // Run event loop.