Repository: obhq/obliteration (language: rust)

Issue:
Center all top-level windows
Only for Windows and macOS is enough since Wayland does not allow the application to position itself.


Discussion / hints:

Fix patch:
diff --git a/gui/src/graphics/metal/engine.rs b/gui/src/graphics/metal/engine.rs
index 62014474f..737197caa 100644
--- a/gui/src/graphics/metal/engine.rs
+++ b/gui/src/graphics/metal/engine.rs
@@ -6,11 +6,11 @@ use thiserror::Error;
 /// Implementation of [`Graphics`] using Metal.
 ///
 /// Fields in this struct need to be dropped in a correct order.
-pub struct MetalScreen {
+pub struct Metal {
     device: Device,
 }
 
-impl MetalScreen {
+impl Metal {
     pub fn new() -> Result<Self, MetalError> {
         // Get Metal device.
         let device = match Device::system_default() {
@@ -22,7 +22,7 @@ impl MetalScreen {
     }
 
     /// # Safety
-    /// The returned [`MetalLayer`] must be dropped before this [`MetalScreen`].
+    /// The returned [`MetalLayer`] must be dropped before this [`Metal`].
     pub unsafe fn create_layer(&self) -> MetalLayer {
         let layer = MetalLayer::new();
 
@@ -31,9 +31,9 @@ impl MetalScreen {
     }
 }
 
-impl Graphics for MetalScreen {}
+impl Graphics for Metal {}
 
-/// Represents an error when [`MetalScreen::new()`] fails.
+/// Represents an error when [`Metal::new()`] fails.
 #[derive(Debug, Error)]
 pub enum MetalError {
     #[error("couldn't get default MTLDevice")]
diff --git a/gui/src/graphics/metal/mod.rs b/gui/src/graphics/metal/mod.rs
index 57d152e2b..fa862046f 100644
--- a/gui/src/graphics/metal/mod.rs
+++ b/gui/src/graphics/metal/mod.rs
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: MIT OR Apache-2.0
-use self::engine::MetalScreen;
+use self::engine::Metal;
 use super::EngineBuilder;
 use crate::profile::Profile;
 use metal::Device;
@@ -24,7 +24,7 @@ struct MetalBuilder {
 
 impl EngineBuilder for MetalBuilder {
     type PhysicalDevice = metal::Device;
-    type Engine = MetalScreen;
+    type Engine = Metal;
 
     fn physical_devices(&self) -> &[Self::PhysicalDevice] {
         &self.devices
diff --git a/gui/src/graphics/metal/window.rs b/gui/src/graphics/metal/window.rs
index e8cd4b04b..4385ba9b2 100644
--- a/gui/src/graphics/metal/window.rs
+++ b/gui/src/graphics/metal/window.rs
@@ -1,4 +1,4 @@
-use super::MetalScreen;
+use super::Metal;
 use crate::rt::{Hook, RuntimeWindow};
 use metal::{CAMetalLayer, MetalLayer};
 use objc::runtime::{Object, NO, YES};
@@ -18,15 +18,15 @@ pub struct MetalWindow {
     view: *mut Object,
     layer: MetalLayer,
     window: Window,
-    screen: Arc<MetalScreen>,
+    engine: Arc<Metal>,
 }
 
 impl MetalWindow {
     pub fn new(
-        screen: &Arc<MetalScreen>,
+        engine: &Arc<Metal>,
         window: Window,
     ) -> Result<Rc<Self>, Box<dyn Error + Send + Sync>> {
-        let layer = unsafe { screen.create_layer() };
+        let layer = unsafe { engine.create_layer() };
         let view = match window.raw_window_handle() {
             RawWindowHandle::AppKit(v) => v.ns_view as *mut Object,
             _ => unreachable!(),
@@ -39,7 +39,7 @@ impl MetalWindow {
             view,
             layer,
             window,
-            screen: screen.clone(),
+            engine: engine.clone(),
         }))
     }
 }
diff --git a/gui/src/graphics/vulkan/engine.rs b/gui/src/graphics/vulkan/engine.rs
index 955d61b3b..333c9648b 100644
--- a/gui/src/graphics/vulkan/engine.rs
+++ b/gui/src/graphics/vulkan/engine.rs
@@ -11,12 +11,12 @@ use winit::window::Window;
 /// Implementation of [`Graphics`] using Vulkan.
 ///
 /// Fields in this struct must be dropped in a correct order.
-pub struct VulkanScreen {
+pub struct Vulkan {
     device: Device,
     builder: VulkanBuilder,
 }
 
-impl VulkanScreen {
+impl Vulkan {
     pub fn new(b: VulkanBuilder, profile: &Profile) -> Result<Self, GraphicsError> {
         // TODO: Use selected device.
         let physical = b.devices.first().unwrap().device;
@@ -48,7 +48,7 @@ impl VulkanScreen {
     }
 
     /// # Safety
-    /// The returned [`SurfaceKHR`] must be destroyed before `win` and this [`VulkanScreen`].
+    /// The returned [`SurfaceKHR`] must be destroyed before `win` and this [`Vulkan`].
     pub unsafe fn create_surface(&self, win: &Window) -> Result<SurfaceKHR, ash::vk::Result> {
         let dh = win.raw_display_handle();
         let wh = win.raw_window_handle();
@@ -63,7 +63,7 @@ impl VulkanScreen {
     }
 }
 
-impl Drop for VulkanScreen {
+impl Drop for Vulkan {
     fn drop(&mut self) {
         // Free device.
         unsafe { self.device.device_wait_idle().unwrap() };
@@ -71,4 +71,4 @@ impl Drop for VulkanScreen {
     }
 }
 
-impl Graphics for VulkanScreen {}
+impl Graphics for Vulkan {}
diff --git a/gui/src/graphics/vulkan/mod.rs b/gui/src/graphics/vulkan/mod.rs
index 59c5ce938..e31aff335 100644
--- a/gui/src/graphics/vulkan/mod.rs
+++ b/gui/src/graphics/vulkan/mod.rs
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: MIT OR Apache-2.0
-use self::engine::VulkanScreen;
+use self::engine::Vulkan;
 use self::window::VulkanWindow;
 use super::EngineBuilder;
 use crate::profile::Profile;
@@ -106,7 +106,7 @@ struct VulkanBuilder {
 
 impl EngineBuilder for VulkanBuilder {
     type PhysicalDevice = PhysicalDevice;
-    type Engine = VulkanScreen;
+    type Engine = Vulkan;
 
     fn physical_devices(&self) -> &[Self::PhysicalDevice] {
         &self.devices
@@ -117,7 +117,7 @@ impl EngineBuilder for VulkanBuilder {
         profile: &Profile,
         screen: WindowAttributes,
     ) -> Result<Arc<Self::Engine>, GraphicsError> {
-        let engine = VulkanScreen::new(self, profile).map(Arc::new)?;
+        let engine = Vulkan::new(self, profile).map(Arc::new)?;
         let window = create_window(screen, |w| VulkanWindow::new(&engine, w))
             .map_err(GraphicsError::CreateWindow)?;
 
diff --git a/gui/src/graphics/vulkan/window.rs b/gui/src/graphics/vulkan/window.rs
index c3c9745a9..6d94c9c48 100644
--- a/gui/src/graphics/vulkan/window.rs
+++ b/gui/src/graphics/vulkan/window.rs
@@ -1,4 +1,4 @@
-use super::engine::VulkanScreen;
+use super::engine::Vulkan;
 use super::GraphicsError;
 use crate::rt::{Hook, RuntimeWindow};
 use ash::vk::SurfaceKHR;
@@ -16,12 +16,12 @@ use winit::window::{Window, WindowId};
 pub struct VulkanWindow {
     surface: SurfaceKHR,
     window: Window,
-    engine: Arc<VulkanScreen>,
+    engine: Arc<Vulkan>,
 }
 
 impl VulkanWindow {
     pub fn new(
-        engine: &Arc<VulkanScreen>,
+        engine: &Arc<Vulkan>,
         window: Window,
     ) -> Result<Rc<Self>, Box<dyn Error + Send + Sync>> {
         // Create VkSurfaceKHR.
diff --git a/gui/src/main.rs b/gui/src/main.rs
index debd42947..81cdd493f 100644
--- a/gui/src/main.rs
+++ b/gui/src/main.rs
@@ -5,7 +5,8 @@ use self::graphics::{EngineBuilder, GraphicsError, PhysicalDevice};
 use self::profile::{DisplayResolution, Profile};
 use self::setup::{run_setup, SetupError};
 use self::ui::{
-    MainWindow, ProfileModel, ResolutionModel, RuntimeExt, SlintBackend, WaitForDebugger,
+    MainWindow, PlatformExt, ProfileModel, ResolutionModel, RuntimeExt, SlintBackend,
+    WaitForDebugger,
 };
 use async_net::{TcpListener, TcpStream};
 use clap::{Parser, ValueEnum};
@@ -139,7 +140,7 @@ async fn run(args: ProgramArgs, exe: PathBuf) -> Result<(), ProgramError> {
 
     // Run setup wizard. This will simply return the data manager if the user already has required
     // settings.
-    let data = match run_setup().map_err(ProgramError::Setup)? {
+    let data = match run_setup().await.map_err(ProgramError::Setup)? {
         Some(v) => Arc::new(v),
         None => return Ok(()),
     };
@@ -331,6 +332,7 @@ async fn run_launcher(
     profiles.select(row, &win);
 
     // Run the window.
+    win.set_center().map_err(ProgramError::CenterMainWindow)?;
     win.show().map_err(ProgramError::ShowMainWindow)?;
     win.wait().await;
 
@@ -454,6 +456,9 @@ enum ProgramError {
     #[error("couldn't initialize graphics engine")]
     InitGraphics(#[source] GraphicsError),
 
+    #[error("couldn't center main window")]
+    CenterMainWindow(#[source] self::ui::PlatformError),
+
     #[error("couldn't show main window")]
     ShowMainWindow(#[source] slint::PlatformError),
 
diff --git a/gui/src/setup/mod.rs b/gui/src/setup/mod.rs
index 7aa2fd401..5873aad8f 100644
--- a/gui/src/setup/mod.rs
+++ b/gui/src/setup/mod.rs
@@ -3,7 +3,7 @@ pub use self::data::DataRootError;
 use self::data::{read_data_root, write_data_root};
 use crate::data::{DataError, DataMgr};
 use crate::dialogs::{open_dir, open_file, FileType};
-use crate::ui::SetupWizard;
+use crate::ui::{PlatformExt, RuntimeExt, SetupWizard};
 use crate::vfs::{FsType, FS_TYPE};
 use erdp::ErrorDisplay;
 use obfw::ps4::{PartData, PartReader};
@@ -22,7 +22,7 @@ use thiserror::Error;
 #[cfg_attr(target_os = "windows", path = "windows.rs")]
 mod data;
 
-pub fn run_setup() -> Result<Option<DataMgr>, SetupError> {
+pub async fn run_setup() -> Result<Option<DataMgr>, SetupError> {
     // Load data root.
     let root = read_data_root().map_err(SetupError::ReadDataRoot)?;
 
@@ -62,9 +62,7 @@ pub fn run_setup() -> Result<Option<DataMgr>, SetupError> {
     win.on_browse_data_root({
         let win = win.as_weak();
 
-        move || {
-            slint::spawn_local(browse_data_root(win.unwrap())).unwrap();
-        }
+        move || crate::rt::spawn(browse_data_root(win.unwrap()))
     });
 
     win.on_set_data_root({
@@ -76,9 +74,7 @@ pub fn run_setup() -> Result<Option<DataMgr>, SetupError> {
     win.on_browse_firmware({
         let win = win.as_weak();
 
-        move || {
-            slint::spawn_local(browse_firmware(win.unwrap())).unwrap();
-        }
+        move || crate::rt::spawn(browse_firmware(win.unwrap()))
     });
 
     win.on_install_firmware({
@@ -102,7 +98,9 @@ pub fn run_setup() -> Result<Option<DataMgr>, SetupError> {
     }
 
     // Run the wizard.
-    win.run().map_err(SetupError::RunWindow)?;
+    win.set_center().map_err(SetupError::CenterWindow)?;
+    win.show().map_err(SetupError::ShowWindow)?;
+    win.wait().await;
 
     drop(win);
 
@@ -518,6 +516,9 @@ pub enum SetupError {
     #[error("couldn't create setup wizard")]
     CreateWindow(#[source] PlatformError),
 
-    #[error("couldn't run setup wizard")]
-    RunWindow(#[source] PlatformError),
+    #[error("couldn't center setup wizard")]
+    CenterWindow(#[source] crate::ui::PlatformError),
+
+    #[error("couldn't show setup wizard")]
+    ShowWindow(#[source] PlatformError),
 }
diff --git a/gui/src/ui/backend/window.rs b/gui/src/ui/backend/window.rs
index 4ad28ac8d..bdf9e94a6 100644
--- a/gui/src/ui/backend/window.rs
+++ b/gui/src/ui/backend/window.rs
@@ -2,6 +2,9 @@ use crate::rt::{RuntimeWindow, Signal};
 use i_slint_core::window::WindowAdapterInternal;
 use i_slint_core::InternalToken;
 use i_slint_renderer_skia::SkiaRenderer;
+use raw_window_handle::{
+    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
+};
 use slint::platform::{PointerEventButton, Renderer, WindowAdapter, WindowEvent, WindowProperties};
 use slint::{LogicalPosition, LogicalSize, PhysicalSize, PlatformError, SharedString, WindowSize};
 use std::any::Any;
@@ -237,6 +240,14 @@ impl WindowAdapter for Window {
     fn internal(&self, _: InternalToken) -> Option<&dyn WindowAdapterInternal> {
         Some(self)
     }
+
+    fn window_handle_06(&self) -> Result<WindowHandle<'_>, HandleError> {
+        self.winit.window_handle()
+    }
+
+    fn display_handle_06(&self) -> Result<DisplayHandle<'_>, HandleError> {
+        self.winit.display_handle()
+    }
 }
 
 impl WindowAdapterInternal for Window {
diff --git a/gui/src/ui/linux/mod.rs b/gui/src/ui/linux/mod.rs
new file mode 100644
index 000000000..693c3b3d9
--- /dev/null
+++ b/gui/src/ui/linux/mod.rs
@@ -0,0 +1,25 @@
+use super::PlatformExt;
+use raw_window_handle::{HasWindowHandle, RawWindowHandle};
+use slint::ComponentHandle;
+use thiserror::Error;
+
+impl<T: ComponentHandle> PlatformExt for T {
+    fn set_center(&self) -> Result<(), PlatformError> {
+        let win = self.window().window_handle();
+        let win = win.window_handle().unwrap();
+
+        match win.as_ref() {
+            RawWindowHandle::Xlib(xlib_window_handle) => todo!(),
+            RawWindowHandle::Xcb(xcb_window_handle) => todo!(),
+            RawWindowHandle::Wayland(_) => (), // Wayland don't allow window to position itself.
+            RawWindowHandle::Drm(_) | RawWindowHandle::Gbm(_) => unimplemented!(),
+            _ => unreachable!(),
+        }
+
+        Ok(())
+    }
+}
+
+/// Linux-specific error for [`PlatformExt`].
+#[derive(Debug, Error)]
+pub enum PlatformError {}
diff --git a/gui/src/ui/macos/mod.rs b/gui/src/ui/macos/mod.rs
new file mode 100644
index 000000000..233eb145f
--- /dev/null
+++ b/gui/src/ui/macos/mod.rs
@@ -0,0 +1,13 @@
+use super::PlatformExt;
+use slint::ComponentHandle;
+use thiserror::Error;
+
+impl<T: ComponentHandle> PlatformExt for T {
+    fn set_center(&self) -> Result<(), PlatformError> {
+        todo!()
+    }
+}
+
+/// macOS-specific error for [`PlatformExt`].
+#[derive(Debug, Error)]
+pub enum PlatformError {}
diff --git a/gui/src/ui/mod.rs b/gui/src/ui/mod.rs
index ebfc08a0d..5e3d5ee99 100644
--- a/gui/src/ui/mod.rs
+++ b/gui/src/ui/mod.rs
@@ -1,4 +1,5 @@
 pub use self::backend::*;
+pub use self::os::PlatformError;
 pub use self::profile::*;
 
 use i_slint_core::window::WindowInner;
@@ -7,6 +8,10 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
 use slint::{ComponentHandle, SharedString};
 
 mod backend;
+#[cfg_attr(target_os = "linux", path = "linux/mod.rs")]
+#[cfg_attr(target_os = "macos", path = "macos/mod.rs")]
+#[cfg_attr(target_os = "windows", path = "windows/mod.rs")]
+mod os;
 mod profile;
 
 pub async fn error<T>(parent: Option<&T>, msg: impl Into<SharedString>)
@@ -22,10 +27,16 @@ where
         move || win.unwrap().hide().unwrap()
     });
 
+    win.set_center().unwrap();
     win.show().unwrap();
     win.wait().await;
 }
 
+/// Provides platform-specific methods for [`ComponentHandle`].
+pub trait PlatformExt: ComponentHandle {
+    fn set_center(&self) -> Result<(), PlatformError>;
+}
+
 /// Provides methods for [`ComponentHandle`] to work with our async runtime.
 pub trait RuntimeExt: ComponentHandle {
     async fn wait(&self);
diff --git a/gui/src/ui/windows/mod.rs b/gui/src/ui/windows/mod.rs
new file mode 100644
index 000000000..feb1848f8
--- /dev/null
+++ b/gui/src/ui/windows/mod.rs
@@ -0,0 +1,13 @@
+use super::PlatformExt;
+use slint::ComponentHandle;
+use thiserror::Error;
+
+impl<T: ComponentHandle> PlatformExt for T {
+    fn set_center(&self) -> Result<(), PlatformError> {
+        todo!()
+    }
+}
+
+/// Windows-specific error for [`PlatformExt`].
+#[derive(Debug, Error)]
+pub enum PlatformError {}


Test patch: