1use 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, 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
246impl Drop for Window {
247 fn drop(&mut self) {
248 self.window_requests.closed.store(true, Ordering::Relaxed);
249 self.event_loop_awakener.ping();
250 }
251}
252
253#[cfg(feature = "rwh_06")]
254impl rwh_06::HasWindowHandle for Window {
255 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
256 let raw = rwh_06::WaylandWindowHandle::new({
257 let ptr = self.window.wl_surface().id().as_ptr();
258 std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
259 });
260
261 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
262 }
263}
264
265#[cfg(feature = "rwh_06")]
266impl rwh_06::HasDisplayHandle for Window {
267 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
268 let raw = rwh_06::WaylandDisplayHandle::new({
269 let ptr = self.display.id().as_ptr();
270 std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
271 });
272
273 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
274 }
275}
276
277impl CoreWindow for Window {
278 fn id(&self) -> CoreWindowId {
279 CoreWindowId(self.window_id)
280 }
281
282 fn request_redraw(&self) {
283 if self
288 .window_requests
289 .redraw_requested
290 .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
291 .is_ok()
292 {
293 self.event_loop_awakener.ping();
294 }
295 }
296
297 #[inline]
298 fn title(&self) -> String {
299 self.window_state.lock().unwrap().title().to_owned()
300 }
301
302 fn pre_present_notify(&self) {
303 self.window_state.lock().unwrap().request_frame_callback();
304 }
305
306 fn reset_dead_keys(&self) {
307 crate::platform_impl::common::xkb::reset_dead_keys()
308 }
309
310 fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
311 Err(NotSupportedError::new("window position information is not available on Wayland")
312 .into())
313 }
314
315 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
316 Err(NotSupportedError::new("window position information is not available on Wayland")
317 .into())
318 }
319
320 fn set_outer_position(&self, _position: Position) {
321 }
323
324 fn surface_size(&self) -> PhysicalSize<u32> {
325 let window_state = self.window_state.lock().unwrap();
326 let scale_factor = window_state.scale_factor();
327 super::logical_to_physical_rounded(window_state.surface_size(), scale_factor)
328 }
329
330 fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
331 let mut window_state = self.window_state.lock().unwrap();
332 let new_size = window_state.request_surface_size(size);
333 self.request_redraw();
334 Some(new_size)
335 }
336
337 fn outer_size(&self) -> PhysicalSize<u32> {
338 let window_state = self.window_state.lock().unwrap();
339 let scale_factor = window_state.scale_factor();
340 super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
341 }
342
343 fn set_min_surface_size(&self, min_size: Option<Size>) {
344 let scale_factor = self.scale_factor();
345 let min_size = min_size.map(|size| size.to_logical(scale_factor));
346 self.window_state.lock().unwrap().set_min_surface_size(min_size);
347 self.request_redraw();
349 }
350
351 #[inline]
353 fn set_max_surface_size(&self, max_size: Option<Size>) {
354 let scale_factor = self.scale_factor();
355 let max_size = max_size.map(|size| size.to_logical(scale_factor));
356 self.window_state.lock().unwrap().set_max_surface_size(max_size);
357 self.request_redraw();
359 }
360
361 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
362 None
363 }
364
365 fn set_surface_resize_increments(&self, _increments: Option<Size>) {
366 warn!("`set_surface_resize_increments` is not implemented for Wayland");
367 }
368
369 fn set_title(&self, title: &str) {
370 let new_title = title.to_string();
371 self.window_state.lock().unwrap().set_title(new_title);
372 }
373
374 #[inline]
375 fn set_transparent(&self, transparent: bool) {
376 self.window_state.lock().unwrap().set_transparent(transparent);
377 }
378
379 fn set_visible(&self, _visible: bool) {
380 }
382
383 fn is_visible(&self) -> Option<bool> {
384 None
385 }
386
387 fn set_resizable(&self, resizable: bool) {
388 if self.window_state.lock().unwrap().set_resizable(resizable) {
389 self.request_redraw();
391 }
392 }
393
394 fn is_resizable(&self) -> bool {
395 self.window_state.lock().unwrap().resizable()
396 }
397
398 fn set_enabled_buttons(&self, _buttons: WindowButtons) {
399 }
401
402 fn enabled_buttons(&self) -> WindowButtons {
403 WindowButtons::all()
405 }
406
407 fn set_minimized(&self, minimized: bool) {
408 if !minimized {
410 warn!("Unminimizing is ignored on Wayland.");
411 return;
412 }
413
414 self.window.set_minimized();
415 }
416
417 fn is_minimized(&self) -> Option<bool> {
418 None
420 }
421
422 fn set_maximized(&self, maximized: bool) {
423 if maximized {
424 self.window.set_maximized()
425 } else {
426 self.window.unset_maximized()
427 }
428 }
429
430 fn is_maximized(&self) -> bool {
431 self.window_state
432 .lock()
433 .unwrap()
434 .last_configure
435 .as_ref()
436 .map(|last_configure| last_configure.is_maximized())
437 .unwrap_or_default()
438 }
439
440 fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
441 match fullscreen {
442 Some(CoreFullscreen::Exclusive(_)) => {
443 warn!("`Fullscreen::Exclusive` is ignored on Wayland");
444 },
445 #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
446 Some(CoreFullscreen::Borderless(monitor)) => {
447 let output = monitor.and_then(|monitor| match monitor.inner {
448 PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
449 #[cfg(x11_platform)]
450 PlatformMonitorHandle::X(_) => None,
451 });
452
453 self.window.set_fullscreen(output.as_ref())
454 },
455 None => self.window.unset_fullscreen(),
456 }
457 }
458
459 fn fullscreen(&self) -> Option<CoreFullscreen> {
460 let is_fullscreen = self
461 .window_state
462 .lock()
463 .unwrap()
464 .last_configure
465 .as_ref()
466 .map(|last_configure| last_configure.is_fullscreen())
467 .unwrap_or_default();
468
469 if is_fullscreen {
470 let current_monitor = self.current_monitor();
471 Some(CoreFullscreen::Borderless(current_monitor))
472 } else {
473 None
474 }
475 }
476
477 #[inline]
478 fn scale_factor(&self) -> f64 {
479 self.window_state.lock().unwrap().scale_factor()
480 }
481
482 #[inline]
483 fn set_blur(&self, blur: bool) {
484 self.window_state.lock().unwrap().set_blur(blur);
485 }
486
487 #[inline]
488 fn set_decorations(&self, decorate: bool) {
489 self.window_state.lock().unwrap().set_decorate(decorate)
490 }
491
492 #[inline]
493 fn is_decorated(&self) -> bool {
494 self.window_state.lock().unwrap().is_decorated()
495 }
496
497 fn set_window_level(&self, _level: WindowLevel) {}
498
499 fn set_window_icon(&self, _window_icon: Option<crate::window::Icon>) {}
500
501 #[inline]
502 fn set_ime_cursor_area(&self, position: Position, size: Size) {
503 let window_state = self.window_state.lock().unwrap();
504 if window_state.ime_allowed() {
505 let scale_factor = window_state.scale_factor();
506 let position = position.to_logical(scale_factor);
507 let size = size.to_logical(scale_factor);
508 window_state.set_ime_cursor_area(position, size);
509 }
510 }
511
512 #[inline]
513 fn set_ime_allowed(&self, allowed: bool) {
514 let mut window_state = self.window_state.lock().unwrap();
515
516 if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
517 let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
518 self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
519 self.event_loop_awakener.ping();
520 }
521 }
522
523 #[inline]
524 fn set_ime_purpose(&self, purpose: ImePurpose) {
525 self.window_state.lock().unwrap().set_ime_purpose(purpose);
526 }
527
528 fn focus_window(&self) {}
529
530 fn has_focus(&self) -> bool {
531 self.window_state.lock().unwrap().has_focus()
532 }
533
534 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
535 let xdg_activation = match self.xdg_activation.as_ref() {
536 Some(xdg_activation) => xdg_activation,
537 None => {
538 warn!("`request_user_attention` isn't supported");
539 return;
540 },
541 };
542
543 if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
546 return;
547 }
548
549 self.attention_requested.store(true, Ordering::Relaxed);
550 let surface = self.surface().clone();
551 let data = XdgActivationTokenData::Attention((
552 surface.clone(),
553 Arc::downgrade(&self.attention_requested),
554 ));
555 let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
556 xdg_activation_token.set_surface(&surface);
557 xdg_activation_token.commit();
558 }
559
560 fn set_theme(&self, theme: Option<Theme>) {
561 self.window_state.lock().unwrap().set_theme(theme)
562 }
563
564 fn theme(&self) -> Option<Theme> {
565 self.window_state.lock().unwrap().theme()
566 }
567
568 fn set_content_protected(&self, _protected: bool) {}
569
570 fn set_cursor(&self, cursor: Cursor) {
571 let window_state = &mut self.window_state.lock().unwrap();
572
573 match cursor {
574 Cursor::Icon(icon) => window_state.set_cursor(icon),
575 Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
576 }
577 }
578
579 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
580 let scale_factor = self.scale_factor();
581 let position = position.to_logical(scale_factor);
582 self.window_state
583 .lock()
584 .unwrap()
585 .set_cursor_position(position)
586 .map(|_| self.request_redraw())
588 }
589
590 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
591 self.window_state.lock().unwrap().set_cursor_grab(mode)
592 }
593
594 fn set_cursor_visible(&self, visible: bool) {
595 self.window_state.lock().unwrap().set_cursor_visible(visible);
596 }
597
598 fn drag_window(&self) -> Result<(), RequestError> {
599 self.window_state.lock().unwrap().drag_window()
600 }
601
602 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
603 self.window_state.lock().unwrap().drag_resize_window(direction)
604 }
605
606 fn show_window_menu(&self, position: Position) {
607 let scale_factor = self.scale_factor();
608 let position = position.to_logical(scale_factor);
609 self.window_state.lock().unwrap().show_window_menu(position);
610 }
611
612 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
613 let surface = self.window.wl_surface();
614
615 if hittest {
616 surface.set_input_region(None);
617 Ok(())
618 } else {
619 let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
620 region.add(0, 0, 0, 0);
621 surface.set_input_region(Some(region.wl_region()));
622 Ok(())
623 }
624 }
625
626 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
627 let data = self.window.wl_surface().data::<SurfaceData>()?;
628 data.outputs()
629 .next()
630 .map(MonitorHandle::new)
631 .map(crate::platform_impl::MonitorHandle::Wayland)
632 .map(|inner| CoreMonitorHandle { inner })
633 }
634
635 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
636 Box::new(
637 self.monitors
638 .lock()
639 .unwrap()
640 .clone()
641 .into_iter()
642 .map(crate::platform_impl::MonitorHandle::Wayland)
643 .map(|inner| CoreMonitorHandle { inner }),
644 )
645 }
646
647 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
648 None
650 }
651
652 #[cfg(feature = "rwh_06")]
654 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
655 self
656 }
657
658 #[cfg(feature = "rwh_06")]
660 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
661 self
662 }
663}
664
665impl HasXdgSurfaceHandle for Window {
666 fn xdg_surface_handle(
667 &self,
668 ) -> Result<crate::platform::wayland::XdgSurfaceHandle<'_>, rwh_06::HandleError> {
669 let raw = {
670 let ptr = self.window.xdg_surface().id().as_ptr();
671 std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
672 };
673
674 unsafe { Ok(XdgSurfaceHandle::borrow_raw(raw)) }
675 }
676}
677
678#[derive(Debug)]
680pub struct WindowRequests {
681 pub closed: AtomicBool,
683
684 pub redraw_requested: AtomicBool,
686}
687
688impl WindowRequests {
689 pub fn take_closed(&self) -> bool {
690 self.closed.swap(false, Ordering::Relaxed)
691 }
692
693 pub fn take_redraw_requested(&self) -> bool {
694 self.redraw_requested.swap(false, Ordering::Relaxed)
695 }
696}
697
698impl TryFrom<&str> for Theme {
699 type Error = ();
700
701 fn try_from(theme: &str) -> Result<Self, Self::Error> {
708 if theme.eq_ignore_ascii_case("dark") {
709 Ok(Self::Dark)
710 } else if theme.eq_ignore_ascii_case("light") {
711 Ok(Self::Light)
712 } else {
713 Err(())
714 }
715 }
716}