refactor: state builder to get rid of option fields

WIP/draft
drendog 2024-08-20 12:18:56 +02:00
parent 9b7818e280
commit 0759b688cf
Signed by: dwenya
GPG Key ID: 8DD77074645332D0
4 changed files with 217 additions and 227 deletions

View File

@ -4,9 +4,8 @@ use crate::{
rendering::{egl_context::EGLContext, femtovg_window::FemtoVGWindow}, rendering::{egl_context::EGLContext, femtovg_window::FemtoVGWindow},
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use config::WindowConfig;
use log::{debug, error, info}; use log::{debug, error, info};
use slint::{platform::femtovg_renderer::FemtoVGRenderer, LogicalPosition}; use slint::{platform::femtovg_renderer::FemtoVGRenderer, LogicalPosition, PhysicalSize};
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},
@ -14,6 +13,7 @@ use smithay_client_toolkit::reexports::{
zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
}, },
}; };
use state::builder::WindowStateBuilder;
use std::rc::Rc; use std::rc::Rc;
use wayland_client::{ use wayland_client::{
globals::{registry_queue_init, GlobalList}, globals::{registry_queue_init, GlobalList},
@ -37,32 +37,46 @@ pub struct WindowingSystem {
} }
impl WindowingSystem { impl WindowingSystem {
fn new(config: &mut WindowConfig) -> Result<Self> { fn new(config: &mut config::WindowConfig) -> Result<Self> {
info!("Initializing WindowingSystem"); info!("Initializing WindowingSystem");
let connection = Rc::new(Connection::connect_to_env()?); let connection = Rc::new(Connection::connect_to_env()?);
let global_list = Self::initialize_registry(&connection)?; let global_list = Self::initialize_registry(&connection)?;
let mut event_queue = connection.new_event_queue(); let event_queue = connection.new_event_queue();
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 surface = Rc::new(compositor.create_surface(&event_queue.handle(), ()));
let layer_surface = Rc::new(layer_shell.get_layer_surface(
Self::setup_surface( &surface,
&compositor, Some(&output),
&output, config.layer,
&layer_shell, config.namespace.clone(),
&seat,
&event_queue.handle(), &event_queue.handle(),
&mut state, (),
config, ));
);
Self::wait_for_configure(&mut event_queue, &mut state)?; let pointer = Rc::new(seat.get_pointer(&event_queue.handle(), ()));
Self::configure_layer_surface(&layer_surface, &surface, config);
let mut state_builder = WindowStateBuilder::new()
.component_definition(config.component_definition.take().unwrap())
.surface(Rc::clone(&surface))
.layer_surface(Rc::clone(&layer_surface))
.pointer(Rc::clone(&pointer))
.scale_factor(config.scale_factor)
.height(config.height)
.exclusive_zone(config.exclusive_zone);
//Self::wait_for_configure(&mut event_queue, &mut state_builder)?;
let display = connection.display(); let display = connection.display();
Self::initialize_renderer(&mut state, &display, config)?; let window = Self::initialize_renderer(&state_builder, &display, config)?;
state_builder = state_builder.window(window);
let state = state_builder.build()?;
let event_loop = EventLoop::try_new().context("Failed to create event loop")?; let event_loop = EventLoop::try_new().context("Failed to create event loop")?;
@ -94,38 +108,10 @@ impl WindowingSystem {
) )
} }
fn setup_surface(
compositor: &WlCompositor,
output: &WlOutput,
layer_shell: &ZwlrLayerShellV1,
seat: &WlSeat,
queue_handle: &QueueHandle<WindowState>,
state: &mut WindowState,
config: &WindowConfig,
) {
let surface = Rc::new(compositor.create_surface(queue_handle, ()));
let layer_surface = Rc::new(layer_shell.get_layer_surface(
&surface,
Some(output),
config.layer,
config.namespace.clone(),
queue_handle,
(),
));
let pointer = Rc::new(seat.get_pointer(queue_handle, ()));
state.set_surface(Rc::clone(&surface));
state.set_layer_surface(Rc::clone(&layer_surface));
state.set_pointer(pointer);
Self::configure_layer_surface(&layer_surface, &surface, config);
}
fn configure_layer_surface( fn configure_layer_surface(
layer_surface: &Rc<ZwlrLayerSurfaceV1>, layer_surface: &Rc<ZwlrLayerSurfaceV1>,
surface: &WlSurface, surface: &WlSurface,
config: &WindowConfig, config: &config::WindowConfig,
) { ) {
layer_surface.set_anchor(config.anchor); layer_surface.set_anchor(config.anchor);
layer_surface.set_margin( layer_surface.set_margin(
@ -141,29 +127,14 @@ impl WindowingSystem {
surface.commit(); surface.commit();
} }
fn wait_for_configure( fn create_renderer(
event_queue: &mut EventQueue<WindowState>, state_builder: &WindowStateBuilder,
state: &mut WindowState, display: &WlDisplay,
) -> Result<()> { ) -> Result<FemtoVGRenderer> {
info!("Waiting for surface to be configured..."); let size = state_builder.size.unwrap_or(PhysicalSize::new(1, 1));
event_queue let surface = state_builder
.blocking_dispatch(state) .surface
.context("Failed to dispatch events")?; .as_ref()
info!("Blocking dispatch completed");
let size = state.output_size();
if size.width > 1 && size.height > 1 {
info!("Configured output size: {:?}", size);
} else {
return Err(anyhow::anyhow!("Invalid output size: {:?}", size));
}
debug!("Surface configuration complete");
Ok(())
}
fn create_renderer(state: &WindowState, display: &WlDisplay) -> Result<FemtoVGRenderer> {
let size = state.size();
let surface = state
.surface()
.ok_or_else(|| anyhow::anyhow!("Failed to get surface"))?; .ok_or_else(|| anyhow::anyhow!("Failed to get surface"))?;
debug!("Creating EGL context with size: {:?}", size); debug!("Creating EGL context with size: {:?}", size);
@ -179,14 +150,14 @@ impl WindowingSystem {
} }
fn initialize_renderer( fn initialize_renderer(
state: &mut WindowState, state_builder: &WindowStateBuilder,
display: &WlDisplay, display: &WlDisplay,
config: &WindowConfig, config: &config::WindowConfig,
) -> Result<()> { ) -> Result<Rc<FemtoVGWindow>> {
let renderer = Self::create_renderer(state, display)?; let renderer = Self::create_renderer(state_builder, display)?;
let femtovg_window = FemtoVGWindow::new(renderer); let femtovg_window = FemtoVGWindow::new(renderer);
let size = state.size(); let size = state_builder.size.unwrap_or_default();
info!("Initializing UI with size: {:?}", size); info!("Initializing UI with size: {:?}", size);
femtovg_window.set_size(slint::WindowSize::Physical(size)); femtovg_window.set_size(slint::WindowSize::Physical(size));
femtovg_window.set_scale_factor(config.scale_factor); femtovg_window.set_scale_factor(config.scale_factor);
@ -194,9 +165,7 @@ impl WindowingSystem {
debug!("Creating Slint component instance"); debug!("Creating Slint component instance");
state.set_window(Rc::clone(&femtovg_window)); Ok(femtovg_window)
Ok(())
} }
pub fn event_loop_handle(&self) -> LoopHandle<'static, WindowState> { pub fn event_loop_handle(&self) -> LoopHandle<'static, WindowState> {
@ -206,7 +175,7 @@ impl WindowingSystem {
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
info!("Starting WindowingSystem main loop"); info!("Starting WindowingSystem main loop");
self.initialize_component()?; self.state.window().render_frame_if_dirty()?;
self.setup_wayland_event_source()?; self.setup_wayland_event_source()?;
let connection = Rc::clone(&self.connection); let connection = Rc::clone(&self.connection);
@ -223,17 +192,6 @@ impl WindowingSystem {
.map_err(|e| anyhow::anyhow!("Failed to run event loop: {}", e)) .map_err(|e| anyhow::anyhow!("Failed to run event loop: {}", e))
} }
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<()> { fn setup_wayland_event_source(&self) -> Result<()> {
debug!("Setting up Wayland event source"); debug!("Setting up Wayland event source");
@ -267,21 +225,17 @@ impl WindowingSystem {
slint::platform::update_timers_and_animations(); slint::platform::update_timers_and_animations();
if let Some(window) = shared_data.window() { shared_data.window().render_frame_if_dirty()?;
window.render_frame_if_dirty()?;
} else {
return Err(anyhow::anyhow!("Window not initialized"));
}
Ok(()) Ok(())
} }
pub const fn component_instance(&self) -> Option<&ComponentInstance> { pub const fn component_instance(&self) -> &ComponentInstance {
self.state.component_instance() self.state.component_instance()
} }
pub fn window(&self) -> Option<Rc<FemtoVGWindow>> { pub fn window(&self) -> Rc<FemtoVGWindow> {
self.state().window().as_ref().map(Rc::clone) self.state.window()
} }
pub const fn state(&self) -> &WindowState { pub const fn state(&self) -> &WindowState {

View File

@ -0,0 +1,98 @@
use std::rc::Rc;
use slint::PhysicalSize;
use slint_interpreter::ComponentDefinition;
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use wayland_client::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface};
use crate::rendering::{femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform};
use anyhow::Result;
use super::WindowState;
#[derive(Clone)]
pub struct WindowStateBuilder {
pub component_definition: Option<ComponentDefinition>,
pub surface: Option<Rc<WlSurface>>,
pub layer_surface: Option<Rc<ZwlrLayerSurfaceV1>>,
pub size: Option<PhysicalSize>,
pub output_size: Option<PhysicalSize>,
pub pointer: Option<Rc<WlPointer>>,
pub window: Option<Rc<FemtoVGWindow>>,
pub scale_factor: f32,
pub height: u32,
pub exclusive_zone: i32,
}
impl WindowStateBuilder {
pub const fn new() -> Self {
Self {
component_definition: None,
surface: None,
layer_surface: None,
size: None,
output_size: None,
pointer: None,
window: None,
scale_factor: 1.0,
height: 30,
exclusive_zone: -1,
}
}
pub fn surface(mut self, surface: Rc<WlSurface>) -> Self {
self.surface = Some(surface);
self
}
pub fn layer_surface(mut self, layer_surface: Rc<ZwlrLayerSurfaceV1>) -> Self {
self.layer_surface = Some(layer_surface);
self
}
pub const fn size(mut self, size: PhysicalSize) -> Self {
self.size = Some(size);
self
}
pub const fn output_size(mut self, output_size: PhysicalSize) -> Self {
self.output_size = Some(output_size);
self
}
pub fn pointer(mut self, pointer: Rc<WlPointer>) -> Self {
self.pointer = Some(pointer);
self
}
pub fn window(mut self, window: Rc<FemtoVGWindow>) -> Self {
self.window = Some(window);
self
}
pub const fn scale_factor(mut self, scale_factor: f32) -> Self {
self.scale_factor = scale_factor;
self
}
pub const fn height(mut self, height: u32) -> Self {
self.height = height;
self
}
pub const fn exclusive_zone(mut self, exclusive_zone: i32) -> Self {
self.exclusive_zone = exclusive_zone;
self
}
pub fn component_definition(mut self, component_definition: ComponentDefinition) -> Self {
self.component_definition = Some(component_definition);
self
}
pub fn build(self) -> Result<WindowState> {
let platform = CustomSlintPlatform::new(Rc::clone(self.window.as_ref().unwrap()));
slint::platform::set_platform(Box::new(platform))
.map_err(|e| anyhow::anyhow!("Failed to set platform: {:?}", e))?;
WindowState::new(self)
}
}

View File

@ -1,5 +1,5 @@
use crate::impl_empty_dispatch; use crate::impl_empty_dispatch;
use log::{info, warn}; use log::info;
use slint::{ use slint::{
platform::{PointerEventButton, WindowEvent}, platform::{PointerEventButton, WindowEvent},
PhysicalSize, PhysicalSize,
@ -42,20 +42,10 @@ 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 state.update_size(state.output_size().width, state.height());
.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 state.update_size(current_size.width, current_size.height);
.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 => {
@ -125,33 +115,23 @@ impl Dispatch<WlPointer, ()> for WindowState {
surface_x, surface_x,
surface_y, surface_y,
.. ..
} => {
state.set_current_pointer_position(surface_x, surface_y);
let logical_position = state.current_pointer_position();
if let Some(window) = state.window() {
window.dispatch_event(WindowEvent::PointerMoved {
position: logical_position,
});
} }
} | wl_pointer::Event::Motion {
wl_pointer::Event::Leave { .. } => {
if let Some(window) = state.window() {
window.dispatch_event(WindowEvent::PointerExited);
}
}
wl_pointer::Event::Motion {
surface_x, surface_x,
surface_y, surface_y,
.. ..
} => { } => {
state.set_current_pointer_position(surface_x, surface_y); state.set_current_pointer_position(surface_x, surface_y);
if let Some(window) = state.window() {
let logical_position = state.current_pointer_position(); let logical_position = state.current_pointer_position();
window.dispatch_event(WindowEvent::PointerMoved { state.window().dispatch_event(WindowEvent::PointerMoved {
position: logical_position, position: *logical_position,
}); });
} }
wl_pointer::Event::Leave { .. } => {
state.window().dispatch_event(WindowEvent::PointerExited);
} }
wl_pointer::Event::Button { wl_pointer::Event::Button {
state: button_state, state: button_state,
.. ..
@ -159,16 +139,14 @@ impl Dispatch<WlPointer, ()> for WindowState {
let event = match button_state { let event = match button_state {
WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed { WEnum::Value(wl_pointer::ButtonState::Pressed) => WindowEvent::PointerPressed {
button: PointerEventButton::Left, button: PointerEventButton::Left,
position: state.current_pointer_position(), position: *state.current_pointer_position(),
}, },
_ => WindowEvent::PointerReleased { _ => WindowEvent::PointerReleased {
button: PointerEventButton::Left, button: PointerEventButton::Left,
position: state.current_pointer_position(), position: *state.current_pointer_position(),
}, },
}; };
if let Some(window) = state.window() { state.window().dispatch_event(event);
window.dispatch_event(event);
}
} }
_ => {} _ => {}
} }

View File

@ -1,24 +1,23 @@
use std::rc::Rc; use std::rc::Rc;
use builder::WindowStateBuilder;
use log::info; use log::info;
use slint::{LogicalPosition, PhysicalSize, ComponentHandle}; use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
use slint_interpreter::{ComponentDefinition, ComponentInstance}; use slint_interpreter::ComponentInstance;
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use wayland_client::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface}; use wayland_client::protocol::wl_surface::WlSurface;
use crate::rendering::{femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform}; use crate::rendering::femtovg_window::FemtoVGWindow;
use super::WindowConfig; use anyhow::{Context, Result};
use anyhow::Result;
pub mod builder;
pub mod dispatches; pub mod dispatches;
pub struct WindowState { pub struct WindowState {
component_definition: ComponentDefinition, component_instance: ComponentInstance,
component_instance: Option<ComponentInstance>, surface: Rc<WlSurface>,
surface: Option<Rc<WlSurface>>, layer_surface: Rc<ZwlrLayerSurfaceV1>,
layer_surface: Option<Rc<ZwlrLayerSurfaceV1>>,
size: PhysicalSize, size: PhysicalSize,
output_size: PhysicalSize, output_size: PhysicalSize,
pointer: Option<Rc<WlPointer>>, window: Rc<FemtoVGWindow>,
window: Option<Rc<FemtoVGWindow>>,
current_pointer_position: LogicalPosition, current_pointer_position: LogicalPosition,
scale_factor: f32, scale_factor: f32,
height: u32, height: u32,
@ -26,68 +25,42 @@ pub struct WindowState {
} }
impl WindowState { impl WindowState {
pub fn new(config: &mut WindowConfig) -> Result<Self> { pub fn new(builder: WindowStateBuilder) -> Result<Self> {
let component_definition = config let component_definition = builder
.component_definition .component_definition
.take() .context("Component definition is required")?;
.ok_or_else(|| anyhow::anyhow!("Component definition is required"))?; let component_instance = component_definition
.create()
.context("Failed to create component instance")?;
component_instance
.show()
.context("Failed to show component")?;
Ok(Self { Ok(Self {
surface: None, component_instance,
layer_surface: None, surface: builder.surface.context("Surface is required")?,
size: PhysicalSize::default(), layer_surface: builder.layer_surface.context("Layer surface is required")?,
output_size: PhysicalSize::default(), size: builder.size.unwrap_or_default(),
pointer: None, output_size: builder.output_size.unwrap_or_default(),
window: None, window: builder.window.context("Window is required")?,
current_pointer_position: LogicalPosition::default(), current_pointer_position: LogicalPosition::default(),
scale_factor: config.scale_factor, scale_factor: builder.scale_factor,
height: config.height, height: builder.height,
exclusive_zone: config.exclusive_zone, exclusive_zone: builder.exclusive_zone,
component_definition,
component_instance: None,
}) })
} }
pub fn show_component(&mut self) -> Result<()> { pub fn update_size(&mut self, width: u32, height: u32) {
if let Some(window) = &self.window {
let platform = CustomSlintPlatform::new(Rc::clone(window));
slint::platform::set_platform(Box::new(platform))
.map_err(|e| anyhow::anyhow!("Failed to set platform: {:?}", e))?;
}
self.component_instance = Some(self.component_definition.create()?);
if let Some(component_instance) = &self.component_instance {
component_instance.show()?;
} else {
return Err(anyhow::anyhow!("Component instance not initialized"));
}
Ok(())
}
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() {
info!("Updating window size to {}x{}", width, height); info!("Updating window size to {}x{}", width, height);
window.set_size(slint::WindowSize::Physical(new_size)); self.window.set_size(slint::WindowSize::Physical(new_size));
window.set_scale_factor(self.scale_factor); self.window.set_scale_factor(self.scale_factor);
}
if let Some(layer_surface) = &self.layer_surface() {
info!("Updating layer surface size to {}x{}", width, height); info!("Updating layer surface size to {}x{}", width, height);
layer_surface.set_size(width, height); self.layer_surface.set_size(width, height);
layer_surface.set_exclusive_zone(self.exclusive_zone); self.layer_surface.set_exclusive_zone(self.exclusive_zone);
}
match self.surface.as_ref() { self.surface.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) {
@ -99,22 +72,24 @@ impl WindowState {
self.current_pointer_position = logical_position; self.current_pointer_position = logical_position;
} }
pub const fn size(&self) -> PhysicalSize { pub const fn size(&self) -> &PhysicalSize {
self.size &self.size
} }
pub const fn current_pointer_position(&self) -> LogicalPosition { pub const fn current_pointer_position(&self) -> &LogicalPosition {
self.current_pointer_position &self.current_pointer_position
}
pub fn window(&self) -> Option<Rc<FemtoVGWindow>> {
self.window.clone()
} }
pub fn layer_surface(&self) -> Option<Rc<ZwlrLayerSurfaceV1>> { pub fn window(&self) -> Rc<FemtoVGWindow> {
self.layer_surface.clone() Rc::clone(&self.window)
} }
pub fn surface(&self) -> Option<Rc<WlSurface>> {
self.surface.clone() pub fn layer_surface(&self) -> Rc<ZwlrLayerSurfaceV1> {
Rc::clone(&self.layer_surface)
}
pub fn surface(&self) -> Rc<WlSurface> {
Rc::clone(&self.surface)
} }
pub const fn height(&self) -> u32 { pub const fn height(&self) -> u32 {
@ -125,26 +100,11 @@ impl WindowState {
self.output_size = output_size; self.output_size = output_size;
} }
pub const fn output_size(&self) -> PhysicalSize { pub const fn output_size(&self) -> &PhysicalSize {
self.output_size &self.output_size
} }
pub fn set_window(&mut self, window: Rc<FemtoVGWindow>) { pub const fn component_instance(&self) -> &ComponentInstance {
self.window = Some(window); &self.component_instance
}
pub fn set_layer_surface(&mut self, layer_surface: Rc<ZwlrLayerSurfaceV1>) {
self.layer_surface = Some(layer_surface);
}
pub fn set_surface(&mut self, surface: Rc<WlSurface>) {
self.surface = Some(surface);
}
pub fn set_pointer(&mut self, pointer: Rc<WlPointer>) {
self.pointer = Some(pointer);
}
pub const fn component_instance(&self) -> Option<&ComponentInstance> {
self.component_instance.as_ref()
} }
} }