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