| 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 | |