refactor: modularize ev loop and improve error propagation
parent
6654e0f9bc
commit
9b7818e280
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue