refactor: modularize ev loop and improve error propagation

WIP/draft
drendog 2024-08-19 02:25:26 +02:00
parent 6654e0f9bc
commit 9b7818e280
Signed by: dwenya
GPG Key ID: 8DD77074645332D0
5 changed files with 131 additions and 64 deletions

View File

@ -74,7 +74,7 @@ impl EGLContextBuilder {
.ok_or_else(|| anyhow!("Surface ID is required"))?; .ok_or_else(|| anyhow!("Surface ID is required"))?;
let size = self.size.ok_or_else(|| anyhow!("Size is required"))?; let size = self.size.ok_or_else(|| anyhow!("Size is required"))?;
let display_handle = create_wayland_display_handle(&display_id); let display_handle = create_wayland_display_handle(&display_id)?;
let glutin_display = unsafe { Display::new(display_handle) }?; let glutin_display = unsafe { Display::new(display_handle) }?;
let config_template = self.config_template.unwrap_or_default(); let config_template = self.config_template.unwrap_or_default();
@ -85,7 +85,7 @@ impl EGLContextBuilder {
let context = create_context(&glutin_display, &config, context_attributes)?; let context = create_context(&glutin_display, &config, context_attributes)?;
let surface_handle = create_surface_handle(&surface_id); let surface_handle = create_surface_handle(&surface_id)?;
let surface = create_surface(&glutin_display, &config, surface_handle, size)?; let surface = create_surface(&glutin_display, &config, surface_handle, size)?;
let context = context let context = context
@ -109,11 +109,11 @@ impl EGLContext {
} }
} }
fn create_wayland_display_handle(display_id: &ObjectId) -> RawDisplayHandle { fn create_wayland_display_handle(display_id: &ObjectId) -> Result<RawDisplayHandle> {
let display = NonNull::new(display_id.as_ptr().cast::<c_void>()) let display = NonNull::new(display_id.as_ptr().cast::<c_void>())
.expect("NonNull pointer creation failed"); .ok_or_else(|| anyhow!("Failed to create NonNull pointer for display"))?;
let handle = WaylandDisplayHandle::new(display); let handle = WaylandDisplayHandle::new(display);
RawDisplayHandle::Wayland(handle) Ok(RawDisplayHandle::Wayland(handle))
} }
fn select_config( fn select_config(
@ -134,11 +134,11 @@ fn create_context(
.map_err(|e| anyhow!("Failed to create context: {}", e)) .map_err(|e| anyhow!("Failed to create context: {}", e))
} }
fn create_surface_handle(surface_id: &ObjectId) -> RawWindowHandle { fn create_surface_handle(surface_id: &ObjectId) -> Result<RawWindowHandle> {
let surface = NonNull::new(surface_id.as_ptr().cast::<c_void>()) let surface = NonNull::new(surface_id.as_ptr().cast::<c_void>())
.expect("NonNull pointer creation failed"); .ok_or_else(|| anyhow!("Failed to create NonNull pointer for surface"))?;
let handle = WaylandWindowHandle::new(surface); let handle = WaylandWindowHandle::new(surface);
RawWindowHandle::Wayland(handle) Ok(RawWindowHandle::Wayland(handle))
} }
fn create_surface( fn create_surface(
@ -147,11 +147,17 @@ fn create_surface(
surface_handle: RawWindowHandle, surface_handle: RawWindowHandle,
size: PhysicalSize, size: PhysicalSize,
) -> Result<Surface<WindowSurface>> { ) -> Result<Surface<WindowSurface>> {
let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build( let Some(width) = NonZeroU32::new(size.width) else {
surface_handle, return Err(anyhow!("Width cannot be zero"));
NonZeroU32::new(size.width).unwrap(), };
NonZeroU32::new(size.height).unwrap(),
); let Some(height) = NonZeroU32::new(size.height) else {
return Err(anyhow!("Height cannot be zero"));
};
let attrs =
SurfaceAttributesBuilder::<WindowSurface>::new().build(surface_handle, width, height);
unsafe { glutin_display.create_window_surface(config, &attrs) } unsafe { glutin_display.create_window_surface(config, &attrs) }
.map_err(|e| anyhow!("Failed to create window surface: {}", e)) .map_err(|e| anyhow!("Failed to create window surface: {}", e))
} }

View File

@ -1,3 +1,4 @@
use anyhow::{anyhow, Result};
use log::info; use log::info;
use slint::{ use slint::{
platform::{femtovg_renderer::FemtoVGRenderer, Renderer, WindowAdapter, WindowEvent}, platform::{femtovg_renderer::FemtoVGRenderer, Renderer, WindowAdapter, WindowEvent},
@ -33,15 +34,16 @@ impl FemtoVGWindow {
}) })
} }
pub fn render_frame_if_dirty(&self) { pub fn render_frame_if_dirty(&self) -> Result<()> {
if matches!( if matches!(
self.render_state.replace(RenderState::Clean), self.render_state.replace(RenderState::Clean),
RenderState::Dirty RenderState::Dirty
) { ) {
if let Err(e) = self.renderer.render() { self.renderer
log::error!("Error rendering frame: {}", e); .render()
} .map_err(|e| anyhow!("Error rendering frame: {}", e))?;
} }
Ok(())
} }
pub fn set_scale_factor(&self, scale_factor: f32) { pub fn set_scale_factor(&self, scale_factor: f32) {

View File

@ -1,14 +1,12 @@
use self::state::WindowState; use self::state::WindowState;
use crate::{ use crate::{
bind_globals, bind_globals,
rendering::{ rendering::{egl_context::EGLContext, femtovg_window::FemtoVGWindow},
egl_context::EGLContext, femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform,
},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use config::WindowConfig; use config::WindowConfig;
use log::{debug, info}; use log::{debug, error, info};
use slint::{platform::femtovg_renderer::FemtoVGRenderer, ComponentHandle, LogicalPosition}; use slint::{platform::femtovg_renderer::FemtoVGRenderer, LogicalPosition};
use slint_interpreter::ComponentInstance; use slint_interpreter::ComponentInstance;
use smithay_client_toolkit::reexports::{ use smithay_client_toolkit::reexports::{
calloop::{self, EventLoop, Interest, LoopHandle, Mode, PostAction}, calloop::{self, EventLoop, Interest, LoopHandle, Mode, PostAction},
@ -49,7 +47,7 @@ impl WindowingSystem {
let (compositor, output, layer_shell, seat) = let (compositor, output, layer_shell, seat) =
Self::bind_globals(&global_list, &event_queue.handle())?; Self::bind_globals(&global_list, &event_queue.handle())?;
let mut state = WindowState::new(config); let mut state = WindowState::new(config)?;
Self::setup_surface( Self::setup_surface(
&compositor, &compositor,
@ -164,7 +162,9 @@ impl WindowingSystem {
fn create_renderer(state: &WindowState, display: &WlDisplay) -> Result<FemtoVGRenderer> { fn create_renderer(state: &WindowState, display: &WlDisplay) -> Result<FemtoVGRenderer> {
let size = state.size(); let size = state.size();
let surface = state.surface().unwrap(); let surface = state
.surface()
.ok_or_else(|| anyhow::anyhow!("Failed to get surface"))?;
debug!("Creating EGL context with size: {:?}", size); debug!("Creating EGL context with size: {:?}", size);
let context = EGLContext::builder() let context = EGLContext::builder()
@ -205,48 +205,83 @@ impl WindowingSystem {
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
info!("Starting WindowingSystem main loop"); info!("Starting WindowingSystem main loop");
if let Some(window) = &self.state.window() {
window.render_frame_if_dirty(); self.initialize_component()?;
} self.setup_wayland_event_source()?;
self.state.show_component()?;
self.setup_wayland_event_source(); let connection = Rc::clone(&self.connection);
let event_queue = &mut self.event_queue;
self.event_loop self.event_loop
.run(None, &mut self.state, |shared_data| { .run(None, &mut self.state, move |shared_data| {
let _ = self.connection.flush(); if let Err(e) =
if let Some(guard) = self.event_queue.prepare_read() { Self::process_events(&Rc::clone(&connection), event_queue, shared_data)
let _ = guard.read(); {
error!("Error processing events: {}", e);
} }
let _ = self.event_queue.dispatch_pending(shared_data);
slint::platform::update_timers_and_animations();
shared_data
.window()
.as_ref()
.unwrap()
.render_frame_if_dirty();
}) })
.map_err(|e| anyhow::anyhow!("Failed to run event loop: {}", e)) .map_err(|e| anyhow::anyhow!("Failed to run event loop: {}", e))
} }
pub fn setup_wayland_event_source(&self) { fn initialize_component(&mut self) -> Result<()> {
if let Some(window) = &self.state.window() {
window.render_frame_if_dirty()?;
} else {
return Err(anyhow::anyhow!("Window not initialized"));
}
self.state.show_component()?;
Ok(())
}
fn setup_wayland_event_source(&self) -> Result<()> {
debug!("Setting up Wayland event source"); debug!("Setting up Wayland event source");
let connection = Rc::clone(&self.connection); let connection = Rc::clone(&self.connection);
let _ = self.event_loop.handle().insert_source( self.event_loop
calloop::generic::Generic::new(connection, Interest::READ, Mode::Level), .handle()
move |_, _connection, _shared_data| Ok(PostAction::Continue), .insert_source(
); calloop::generic::Generic::new(connection, Interest::READ, Mode::Level),
move |_, _connection, _shared_data| Ok(PostAction::Continue),
)
.map_err(|e| anyhow::anyhow!("Failed to set up Wayland event source: {}", e))?;
Ok(())
} }
pub fn component_instance(&self) -> &ComponentInstance { fn process_events(
connection: &Rc<Connection>,
event_queue: &mut EventQueue<WindowState>,
shared_data: &mut WindowState,
) -> Result<()> {
connection.flush()?;
if let Some(guard) = event_queue.prepare_read() {
guard
.read()
.map_err(|e| anyhow::anyhow!("Failed to read events: {}", e))?;
}
event_queue.dispatch_pending(shared_data)?;
slint::platform::update_timers_and_animations();
if let Some(window) = shared_data.window() {
window.render_frame_if_dirty()?;
} else {
return Err(anyhow::anyhow!("Window not initialized"));
}
Ok(())
}
pub const fn component_instance(&self) -> Option<&ComponentInstance> {
self.state.component_instance() self.state.component_instance()
} }
pub fn window(&self) -> Rc<FemtoVGWindow> { pub fn window(&self) -> Option<Rc<FemtoVGWindow>> {
Rc::clone(self.state().window().as_ref().unwrap()) self.state().window().as_ref().map(Rc::clone)
} }
pub const fn state(&self) -> &WindowState { pub const fn state(&self) -> &WindowState {

View File

@ -1,5 +1,5 @@
use crate::impl_empty_dispatch; use crate::impl_empty_dispatch;
use log::info; use log::{info, warn};
use slint::{ use slint::{
platform::{PointerEventButton, WindowEvent}, platform::{PointerEventButton, WindowEvent},
PhysicalSize, PhysicalSize,
@ -42,10 +42,20 @@ impl Dispatch<ZwlrLayerSurfaceV1, ()> for WindowState {
info!("Layer surface configured with size: {}x{}", width, height); info!("Layer surface configured with size: {}x{}", width, height);
layer_surface.ack_configure(serial); layer_surface.ack_configure(serial);
if width > 0 && height > 0 { if width > 0 && height > 0 {
state.update_size(state.output_size().width, state.height()); state
.update_size(state.output_size().width, state.height())
.unwrap_or(warn!(
"Failed to update window size with width: {} and height: {}",
width, height
));
} else { } else {
let current_size = state.output_size(); let current_size = state.output_size();
state.update_size(current_size.width, current_size.height); state
.update_size(current_size.width, current_size.height)
.unwrap_or(warn!(
"Failed to update window size with width: {} and height: {}",
width, height
));
} }
} }
zwlr_layer_surface_v1::Event::Closed => { zwlr_layer_surface_v1::Event::Closed => {

View File

@ -1,4 +1,4 @@
use std::{rc::Rc}; use std::rc::Rc;
use log::info; use log::info;
use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
use slint_interpreter::{ComponentDefinition, ComponentInstance}; use slint_interpreter::{ComponentDefinition, ComponentInstance};
@ -26,8 +26,13 @@ pub struct WindowState {
} }
impl WindowState { impl WindowState {
pub fn new(config: &mut WindowConfig) -> Self { pub fn new(config: &mut WindowConfig) -> Result<Self> {
Self { let component_definition = config
.component_definition
.take()
.ok_or_else(|| anyhow::anyhow!("Component definition is required"))?;
Ok(Self {
surface: None, surface: None,
layer_surface: None, layer_surface: None,
size: PhysicalSize::default(), size: PhysicalSize::default(),
@ -38,9 +43,9 @@ impl WindowState {
scale_factor: config.scale_factor, scale_factor: config.scale_factor,
height: config.height, height: config.height,
exclusive_zone: config.exclusive_zone, exclusive_zone: config.exclusive_zone,
component_definition: config.component_definition.take().unwrap(), component_definition,
component_instance: None, component_instance: None,
} })
} }
pub fn show_component(&mut self) -> Result<()> { pub fn show_component(&mut self) -> Result<()> {
@ -52,11 +57,16 @@ impl WindowState {
self.component_instance = Some(self.component_definition.create()?); self.component_instance = Some(self.component_definition.create()?);
self.component_instance.as_ref().unwrap().show()?; if let Some(component_instance) = &self.component_instance {
component_instance.show()?;
} else {
return Err(anyhow::anyhow!("Component instance not initialized"));
}
Ok(()) Ok(())
} }
pub fn update_size(&mut self, width: u32, height: u32) { pub fn update_size(&mut self, width: u32, height: u32) -> Result<()> {
let new_size = PhysicalSize::new(width, height); let new_size = PhysicalSize::new(width, height);
if let Some(window) = &self.window() { if let Some(window) = &self.window() {
info!("Updating window size to {}x{}", width, height); info!("Updating window size to {}x{}", width, height);
@ -70,10 +80,14 @@ impl WindowState {
layer_surface.set_exclusive_zone(self.exclusive_zone); layer_surface.set_exclusive_zone(self.exclusive_zone);
} }
if let Some(s) = self.surface.as_ref() { match self.surface.as_ref() {
s.commit(); Some(surface) => surface.commit(),
None => {
return Err(anyhow::anyhow!("Surface not initialized"));
}
} }
self.size = new_size; self.size = new_size;
Ok(())
} }
pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) { pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) {
@ -130,7 +144,7 @@ impl WindowState {
self.pointer = Some(pointer); self.pointer = Some(pointer);
} }
pub fn component_instance(&self) -> &ComponentInstance { pub const fn component_instance(&self) -> Option<&ComponentInstance> {
self.component_instance.as_ref().unwrap() self.component_instance.as_ref()
} }
} }