layer_shika_adapters/wayland/surfaces/
surface_state.rs1use std::rc::Rc;
2use std::cell::RefCell;
3use super::surface_builder::SurfaceStateBuilder;
4use super::component_state::ComponentState;
5use super::rendering_state::RenderingState;
6use super::event_context::{EventContext, SharedPointerSerial};
7use super::popup_manager::PopupManager;
8use super::surface_renderer::SurfaceRendererParams;
9use super::display_metrics::{DisplayMetrics, SharedDisplayMetrics};
10use crate::wayland::managed_proxies::{
11 ManagedWlPointer, ManagedWlSurface, ManagedZwlrLayerSurfaceV1,
12 ManagedWpFractionalScaleV1, ManagedWpViewport,
13};
14use crate::rendering::femtovg::main_window::FemtoVGWindow;
15use crate::errors::{LayerShikaError, Result};
16use core::result::Result as CoreResult;
17use layer_shika_domain::errors::DomainError;
18use layer_shika_domain::ports::shell::ShellContextPort;
19use slint::{LogicalPosition, PhysicalSize};
20use slint::platform::WindowEvent;
21use slint_interpreter::{ComponentInstance, CompilationResult};
22use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
23use wayland_client::{
24 Proxy,
25 backend::ObjectId,
26 protocol::{wl_pointer, wl_surface::WlSurface},
27};
28use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
29
30pub struct SurfaceState {
31 component: ComponentState,
32 rendering: RenderingState<FemtoVGWindow>,
33 event_context: RefCell<EventContext>,
34 display_metrics: SharedDisplayMetrics,
35 #[allow(dead_code)]
36 pointer: ManagedWlPointer,
37}
38
39impl SurfaceState {
40 pub fn new(builder: SurfaceStateBuilder) -> Result<Self> {
41 let component_definition =
42 builder
43 .component_definition
44 .ok_or_else(|| LayerShikaError::InvalidInput {
45 message: "Component definition is required".into(),
46 })?;
47 let window = builder
48 .window
49 .ok_or_else(|| LayerShikaError::InvalidInput {
50 message: "Window is required".into(),
51 })?;
52
53 let component =
54 ComponentState::new(component_definition, builder.compilation_result, &window)?;
55
56 let connection = builder
57 .connection
58 .ok_or_else(|| LayerShikaError::InvalidInput {
59 message: "Connection is required".into(),
60 })?;
61
62 let surface_rc = builder
63 .surface
64 .ok_or_else(|| LayerShikaError::InvalidInput {
65 message: "Surface is required".into(),
66 })?;
67 let layer_surface_rc =
68 builder
69 .layer_surface
70 .ok_or_else(|| LayerShikaError::InvalidInput {
71 message: "Layer surface is required".into(),
72 })?;
73 let pointer_rc = builder
74 .pointer
75 .ok_or_else(|| LayerShikaError::InvalidInput {
76 message: "Pointer is required".into(),
77 })?;
78
79 let viewport = builder
80 .viewport
81 .map(|vp| ManagedWpViewport::new(vp, Rc::clone(&connection)));
82 let fractional_scale = builder
83 .fractional_scale
84 .map(|fs| ManagedWpFractionalScaleV1::new(fs, Rc::clone(&connection)));
85 let layer_surface =
86 ManagedZwlrLayerSurfaceV1::new(layer_surface_rc, Rc::clone(&connection));
87 let surface = ManagedWlSurface::new(Rc::clone(&surface_rc), Rc::clone(&connection));
88 let pointer = ManagedWlPointer::new(pointer_rc, Rc::clone(&connection));
89
90 let has_fractional_scale = fractional_scale.is_some();
91 let size = builder.size.unwrap_or_default();
92
93 let main_surface_id = (*surface_rc).id();
94
95 let display_metrics = Rc::new(RefCell::new(
96 DisplayMetrics::new(builder.scale_factor, has_fractional_scale)
97 .with_output_size(builder.output_size.unwrap_or_default()),
98 ));
99
100 let event_context = EventContext::new(
101 Rc::clone(&window),
102 main_surface_id,
103 Rc::clone(&display_metrics),
104 );
105
106 let rendering = RenderingState::new(SurfaceRendererParams {
107 window: Rc::clone(&window),
108 surface,
109 layer_surface,
110 viewport,
111 fractional_scale,
112 height: builder.height,
113 size,
114 });
115
116 Ok(Self {
117 component,
118 rendering,
119 event_context: RefCell::new(event_context),
120 display_metrics,
121 pointer,
122 })
123 }
124
125 pub fn update_size(&mut self, width: u32, height: u32) {
126 let scale_factor = self.event_context.borrow().scale_factor();
127 self.rendering.update_size(width, height, scale_factor);
128 }
129
130 #[allow(clippy::cast_precision_loss)]
131 #[allow(clippy::cast_possible_truncation)]
132 #[allow(clippy::cast_sign_loss)]
133 pub fn update_size_with_compositor_logic(
134 &mut self,
135 requested_width: u32,
136 requested_height: u32,
137 ) {
138 let scale_factor = self.event_context.borrow().scale_factor();
139 let output_width = self.output_size().width;
140
141 let target_width = if requested_width == 0 || (requested_width == 1 && output_width > 1) {
142 if scale_factor > 1.0 {
143 (output_width as f32 / scale_factor).round() as u32
144 } else {
145 output_width
146 }
147 } else {
148 requested_width
149 };
150
151 let target_height = if requested_height > 0 {
152 requested_height
153 } else {
154 let h = self.height();
155 if scale_factor > 1.0 {
156 (h as f32 / scale_factor).round() as u32
157 } else {
158 h
159 }
160 };
161
162 self.rendering
163 .update_size(target_width, target_height, scale_factor);
164 }
165
166 #[allow(clippy::cast_possible_truncation)]
167 pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) {
168 self.event_context
169 .borrow_mut()
170 .set_current_pointer_position(physical_x, physical_y);
171 }
172
173 pub fn size(&self) -> PhysicalSize {
174 self.rendering.size()
175 }
176
177 pub fn current_pointer_position(&self) -> LogicalPosition {
178 self.event_context.borrow().current_pointer_position()
179 }
180
181 pub(crate) fn window(&self) -> Rc<FemtoVGWindow> {
182 Rc::clone(self.rendering.window())
183 }
184
185 pub fn layer_surface(&self) -> Rc<ZwlrLayerSurfaceV1> {
186 self.rendering.layer_surface()
187 }
188
189 pub fn commit_surface(&self) {
190 self.rendering.commit_surface();
191 }
192
193 pub fn height(&self) -> u32 {
194 self.rendering.height()
195 }
196
197 pub fn set_output_size(&mut self, output_size: PhysicalSize) {
198 self.display_metrics
199 .borrow_mut()
200 .update_output_size(output_size);
201 self.event_context.borrow().update_output_size(output_size);
202 }
203
204 pub fn output_size(&self) -> PhysicalSize {
205 self.display_metrics.borrow().output_size()
206 }
207
208 pub const fn component_instance(&self) -> &ComponentInstance {
209 self.component.component_instance()
210 }
211
212 #[must_use]
213 pub fn compilation_result(&self) -> Option<Rc<CompilationResult>> {
214 self.component.compilation_result()
215 }
216
217 pub fn render_frame_if_dirty(&self) -> Result<()> {
218 self.rendering.render_frame_if_dirty()
219 }
220
221 #[allow(clippy::cast_precision_loss)]
222 pub fn update_scale_factor(&mut self, scale_120ths: u32) {
223 self.event_context
224 .borrow_mut()
225 .update_scale_factor(scale_120ths);
226
227 let current_logical_size = self.rendering.logical_size();
228 if current_logical_size.width > 0 && current_logical_size.height > 0 {
229 self.update_size(current_logical_size.width, current_logical_size.height);
230 }
231 }
232
233 pub fn scale_factor(&self) -> f32 {
234 self.event_context.borrow().scale_factor()
235 }
236
237 pub const fn display_metrics(&self) -> &SharedDisplayMetrics {
238 &self.display_metrics
239 }
240
241 pub fn last_pointer_serial(&self) -> u32 {
242 self.event_context.borrow().last_pointer_serial()
243 }
244
245 pub fn set_last_pointer_serial(&mut self, serial: u32) {
246 self.event_context
247 .borrow_mut()
248 .set_last_pointer_serial(serial);
249 }
250
251 pub fn set_shared_pointer_serial(&mut self, shared_serial: Rc<SharedPointerSerial>) {
252 self.event_context
253 .borrow_mut()
254 .set_shared_pointer_serial(shared_serial);
255 }
256
257 pub fn set_popup_manager(&mut self, popup_manager: Rc<PopupManager>) {
258 self.event_context
259 .borrow_mut()
260 .set_popup_manager(popup_manager);
261 }
262
263 pub fn set_entered_surface(&self, surface: &WlSurface) {
264 self.event_context.borrow_mut().set_entered_surface(surface);
265 }
266
267 pub fn clear_entered_surface(&self) {
268 self.event_context.borrow_mut().clear_entered_surface();
269 }
270
271 pub fn is_popup_active(&self) -> bool {
272 self.event_context.borrow().is_popup_active()
273 }
274
275 pub fn dispatch_to_active_window(&self, event: WindowEvent) {
276 self.event_context.borrow().dispatch_to_active_window(event);
277 }
278
279 pub fn dispatch_to_surface(&self, surface_id: &ObjectId, event: WindowEvent) {
280 self.event_context
281 .borrow()
282 .dispatch_to_surface(surface_id, event);
283 }
284
285 pub fn set_axis_source(&self, axis_source: wl_pointer::AxisSource) {
286 self.event_context.borrow_mut().set_axis_source(axis_source);
287 }
288
289 pub fn accumulate_axis(&self, axis: wl_pointer::Axis, value: f64) {
290 self.event_context.borrow_mut().accumulate_axis(axis, value);
291 }
292
293 pub fn accumulate_axis_discrete(&self, axis: wl_pointer::Axis, discrete: i32) {
294 self.event_context
295 .borrow_mut()
296 .accumulate_axis_discrete(axis, discrete);
297 }
298
299 pub fn take_accumulated_axis(&self) -> (f32, f32) {
300 self.event_context.borrow_mut().take_accumulated_axis()
301 }
302
303 #[allow(clippy::cast_precision_loss)]
304 pub fn update_scale_for_fractional_scale_object(
305 &mut self,
306 fractional_scale_proxy: &WpFractionalScaleV1,
307 scale_120ths: u32,
308 ) {
309 let fractional_scale_id = fractional_scale_proxy.id();
310
311 if let Some(main_fractional_scale) = self.rendering.fractional_scale() {
312 if (**main_fractional_scale.inner()).id() == fractional_scale_id {
313 self.update_scale_factor(scale_120ths);
314 return;
315 }
316 }
317
318 self.event_context
319 .borrow()
320 .update_scale_for_fractional_scale_object(fractional_scale_proxy, scale_120ths);
321 }
322
323 pub fn popup_manager(&self) -> Option<Rc<PopupManager>> {
324 self.event_context.borrow().popup_manager().cloned()
325 }
326}
327
328impl ShellContextPort for SurfaceState {
329 fn render_frame_if_dirty(&mut self) -> CoreResult<(), DomainError> {
330 SurfaceState::render_frame_if_dirty(self).map_err(|e| DomainError::Adapter {
331 source: Box::new(e),
332 })
333 }
334}