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