Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revises create_window #1224

Merged
merged 2 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ block = "0.1.6"
core-foundation = "0.10.0"
core-foundation-sys = "0.8.7"
core-graphics-types = "0.1.3"
i-slint-renderer-skia = { version = "=1.9.0" }
i-slint-renderer-skia = "=1.9.0"
metal = "0.29.0"
objc = "0.2.7"

Expand Down
23 changes: 4 additions & 19 deletions gui/src/graphics/metal/window.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use super::Metal;
use crate::rt::{Hook, RuntimeWindow};
use crate::rt::{Hook, WindowHandler};
use metal::{CAMetalLayer, MetalLayer};
use objc::runtime::{Object, NO, YES};
use objc::{msg_send, sel, sel_impl};
use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
};
use rwh05::{HasRawWindowHandle, RawWindowHandle};
use std::error::Error;
use std::ptr::null_mut;
Expand All @@ -16,7 +13,7 @@ use winit::event::{DeviceId, ElementState, InnerSizeWriter, MouseButton, StartCa
use winit::event_loop::ControlFlow;
use winit::window::{Window, WindowId};

/// Implementation of [`RuntimeWindow`] and [`Hook`] for Metal.
/// Implementation of [`WindowHandler`] and [`Hook`] for Metal.
pub struct MetalWindow {
view: *mut Object,
layer: MetalLayer,
Expand Down Expand Up @@ -55,8 +52,8 @@ impl Drop for MetalWindow {
}
}

impl RuntimeWindow for MetalWindow {
fn id(&self) -> WindowId {
impl WindowHandler for MetalWindow {
fn window_id(&self) -> WindowId {
self.window.id()
}

Expand Down Expand Up @@ -106,18 +103,6 @@ impl RuntimeWindow for MetalWindow {
}
}

impl HasDisplayHandle for MetalWindow {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.window.display_handle()
}
}

impl HasWindowHandle for MetalWindow {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
self.window.window_handle()
}
}

impl Hook for MetalWindow {
fn new_events(&self, cause: &StartCause) -> Result<(), Box<dyn Error + Send + Sync>> {
todo!()
Expand Down
10 changes: 6 additions & 4 deletions gui/src/graphics/vulkan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use self::engine::Vulkan;
use self::window::VulkanWindow;
use super::EngineBuilder;
use crate::profile::Profile;
use crate::rt::{create_window, raw_display_handle, RuntimeError};
use crate::rt::{create_window, raw_display_handle};
use ash::extensions::khr::Surface;
use ash::vk::{ApplicationInfo, InstanceCreateInfo, QueueFlags, API_VERSION_1_3};
use ash::{Entry, Instance};
use ash_window::enumerate_required_extensions;
use std::ffi::CStr;
use std::mem::ManuallyDrop;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use thiserror::Error;
Expand Down Expand Up @@ -120,9 +121,10 @@ impl EngineBuilder for VulkanBuilder {
shutdown: &Arc<AtomicBool>,
) -> Result<Arc<Self::Engine>, GraphicsError> {
let engine = Vulkan::new(self, profile).map(Arc::new)?;
let window = create_window(screen, |w| VulkanWindow::new(&engine, w, shutdown))
.map_err(GraphicsError::CreateWindow)?;
let window = create_window(screen).map_err(GraphicsError::CreateWindow)?;
let window = VulkanWindow::new(&engine, window, shutdown).map(Rc::new)?;

crate::rt::register_window(&window);
crate::rt::push_hook(window);

Ok(engine)
Expand Down Expand Up @@ -173,5 +175,5 @@ pub enum GraphicsError {
CreateSurface(#[source] ash::vk::Result),

#[error("couldn't create window")]
CreateWindow(#[source] RuntimeError),
CreateWindow(#[source] winit::error::OsError),
}
30 changes: 7 additions & 23 deletions gui/src/graphics/vulkan/window.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
use super::engine::Vulkan;
use super::GraphicsError;
use crate::rt::{Hook, RuntimeWindow};
use crate::rt::{Hook, WindowHandler};
use ash::vk::SurfaceKHR;
use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
};
use std::error::Error;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{DeviceId, ElementState, InnerSizeWriter, MouseButton, StartCause};
use winit::event_loop::ControlFlow;
use winit::window::{Window, WindowId};

/// Implementation of [`RuntimeWindow`] and [`Hook`] for Vulkan.
/// Implementation of [`WindowHandler`] and [`Hook`] for Vulkan.
///
/// Fields in this struct must be dropped in a correct order.
pub struct VulkanWindow {
Expand All @@ -29,17 +25,17 @@ impl VulkanWindow {
engine: &Arc<Vulkan>,
window: Window,
shutdown: &Arc<AtomicBool>,
) -> Result<Rc<Self>, Box<dyn Error + Send + Sync>> {
) -> Result<Self, GraphicsError> {
// Create VkSurfaceKHR.
let surface =
unsafe { engine.create_surface(&window) }.map_err(GraphicsError::CreateSurface)?;

Ok(Rc::new(Self {
Ok(Self {
surface,
window,
engine: engine.clone(),
shutdown: shutdown.clone(),
}))
})
}
}

Expand All @@ -49,8 +45,8 @@ impl Drop for VulkanWindow {
}
}

impl RuntimeWindow for VulkanWindow {
fn id(&self) -> WindowId {
impl WindowHandler for VulkanWindow {
fn window_id(&self) -> WindowId {
self.window.id()
}

Expand Down Expand Up @@ -103,18 +99,6 @@ impl RuntimeWindow for VulkanWindow {
}
}

impl HasDisplayHandle for VulkanWindow {
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
self.window.display_handle()
}
}

impl HasWindowHandle for VulkanWindow {
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
self.window.window_handle()
}
}

impl Hook for VulkanWindow {
fn new_events(&self, _: &StartCause) -> Result<(), Box<dyn Error + Send + Sync>> {
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fn main() -> ExitCode {
// Show error window.
let msg = format!("An unexpected error has occurred: {}.", e.display());

self::ui::error(msg).await;
self::ui::error::<MainWindow>(None, msg).await;

ExitCode::FAILURE
};
Expand Down
5 changes: 2 additions & 3 deletions gui/src/rt/context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::task::TaskList;
use super::{Event, Hook, RuntimeWindow};
use super::{Event, Hook, WindowHandler};
use rustc_hash::FxHashMap;
use std::any::{Any, TypeId};
use std::cell::Cell;
Expand All @@ -16,8 +16,7 @@ pub struct Context<'a> {
pub tasks: &'a mut TaskList,
pub objects: &'a mut FxHashMap<TypeId, Rc<dyn Any>>,
pub hooks: Option<&'a mut Vec<Rc<dyn Hook>>>,
pub windows: &'a mut FxHashMap<WindowId, Weak<dyn RuntimeWindow>>,
pub active: Option<&'a WindowId>,
pub windows: &'a mut FxHashMap<WindowId, Weak<dyn WindowHandler>>,
}

impl<'a> Context<'a> {
Expand Down
74 changes: 20 additions & 54 deletions gui/src/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ mod window;
/// [`std::task::Waker`]. Any pending futures that need to wakeup by an external event like I/O need
/// a dedicated thread to invoke [`std::task::Waker::wake()`] when the I/O is ready. That mean our
/// async executor will not work with Tokio by default.
///
/// To create a window, call [`create_window()`] from `main` future.
pub fn run<T: 'static>(main: impl Future<Output = T> + 'static) -> Result<T, RuntimeError> {
// Setup winit event loop.
let mut el = EventLoop::<Event>::with_user_event();
Expand All @@ -57,7 +59,6 @@ pub fn run<T: 'static>(main: impl Future<Output = T> + 'static) -> Result<T, Run
objects: HashMap::default(),
hooks: Vec::new(),
windows: HashMap::default(),
active: None,
exit,
};

Expand Down Expand Up @@ -86,47 +87,34 @@ pub fn raw_display_handle() -> RawDisplayHandle {
Context::with(|cx| cx.el.raw_display_handle())
}

/// Note that the runtime do **not** hold a strong reference to the [`RuntimeWindow`] returned from
/// `f`.
/// You need to call [`register_window()`] after this to receive events for the created window and
/// you should do it before the first `await` otherwise you may missed some initial events.
///
/// # Panics
/// - If called from the other thread than main thread.
/// - If called from `f`.
pub fn create_window<T: RuntimeWindow + 'static>(
attrs: WindowAttributes,
f: impl FnOnce(Window) -> Result<Rc<T>, Box<dyn Error + Send + Sync>>,
) -> Result<Rc<T>, RuntimeError> {
Context::with(move |cx| {
let win = cx
.el
.create_window(attrs)
.map_err(RuntimeError::CreateWinitWindow)?;
let id = win.id();
let win = f(win).map_err(RuntimeError::CreateRuntimeWindow)?;
let weak = Rc::downgrade(&win);

assert!(cx.windows.insert(id, weak).is_none());

Ok(win)
})
/// If called from the other thread than main thread.
pub fn create_window(attrs: WindowAttributes) -> Result<Window, OsError> {
Context::with(move |cx| cx.el.create_window(attrs))
}

/// Note that the runtime **do not** hold a strong reference to `win` and it will automatically
/// removed when the underlying winit window destroyed.
///
/// # Panics
/// If called from the other thread than main thread.
pub fn active_window() -> Option<Rc<dyn RuntimeWindow>> {
Context::with(|cx| {
cx.active
.and_then(|i| cx.windows.get(i))
.and_then(|w| w.upgrade())
})
/// - If called from the other thread than main thread.
/// - If the underlying winit window on `win` already registered.
pub fn register_window<T: WindowHandler + 'static>(win: &Rc<T>) {
let id = win.window_id();
let win = Rc::downgrade(win);

Context::with(move |cx| assert!(cx.windows.insert(id, win).is_none()));
}

/// All objects will be destroyed before event loop exit.
///
/// # Panics
/// - If called from the other thread than main thread.
/// - If object with the same type as `obj` already registered.
pub fn register(obj: Rc<dyn Any>) {
pub fn register_global(obj: Rc<dyn Any>) {
let id = obj.as_ref().type_id();

Context::with(move |cx| assert!(cx.objects.insert(id, obj).is_none()))
Expand Down Expand Up @@ -159,8 +147,7 @@ struct Runtime<T> {
main: u64,
objects: FxHashMap<TypeId, Rc<dyn Any>>,
hooks: Vec<Rc<dyn Hook>>,
windows: FxHashMap<WindowId, Weak<dyn RuntimeWindow>>,
active: Option<WindowId>,
windows: FxHashMap<WindowId, Weak<dyn WindowHandler>>,
exit: Rc<Cell<Option<Result<T, RuntimeError>>>>,
}

Expand Down Expand Up @@ -188,7 +175,6 @@ impl<T> Runtime<T> {
None
},
windows: &mut self.windows,
active: self.active.as_ref(),
};

// Poll the task.
Expand Down Expand Up @@ -222,7 +208,6 @@ impl<T> ApplicationHandler<Event> for Runtime<T> {
objects: &mut self.objects,
hooks: None,
windows: &mut self.windows,
active: self.active.as_ref(),
};

if let Err(e) = cx.run(|| {
Expand Down Expand Up @@ -265,7 +250,6 @@ impl<T> ApplicationHandler<Event> for Runtime<T> {
objects: &mut self.objects,
hooks: None,
windows: &mut self.windows,
active: self.active.as_ref(),
};

if let Err(e) = cx.run(|| {
Expand Down Expand Up @@ -314,17 +298,7 @@ impl<T> ApplicationHandler<Event> for Runtime<T> {
r
}
WindowEvent::Focused(v) => {
let r = dispatch!(w => { w.on_focused(v).map_err(RuntimeError::Focused) });

// TODO: Is it possible for the lost focus to deliver to the previous window after
// the new window got focused?
if v {
self.active = Some(id);
} else {
self.active = None;
}

r
dispatch!(w => { w.on_focused(v).map_err(RuntimeError::Focused) })
}
WindowEvent::CursorMoved {
device_id: dev,
Expand Down Expand Up @@ -365,7 +339,6 @@ impl<T> ApplicationHandler<Event> for Runtime<T> {
objects: &mut self.objects,
hooks: None,
windows: &mut self.windows,
active: self.active.as_ref(),
};

if let Err(e) = cx.run(|| {
Expand Down Expand Up @@ -394,7 +367,6 @@ impl<T> ApplicationHandler<Event> for Runtime<T> {
objects: &mut self.objects,
hooks: None,
windows: &mut self.windows,
active: self.active.as_ref(),
};

if let Err(e) = cx.run(|| {
Expand Down Expand Up @@ -453,12 +425,6 @@ pub enum RuntimeError {
#[error("couldn't run event loop")]
RunEventLoop(#[source] EventLoopError),

#[error("couldn't create winit window")]
CreateWinitWindow(#[source] OsError),

#[error("couldn't create runtime window")]
CreateRuntimeWindow(#[source] Box<dyn Error + Send + Sync>),

#[error("couldn't handle event loop wakeup event")]
NewEvents(#[source] Box<dyn Error + Send + Sync>),

Expand Down
5 changes: 2 additions & 3 deletions gui/src/rt/window.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::error::Error;
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::event::{DeviceId, ElementState, InnerSizeWriter, MouseButton};
Expand All @@ -7,8 +6,8 @@ use winit::window::WindowId;
/// Encapsulates winit window with window-specific logic.
///
/// The event loop will exit immediately if any method return an error.
pub trait RuntimeWindow: HasDisplayHandle + HasWindowHandle + 'static {
fn id(&self) -> WindowId;
pub trait WindowHandler {
fn window_id(&self) -> WindowId;
fn on_resized(&self, new: PhysicalSize<u32>) -> Result<(), Box<dyn Error + Send + Sync>>;
fn on_close_requested(&self) -> Result<(), Box<dyn Error + Send + Sync>>;
fn on_focused(&self, gained: bool) -> Result<(), Box<dyn Error + Send + Sync>>;
Expand Down
2 changes: 1 addition & 1 deletion gui/src/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ fn set_data_root(win: SetupWizard) {

if input.is_empty() {
let msg = SharedString::from("You need to choose where to store data before proceed.");
crate::rt::spawn(error(msg));
crate::rt::spawn(async move { error(Some(&win), msg).await });
return;
}

Expand Down
Loading