layer_shika_adapters/wayland/session_lock/manager/
state.rs

1use super::callbacks::{LockCallbackContext, LockCallbackExt, LockPropertyOperationExt};
2use crate::errors::Result;
3use crate::rendering::femtovg::main_window::FemtoVGWindow;
4use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
5use crate::rendering::slint_integration::platform::CustomSlintPlatform;
6use crate::wayland::session_lock::lock_surface::LockSurface;
7use crate::wayland::surfaces::component_state::ComponentState;
8use crate::wayland::surfaces::display_metrics::DisplayMetrics;
9use layer_shika_domain::surface_dimensions::SurfaceDimensions;
10use layer_shika_domain::value_objects::output_handle::OutputHandle;
11use layer_shika_domain::value_objects::output_info::OutputInfo;
12use log::info;
13use slint::{
14    LogicalPosition, LogicalSize, WindowSize,
15    platform::{WindowAdapter, WindowEvent},
16};
17use slint_interpreter::{CompilationResult, ComponentDefinition};
18use std::rc::Rc;
19
20use super::callbacks::LockCallback;
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum LockScalingMode {
24    FractionalWithViewport,
25    FractionalOnly,
26    Integer,
27}
28
29pub struct LockSurfaceOutputContext {
30    pub output_handle: OutputHandle,
31    pub output_info: Option<OutputInfo>,
32    pub primary_handle: Option<OutputHandle>,
33    pub active_handle: Option<OutputHandle>,
34}
35
36pub struct LockConfigureContext {
37    pub scale_factor: f32,
38    pub component_definition: ComponentDefinition,
39    pub compilation_result: Option<Rc<CompilationResult>>,
40    pub platform: Rc<CustomSlintPlatform>,
41    pub callbacks: Vec<LockCallback>,
42    pub property_operations: Vec<super::callbacks::LockPropertyOperation>,
43    pub component_name: String,
44    pub output_handle: OutputHandle,
45    pub output_info: Option<OutputInfo>,
46    pub primary_handle: Option<OutputHandle>,
47    pub active_handle: Option<OutputHandle>,
48}
49
50pub struct ActiveLockSurface {
51    surface: LockSurface,
52    window: Rc<FemtoVGWindow>,
53    component: Option<ComponentState>,
54    scale_factor: f32,
55    has_fractional_scale: bool,
56    component_name: Option<String>,
57    pub(super) output_handle: Option<OutputHandle>,
58    pub(super) output_info: Option<OutputInfo>,
59    pub(super) primary_handle: Option<OutputHandle>,
60    pub(super) active_handle: Option<OutputHandle>,
61    pending_component_initialization: bool,
62}
63
64impl ActiveLockSurface {
65    pub fn new(surface: LockSurface, window: Rc<FemtoVGWindow>) -> Self {
66        Self {
67            has_fractional_scale: surface.fractional_scale().is_some(),
68            surface,
69            window,
70            component: None,
71            scale_factor: 1.0,
72            output_handle: None,
73            component_name: None,
74            output_info: None,
75            primary_handle: None,
76            active_handle: None,
77            pending_component_initialization: false,
78        }
79    }
80
81    pub fn handle_surface_configured(
82        &mut self,
83        serial: u32,
84        width: u32,
85        height: u32,
86        context: &LockConfigureContext,
87    ) {
88        self.surface.handle_configure(serial, width, height);
89        self.scale_factor = context.scale_factor;
90        self.output_handle = Some(context.output_handle);
91        self.component_name = Some(context.component_name.clone());
92        self.output_info.clone_from(&context.output_info);
93        self.primary_handle = context.primary_handle;
94        self.active_handle = context.active_handle;
95        let dimensions = match SurfaceDimensions::calculate(width, height, context.scale_factor) {
96            Ok(dimensions) => dimensions,
97            Err(err) => {
98                info!("Failed to calculate lock surface dimensions: {err}");
99                return;
100            }
101        };
102        let scaling_mode = self.scaling_mode();
103        info!(
104            "Lock surface dimensions: logical {}x{}, physical {}x{}, scale {}, mode {:?}",
105            dimensions.logical_width(),
106            dimensions.logical_height(),
107            dimensions.physical_width(),
108            dimensions.physical_height(),
109            context.scale_factor,
110            scaling_mode
111        );
112        self.configure_window(&dimensions, scaling_mode, context.scale_factor);
113        self.configure_surface(&dimensions, scaling_mode);
114
115        if self.component.is_none() {
116            self.pending_component_initialization = true;
117        }
118
119        RenderableWindow::request_redraw(self.window.as_ref());
120    }
121
122    pub fn handle_configure(
123        &mut self,
124        serial: u32,
125        width: u32,
126        height: u32,
127        context: &LockConfigureContext,
128    ) -> Result<()> {
129        self.handle_surface_configured(serial, width, height, context);
130
131        if self.pending_component_initialization {
132            self.initialize_component(context)?;
133        }
134
135        Ok(())
136    }
137
138    fn initialize_component(&mut self, context: &LockConfigureContext) -> Result<()> {
139        if self.component.is_some() {
140            return Ok(());
141        }
142
143        context.platform.add_window(Rc::clone(&self.window));
144        let component = ComponentState::new(
145            context.component_definition.clone(),
146            context.compilation_result.clone(),
147            &self.window,
148        )?;
149        self.window
150            .window()
151            .dispatch_event(WindowEvent::WindowActiveChanged(true));
152
153        let callback_context = LockCallbackContext::new(
154            context.component_name.clone(),
155            context.output_handle,
156            context.output_info.clone(),
157            context.primary_handle,
158            context.active_handle,
159        );
160
161        for callback in &context.callbacks {
162            if let Err(err) =
163                callback.apply_with_context(component.component_instance(), &callback_context)
164            {
165                info!(
166                    "Failed to register lock callback '{}': {err}",
167                    callback.name()
168                );
169            } else if callback.should_apply(&callback_context) {
170                info!("Registered lock callback '{}'", callback.name());
171            } else {
172                info!(
173                    "Skipping callback '{}' due to selector filter (output {:?})",
174                    callback.name(),
175                    context.output_handle
176                );
177            }
178        }
179
180        for property_op in &context.property_operations {
181            if property_op.should_apply(&callback_context) {
182                if let Err(err) = property_op.apply_to_component(component.component_instance()) {
183                    info!(
184                        "Failed to set lock property '{}': {err}",
185                        property_op.name()
186                    );
187                } else {
188                    info!(
189                        "Set lock property '{}' on output {:?}",
190                        property_op.name(),
191                        context.output_handle
192                    );
193                }
194            } else {
195                info!(
196                    "Skipping property '{}' due to selector filter (output {:?}, primary={:?})",
197                    property_op.name(),
198                    context.output_handle,
199                    context.primary_handle
200                );
201            }
202        }
203
204        self.component = Some(component);
205        self.pending_component_initialization = false;
206
207        Ok(())
208    }
209
210    pub fn render_frame_if_dirty(&self) -> Result<()> {
211        self.window.render_frame_if_dirty()
212    }
213
214    pub fn handle_fractional_scale(&mut self, scale_120ths: u32) {
215        let scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths);
216        self.scale_factor = scale_factor;
217        if self.surface.width() == 0 || self.surface.height() == 0 {
218            return;
219        }
220        let Ok(dimensions) =
221            SurfaceDimensions::calculate(self.surface.width(), self.surface.height(), scale_factor)
222        else {
223            return;
224        };
225        let scaling_mode = self.scaling_mode();
226        self.configure_window(&dimensions, scaling_mode, scale_factor);
227        self.configure_surface(&dimensions, scaling_mode);
228        RenderableWindow::request_redraw(self.window.as_ref());
229    }
230
231    pub fn apply_callback(&self, callback: &LockCallback) {
232        let Some(component) = self.component.as_ref() else {
233            return;
234        };
235
236        let Some(component_name) = &self.component_name else {
237            return;
238        };
239
240        let Some(output_handle) = self.output_handle else {
241            return;
242        };
243
244        let callback_context = LockCallbackContext::new(
245            component_name.clone(),
246            output_handle,
247            self.output_info.clone(),
248            self.primary_handle,
249            self.active_handle,
250        );
251
252        if let Err(err) =
253            callback.apply_with_context(component.component_instance(), &callback_context)
254        {
255            info!(
256                "Failed to register lock callback '{}': {err}",
257                callback.name()
258            );
259        }
260    }
261
262    pub fn apply_property_operation(&self, property_op: &super::callbacks::LockPropertyOperation) {
263        let Some(component) = self.component.as_ref() else {
264            return;
265        };
266
267        let Some(component_name) = &self.component_name else {
268            return;
269        };
270
271        let Some(output_handle) = self.output_handle else {
272            return;
273        };
274
275        let callback_context = LockCallbackContext::new(
276            component_name.clone(),
277            output_handle,
278            self.output_info.clone(),
279            self.primary_handle,
280            self.active_handle,
281        );
282
283        if let Err(err) =
284            property_op.apply_with_context(component.component_instance(), &callback_context)
285        {
286            info!(
287                "Failed to set lock property '{}': {err}",
288                property_op.name()
289            );
290        }
291    }
292
293    fn scaling_mode(&self) -> LockScalingMode {
294        if self.surface.has_fractional_scale() && self.surface.has_viewport() {
295            LockScalingMode::FractionalWithViewport
296        } else if self.surface.has_fractional_scale() {
297            LockScalingMode::FractionalOnly
298        } else {
299            LockScalingMode::Integer
300        }
301    }
302
303    #[allow(clippy::cast_precision_loss)]
304    fn configure_window(
305        &self,
306        dimensions: &SurfaceDimensions,
307        mode: LockScalingMode,
308        scale_factor: f32,
309    ) {
310        match mode {
311            LockScalingMode::FractionalWithViewport => {
312                let config = FractionalScaleConfig::new(
313                    dimensions.logical_width() as f32,
314                    dimensions.logical_height() as f32,
315                    scale_factor,
316                );
317                info!(
318                    "Lock FractionalWithViewport: render scale {} (from {}), physical {}x{}",
319                    config.render_scale,
320                    scale_factor,
321                    config.render_physical_size.width,
322                    config.render_physical_size.height
323                );
324                config.apply_to(self.window.as_ref());
325            }
326            LockScalingMode::FractionalOnly => {
327                RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
328                self.window.set_size(WindowSize::Logical(LogicalSize::new(
329                    dimensions.logical_width() as f32,
330                    dimensions.logical_height() as f32,
331                )));
332            }
333            LockScalingMode::Integer => {
334                RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
335                self.window
336                    .set_size(WindowSize::Physical(slint::PhysicalSize::new(
337                        dimensions.physical_width(),
338                        dimensions.physical_height(),
339                    )));
340            }
341        }
342    }
343
344    fn configure_surface(&self, dimensions: &SurfaceDimensions, mode: LockScalingMode) {
345        match mode {
346            LockScalingMode::FractionalWithViewport => {
347                self.surface.configure_fractional_viewport(
348                    dimensions.logical_width(),
349                    dimensions.logical_height(),
350                );
351            }
352            LockScalingMode::FractionalOnly | LockScalingMode::Integer => {
353                self.surface
354                    .configure_buffer_scale(dimensions.buffer_scale());
355            }
356        }
357    }
358
359    #[allow(clippy::cast_possible_truncation)]
360    pub fn to_logical_position(&self, surface_x: f64, surface_y: f64) -> LogicalPosition {
361        if self.has_fractional_scale {
362            let x = surface_x as f32;
363            let y = surface_y as f32;
364            LogicalPosition::new(x, y)
365        } else {
366            let x = (surface_x / f64::from(self.scale_factor)) as f32;
367            let y = (surface_y / f64::from(self.scale_factor)) as f32;
368            LogicalPosition::new(x, y)
369        }
370    }
371
372    pub fn dispatch_event(&self, event: WindowEvent) {
373        self.window.window().dispatch_event(event);
374    }
375
376    pub const fn surface(&self) -> &LockSurface {
377        &self.surface
378    }
379
380    pub const fn component(&self) -> Option<&ComponentState> {
381        self.component.as_ref()
382    }
383
384    pub const fn has_pending_initialization(&self) -> bool {
385        self.pending_component_initialization
386    }
387
388    pub fn initialize_pending_component(&mut self, context: &LockConfigureContext) -> Result<()> {
389        if self.pending_component_initialization {
390            self.initialize_component(context)?;
391        }
392        Ok(())
393    }
394}