winit/platform_impl/linux/wayland/window/
mod.rs

1//! The Wayland window.
2
3use 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, HasXdgToplevelHandle, 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
38/// The Wayland window.
39pub struct Window {
40    /// Reference to the underlying SCTK window.
41    window: SctkWindow,
42
43    /// Window id.
44    window_id: WindowId,
45
46    /// The state of the window.
47    window_state: Arc<Mutex<WindowState>>,
48
49    /// Compositor to handle WlRegion stuff.
50    compositor: Arc<CompositorState>,
51
52    /// The wayland display used solely for raw window handle.
53    #[allow(dead_code)]
54    display: WlDisplay,
55
56    /// Xdg activation to request user attention.
57    xdg_activation: Option<XdgActivationV1>,
58
59    /// The state of the requested attention from the `xdg_activation`.
60    attention_requested: Arc<AtomicBool>,
61
62    /// Handle to the main queue to perform requests.
63    queue_handle: QueueHandle<WinitState>,
64
65    /// Window requests to the event loop.
66    window_requests: Arc<WindowRequests>,
67
68    /// Observed monitors.
69    monitors: Arc<Mutex<Vec<MonitorHandle>>>,
70
71    /// Source to wake-up the event-loop for window requests.
72    event_loop_awakener: calloop::ping::Ping,
73
74    /// The event sink to deliver synthetic events.
75    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        // We prefer server side decorations, however to not have decorations we ask for client
97        // side decorations instead.
98        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        // Set transparency hint.
117        window_state.set_transparent(attributes.transparent);
118
119        window_state.set_blur(attributes.blur);
120
121        // Set the decorations hint.
122        window_state.set_decorate(attributes.decorations);
123
124        // Set the app_id.
125        if let Some(name) = attributes.platform_specific.name.map(|name| name.general) {
126            window.set_app_id(name);
127        }
128
129        // Set the window title.
130        window_state.set_title(attributes.title);
131
132        // Set the min and max sizes. We must set the hints upon creating a window, so
133        // we use the default `1.` scaling...
134        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        // Non-resizable implies that the min and max sizes are set to the same value.
140        window_state.set_resizable(attributes.resizable);
141
142        // Set startup mode.
143        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        // Activate the window when the token is passed.
167        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        // XXX Do initial commit.
174        window.commit();
175
176        // Add the window and window requests into the state.
177        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        // Setup the event sync to insert `WindowEvents` right from the window.
189        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        // Do a roundtrip.
195        event_queue.roundtrip(&mut state).map_err(|err| os_error!(err))?;
196
197        // XXX Wait for the initial configure to arrive.
198        while !window_state.lock().unwrap().is_configured() {
199            event_queue.blocking_dispatch(&mut state).map_err(|err| os_error!(err))?;
200        }
201
202        // Wake-up event loop, so it'll send initial redraw requested.
203        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    #[inline]
246    pub fn xdg_window_state(&self) -> Option<sctk::reexports::csd_frame::WindowState> {
247        self.window_state.lock().unwrap().last_configure.as_ref().map(|c| c.state)
248    }
249}
250
251impl Drop for Window {
252    fn drop(&mut self) {
253        self.window_requests.closed.store(true, Ordering::Relaxed);
254        self.event_loop_awakener.ping();
255    }
256}
257
258#[cfg(feature = "rwh_06")]
259impl rwh_06::HasWindowHandle for Window {
260    fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
261        let raw = rwh_06::WaylandWindowHandle::new({
262            let ptr = self.window.wl_surface().id().as_ptr();
263            std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
264        });
265
266        unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw.into())) }
267    }
268}
269
270#[cfg(feature = "rwh_06")]
271impl rwh_06::HasDisplayHandle for Window {
272    fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
273        let raw = rwh_06::WaylandDisplayHandle::new({
274            let ptr = self.display.id().as_ptr();
275            std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
276        });
277
278        unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw.into())) }
279    }
280}
281
282impl CoreWindow for Window {
283    fn id(&self) -> CoreWindowId {
284        CoreWindowId(self.window_id)
285    }
286
287    fn request_redraw(&self) {
288        // NOTE: try to not wake up the loop when the event was already scheduled and not yet
289        // processed by the loop, because if at this point the value was `true` it could only
290        // mean that the loop still haven't dispatched the value to the client and will do
291        // eventually, resetting it to `false`.
292        if self
293            .window_requests
294            .redraw_requested
295            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
296            .is_ok()
297        {
298            self.event_loop_awakener.ping();
299        }
300    }
301
302    #[inline]
303    fn title(&self) -> String {
304        self.window_state.lock().unwrap().title().to_owned()
305    }
306
307    fn pre_present_notify(&self) {
308        self.window_state.lock().unwrap().request_frame_callback();
309    }
310
311    fn reset_dead_keys(&self) {
312        crate::platform_impl::common::xkb::reset_dead_keys()
313    }
314
315    fn inner_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
316        Err(NotSupportedError::new("window position information is not available on Wayland")
317            .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 set_min_surface_size(&self, min_size: Option<Size>) {
349        let scale_factor = self.scale_factor();
350        let min_size = min_size.map(|size| size.to_logical(scale_factor));
351        self.window_state.lock().unwrap().set_min_surface_size(min_size);
352        // NOTE: Requires commit to be applied.
353        self.request_redraw();
354    }
355
356    /// Set the maximum surface size for the window.
357    #[inline]
358    fn set_max_surface_size(&self, max_size: Option<Size>) {
359        let scale_factor = self.scale_factor();
360        let max_size = max_size.map(|size| size.to_logical(scale_factor));
361        self.window_state.lock().unwrap().set_max_surface_size(max_size);
362        // NOTE: Requires commit to be applied.
363        self.request_redraw();
364    }
365
366    fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
367        None
368    }
369
370    fn set_surface_resize_increments(&self, _increments: Option<Size>) {
371        warn!("`set_surface_resize_increments` is not implemented for Wayland");
372    }
373
374    fn set_title(&self, title: &str) {
375        let new_title = title.to_string();
376        self.window_state.lock().unwrap().set_title(new_title);
377    }
378
379    #[inline]
380    fn set_transparent(&self, transparent: bool) {
381        self.window_state.lock().unwrap().set_transparent(transparent);
382    }
383
384    fn set_visible(&self, _visible: bool) {
385        // Not possible on Wayland.
386    }
387
388    fn is_visible(&self) -> Option<bool> {
389        None
390    }
391
392    fn set_resizable(&self, resizable: bool) {
393        if self.window_state.lock().unwrap().set_resizable(resizable) {
394            // NOTE: Requires commit to be applied.
395            self.request_redraw();
396        }
397    }
398
399    fn is_resizable(&self) -> bool {
400        self.window_state.lock().unwrap().resizable()
401    }
402
403    fn set_enabled_buttons(&self, _buttons: WindowButtons) {
404        // TODO(kchibisov) v5 of the xdg_shell allows that.
405    }
406
407    fn enabled_buttons(&self) -> WindowButtons {
408        // TODO(kchibisov) v5 of the xdg_shell allows that.
409        WindowButtons::all()
410    }
411
412    fn set_minimized(&self, minimized: bool) {
413        // You can't unminimize the window on Wayland.
414        if !minimized {
415            warn!("Unminimizing is ignored on Wayland.");
416            return;
417        }
418
419        self.window.set_minimized();
420    }
421
422    fn is_minimized(&self) -> Option<bool> {
423        // XXX clients don't know whether they are minimized or not.
424        None
425    }
426
427    fn set_maximized(&self, maximized: bool) {
428        if maximized {
429            self.window.set_maximized()
430        } else {
431            self.window.unset_maximized()
432        }
433    }
434
435    fn is_maximized(&self) -> bool {
436        self.window_state
437            .lock()
438            .unwrap()
439            .last_configure
440            .as_ref()
441            .map(|last_configure| last_configure.is_maximized())
442            .unwrap_or_default()
443    }
444
445    fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
446        match fullscreen {
447            Some(CoreFullscreen::Exclusive(_)) => {
448                warn!("`Fullscreen::Exclusive` is ignored on Wayland");
449            },
450            #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
451            Some(CoreFullscreen::Borderless(monitor)) => {
452                let output = monitor.and_then(|monitor| match monitor.inner {
453                    PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
454                    #[cfg(x11_platform)]
455                    PlatformMonitorHandle::X(_) => None,
456                });
457
458                self.window.set_fullscreen(output.as_ref())
459            },
460            None => self.window.unset_fullscreen(),
461        }
462    }
463
464    fn fullscreen(&self) -> Option<CoreFullscreen> {
465        let is_fullscreen = self
466            .window_state
467            .lock()
468            .unwrap()
469            .last_configure
470            .as_ref()
471            .map(|last_configure| last_configure.is_fullscreen())
472            .unwrap_or_default();
473
474        if is_fullscreen {
475            let current_monitor = self.current_monitor();
476            Some(CoreFullscreen::Borderless(current_monitor))
477        } else {
478            None
479        }
480    }
481
482    #[inline]
483    fn scale_factor(&self) -> f64 {
484        self.window_state.lock().unwrap().scale_factor()
485    }
486
487    #[inline]
488    fn set_blur(&self, blur: bool) {
489        self.window_state.lock().unwrap().set_blur(blur);
490    }
491
492    #[inline]
493    fn set_decorations(&self, decorate: bool) {
494        self.window_state.lock().unwrap().set_decorate(decorate)
495    }
496
497    #[inline]
498    fn is_decorated(&self) -> bool {
499        self.window_state.lock().unwrap().is_decorated()
500    }
501
502    fn set_window_level(&self, _level: WindowLevel) {}
503
504    fn set_window_icon(&self, _window_icon: Option<crate::window::Icon>) {}
505
506    #[inline]
507    fn set_ime_cursor_area(&self, position: Position, size: Size) {
508        let window_state = self.window_state.lock().unwrap();
509        if window_state.ime_allowed() {
510            let scale_factor = window_state.scale_factor();
511            let position = position.to_logical(scale_factor);
512            let size = size.to_logical(scale_factor);
513            window_state.set_ime_cursor_area(position, size);
514        }
515    }
516
517    #[inline]
518    fn set_ime_allowed(&self, allowed: bool) {
519        let mut window_state = self.window_state.lock().unwrap();
520
521        if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
522            let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
523            self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
524            self.event_loop_awakener.ping();
525        }
526    }
527
528    #[inline]
529    fn set_ime_purpose(&self, purpose: ImePurpose) {
530        self.window_state.lock().unwrap().set_ime_purpose(purpose);
531    }
532
533    fn focus_window(&self) {}
534
535    fn has_focus(&self) -> bool {
536        self.window_state.lock().unwrap().has_focus()
537    }
538
539    fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
540        let xdg_activation = match self.xdg_activation.as_ref() {
541            Some(xdg_activation) => xdg_activation,
542            None => {
543                warn!("`request_user_attention` isn't supported");
544                return;
545            },
546        };
547
548        // Urgency is only removed by the compositor and there's no need to raise urgency when it
549        // was already raised.
550        if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
551            return;
552        }
553
554        self.attention_requested.store(true, Ordering::Relaxed);
555        let surface = self.surface().clone();
556        let data = XdgActivationTokenData::Attention((
557            surface.clone(),
558            Arc::downgrade(&self.attention_requested),
559        ));
560        let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
561        xdg_activation_token.set_surface(&surface);
562        xdg_activation_token.commit();
563    }
564
565    fn set_theme(&self, theme: Option<Theme>) {
566        self.window_state.lock().unwrap().set_theme(theme)
567    }
568
569    fn theme(&self) -> Option<Theme> {
570        self.window_state.lock().unwrap().theme()
571    }
572
573    fn set_content_protected(&self, _protected: bool) {}
574
575    fn set_cursor(&self, cursor: Cursor) {
576        let window_state = &mut self.window_state.lock().unwrap();
577
578        match cursor {
579            Cursor::Icon(icon) => window_state.set_cursor(icon),
580            Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
581        }
582    }
583
584    fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
585        let scale_factor = self.scale_factor();
586        let position = position.to_logical(scale_factor);
587        self.window_state
588            .lock()
589            .unwrap()
590            .set_cursor_position(position)
591            // Request redraw on success, since the state is double buffered.
592            .map(|_| self.request_redraw())
593    }
594
595    fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
596        self.window_state.lock().unwrap().set_cursor_grab(mode)
597    }
598
599    fn set_cursor_visible(&self, visible: bool) {
600        self.window_state.lock().unwrap().set_cursor_visible(visible);
601    }
602
603    fn drag_window(&self) -> Result<(), RequestError> {
604        self.window_state.lock().unwrap().drag_window()
605    }
606
607    fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
608        self.window_state.lock().unwrap().drag_resize_window(direction)
609    }
610
611    fn show_window_menu(&self, position: Position) {
612        let scale_factor = self.scale_factor();
613        let position = position.to_logical(scale_factor);
614        self.window_state.lock().unwrap().show_window_menu(position);
615    }
616
617    fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
618        let surface = self.window.wl_surface();
619
620        if hittest {
621            surface.set_input_region(None);
622            Ok(())
623        } else {
624            let region = Region::new(&*self.compositor).map_err(|err| os_error!(err))?;
625            region.add(0, 0, 0, 0);
626            surface.set_input_region(Some(region.wl_region()));
627            Ok(())
628        }
629    }
630
631    fn current_monitor(&self) -> Option<CoreMonitorHandle> {
632        let data = self.window.wl_surface().data::<SurfaceData>()?;
633        data.outputs()
634            .next()
635            .map(MonitorHandle::new)
636            .map(crate::platform_impl::MonitorHandle::Wayland)
637            .map(|inner| CoreMonitorHandle { inner })
638    }
639
640    fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
641        Box::new(
642            self.monitors
643                .lock()
644                .unwrap()
645                .clone()
646                .into_iter()
647                .map(crate::platform_impl::MonitorHandle::Wayland)
648                .map(|inner| CoreMonitorHandle { inner }),
649        )
650    }
651
652    fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
653        // NOTE: There's no such concept on Wayland.
654        None
655    }
656
657    /// Get the raw-window-handle v0.6 display handle.
658    #[cfg(feature = "rwh_06")]
659    fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
660        self
661    }
662
663    /// Get the raw-window-handle v0.6 window handle.
664    #[cfg(feature = "rwh_06")]
665    fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
666        self
667    }
668}
669
670impl HasXdgSurfaceHandle for Window {
671    fn xdg_surface_handle(
672        &self,
673    ) -> Result<crate::platform::wayland::XdgSurfaceHandle<'_>, rwh_06::HandleError> {
674        let raw = {
675            let ptr = self.window.xdg_surface().id().as_ptr();
676            std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
677        };
678
679        unsafe { Ok(XdgSurfaceHandle::borrow_raw(raw)) }
680    }
681}
682
683impl HasXdgToplevelHandle for Window {
684    fn xdg_toplevel_handle(
685        &self,
686    ) -> Result<crate::platform::wayland::XdgToplevelHandle<'_>, rwh_06::HandleError> {
687        let raw = {
688            let ptr = self.window.xdg_toplevel().id().as_ptr();
689            std::ptr::NonNull::new(ptr as *mut _).expect("xdg_toplevel will never be null")
690        };
691
692        unsafe { Ok(crate::platform::wayland::XdgToplevelHandle::borrow_raw(raw)) }
693    }
694}
695
696/// The request from the window to the event loop.
697#[derive(Debug)]
698pub struct WindowRequests {
699    /// The window was closed.
700    pub closed: AtomicBool,
701
702    /// Redraw Requested.
703    pub redraw_requested: AtomicBool,
704}
705
706impl WindowRequests {
707    pub fn take_closed(&self) -> bool {
708        self.closed.swap(false, Ordering::Relaxed)
709    }
710
711    pub fn take_redraw_requested(&self) -> bool {
712        self.redraw_requested.swap(false, Ordering::Relaxed)
713    }
714}
715
716impl TryFrom<&str> for Theme {
717    type Error = ();
718
719    /// ```
720    /// use winit::window::Theme;
721    ///
722    /// assert_eq!("dark".try_into(), Ok(Theme::Dark));
723    /// assert_eq!("lIghT".try_into(), Ok(Theme::Light));
724    /// ```
725    fn try_from(theme: &str) -> Result<Self, Self::Error> {
726        if theme.eq_ignore_ascii_case("dark") {
727            Ok(Self::Dark)
728        } else if theme.eq_ignore_ascii_case("light") {
729            Ok(Self::Light)
730        } else {
731            Err(())
732        }
733    }
734}