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 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/// 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    /// Compositor to handle WlRegion stuff.
52    compositor: Arc<CompositorState>,
53
54    /// The wayland display used solely for raw window handle.
55    #[allow(dead_code)]
56    display: WlDisplay,
57
58    /// Xdg activation to request user attention.
59    xdg_activation: Option<XdgActivationV1>,
60
61    /// The state of the requested attention from the `xdg_activation`.
62    attention_requested: Arc<AtomicBool>,
63
64    /// Handle to the main queue to perform requests.
65    queue_handle: QueueHandle<WinitState>,
66
67    /// Window requests to the event loop.
68    window_requests: Arc<WindowRequests>,
69
70    /// Observed monitors.
71    monitors: Arc<Mutex<Vec<MonitorHandle>>>,
72
73    /// Source to wake-up the event-loop for window requests.
74    event_loop_awakener: calloop::ping::Ping,
75
76    /// The event sink to deliver synthetic events.
77    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        // We prefer server side decorations, however to not have decorations we ask for client
99        // side decorations instead.
100        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        // Set transparency hint.
128        window_state.set_transparent(attributes.transparent);
129
130        window_state.set_blur(attributes.blur);
131
132        // Set the decorations hint.
133        window_state.set_decorate(attributes.decorations);
134
135        // Set the app_id.
136        if let Some(name) = app_name.map(|name| name.general) {
137            window.set_app_id(name);
138        }
139
140        // Set the window title.
141        window_state.set_title(attributes.title);
142
143        // Set the min and max sizes. We must set the hints upon creating a window, so
144        // we use the default `1.` scaling...
145        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        // Non-resizable implies that the min and max sizes are set to the same value.
151        window_state.set_resizable(attributes.resizable);
152
153        // Set startup mode.
154        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        // Activate the window when the token is passed.
175        if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) {
176            xdg_activation.activate(token.into_raw(), &surface);
177        }
178
179        // XXX Do initial commit.
180        window.commit();
181
182        // Add the window and window requests into the state.
183        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        // Setup the event sync to insert `WindowEvents` right from the window.
195        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        // Do a roundtrip.
201        event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
202
203        // XXX Wait for the initial configure to arrive.
204        while !window_state.lock().unwrap().is_configured() {
205            event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
206        }
207
208        // Wake-up event loop, so it'll send initial redraw requested.
209        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        // NOTE: try to not wake up the loop when the event was already scheduled and not yet
292        // processed by the loop, because if at this point the value was `true` it could only
293        // mean that the loop still haven't dispatched the value to the client and will do
294        // eventually, resetting it to `false`.
295        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        // Not possible.
329    }
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        // NOTE: Requires commit to be applied.
359        self.request_redraw();
360    }
361
362    /// Set the maximum surface size for the window.
363    #[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        // NOTE: Requires commit to be applied.
369        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        // Not possible on Wayland.
392    }
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            // NOTE: Requires commit to be applied.
401            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        // TODO(kchibisov) v5 of the xdg_shell allows that.
411    }
412
413    fn enabled_buttons(&self) -> WindowButtons {
414        // TODO(kchibisov) v5 of the xdg_shell allows that.
415        WindowButtons::all()
416    }
417
418    fn set_minimized(&self, minimized: bool) {
419        // You can't unminimize the window on Wayland.
420        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        // XXX clients don't know whether they are minimized or not.
430        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        // Urgency is only removed by the compositor and there's no need to raise urgency when it
541        // was already raised.
542        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            // Request redraw on success, since the state is double buffered.
584            .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        // NOTE: There's no such concept on Wayland.
644        None
645    }
646
647    /// Get the raw-window-handle v0.6 display handle.
648    fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
649        self
650    }
651
652    /// Get the raw-window-handle v0.6 window handle.
653    fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
654        self
655    }
656}
657
658/// The request from the window to the event loop.
659#[derive(Debug)]
660pub struct WindowRequests {
661    /// The window was closed.
662    pub closed: AtomicBool,
663
664    /// Redraw Requested.
665    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}