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