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