layer_shika_composition/
shell.rs

1use crate::event_loop::{EventLoopHandle, FromAppState};
2use crate::layer_surface::LayerSurfaceHandle;
3use crate::popup_builder::PopupBuilder;
4use crate::shell_config::{CompiledUiSource, ShellConfig};
5use crate::shell_runtime::ShellRuntime;
6use crate::surface_registry::{SurfaceDefinition, SurfaceEntry, SurfaceRegistry};
7use crate::system::{
8    CallbackContext, EventDispatchContext, PopupCommand, ShellCommand, ShellControl,
9    SurfaceCommand, SurfaceTarget,
10};
11use crate::value_conversion::IntoValue;
12use crate::{Error, Result};
13use layer_shika_adapters::errors::EventLoopError;
14use layer_shika_adapters::platform::calloop::channel;
15use layer_shika_adapters::platform::slint_interpreter::{
16    CompilationResult, Compiler, ComponentInstance, Value,
17};
18use layer_shika_adapters::{
19    AppState, ShellSurfaceConfig, SurfaceState, WaylandSurfaceConfig, WaylandSystemOps,
20};
21use layer_shika_domain::config::SurfaceConfig;
22use layer_shika_domain::entities::output_registry::OutputRegistry;
23use layer_shika_domain::errors::DomainError;
24use layer_shika_domain::prelude::{
25    AnchorEdges, KeyboardInteractivity, Layer, Margins, OutputPolicy, ScaleFactor, SurfaceDimension,
26};
27use layer_shika_domain::value_objects::handle::SurfaceHandle;
28use layer_shika_domain::value_objects::output_handle::OutputHandle;
29use layer_shika_domain::value_objects::output_info::OutputInfo;
30use layer_shika_domain::value_objects::surface_instance_id::SurfaceInstanceId;
31use spin_on::spin_on;
32use std::cell::RefCell;
33use std::path::{Path, PathBuf};
34use std::rc::Rc;
35
36/// Default Slint component name used when none is specified
37pub const DEFAULT_COMPONENT_NAME: &str = "Main";
38
39enum CompilationSource {
40    File { path: PathBuf, compiler: Compiler },
41    Source { code: String, compiler: Compiler },
42    Compiled(Rc<CompilationResult>),
43}
44
45/// Builder for configuring and creating a Shell with one or more surfaces
46///
47/// Chain `.surface()` calls to configure multiple surfaces, then call `.build()` or `.run()`.
48/// If no surfaces are configured, a default "Main" surface is created.
49pub struct ShellBuilder {
50    compilation: CompilationSource,
51    surfaces: Vec<SurfaceDefinition>,
52}
53
54impl ShellBuilder {
55    /// Starts configuration for a new surface with the given component name
56    pub fn surface(self, component: impl Into<String>) -> SurfaceConfigBuilder {
57        SurfaceConfigBuilder {
58            shell_builder: self,
59            component: component.into(),
60            config: SurfaceConfig::default(),
61        }
62    }
63
64    /// Discovers and registers multiple surfaces by component names
65    #[must_use]
66    pub fn discover_surfaces(
67        mut self,
68        components: impl IntoIterator<Item = impl Into<String>>,
69    ) -> Self {
70        for component in components {
71            self.surfaces.push(SurfaceDefinition {
72                component: component.into(),
73                config: SurfaceConfig::default(),
74            });
75        }
76        self
77    }
78
79    /// Builds the shell from the configured surfaces
80    pub fn build(self) -> Result<Shell> {
81        let surfaces = if self.surfaces.is_empty() {
82            vec![SurfaceDefinition {
83                component: DEFAULT_COMPONENT_NAME.to_string(),
84                config: SurfaceConfig::default(),
85            }]
86        } else {
87            self.surfaces
88        };
89
90        let compilation_result = match self.compilation {
91            CompilationSource::File { path, compiler } => {
92                let result = spin_on(compiler.build_from_path(&path));
93                let diagnostics: Vec<_> = result.diagnostics().collect();
94                if !diagnostics.is_empty() {
95                    let messages: Vec<String> =
96                        diagnostics.iter().map(ToString::to_string).collect();
97                    return Err(DomainError::Configuration {
98                        message: format!(
99                            "Failed to compile Slint file '{}':\n{}",
100                            path.display(),
101                            messages.join("\n")
102                        ),
103                    }
104                    .into());
105                }
106                Rc::new(result)
107            }
108            CompilationSource::Source { code, compiler } => {
109                let result = spin_on(compiler.build_from_source(code, PathBuf::default()));
110                let diagnostics: Vec<_> = result.diagnostics().collect();
111                if !diagnostics.is_empty() {
112                    let messages: Vec<String> =
113                        diagnostics.iter().map(ToString::to_string).collect();
114                    return Err(DomainError::Configuration {
115                        message: format!(
116                            "Failed to compile Slint source:\n{}",
117                            messages.join("\n")
118                        ),
119                    }
120                    .into());
121                }
122                Rc::new(result)
123            }
124            CompilationSource::Compiled(result) => result,
125        };
126
127        Shell::new(compilation_result, surfaces)
128    }
129}
130
131/// Builder for configuring a single surface within a Shell
132///
133/// Chain configuration methods, then either start a new surface with `.surface()`
134/// or finalize with `.build()` or `.run()`.
135pub struct SurfaceConfigBuilder {
136    shell_builder: ShellBuilder,
137    component: String,
138    config: SurfaceConfig,
139}
140
141impl SurfaceConfigBuilder {
142    /// Sets both width and height for the surface
143    #[must_use]
144    pub fn size(mut self, width: u32, height: u32) -> Self {
145        self.config.dimensions = SurfaceDimension::new(width, height);
146        self
147    }
148
149    /// Sets the height of the surface
150    #[must_use]
151    pub fn height(mut self, height: u32) -> Self {
152        self.config.dimensions = SurfaceDimension::new(self.config.dimensions.width(), height);
153        self
154    }
155
156    /// Sets the width of the surface
157    #[must_use]
158    pub fn width(mut self, width: u32) -> Self {
159        self.config.dimensions = SurfaceDimension::new(width, self.config.dimensions.height());
160        self
161    }
162
163    /// Sets the layer (stacking order) for the surface
164    #[must_use]
165    pub const fn layer(mut self, layer: Layer) -> Self {
166        self.config.layer = layer;
167        self
168    }
169
170    /// Sets the margins around the surface
171    #[must_use]
172    pub fn margin(mut self, margin: impl Into<Margins>) -> Self {
173        self.config.margin = margin.into();
174        self
175    }
176
177    /// Sets the anchor edges for positioning
178    #[must_use]
179    pub const fn anchor(mut self, anchor: AnchorEdges) -> Self {
180        self.config.anchor = anchor;
181        self
182    }
183
184    /// Sets the exclusive zone in pixels
185    ///
186    /// Reserves screen space that other windows avoid. Positive values reserve from anchored edge,
187    /// `0` means no reservation, `-1` lets compositor decide.
188    #[must_use]
189    pub const fn exclusive_zone(mut self, zone: i32) -> Self {
190        self.config.exclusive_zone = zone;
191        self
192    }
193
194    /// Sets the namespace identifier for the surface
195    #[must_use]
196    pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
197        self.config.namespace = namespace.into();
198        self
199    }
200
201    /// Sets the scale factor for the surface
202    #[must_use]
203    pub fn scale_factor(mut self, sf: impl TryInto<ScaleFactor, Error = DomainError>) -> Self {
204        self.config.scale_factor = sf.try_into().unwrap_or_default();
205        self
206    }
207
208    /// Sets the keyboard interactivity mode
209    #[must_use]
210    pub const fn keyboard_interactivity(mut self, mode: KeyboardInteractivity) -> Self {
211        self.config.keyboard_interactivity = mode;
212        self
213    }
214
215    /// Sets the output policy for multi-monitor configuration
216    ///
217    /// Controls which monitors display this surface. Default is `OutputPolicy::All`.
218    #[must_use]
219    pub fn output_policy(mut self, policy: OutputPolicy) -> Self {
220        self.config.output_policy = policy;
221        self
222    }
223
224    /// Starts configuration for another surface
225    #[must_use]
226    pub fn surface(self, component: impl Into<String>) -> SurfaceConfigBuilder {
227        let shell_builder = self.complete();
228        shell_builder.surface(component)
229    }
230
231    /// Builds the shell with all configured surfaces
232    pub fn build(self) -> Result<Shell> {
233        self.complete().build()
234    }
235
236    /// Builds and runs the shell
237    pub fn run(self) -> Result<()> {
238        let mut shell = self.build()?;
239        shell.run()
240    }
241
242    fn complete(mut self) -> ShellBuilder {
243        self.shell_builder.surfaces.push(SurfaceDefinition {
244            component: self.component,
245            config: self.config,
246        });
247        self.shell_builder
248    }
249}
250
251type OutputConnectedHandler = Box<dyn Fn(&OutputInfo)>;
252type OutputDisconnectedHandler = Box<dyn Fn(OutputHandle)>;
253
254/// Main runtime for managing Wayland layer-shell surfaces with Slint UI
255///
256/// Manages surface lifecycle, event loop integration, and component instantiation.
257/// Supports multiple surfaces across monitors, dynamic spawning, and popup windows.
258///
259/// Create via `Shell::from_file()`, `from_source()`, or `from_compilation()`.
260pub struct Shell {
261    inner: Rc<RefCell<dyn WaylandSystemOps>>,
262    registry: SurfaceRegistry,
263    compilation_result: Rc<CompilationResult>,
264    command_sender: channel::Sender<ShellCommand>,
265    output_connected_handlers: Rc<RefCell<Vec<OutputConnectedHandler>>>,
266    output_disconnected_handlers: Rc<RefCell<Vec<OutputDisconnectedHandler>>>,
267}
268
269impl Shell {
270    /// Creates a shell builder from a Slint file path
271    pub fn from_file(path: impl AsRef<Path>) -> ShellBuilder {
272        ShellBuilder {
273            compilation: CompilationSource::File {
274                path: path.as_ref().to_path_buf(),
275                compiler: Compiler::default(),
276            },
277            surfaces: Vec::new(),
278        }
279    }
280
281    /// Creates a shell builder from a Slint file path with a custom compiler
282    ///
283    /// Useful for configuring include paths, style overrides, or compilation settings.
284    pub fn from_file_with_compiler(path: impl AsRef<Path>, compiler: Compiler) -> ShellBuilder {
285        ShellBuilder {
286            compilation: CompilationSource::File {
287                path: path.as_ref().to_path_buf(),
288                compiler,
289            },
290            surfaces: Vec::new(),
291        }
292    }
293
294    /// Creates a shell builder from Slint source code
295    pub fn from_source(code: impl Into<String>) -> ShellBuilder {
296        ShellBuilder {
297            compilation: CompilationSource::Source {
298                code: code.into(),
299                compiler: Compiler::default(),
300            },
301            surfaces: Vec::new(),
302        }
303    }
304
305    /// Creates a shell builder from Slint source code with a custom compiler
306    pub fn from_source_with_compiler(code: impl Into<String>, compiler: Compiler) -> ShellBuilder {
307        ShellBuilder {
308            compilation: CompilationSource::Source {
309                code: code.into(),
310                compiler,
311            },
312            surfaces: Vec::new(),
313        }
314    }
315
316    /// Creates a shell builder from a pre-compiled Slint compilation result
317    pub fn from_compilation(result: Rc<CompilationResult>) -> ShellBuilder {
318        ShellBuilder {
319            compilation: CompilationSource::Compiled(result),
320            surfaces: Vec::new(),
321        }
322    }
323
324    /// Creates an empty shell builder for manual configuration
325    pub fn builder() -> ShellBuilder {
326        ShellBuilder {
327            compilation: CompilationSource::Source {
328                code: String::new(),
329                compiler: Compiler::default(),
330            },
331            surfaces: Vec::new(),
332        }
333    }
334
335    /// Compiles a Slint file and returns the compilation result
336    pub fn compile_file(path: impl AsRef<Path>) -> Result<Rc<CompilationResult>> {
337        let compiler = Compiler::default();
338        let result = spin_on(compiler.build_from_path(path.as_ref()));
339        let diagnostics: Vec<_> = result.diagnostics().collect();
340        if !diagnostics.is_empty() {
341            let messages: Vec<String> = diagnostics.iter().map(ToString::to_string).collect();
342            return Err(DomainError::Configuration {
343                message: format!(
344                    "Failed to compile Slint file '{}':\n{}",
345                    path.as_ref().display(),
346                    messages.join("\n")
347                ),
348            }
349            .into());
350        }
351        Ok(Rc::new(result))
352    }
353
354    /// Compiles Slint source code and returns the compilation result
355    pub fn compile_source(code: impl Into<String>) -> Result<Rc<CompilationResult>> {
356        let compiler = Compiler::default();
357        let result = spin_on(compiler.build_from_source(code.into(), PathBuf::default()));
358        let diagnostics: Vec<_> = result.diagnostics().collect();
359        if !diagnostics.is_empty() {
360            let messages: Vec<String> = diagnostics.iter().map(ToString::to_string).collect();
361            return Err(DomainError::Configuration {
362                message: format!("Failed to compile Slint source:\n{}", messages.join("\n")),
363            }
364            .into());
365        }
366        Ok(Rc::new(result))
367    }
368
369    /// Creates a shell from a complete configuration object
370    pub fn from_config(config: ShellConfig) -> Result<Self> {
371        let compilation_result = match config.ui_source {
372            CompiledUiSource::File(path) => Self::compile_file(&path)?,
373            CompiledUiSource::Source(code) => Self::compile_source(code)?,
374            CompiledUiSource::Compiled(result) => result,
375        };
376
377        let surfaces: Vec<SurfaceDefinition> = if config.surfaces.is_empty() {
378            vec![SurfaceDefinition {
379                component: DEFAULT_COMPONENT_NAME.to_string(),
380                config: SurfaceConfig::default(),
381            }]
382        } else {
383            config
384                .surfaces
385                .into_iter()
386                .map(|s| SurfaceDefinition {
387                    component: s.component,
388                    config: s.config,
389                })
390                .collect()
391        };
392
393        Self::new(compilation_result, surfaces)
394    }
395
396    pub(crate) fn new(
397        compilation_result: Rc<CompilationResult>,
398        definitions: Vec<SurfaceDefinition>,
399    ) -> Result<Self> {
400        log::info!("Creating Shell with {} windows", definitions.len());
401
402        if definitions.is_empty() {
403            return Err(Error::Domain(DomainError::Configuration {
404                message: "At least one window definition is required".to_string(),
405            }));
406        }
407
408        let is_single_window = definitions.len() == 1;
409
410        if is_single_window {
411            let definition = definitions.into_iter().next().ok_or_else(|| {
412                Error::Domain(DomainError::Configuration {
413                    message: "Expected at least one window definition".to_string(),
414                })
415            })?;
416            Self::new_single_window(compilation_result, definition)
417        } else {
418            Self::new_multi_window(compilation_result, &definitions)
419        }
420    }
421
422    fn new_single_window(
423        compilation_result: Rc<CompilationResult>,
424        definition: SurfaceDefinition,
425    ) -> Result<Self> {
426        let component_definition = compilation_result
427            .component(&definition.component)
428            .ok_or_else(|| {
429                Error::Domain(DomainError::Configuration {
430                    message: format!(
431                        "Component '{}' not found in compilation result",
432                        definition.component
433                    ),
434                })
435            })?;
436
437        let handle = SurfaceHandle::new();
438        let wayland_config = WaylandSurfaceConfig::from_domain_config(
439            handle,
440            &definition.component,
441            component_definition,
442            Some(Rc::clone(&compilation_result)),
443            definition.config.clone(),
444        );
445
446        let inner = layer_shika_adapters::WaylandShellSystem::new(&wayland_config)?;
447        let inner_rc: Rc<RefCell<dyn WaylandSystemOps>> = Rc::new(RefCell::new(inner));
448
449        let (sender, receiver) = channel::channel();
450
451        let mut registry = SurfaceRegistry::new();
452        let entry = SurfaceEntry::new(handle, definition.component.clone(), definition);
453        registry.insert(entry)?;
454
455        let shell = Self {
456            inner: Rc::clone(&inner_rc),
457            registry,
458            compilation_result,
459            command_sender: sender,
460            output_connected_handlers: Rc::new(RefCell::new(Vec::new())),
461            output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())),
462        };
463
464        shell.setup_command_handler(receiver)?;
465
466        log::info!("Shell created (single-window mode)");
467
468        Ok(shell)
469    }
470
471    fn new_multi_window(
472        compilation_result: Rc<CompilationResult>,
473        definitions: &[SurfaceDefinition],
474    ) -> Result<Self> {
475        let shell_configs_with_handles: Vec<(SurfaceHandle, ShellSurfaceConfig)> = definitions
476            .iter()
477            .map(|def| {
478                let component_definition = compilation_result
479                    .component(&def.component)
480                    .ok_or_else(|| {
481                        Error::Domain(DomainError::Configuration {
482                            message: format!(
483                                "Component '{}' not found in compilation result",
484                                def.component
485                            ),
486                        })
487                    })?;
488
489                let handle = SurfaceHandle::new();
490                let wayland_config = WaylandSurfaceConfig::from_domain_config(
491                    handle,
492                    &def.component,
493                    component_definition,
494                    Some(Rc::clone(&compilation_result)),
495                    def.config.clone(),
496                );
497
498                Ok((
499                    handle,
500                    ShellSurfaceConfig {
501                        name: def.component.clone(),
502                        config: wayland_config,
503                    },
504                ))
505            })
506            .collect::<Result<Vec<_>>>()?;
507
508        let shell_configs: Vec<ShellSurfaceConfig> = shell_configs_with_handles
509            .iter()
510            .map(|(_, cfg)| cfg.clone())
511            .collect();
512
513        let inner = layer_shika_adapters::WaylandShellSystem::new_multi(&shell_configs)?;
514        let inner_rc: Rc<RefCell<dyn WaylandSystemOps>> = Rc::new(RefCell::new(inner));
515
516        let (sender, receiver) = channel::channel();
517
518        let mut registry = SurfaceRegistry::new();
519        for ((handle, _), definition) in shell_configs_with_handles.iter().zip(definitions.iter()) {
520            let entry =
521                SurfaceEntry::new(*handle, definition.component.clone(), definition.clone());
522            registry.insert(entry)?;
523        }
524
525        let shell = Self {
526            inner: Rc::clone(&inner_rc),
527            registry,
528            compilation_result,
529            command_sender: sender,
530            output_connected_handlers: Rc::new(RefCell::new(Vec::new())),
531            output_disconnected_handlers: Rc::new(RefCell::new(Vec::new())),
532        };
533
534        shell.setup_command_handler(receiver)?;
535
536        log::info!(
537            "Shell created (multi-surface mode) with surfaces: {:?}",
538            shell.surface_names()
539        );
540
541        Ok(shell)
542    }
543
544    fn setup_command_handler(&self, receiver: channel::Channel<ShellCommand>) -> Result<()> {
545        let loop_handle = self.inner.borrow().event_loop_handle();
546        let control = self.control();
547
548        loop_handle
549            .insert_source(receiver, move |event, (), app_state| {
550                if let channel::Event::Msg(command) = event {
551                    let mut ctx = crate::system::EventDispatchContext::from_app_state(app_state);
552
553                    match command {
554                        ShellCommand::Popup(popup_cmd) => {
555                            Self::handle_popup_command(popup_cmd, &mut ctx, &control);
556                        }
557                        ShellCommand::Surface(surface_cmd) => {
558                            Self::handle_surface_command(surface_cmd, &mut ctx);
559                        }
560                        ShellCommand::Render => {
561                            if let Err(e) = ctx.render_frame_if_dirty() {
562                                log::error!("Failed to render frame: {}", e);
563                            }
564                        }
565                    }
566                }
567            })
568            .map_err(|e| {
569                Error::Adapter(
570                    EventLoopError::InsertSource {
571                        message: format!("Failed to setup command handler: {e:?}"),
572                    }
573                    .into(),
574                )
575            })?;
576
577        Ok(())
578    }
579
580    fn handle_popup_command(
581        command: PopupCommand,
582        ctx: &mut EventDispatchContext<'_>,
583        control: &ShellControl,
584    ) {
585        match command {
586            PopupCommand::Show(request) => {
587                if let Err(e) = ctx.show_popup(&request, Some(control.clone())) {
588                    log::error!("Failed to show popup: {}", e);
589                }
590            }
591            PopupCommand::Close(handle) => {
592                if let Err(e) = ctx.close_popup(handle) {
593                    log::error!("Failed to close popup: {}", e);
594                }
595            }
596            PopupCommand::Resize {
597                handle,
598                width,
599                height,
600            } => {
601                if let Err(e) = ctx.resize_popup(handle, width, height) {
602                    log::error!("Failed to resize popup: {}", e);
603                }
604            }
605        }
606    }
607
608    fn resolve_surface_target<'a>(
609        ctx: &'a mut EventDispatchContext<'_>,
610        target: &SurfaceTarget,
611    ) -> Vec<&'a mut SurfaceState> {
612        match target {
613            SurfaceTarget::ByInstance(id) => {
614                if let Some(surface) = ctx.surface_by_instance_mut(id.surface(), id.output()) {
615                    vec![surface]
616                } else {
617                    log::warn!(
618                        "Surface instance not found: handle {:?} on output {:?}",
619                        id.surface(),
620                        id.output()
621                    );
622                    vec![]
623                }
624            }
625            SurfaceTarget::ByHandle(handle) => ctx.surfaces_by_handle_mut(*handle),
626            SurfaceTarget::ByName(name) => ctx.surfaces_by_name_mut(name),
627            SurfaceTarget::ByNameAndOutput { name, output } => {
628                ctx.surfaces_by_name_and_output_mut(name, *output)
629            }
630        }
631    }
632
633    fn apply_surface_resize(
634        ctx: &mut EventDispatchContext<'_>,
635        target: &SurfaceTarget,
636        width: u32,
637        height: u32,
638    ) {
639        log::debug!(
640            "Surface command: Resize {:?} to {}x{}",
641            target,
642            width,
643            height
644        );
645        for surface in Self::resolve_surface_target(ctx, target) {
646            let handle = LayerSurfaceHandle::from_window_state(surface);
647            handle.set_size(width, height);
648            handle.commit();
649            surface.update_size_with_compositor_logic(width, height);
650        }
651    }
652
653    fn apply_surface_config_change<F>(
654        ctx: &mut EventDispatchContext<'_>,
655        target: &SurfaceTarget,
656        operation: &str,
657        apply: F,
658    ) where
659        F: Fn(&LayerSurfaceHandle<'_>),
660    {
661        log::debug!("Surface command: {} {:?}", operation, target);
662        for surface in Self::resolve_surface_target(ctx, target) {
663            let handle = LayerSurfaceHandle::from_window_state(surface);
664            apply(&handle);
665            handle.commit();
666        }
667    }
668
669    fn apply_full_config(
670        ctx: &mut EventDispatchContext<'_>,
671        target: &SurfaceTarget,
672        config: &SurfaceConfig,
673    ) {
674        log::debug!("Surface command: ApplyConfig {:?}", target);
675        for surface in Self::resolve_surface_target(ctx, target) {
676            let handle = LayerSurfaceHandle::from_window_state(surface);
677
678            handle.set_size(config.dimensions.width(), config.dimensions.height());
679            handle.set_anchor_edges(config.anchor);
680            handle.set_exclusive_zone(config.exclusive_zone);
681            handle.set_margins(config.margin);
682            handle.set_layer(config.layer);
683            handle.set_keyboard_interactivity(config.keyboard_interactivity);
684            handle.commit();
685
686            surface.update_size_with_compositor_logic(
687                config.dimensions.width(),
688                config.dimensions.height(),
689            );
690        }
691    }
692
693    fn handle_surface_command(command: SurfaceCommand, ctx: &mut EventDispatchContext<'_>) {
694        match command {
695            SurfaceCommand::Resize {
696                target,
697                width,
698                height,
699            } => {
700                Self::apply_surface_resize(ctx, &target, width, height);
701            }
702            SurfaceCommand::SetAnchor { target, anchor } => {
703                Self::apply_surface_config_change(ctx, &target, "SetAnchor", |handle| {
704                    handle.set_anchor_edges(anchor);
705                });
706            }
707            SurfaceCommand::SetExclusiveZone { target, zone } => {
708                Self::apply_surface_config_change(ctx, &target, "SetExclusiveZone", |handle| {
709                    handle.set_exclusive_zone(zone);
710                });
711            }
712            SurfaceCommand::SetMargins { target, margins } => {
713                Self::apply_surface_config_change(ctx, &target, "SetMargins", |handle| {
714                    handle.set_margins(margins);
715                });
716            }
717            SurfaceCommand::SetLayer { target, layer } => {
718                Self::apply_surface_config_change(ctx, &target, "SetLayer", |handle| {
719                    handle.set_layer(layer);
720                });
721            }
722            SurfaceCommand::SetKeyboardInteractivity { target, mode } => {
723                Self::apply_surface_config_change(
724                    ctx,
725                    &target,
726                    "SetKeyboardInteractivity",
727                    |handle| {
728                        handle.set_keyboard_interactivity(mode);
729                    },
730                );
731            }
732            SurfaceCommand::SetOutputPolicy { target, policy } => {
733                log::debug!(
734                    "Surface command: SetOutputPolicy {:?} to {:?}",
735                    target,
736                    policy
737                );
738                log::warn!(
739                    "SetOutputPolicy is not yet implemented - requires runtime surface spawning"
740                );
741            }
742            SurfaceCommand::SetScaleFactor { target, factor } => {
743                log::debug!(
744                    "Surface command: SetScaleFactor {:?} to {:?}",
745                    target,
746                    factor
747                );
748                log::warn!(
749                    "SetScaleFactor is not yet implemented - requires runtime surface property updates"
750                );
751            }
752            SurfaceCommand::ApplyConfig { target, config } => {
753                Self::apply_full_config(ctx, &target, &config);
754            }
755        }
756
757        if let Err(e) = ctx.render_frame_if_dirty() {
758            log::error!("Failed to render frame after surface command: {}", e);
759        }
760    }
761
762    /// Returns a control handle for sending commands to the shell
763    #[must_use]
764    pub fn control(&self) -> ShellControl {
765        ShellControl::new(self.command_sender.clone())
766    }
767
768    /// Returns the names of all registered surfaces
769    pub fn surface_names(&self) -> Vec<&str> {
770        self.registry.surface_names()
771    }
772
773    /// Checks if a surface with the given name exists
774    pub fn has_surface(&self, name: &str) -> bool {
775        self.registry.contains_name(name)
776    }
777
778    /// Returns a handle to the event loop for registering custom event sources
779    pub fn event_loop_handle(&self) -> EventLoopHandle {
780        EventLoopHandle::new(Rc::downgrade(&self.inner))
781    }
782
783    /// Starts the event loop and runs the shell until exit
784    pub fn run(&mut self) -> Result<()> {
785        log::info!(
786            "Starting Shell event loop with {} windows",
787            self.registry.len()
788        );
789        self.inner.borrow_mut().run()?;
790        Ok(())
791    }
792
793    /// Spawns a new surface at runtime from the given definition
794    ///
795    /// The surface is instantiated on outputs according to its `OutputPolicy`.
796    pub fn spawn_surface(&mut self, definition: SurfaceDefinition) -> Result<Vec<SurfaceHandle>> {
797        let component_definition = self
798            .compilation_result
799            .component(&definition.component)
800            .ok_or_else(|| {
801                Error::Domain(DomainError::Configuration {
802                    message: format!(
803                        "Component '{}' not found in compilation result",
804                        definition.component
805                    ),
806                })
807            })?;
808
809        let handle = SurfaceHandle::new();
810        let wayland_config = WaylandSurfaceConfig::from_domain_config(
811            handle,
812            &definition.component,
813            component_definition,
814            Some(Rc::clone(&self.compilation_result)),
815            definition.config.clone(),
816        );
817
818        let shell_config = ShellSurfaceConfig {
819            name: definition.component.clone(),
820            config: wayland_config,
821        };
822
823        let mut system = self.inner.borrow_mut();
824        let handles = system.spawn_surface(&shell_config)?;
825
826        let surface_handle = SurfaceHandle::new();
827        let entry = SurfaceEntry::new(surface_handle, definition.component.clone(), definition);
828        self.registry.insert(entry)?;
829
830        log::info!(
831            "Spawned surface with handle {:?}, created {} output instances",
832            surface_handle,
833            handles.len()
834        );
835
836        Ok(vec![surface_handle])
837    }
838
839    /// Removes and destroys a surface by its handle
840    pub fn despawn_surface(&mut self, handle: SurfaceHandle) -> Result<()> {
841        let entry = self.registry.remove(handle).ok_or_else(|| {
842            Error::Domain(DomainError::Configuration {
843                message: format!("Surface handle {:?} not found", handle),
844            })
845        })?;
846
847        let mut system = self.inner.borrow_mut();
848        system.despawn_surface(&entry.name)?;
849
850        log::info!(
851            "Despawned surface '{}' with handle {:?}",
852            entry.name,
853            handle
854        );
855
856        Ok(())
857    }
858
859    /// Registers a handler called when a new output (monitor) is connected
860    ///
861    /// Surfaces with `OutputPolicy::All` spawn automatically on new outputs.
862    pub fn on_output_connected<F>(&mut self, handler: F) -> Result<()>
863    where
864        F: Fn(&OutputInfo) + 'static,
865    {
866        self.output_connected_handlers
867            .borrow_mut()
868            .push(Box::new(handler));
869        Ok(())
870    }
871
872    /// Registers a handler called when an output is disconnected
873    pub fn on_output_disconnected<F>(&mut self, handler: F) -> Result<()>
874    where
875        F: Fn(OutputHandle) + 'static,
876    {
877        self.output_disconnected_handlers
878            .borrow_mut()
879            .push(Box::new(handler));
880        Ok(())
881    }
882
883    /// Returns the handle for a surface by name
884    pub fn get_surface_handle(&self, name: &str) -> Option<SurfaceHandle> {
885        self.registry.handle_by_name(name)
886    }
887
888    /// Returns the name of a surface by its handle
889    pub fn get_surface_name(&self, handle: SurfaceHandle) -> Option<&str> {
890        self.registry.name_by_handle(handle)
891    }
892
893    /// Executes a function with access to a surface component instance by name
894    pub fn with_surface<F, R>(&self, name: &str, f: F) -> Result<R>
895    where
896        F: FnOnce(&ComponentInstance) -> R,
897    {
898        if !self.registry.contains_name(name) {
899            return Err(Error::Domain(DomainError::Configuration {
900                message: format!("Window '{}' not found", name),
901            }));
902        }
903
904        let system = self.inner.borrow();
905
906        system
907            .app_state()
908            .surfaces_by_name(name)
909            .first()
910            .map(|surface| f(surface.component_instance()))
911            .ok_or_else(|| {
912                Error::Domain(DomainError::Configuration {
913                    message: format!("No instance found for window '{}'", name),
914                })
915            })
916    }
917
918    /// Executes a function with each surface name and component instance
919    pub fn with_all_surfaces<F>(&self, mut f: F)
920    where
921        F: FnMut(&str, &ComponentInstance),
922    {
923        let system = self.inner.borrow();
924
925        for name in self.registry.surface_names() {
926            for surface in system.app_state().surfaces_by_name(name) {
927                f(name, surface.component_instance());
928            }
929        }
930    }
931
932    /// Executes a function with access to a surface on a specific output
933    pub fn with_output<F, R>(&self, handle: OutputHandle, f: F) -> Result<R>
934    where
935        F: FnOnce(&ComponentInstance) -> R,
936    {
937        let system = self.inner.borrow();
938        let window = system
939            .app_state()
940            .get_output_by_handle(handle)
941            .ok_or_else(|| {
942                Error::Domain(DomainError::Configuration {
943                    message: format!("Output with handle {:?} not found", handle),
944                })
945            })?;
946        Ok(f(window.component_instance()))
947    }
948
949    /// Executes a function with each output handle and component instance
950    pub fn with_all_outputs<F>(&self, mut f: F)
951    where
952        F: FnMut(OutputHandle, &ComponentInstance),
953    {
954        let system = self.inner.borrow();
955        for (handle, surface) in system.app_state().outputs_with_handles() {
956            f(handle, surface.component_instance());
957        }
958    }
959
960    /// Returns the Slint compilation result used by this shell
961    #[must_use]
962    pub fn compilation_result(&self) -> &Rc<CompilationResult> {
963        &self.compilation_result
964    }
965
966    /// Creates a popup builder for showing a popup window
967    #[must_use]
968    pub fn popup(&self, component_name: impl Into<String>) -> PopupBuilder<'_> {
969        PopupBuilder::new(self, component_name.into())
970    }
971
972    /// Returns the registry of all connected outputs
973    pub fn output_registry(&self) -> OutputRegistry {
974        let system = self.inner.borrow();
975        system.app_state().output_registry().clone()
976    }
977
978    /// Returns information about a specific output by handle
979    pub fn get_output_info(&self, handle: OutputHandle) -> Option<OutputInfo> {
980        let system = self.inner.borrow();
981        system.app_state().get_output_info(handle).cloned()
982    }
983
984    /// Returns information about all connected outputs
985    pub fn all_output_info(&self) -> Vec<OutputInfo> {
986        let system = self.inner.borrow();
987        system.app_state().all_output_info().cloned().collect()
988    }
989
990    /// Creates a selection for targeting specific surfaces by criteria
991    pub fn select(&self, selector: impl Into<crate::Selector>) -> crate::Selection<'_> {
992        crate::Selection::new(self, selector.into())
993    }
994
995    fn get_output_handles(&self) -> (Option<OutputHandle>, Option<OutputHandle>) {
996        let registry = &self.output_registry();
997        (registry.primary_handle(), registry.active_handle())
998    }
999
1000    pub(crate) fn on_internal<F, R>(
1001        &self,
1002        selector: &crate::Selector,
1003        callback_name: &str,
1004        handler: F,
1005    ) where
1006        F: Fn(CallbackContext) -> R + Clone + 'static,
1007        R: IntoValue,
1008    {
1009        let control = self.control();
1010        let handler = Rc::new(handler);
1011        let system = self.inner.borrow();
1012        let (primary, active) = self.get_output_handles();
1013
1014        for (key, surface) in system.app_state().surfaces_with_keys() {
1015            let surface_handle = key.surface_handle;
1016            let output_handle = key.output_handle;
1017
1018            let surface_name = self.registry.by_handle(surface_handle).map_or_else(
1019                || format!("Unknown-{}", surface_handle.id()),
1020                |entry| entry.name.clone(),
1021            );
1022
1023            let surface_info = crate::SurfaceInfo {
1024                name: surface_name.clone(),
1025                output: output_handle,
1026            };
1027
1028            let output_info = system.app_state().get_output_info(output_handle);
1029
1030            if selector.matches(&surface_info, output_info, primary, active) {
1031                let instance_id = SurfaceInstanceId::new(surface_handle, output_handle);
1032
1033                let handler_rc = Rc::clone(&handler);
1034                let control_clone = control.clone();
1035                let surface_name_clone = surface_name.clone();
1036
1037                if let Err(e) =
1038                    surface
1039                        .component_instance()
1040                        .set_callback(callback_name, move |_args| {
1041                            let ctx = CallbackContext::new(
1042                                instance_id,
1043                                surface_name_clone.clone(),
1044                                control_clone.clone(),
1045                            );
1046                            handler_rc(ctx).into_value()
1047                        })
1048                {
1049                    log::error!(
1050                        "Failed to register callback '{}' on surface '{}': {}",
1051                        callback_name,
1052                        surface_name,
1053                        e
1054                    );
1055                }
1056            }
1057        }
1058    }
1059
1060    pub(crate) fn on_with_args_internal<F, R>(
1061        &self,
1062        selector: &crate::Selector,
1063        callback_name: &str,
1064        handler: F,
1065    ) where
1066        F: Fn(&[Value], CallbackContext) -> R + Clone + 'static,
1067        R: IntoValue,
1068    {
1069        let control = self.control();
1070        let handler = Rc::new(handler);
1071        let system = self.inner.borrow();
1072        let (primary, active) = self.get_output_handles();
1073
1074        for (key, surface) in system.app_state().surfaces_with_keys() {
1075            let surface_handle = key.surface_handle;
1076            let output_handle = key.output_handle;
1077
1078            let surface_name = self.registry.by_handle(surface_handle).map_or_else(
1079                || format!("Unknown-{}", surface_handle.id()),
1080                |entry| entry.name.clone(),
1081            );
1082
1083            let surface_info = crate::SurfaceInfo {
1084                name: surface_name.clone(),
1085                output: output_handle,
1086            };
1087
1088            let output_info = system.app_state().get_output_info(output_handle);
1089
1090            if selector.matches(&surface_info, output_info, primary, active) {
1091                let instance_id = SurfaceInstanceId::new(surface_handle, output_handle);
1092
1093                let handler_rc = Rc::clone(&handler);
1094                let control_clone = control.clone();
1095                let surface_name_clone = surface_name.clone();
1096
1097                if let Err(e) =
1098                    surface
1099                        .component_instance()
1100                        .set_callback(callback_name, move |args| {
1101                            let ctx = CallbackContext::new(
1102                                instance_id,
1103                                surface_name_clone.clone(),
1104                                control_clone.clone(),
1105                            );
1106                            handler_rc(args, ctx).into_value()
1107                        })
1108                {
1109                    log::error!(
1110                        "Failed to register callback '{}' on surface '{}': {}",
1111                        callback_name,
1112                        surface_name,
1113                        e
1114                    );
1115                }
1116            }
1117        }
1118    }
1119
1120    pub(crate) fn with_selected<F>(&self, selector: &crate::Selector, mut f: F)
1121    where
1122        F: FnMut(&str, &ComponentInstance),
1123    {
1124        let system = self.inner.borrow();
1125        let (primary, active) = self.get_output_handles();
1126
1127        for (key, surface) in system.app_state().surfaces_with_keys() {
1128            let surface_name = system
1129                .app_state()
1130                .get_surface_name(key.surface_handle)
1131                .unwrap_or("unknown");
1132            let surface_info = crate::SurfaceInfo {
1133                name: surface_name.to_string(),
1134                output: key.output_handle,
1135            };
1136
1137            let output_info = system.app_state().get_output_info(key.output_handle);
1138
1139            if selector.matches(&surface_info, output_info, primary, active) {
1140                f(surface_name, surface.component_instance());
1141            }
1142        }
1143    }
1144
1145    pub(crate) fn configure_selected<F>(&self, selector: &crate::Selector, mut f: F)
1146    where
1147        F: FnMut(&ComponentInstance, LayerSurfaceHandle<'_>),
1148    {
1149        let system = self.inner.borrow();
1150        let (primary, active) = self.get_output_handles();
1151
1152        for (key, surface) in system.app_state().surfaces_with_keys() {
1153            let surface_name = system
1154                .app_state()
1155                .get_surface_name(key.surface_handle)
1156                .unwrap_or("unknown");
1157            let surface_info = crate::SurfaceInfo {
1158                name: surface_name.to_string(),
1159                output: key.output_handle,
1160            };
1161
1162            let output_info = system.app_state().get_output_info(key.output_handle);
1163
1164            if selector.matches(&surface_info, output_info, primary, active) {
1165                let surface_handle = LayerSurfaceHandle::from_window_state(surface);
1166                f(surface.component_instance(), surface_handle);
1167            }
1168        }
1169    }
1170
1171    pub(crate) fn count_selected(&self, selector: &crate::Selector) -> usize {
1172        let system = self.inner.borrow();
1173        let (primary, active) = self.get_output_handles();
1174
1175        system
1176            .app_state()
1177            .surfaces_with_keys()
1178            .filter(|(key, _)| {
1179                let surface_name = system
1180                    .app_state()
1181                    .get_surface_name(key.surface_handle)
1182                    .unwrap_or("unknown");
1183                let surface_info = crate::SurfaceInfo {
1184                    name: surface_name.to_string(),
1185                    output: key.output_handle,
1186                };
1187
1188                let output_info = system.app_state().get_output_info(key.output_handle);
1189
1190                selector.matches(&surface_info, output_info, primary, active)
1191            })
1192            .count()
1193    }
1194
1195    pub(crate) fn get_selected_info(&self, selector: &crate::Selector) -> Vec<crate::SurfaceInfo> {
1196        let system = self.inner.borrow();
1197        let (primary, active) = self.get_output_handles();
1198
1199        system
1200            .app_state()
1201            .surfaces_with_keys()
1202            .filter_map(|(key, _)| {
1203                let surface_name = system
1204                    .app_state()
1205                    .get_surface_name(key.surface_handle)
1206                    .unwrap_or("unknown");
1207                let surface_info = crate::SurfaceInfo {
1208                    name: surface_name.to_string(),
1209                    output: key.output_handle,
1210                };
1211
1212                let output_info = system.app_state().get_output_info(key.output_handle);
1213
1214                if selector.matches(&surface_info, output_info, primary, active) {
1215                    Some(surface_info)
1216                } else {
1217                    None
1218                }
1219            })
1220            .collect()
1221    }
1222}
1223
1224impl ShellRuntime for Shell {
1225    type LoopHandle = EventLoopHandle;
1226    type Context<'a> = ShellEventContext<'a>;
1227
1228    fn event_loop_handle(&self) -> Self::LoopHandle {
1229        EventLoopHandle::new(Rc::downgrade(&self.inner))
1230    }
1231
1232    fn with_component<F>(&self, name: &str, mut f: F)
1233    where
1234        F: FnMut(&ComponentInstance),
1235    {
1236        let system = self.inner.borrow();
1237
1238        if self.registry.contains_name(name) {
1239            for surface in system.app_state().surfaces_by_name(name) {
1240                f(surface.component_instance());
1241            }
1242        }
1243    }
1244
1245    fn with_all_components<F>(&self, mut f: F)
1246    where
1247        F: FnMut(&str, &ComponentInstance),
1248    {
1249        let system = self.inner.borrow();
1250
1251        for name in self.registry.surface_names() {
1252            for surface in system.app_state().surfaces_by_name(name) {
1253                f(name, surface.component_instance());
1254            }
1255        }
1256    }
1257
1258    fn run(&mut self) -> Result<()> {
1259        self.inner.borrow_mut().run()?;
1260        Ok(())
1261    }
1262}
1263
1264/// Context providing access to shell state within custom event source callbacks
1265///
1266/// Obtained via event source callbacks registered through `EventLoopHandle`.
1267pub struct ShellEventContext<'a> {
1268    app_state: &'a mut AppState,
1269}
1270
1271impl<'a> FromAppState<'a> for ShellEventContext<'a> {
1272    fn from_app_state(app_state: &'a mut AppState) -> Self {
1273        Self { app_state }
1274    }
1275}
1276
1277impl ShellEventContext<'_> {
1278    /// Returns the component instance for a surface by name
1279    pub fn get_surface_component(&self, name: &str) -> Option<&ComponentInstance> {
1280        self.app_state
1281            .surfaces_by_name(name)
1282            .first()
1283            .map(|s| s.component_instance())
1284    }
1285
1286    /// Returns all surface component instances
1287    pub fn all_surface_components(&self) -> impl Iterator<Item = &ComponentInstance> {
1288        self.app_state
1289            .all_outputs()
1290            .map(SurfaceState::component_instance)
1291    }
1292
1293    /// Renders a new frame for all dirty surfaces
1294    pub fn render_frame_if_dirty(&mut self) -> Result<()> {
1295        for surface in self.app_state.all_outputs() {
1296            surface.render_frame_if_dirty()?;
1297        }
1298        Ok(())
1299    }
1300
1301    /// Returns the primary output handle
1302    #[must_use]
1303    pub fn primary_output_handle(&self) -> Option<OutputHandle> {
1304        self.app_state.primary_output_handle()
1305    }
1306
1307    /// Returns the active output handle
1308    #[must_use]
1309    pub fn active_output_handle(&self) -> Option<OutputHandle> {
1310        self.app_state.active_output_handle()
1311    }
1312
1313    /// Returns the output registry
1314    pub fn output_registry(&self) -> &OutputRegistry {
1315        self.app_state.output_registry()
1316    }
1317
1318    /// Returns all outputs with their handles and component instances
1319    pub fn outputs(&self) -> impl Iterator<Item = (OutputHandle, &ComponentInstance)> {
1320        self.app_state
1321            .outputs_with_handles()
1322            .map(|(handle, surface)| (handle, surface.component_instance()))
1323    }
1324
1325    /// Returns the component instance for a specific output
1326    pub fn get_output_component(&self, handle: OutputHandle) -> Option<&ComponentInstance> {
1327        self.app_state
1328            .get_output_by_handle(handle)
1329            .map(SurfaceState::component_instance)
1330    }
1331
1332    /// Returns information about a specific output
1333    pub fn get_output_info(&self, handle: OutputHandle) -> Option<&OutputInfo> {
1334        self.app_state.get_output_info(handle)
1335    }
1336
1337    /// Returns information about all outputs
1338    pub fn all_output_info(&self) -> impl Iterator<Item = &OutputInfo> {
1339        self.app_state.all_output_info()
1340    }
1341
1342    /// Returns all outputs with their info and component instances
1343    pub fn outputs_with_info(&self) -> impl Iterator<Item = (&OutputInfo, &ComponentInstance)> {
1344        self.app_state
1345            .outputs_with_info()
1346            .map(|(info, surface)| (info, surface.component_instance()))
1347    }
1348
1349    /// Returns the compilation result if available
1350    #[must_use]
1351    pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
1352        self.app_state
1353            .primary_output()
1354            .and_then(SurfaceState::compilation_result)
1355    }
1356}