From 2a6aa247d3dde3c289064c8b4ab194bfe854fab3 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Wed, 11 Dec 2024 05:15:24 +0700 Subject: [PATCH] Initializes custom back-end for Slint (#1176) --- Cargo.lock | 13 +-- gui/Cargo.toml | 3 +- gui/main_window.cpp | 12 --- gui/main_window.hpp | 2 - gui/src/main.rs | 187 +++++++++++++++++++++----------------- gui/src/panic/mod.rs | 40 ++++---- gui/src/ui/backend/mod.rs | 20 ++++ gui/src/ui/mod.rs | 2 + 8 files changed, 160 insertions(+), 119 deletions(-) create mode 100644 gui/src/ui/backend/mod.rs diff --git a/Cargo.lock b/Cargo.lock index eb90a635b..dd5fa812f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1579,6 +1579,7 @@ dependencies = [ "gdbstub", "gdbstub_arch", "humansize", + "i-slint-renderer-skia", "libc", "metal 0.29.0", "num_enum", @@ -3335,9 +3336,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3809,9 +3810,9 @@ dependencies = [ [[package]] name = "tiny-xlib" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d52f22673960ad13af14ff4025997312def1223bfa7c8e4949d099e6b3d5d1c" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" dependencies = [ "as-raw-xcb-connection", "ctor-lite", @@ -4863,9 +4864,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "xmlwriter" diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 3a5ee9dc0..331271e2b 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -15,6 +15,7 @@ erdp = "0.1.1" gdbstub = "0.7.3" gdbstub_arch = "0.3.1" humansize = "2.1.3" +i-slint-renderer-skia = "=1.8.0" libc = "0.2.164" num_enum = "0.7.3" obconf = { path = "../src/obconf", features = ["serde", "virt"] } @@ -32,7 +33,7 @@ rev = "64787fdc0489724f0914356d925be014a2f1bf3e" features = ["read"] [dependencies.slint] -version = "1.8.0" +version = "=1.8.0" features = [ "backend-winit", "compat-1-2", diff --git a/gui/main_window.cpp b/gui/main_window.cpp index 557ba7aca..7c6af39dd 100644 --- a/gui/main_window.cpp +++ b/gui/main_window.cpp @@ -76,15 +76,3 @@ void MainWindow::aboutObliteration() "PC. This will allows you to play your games forever even if your PlayStation 4 stopped " "working in the future."); } - -void MainWindow::waitKernelExit(bool success) -{ - if (!success) { - QMessageBox::critical( - this, - "Error", - "The kernel was stopped unexpectedly. See the kernel logs for more details."); - } - - m_main->setCurrentIndex(0); -} diff --git a/gui/main_window.hpp b/gui/main_window.hpp index 5daaadff0..79295f908 100644 --- a/gui/main_window.hpp +++ b/gui/main_window.hpp @@ -13,7 +13,5 @@ private slots: void reportIssue(); void aboutObliteration(); private: - void waitKernelExit(bool success); - QStackedWidget *m_main; }; diff --git a/gui/src/main.rs b/gui/src/main.rs index 0c3b7b9c4..1c2b42742 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -1,24 +1,25 @@ #![windows_subsystem = "windows"] use self::data::{DataError, DataMgr}; -use self::graphics::{Graphics, GraphicsError, PhysicalDevice, Screen}; +use self::graphics::{Graphics, GraphicsError, PhysicalDevice}; use self::profile::Profile; use self::setup::{run_setup, SetupError}; -use self::ui::{ErrorWindow, MainWindow, ProfileModel, ResolutionModel}; -use self::vmm::{Vmm, VmmArgs, VmmError, VmmEvent}; +use self::ui::{ErrorWindow, MainWindow, ProfileModel, ResolutionModel, SlintBackend}; use clap::{Parser, ValueEnum}; 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; use std::rc::Rc; use std::sync::Arc; use thiserror::Error; -use winit::error::EventLoopError; -use winit::event_loop::EventLoop; +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::window::WindowId; mod data; mod debug; @@ -35,53 +36,73 @@ mod vfs; mod vmm; fn main() -> ExitCode { - use std::fmt::Write; - // Check program mode. let args = ProgramArgs::parse(); - let r = match &args.mode { - Some(ProgramMode::PanicHandler) => self::panic::run_handler(), - None => run_vmm(&args), - }; - - // Check program result. - let e = match r { - Ok(_) => return ExitCode::SUCCESS, - Err(e) => e, - }; - // Get full message. - let mut msg = e.to_string(); - let mut src = e.source(); + match &args.mode { + Some(ProgramMode::PanicHandler) => return self::panic::run_handler(), + None => {} + } - while let Some(e) = src { - write!(msg, " -> {e}").unwrap(); - src = e.source(); + #[cfg(target_os = "windows")] + fn error(msg: impl AsRef) { + todo!() } - // Show error window. - let win = ErrorWindow::new().unwrap(); + #[cfg(not(target_os = "windows"))] + fn error(msg: impl AsRef) { + eprintln!("{}", msg.as_ref()); + } - win.set_message(format!("An unexpected error has occurred: {msg}.").into()); - win.on_close({ - let win = win.as_weak(); + // Spawn panic handler. + let exe = match std::env::current_exe().and_then(std::fs::canonicalize) { + Ok(v) => v, + Err(e) => { + error(format!( + "Failed to get application executable path: {}.", + e.display() + )); + + return ExitCode::FAILURE; + } + }; - move || win.unwrap().hide().unwrap() - }); + if let Err(e) = self::panic::spawn_handler(&exe) { + error(format!( + "Failed to spawn panic handler process: {}.", + e.display() + )); - win.run().unwrap(); + return ExitCode::FAILURE; + } - ExitCode::FAILURE -} + // Setup UI event loop. + let mut el = EventLoop::::with_user_event(); + let el = match el.build() { + Ok(v) => v, + Err(e) => { + error(format!( + "Failed to create winit event loop: {}.", + e.display() + )); + + return ExitCode::FAILURE; + } + }; -fn run_vmm(args: &ProgramArgs) -> Result<(), ProgramError> { - // Spawn panic handler. - let exe = std::env::current_exe() - .and_then(std::fs::canonicalize) - .map_err(ProgramError::GetCurrentExePath)?; + // Run. + let mut prog = Program { args }; - self::panic::spawn_handler(&exe)?; + match el.run_app(&mut prog) { + Ok(_) => ExitCode::SUCCESS, + Err(e) => { + error(format!("Failed to run winit event loop: {}.", e.display())); + ExitCode::FAILURE + } + } +} +fn run_vmm(args: &ProgramArgs, exe: &PathBuf) -> Result<(), ProgramError> { #[cfg(unix)] rlim::set_rlimit_nofile().map_err(ProgramError::FdLimit)?; @@ -176,34 +197,11 @@ fn run_vmm(args: &ProgramArgs) -> Result<(), ProgramError> { }; // Setup VMM screen. - let mut el = EventLoop::::with_user_event(); - let screen = graphics .create_screen(&profile) .map_err(|e| ProgramError::CreateScreen(Box::new(e)))?; - // Start VMM. - let el = el.build().map_err(ProgramError::CreateVmmEventLoop)?; - - std::thread::scope(|scope| { - let vmm = Vmm::new( - VmmArgs { - profile: &profile, - kernel, - debugger, - el: el.create_proxy(), - }, - scope, - ) - .map_err(ProgramError::StartVmm)?; - - // Run the screen. - screen - .run() - .map_err(|e| ProgramError::RunScreen(Box::new(e)))?; - - Ok(()) - }) + todo!() } fn run_launcher( @@ -318,6 +316,49 @@ fn run_launcher( Ok(Some((profile, exit))) } +/// Implementation of [`ApplicationHandler`] for main program mode. +struct Program { + args: ProgramArgs, +} + +impl Program { + async fn error(&self, msg: impl Into) { + // Show error window. + let win = ErrorWindow::new().unwrap(); + + win.set_message(msg.into()); + win.on_close({ + let win = win.as_weak(); + + move || win.unwrap().hide().unwrap() + }); + + win.show(); + + todo!() + } +} + +impl ApplicationHandler for Program { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + slint::platform::set_platform(Box::new(SlintBackend::new())).unwrap(); + + todo!() + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + window_id: WindowId, + event: WindowEvent, + ) { + todo!() + } +} + +/// Event to wakeup UI event loop. +enum ProgramEvent {} + /// Program arguments parsed from command line. #[derive(Parser)] #[command(about = None)] @@ -349,9 +390,6 @@ enum ProgramMode { /// Represents an error when our program fails. #[derive(Debug, Error)] enum ProgramError { - #[error("couldn't spawn panic handler process")] - SpawnPanicHandler(#[source] std::io::Error), - #[cfg(unix)] #[error("couldn't increase file descriptor limit")] FdLimit(#[source] self::rlim::RlimitError), @@ -371,9 +409,6 @@ enum ProgramError { #[error("couldn't save default profile")] SaveDefaultProfile(#[source] self::profile::SaveError), - #[error("couldn't get application executable path")] - GetCurrentExePath(#[source] std::io::Error), - #[error("failed to start debug server on {1}")] StartDebugServer( #[source] debug::StartDebugServerError, @@ -394,16 +429,4 @@ enum ProgramError { #[error("couldn't create VMM screen")] CreateScreen(#[source] Box), - - #[error("couldn't create VMM event loop")] - CreateVmmEventLoop(#[source] EventLoopError), - - #[error("couldn't start VMM")] - StartVmm(#[source] VmmError), - - #[error("couldn't run VMM screen")] - RunScreen(#[source] Box), - - #[error("couldn't read panic info")] - ReadPanicInfo(#[source] ciborium::de::Error), } diff --git a/gui/src/panic/mod.rs b/gui/src/panic/mod.rs index 0f44a41fe..0b35d01cb 100644 --- a/gui/src/panic/mod.rs +++ b/gui/src/panic/mod.rs @@ -1,21 +1,20 @@ use crate::ui::ErrorWindow; -use crate::ProgramError; +use erdp::ErrorDisplay; use serde::{Deserialize, Serialize}; use slint::ComponentHandle; use std::borrow::Cow; use std::io::Write; use std::panic::PanicHookInfo; use std::path::Path; -use std::process::{Child, Command, Stdio}; +use std::process::{Child, Command, ExitCode, Stdio}; use std::sync::Mutex; -pub fn spawn_handler(exe: &Path) -> Result<(), ProgramError> { +pub fn spawn_handler(exe: &Path) -> Result<(), std::io::Error> { // Spawn the process in panic handler mode. let ph = Command::new(exe) .args(["--mode", "panic-handler"]) .stdin(Stdio::piped()) - .spawn() - .map_err(ProgramError::SpawnPanicHandler)?; + .spawn()?; // Set panic hook to send panic to the handler. let ph = Mutex::new(Some(PanicHandler(ph))); @@ -25,24 +24,33 @@ pub fn spawn_handler(exe: &Path) -> Result<(), ProgramError> { Ok(()) } -pub fn run_handler() -> Result<(), ProgramError> { +pub fn run_handler() -> ExitCode { use std::io::ErrorKind; // Wait for panic info. let stdin = std::io::stdin(); let mut stdin = stdin.lock(); - let info: PanicInfo = match ciborium::from_reader(&mut stdin) { - Ok(v) => v, - Err(ciborium::de::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => return Ok(()), - Err(e) => return Err(ProgramError::ReadPanicInfo(e)), + let (msg, exit) = match ciborium::from_reader::(&mut stdin) { + Ok(v) => { + let m = format!( + "An unexpected error has occurred at {}:{}: {}.", + v.file, v.line, v.message + ); + + (m, ExitCode::SUCCESS) + } + Err(ciborium::de::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => { + return ExitCode::SUCCESS; + } + Err(e) => { + let m = format!("Failed to read panic info: {}.", e.display()); + + (m, ExitCode::FAILURE) + } }; - // Display panic info. + // Display error window. let win = ErrorWindow::new().unwrap(); - let msg = format!( - "An unexpected error has occurred at {}:{}: {}.", - info.file, info.line, info.message - ); win.set_message(msg.into()); win.on_close({ @@ -53,7 +61,7 @@ pub fn run_handler() -> Result<(), ProgramError> { win.run().unwrap(); - Ok(()) + exit } fn panic_hook(i: &PanicHookInfo, ph: &Mutex>) { diff --git a/gui/src/ui/backend/mod.rs b/gui/src/ui/backend/mod.rs new file mode 100644 index 000000000..1970948a2 --- /dev/null +++ b/gui/src/ui/backend/mod.rs @@ -0,0 +1,20 @@ +use slint::platform::WindowAdapter; +use slint::PlatformError; +use std::rc::Rc; + +/// Back-end for Slint to run on top of winit event loop. +/// +/// This back-end does not supports [`slint::run_event_loop()`]. +pub struct SlintBackend {} + +impl SlintBackend { + pub fn new() -> Self { + Self {} + } +} + +impl slint::platform::Platform for SlintBackend { + fn create_window_adapter(&self) -> Result, PlatformError> { + todo!() + } +} diff --git a/gui/src/ui/mod.rs b/gui/src/ui/mod.rs index ecc0a3586..105299b56 100644 --- a/gui/src/ui/mod.rs +++ b/gui/src/ui/mod.rs @@ -1,5 +1,7 @@ +pub use self::backend::*; pub use self::profile::*; +mod backend; mod profile; // This macro includes the generated Rust code from .slint files