1 | use std::cell::RefCell; |
2 | use std::sync::atomic::Ordering; |
3 | use std::sync::{Arc, Mutex}; |
4 | |
5 | use ahash::AHashMap; |
6 | |
7 | use sctk::reexports::calloop::LoopHandle; |
8 | use sctk::reexports::client::backend::ObjectId; |
9 | use sctk::reexports::client::globals::GlobalList; |
10 | use sctk::reexports::client::protocol::wl_output::WlOutput; |
11 | use sctk::reexports::client::protocol::wl_surface::WlSurface; |
12 | use sctk::reexports::client::{Connection, Proxy, QueueHandle}; |
13 | |
14 | use sctk::compositor::{CompositorHandler, CompositorState}; |
15 | use sctk::output::{OutputHandler, OutputState}; |
16 | use sctk::registry::{ProvidesRegistryState, RegistryState}; |
17 | use sctk::seat::pointer::ThemedPointer; |
18 | use sctk::seat::SeatState; |
19 | use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler}; |
20 | use sctk::shell::xdg::XdgShell; |
21 | use sctk::shell::WaylandSurface; |
22 | use sctk::shm::slot::SlotPool; |
23 | use sctk::shm::{Shm, ShmHandler}; |
24 | use sctk::subcompositor::SubcompositorState; |
25 | |
26 | use crate::platform_impl::wayland::event_loop::sink::EventSink; |
27 | use crate::platform_impl::wayland::output::MonitorHandle; |
28 | use crate::platform_impl::wayland::seat::{ |
29 | PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData, |
30 | WinitPointerDataExt, WinitSeatState, |
31 | }; |
32 | use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; |
33 | use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager; |
34 | use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState; |
35 | use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState; |
36 | use crate::platform_impl::wayland::window::{WindowRequests, WindowState}; |
37 | use crate::platform_impl::wayland::{WaylandError, WindowId}; |
38 | use crate::platform_impl::OsError; |
39 | |
40 | /// Winit's Wayland state. |
41 | pub 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 | |
120 | impl 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 | |
254 | impl ShmHandler for WinitState { |
255 | fn shm_state(&mut self) -> &mut Shm { |
256 | &mut self.shm |
257 | } |
258 | } |
259 | |
260 | impl 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 | |
309 | impl 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 | |
337 | impl 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 | |
399 | impl 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)] |
409 | pub 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 | |
423 | impl WindowCompositorUpdate { |
424 | fn new(window_id: WindowId) -> Self { |
425 | Self { window_id, resized: false, scale_changed: false, close_window: false } |
426 | } |
427 | } |
428 | |
429 | sctk::delegate_subcompositor!(WinitState); |
430 | sctk::delegate_compositor!(WinitState); |
431 | sctk::delegate_output!(WinitState); |
432 | sctk::delegate_registry!(WinitState); |
433 | sctk::delegate_shm!(WinitState); |
434 | sctk::delegate_xdg_shell!(WinitState); |
435 | sctk::delegate_xdg_window!(WinitState); |
436 | |