Skip to main content

winit_wayland/window/
mod.rs

1//! The Wayland window.
2
3use 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/// The Wayland window.
40#[derive(Debug)]
41pub struct Window {
42    /// Reference to the underlying SCTK window.
43    window: SctkWindow,
44
45    /// Window id.
46    window_id: WindowId,
47
48    /// The state of the window.
49    window_state: Arc<Mutex<WindowState>>,
50
51    /// The wayland display used solely for raw window handle.
52    #[allow(dead_code)]
53    display: WlDisplay,
54
55    /// Common handles like queue, window requests, monitors and so on
56    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        // We prefer server side decorations, however to not have decorations we ask for client
78        // side decorations instead.
79        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        // Set transparency hint.
119        window_state.set_transparent(attributes.transparent);
120
121        // Set blur.
122        let _ = window_state.set_blur(attributes.blur);
123
124        // Set the decorations hint.
125        window_state.set_decorate(attributes.decorations);
126
127        // Set the app_id.
128        if let Some(name) = app_name.map(|name| name.general) {
129            window.set_app_id(name);
130        }
131
132        // Set the window title.
133        window_state.set_title(attributes.title);
134
135        // Set the min and max sizes. We must set the hints upon creating a window, so
136        // we use the default `1.` scaling...
137        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        // Non-resizable implies that the min and max sizes are set to the same value.
143        window_state.set_resizable(attributes.resizable);
144
145        // Set startup mode.
146        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        // Apply resize increments.
167        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        // Activate the window when the token is passed.
173        if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) {
174            xdg_activation.activate(token.into_raw(), &surface);
175        }
176
177        // XXX Do initial commit.
178        window.commit();
179
180        // Add the window and window requests into the state.
181        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        // Setup the event sync to insert `WindowEvents` right from the window.
193        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        // Do a roundtrip.
199        event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
200
201        // XXX Wait for the initial configure to arrive.
202        while !window_state.lock().unwrap().is_configured() {
203            event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
204        }
205
206        // Wake-up event loop, so it'll send initial redraw requested.
207        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        // Not possible.
327    }
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        // NOTE: Requires commit to be applied.
357        self.request_redraw();
358    }
359
360    /// Set the maximum surface size for the window.
361    #[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        // NOTE: Requires commit to be applied.
367        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        // Not possible on Wayland.
397    }
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            // NOTE: Requires commit to be applied.
406            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        // TODO(kchibisov) v5 of the xdg_shell allows that.
416    }
417
418    fn enabled_buttons(&self) -> WindowButtons {
419        // TODO(kchibisov) v5 of the xdg_shell allows that.
420        WindowButtons::all()
421    }
422
423    fn set_minimized(&self, minimized: bool) {
424        // You can't unminimize the window on Wayland.
425        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        // XXX clients don't know whether they are minimized or not.
435        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            // Request redraw on success, since the state is double buffered.
572            .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        // NOTE: There's no such concept on Wayland.
615        None
616    }
617
618    /// Get the raw-window-handle v0.6 display handle.
619    fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
620        self
621    }
622
623    /// Get the raw-window-handle v0.6 window handle.
624    fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
625        self
626    }
627}