winit/platform_impl/linux/wayland/
state.rs

1use std::cell::RefCell;
2use std::sync::atomic::Ordering;
3use std::sync::{Arc, Mutex};
4
5use ahash::AHashMap;
6use sctk::compositor::{CompositorHandler, CompositorState};
7use sctk::output::{OutputHandler, OutputState};
8use sctk::reexports::calloop::LoopHandle;
9use sctk::reexports::client::backend::ObjectId;
10use sctk::reexports::client::globals::GlobalList;
11use sctk::reexports::client::protocol::wl_output::WlOutput;
12use sctk::reexports::client::protocol::wl_surface::WlSurface;
13use sctk::reexports::client::{Connection, Proxy, QueueHandle};
14use sctk::registry::{ProvidesRegistryState, RegistryState};
15use sctk::seat::pointer::ThemedPointer;
16use sctk::seat::SeatState;
17use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler};
18use sctk::shell::xdg::XdgShell;
19use sctk::shell::WaylandSurface;
20use sctk::shm::slot::SlotPool;
21use sctk::shm::{Shm, ShmHandler};
22use sctk::subcompositor::SubcompositorState;
23
24use crate::error::OsError;
25use crate::platform_impl::wayland::event_loop::sink::EventSink;
26use crate::platform_impl::wayland::output::MonitorHandle;
27use crate::platform_impl::wayland::seat::{
28    PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
29    WinitPointerDataExt, WinitSeatState,
30};
31use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
32use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager;
33use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState;
34use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState;
35use crate::platform_impl::wayland::window::{WindowRequests, WindowState};
36use crate::platform_impl::wayland::WindowId;
37
38/// Winit's Wayland state.
39pub struct WinitState {
40    /// The WlRegistry.
41    pub registry_state: RegistryState,
42
43    /// The state of the WlOutput handling.
44    pub output_state: OutputState,
45
46    /// The compositor state which is used to create new windows and regions.
47    pub compositor_state: Arc<CompositorState>,
48
49    /// The state of the subcompositor.
50    pub subcompositor_state: Option<Arc<SubcompositorState>>,
51
52    /// The seat state responsible for all sorts of input.
53    pub seat_state: SeatState,
54
55    /// The shm for software buffers, such as cursors.
56    pub shm: Shm,
57
58    /// The pool where custom cursors are allocated.
59    pub custom_cursor_pool: Arc<Mutex<SlotPool>>,
60
61    /// The XDG shell that is used for windows.
62    pub xdg_shell: XdgShell,
63
64    /// The currently present windows.
65    pub windows: RefCell<AHashMap<WindowId, Arc<Mutex<WindowState>>>>,
66
67    /// The requests from the `Window` to EventLoop, such as close operations and redraw requests.
68    pub window_requests: RefCell<AHashMap<WindowId, Arc<WindowRequests>>>,
69
70    /// The events that were generated directly from the window.
71    pub window_events_sink: Arc<Mutex<EventSink>>,
72
73    /// The update for the `windows` coming from the compositor.
74    pub window_compositor_updates: Vec<WindowCompositorUpdate>,
75
76    /// Currently handled seats.
77    pub seats: AHashMap<ObjectId, WinitSeatState>,
78
79    /// Currently present cursor surfaces.
80    pub pointer_surfaces: AHashMap<ObjectId, Arc<ThemedPointer<WinitPointerData>>>,
81
82    /// The state of the text input on the client.
83    pub text_input_state: Option<TextInputState>,
84
85    /// Observed monitors.
86    pub monitors: Arc<Mutex<Vec<MonitorHandle>>>,
87
88    /// Sink to accumulate window events from the compositor, which is latter dispatched in
89    /// event loop run.
90    pub events_sink: EventSink,
91
92    /// Xdg activation.
93    pub xdg_activation: Option<XdgActivationState>,
94
95    /// Relative pointer.
96    pub relative_pointer: Option<RelativePointerState>,
97
98    /// Pointer constraints to handle pointer locking and confining.
99    pub pointer_constraints: Option<Arc<PointerConstraintsState>>,
100
101    /// Viewporter state on the given window.
102    pub viewporter_state: Option<ViewporterState>,
103
104    /// Fractional scaling manager.
105    pub fractional_scaling_manager: Option<FractionalScalingManager>,
106
107    /// KWin blur manager.
108    pub kwin_blur_manager: Option<KWinBlurManager>,
109
110    /// Loop handle to re-register event sources, such as keyboard repeat.
111    pub loop_handle: LoopHandle<'static, Self>,
112
113    /// Whether we have dispatched events to the user thus we want to
114    /// send `AboutToWait` and normally wakeup the user.
115    pub dispatched_events: bool,
116
117    /// Whether the user initiated a wake up.
118    pub proxy_wake_up: bool,
119}
120
121impl WinitState {
122    pub fn new(
123        globals: &GlobalList,
124        queue_handle: &QueueHandle<Self>,
125        loop_handle: LoopHandle<'static, WinitState>,
126    ) -> Result<Self, OsError> {
127        let registry_state = RegistryState::new(globals);
128        let compositor_state =
129            CompositorState::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
130        let subcompositor_state = match SubcompositorState::bind(
131            compositor_state.wl_compositor().clone(),
132            globals,
133            queue_handle,
134        ) {
135            Ok(c) => Some(c),
136            Err(e) => {
137                tracing::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
138                None
139            },
140        };
141
142        let output_state = OutputState::new(globals, queue_handle);
143        let monitors = output_state.outputs().map(MonitorHandle::new).collect();
144
145        let seat_state = SeatState::new(globals, queue_handle);
146
147        let mut seats = AHashMap::default();
148        for seat in seat_state.seats() {
149            seats.insert(seat.id(), WinitSeatState::new());
150        }
151
152        let (viewporter_state, fractional_scaling_manager) =
153            if let Ok(fsm) = FractionalScalingManager::new(globals, queue_handle) {
154                (ViewporterState::new(globals, queue_handle).ok(), Some(fsm))
155            } else {
156                (None, None)
157            };
158
159        let shm = Shm::bind(globals, queue_handle).map_err(|err| os_error!(err))?;
160        let custom_cursor_pool = Arc::new(Mutex::new(SlotPool::new(2, &shm).unwrap()));
161
162        Ok(Self {
163            registry_state,
164            compositor_state: Arc::new(compositor_state),
165            subcompositor_state: subcompositor_state.map(Arc::new),
166            output_state,
167            seat_state,
168            shm,
169            custom_cursor_pool,
170
171            xdg_shell: XdgShell::bind(globals, queue_handle).map_err(|err| os_error!(err))?,
172            xdg_activation: XdgActivationState::bind(globals, queue_handle).ok(),
173
174            windows: Default::default(),
175            window_requests: Default::default(),
176            window_compositor_updates: Vec::new(),
177            window_events_sink: Default::default(),
178            viewporter_state,
179            fractional_scaling_manager,
180            kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(),
181
182            seats,
183            text_input_state: TextInputState::new(globals, queue_handle).ok(),
184
185            relative_pointer: RelativePointerState::new(globals, queue_handle).ok(),
186            pointer_constraints: PointerConstraintsState::new(globals, queue_handle)
187                .map(Arc::new)
188                .ok(),
189            pointer_surfaces: Default::default(),
190
191            monitors: Arc::new(Mutex::new(monitors)),
192            events_sink: EventSink::new(),
193            loop_handle,
194            // Make it true by default.
195            dispatched_events: true,
196            proxy_wake_up: false,
197        })
198    }
199
200    pub fn scale_factor_changed(
201        &mut self,
202        surface: &WlSurface,
203        scale_factor: f64,
204        is_legacy: bool,
205    ) {
206        // Check if the cursor surface.
207        let window_id = super::make_wid(surface);
208
209        if let Some(window) = self.windows.get_mut().get(&window_id) {
210            // Don't update the scaling factor, when legacy method is used.
211            if is_legacy && self.fractional_scaling_manager.is_some() {
212                return;
213            }
214
215            // The scale factor change is for the window.
216            let pos = if let Some(pos) = self
217                .window_compositor_updates
218                .iter()
219                .position(|update| update.window_id == window_id)
220            {
221                pos
222            } else {
223                self.window_compositor_updates.push(WindowCompositorUpdate::new(window_id));
224                self.window_compositor_updates.len() - 1
225            };
226
227            // Update the scale factor right away.
228            window.lock().unwrap().set_scale_factor(scale_factor);
229            self.window_compositor_updates[pos].scale_changed = true;
230        } else if let Some(pointer) = self.pointer_surfaces.get(&surface.id()) {
231            // Get the window, where the pointer resides right now.
232            let focused_window = match pointer.pointer().winit_data().focused_window() {
233                Some(focused_window) => focused_window,
234                None => return,
235            };
236
237            if let Some(window_state) = self.windows.get_mut().get(&focused_window) {
238                window_state.lock().unwrap().reload_cursor_style()
239            }
240        }
241    }
242
243    pub fn queue_close(updates: &mut Vec<WindowCompositorUpdate>, window_id: WindowId) {
244        let pos = if let Some(pos) = updates.iter().position(|update| update.window_id == window_id)
245        {
246            pos
247        } else {
248            updates.push(WindowCompositorUpdate::new(window_id));
249            updates.len() - 1
250        };
251
252        updates[pos].close_window = true;
253    }
254}
255
256impl ShmHandler for WinitState {
257    fn shm_state(&mut self) -> &mut Shm {
258        &mut self.shm
259    }
260}
261
262impl WindowHandler for WinitState {
263    fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, window: &Window) {
264        let window_id = super::make_wid(window.wl_surface());
265        Self::queue_close(&mut self.window_compositor_updates, window_id);
266    }
267
268    fn configure(
269        &mut self,
270        _: &Connection,
271        _: &QueueHandle<Self>,
272        window: &Window,
273        configure: WindowConfigure,
274        _serial: u32,
275    ) {
276        let window_id = super::make_wid(window.wl_surface());
277
278        let pos = if let Some(pos) =
279            self.window_compositor_updates.iter().position(|update| update.window_id == window_id)
280        {
281            pos
282        } else {
283            self.window_compositor_updates.push(WindowCompositorUpdate::new(window_id));
284            self.window_compositor_updates.len() - 1
285        };
286
287        let mut winit_window = self
288            .windows
289            .get_mut()
290            .get_mut(&window_id)
291            .expect("got configure for dead window.")
292            .lock()
293            .unwrap();
294        // Populate the configure to the window.
295
296        self.window_compositor_updates[pos].suggested_bounds |= configure.suggested_bounds
297            != winit_window.last_configure.as_ref().and_then(|last| last.suggested_bounds);
298
299        self.window_compositor_updates[pos].resized |=
300            winit_window.configure(configure, &self.shm, &self.subcompositor_state);
301
302        // NOTE: configure demands wl_surface::commit, however winit doesn't commit on behalf of the
303        // users, since it can break a lot of things, thus it'll ask users to redraw instead.
304        self.window_requests
305            .get_mut()
306            .get(&window_id)
307            .unwrap()
308            .redraw_requested
309            .store(true, Ordering::Relaxed);
310
311        // Manually mark that we've got an event, since configure may not generate a resize.
312        self.dispatched_events = true;
313    }
314}
315
316impl OutputHandler for WinitState {
317    fn output_state(&mut self) -> &mut OutputState {
318        &mut self.output_state
319    }
320
321    fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, output: WlOutput) {
322        self.monitors.lock().unwrap().push(MonitorHandle::new(output));
323    }
324
325    fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, updated: WlOutput) {
326        let mut monitors = self.monitors.lock().unwrap();
327        let updated = MonitorHandle::new(updated);
328        if let Some(pos) = monitors.iter().position(|output| output == &updated) {
329            monitors[pos] = updated
330        } else {
331            monitors.push(updated)
332        }
333    }
334
335    fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, removed: WlOutput) {
336        let mut monitors = self.monitors.lock().unwrap();
337        let removed = MonitorHandle::new(removed);
338        if let Some(pos) = monitors.iter().position(|output| output == &removed) {
339            monitors.remove(pos);
340        }
341    }
342}
343
344impl CompositorHandler for WinitState {
345    fn transform_changed(
346        &mut self,
347        _: &Connection,
348        _: &QueueHandle<Self>,
349        _: &WlSurface,
350        _: wayland_client::protocol::wl_output::Transform,
351    ) {
352        // TODO(kchibisov) we need to expose it somehow in winit.
353    }
354
355    fn surface_enter(
356        &mut self,
357        _: &Connection,
358        _: &QueueHandle<Self>,
359        _: &WlSurface,
360        _: &WlOutput,
361    ) {
362    }
363
364    fn surface_leave(
365        &mut self,
366        _: &Connection,
367        _: &QueueHandle<Self>,
368        _: &WlSurface,
369        _: &WlOutput,
370    ) {
371    }
372
373    fn scale_factor_changed(
374        &mut self,
375        _: &Connection,
376        _: &QueueHandle<Self>,
377        surface: &WlSurface,
378        scale_factor: i32,
379    ) {
380        self.scale_factor_changed(surface, scale_factor as f64, true)
381    }
382
383    fn frame(&mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, _: u32) {
384        let window_id = super::make_wid(surface);
385        let window = match self.windows.get_mut().get(&window_id) {
386            Some(window) => window,
387            None => return,
388        };
389
390        // In case we have a redraw requested we must indicate the wake up.
391        if self
392            .window_requests
393            .get_mut()
394            .get(&window_id)
395            .unwrap()
396            .redraw_requested
397            .load(Ordering::Relaxed)
398        {
399            self.dispatched_events = true;
400        }
401
402        window.lock().unwrap().frame_callback_received();
403    }
404}
405
406impl ProvidesRegistryState for WinitState {
407    sctk::registry_handlers![OutputState, SeatState];
408
409    fn registry(&mut self) -> &mut RegistryState {
410        &mut self.registry_state
411    }
412}
413
414// The window update coming from the compositor.
415#[derive(Debug, Clone, Copy)]
416pub struct WindowCompositorUpdate {
417    /// The id of the window this updates belongs to.
418    pub window_id: WindowId,
419
420    /// New window size.
421    pub resized: bool,
422
423    /// New scale factor.
424    pub scale_changed: bool,
425
426    /// Close the window.
427    pub close_window: bool,
428
429    /// New suggested bounds.
430    pub suggested_bounds: bool,
431}
432
433impl WindowCompositorUpdate {
434    fn new(window_id: WindowId) -> Self {
435        Self {
436            window_id,
437            resized: false,
438            scale_changed: false,
439            close_window: false,
440            suggested_bounds: false,
441        }
442    }
443}
444
445sctk::delegate_subcompositor!(WinitState);
446sctk::delegate_compositor!(WinitState);
447sctk::delegate_output!(WinitState);
448sctk::delegate_registry!(WinitState);
449sctk::delegate_shm!(WinitState);
450sctk::delegate_xdg_shell!(WinitState);
451sctk::delegate_xdg_window!(WinitState);