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 rwh_06::RawWindowHandle;
10use sctk::compositor::SurfaceData;
11use sctk::reexports::client::Proxy;
12use sctk::reexports::client::protocol::wl_display::WlDisplay;
13use sctk::reexports::client::protocol::wl_surface::WlSurface;
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::output::MonitorHandle;
30use super::types::xdg_activation::XdgActivationTokenData;
31use crate::window::state::WindowType;
32use crate::{WindowAttributesWayland, output};
33pub(crate) mod state;
34pub use state::WindowState;
35pub(crate) mod handles;
36pub use handles::Handles;
37use handles::WindowRequests;
38
39#[derive(Debug)]
41pub struct Window {
42 window: SctkWindow,
44
45 window_id: WindowId,
47
48 window_state: Arc<Mutex<WindowState>>,
50
51 #[allow(dead_code)]
53 display: WlDisplay,
54
55 handles: Handles,
57}
58
59impl Window {
60 pub(crate) fn new(
61 event_loop_window_target: &ActiveEventLoop,
62 mut attributes: WindowAttributes,
63 ) -> Result<Self, RequestError> {
64 let queue_handle = event_loop_window_target.queue_handle.clone();
65 let mut state = event_loop_window_target.state.borrow_mut();
66
67 let monitors = state.monitors.clone();
68
69 let surface = state.compositor_state.create_surface(&queue_handle);
70 let compositor = state.compositor_state.clone();
71 let xdg_activation =
72 state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
73 let display = event_loop_window_target.handle.connection.display();
74
75 let size: Size = attributes.surface_size.unwrap_or(LogicalSize::new(800., 600.).into());
76
77 let default_decorations = if attributes.decorations {
80 WindowDecorations::RequestServer
81 } else {
82 WindowDecorations::RequestClient
83 };
84
85 let window =
86 state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
87
88 let WindowAttributesWayland { name: app_name, activation_token, prefer_csd, .. } =
89 *attributes
90 .platform
91 .take()
92 .and_then(|p| p.cast::<WindowAttributesWayland>().ok())
93 .unwrap_or_default();
94
95 let mut scale_factor = None;
96 if let Some(RawWindowHandle::Wayland(handle)) = attributes.parent_window() {
97 if let Some(s) =
98 state.windows.borrow().get(&WindowId::from_raw(handle.surface.as_ptr() as usize))
99 {
100 scale_factor = Some(s.lock().unwrap().scale_factor());
101 }
102 }
103 let scale_factor = scale_factor.unwrap_or(1.0);
104
105 let mut window_state = WindowState::new(
106 event_loop_window_target,
107 &state,
108 size,
109 state::WindowType::Window { window: window.clone(), last_configure: None },
110 attributes.preferred_theme,
111 prefer_csd,
112 scale_factor,
113 None,
114 );
115
116 window_state.set_window_icon(attributes.window_icon);
117
118 window_state.set_transparent(attributes.transparent);
120
121 let _ = window_state.set_blur(attributes.blur);
123
124 window_state.set_decorate(attributes.decorations);
126
127 if let Some(name) = app_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_surface_size.map(|size| size.to_logical(1.));
138 let max_size = attributes.max_surface_size.map(|size| size.to_logical(1.));
139 window_state.set_min_surface_size(min_size);
140 window_state.set_max_surface_size(max_size);
141
142 window_state.set_resizable(attributes.resizable);
144
145 match attributes.fullscreen {
147 Some(Fullscreen::Exclusive(..)) => {
148 warn!("`Fullscreen::Exclusive` is ignored on Wayland");
149 },
150 Some(Fullscreen::Borderless(monitor)) => {
151 let output = monitor.as_ref().and_then(|monitor| {
152 monitor.cast_ref::<output::MonitorHandle>().map(|handle| &handle.proxy)
153 });
154
155 window.set_fullscreen(output)
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(increments) = attributes.surface_resize_increments {
168 let increments = increments.to_logical(window_state.scale_factor());
169 window_state.set_resize_increments(Some(increments));
170 }
171
172 if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) {
174 xdg_activation.activate(token.into_raw(), &surface);
175 }
176
177 window.commit();
179
180 let window_state = Arc::new(Mutex::new(window_state));
182 let window_id = super::make_wid(&surface);
183 state.windows.get_mut().insert(window_id, window_state.clone());
184
185 let window_requests = WindowRequests {
186 redraw_requested: AtomicBool::new(true),
187 closed: AtomicBool::new(false),
188 };
189 let window_requests = Arc::new(window_requests);
190 state.window_requests.get_mut().insert(window_id, window_requests.clone());
191
192 let window_events_sink = state.window_events_sink.clone();
194
195 let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
196 let event_queue = wayland_source.queue();
197
198 event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
200
201 while !window_state.lock().unwrap().is_configured() {
203 event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
204 }
205
206 let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
208 event_loop_awakener.ping();
209
210 Ok(Self {
211 window,
212 display,
213
214 window_id,
215 window_state,
216
217 handles: Handles {
218 queue_handle,
219 window_requests,
220 monitors,
221 event_loop_awakener,
222 window_events_sink,
223
224 compositor,
225
226 xdg_activation,
227 attention_requested: Arc::new(AtomicBool::new(false)),
228 },
229 })
230 }
231
232 pub(crate) fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
233 NonNull::new(self.window.xdg_toplevel().id().as_ptr().cast())
234 }
235}
236
237impl Window {
238 pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, RequestError> {
239 let xdg_activation = match self.handles.xdg_activation.as_ref() {
240 Some(xdg_activation) => xdg_activation,
241 None => return Err(NotSupportedError::new("xdg_activation_v1 is not available").into()),
242 };
243
244 let serial = AsyncRequestSerial::get();
245
246 let data = XdgActivationTokenData::Obtain((self.window_id, serial));
247 let xdg_activation_token =
248 xdg_activation.get_activation_token(&self.handles.queue_handle, data);
249 xdg_activation_token.set_surface(self.surface());
250 xdg_activation_token.commit();
251
252 Ok(serial)
253 }
254
255 #[inline]
256 pub fn surface(&self) -> &WlSurface {
257 self.window.wl_surface()
258 }
259}
260
261impl Drop for Window {
262 fn drop(&mut self) {
263 self.handles.window_requests.closed.store(true, Ordering::Relaxed);
264 self.handles.event_loop_awakener.ping();
265 }
266}
267
268impl rwh_06::HasWindowHandle for Window {
269 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
270 let raw = rwh_06::WaylandWindowHandle::new({
271 let ptr = self.window.wl_surface().id().as_ptr();
272 std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
273 });
274
275 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
276 }
277}
278
279impl rwh_06::HasDisplayHandle for Window {
280 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
281 let raw = rwh_06::WaylandDisplayHandle::new({
282 let ptr = self.display.id().as_ptr();
283 std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
284 });
285
286 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
287 }
288}
289
290impl CoreWindow for Window {
291 fn window_type(&self) -> winit_core::window::WindowType {
292 winit_core::window::WindowType::Window
293 }
294
295 fn id(&self) -> WindowId {
296 self.window_id
297 }
298
299 fn request_redraw(&self) {
300 self.handles.request_redraw();
301 }
302
303 #[inline]
304 fn title(&self) -> String {
305 self.window_state.lock().unwrap().title().to_owned()
306 }
307
308 fn pre_present_notify(&self) {
309 self.window_state.lock().unwrap().request_frame_callback();
310 }
311
312 fn reset_dead_keys(&self) {
313 winit_common::xkb::reset_dead_keys()
314 }
315
316 fn surface_position(&self) -> PhysicalPosition<i32> {
317 (0, 0).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 safe_area(&self) -> PhysicalInsets<u32> {
349 PhysicalInsets::new(0, 0, 0, 0)
350 }
351
352 fn set_min_surface_size(&self, min_size: Option<Size>) {
353 let scale_factor = self.scale_factor();
354 let min_size = min_size.map(|size| size.to_logical(scale_factor));
355 self.window_state.lock().unwrap().set_min_surface_size(min_size);
356 self.request_redraw();
358 }
359
360 #[inline]
362 fn set_max_surface_size(&self, max_size: Option<Size>) {
363 let scale_factor = self.scale_factor();
364 let max_size = max_size.map(|size| size.to_logical(scale_factor));
365 self.window_state.lock().unwrap().set_max_surface_size(max_size);
366 self.request_redraw();
368 }
369
370 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
371 let window_state = self.window_state.lock().unwrap();
372 let scale_factor = window_state.scale_factor();
373 window_state
374 .resize_increments()
375 .map(|size| super::logical_to_physical_rounded(size, scale_factor))
376 }
377
378 fn set_surface_resize_increments(&self, increments: Option<Size>) {
379 let mut window_state = self.window_state.lock().unwrap();
380 let scale_factor = window_state.scale_factor();
381 let increments = increments.map(|size| size.to_logical(scale_factor));
382 window_state.set_resize_increments(increments);
383 }
384
385 fn set_title(&self, title: &str) {
386 let new_title = title.to_string();
387 self.window_state.lock().unwrap().set_title(new_title);
388 }
389
390 #[inline]
391 fn set_transparent(&self, transparent: bool) {
392 self.window_state.lock().unwrap().set_transparent(transparent);
393 }
394
395 fn set_visible(&self, _visible: bool) {
396 }
398
399 fn is_visible(&self) -> Option<bool> {
400 None
401 }
402
403 fn set_resizable(&self, resizable: bool) {
404 if self.window_state.lock().unwrap().set_resizable(resizable) {
405 self.request_redraw();
407 }
408 }
409
410 fn is_resizable(&self) -> bool {
411 self.window_state.lock().unwrap().resizable()
412 }
413
414 fn set_enabled_buttons(&self, _buttons: WindowButtons) {
415 }
417
418 fn enabled_buttons(&self) -> WindowButtons {
419 WindowButtons::all()
421 }
422
423 fn set_minimized(&self, minimized: bool) {
424 if !minimized {
426 warn!("Unminimizing is ignored on Wayland.");
427 return;
428 }
429
430 self.window.set_minimized();
431 }
432
433 fn is_minimized(&self) -> Option<bool> {
434 None
436 }
437
438 fn set_maximized(&self, maximized: bool) {
439 if maximized { self.window.set_maximized() } else { self.window.unset_maximized() }
440 }
441
442 fn is_maximized(&self) -> bool {
443 if let WindowType::Window { last_configure, .. } = &self.window_state.lock().unwrap().window
444 {
445 last_configure
446 .as_ref()
447 .map(|last_configure| last_configure.is_maximized())
448 .unwrap_or_default()
449 } else {
450 false
451 }
452 }
453
454 fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
455 match fullscreen {
456 Some(Fullscreen::Borderless(monitor)) => {
457 let output = monitor.as_ref().and_then(|monitor| {
458 monitor.cast_ref::<output::MonitorHandle>().map(|handle| &handle.proxy)
459 });
460
461 self.window.set_fullscreen(output)
462 },
463 Some(_) => {
464 warn!("this fullscreen mode is ignored on Wayland");
465 },
466 None => self.window.unset_fullscreen(),
467 }
468 }
469
470 fn fullscreen(&self) -> Option<Fullscreen> {
471 let is_fullscreen = if let WindowType::Window { last_configure, .. } =
472 &self.window_state.lock().unwrap().window
473 {
474 last_configure
475 .as_ref()
476 .map(|last_configure| last_configure.is_fullscreen())
477 .unwrap_or_default()
478 } else {
479 false
480 };
481
482 if is_fullscreen {
483 let current_monitor = self.current_monitor();
484 Some(Fullscreen::Borderless(current_monitor))
485 } else {
486 None
487 }
488 }
489
490 #[inline]
491 fn scale_factor(&self) -> f64 {
492 self.window_state.lock().unwrap().scale_factor()
493 }
494
495 #[inline]
496 fn set_blur(&self, blur: bool) {
497 if self.window_state.lock().unwrap().set_blur(blur) {
498 self.request_redraw();
499 }
500 }
501
502 #[inline]
503 fn set_decorations(&self, decorate: bool) {
504 self.window_state.lock().unwrap().set_decorate(decorate)
505 }
506
507 #[inline]
508 fn is_decorated(&self) -> bool {
509 self.window_state.lock().unwrap().is_decorated()
510 }
511
512 fn set_window_level(&self, _level: WindowLevel) {}
513
514 fn set_window_icon(&self, window_icon: Option<winit_core::icon::Icon>) {
515 self.window_state.lock().unwrap().set_window_icon(window_icon)
516 }
517
518 #[inline]
519 fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
520 let state_changed = self.window_state.lock().unwrap().request_ime_update(request)?;
521
522 if let Some(allowed) = state_changed {
523 let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
524 self.handles.push_window_event(event, self.window_id);
525 }
526
527 Ok(())
528 }
529
530 #[inline]
531 fn ime_capabilities(&self) -> Option<ImeCapabilities> {
532 self.window_state.lock().unwrap().ime_allowed()
533 }
534
535 fn focus_window(&self) {}
536
537 fn has_focus(&self) -> bool {
538 self.window_state.lock().unwrap().has_focus()
539 }
540
541 fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
542 self.handles.request_user_attention(self.surface(), request_type);
543 }
544
545 fn set_theme(&self, theme: Option<Theme>) {
546 self.window_state.lock().unwrap().set_theme(theme)
547 }
548
549 fn theme(&self) -> Option<Theme> {
550 self.window_state.lock().unwrap().theme()
551 }
552
553 fn set_content_protected(&self, _protected: bool) {}
554
555 fn set_cursor(&self, cursor: Cursor) {
556 let window_state = &mut self.window_state.lock().unwrap();
557
558 match cursor {
559 Cursor::Icon(icon) => window_state.set_cursor(icon),
560 Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
561 }
562 }
563
564 fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
565 let scale_factor = self.scale_factor();
566 let position = position.to_logical(scale_factor);
567 self.window_state
568 .lock()
569 .unwrap()
570 .set_cursor_position(position)
571 .map(|_| self.request_redraw())
573 }
574
575 fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
576 self.window_state.lock().unwrap().set_cursor_grab(mode)
577 }
578
579 fn set_cursor_visible(&self, visible: bool) {
580 self.window_state.lock().unwrap().set_cursor_visible(visible);
581 }
582
583 fn drag_window(&self) -> Result<(), RequestError> {
584 self.window_state.lock().unwrap().drag_window()
585 }
586
587 fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
588 self.window_state.lock().unwrap().drag_resize_window(direction)
589 }
590
591 fn show_window_menu(&self, position: Position) {
592 let scale_factor = self.scale_factor();
593 let position = position.to_logical(scale_factor);
594 self.window_state.lock().unwrap().show_window_menu(position);
595 }
596
597 fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
598 self.handles.set_cursor_hittest(self.surface(), hittest)
599 }
600
601 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
602 let data = self.window.wl_surface().data::<SurfaceData<()>>()?;
603 data.outputs()
604 .next()
605 .map(MonitorHandle::new)
606 .map(|monitor| CoreMonitorHandle(Arc::new(monitor)))
607 }
608
609 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
610 self.handles.available_monitors()
611 }
612
613 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
614 None
616 }
617
618 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
620 self
621 }
622
623 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
625 self
626 }
627}