From ccf0c9caf82e32fc5752f5fded6f6ec8d5a47bce Mon Sep 17 00:00:00 2001 From: drendog Date: Wed, 3 Dec 2025 20:00:11 +0100 Subject: [PATCH] refactor: consolidate event loop --- crates/composition/src/event_loop.rs | 109 ++++++++++++++++++ crates/composition/src/lib.rs | 1 + crates/composition/src/shell.rs | 161 +++++++++++---------------- crates/composition/src/system.rs | 111 ++---------------- 4 files changed, 186 insertions(+), 196 deletions(-) create mode 100644 crates/composition/src/event_loop.rs diff --git a/crates/composition/src/event_loop.rs b/crates/composition/src/event_loop.rs new file mode 100644 index 0000000..7dc0aca --- /dev/null +++ b/crates/composition/src/event_loop.rs @@ -0,0 +1,109 @@ +use crate::{Error, Result}; +use layer_shika_adapters::errors::EventLoopError; +use layer_shika_adapters::platform::calloop::{ + EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, + channel, +}; +use layer_shika_adapters::{AppState, WindowingSystemFacade}; +use std::cell::RefCell; +use std::marker::PhantomData; +use std::os::unix::io::AsFd; +use std::rc::Weak; +use std::time::{Duration, Instant}; + +pub trait FromAppState<'a> { + fn from_app_state(app_state: &'a mut AppState) -> Self; +} + +pub struct EventLoopHandleBase { + system: Weak>, + _marker: PhantomData Ctx>, +} + +impl EventLoopHandleBase { + pub fn new(system: Weak>) -> Self { + Self { + system, + _marker: PhantomData, + } + } +} + +impl EventLoopHandleBase +where + for<'a> Ctx: FromAppState<'a> + 'a, +{ + pub fn insert_source(&self, source: S, mut callback: F) -> Result + where + S: EventSource + 'static, + F: FnMut(S::Event, &mut S::Metadata, Ctx) -> R + 'static, + { + let system = self.system.upgrade().ok_or(Error::SystemDropped)?; + let loop_handle = system.borrow().inner_ref().event_loop_handle(); + + loop_handle + .insert_source(source, move |event, metadata, app_state| { + let ctx = Ctx::from_app_state(app_state); + callback(event, metadata, ctx) + }) + .map_err(|e| { + Error::Adapter( + EventLoopError::InsertSource { + message: format!("{e:?}"), + } + .into(), + ) + }) + } + + pub fn add_timer(&self, duration: Duration, mut callback: F) -> Result + where + F: FnMut(Instant, Ctx) -> TimeoutAction + 'static, + { + let timer = Timer::from_duration(duration); + self.insert_source(timer, move |deadline, (), ctx| callback(deadline, ctx)) + } + + pub fn add_timer_at(&self, deadline: Instant, mut callback: F) -> Result + where + F: FnMut(Instant, Ctx) -> TimeoutAction + 'static, + { + let timer = Timer::from_deadline(deadline); + self.insert_source(timer, move |deadline, (), ctx| callback(deadline, ctx)) + } + + pub fn add_channel( + &self, + mut callback: F, + ) -> Result<(RegistrationToken, channel::Sender)> + where + T: 'static, + F: FnMut(T, Ctx) + 'static, + { + let (sender, receiver) = channel::channel(); + let token = self.insert_source(receiver, move |event, (), ctx| { + if let channel::Event::Msg(msg) = event { + callback(msg, ctx); + } + })?; + Ok((token, sender)) + } + + pub fn add_fd( + &self, + fd: T, + interest: Interest, + mode: Mode, + mut callback: F, + ) -> Result + where + T: AsFd + 'static, + F: FnMut(Ctx) + 'static, + { + let generic = Generic::new(fd, interest, mode); + self.insert_source(generic, move |_readiness, _fd, ctx| { + callback(ctx); + Ok(PostAction::Continue) + }) + } +} diff --git a/crates/composition/src/lib.rs b/crates/composition/src/lib.rs index c76d640..c76d1fe 100644 --- a/crates/composition/src/lib.rs +++ b/crates/composition/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::pub_use)] mod builder; +mod event_loop; mod popup_builder; mod shell; mod shell_composition; diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index 223d8e1..7d33b46 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -1,13 +1,11 @@ +use crate::event_loop::{EventLoopHandleBase, FromAppState}; use crate::shell_composition::ShellWindowDefinition; use crate::shell_runtime::ShellRuntime; use crate::system::{EventContext, PopupCommand, ShellControl}; use crate::value_conversion::IntoValue; use crate::{Error, Result}; use layer_shika_adapters::errors::EventLoopError; -use layer_shika_adapters::platform::calloop::{ - EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, - channel, -}; +use layer_shika_adapters::platform::calloop::channel; use layer_shika_adapters::platform::slint_interpreter::{ CompilationResult, ComponentInstance, Value, }; @@ -16,16 +14,16 @@ use layer_shika_adapters::{ AppState, ShellWindowConfig, WaylandWindowConfig, WindowState, WindowingSystemFacade, }; use layer_shika_domain::config::WindowConfig; +use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::errors::DomainError; use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity; use layer_shika_domain::value_objects::layer::Layer; use layer_shika_domain::value_objects::margins::Margins; +use layer_shika_domain::value_objects::output_handle::OutputHandle; +use layer_shika_domain::value_objects::output_info::OutputInfo; use std::cell::RefCell; use std::collections::HashMap; -use std::os::unix::io::AsFd; -use std::rc::{Rc, Weak}; -use std::result::Result as StdResult; -use std::time::{Duration, Instant}; +use std::rc::Rc; pub struct LayerSurfaceHandle<'a> { window_state: &'a WindowState, @@ -324,9 +322,7 @@ impl Shell { } pub fn event_loop_handle(&self) -> ShellEventLoopHandle { - ShellEventLoopHandle { - system: Rc::downgrade(&self.inner), - } + ShellEventLoopHandle::new(Rc::downgrade(&self.inner)) } pub fn run(&mut self) -> Result<()> { @@ -516,9 +512,7 @@ impl ShellRuntime for Shell { type Context<'a> = ShellEventContext<'a>; fn event_loop_handle(&self) -> Self::LoopHandle { - ShellEventLoopHandle { - system: Rc::downgrade(&self.inner), - } + ShellEventLoopHandle::new(Rc::downgrade(&self.inner)) } fn with_component(&self, name: &str, mut f: F) @@ -559,102 +553,36 @@ impl ShellRuntime for Shell { } } -pub struct ShellEventLoopHandle { - system: Weak>, -} - -impl ShellEventLoopHandle { - pub fn insert_source( - &self, - source: S, - mut callback: F, - ) -> StdResult - where - S: EventSource + 'static, - F: FnMut(S::Event, &mut S::Metadata, ShellEventContext<'_>) -> R + 'static, - { - let system = self.system.upgrade().ok_or(Error::SystemDropped)?; - let loop_handle = system.borrow().inner_ref().event_loop_handle(); - - loop_handle - .insert_source(source, move |event, metadata, app_state| { - let ctx = ShellEventContext { app_state }; - callback(event, metadata, ctx) - }) - .map_err(|e| { - Error::Adapter( - EventLoopError::InsertSource { - message: format!("{e:?}"), - } - .into(), - ) - }) - } - - pub fn add_timer(&self, duration: Duration, mut callback: F) -> Result - where - F: FnMut(Instant, ShellEventContext<'_>) -> TimeoutAction + 'static, - { - let timer = Timer::from_duration(duration); - self.insert_source(timer, move |deadline, (), ctx| callback(deadline, ctx)) - } - - pub fn add_channel( - &self, - mut callback: F, - ) -> Result<(RegistrationToken, channel::Sender)> - where - T: 'static, - F: FnMut(T, ShellEventContext<'_>) + 'static, - { - let (sender, receiver) = channel::channel(); - let token = self.insert_source(receiver, move |event, (), ctx| { - if let channel::Event::Msg(msg) = event { - callback(msg, ctx); - } - })?; - Ok((token, sender)) - } - - pub fn add_fd( - &self, - fd: T, - interest: Interest, - mode: Mode, - mut callback: F, - ) -> Result - where - T: AsFd + 'static, - F: FnMut(ShellEventContext<'_>) + 'static, - { - let generic = Generic::new(fd, interest, mode); - self.insert_source(generic, move |_readiness, _fd, ctx| { - callback(ctx); - Ok(PostAction::Continue) - }) - } -} +pub type ShellEventLoopHandle = EventLoopHandleBase>; pub struct ShellEventContext<'a> { app_state: &'a mut AppState, } +impl<'a> FromAppState<'a> for ShellEventContext<'a> { + fn from_app_state(app_state: &'a mut AppState) -> Self { + Self { app_state } + } +} + impl ShellEventContext<'_> { pub fn get_shell_window_component( &self, - _shell_window_name: &str, + shell_window_name: &str, ) -> Option<&ComponentInstance> { self.app_state - .primary_output() + .windows_by_shell_name(shell_window_name) + .next() .map(WindowState::component_instance) } pub fn get_shell_window_component_mut( &mut self, - _shell_window_name: &str, + shell_window_name: &str, ) -> Option<&ComponentInstance> { self.app_state - .primary_output() + .windows_by_shell_name(shell_window_name) + .next() .map(WindowState::component_instance) } @@ -670,4 +598,51 @@ impl ShellEventContext<'_> { } Ok(()) } + + #[must_use] + pub fn primary_output_handle(&self) -> Option { + self.app_state.primary_output_handle() + } + + #[must_use] + pub fn active_output_handle(&self) -> Option { + self.app_state.active_output_handle() + } + + pub fn output_registry(&self) -> &OutputRegistry { + self.app_state.output_registry() + } + + pub fn outputs(&self) -> impl Iterator { + self.app_state + .outputs_with_handles() + .map(|(handle, window)| (handle, window.component_instance())) + } + + pub fn get_output_component(&self, handle: OutputHandle) -> Option<&ComponentInstance> { + self.app_state + .get_output_by_handle(handle) + .map(WindowState::component_instance) + } + + pub fn get_output_info(&self, handle: OutputHandle) -> Option<&OutputInfo> { + self.app_state.get_output_info(handle) + } + + pub fn all_output_info(&self) -> impl Iterator { + self.app_state.all_output_info() + } + + pub fn outputs_with_info(&self) -> impl Iterator { + self.app_state + .outputs_with_info() + .map(|(info, window)| (info, window.component_instance())) + } + + #[must_use] + pub fn compilation_result(&self) -> Option> { + self.app_state + .primary_output() + .and_then(WindowState::compilation_result) + } } diff --git a/crates/composition/src/system.rs b/crates/composition/src/system.rs index 1dd48e0..442bbd5 100644 --- a/crates/composition/src/system.rs +++ b/crates/composition/src/system.rs @@ -1,12 +1,10 @@ +use crate::event_loop::{EventLoopHandleBase, FromAppState}; use crate::popup_builder::PopupBuilder; use crate::shell_runtime::{DEFAULT_WINDOW_NAME, ShellRuntime}; use crate::value_conversion::IntoValue; use crate::{Error, Result}; use layer_shika_adapters::errors::EventLoopError; -use layer_shika_adapters::platform::calloop::{ - EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer, - channel, -}; +use layer_shika_adapters::platform::calloop::channel; use layer_shika_adapters::platform::slint::ComponentHandle; use layer_shika_adapters::platform::slint_interpreter::{ CompilationResult, ComponentDefinition, ComponentInstance, Value, @@ -26,10 +24,7 @@ use layer_shika_domain::value_objects::popup_request::{ }; use std::cell::Cell; use std::cell::RefCell; -use std::os::unix::io::AsFd; -use std::rc::{Rc, Weak}; -use std::result::Result as StdResult; -use std::time::{Duration, Instant}; +use std::rc::Rc; pub enum PopupCommand { Show(PopupRequest), @@ -111,100 +106,14 @@ impl ShellControl { } } -pub struct EventLoopHandle { - system: Weak>, -} - -impl EventLoopHandle { - pub fn insert_source( - &self, - source: S, - mut callback: F, - ) -> StdResult - where - S: EventSource + 'static, - F: FnMut(S::Event, &mut S::Metadata, EventContext<'_>) -> R + 'static, - { - let system = self.system.upgrade().ok_or(Error::SystemDropped)?; - let loop_handle = system.borrow().inner_ref().event_loop_handle(); - - loop_handle - .insert_source(source, move |event, metadata, app_state| { - let shell_context = EventContext { app_state }; - callback(event, metadata, shell_context) - }) - .map_err(|e| { - Error::Adapter( - EventLoopError::InsertSource { - message: format!("{e:?}"), - } - .into(), - ) - }) - } - - pub fn add_timer(&self, duration: Duration, mut callback: F) -> Result - where - F: FnMut(Instant, EventContext<'_>) -> TimeoutAction + 'static, - { - let timer = Timer::from_duration(duration); - self.insert_source(timer, move |deadline, (), shell_context| { - callback(deadline, shell_context) - }) - } - - pub fn add_timer_at(&self, deadline: Instant, mut callback: F) -> Result - where - F: FnMut(Instant, EventContext<'_>) -> TimeoutAction + 'static, - { - let timer = Timer::from_deadline(deadline); - self.insert_source(timer, move |deadline, (), shell_context| { - callback(deadline, shell_context) - }) - } - - pub fn add_channel( - &self, - mut callback: F, - ) -> Result<(RegistrationToken, channel::Sender)> - where - T: 'static, - F: FnMut(T, EventContext<'_>) + 'static, - { - let (sender, receiver) = channel::channel(); - let token = self.insert_source(receiver, move |event, (), shell_context| { - if let channel::Event::Msg(msg) = event { - callback(msg, shell_context); - } - })?; - Ok((token, sender)) - } - - pub fn add_fd( - &self, - fd: T, - interest: Interest, - mode: Mode, - mut callback: F, - ) -> Result - where - T: AsFd + 'static, - F: FnMut(EventContext<'_>) + 'static, - { - let generic = Generic::new(fd, interest, mode); - self.insert_source(generic, move |_readiness, _fd, shell_context| { - callback(shell_context); - Ok(PostAction::Continue) - }) - } -} +pub type EventLoopHandle = EventLoopHandleBase>; pub struct EventContext<'a> { app_state: &'a mut AppState, } -impl<'a> EventContext<'a> { - pub fn from_app_state(app_state: &'a mut AppState) -> Self { +impl<'a> FromAppState<'a> for EventContext<'a> { + fn from_app_state(app_state: &'a mut AppState) -> Self { Self { app_state } } } @@ -744,9 +653,7 @@ impl SingleWindowShell { #[must_use] pub fn event_loop_handle(&self) -> EventLoopHandle { - EventLoopHandle { - system: Rc::downgrade(&self.inner), - } + EventLoopHandle::new(Rc::downgrade(&self.inner)) } pub fn on(&self, callback_name: &str, handler: F) -> Result<()> @@ -931,9 +838,7 @@ impl ShellRuntime for SingleWindowShell { type Context<'a> = EventContext<'a>; fn event_loop_handle(&self) -> Self::LoopHandle { - EventLoopHandle { - system: Rc::downgrade(&self.inner), - } + EventLoopHandle::new(Rc::downgrade(&self.inner)) } fn with_component(&self, _name: &str, mut f: F)