winit/platform_impl/linux/wayland/window/
mod.rs1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, Mutex};
5
6use sctk::compositor::{CompositorState, Region, SurfaceData};
7use sctk::reexports::client::protocol::wl_display::WlDisplay;
8use sctk::reexports::client::protocol::wl_surface::WlSurface;
9use sctk::reexports::client::{Proxy, QueueHandle};
10use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
11use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations};
12use sctk::shell::xdg::XdgSurface;
13use sctk::shell::WaylandSurface;
14use tracing::warn;
15
16use super::event_loop::sink::EventSink;
17use super::output::MonitorHandle;
18use super::state::WinitState;
19use super::types::xdg_activation::XdgActivationTokenData;
20use super::{ActiveEventLoop, WindowId};
21use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
22use crate::error::{NotSupportedError, RequestError};
23use crate::event::{Ime, WindowEvent};
24use crate::event_loop::AsyncRequestSerial;
25use crate::monitor::MonitorHandle as CoreMonitorHandle;
26use crate::platform::wayland::{HasXdgSurfaceHandle, HasXdgToplevelHandle, XdgSurfaceHandle};
27use crate::platform_impl::{Fullscreen, MonitorHandle as PlatformMonitorHandle};
28use crate::window::{
29 Cursor, CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme,
30 UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons,
31 WindowId as CoreWindowId, WindowLevel,
32};
33
34pub(crate) mod state;
35
36pub use state::WindowState;
37
38pub struct Window {
40 window: SctkWindow,
42
43 window_id: WindowId,
45
46 window_state: Arc<Mutex<WindowState>>,
48
49 compositor: Arc<CompositorState>,
51
52 #[allow(dead_code)]
54 display: WlDisplay,
55
56 xdg_activation: Option<XdgActivationV1>,
58
59 attention_requested: Arc<AtomicBool>,
61
62 queue_handle: QueueHandle<WinitState>,
64
65 window_requests: Arc<WindowRequests>,
67
68 monitors: Arc<Mutex<Vec<MonitorHandle>>>,
70
71 event_loop_awakener: calloop::ping::Ping,
73
74 window_events_sink: Arc<Mutex<EventSink>>,
76}
77
78impl Window {
79 pub(crate) fn new(
80 event_loop_window_target: &ActiveEventLoop,
81 attributes: WindowAttributes,
82 ) -> Result<Self, RequestError> {
83 let queue_handle = event_loop_window_target.queue_handle.clone();
84 let mut state = event_loop_window_target.state.borrow_mut();
85
86 let monitors = state.monitors.clone();
87
88 let surface = state.compositor_state.create_surface(&queue_handle);
89 let compositor = state.compositor_state.clone();
90 let xdg_activation =
91 state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
92 let display = event_loop_window_target.connection.display();
93
94 let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
95
96 let default_decorations = if attributes.decorations {
99 WindowDecorations::RequestServer
100 } else {
101 WindowDecorations::RequestClient
102 };
103
104 let window =
105 state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
106
107 let mut window_state = WindowState::new(
108 event_loop_window_target.connection.clone(),
109 &event_loop_window_target.queue_handle,
110 &state,
111 size,
112 window.clone(),
113 attributes.preferred_theme,
114 );
115
116 window_state.set_transparent(attributes.transparent);
118
119 window_state.set_blur(attributes.blur);
120
121 window_state.set_decorate(attributes.decorations);
123
124 if let Some(name) = attributes.platform_specific.name.map(|name| name.general) {
126 window.set_app_id(name);
127 }
128
129 window_state.set_title(attributes.title);
131
132 let min_size = attributes.min_surface_size.map(|size| size.to_logical(1.));
135 let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
136 window_state.set_min_surface_size(min_size);
137 window_state.set_max_surface_size(max_size);
138
139 window_state.set_resizable(attributes.resizable);
141
142 match attributes.fullscreen.map(Into::into) {
144 Some(Fullscreen::Exclusive(_)) => {
145 warn!("`Fullscreen::Exclusive` is ignored on Wayland");
146 },
147 #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
148 Some(Fullscreen::Borderless(monitor)) => {
149 let output = monitor.and_then(|monitor| match monitor {
150 PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
151 #[cfg(x11_platform)]
152 PlatformMonitorHandle::X(_) => None,
153 });
154
155 window.set_fullscreen(output.as_ref())
156 },
157 _ if attributes.maximized => window.set_maximized(),
158 _ => (),
159 };
160
161 match attributes.cursor {
162 Cursor::Icon(icon) => window_state.set_cursor(icon),
163 Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
164 }
165
166 if let (Some(xdg_activation), Some(token)) =
168 (xdg_activation.as_ref(), attributes.platform_specific.activation_token)
169 {
170 xdg_activation.activate(token._token, &surface);
171 }
172
173 window.commit();
175
176 let window_state = Arc::new(Mutex::new(window_state));
178 let window_id = super::make_wid(&surface);
179 state.windows.get_mut().insert(window_id, window_state.clone());
180
181 let window_requests = WindowRequests {
182 redraw_requested: AtomicBool::new(true),
183 closed: AtomicBool::new(false),
184 };
185 let window_requests = Arc::new(window_requests);
186 state.window_requests.get_mut().insert(window_id, window_requests.clone());
187
188 let window_events_sink = state.window_events_sink.clone();
190
191 let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
192 let event_queue = wayland_source.queue();
193
194 event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
196
197 while !window_state.lock().unwrap().is_configured() {
199 event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
200 }
201
202 let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
204 event_loop_awakener.ping();
205
206 Ok(Self {
207 window,
208 display,
209 monitors,
210 window_id,
211 compositor,
212 window_state,
213 queue_handle,
214 xdg_activation,
215 attention_requested: Arc::new(AtomicBool::new(false)),
216 event_loop_awakener,
217 window_requests,
218 window_events_sink,
219 })
220 }
221}
222
223impl Window {
224 pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
225 let xdg_activation = match self.xdg_activation.as_ref() {
226 Some(xdg_activation) => xdg_activation,
227 None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
228 };
229
230 let serial = AsyncRequestSerial::get();
231
232 let data = XdgActivationTokenData::Obtain((self.window_id, serial));
233 let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
234 xdg_activation_token.set_surface(self.surface());
235 xdg_activation_token.commit();
236
237 Ok(serial)
238 }
239
240 #[inline]
241 pub fn surface(&self) -> &WlSurface {
242 self.window.wl_surface()
243 }
244
245 #[inline]
246 pub fn xdg_window_state(&self) -> Option<sctk::reexports::csd_frame::WindowState> {
247 self.window_state.lock().unwrap().last_configure.as_ref().map(|c| c.state)
248 }
249}
250
251impl Drop for Window {
252 fn drop(&mut self) {
253 self.window_requests.closed.store(true, Ordering::Relaxed);
254 self.event_loop_awakener.ping();
255 }
256}
257
258#[cfg(feature = "rwh_06")]
259impl rwh_06::HasWindowHandle for Window {
260 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
261 let raw = rwh_06::WaylandWindowHandle::new({
262 let ptr = self.window.wl_surface().id().as_ptr();
263 std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
264 });
265
266 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
267 }
268}
269
270#[cfg(feature = "rwh_06")]
271impl rwh_06::HasDisplayHandle for Window {
272 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
273 let raw = rwh_06::WaylandDisplayHandle::new({
274 let ptr = self.display.id().as_ptr();
275 std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
276 });
277
278 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
279 }
280}
281
282impl CoreWindow for Window {
283 fn id(&self) -> CoreWindowId {
284 CoreWindowId(self.window_id)
285 }
286
287 fn request_redraw(&self) {
288 if self
293 .window_requests
294 .redraw_requested
295 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
296 .is_ok()
297 {
298 self.event_loop_awakener.ping();
299 }
300 }
301
302 #[inline]
303 fn title(&self) -> String {
304 self.window_state.lock().unwrap().title().to_owned()
305 }
306
307 fn pre_present_notify(&self) {
308 self.window_state.lock().unwrap().request_frame_callback();
309 }
310
311 fn reset_dead_keys(&self) {
312 crate::platform_impl::common::xkb::reset_dead_keys()
313 }
314
315 fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
316 Err(NotSupportedError::new("window position information is not available on Wayland")
317 .into())
318 }
319
320 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
321 Err(NotSupportedError::new("window position information is not available on Wayland")
322 .into())
323 }
324
325 fn set_outer_position(&self, _position: Position) {
326 }
328
329 fn surface_size(&self) -> PhysicalSize<u32> {
330 let window_state = self.window_state.lock().unwrap();
331 let scale_factor = window_state.scale_factor();
332 super::logical_to_physical_rounded(window_state.surface_size(), scale_factor)
333 }
334
335 fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
336 let mut window_state = self.window_state.lock().unwrap();
337 let new_size = window_state.request_surface_size(size);
338 self.request_redraw();
339 Some(new_size)
340 }
341
342 fn outer_size(&self) -> PhysicalSize<u32> {
343 let window_state = self.window_state.lock().unwrap();
344 let scale_factor = window_state.scale_factor();
345 super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
346 }
347
348 fn set_min_surface_size(&self, min_size: Option<Size>) {
349 let scale_factor = self.scale_factor();
350 let min_size = min_size.map(|size| size.to_logical(scale_factor));
351 self.window_state.lock().unwrap().set_min_surface_size(min_size);
352 self.request_redraw();
354 }
355
356 #[inline]
358 fn set_max_surface_size(&self, max_size: Option<Size>) {
359 let scale_factor = self.scale_factor();
360 let max_size = max_size.map(|size| size.to_logical(scale_factor));
361 self.window_state.lock().unwrap().set_max_surface_size(max_size);
362 self.request_redraw();
364 }
365
366 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
367 None
368 }
369
370 fn set_surface_resize_increments(&self, _increments: Option<Size>) {
371 warn!("`set_surface_resize_increments` is not implemented for Wayland");
372 }
373
374 fn set_title(&self, title: &str) {
375 let new_title = title.to_string();
376 self.window_state.lock().unwrap().set_title(new_title);
377 }
378
379 #[inline]
380 fn set_transparent(&self, transparent: bool) {
381 self.window_state.lock().unwrap().set_transparent(transparent);
382 }
383
384 fn set_visible(&self, _visible: bool) {
385 }
387
388 fn is_visible(&self) -> Option<bool> {
389 None
390 }
391
392 fn set_resizable(&self, resizable: bool) {
393 if self.window_state.lock().unwrap().set_resizable(resizable) {
394 self.request_redraw();
396 }
397 }
398
399 fn is_resizable(&self) -> bool {
400 self.window_state.lock().unwrap().resizable()
401 }
402
403 fn set_enabled_buttons(&self, _buttons: WindowButtons) {
404 }
406
407 fn enabled_buttons(&self) -> WindowButtons {
408 WindowButtons::all()
410 }
411
412 fn set_minimized(&self, minimized: bool) {
413 if !minimized {
415 warn!("Unminimizing is ignored on Wayland.");
416 return;
417 }
418
419 self.window.set_minimized();
420 }
421
422 fn is_minimized(&self) -> Option<bool> {
423 None
425 }
426
427 fn set_maximized(&self, maximized: bool) {
428 if maximized {
429 self.window.set_maximized()
430 } else {
431 self.window.unset_maximized()
432 }
433 }
434
435 fn is_maximized(&self) -> bool {
436 self.window_state
437 .lock()
438 .unwrap()
439 .last_configure
440 .as_ref()
441 .map(|last_configure| last_configure.is_maximized())
442 .unwrap_or_default()
443 }
444
445 fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
446 match fullscreen {
447 Some(CoreFullscreen::Exclusive(_)) => {
448 warn!("`Fullscreen::Exclusive` is ignored on Wayland");
449 },
450 #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
451 Some(CoreFullscreen::Borderless(monitor)) => {
452 let output = monitor.and_then(|monitor| match monitor.inner {
453 PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
454 #[cfg(x11_platform)]
455 PlatformMonitorHandle::X(_) => None,
456 });
457
458 self.window.set_fullscreen(output.as_ref())
459 },
460 None => self.window.unset_fullscreen(),
461 }
462 }
463
464 fn fullscreen(&self) -> Option<CoreFullscreen> {
465 let is_fullscreen = self
466 .window_state
467 .lock()
468 .unwrap()
469 .last_configure
470 .as_ref()
471 .map(|last_configure| last_configure.is_fullscreen())
472 .unwrap_or_default();
473
474 if is_fullscreen {
475 let current_monitor = self.current_monitor();
476 Some(CoreFullscreen::Borderless(current_monitor))
477 } else {
478 None
479 }
480 }
481
482 #[inline]
483 fn scale_factor(&self) -> f64 {
484 self.window_state.lock().unwrap().scale_factor()
485 }
486
487 #[inline]
488 fn set_blur(&self, blur: bool) {
489 self.window_state.lock().unwrap().set_blur(blur);
490 }
491
492 #[inline]
493 fn set_decorations(&self, decorate: bool) {
494 self.window_state.lock().unwrap().set_decorate(decorate)
495 }
496
497 #[inline]
498 fn is_decorated(&self) -> bool {
499 self.window_state.lock().unwrap().is_decorated()
500 }
501
502 fn set_window_level(&self, _level: WindowLevel) {}
503
504 fn set_window_icon(&self, _window_icon: Option<crate::window::Icon>) {}
505
506 #[inline]
507 fn set_ime_cursor_area(&self, position: Position, size: Size) {
508 let window_state = self.window_state.lock().unwrap();
509 if window_state.ime_allowed() {
510 let scale_factor = window_state.scale_factor();
511 let position = position.to_logical(scale_factor);
512 let size = size.to_logical(scale_factor);
513 window_state.set_ime_cursor_area(position, size);
514 }
515 }
516
517 #[inline]
518 fn set_ime_allowed(&self, allowed: bool) {
519 let mut window_state = self.window_state.lock().unwrap();
520
521 if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
522 let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
523 self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
524 self.event_loop_awakener.ping();
525 }
526 }
527
528 #[inline]
529 fn set_ime_purpose(&self, purpose: ImePurpose) {
530 self.window_state.lock().unwrap().set_ime_purpose(purpose);
531 }
532
533 fn focus_window(&self) {}
534
535 fn has_focus(&self) -> bool {
536 self.window_state.lock().unwrap().has_focus()
537 }
538
539 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
540 let xdg_activation = match self.xdg_activation.as_ref() {
541 Some(xdg_activation) => xdg_activation,
542 None => {
543 warn!("`request_user_attention` isn't supported");
544 return;
545 },
546 };
547
548 if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
551 return;
552 }
553
554 self.attention_requested.store(true, Ordering::Relaxed);
555 let surface = self.surface().clone();
556 let data = XdgActivationTokenData::Attention((
557 surface.clone(),
558 Arc::downgrade(&self.attention_requested),
559 ));
560 let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
561 xdg_activation_token.set_surface(&surface);
562 xdg_activation_token.commit();
563 }
564
565 fn set_theme(&self, theme: Option<Theme>) {
566 self.window_state.lock().unwrap().set_theme(theme)
567 }
568
569 fn theme(&self) -> Option<Theme> {
570 self.window_state.lock().unwrap().theme()
571 }
572
573 fn set_content_protected(&self, _protected: bool) {}
574
575 fn set_cursor(&self, cursor: Cursor) {
576 let window_state = &mut self.window_state.lock().unwrap();
577
578 match cursor {
579 Cursor::Icon(icon) => window_state.set_cursor(icon),
580 Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
581 }
582 }
583
584 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
585 let scale_factor = self.scale_factor();
586 let position = position.to_logical(scale_factor);
587 self.window_state
588 .lock()
589 .unwrap()
590 .set_cursor_position(position)
591 .map(|_| self.request_redraw())
593 }
594
595 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
596 self.window_state.lock().unwrap().set_cursor_grab(mode)
597 }
598
599 fn set_cursor_visible(&self, visible: bool) {
600 self.window_state.lock().unwrap().set_cursor_visible(visible);
601 }
602
603 fn drag_window(&self) -> Result<(), RequestError> {
604 self.window_state.lock().unwrap().drag_window()
605 }
606
607 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
608 self.window_state.lock().unwrap().drag_resize_window(direction)
609 }
610
611 fn show_window_menu(&self, position: Position) {
612 let scale_factor = self.scale_factor();
613 let position = position.to_logical(scale_factor);
614 self.window_state.lock().unwrap().show_window_menu(position);
615 }
616
617 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
618 let surface = self.window.wl_surface();
619
620 if hittest {
621 surface.set_input_region(None);
622 Ok(())
623 } else {
624 let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
625 region.add(0, 0, 0, 0);
626 surface.set_input_region(Some(region.wl_region()));
627 Ok(())
628 }
629 }
630
631 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
632 let data = self.window.wl_surface().data::<SurfaceData>()?;
633 data.outputs()
634 .next()
635 .map(MonitorHandle::new)
636 .map(crate::platform_impl::MonitorHandle::Wayland)
637 .map(|inner| CoreMonitorHandle { inner })
638 }
639
640 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
641 Box::new(
642 self.monitors
643 .lock()
644 .unwrap()
645 .clone()
646 .into_iter()
647 .map(crate::platform_impl::MonitorHandle::Wayland)
648 .map(|inner| CoreMonitorHandle { inner }),
649 )
650 }
651
652 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
653 None
655 }
656
657 #[cfg(feature = "rwh_06")]
659 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
660 self
661 }
662
663 #[cfg(feature = "rwh_06")]
665 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
666 self
667 }
668}
669
670impl HasXdgSurfaceHandle for Window {
671 fn xdg_surface_handle(
672 &self,
673 ) -> Result<crate::platform::wayland::XdgSurfaceHandle<'_>, rwh_06::HandleError> {
674 let raw = {
675 let ptr = self.window.xdg_surface().id().as_ptr();
676 std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
677 };
678
679 unsafe { Ok(XdgSurfaceHandle::borrow_raw(raw)) }
680 }
681}
682
683impl HasXdgToplevelHandle for Window {
684 fn xdg_toplevel_handle(
685 &self,
686 ) -> Result<crate::platform::wayland::XdgToplevelHandle<'_>, rwh_06::HandleError> {
687 let raw = {
688 let ptr = self.window.xdg_toplevel().id().as_ptr();
689 std::ptr::NonNull::new(ptr as *mut _).expect("xdg_toplevel will never be null")
690 };
691
692 unsafe { Ok(crate::platform::wayland::XdgToplevelHandle::borrow_raw(raw)) }
693 }
694}
695
696#[derive(Debug)]
698pub struct WindowRequests {
699 pub closed: AtomicBool,
701
702 pub redraw_requested: AtomicBool,
704}
705
706impl WindowRequests {
707 pub fn take_closed(&self) -> bool {
708 self.closed.swap(false, Ordering::Relaxed)
709 }
710
711 pub fn take_redraw_requested(&self) -> bool {
712 self.redraw_requested.swap(false, Ordering::Relaxed)
713 }
714}
715
716impl TryFrom<&str> for Theme {
717 type Error = ();
718
719 fn try_from(theme: &str) -> Result<Self, Self::Error> {
726 if theme.eq_ignore_ascii_case("dark") {
727 Ok(Self::Dark)
728 } else if theme.eq_ignore_ascii_case("light") {
729 Ok(Self::Light)
730 } else {
731 Err(())
732 }
733 }
734}