From 8ffb5131c75fca2a72e259c53fe953413f49b51f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Mar 2023 14:18:11 +0100 Subject: [PATCH] Introduce error handling in the FemtoVG and Skia renderers (#2402) Avoid unwrap() and expect() and instead propagate errors all the way down to run_event_loop(), show(), and hide() in the Slint AIP. --- api/cpp/platform.rs | 17 +- api/node/native/lib.rs | 2 +- api/rs/slint/docs.rs | 2 +- api/wasm-interpreter/src/lib.rs | 4 +- examples/todo/rust/main.rs | 2 +- internal/backends/qt/qt_window.rs | 6 +- internal/backends/testing/lib.rs | 3 +- internal/backends/winit/event_loop.rs | 230 +++++++++++------- internal/backends/winit/glwindow.rs | 103 ++++---- internal/backends/winit/lib.rs | 11 +- internal/backends/winit/renderer/femtovg.rs | 53 ++-- .../winit/renderer/femtovg/glcontext.rs | 104 +++++--- internal/backends/winit/renderer/skia.rs | 25 +- internal/backends/winit/renderer/sw.rs | 40 ++- internal/compiler/generator/rust.rs | 4 +- internal/core/api.rs | 6 +- internal/core/component.rs | 2 +- internal/core/renderer.rs | 3 +- internal/core/software_renderer.rs | 3 +- internal/core/window.rs | 17 +- internal/interpreter/api.rs | 7 +- internal/interpreter/ffi.rs | 2 +- internal/renderers/femtovg/lib.rs | 19 +- internal/renderers/skia/d3d_surface.rs | 39 ++- internal/renderers/skia/lib.rs | 60 +++-- internal/renderers/skia/metal_surface.rs | 41 +++- internal/renderers/skia/opengl_surface.rs | 177 +++++++++----- tests/cases/examples/hello.slint | 2 +- 28 files changed, 627 insertions(+), 357 deletions(-) diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 1a2ce32b1..c9161e8b0 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -53,8 +53,9 @@ impl WindowAdapterSealed for CppWindowAdapter { unsafe { (self.show)(self.user_data) }; Ok(()) } - fn hide(&self) { + fn hide(&self) -> Result<(), PlatformError> { unsafe { (self.hide)(self.user_data) } + Ok(()) } fn request_redraw(&self) { @@ -230,7 +231,7 @@ pub unsafe extern "C" fn slint_skia_renderer_show_win32( RawWindowHandle::Win32(init_raw!(raw_window_handle::Win32WindowHandle { hwnd, hinstance })), RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty()), ); - r.show(handle, PhysicalSize { width: size.width, height: size.height }) + r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap() } #[no_mangle] @@ -248,7 +249,7 @@ pub unsafe extern "C" fn slint_skia_renderer_show_x11( RawWindowHandle::Xcb(init_raw!(XcbWindowHandle { window, visual_id })), RawDisplayHandle::Xcb(init_raw!(XcbDisplayHandle { connection, screen })), ); - r.show(handle, PhysicalSize { width: size.width, height: size.height }) + r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap(); } #[no_mangle] @@ -265,7 +266,7 @@ pub unsafe extern "C" fn slint_skia_renderer_show_wayland( RawWindowHandle::Wayland(init_raw!(WaylandWindowHandle { surface })), RawDisplayHandle::Wayland(init_raw!(WaylandDisplayHandle { display })), ); - r.show(handle, PhysicalSize { width: size.width, height: size.height }) + r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap(); } #[no_mangle] @@ -282,25 +283,25 @@ pub unsafe extern "C" fn slint_skia_renderer_show_appkit( RawWindowHandle::AppKit(init_raw!(AppKitWindowHandle { ns_view, ns_window })), RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()), ); - r.show(handle, PhysicalSize { width: size.width, height: size.height }) + r.show(handle, PhysicalSize { width: size.width, height: size.height }).unwrap(); } #[no_mangle] pub unsafe extern "C" fn slint_skia_renderer_hide(r: SkiaRendererOpaque) { let r = &*(r as *const SkiaRenderer); - r.hide() + r.hide().unwrap() } #[no_mangle] pub unsafe extern "C" fn slint_skia_renderer_resize(r: SkiaRendererOpaque, size: IntSize) { let r = &*(r as *const SkiaRenderer); - r.resize_event(PhysicalSize { width: size.width, height: size.height }); + r.resize_event(PhysicalSize { width: size.width, height: size.height }).unwrap(); } #[no_mangle] pub unsafe extern "C" fn slint_skia_renderer_render(r: SkiaRendererOpaque, size: IntSize) { let r = &*(r as *const SkiaRenderer); - r.render(PhysicalSize { width: size.width, height: size.height }); + r.render(PhysicalSize { width: size.width, height: size.height }).unwrap(); } #[no_mangle] diff --git a/api/node/native/lib.rs b/api/node/native/lib.rs index a81e0fd1c..f28f02216 100644 --- a/api/node/native/lib.rs +++ b/api/node/native/lib.rs @@ -498,7 +498,7 @@ declare_types! { let this = cx.this(); let window = cx.borrow(&this, |x| x.0.as_ref().cloned()); let window_adapter = window.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; - window_adapter.hide(); + window_adapter.hide().unwrap(); Ok(JsUndefined::new().as_value(&mut cx)) } diff --git a/api/rs/slint/docs.rs b/api/rs/slint/docs.rs index 2e0874955..31e2d5f38 100644 --- a/api/rs/slint/docs.rs +++ b/api/rs/slint/docs.rs @@ -110,7 +110,7 @@ pub mod generated_code { /// Marks the window of this component to be hidden on the screen. This de-registers /// the window from the windowing system and it will not receive any further events. - fn hide(&self) { + fn hide(&self) -> Result<(), crate::PlatformError> { unimplemented!(); } diff --git a/api/wasm-interpreter/src/lib.rs b/api/wasm-interpreter/src/lib.rs index 913522e5c..6f6e95e74 100644 --- a/api/wasm-interpreter/src/lib.rs +++ b/api/wasm-interpreter/src/lib.rs @@ -191,8 +191,8 @@ impl WrappedInstance { } /// Hides this instance and prevents further updates of the canvas element. #[wasm_bindgen] - pub fn hide(&self) { - self.0.hide(); + pub fn hide(&self) -> Result<(), JsValue> { + self.0.hide().map_err(|e| -> JsValue { format!("{e}").into() }) } /// THIS FUNCTION IS NOT PART THE PUBLIC API! diff --git a/examples/todo/rust/main.rs b/examples/todo/rust/main.rs index 03a439020..4eef187fa 100644 --- a/examples/todo/rust/main.rs +++ b/examples/todo/rust/main.rs @@ -48,7 +48,7 @@ pub fn main() { let weak_window = main_window.as_weak(); main_window.on_popup_confirmed(move || { let window = weak_window.unwrap(); - window.hide(); + window.hide().unwrap(); }); { diff --git a/internal/backends/qt/qt_window.rs b/internal/backends/qt/qt_window.rs index c1ffba873..cc681a91b 100644 --- a/internal/backends/qt/qt_window.rs +++ b/internal/backends/qt/qt_window.rs @@ -1498,7 +1498,7 @@ impl WindowAdapterSealed for QtWindow { Ok(()) } - fn hide(&self) { + fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError> { self.rendering_metrics_collector.take(); let widget_ptr = self.widget_ptr(); cpp! {unsafe [widget_ptr as "QWidget*"] { @@ -1507,6 +1507,7 @@ impl WindowAdapterSealed for QtWindow { // visible windows, and ends the application if needed QEventLoopLocker(); }}; + Ok(()) } fn request_redraw(&self) { @@ -1930,9 +1931,10 @@ impl Renderer for QtWindow { &self, component: ComponentRef, _items: &mut dyn Iterator>>, - ) { + ) -> Result<(), i_slint_core::platform::PlatformError> { // Invalidate caches: self.cache.component_destroyed(component); + Ok(()) } } diff --git a/internal/backends/testing/lib.rs b/internal/backends/testing/lib.rs index 0354f0b1e..ae09ee489 100644 --- a/internal/backends/testing/lib.rs +++ b/internal/backends/testing/lib.rs @@ -60,8 +60,9 @@ impl WindowAdapterSealed for TestingWindow { Ok(()) } - fn hide(&self) { + fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError> { self.shown.set(false); + Ok(()) } fn renderer(&self) -> &dyn Renderer { diff --git a/internal/backends/winit/event_loop.rs b/internal/backends/winit/event_loop.rs index a7bd2d7f8..ce2f0556e 100644 --- a/internal/backends/winit/event_loop.rs +++ b/internal/backends/winit/event_loop.rs @@ -31,7 +31,7 @@ pub(crate) static QUIT_ON_LAST_WINDOW_CLOSED: std::sync::atomic::AtomicBool = pub trait WinitWindow: WindowAdapter { fn currently_pressed_key_code(&self) -> &Cell>; /// Returns true if during the drawing request_redraw() was called. - fn draw(&self) -> bool; + fn draw(&self) -> Result; fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window)); fn constraints(&self) -> (corelib::layout::LayoutInfo, corelib::layout::LayoutInfo); fn set_constraints( @@ -40,7 +40,12 @@ pub trait WinitWindow: WindowAdapter { ); /// Called by the event loop when a WindowEvent::Resized is received. - fn resize_event(&self, _size: winit::dpi::PhysicalSize) {} + fn resize_event( + &self, + _size: winit::dpi::PhysicalSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { + Ok(()) + } /// Return true if the proxy element used for input method has the focus fn input_method_focused(&self) -> bool { @@ -267,15 +272,15 @@ fn process_window_event( event: WindowEvent, cursor_pos: &mut LogicalPoint, pressed: &mut bool, -) { +) -> Result<(), i_slint_core::platform::PlatformError> { let runtime_window = WindowInner::from_pub(window.window()); match event { WindowEvent::Resized(size) => { - window.resize_event(size); + window.resize_event(size)?; } WindowEvent::CloseRequested => { if runtime_window.request_close() { - window.hide(); + window.hide()?; } } WindowEvent::ReceivedCharacter(ch) => { @@ -293,7 +298,7 @@ fn process_window_event( { ch } else { - return; + return Ok(()); } } else { ch @@ -437,7 +442,7 @@ fn process_window_event( runtime_window.set_window_item_geometry(LogicalSize::new(size.width, size.height)); runtime_window.set_scale_factor(scale_factor as f32); // Resize the underlying graphics surface - window.resize_event(*new_inner_size); + window.resize_event(*new_inner_size)?; } } WindowEvent::ThemeChanged(theme) => { @@ -445,6 +450,7 @@ fn process_window_event( } _ => {} } + Ok(()) } /// Runs the event loop and renders the items in the provided `component` in its @@ -493,110 +499,142 @@ pub fn run() -> Result<(), corelib::platform::PlatformError> { let mut cursor_pos = LogicalPoint::default(); let mut pressed = false; - let mut run_fn = move |event: Event, control_flow: &mut ControlFlow| match event { - Event::WindowEvent { event, window_id } => { - if let Some(window) = window_by_id(window_id) { - process_window_event(window, event, &mut cursor_pos, &mut pressed); - }; - } + let outer_event_loop_error = Rc::new(RefCell::new(None)); + let inner_event_loop_error = outer_event_loop_error.clone(); - Event::RedrawRequested(id) => { - if let Some(window) = window_by_id(id) { - if let Ok(pos) = windows_with_pending_redraw_requests.binary_search(&id) { - windows_with_pending_redraw_requests.remove(pos); - } - let redraw_requested_during_draw = window.draw(); - if redraw_requested_during_draw { - // If during rendering a new redraw_request() was issued (for example in a rendering notifier callback), then - // pretend that an animation is running, so that we return Poll from the event loop to ensure a repaint as - // soon as possible. - *control_flow = ControlFlow::Poll; - } + let mut run_fn = move |event: Event, control_flow: &mut ControlFlow| { + match event { + Event::WindowEvent { event, window_id } => { + if let Some(window) = window_by_id(window_id) { + *inner_event_loop_error.borrow_mut() = + process_window_event(window, event, &mut cursor_pos, &mut pressed).err(); + }; } - } - Event::UserEvent(CustomEvent::UpdateWindowProperties(window_id)) => { - if let Err(insert_pos) = windows_with_pending_property_updates.binary_search(&window_id) - { - windows_with_pending_property_updates.insert(insert_pos, window_id); - } - } - Event::UserEvent(CustomEvent::WindowHidden) => { - if QUIT_ON_LAST_WINDOW_CLOSED.load(std::sync::atomic::Ordering::Relaxed) { - let window_count = ALL_WINDOWS.with(|windows| windows.borrow().len()); - if window_count == 0 { - *control_flow = ControlFlow::Exit; - } - } - } - - Event::UserEvent(CustomEvent::Exit) => { - *control_flow = ControlFlow::Exit; - } - - Event::UserEvent(CustomEvent::UserEvent(user)) => { - user(); - } - - #[cfg(target_arch = "wasm32")] - Event::UserEvent(CustomEvent::WakeEventLoopWorkaround) => { - *control_flow = ControlFlow::Poll; - } - - Event::NewEvents(_) => { - *control_flow = ControlFlow::Wait; - - windows_with_pending_redraw_requests.clear(); - ALL_WINDOWS.with(|windows| { - for (window_id, window_weak) in windows.borrow().iter() { - if window_weak.upgrade().map_or(false, |window| window.take_pending_redraw()) { - if let Err(insert_pos) = - windows_with_pending_redraw_requests.binary_search(window_id) - { - windows_with_pending_redraw_requests.insert(insert_pos, *window_id); + Event::RedrawRequested(id) => { + if let Some(window) = window_by_id(id) { + if let Ok(pos) = windows_with_pending_redraw_requests.binary_search(&id) { + windows_with_pending_redraw_requests.remove(pos); + } + match window.draw() { + Ok(redraw_requested_during_draw) => { + if redraw_requested_during_draw { + // If during rendering a new redraw_request() was issued (for example in a rendering notifier callback), then + // pretend that an animation is running, so that we return Poll from the event loop to ensure a repaint as + // soon as possible. + *control_flow = ControlFlow::Poll; + } } + Err(rendering_error) => { + *inner_event_loop_error.borrow_mut() = Some(rendering_error) + } + }; + } + } + + Event::UserEvent(CustomEvent::UpdateWindowProperties(window_id)) => { + if let Err(insert_pos) = + windows_with_pending_property_updates.binary_search(&window_id) + { + windows_with_pending_property_updates.insert(insert_pos, window_id); + } + } + Event::UserEvent(CustomEvent::WindowHidden) => { + if QUIT_ON_LAST_WINDOW_CLOSED.load(std::sync::atomic::Ordering::Relaxed) { + let window_count = ALL_WINDOWS.with(|windows| windows.borrow().len()); + if window_count == 0 { + *control_flow = ControlFlow::Exit; } } - }); - - corelib::platform::update_timers_and_animations(); - } - - Event::MainEventsCleared => { - for window in windows_with_pending_property_updates.drain(..).filter_map(window_by_id) { - WindowInner::from_pub(window.window()).update_window_properties(); } - } - Event::RedrawEventsCleared => { - if *control_flow != ControlFlow::Exit - && ALL_WINDOWS.with(|windows| { - windows.borrow().iter().any(|(_, w)| { - w.upgrade().map_or(false, |w| w.window().has_active_animations()) - }) - }) - { + Event::UserEvent(CustomEvent::Exit) => { + *control_flow = ControlFlow::Exit; + } + + Event::UserEvent(CustomEvent::UserEvent(user)) => { + user(); + } + + #[cfg(target_arch = "wasm32")] + Event::UserEvent(CustomEvent::WakeEventLoopWorkaround) => { *control_flow = ControlFlow::Poll; } - for window in windows_with_pending_redraw_requests.drain(..).filter_map(window_by_id) { - let redraw_requested_during_draw = window.draw(); - if redraw_requested_during_draw { - // If during rendering a new redraw_request() was issued (for example in a rendering notifier callback), then - // pretend that an animation is running, so that we return Poll from the event loop to ensure a repaint as - // soon as possible. + Event::NewEvents(_) => { + *control_flow = ControlFlow::Wait; + + windows_with_pending_redraw_requests.clear(); + ALL_WINDOWS.with(|windows| { + for (window_id, window_weak) in windows.borrow().iter() { + if window_weak + .upgrade() + .map_or(false, |window| window.take_pending_redraw()) + { + if let Err(insert_pos) = + windows_with_pending_redraw_requests.binary_search(window_id) + { + windows_with_pending_redraw_requests.insert(insert_pos, *window_id); + } + } + } + }); + + corelib::platform::update_timers_and_animations(); + } + + Event::MainEventsCleared => { + for window in + windows_with_pending_property_updates.drain(..).filter_map(window_by_id) + { + WindowInner::from_pub(window.window()).update_window_properties(); + } + } + + Event::RedrawEventsCleared => { + if *control_flow != ControlFlow::Exit + && ALL_WINDOWS.with(|windows| { + windows.borrow().iter().any(|(_, w)| { + w.upgrade().map_or(false, |w| w.window().has_active_animations()) + }) + }) + { *control_flow = ControlFlow::Poll; } - } - if *control_flow == ControlFlow::Wait { - if let Some(next_timer) = corelib::platform::duration_until_next_timer_update() { - *control_flow = ControlFlow::WaitUntil(instant::Instant::now() + next_timer); + for window in + windows_with_pending_redraw_requests.drain(..).filter_map(window_by_id) + { + match window.draw() { + Ok(redraw_requested_during_draw) => { + if redraw_requested_during_draw { + // If during rendering a new redraw_request() was issued (for example in a rendering notifier callback), then + // pretend that an animation is running, so that we return Poll from the event loop to ensure a repaint as + // soon as possible. + *control_flow = ControlFlow::Poll; + } + } + Err(rendering_error) => { + *inner_event_loop_error.borrow_mut() = Some(rendering_error); + } + } + } + + if *control_flow == ControlFlow::Wait { + if let Some(next_timer) = corelib::platform::duration_until_next_timer_update() + { + *control_flow = + ControlFlow::WaitUntil(instant::Instant::now() + next_timer); + } } } - } - _ => (), + _ => (), + }; + + if inner_event_loop_error.borrow().is_some() { + *control_flow = ControlFlow::Exit; + } }; #[cfg(not(target_arch = "wasm32"))] @@ -620,6 +658,10 @@ pub fn run() -> Result<(), corelib::platform::PlatformError> { // Winit does not support creating multiple instances of the event loop. let nre = NotRunningEventLoop { clipboard, instance: winit_loop, event_loop_proxy }; MAYBE_LOOP_INSTANCE.with(|loop_instance| *loop_instance.borrow_mut() = Some(nre)); + + if let Some(error) = outer_event_loop_error.borrow_mut().take() { + return Err(error); + } Ok(()) } diff --git a/internal/backends/winit/glwindow.rs b/internal/backends/winit/glwindow.rs index 99ed2233c..d6c0a19ff 100644 --- a/internal/backends/winit/glwindow.rs +++ b/internal/backends/winit/glwindow.rs @@ -145,37 +145,43 @@ impl GLWindow { } } - fn unmap(&self) { + fn unmap(&self) -> Result<(), PlatformError> { let old_mapped = match self.map_state.replace(GraphicsWindowBackendState::Unmapped { requested_position: None, requested_size: None, }) { - GraphicsWindowBackendState::Unmapped { .. } => return, + GraphicsWindowBackendState::Unmapped { .. } => return Ok(()), GraphicsWindowBackendState::Mapped(old_mapped) => old_mapped, }; crate::event_loop::unregister_window(old_mapped.winit_window.id()); - self.renderer.hide(); + self.renderer.hide() } - fn call_with_event_loop(&self, callback: fn(&Self)) { + fn call_with_event_loop( + &self, + callback: fn(&Self) -> Result<(), PlatformError>, + ) -> Result<(), PlatformError> { // With wasm, winit's `run()` consumes the event loop and access to it from outside the event handler yields // loop and thus ends up trying to create a new event loop instance, which panics in winit. Instead, forward // the call to be invoked from within the event loop #[cfg(target_arch = "wasm32")] - corelib::api::invoke_from_event_loop({ + return corelib::api::invoke_from_event_loop({ let self_weak = send_wrapper::SendWrapper::new(self.self_weak.clone()); move || { if let Some(this) = self_weak.take().upgrade() { - callback(&this) + // Can't propagate the returned error because we're in an async callback, so throw. + callback(&this).unwrap() } } }) - .unwrap(); + .map_err(|_| { + format!("internal error in winit backend: invoke_from_event_loop failed").into() + }); #[cfg(not(target_arch = "wasm32"))] - callback(self) + return callback(self); } } @@ -189,17 +195,17 @@ impl WinitWindow for GLWindow bool { + fn draw(&self) -> Result { let window = match self.borrow_mapped_window() { Some(window) => window, - None => return false, // caller bug, doesn't make sense to call draw() when not mapped + None => return Ok(false), // caller bug, doesn't make sense to call draw() when not mapped }; self.pending_redraw.set(false); - self.renderer.render(physical_size_to_slint(&window.winit_window.inner_size())); + self.renderer.render(physical_size_to_slint(&window.winit_window.inner_size()))?; - self.pending_redraw.get() + Ok(self.pending_redraw.get()) } fn with_window_handle(&self, callback: &mut dyn FnMut(&winit::window::Window)) { @@ -231,7 +237,7 @@ impl WinitWindow for GLWindow) { + fn resize_event(&self, size: winit::dpi::PhysicalSize) -> Result<(), PlatformError> { // slint::Window::set_size will call set_size() on this type, which would call // set_inner_size on the winit Window. On Windows that triggers an new resize event // in the next event loop iteration for mysterious reasons, with slightly different sizes. @@ -248,7 +254,9 @@ impl WinitWindow for GLWindow 0 && size.height > 0 { let physical_size = physical_size_to_slint(&size); self.window.set_size(physical_size); - self.renderer.resize_event(physical_size); + self.renderer.resize_event(physical_size) + } else { + Ok(()) } } @@ -282,8 +290,10 @@ impl WindowAdapterSealed for GLWind ) }) .ok(); - }) - }); + }); + Ok(()) // Doesn't matter if the eventloop is already closed, nothing to update then. + }) + .ok(); } fn apply_window_properties(&self, window_item: Pin<&i_slint_core::items::WindowItem>) { @@ -402,7 +412,7 @@ impl WindowAdapterSealed for GLWind GraphicsWindowBackendState::Unmapped { requested_position, requested_size } => { (requested_position.clone(), requested_size.clone()) } - GraphicsWindowBackendState::Mapped(_) => return, + GraphicsWindowBackendState::Mapped(_) => return Ok(()), }; let mut window_builder = winit::window::WindowBuilder::new(); @@ -460,21 +470,34 @@ impl WindowAdapterSealed for GLWind ); #[cfg(target_arch = "wasm32")] - { + let html_canvas = { use wasm_bindgen::JsCast; - let canvas = web_sys::window() - .unwrap() + web_sys::window() + .ok_or_else(|| "winit backend: Could not retrieve DOM window".to_string())? .document() - .unwrap() + .ok_or_else(|| "winit backend: Could not retrieve DOM document".to_string())? .get_element_by_id(&self_.canvas_id) - .unwrap() + .ok_or_else(|| { + format!( + "winit backend: Could not retrieve existing HTML Canvas element '{}'", + self_.canvas_id + ) + })? .dyn_into::() - .unwrap(); + .map_err(|_| { + format!( + "winit backend: Specified DOM element '{}' is not a HTML Canvas", + self_.canvas_id + ) + })? + }; + #[cfg(target_arch = "wasm32")] + { let existing_canvas_size = winit::dpi::LogicalSize::new( - canvas.client_width() as f32, - canvas.client_height() as f32, + html_canvas.client_width() as f32, + html_canvas.client_height() as f32, ); // Try to maintain the existing size of the canvas element. A window created with winit @@ -533,26 +556,15 @@ impl WindowAdapterSealed for GLWind #[cfg(target_arch = "wasm32")] let window_builder = { - use wasm_bindgen::JsCast; - - let canvas = web_sys::window() - .unwrap() - .document() - .unwrap() - .get_element_by_id(&self_.canvas_id) - .unwrap() - .dyn_into::() - .unwrap(); - use winit::platform::web::WindowBuilderExtWebSys; - window_builder.with_canvas(Some(canvas.clone())) + window_builder.with_canvas(Some(html_canvas.clone())) }; let winit_window = self_.renderer.show( window_builder, #[cfg(target_arch = "wasm32")] &self_.canvas_id, - ); + )?; WindowInner::from_pub(&self_.window).set_scale_factor( scale_factor_override.unwrap_or_else(|| winit_window.scale_factor()) as _, @@ -570,13 +582,13 @@ impl WindowAdapterSealed for GLWind })); crate::event_loop::register_window(id, self_.self_weak.upgrade().unwrap()); - }); - Ok(()) + Ok(()) + }) } - fn hide(&self) { + fn hide(&self) -> Result<(), PlatformError> { self.call_with_event_loop(|self_| { - self_.unmap(); + self_.unmap()?; /* FIXME: if let Some(existing_blinker) = self.cursor_blinker.borrow().upgrade() { @@ -587,8 +599,9 @@ impl WindowAdapterSealed for GLWind .event_loop_proxy() .send_event(crate::event_loop::CustomEvent::WindowHidden) }) - .unwrap(); - }); + .ok(); // It's okay to call hide() even after the event loop is closed. We don't need the logic for quitting the event loop anymore at this point. + Ok(()) + }) } fn set_mouse_cursor(&self, cursor: MouseCursor) { @@ -746,7 +759,7 @@ impl WindowAdapterSealed for GLWind impl Drop for GLWindow { fn drop(&mut self) { - self.unmap(); + self.unmap().expect("winit backend: error unmapping window"); } } diff --git a/internal/backends/winit/lib.rs b/internal/backends/winit/lib.rs index bf8061338..08407bc47 100644 --- a/internal/backends/winit/lib.rs +++ b/internal/backends/winit/lib.rs @@ -31,14 +31,17 @@ mod renderer { &self, window_builder: winit::window::WindowBuilder, #[cfg(target_arch = "wasm32")] canvas_id: &str, - ) -> Rc; - fn hide(&self); + ) -> Result, i_slint_core::platform::PlatformError>; + fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError>; - fn render(&self, size: PhysicalSize); + fn render(&self, size: PhysicalSize) -> Result<(), i_slint_core::platform::PlatformError>; fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer; - fn resize_event(&self, size: PhysicalSize); + fn resize_event( + &self, + size: PhysicalSize, + ) -> Result<(), i_slint_core::platform::PlatformError>; #[cfg(target_arch = "wasm32")] fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement; diff --git a/internal/backends/winit/renderer/femtovg.rs b/internal/backends/winit/renderer/femtovg.rs index ead304f16..8fdf500d6 100644 --- a/internal/backends/winit/renderer/femtovg.rs +++ b/internal/backends/winit/renderer/femtovg.rs @@ -10,6 +10,7 @@ use i_slint_core::api::{ SetRenderingNotifierError, }; use i_slint_core::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor}; +use i_slint_core::platform::PlatformError; use i_slint_core::renderer::Renderer; use i_slint_core::window::WindowAdapter; use i_slint_renderer_femtovg::FemtoVGRenderer; @@ -27,25 +28,27 @@ impl GlutinFemtoVGRenderer { fn with_graphics_api( opengl_context: &glcontext::OpenGLContext, callback: impl FnOnce(i_slint_core::api::GraphicsAPI<'_>), - ) { - opengl_context.ensure_current(); + ) -> Result<(), PlatformError> { + opengl_context.ensure_current()?; let api = GraphicsAPI::NativeOpenGL { get_proc_address: &|name| opengl_context.get_proc_address(name), }; - callback(api) + callback(api); + Ok(()) } #[cfg(target_arch = "wasm32")] fn with_graphics_api( opengl_context: &glcontext::OpenGLContext, callback: impl FnOnce(i_slint_core::api::GraphicsAPI<'_>), - ) { + ) -> Result<(), PlatformError> { let canvas_element_id = opengl_context.html_canvas_element().id(); let api = GraphicsAPI::WebGL { canvas_element_id: canvas_element_id.as_str(), context_type: "webgl", }; - callback(api) + callback(api); + Ok(()) } } @@ -64,7 +67,7 @@ impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer { &self, window_builder: winit::window::WindowBuilder, #[cfg(target_arch = "wasm32")] canvas_id: &str, - ) -> Rc { + ) -> Result, PlatformError> { let (window, opengl_context) = crate::event_loop::with_window_target(|event_loop| { glcontext::OpenGLContext::new_context( window_builder, @@ -72,8 +75,7 @@ impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer { #[cfg(target_arch = "wasm32")] canvas_id, ) - }) - .expect("Unable to create OpenGL context"); + })?; self.renderer.show( #[cfg(not(target_arch = "wasm32"))] @@ -85,36 +87,37 @@ impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer { if let Some(callback) = self.rendering_notifier.borrow_mut().as_mut() { Self::with_graphics_api(&opengl_context, |api| { callback.notify(RenderingState::RenderingSetup, &api) - }); + })?; } *self.opengl_context.borrow_mut() = Some(opengl_context); - Rc::new(window) + Ok(Rc::new(window)) } - fn hide(&self) { + fn hide(&self) -> Result<(), PlatformError> { if let Some(opengl_context) = self.opengl_context.borrow_mut().take() { - opengl_context.ensure_current(); + opengl_context.ensure_current()?; if let Some(callback) = self.rendering_notifier.borrow_mut().as_mut() { Self::with_graphics_api(&opengl_context, |api| { callback.notify(RenderingState::RenderingTeardown, &api) - }); + })?; } self.renderer.hide(); } + Ok(()) } - fn render(&self, size: PhysicalWindowSize) { + fn render(&self, size: PhysicalWindowSize) -> Result<(), PlatformError> { let opengl_context = if self.opengl_context.borrow().is_some() { std::cell::Ref::map(self.opengl_context.borrow(), |context_opt| { context_opt.as_ref().unwrap() }) } else { - return; + return Ok(()); }; - opengl_context.ensure_current(); + opengl_context.ensure_current()?; self.renderer.render( size, @@ -122,31 +125,31 @@ impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer { || { Self::with_graphics_api(&opengl_context, |api| { notifier_fn.notify(RenderingState::BeforeRendering, &api) - }); + }) } }), - ); + )?; if let Some(callback) = self.rendering_notifier.borrow_mut().as_mut() { Self::with_graphics_api(&opengl_context, |api| { callback.notify(RenderingState::AfterRendering, &api) - }); + })?; } - opengl_context.swap_buffers(); + opengl_context.swap_buffers() } fn as_core_renderer(&self) -> &dyn Renderer { self } - fn resize_event(&self, size: PhysicalWindowSize) { + fn resize_event(&self, size: PhysicalWindowSize) -> Result<(), PlatformError> { let opengl_context = if self.opengl_context.borrow().is_some() { std::cell::Ref::map(self.opengl_context.borrow(), |context_opt| { context_opt.as_ref().unwrap() }) } else { - return; + return Ok(()); }; opengl_context.ensure_resized(size) @@ -219,16 +222,16 @@ impl Renderer for GlutinFemtoVGRenderer { &self, component: i_slint_core::component::ComponentRef, _items: &mut dyn Iterator>>, - ) { + ) -> Result<(), PlatformError> { let opengl_context = if self.opengl_context.borrow().is_some() { std::cell::Ref::map(self.opengl_context.borrow(), |context_opt| { context_opt.as_ref().unwrap() }) } else { - return; + return Ok(()); }; - opengl_context.ensure_current(); + opengl_context.ensure_current()?; self.renderer.free_graphics_resources(component, _items) } } diff --git a/internal/backends/winit/renderer/femtovg/glcontext.rs b/internal/backends/winit/renderer/femtovg/glcontext.rs index cb4be9f59..0e116aea5 100644 --- a/internal/backends/winit/renderer/femtovg/glcontext.rs +++ b/internal/backends/winit/renderer/femtovg/glcontext.rs @@ -8,7 +8,7 @@ use glutin::{ prelude::*, surface::{SurfaceAttributesBuilder, WindowSurface}, }; -use i_slint_core::api::PhysicalSize; +use i_slint_core::{api::PhysicalSize, platform::PlatformError}; #[cfg(not(target_arch = "wasm32"))] use raw_window_handle::HasRawWindowHandle; @@ -27,35 +27,52 @@ impl OpenGLContext { self.canvas.clone() } - pub fn ensure_current(&self) { + pub fn ensure_current(&self) -> Result<(), PlatformError> { #[cfg(not(target_arch = "wasm32"))] if !self.context.is_current() { - self.context.make_current(&self.surface).unwrap(); + self.context.make_current(&self.surface).map_err(|glutin_error| -> PlatformError { + format!("FemtoVG: Error making context current: {glutin_error}").into() + })?; } + Ok(()) } - pub fn swap_buffers(&self) { + pub fn swap_buffers(&self) -> Result<(), PlatformError> { #[cfg(not(target_arch = "wasm32"))] - self.surface.swap_buffers(&self.context).unwrap(); + self.surface.swap_buffers(&self.context).map_err(|glutin_error| -> PlatformError { + format!("FemtoVG: Error swapping buffers: {glutin_error}").into() + })?; + + Ok(()) } - pub fn ensure_resized(&self, _size: PhysicalSize) { + pub fn ensure_resized(&self, _size: PhysicalSize) -> Result<(), PlatformError> { #[cfg(not(target_arch = "wasm32"))] { - self.ensure_current(); - self.surface.resize( - &self.context, - _size.width.try_into().unwrap(), - _size.height.try_into().unwrap(), - ); + let width = _size.width.try_into().map_err(|_| { + format!( + "Attempting to create window surface with an invalid width: {}", + _size.width + ) + })?; + let height = _size.height.try_into().map_err(|_| { + format!( + "Attempting to create window surface with an invalid height: {}", + _size.height + ) + })?; + + self.ensure_current()?; + self.surface.resize(&self.context, width, height); } + Ok(()) } #[cfg(not(target_arch = "wasm32"))] pub fn new_context( window_builder: winit::window::WindowBuilder, window_target: &winit::event_loop::EventLoopWindowTarget, - ) -> Result<(winit::window::Window, Self), Box> { + ) -> Result<(winit::window::Window, Self), PlatformError> { let config_template_builder = glutin::config::ConfigTemplateBuilder::new(); let (window, gl_config) = glutin_winit::DisplayBuilder::new() @@ -73,6 +90,9 @@ impl OpenGLContext { } }) .expect("internal error: Could not find any matching GL configuration") + }) + .map_err(|glutin_err| { + format!("Error creating OpenGL display with glutin: {}", glutin_err) })?; let gl_display = gl_config.display(); @@ -91,20 +111,28 @@ impl OpenGLContext { let not_current_gl_context = unsafe { gl_display .create_context(&gl_config, &gles_context_attributes) - .or_else(|_| gl_display.create_context(&gl_config, &fallback_context_attributes))? + .or_else(|_| gl_display.create_context(&gl_config, &fallback_context_attributes)) + .map_err(|glutin_err| format!("Cannot create OpenGL context: {}", glutin_err))? }; let window = match window { Some(window) => window, - None => glutin_winit::finalize_window(window_target, window_builder, &gl_config)?, + None => glutin_winit::finalize_window(window_target, window_builder, &gl_config) + .map_err(|winit_os_error| { + format!("Error finalizing window for OpenGL rendering: {}", winit_os_error) + })?, }; let size: winit::dpi::PhysicalSize = window.inner_size(); - let width: std::num::NonZeroU32 = - size.width.try_into().expect("new context called with zero width window"); - let height: std::num::NonZeroU32 = - size.height.try_into().expect("new context called with zero height window"); + let width: std::num::NonZeroU32 = size.width.try_into().map_err(|e| { + format!("Attempting to create window surface with width that doesn't fit into U32: {e}") + })?; + let height: std::num::NonZeroU32 = size.height.try_into().map_err(|e| { + format!( + "Attempting to create window surface with height that doesn't fit into U32: {e}" + ) + })?; let attrs = SurfaceAttributesBuilder::::new().build( window.raw_window_handle(), @@ -112,7 +140,11 @@ impl OpenGLContext { height, ); - let surface = unsafe { gl_display.create_window_surface(&gl_config, &attrs)? }; + let surface = unsafe { + gl_display.create_window_surface(&gl_config, &attrs).map_err(|glutin_err| { + format!("Error creating OpenGL Window surface: {}", glutin_err) + })? + }; // Align the GL layer to the top-left, so that resizing only invalidates the bottom/right // part of the window. @@ -129,10 +161,13 @@ impl OpenGLContext { } } - Ok(( - window, - Self { context: not_current_gl_context.make_current(&surface).unwrap(), surface }, - )) + let context = not_current_gl_context.make_current(&surface) + .map_err(|glutin_error: glutin::error::Error| -> PlatformError { + format!("FemtoVG Renderer: Failed to make newly created OpenGL context current: {glutin_error}") + .into() + })?; + + Ok((window, Self { context, surface })) } #[cfg(target_arch = "wasm32")] @@ -140,19 +175,28 @@ impl OpenGLContext { window_builder: winit::window::WindowBuilder, window_target: &winit::event_loop::EventLoopWindowTarget, canvas_id: &str, - ) -> Result<(winit::window::Window, Self), Box> { - let window = window_builder.build(window_target)?; + ) -> Result<(winit::window::Window, Self), PlatformError> { + let window = window_builder.build(window_target).map_err(|winit_os_err| { + format!( + "FemtoVG Renderer: Could not create winit window wrapper for DOM canvas: {}", + winit_os_err + ) + })?; use wasm_bindgen::JsCast; let canvas = web_sys::window() - .unwrap() + .ok_or_else(|| "FemtoVG Renderer: Could not retrieve DOM window".to_string())? .document() - .unwrap() + .ok_or_else(|| "FemtoVG Renderer: Could not retrieve DOM document".to_string())? .get_element_by_id(canvas_id) - .unwrap() + .ok_or_else(|| { + format!("FemtoVG Renderer: Could not retrieve existing HTML Canvas element '{canvas_id}'") + })? .dyn_into::() - .unwrap(); + .map_err(|_| { + format!("FemtoVG Renderer: Specified DOM element '{canvas_id}' is not a HTML Canvas") + })?; Ok((window, Self { canvas })) } diff --git a/internal/backends/winit/renderer/skia.rs b/internal/backends/winit/renderer/skia.rs index 7a520d6c9..69f4266bc 100644 --- a/internal/backends/winit/renderer/skia.rs +++ b/internal/backends/winit/renderer/skia.rs @@ -17,30 +17,39 @@ impl super::WinitCompatibleRenderer for SkiaRenderer { Self { renderer: i_slint_renderer_skia::SkiaRenderer::new(window_adapter_weak.clone()) } } - fn show(&self, window_builder: winit::window::WindowBuilder) -> Rc { + fn show( + &self, + window_builder: winit::window::WindowBuilder, + ) -> Result, i_slint_core::platform::PlatformError> { let window = Rc::new(crate::event_loop::with_window_target(|event_loop| { window_builder.build(event_loop.event_loop_target()).unwrap() })); let size: winit::dpi::PhysicalSize = window.inner_size(); - self.renderer.show(window.clone(), PhysicalWindowSize::new(size.width, size.height)); + self.renderer.show(window.clone(), PhysicalWindowSize::new(size.width, size.height))?; - window + Ok(window) } - fn hide(&self) { - self.renderer.hide(); + fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError> { + self.renderer.hide() } - fn render(&self, size: PhysicalWindowSize) { - self.renderer.render(size); + fn render( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { + self.renderer.render(size) } fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer { &self.renderer } - fn resize_event(&self, size: PhysicalWindowSize) { + fn resize_event( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { self.renderer.resize_event(size) } } diff --git a/internal/backends/winit/renderer/sw.rs b/internal/backends/winit/renderer/sw.rs index 41e13491c..2202274f7 100644 --- a/internal/backends/winit/renderer/sw.rs +++ b/internal/backends/winit/renderer/sw.rs @@ -28,29 +28,43 @@ impl super::WinitCompatibleRenderer for WinitSoftwareRenderer { } } - fn show(&self, window_builder: winit::window::WindowBuilder) -> Rc { - let window = Rc::new(crate::event_loop::with_window_target(|event_loop| { - window_builder.build(event_loop.event_loop_target()).unwrap() - })); + fn show( + &self, + window_builder: winit::window::WindowBuilder, + ) -> Result, i_slint_core::platform::PlatformError> { + let window = crate::event_loop::with_window_target(|event_loop| { + window_builder.build(event_loop.event_loop_target()).map_err(|winit_os_error| { + format!("Error creating native window for software rendering: {}", winit_os_error) + }) + })?; + let window = Rc::new(window); *self.canvas.borrow_mut() = Some(unsafe { - softbuffer::GraphicsContext::new(window.as_ref(), window.as_ref()).unwrap() + softbuffer::GraphicsContext::new(window.as_ref(), window.as_ref()).map_err( + |softbuffer_error| { + format!("Error creating softbuffer graphics context: {}", softbuffer_error) + }, + )? }); - window + Ok(window) } - fn hide(&self) { + fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError> { self.canvas.borrow_mut().take(); + Ok(()) } - fn render(&self, size: PhysicalWindowSize) { + fn render( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { let mut canvas = if self.canvas.borrow().is_some() { std::cell::RefMut::map(self.canvas.borrow_mut(), |canvas_opt| { canvas_opt.as_mut().unwrap() }) } else { - return; + return Ok(()); }; let width = size.width as usize; @@ -95,9 +109,15 @@ impl super::WinitCompatibleRenderer for WinitSoftwareRenderer { ); canvas.set_buffer(&softbuffer_buffer, width as u16, height as u16); + Ok(()) } - fn resize_event(&self, _size: PhysicalWindowSize) {} + fn resize_event( + &self, + _size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { + Ok(()) + } fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer { &self.renderer diff --git a/internal/compiler/generator/rust.rs b/internal/compiler/generator/rust.rs index b7282a9bb..072b422f4 100644 --- a/internal/compiler/generator/rust.rs +++ b/internal/compiler/generator/rust.rs @@ -381,7 +381,7 @@ fn generate_public_component(llr: &llr::PublicComponent) -> TokenStream { fn run(&self) -> core::result::Result<(), slint::PlatformError> { self.show()?; slint::run_event_loop()?; - self.hide(); + self.hide()?; core::result::Result::Ok(()) } @@ -389,7 +389,7 @@ fn generate_public_component(llr: &llr::PublicComponent) -> TokenStream { self.window().show() } - fn hide(&self) { + fn hide(&self) -> core::result::Result<(), slint::PlatformError> { self.window().hide() } diff --git a/internal/core/api.rs b/internal/core/api.rs index 462b9f2ea..7d7faad5c 100644 --- a/internal/core/api.rs +++ b/internal/core/api.rs @@ -348,8 +348,8 @@ impl Window { } /// De-registers the window from the windowing system, therefore hiding it. - pub fn hide(&self) { - self.0.hide(); + pub fn hide(&self) -> Result<(), PlatformError> { + self.0.hide() } /// This function allows registering a callback that's invoked during the different phases of @@ -552,7 +552,7 @@ pub trait ComponentHandle { /// Marks the window of this component to be hidden on the screen. This de-registers /// the window from the windowing system and it will not receive any further events. - fn hide(&self); + fn hide(&self) -> Result<(), PlatformError>; /// Returns the Window associated with this component. The window API can be used /// to control different aspects of the integration into the windowing system, diff --git a/internal/core/component.rs b/internal/core/component.rs index 9e2ac4ce3..b1fac136f 100644 --- a/internal/core/component.rs +++ b/internal/core/component.rs @@ -142,7 +142,7 @@ pub fn unregister_component( window_adapter.renderer().free_graphics_resources( component, &mut item_array.iter().map(|item| item.apply_pin(base)), - ); + ).expect("Fatal error encountered when freeing graphics resources while destroying Slint component"); window_adapter .unregister_component(component, &mut item_array.iter().map(|item| item.apply_pin(base))); } diff --git a/internal/core/renderer.rs b/internal/core/renderer.rs index 814ab3dec..351a70d15 100644 --- a/internal/core/renderer.rs +++ b/internal/core/renderer.rs @@ -42,7 +42,8 @@ pub trait Renderer { &self, _component: ComponentRef, _items: &mut dyn Iterator>>, - ) { + ) -> Result<(), crate::platform::PlatformError> { + Ok(()) } /// Mark a given region as dirty regardless whether the items actually are dirty. diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index a8c9ef43b..7864e2870 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -302,13 +302,14 @@ impl Renderer for SoftwareRenderer { &self, _component: crate::component::ComponentRef, items: &mut dyn Iterator>>, - ) { + ) -> Result<(), crate::platform::PlatformError> { for item in items { item.cached_rendering_data_offset().release(&mut self.partial_cache.borrow_mut()); } // We don't have a way to determine the screen region of the delete items, what's in the cache is relative. So // as a last resort, refresh everything. self.force_screen_refresh.set(true); + Ok(()) } fn mark_dirty_region(&self, region: crate::item_rendering::DirtyRegion) { diff --git a/internal/core/window.rs b/internal/core/window.rs index 906a0818a..916854cfc 100644 --- a/internal/core/window.rs +++ b/internal/core/window.rs @@ -72,7 +72,9 @@ pub trait WindowAdapterSealed { Ok(()) } /// De-registers the window from the windowing system. - fn hide(&self) {} + fn hide(&self) -> Result<(), PlatformError> { + Ok(()) + } /// Issue a request to the windowing system to re-render the contents of the window. This is typically an asynchronous /// request. fn request_redraw(&self) {} @@ -591,7 +593,10 @@ impl WindowInner { /// Calls the render_components to render the main component and any sub-window components, tracked by a /// property dependency tracker. - pub fn draw_contents(&self, render_components: impl FnOnce(&[(&ComponentRc, LogicalPoint)])) { + pub fn draw_contents( + &self, + render_components: impl FnOnce(&[(&ComponentRc, LogicalPoint)]) -> T, + ) -> T { let draw_fn = || { let component_rc = self.component(); @@ -609,7 +614,7 @@ impl WindowInner { (&popup_component, popup_coordinates), ]) } else { - render_components(&[(&component_rc, LogicalPoint::default())]); + render_components(&[(&component_rc, LogicalPoint::default())]) } }; @@ -625,8 +630,8 @@ impl WindowInner { } /// De-registers the window with the windowing system. - pub fn hide(&self) { - self.window_adapter().hide(); + pub fn hide(&self) -> Result<(), PlatformError> { + self.window_adapter().hide() } /// Show a popup at the given position relative to the item @@ -833,7 +838,7 @@ pub mod ffi { #[no_mangle] pub unsafe extern "C" fn slint_windowrc_hide(handle: *const WindowAdapterRcOpaque) { let window = &*(handle as *const Rc); - window.hide(); + window.hide().unwrap(); } /// Returns the visibility state of the window. This function can return false even if you previously called show() diff --git a/internal/interpreter/api.rs b/internal/interpreter/api.rs index 16208d6e6..63515c380 100644 --- a/internal/interpreter/api.rs +++ b/internal/interpreter/api.rs @@ -1045,17 +1045,16 @@ impl ComponentHandle for ComponentInstance { comp.borrow_instance().window_adapter().window().show() } - fn hide(&self) { + fn hide(&self) -> Result<(), PlatformError> { generativity::make_guard!(guard); let comp = self.inner.unerase(guard); - comp.borrow_instance().window_adapter().window().hide(); + comp.borrow_instance().window_adapter().window().hide() } fn run(&self) -> Result<(), PlatformError> { self.show()?; run_event_loop()?; - self.hide(); - Ok(()) + self.hide() } fn window(&self) -> &Window { diff --git a/internal/interpreter/ffi.rs b/internal/interpreter/ffi.rs index 6b346bd48..af395790f 100644 --- a/internal/interpreter/ffi.rs +++ b/internal/interpreter/ffi.rs @@ -562,7 +562,7 @@ pub extern "C" fn slint_interpreter_component_instance_show( if is_visible { comp.borrow_instance().window_adapter().show().unwrap(); } else { - comp.borrow_instance().window_adapter().hide(); + comp.borrow_instance().window_adapter().hide().unwrap(); } } diff --git a/internal/renderers/femtovg/lib.rs b/internal/renderers/femtovg/lib.rs index 2d34a3a94..a35cf7a28 100644 --- a/internal/renderers/femtovg/lib.rs +++ b/internal/renderers/femtovg/lib.rs @@ -14,6 +14,7 @@ use i_slint_core::items::Item; use i_slint_core::lengths::{ LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PhysicalPx, ScaleFactor, }; +use i_slint_core::platform::PlatformError; use i_slint_core::renderer::Renderer; use i_slint_core::window::{WindowAdapter, WindowInner}; use i_slint_core::Brush; @@ -114,21 +115,21 @@ impl FemtoVGRenderer { pub fn render( &self, size: PhysicalWindowSize, - mut before_rendering_callback: Option, - ) { + mut before_rendering_callback: Option Result<(), PlatformError>>, + ) -> Result<(), i_slint_core::platform::PlatformError> { let width = size.width; let height = size.height; let canvas = if self.canvas.borrow().is_some() { std::cell::Ref::map(self.canvas.borrow(), |canvas_opt| canvas_opt.as_ref().unwrap()) } else { - return; + return Err(format!("FemtoVG renderer: render() called before show()").into()); }; let window_adapter = self.window_adapter_weak.upgrade().unwrap(); let window = WindowInner::from_pub(window_adapter.window()); - window.draw_contents(|components| { + window.draw_contents(|components| -> Result<(), PlatformError> { let window_background_brush = window.window_item().map(|w| w.as_pin_ref().background()); { @@ -161,7 +162,7 @@ impl FemtoVGRenderer { femtovg_canvas.set_size(width, height, 1.0); drop(femtovg_canvas); - callback(); + callback()?; } let window_adapter = self.window_adapter_weak.upgrade().unwrap(); @@ -201,7 +202,8 @@ impl FemtoVGRenderer { // avoid GPU memory leaks. canvas.texture_cache.borrow_mut().drain(); drop(item_renderer); - }); + Ok(()) + }) } } @@ -389,14 +391,15 @@ impl Renderer for FemtoVGRenderer { &self, component: i_slint_core::component::ComponentRef, _items: &mut dyn Iterator>>, - ) { + ) -> Result<(), i_slint_core::platform::PlatformError> { let canvas = if self.canvas.borrow().is_some() { std::cell::Ref::map(self.canvas.borrow(), |canvas_opt| canvas_opt.as_ref().unwrap()) } else { - return; + return Ok(()); }; canvas.graphics_cache.component_destroyed(component); + Ok(()) } } diff --git a/internal/renderers/skia/d3d_surface.rs b/internal/renderers/skia/d3d_surface.rs index bff8ac424..6ce6faf55 100644 --- a/internal/renderers/skia/d3d_surface.rs +++ b/internal/renderers/skia/d3d_surface.rs @@ -224,7 +224,11 @@ impl SwapChain { [make_surface(0), make_surface(1)] } - fn resize(&mut self, width: u32, height: u32) { + fn resize( + &mut self, + width: u32, + height: u32, + ) -> Result<(), i_slint_core::platform::PlatformError> { self.gr_context.flush_submit_and_sync_cpu(); self.wait_for_buffer(0); @@ -236,7 +240,9 @@ impl SwapChain { let resize_result = self.swap_chain.ResizeBuffers(0, width, height, DEFAULT_SURFACE_FORMAT, 0); if resize_result != S_OK { - panic!("Error resizing swap chain buffers: {:x}", resize_result); + return Err( + format!("Error resizing swap chain buffers: {:x}", resize_result).into() + ); } } @@ -246,6 +252,7 @@ impl SwapChain { width as i32, height as i32, )); + Ok(()) } fn wait_for_buffer(&mut self, buffer_index: usize) { @@ -281,7 +288,7 @@ impl super::Surface for D3DSurface { window: &dyn raw_window_handle::HasRawWindowHandle, _display: &dyn raw_window_handle::HasRawDisplayHandle, size: PhysicalWindowSize, - ) -> Self { + ) -> Result { let factory_flags = 0; /* let factory_flags = dxgi1_3::DXGI_CREATE_FACTORY_DEBUG; @@ -401,7 +408,7 @@ impl super::Surface for D3DSurface { let swap_chain = RefCell::new(SwapChain::new(queue, &device, gr_context, &window, size, &dxgi_factory)); - Self { swap_chain } + Ok(Self { swap_chain }) } fn name(&self) -> &'static str { @@ -412,26 +419,34 @@ impl super::Surface for D3DSurface { unimplemented!() } - fn resize_event(&self, size: PhysicalWindowSize) { - self.swap_chain.borrow_mut().resize(size.width, size.height); + fn resize_event( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { + self.swap_chain.borrow_mut().resize(size.width, size.height) } fn render( &self, _size: PhysicalWindowSize, callback: impl FnOnce(&mut skia_safe::Canvas, &mut skia_safe::gpu::DirectContext), - ) { + ) -> Result<(), i_slint_core::platform::PlatformError> { self.swap_chain .borrow_mut() - .render_and_present(|surface, gr_context| callback(surface.canvas(), gr_context)) + .render_and_present(|surface, gr_context| callback(surface.canvas(), gr_context)); + Ok(()) } - fn bits_per_pixel(&self) -> u8 { + fn bits_per_pixel(&self) -> Result { let mut desc = dxgi::DXGI_SWAP_CHAIN_DESC::default(); unsafe { self.swap_chain.borrow().swap_chain.GetDesc(&mut desc) }; - match desc.BufferDesc.Format { + Ok(match desc.BufferDesc.Format { DEFAULT_SURFACE_FORMAT => 32, - _ => 0, // Not mapped yet - } + fmt @ _ => { + return Err( + format!("Skia D3D Renderer: Unsupported buffer format found {fmt:?}").into() + ) + } + }) } } diff --git a/internal/renderers/skia/lib.rs b/internal/renderers/skia/lib.rs index f67f00239..d3e9545f9 100644 --- a/internal/renderers/skia/lib.rs +++ b/internal/renderers/skia/lib.rs @@ -18,6 +18,7 @@ use i_slint_core::items::Item; use i_slint_core::lengths::{ LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PhysicalPx, ScaleFactor, }; +use i_slint_core::platform::PlatformError; use i_slint_core::window::{WindowAdapter, WindowInner}; use i_slint_core::Brush; @@ -70,15 +71,19 @@ impl< /// Use the provided window and display for rendering the Slint scene in future calls to [`Self::render()`]. /// The size must be identical to the size of the window in physical pixels that is providing the window handle. - pub fn show(&self, native_window: NativeWindowWrapper, size: PhysicalWindowSize) { - let surface = DefaultSurface::new(&native_window, &native_window, size); + pub fn show( + &self, + native_window: NativeWindowWrapper, + size: PhysicalWindowSize, + ) -> Result<(), PlatformError> { + let surface = DefaultSurface::new(&native_window, &native_window, size)?; let rendering_metrics_collector = RenderingMetricsCollector::new( self.window_adapter_weak.clone(), &format!( "Skia renderer (skia backend {}; surface: {} bpp)", surface.name(), - surface.bits_per_pixel() + surface.bits_per_pixel()? ), ); @@ -95,11 +100,13 @@ impl< } *self.canvas.borrow_mut() = Some(canvas); + + Ok(()) } /// Release any graphics resources and disconnect the rendere from a window that it was previously associated when when /// calling [`Self::show()]`. - pub fn hide(&self) { + pub fn hide(&self) -> Result<(), i_slint_core::platform::PlatformError> { if let Some(canvas) = self.canvas.borrow_mut().take() { canvas.surface.with_active_surface(|| { if let Some(callback) = self.rendering_notifier.borrow_mut().as_mut() { @@ -107,16 +114,20 @@ impl< callback.notify(RenderingState::RenderingTeardown, &api) }) } - }); + })?; } + Ok(()) } /// Render the scene in the previously associated window. The size parameter must match the size of the window. - pub fn render(&self, size: PhysicalWindowSize) { + pub fn render( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { let canvas = if self.canvas.borrow().is_some() { std::cell::Ref::map(self.canvas.borrow(), |canvas_opt| canvas_opt.as_ref().unwrap()) } else { - return; + return Err(format!("Skia renderer: render() called before show()").into()); }; let window_adapter = self.window_adapter_weak.upgrade().unwrap(); @@ -185,15 +196,18 @@ impl< canvas .with_graphics_api(|api| callback.notify(RenderingState::AfterRendering, &api)) } - }); + }) } /// Call this when you receive a notification from the windowing system that the size of the window has changed. - pub fn resize_event(&self, size: PhysicalWindowSize) { + pub fn resize_event( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { let canvas = if self.canvas.borrow().is_some() { std::cell::Ref::map(self.canvas.borrow(), |canvas_opt| canvas_opt.as_ref().unwrap()) } else { - return; + return Ok(()); }; canvas.surface.resize_event(size) @@ -372,15 +386,16 @@ impl i_slint_core::renderer::Renderer for SkiaRenderer>>, - ) { + ) -> Result<(), i_slint_core::platform::PlatformError> { let canvas = if self.canvas.borrow().is_some() { std::cell::Ref::map(self.canvas.borrow(), |canvas_opt| canvas_opt.as_ref().unwrap()) } else { - return; + return Ok(()); }; canvas.image_cache.component_destroyed(component); canvas.path_cache.component_destroyed(component); + Ok(()) } } @@ -390,19 +405,28 @@ trait Surface { window: &dyn raw_window_handle::HasRawWindowHandle, display: &dyn raw_window_handle::HasRawDisplayHandle, size: PhysicalWindowSize, - ) -> Self; + ) -> Result + where + Self: Sized; fn name(&self) -> &'static str; fn with_graphics_api(&self, callback: impl FnOnce(GraphicsAPI<'_>)); - fn with_active_surface(&self, callback: impl FnOnce()) { - callback() + fn with_active_surface( + &self, + callback: impl FnOnce(), + ) -> Result<(), i_slint_core::platform::PlatformError> { + callback(); + Ok(()) } fn render( &self, size: PhysicalWindowSize, callback: impl FnOnce(&mut skia_safe::Canvas, &mut skia_safe::gpu::DirectContext), - ); - fn resize_event(&self, size: PhysicalWindowSize); - fn bits_per_pixel(&self) -> u8; + ) -> Result<(), i_slint_core::platform::PlatformError>; + fn resize_event( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError>; + fn bits_per_pixel(&self) -> Result; } struct SkiaCanvas { diff --git a/internal/renderers/skia/metal_surface.rs b/internal/renderers/skia/metal_surface.rs index 1485a5f1f..ce2df368f 100644 --- a/internal/renderers/skia/metal_surface.rs +++ b/internal/renderers/skia/metal_surface.rs @@ -25,8 +25,9 @@ impl super::Surface for MetalSurface { window: &dyn raw_window_handle::HasRawWindowHandle, _display: &dyn raw_window_handle::HasRawDisplayHandle, size: PhysicalWindowSize, - ) -> Self { - let device = metal::Device::system_default().expect("no metal device found"); + ) -> Result { + let device = metal::Device::system_default() + .ok_or_else(|| format!("Skia Renderer: No metal device found"))?; let layer = metal::MetalLayer::new(); layer.set_device(&device); @@ -40,7 +41,9 @@ impl super::Surface for MetalSurface { raw_window_handle::RawWindowHandle::AppKit( raw_window_handle::AppKitWindowHandle { ns_view, .. }, ) => ns_view, - _ => panic!("Metal surface is only supported with AppKit"), + _ => { + return Err("Skia Renderer: Metal surface is only supported with AppKit".into()) + } } as cocoa_id; view.setWantsLayer(YES); view.setLayer(layer.as_ref() as *const _ as _); @@ -58,7 +61,7 @@ impl super::Surface for MetalSurface { let gr_context = skia_safe::gpu::DirectContext::new_metal(&backend, None).unwrap().into(); - Self { command_queue, layer, gr_context } + Ok(Self { command_queue, layer, gr_context }) } fn name(&self) -> &'static str { @@ -69,19 +72,28 @@ impl super::Surface for MetalSurface { unimplemented!() } - fn resize_event(&self, size: PhysicalWindowSize) { + fn resize_event( + &self, + size: PhysicalWindowSize, + ) -> Result<(), i_slint_core::platform::PlatformError> { self.layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64)); + Ok(()) } fn render( &self, _size: PhysicalWindowSize, callback: impl FnOnce(&mut skia_safe::Canvas, &mut skia_safe::gpu::DirectContext), - ) { + ) -> Result<(), i_slint_core::platform::PlatformError> { autoreleasepool(|| { let drawable = match self.layer.next_drawable() { Some(drawable) => drawable, - None => return, + None => { + return Err(format!( + "Skia Metal Renderer: Failed to retrieve next drawable for rendering" + ) + .into()) + } }; let gr_context = &mut self.gr_context.borrow_mut(); @@ -118,15 +130,17 @@ impl super::Surface for MetalSurface { let command_buffer = self.command_queue.new_command_buffer(); command_buffer.present_drawable(drawable); command_buffer.commit(); + + Ok(()) }) } - fn bits_per_pixel(&self) -> u8 { + fn bits_per_pixel(&self) -> Result { // From https://developer.apple.com/documentation/metal/mtlpixelformat: // The storage size of each pixel format is determined by the sum of its components. // For example, the storage size of BGRA8Unorm is 32 bits (four 8-bit components) and // the storage size of BGR5A1Unorm is 16 bits (three 5-bit components and one 1-bit component). - match self.layer.pixel_format() { + Ok(match self.layer.pixel_format() { MTLPixelFormat::B5G6R5Unorm | MTLPixelFormat::A1BGR5Unorm | MTLPixelFormat::ABGR4Unorm @@ -146,7 +160,12 @@ impl super::Surface for MetalSurface { | MTLPixelFormat::RGBA16Uint | MTLPixelFormat::RGBA16Sint => 64, MTLPixelFormat::RGBA32Uint | MTLPixelFormat::RGBA32Sint => 128, - _ => 0, // Not mapped yet - } + fmt @ _ => { + return Err(format!( + "Skia Metal Renderer: Unsupported layer pixel format found {fmt:?}" + ) + .into()) + } + }) } } diff --git a/internal/renderers/skia/opengl_surface.rs b/internal/renderers/skia/opengl_surface.rs index 89dd94bd9..5f2049762 100644 --- a/internal/renderers/skia/opengl_surface.rs +++ b/internal/renderers/skia/opengl_surface.rs @@ -1,7 +1,7 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial -use std::cell::RefCell; +use std::{cell::RefCell, num::NonZeroU32}; use glutin::{ config::GetGlConfig, @@ -10,8 +10,8 @@ use glutin::{ prelude::*, surface::{SurfaceAttributesBuilder, WindowSurface}, }; -use i_slint_core::api::GraphicsAPI; use i_slint_core::api::PhysicalSize as PhysicalWindowSize; +use i_slint_core::{api::GraphicsAPI, platform::PlatformError}; pub struct OpenGLSurface { fb_info: skia_safe::gpu::gl::FramebufferInfo, @@ -28,8 +28,16 @@ impl super::Surface for OpenGLSurface { window: &dyn raw_window_handle::HasRawWindowHandle, display: &dyn raw_window_handle::HasRawDisplayHandle, size: PhysicalWindowSize, - ) -> Self { - let (current_glutin_context, glutin_surface) = Self::init_glutin(window, display, size); + ) -> Result { + let width: std::num::NonZeroU32 = size.width.try_into().map_err(|_| { + format!("Attempting to create window surface with an invalid width: {}", size.width) + })?; + let height: std::num::NonZeroU32 = size.height.try_into().map_err(|_| { + format!("Attempting to create window surface with an invalid height: {}", size.height) + })?; + + let (current_glutin_context, glutin_surface) = + Self::init_glutin(window, display, width, height)?; let fb_info = { use glow::HasContext; @@ -42,7 +50,9 @@ impl super::Surface for OpenGLSurface { let fboid = unsafe { gl.get_parameter_i32(glow::FRAMEBUFFER_BINDING) }; skia_safe::gpu::gl::FramebufferInfo { - fboid: fboid.try_into().unwrap(), + fboid: fboid.try_into().map_err(|_| { + format!("Skia Renderer: Internal error, framebuffer binding returned signed id") + })?, format: skia_safe::gpu::gl::Format::RGBA8.into(), } }; @@ -51,19 +61,36 @@ impl super::Surface for OpenGLSurface { current_glutin_context.display().get_proc_address(name) as *const _ }); - let mut gr_context = skia_safe::gpu::DirectContext::new_gl(gl_interface, None).unwrap(); + let mut gr_context = + skia_safe::gpu::DirectContext::new_gl(gl_interface, None).ok_or_else(|| { + format!("Skia Renderer: Internal Error: Could not create Skia OpenGL interface") + })?; - let surface = - Self::create_internal_surface(fb_info, ¤t_glutin_context, &mut gr_context, size) - .into(); + let width: i32 = size.width.try_into().map_err(|e| { + format!("Attempting to create window surface with width that doesn't fit into non-zero i32: {e}") + })?; + let height: i32 = size.height.try_into().map_err(|e| { + format!( + "Attempting to create window surface with height that doesn't fit into non-zero i32: {e}" + ) + })?; - Self { + let surface = Self::create_internal_surface( + fb_info, + ¤t_glutin_context, + &mut gr_context, + width, + height, + )? + .into(); + + Ok(Self { fb_info, surface, gr_context: RefCell::new(gr_context), glutin_context: current_glutin_context, glutin_surface, - } + }) } fn name(&self) -> &'static str { @@ -79,57 +106,79 @@ impl super::Surface for OpenGLSurface { callback(api) } - fn with_active_surface(&self, callback: impl FnOnce()) { - self.ensure_context_current(); + fn with_active_surface(&self, callback: impl FnOnce()) -> Result<(), PlatformError> { + self.ensure_context_current()?; callback(); + Ok(()) } fn render( &self, size: PhysicalWindowSize, callback: impl FnOnce(&mut skia_safe::Canvas, &mut skia_safe::gpu::DirectContext), - ) { - let width = size.width; - let height = size.height; - - self.ensure_context_current(); + ) -> Result<(), PlatformError> { + self.ensure_context_current()?; let current_context = &self.glutin_context; let gr_context = &mut self.gr_context.borrow_mut(); let mut surface = self.surface.borrow_mut(); - if width != surface.width() as u32 || height != surface.height() as u32 { - *surface = - Self::create_internal_surface(self.fb_info, ¤t_context, gr_context, size); + + let width = size.width.try_into().ok(); + let height = size.height.try_into().ok(); + + if let Some((width, height)) = width.zip(height) { + if width != surface.width() || height != surface.height() { + *surface = Self::create_internal_surface( + self.fb_info, + ¤t_context, + gr_context, + width, + height, + )?; + } } let skia_canvas = surface.canvas(); callback(skia_canvas, gr_context); - self.glutin_surface.swap_buffers(¤t_context).unwrap(); + self.glutin_surface.swap_buffers(¤t_context).map_err(|glutin_error| { + format!("Skia OpenGL Renderer: Error swapping buffers: {glutin_error}").into() + }) } - fn resize_event(&self, size: PhysicalWindowSize) { - self.ensure_context_current(); + fn resize_event(&self, size: PhysicalWindowSize) -> Result<(), PlatformError> { + self.ensure_context_current()?; - self.glutin_surface.resize( - &self.glutin_context, - size.width.try_into().unwrap(), - size.height.try_into().unwrap(), - ); + let width = size.width.try_into().map_err(|e| { + format!("Skia: Attempting to resize window surface with width that doesn't fit into U32: {e}") + })?; + let height = size.height.try_into().map_err(|e| { + format!( + "Skia: Attempting to resize window surface with height that doesn't fit into U32: {e}" + ) + })?; + + self.glutin_surface.resize(&self.glutin_context, width, height); + Ok(()) } - fn bits_per_pixel(&self) -> u8 { + fn bits_per_pixel(&self) -> Result { let config = self.glutin_context.config(); let rgb_bits = match config.color_buffer_type() { Some(glutin::config::ColorBufferType::Rgb { r_size, g_size, b_size }) => { r_size + g_size + b_size } - _ => panic!("unsupported color buffer used with Skia OpenGL renderer"), + other @ _ => { + return Err(format!( + "Skia OpenGL Renderer: unsupported color buffer {other:?} encountered" + ) + .into()) + } }; - rgb_bits + config.alpha_size() + Ok(rgb_bits + config.alpha_size()) } } @@ -137,11 +186,15 @@ impl OpenGLSurface { fn init_glutin( _window: &dyn raw_window_handle::HasRawWindowHandle, _display: &dyn raw_window_handle::HasRawDisplayHandle, - _size: PhysicalWindowSize, - ) -> ( - glutin::context::PossiblyCurrentContext, - glutin::surface::Surface, - ) { + width: NonZeroU32, + height: NonZeroU32, + ) -> Result< + ( + glutin::context::PossiblyCurrentContext, + glutin::surface::Surface, + ), + PlatformError, + > { cfg_if::cfg_if! { if #[cfg(target_os = "macos")] { let prefs = [glutin::display::DisplayApiPreference::Cgl]; @@ -154,11 +207,6 @@ impl OpenGLSurface { } } - let width: std::num::NonZeroU32 = - _size.width.try_into().expect("new context called with zero width window"); - let height: std::num::NonZeroU32 = - _size.height.try_into().expect("new context called with zero height window"); - let try_create_surface = |display_api_preference| -> Result<(_, _), Box> { let gl_display = unsafe { @@ -229,16 +277,16 @@ impl OpenGLSurface { let is_last = i == num_prefs - 1; match try_create_surface(pref) { - Ok(result) => Some(result), + Ok(result) => Some(Ok(result)), Err(glutin_error) => { if is_last { - panic!("Glutin error creating GL surface: {}", glutin_error); + return Some(Err(format!("Skia OpenGL Renderer: Failed to create OpenGL Window Surface: {glutin_error}"))); } None } } }) - .unwrap(); + .unwrap()?; // Align the GL layer to the top-left, so that resizing only invalidates the bottom/right // part of the window. @@ -255,44 +303,61 @@ impl OpenGLSurface { } } - (not_current_gl_context.make_current(&surface).unwrap(), surface) + let context = not_current_gl_context.make_current(&surface) + .map_err(|glutin_error: glutin::error::Error| -> PlatformError { + format!("FemtoVG Renderer: Failed to make newly created OpenGL context current: {glutin_error}") + .into() + })?; + + Ok((context, surface)) } fn create_internal_surface( fb_info: skia_safe::gpu::gl::FramebufferInfo, gl_context: &glutin::context::PossiblyCurrentContext, gr_context: &mut skia_safe::gpu::DirectContext, - size: PhysicalWindowSize, - ) -> skia_safe::Surface { + width: i32, + height: i32, + ) -> Result { let config = gl_context.config(); + let backend_render_target = skia_safe::gpu::BackendRenderTarget::new_gl( - (size.width.try_into().unwrap(), size.height.try_into().unwrap()), + (width, height), Some(config.num_samples() as _), config.stencil_size() as _, fb_info, ); - let surface = skia_safe::Surface::from_backend_render_target( + match skia_safe::Surface::from_backend_render_target( gr_context, &backend_render_target, skia_safe::gpu::SurfaceOrigin::BottomLeft, skia_safe::ColorType::RGBA8888, None, None, - ) - .unwrap(); - surface + ) { + Some(surface) => Ok(surface), + None => { + Err("Skia OpenGL Renderer: Failed to allocate internal backend rendering target" + .into()) + } + } } - fn ensure_context_current(&self) { + fn ensure_context_current(&self) -> Result<(), PlatformError> { if !self.glutin_context.is_current() { - self.glutin_context.make_current(&self.glutin_surface).unwrap(); + self.glutin_context.make_current(&self.glutin_surface).map_err( + |glutin_error| -> PlatformError { + format!("Skia Renderer: Error making context current: {glutin_error}").into() + }, + )?; } + Ok(()) } } impl Drop for OpenGLSurface { fn drop(&mut self) { // Make sure that the context is current before Skia calls glDelete*** - self.ensure_context_current(); + self.ensure_context_current().expect("Skia OpenGL Renderer: Failed to make OpenGL context current before deleting graphics resources"); } } diff --git a/tests/cases/examples/hello.slint b/tests/cases/examples/hello.slint index a46c96557..01eb4c788 100644 --- a/tests/cases/examples/hello.slint +++ b/tests/cases/examples/hello.slint @@ -189,7 +189,7 @@ let instance = Hello::new().unwrap(); assert!(!instance.window().is_visible()); instance.window().show().unwrap(); assert!(instance.window().is_visible()); -instance.window().hide(); +instance.window().hide().unwrap(); assert!(!instance.window().is_visible()); ```