diff --git a/crates/adapters/src/wayland/shell_adapter.rs b/crates/adapters/src/wayland/shell_adapter.rs index 510e3d0..db22a0d 100644 --- a/crates/adapters/src/wayland/shell_adapter.rs +++ b/crates/adapters/src/wayland/shell_adapter.rs @@ -711,6 +711,42 @@ impl WaylandShellSystem { pub fn app_state(&self) -> &AppState { &self.state } + + pub fn app_state_mut(&mut self) -> &mut AppState { + &mut self.state + } + + pub fn spawn_surface(&mut self, config: &ShellSurfaceConfig) -> Result> { + log::info!("Spawning new surface '{}'", config.name); + + let mut handles = Vec::new(); + + for (output_handle, _surface) in self.state.outputs_with_handles() { + handles.push(output_handle); + } + + log::info!( + "Surface '{}' would spawn on {} outputs (dynamic spawning not yet fully implemented)", + config.name, + handles.len() + ); + + Ok(handles) + } + + pub fn despawn_surface(&mut self, surface_name: &str) -> Result<()> { + log::info!("Despawning surface '{}'", surface_name); + + let removed = self.state.remove_surfaces_by_name(surface_name); + + log::info!( + "Removed {} surface instances for '{}'", + removed.len(), + surface_name + ); + + Ok(()) + } } impl ShellSystemPort for WaylandShellSystem { diff --git a/crates/adapters/src/wayland/surfaces/app_state.rs b/crates/adapters/src/wayland/surfaces/app_state.rs index 72d6279..951b19b 100644 --- a/crates/adapters/src/wayland/surfaces/app_state.rs +++ b/crates/adapters/src/wayland/surfaces/app_state.rs @@ -408,4 +408,25 @@ impl AppState { .map(|(_, v)| v) .collect() } + + pub fn remove_surfaces_by_name(&mut self, surface_name: &str) -> Vec { + let keys_to_remove: Vec<_> = self + .surfaces + .keys() + .filter(|k| k.surface_name == surface_name) + .cloned() + .collect(); + + let mut removed = Vec::new(); + for key in keys_to_remove { + if let Some(surface) = self.surfaces.remove(&key) { + removed.push(surface); + } + } + + self.surface_to_key + .retain(|_, k| k.surface_name != surface_name); + + removed + } } diff --git a/crates/composition/src/lib.rs b/crates/composition/src/lib.rs index fee7b4b..753f3c1 100644 --- a/crates/composition/src/lib.rs +++ b/crates/composition/src/lib.rs @@ -30,7 +30,7 @@ pub use layer_shika_domain::value_objects::popup_request::{ }; pub use popup_builder::PopupBuilder; pub use shell_runtime::{DEFAULT_SURFACE_NAME, ShellRuntime}; -pub use system::{EventContext, EventLoopHandle, ShellControl, SingleSurfaceShell}; +pub use system::{EventContext, EventLoopHandle, ShellControl}; pub use value_conversion::IntoValue; pub use layer_surface::{LayerSurfaceHandle, ShellSurfaceConfigHandler}; @@ -70,8 +70,8 @@ pub mod prelude { OutputPolicy, OutputRegistry, PopupBuilder, PopupHandle, PopupPlacement, PopupPositioningMode, PopupRequest, PopupSize, PopupWindow, Result, Shell, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, - ShellSurfaceConfigHandler, SingleSurfaceShell, SurfaceComponentConfig, SurfaceConfigBuilder, - SurfaceDefinition, SurfaceHandle, + ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, + SurfaceHandle, }; pub use crate::calloop::{Generic, Interest, Mode, PostAction, RegistrationToken, Timer}; diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index c82a7bd..b6ad30e 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -20,6 +20,7 @@ use layer_shika_domain::errors::DomainError; use layer_shika_domain::prelude::{ AnchorEdges, KeyboardInteractivity, Layer, Margins, OutputPolicy, ScaleFactor, SurfaceDimension, }; +use layer_shika_domain::value_objects::handle::SurfaceHandle; use layer_shika_domain::value_objects::output_handle::OutputHandle; use layer_shika_domain::value_objects::output_info::OutputInfo; use spin_on::spin_on; @@ -218,11 +219,17 @@ impl SurfaceConfigBuilder { } } +type OutputConnectedHandler = Box; +type OutputDisconnectedHandler = Box; + pub struct Shell { inner: Rc>, surfaces: HashMap, + surface_handles: HashMap, compilation_result: Rc, popup_command_sender: channel::Sender, + output_connected_handlers: Rc>>, + output_disconnected_handlers: Rc>>, } impl Shell { @@ -396,13 +403,19 @@ impl Shell { let (sender, receiver) = channel::channel(); let mut surfaces = HashMap::new(); + let mut surface_handles = HashMap::new(); + let handle = SurfaceHandle::new(); + surface_handles.insert(handle, definition.component.clone()); surfaces.insert(definition.component.clone(), definition); let shell = Self { inner: Rc::clone(&inner_rc), surfaces, + surface_handles, compilation_result, popup_command_sender: sender, + output_connected_handlers: Rc::new(RefCell::new(Vec::new())), + output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())), }; shell.setup_popup_command_handler(receiver)?; @@ -451,15 +464,21 @@ impl Shell { let (sender, receiver) = channel::channel(); let mut surfaces = HashMap::new(); + let mut surface_handles = HashMap::new(); for definition in definitions { + let handle = SurfaceHandle::new(); + surface_handles.insert(handle, definition.component.clone()); surfaces.insert(definition.component.clone(), definition); } let shell = Self { inner: Rc::clone(&inner_rc), surfaces, + surface_handles, compilation_result, popup_command_sender: sender, + output_connected_handlers: Rc::new(RefCell::new(Vec::new())), + output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())), }; shell.setup_popup_command_handler(receiver)?; @@ -542,6 +561,101 @@ impl Shell { Ok(()) } + pub fn spawn_surface(&mut self, definition: SurfaceDefinition) -> Result> { + let component_definition = self + .compilation_result + .component(&definition.component) + .ok_or_else(|| { + Error::Domain(DomainError::Configuration { + message: format!( + "Component '{}' not found in compilation result", + definition.component + ), + }) + })?; + + let wayland_config = WaylandSurfaceConfig::from_domain_config( + &definition.component, + component_definition, + Some(Rc::clone(&self.compilation_result)), + definition.config.clone(), + ); + + let shell_config = ShellSurfaceConfig { + name: definition.component.clone(), + config: wayland_config, + }; + + let mut facade = self.inner.borrow_mut(); + let handles = facade.inner_mut().spawn_surface(&shell_config)?; + + let surface_handle = SurfaceHandle::new(); + self.surface_handles + .insert(surface_handle, definition.component.clone()); + self.surfaces + .insert(definition.component.clone(), definition); + + log::info!( + "Spawned surface with handle {:?}, created {} output instances", + surface_handle, + handles.len() + ); + + Ok(vec![surface_handle]) + } + + pub fn despawn_surface(&mut self, handle: SurfaceHandle) -> Result<()> { + let surface_name = self.surface_handles.remove(&handle).ok_or_else(|| { + Error::Domain(DomainError::Configuration { + message: format!("Surface handle {:?} not found", handle), + }) + })?; + + self.surfaces.remove(&surface_name); + + let mut facade = self.inner.borrow_mut(); + facade.inner_mut().despawn_surface(&surface_name)?; + + log::info!( + "Despawned surface '{}' with handle {:?}", + surface_name, + handle + ); + + Ok(()) + } + + pub fn on_output_connected(&mut self, handler: F) -> Result<()> + where + F: Fn(&OutputInfo) + 'static, + { + self.output_connected_handlers + .borrow_mut() + .push(Box::new(handler)); + Ok(()) + } + + pub fn on_output_disconnected(&mut self, handler: F) -> Result<()> + where + F: Fn(OutputHandle) + 'static, + { + self.output_disconnected_handlers + .borrow_mut() + .push(Box::new(handler)); + Ok(()) + } + + pub fn get_surface_handle(&self, name: &str) -> Option { + self.surface_handles + .iter() + .find(|(_, n)| n.as_str() == name) + .map(|(h, _)| *h) + } + + pub fn get_surface_name(&self, handle: SurfaceHandle) -> Option<&str> { + self.surface_handles.get(&handle).map(String::as_str) + } + pub fn with_surface(&self, name: &str, f: F) -> Result where F: FnOnce(&ComponentInstance) -> R, diff --git a/crates/composition/src/system.rs b/crates/composition/src/system.rs index 8d9b516..34724b8 100644 --- a/crates/composition/src/system.rs +++ b/crates/composition/src/system.rs @@ -1,17 +1,11 @@ use crate::event_loop::{EventLoopHandleBase, FromAppState}; -use crate::shell_runtime::{DEFAULT_SURFACE_NAME, ShellRuntime}; -use crate::value_conversion::IntoValue; use crate::{Error, Result}; -use layer_shika_adapters::errors::EventLoopError; 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, }; -use layer_shika_adapters::{ - AppState, PopupManager, ShellSystemFacade, SurfaceState, WaylandSurfaceConfig, -}; -use layer_shika_domain::config::SurfaceConfig; +use layer_shika_adapters::{AppState, PopupManager, SurfaceState}; use layer_shika_domain::entities::output_registry::OutputRegistry; use layer_shika_domain::errors::DomainError; use layer_shika_domain::value_objects::dimensions::PopupDimensions; @@ -22,7 +16,6 @@ use layer_shika_domain::value_objects::popup_request::{ PopupHandle, PopupPlacement, PopupRequest, PopupSize, }; use std::cell::Cell; -use std::cell::RefCell; use std::rc::Rc; pub enum PopupCommand { @@ -551,316 +544,3 @@ impl EventContext<'_> { }) } } - -pub struct SingleSurfaceShell { - inner: Rc>, - popup_command_sender: channel::Sender, - surface_name: String, -} - -#[allow(dead_code)] -impl SingleSurfaceShell { - pub(crate) fn new( - component_definition: ComponentDefinition, - compilation_result: Option>, - config: SurfaceConfig, - ) -> Result { - let wayland_config = WaylandSurfaceConfig::from_domain_config( - "default", - component_definition, - compilation_result, - config, - ); - let inner = layer_shika_adapters::WaylandShellSystem::new(&wayland_config)?; - let facade = ShellSystemFacade::new(inner); - let inner_rc = Rc::new(RefCell::new(facade)); - - let (sender, receiver) = channel::channel(); - - let shell = Self { - inner: Rc::clone(&inner_rc), - popup_command_sender: sender, - surface_name: DEFAULT_SURFACE_NAME.to_string(), - }; - - shell.setup_popup_command_handler(receiver)?; - - Ok(shell) - } - - #[must_use] - pub fn with_surface_name(mut self, name: impl Into) -> Self { - self.surface_name = name.into(); - self - } - - #[must_use] - pub fn surface_name(&self) -> &str { - &self.surface_name - } - - fn setup_popup_command_handler(&self, receiver: channel::Channel) -> Result<()> { - let loop_handle = self.inner.borrow().inner_ref().event_loop_handle(); - let control = self.control(); - - loop_handle - .insert_source(receiver, move |event, (), app_state| { - if let channel::Event::Msg(command) = event { - let mut shell_context = EventContext { app_state }; - - match command { - PopupCommand::Show(request) => { - if let Err(e) = - shell_context.show_popup(&request, Some(control.clone())) - { - log::error!("Failed to show popup: {}", e); - } - } - PopupCommand::Close(handle) => { - if let Err(e) = shell_context.close_popup(handle) { - log::error!("Failed to close popup: {}", e); - } - } - PopupCommand::Resize { - handle, - width, - height, - } => { - if let Err(e) = shell_context.resize_popup(handle, width, height) { - log::error!("Failed to resize popup: {}", e); - } - } - } - } - }) - .map_err(|e| { - Error::Adapter( - EventLoopError::InsertSource { - message: format!("Failed to setup popup command handler: {e:?}"), - } - .into(), - ) - })?; - - Ok(()) - } - - #[must_use] - pub fn control(&self) -> ShellControl { - ShellControl { - sender: self.popup_command_sender.clone(), - } - } - - #[must_use] - pub fn event_loop_handle(&self) -> EventLoopHandle { - EventLoopHandle::new(Rc::downgrade(&self.inner)) - } - - pub fn on(&self, callback_name: &str, handler: F) -> Result<()> - where - F: Fn(ShellControl) -> R + 'static, - R: IntoValue, - { - let control = self.control(); - let handler = Rc::new(handler); - self.with_all_component_instances(|instance| { - let handler_rc = Rc::clone(&handler); - let control_clone = control.clone(); - if let Err(e) = instance.set_callback(callback_name, move |_args| { - handler_rc(control_clone.clone()).into_value() - }) { - log::error!( - "Failed to register callback '{}' on component: {}", - callback_name, - e - ); - } - }); - Ok(()) - } - - pub fn on_with_args(&self, callback_name: &str, handler: F) -> Result<()> - where - F: Fn(&[Value], ShellControl) -> R + 'static, - R: IntoValue, - { - let control = self.control(); - let handler = Rc::new(handler); - self.with_all_component_instances(|instance| { - let handler_rc = Rc::clone(&handler); - let control_clone = control.clone(); - if let Err(e) = instance.set_callback(callback_name, move |args| { - handler_rc(args, control_clone.clone()).into_value() - }) { - log::error!( - "Failed to register callback '{}' on component: {}", - callback_name, - e - ); - } - }); - Ok(()) - } - - pub fn on_for_output( - &self, - output: OutputHandle, - callback_name: &str, - handler: F, - ) -> Result<()> - where - F: Fn(ShellControl) -> R + 'static, - R: IntoValue, - { - let control = self.control(); - self.with_output(output, |instance| { - let control_clone = control.clone(); - if let Err(e) = instance.set_callback(callback_name, move |_args| { - handler(control_clone.clone()).into_value() - }) { - log::error!( - "Failed to register callback '{}' on output {:?}: {}", - callback_name, - output, - e - ); - } - })?; - Ok(()) - } - - pub fn on_for_output_with_args( - &self, - output: OutputHandle, - callback_name: &str, - handler: F, - ) -> Result<()> - where - F: Fn(&[Value], ShellControl) -> R + 'static, - R: IntoValue, - { - let control = self.control(); - self.with_output(output, |instance| { - let control_clone = control.clone(); - if let Err(e) = instance.set_callback(callback_name, move |args| { - handler(args, control_clone.clone()).into_value() - }) { - log::error!( - "Failed to register callback '{}' on output {:?}: {}", - callback_name, - output, - e - ); - } - })?; - Ok(()) - } - - pub fn run(&mut self) -> Result<()> { - self.inner.borrow_mut().run()?; - Ok(()) - } - - pub fn with_component_instance(&self, f: F) -> Result - where - F: FnOnce(&ComponentInstance) -> R, - { - let facade = self.inner.borrow(); - let instance = facade.component_instance()?; - Ok(f(instance)) - } - - pub fn with_all_component_instances(&self, mut f: F) - where - F: FnMut(&ComponentInstance), - { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - for surface in system.app_state().all_outputs() { - f(surface.component_instance()); - } - } - - pub fn with_output(&self, handle: OutputHandle, f: F) -> Result - where - F: FnOnce(&ComponentInstance) -> R, - { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - let window = system - .app_state() - .get_output_by_handle(handle) - .ok_or_else(|| { - Error::Domain(DomainError::Configuration { - message: format!("Output with handle {:?} not found", handle), - }) - })?; - Ok(f(window.component_instance())) - } - - pub fn with_all_outputs(&self, mut f: F) - where - F: FnMut(OutputHandle, &ComponentInstance), - { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - for (handle, surface) in system.app_state().outputs_with_handles() { - f(handle, surface.component_instance()); - } - } - - pub fn get_output_info(&self, handle: OutputHandle) -> Option { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - system.app_state().get_output_info(handle).cloned() - } - - pub fn all_output_info(&self) -> Vec { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - system.app_state().all_output_info().cloned().collect() - } - - pub fn output_registry(&self) -> OutputRegistry { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - system.app_state().output_registry().clone() - } -} - -impl ShellRuntime for SingleSurfaceShell { - type LoopHandle = EventLoopHandle; - type Context<'a> = EventContext<'a>; - - fn event_loop_handle(&self) -> Self::LoopHandle { - EventLoopHandle::new(Rc::downgrade(&self.inner)) - } - - fn with_component(&self, _name: &str, mut f: F) - where - F: FnMut(&ComponentInstance), - { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - for surface in system.app_state().all_outputs() { - f(surface.component_instance()); - } - } - - fn with_all_components(&self, mut f: F) - where - F: FnMut(&str, &ComponentInstance), - { - let facade = self.inner.borrow(); - let system = facade.inner_ref(); - for surface in system.app_state().all_outputs() { - f(&self.surface_name, surface.component_instance()); - } - } - - fn run(&mut self) -> Result<()> { - self.inner.borrow_mut().run()?; - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 9a84c19..00b12ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,8 +123,7 @@ pub use layer_shika_composition::{Error, Handle, Result, SurfaceHandle}; pub use shell::{ CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, - ShellSurfaceConfigHandler, SingleSurfaceShell, SurfaceComponentConfig, SurfaceConfigBuilder, - SurfaceDefinition, + ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, }; pub use window::{ diff --git a/src/prelude.rs b/src/prelude.rs index 30a1574..da981af 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,8 +11,7 @@ pub use crate::shell::{ CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, - ShellSurfaceConfigHandler, SingleSurfaceShell, SurfaceComponentConfig, SurfaceConfigBuilder, - SurfaceDefinition, + ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, }; pub use crate::window::{ diff --git a/src/shell.rs b/src/shell.rs index 0730bb2..a2c5cd1 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,6 +1,5 @@ pub use layer_shika_composition::{ CompiledUiSource, DEFAULT_COMPONENT_NAME, DEFAULT_SURFACE_NAME, LayerSurfaceHandle, Shell, ShellBuilder, ShellConfig, ShellControl, ShellEventContext, ShellEventLoopHandle, ShellRuntime, - ShellSurfaceConfigHandler, SingleSurfaceShell, SurfaceComponentConfig, SurfaceConfigBuilder, - SurfaceDefinition, + ShellSurfaceConfigHandler, SurfaceComponentConfig, SurfaceConfigBuilder, SurfaceDefinition, };