refactor: consolidate event loop

This commit is contained in:
drendog 2025-12-03 20:00:11 +01:00
parent 042ac8550f
commit ccf0c9caf8
Signed by: dwenya
GPG key ID: 8DD77074645332D0
4 changed files with 186 additions and 196 deletions

View file

@ -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<Ctx> {
system: Weak<RefCell<WindowingSystemFacade>>,
_marker: PhantomData<fn(&mut AppState) -> Ctx>,
}
impl<Ctx> EventLoopHandleBase<Ctx> {
pub fn new(system: Weak<RefCell<WindowingSystemFacade>>) -> Self {
Self {
system,
_marker: PhantomData,
}
}
}
impl<Ctx> EventLoopHandleBase<Ctx>
where
for<'a> Ctx: FromAppState<'a> + 'a,
{
pub fn insert_source<S, F, R>(&self, source: S, mut callback: F) -> Result<RegistrationToken>
where
S: EventSource<Ret = R> + '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<F>(&self, duration: Duration, mut callback: F) -> Result<RegistrationToken>
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<F>(&self, deadline: Instant, mut callback: F) -> Result<RegistrationToken>
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<T, F>(
&self,
mut callback: F,
) -> Result<(RegistrationToken, channel::Sender<T>)>
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<F, T>(
&self,
fd: T,
interest: Interest,
mode: Mode,
mut callback: F,
) -> Result<RegistrationToken>
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)
})
}
}

View file

@ -1,6 +1,7 @@
#![allow(clippy::pub_use)] #![allow(clippy::pub_use)]
mod builder; mod builder;
mod event_loop;
mod popup_builder; mod popup_builder;
mod shell; mod shell;
mod shell_composition; mod shell_composition;

View file

@ -1,13 +1,11 @@
use crate::event_loop::{EventLoopHandleBase, FromAppState};
use crate::shell_composition::ShellWindowDefinition; use crate::shell_composition::ShellWindowDefinition;
use crate::shell_runtime::ShellRuntime; use crate::shell_runtime::ShellRuntime;
use crate::system::{EventContext, PopupCommand, ShellControl}; use crate::system::{EventContext, PopupCommand, ShellControl};
use crate::value_conversion::IntoValue; use crate::value_conversion::IntoValue;
use crate::{Error, Result}; use crate::{Error, Result};
use layer_shika_adapters::errors::EventLoopError; use layer_shika_adapters::errors::EventLoopError;
use layer_shika_adapters::platform::calloop::{ use layer_shika_adapters::platform::calloop::channel;
EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer,
channel,
};
use layer_shika_adapters::platform::slint_interpreter::{ use layer_shika_adapters::platform::slint_interpreter::{
CompilationResult, ComponentInstance, Value, CompilationResult, ComponentInstance, Value,
}; };
@ -16,16 +14,16 @@ use layer_shika_adapters::{
AppState, ShellWindowConfig, WaylandWindowConfig, WindowState, WindowingSystemFacade, AppState, ShellWindowConfig, WaylandWindowConfig, WindowState, WindowingSystemFacade,
}; };
use layer_shika_domain::config::WindowConfig; use layer_shika_domain::config::WindowConfig;
use layer_shika_domain::entities::output_registry::OutputRegistry;
use layer_shika_domain::errors::DomainError; use layer_shika_domain::errors::DomainError;
use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity; use layer_shika_domain::value_objects::keyboard_interactivity::KeyboardInteractivity;
use layer_shika_domain::value_objects::layer::Layer; use layer_shika_domain::value_objects::layer::Layer;
use layer_shika_domain::value_objects::margins::Margins; 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::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::AsFd; use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::result::Result as StdResult;
use std::time::{Duration, Instant};
pub struct LayerSurfaceHandle<'a> { pub struct LayerSurfaceHandle<'a> {
window_state: &'a WindowState, window_state: &'a WindowState,
@ -324,9 +322,7 @@ impl Shell {
} }
pub fn event_loop_handle(&self) -> ShellEventLoopHandle { pub fn event_loop_handle(&self) -> ShellEventLoopHandle {
ShellEventLoopHandle { ShellEventLoopHandle::new(Rc::downgrade(&self.inner))
system: Rc::downgrade(&self.inner),
}
} }
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
@ -516,9 +512,7 @@ impl ShellRuntime for Shell {
type Context<'a> = ShellEventContext<'a>; type Context<'a> = ShellEventContext<'a>;
fn event_loop_handle(&self) -> Self::LoopHandle { fn event_loop_handle(&self) -> Self::LoopHandle {
ShellEventLoopHandle { ShellEventLoopHandle::new(Rc::downgrade(&self.inner))
system: Rc::downgrade(&self.inner),
}
} }
fn with_component<F>(&self, name: &str, mut f: F) fn with_component<F>(&self, name: &str, mut f: F)
@ -559,102 +553,36 @@ impl ShellRuntime for Shell {
} }
} }
pub struct ShellEventLoopHandle { pub type ShellEventLoopHandle = EventLoopHandleBase<ShellEventContext<'static>>;
system: Weak<RefCell<WindowingSystemFacade>>,
}
impl ShellEventLoopHandle {
pub fn insert_source<S, F, R>(
&self,
source: S,
mut callback: F,
) -> StdResult<RegistrationToken, Error>
where
S: EventSource<Ret = R> + '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<F>(&self, duration: Duration, mut callback: F) -> Result<RegistrationToken>
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<T, F>(
&self,
mut callback: F,
) -> Result<(RegistrationToken, channel::Sender<T>)>
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<F, T>(
&self,
fd: T,
interest: Interest,
mode: Mode,
mut callback: F,
) -> Result<RegistrationToken>
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 struct ShellEventContext<'a> { pub struct ShellEventContext<'a> {
app_state: &'a mut AppState, 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<'_> { impl ShellEventContext<'_> {
pub fn get_shell_window_component( pub fn get_shell_window_component(
&self, &self,
_shell_window_name: &str, shell_window_name: &str,
) -> Option<&ComponentInstance> { ) -> Option<&ComponentInstance> {
self.app_state self.app_state
.primary_output() .windows_by_shell_name(shell_window_name)
.next()
.map(WindowState::component_instance) .map(WindowState::component_instance)
} }
pub fn get_shell_window_component_mut( pub fn get_shell_window_component_mut(
&mut self, &mut self,
_shell_window_name: &str, shell_window_name: &str,
) -> Option<&ComponentInstance> { ) -> Option<&ComponentInstance> {
self.app_state self.app_state
.primary_output() .windows_by_shell_name(shell_window_name)
.next()
.map(WindowState::component_instance) .map(WindowState::component_instance)
} }
@ -670,4 +598,51 @@ impl ShellEventContext<'_> {
} }
Ok(()) Ok(())
} }
#[must_use]
pub fn primary_output_handle(&self) -> Option<OutputHandle> {
self.app_state.primary_output_handle()
}
#[must_use]
pub fn active_output_handle(&self) -> Option<OutputHandle> {
self.app_state.active_output_handle()
}
pub fn output_registry(&self) -> &OutputRegistry {
self.app_state.output_registry()
}
pub fn outputs(&self) -> impl Iterator<Item = (OutputHandle, &ComponentInstance)> {
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<Item = &OutputInfo> {
self.app_state.all_output_info()
}
pub fn outputs_with_info(&self) -> impl Iterator<Item = (&OutputInfo, &ComponentInstance)> {
self.app_state
.outputs_with_info()
.map(|(info, window)| (info, window.component_instance()))
}
#[must_use]
pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
self.app_state
.primary_output()
.and_then(WindowState::compilation_result)
}
} }

View file

@ -1,12 +1,10 @@
use crate::event_loop::{EventLoopHandleBase, FromAppState};
use crate::popup_builder::PopupBuilder; use crate::popup_builder::PopupBuilder;
use crate::shell_runtime::{DEFAULT_WINDOW_NAME, ShellRuntime}; use crate::shell_runtime::{DEFAULT_WINDOW_NAME, ShellRuntime};
use crate::value_conversion::IntoValue; use crate::value_conversion::IntoValue;
use crate::{Error, Result}; use crate::{Error, Result};
use layer_shika_adapters::errors::EventLoopError; use layer_shika_adapters::errors::EventLoopError;
use layer_shika_adapters::platform::calloop::{ use layer_shika_adapters::platform::calloop::channel;
EventSource, Generic, Interest, Mode, PostAction, RegistrationToken, TimeoutAction, Timer,
channel,
};
use layer_shika_adapters::platform::slint::ComponentHandle; use layer_shika_adapters::platform::slint::ComponentHandle;
use layer_shika_adapters::platform::slint_interpreter::{ use layer_shika_adapters::platform::slint_interpreter::{
CompilationResult, ComponentDefinition, ComponentInstance, Value, CompilationResult, ComponentDefinition, ComponentInstance, Value,
@ -26,10 +24,7 @@ use layer_shika_domain::value_objects::popup_request::{
}; };
use std::cell::Cell; use std::cell::Cell;
use std::cell::RefCell; use std::cell::RefCell;
use std::os::unix::io::AsFd; use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::result::Result as StdResult;
use std::time::{Duration, Instant};
pub enum PopupCommand { pub enum PopupCommand {
Show(PopupRequest), Show(PopupRequest),
@ -111,100 +106,14 @@ impl ShellControl {
} }
} }
pub struct EventLoopHandle { pub type EventLoopHandle = EventLoopHandleBase<EventContext<'static>>;
system: Weak<RefCell<WindowingSystemFacade>>,
}
impl EventLoopHandle {
pub fn insert_source<S, F, R>(
&self,
source: S,
mut callback: F,
) -> StdResult<RegistrationToken, Error>
where
S: EventSource<Ret = R> + '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<F>(&self, duration: Duration, mut callback: F) -> Result<RegistrationToken>
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<F>(&self, deadline: Instant, mut callback: F) -> Result<RegistrationToken>
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<T, F>(
&self,
mut callback: F,
) -> Result<(RegistrationToken, channel::Sender<T>)>
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<F, T>(
&self,
fd: T,
interest: Interest,
mode: Mode,
mut callback: F,
) -> Result<RegistrationToken>
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 struct EventContext<'a> { pub struct EventContext<'a> {
app_state: &'a mut AppState, app_state: &'a mut AppState,
} }
impl<'a> EventContext<'a> { impl<'a> FromAppState<'a> for EventContext<'a> {
pub fn from_app_state(app_state: &'a mut AppState) -> Self { fn from_app_state(app_state: &'a mut AppState) -> Self {
Self { app_state } Self { app_state }
} }
} }
@ -744,9 +653,7 @@ impl SingleWindowShell {
#[must_use] #[must_use]
pub fn event_loop_handle(&self) -> EventLoopHandle { pub fn event_loop_handle(&self) -> EventLoopHandle {
EventLoopHandle { EventLoopHandle::new(Rc::downgrade(&self.inner))
system: Rc::downgrade(&self.inner),
}
} }
pub fn on<F, R>(&self, callback_name: &str, handler: F) -> Result<()> pub fn on<F, R>(&self, callback_name: &str, handler: F) -> Result<()>
@ -931,9 +838,7 @@ impl ShellRuntime for SingleWindowShell {
type Context<'a> = EventContext<'a>; type Context<'a> = EventContext<'a>;
fn event_loop_handle(&self) -> Self::LoopHandle { fn event_loop_handle(&self) -> Self::LoopHandle {
EventLoopHandle { EventLoopHandle::new(Rc::downgrade(&self.inner))
system: Rc::downgrade(&self.inner),
}
} }
fn with_component<F>(&self, _name: &str, mut f: F) fn with_component<F>(&self, _name: &str, mut f: F)