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