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::reexports::client::protocol::wl_display::WlDisplay;
7use sctk::reexports::client::protocol::wl_surface::WlSurface;
8use sctk::reexports::client::{Proxy, QueueHandle};
9
10use sctk::compositor::{CompositorState, Region, SurfaceData};
11use sctk::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1;
12use sctk::shell::xdg::window::{Window as SctkWindow, WindowDecorations};
13use sctk::shell::WaylandSurface;
14
15use tracing::warn;
16
17use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
18use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
19use crate::event::{Ime, WindowEvent};
20use crate::event_loop::AsyncRequestSerial;
21use crate::platform_impl::{
22    Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
23};
24use crate::window::{
25    Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType,
26    WindowAttributes, WindowButtons, WindowLevel,
27};
28
29use super::event_loop::sink::EventSink;
30use super::output::MonitorHandle;
31use super::state::WinitState;
32use super::types::xdg_activation::XdgActivationTokenData;
33use super::{ActiveEventLoop, WaylandError, WindowId};
34
35pub(crate) mod state;
36
37pub use state::WindowState;
38
39/// The Wayland window.
40pub struct Window {
41    /// Reference to the underlying SCTK window.
42    window: SctkWindow,
43
44    /// Window id.
45    window_id: WindowId,
46
47    /// The state of the window.
48    window_state: Arc<Mutex<WindowState>>,
49
50    /// Compositor to handle WlRegion stuff.
51    compositor: Arc<CompositorState>,
52
53    /// The wayland display used solely for raw window handle.
54    #[allow(dead_code)]
55    display: WlDisplay,
56
57    /// Xdg activation to request user attention.
58    xdg_activation: Option<XdgActivationV1>,
59
60    /// The state of the requested attention from the `xdg_activation`.
61    attention_requested: Arc<AtomicBool>,
62
63    /// Handle to the main queue to perform requests.
64    queue_handle: QueueHandle<WinitState>,
65
66    /// Window requests to the event loop.
67    window_requests: Arc<WindowRequests>,
68
69    /// Observed monitors.
70    monitors: Arc<Mutex<Vec<MonitorHandle>>>,
71
72    /// Source to wake-up the event-loop for window requests.
73    event_loop_awakener: calloop::ping::Ping,
74
75    /// The event sink to deliver synthetic events.
76    window_events_sink: Arc<Mutex<EventSink>>,
77}
78
79impl Window {
80    pub(crate) fn new(
81        event_loop_window_target: &ActiveEventLoop,
82        attributes: WindowAttributes,
83    ) -> Result<Self, RootOsError> {
84        let queue_handle = event_loop_window_target.queue_handle.clone();
85        let mut state = event_loop_window_target.state.borrow_mut();
86
87        let monitors = state.monitors.clone();
88
89        let surface = state.compositor_state.create_surface(&queue_handle);
90        let compositor = state.compositor_state.clone();
91        let xdg_activation =
92            state.xdg_activation.as_ref().map(|activation_state| activation_state.global().clone());
93        let display = event_loop_window_target.connection.display();
94
95        let size: Size = attributes.inner_size.unwrap_or(LogicalSize::new(800., 600.).into());
96
97        // We prefer server side decorations, however to not have decorations we ask for client
98        // side decorations instead.
99        let default_decorations = if attributes.decorations {
100            WindowDecorations::RequestServer
101        } else {
102            WindowDecorations::RequestClient
103        };
104
105        let window =
106            state.xdg_shell.create_window(surface.clone(), default_decorations, &queue_handle);
107
108        let mut window_state = WindowState::new(
109            event_loop_window_target.connection.clone(),
110            &event_loop_window_target.queue_handle,
111            &state,
112            size,
113            window.clone(),
114            attributes.preferred_theme,
115        );
116
117        // Set transparency hint.
118        window_state.set_transparent(attributes.transparent);
119
120        window_state.set_blur(attributes.blur);
121
122        // Set the decorations hint.
123        window_state.set_decorate(attributes.decorations);
124
125        // Set the app_id.
126        if let Some(name) = attributes.platform_specific.name.map(|name| name.general) {
127            window.set_app_id(name);
128        }
129
130        // Set the window title.
131        window_state.set_title(attributes.title);
132
133        // Set the min and max sizes. We must set the hints upon creating a window, so
134        // we use the default `1.` scaling...
135        let min_size = attributes.min_inner_size.map(|size| size.to_logical(1.));
136        let max_size = attributes.max_inner_size.map(|size| size.to_logical(1.));
137        window_state.set_min_inner_size(min_size);
138        window_state.set_max_inner_size(max_size);
139
140        // Non-resizable implies that the min and max sizes are set to the same value.
141        window_state.set_resizable(attributes.resizable);
142
143        // Set startup mode.
144        match attributes.fullscreen.map(Into::into) {
145            Some(Fullscreen::Exclusive(_)) => {
146                warn!("`Fullscreen::Exclusive` is ignored on Wayland");
147            },
148            #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
149            Some(Fullscreen::Borderless(monitor)) => {
150                let output = monitor.and_then(|monitor| match monitor {
151                    PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
152                    #[cfg(x11_platform)]
153                    PlatformMonitorHandle::X(_) => None,
154                });
155
156                window.set_fullscreen(output.as_ref())
157            },
158            _ if attributes.maximized => window.set_maximized(),
159            _ => (),
160        };
161
162        match attributes.cursor {
163            Cursor::Icon(icon) => window_state.set_cursor(icon),
164            Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
165        }
166
167        // Activate the window when the token is passed.
168        if let (Some(xdg_activation), Some(token)) =
169            (xdg_activation.as_ref(), attributes.platform_specific.activation_token)
170        {
171            xdg_activation.activate(token.token, &surface);
172        }
173
174        // XXX Do initial commit.
175        window.commit();
176
177        // Add the window and window requests into the state.
178        let window_state = Arc::new(Mutex::new(window_state));
179        let window_id = super::make_wid(&surface);
180        state.windows.get_mut().insert(window_id, window_state.clone());
181
182        let window_requests = WindowRequests {
183            redraw_requested: AtomicBool::new(true),
184            closed: AtomicBool::new(false),
185        };
186        let window_requests = Arc::new(window_requests);
187        state.window_requests.get_mut().insert(window_id, window_requests.clone());
188
189        // Setup the event sync to insert `WindowEvents` right from the window.
190        let window_events_sink = state.window_events_sink.clone();
191
192        let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
193        let event_queue = wayland_source.queue();
194
195        // Do a roundtrip.
196        event_queue.roundtrip(&mut state).map_err(|error| {
197            os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
198        })?;
199
200        // XXX Wait for the initial configure to arrive.
201        while !window_state.lock().unwrap().is_configured() {
202            event_queue.blocking_dispatch(&mut state).map_err(|error| {
203                os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
204            })?;
205        }
206
207        // Wake-up event loop, so it'll send initial redraw requested.
208        let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
209        event_loop_awakener.ping();
210
211        Ok(Self {
212            window,
213            display,
214            monitors,
215            window_id,
216            compositor,
217            window_state,
218            queue_handle,
219            xdg_activation,
220            attention_requested: Arc::new(AtomicBool::new(false)),
221            event_loop_awakener,
222            window_requests,
223            window_events_sink,
224        })
225    }
226}
227
228impl Window {
229    #[inline]
230    pub fn id(&self) -> WindowId {
231        self.window_id
232    }
233
234    #[inline]
235    pub fn set_title(&self, title: impl ToString) {
236        let new_title = title.to_string();
237        self.window_state.lock().unwrap().set_title(new_title);
238    }
239
240    #[inline]
241    pub fn set_visible(&self, _visible: bool) {
242        // Not possible on Wayland.
243    }
244
245    #[inline]
246    pub fn is_visible(&self) -> Option<bool> {
247        None
248    }
249
250    #[inline]
251    pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
252        Err(NotSupportedError::new())
253    }
254
255    #[inline]
256    pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
257        Err(NotSupportedError::new())
258    }
259
260    #[inline]
261    pub fn set_outer_position(&self, _: Position) {
262        // Not possible on Wayland.
263    }
264
265    #[inline]
266    pub fn inner_size(&self) -> PhysicalSize<u32> {
267        let window_state = self.window_state.lock().unwrap();
268        let scale_factor = window_state.scale_factor();
269        super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
270    }
271
272    #[inline]
273    pub fn request_redraw(&self) {
274        // NOTE: try to not wake up the loop when the event was already scheduled and not yet
275        // processed by the loop, because if at this point the value was `true` it could only
276        // mean that the loop still haven't dispatched the value to the client and will do
277        // eventually, resetting it to `false`.
278        if self
279            .window_requests
280            .redraw_requested
281            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
282            .is_ok()
283        {
284            self.event_loop_awakener.ping();
285        }
286    }
287
288    #[inline]
289    pub fn pre_present_notify(&self) {
290        self.window_state.lock().unwrap().request_frame_callback();
291    }
292
293    #[inline]
294    pub fn outer_size(&self) -> PhysicalSize<u32> {
295        let window_state = self.window_state.lock().unwrap();
296        let scale_factor = window_state.scale_factor();
297        super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
298    }
299
300    #[inline]
301    pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
302        let mut window_state = self.window_state.lock().unwrap();
303        let new_size = window_state.request_inner_size(size);
304        self.request_redraw();
305        Some(new_size)
306    }
307
308    /// Set the minimum inner size for the window.
309    #[inline]
310    pub fn set_min_inner_size(&self, min_size: Option<Size>) {
311        let scale_factor = self.scale_factor();
312        let min_size = min_size.map(|size| size.to_logical(scale_factor));
313        self.window_state.lock().unwrap().set_min_inner_size(min_size);
314        // NOTE: Requires commit to be applied.
315        self.request_redraw();
316    }
317
318    /// Set the maximum inner size for the window.
319    #[inline]
320    pub fn set_max_inner_size(&self, max_size: Option<Size>) {
321        let scale_factor = self.scale_factor();
322        let max_size = max_size.map(|size| size.to_logical(scale_factor));
323        self.window_state.lock().unwrap().set_max_inner_size(max_size);
324        // NOTE: Requires commit to be applied.
325        self.request_redraw();
326    }
327
328    #[inline]
329    pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
330        None
331    }
332
333    #[inline]
334    pub fn set_resize_increments(&self, _increments: Option<Size>) {
335        warn!("`set_resize_increments` is not implemented for Wayland");
336    }
337
338    #[inline]
339    pub fn set_transparent(&self, transparent: bool) {
340        self.window_state.lock().unwrap().set_transparent(transparent);
341    }
342
343    #[inline]
344    pub fn has_focus(&self) -> bool {
345        self.window_state.lock().unwrap().has_focus()
346    }
347
348    #[inline]
349    pub fn is_minimized(&self) -> Option<bool> {
350        // XXX clients don't know whether they are minimized or not.
351        None
352    }
353
354    #[inline]
355    pub fn show_window_menu(&self, position: Position) {
356        let scale_factor = self.scale_factor();
357        let position = position.to_logical(scale_factor);
358        self.window_state.lock().unwrap().show_window_menu(position);
359    }
360
361    #[inline]
362    pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
363        self.window_state.lock().unwrap().drag_resize_window(direction)
364    }
365
366    #[inline]
367    pub fn set_resizable(&self, resizable: bool) {
368        if self.window_state.lock().unwrap().set_resizable(resizable) {
369            // NOTE: Requires commit to be applied.
370            self.request_redraw();
371        }
372    }
373
374    #[inline]
375    pub fn is_resizable(&self) -> bool {
376        self.window_state.lock().unwrap().resizable()
377    }
378
379    #[inline]
380    pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {
381        // TODO(kchibisov) v5 of the xdg_shell allows that.
382    }
383
384    #[inline]
385    pub fn enabled_buttons(&self) -> WindowButtons {
386        // TODO(kchibisov) v5 of the xdg_shell allows that.
387        WindowButtons::all()
388    }
389
390    #[inline]
391    pub fn scale_factor(&self) -> f64 {
392        self.window_state.lock().unwrap().scale_factor()
393    }
394
395    #[inline]
396    pub fn set_blur(&self, blur: bool) {
397        self.window_state.lock().unwrap().set_blur(blur);
398    }
399
400    #[inline]
401    pub fn set_decorations(&self, decorate: bool) {
402        self.window_state.lock().unwrap().set_decorate(decorate)
403    }
404
405    #[inline]
406    pub fn is_decorated(&self) -> bool {
407        self.window_state.lock().unwrap().is_decorated()
408    }
409
410    #[inline]
411    pub fn set_window_level(&self, _level: WindowLevel) {}
412
413    #[inline]
414    pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {}
415
416    #[inline]
417    pub fn set_minimized(&self, minimized: bool) {
418        // You can't unminimize the window on Wayland.
419        if !minimized {
420            warn!("Unminimizing is ignored on Wayland.");
421            return;
422        }
423
424        self.window.set_minimized();
425    }
426
427    #[inline]
428    pub fn is_maximized(&self) -> bool {
429        self.window_state
430            .lock()
431            .unwrap()
432            .last_configure
433            .as_ref()
434            .map(|last_configure| last_configure.is_maximized())
435            .unwrap_or_default()
436    }
437
438    #[inline]
439    pub fn set_maximized(&self, maximized: bool) {
440        if maximized {
441            self.window.set_maximized()
442        } else {
443            self.window.unset_maximized()
444        }
445    }
446
447    #[inline]
448    pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
449        let is_fullscreen = self
450            .window_state
451            .lock()
452            .unwrap()
453            .last_configure
454            .as_ref()
455            .map(|last_configure| last_configure.is_fullscreen())
456            .unwrap_or_default();
457
458        if is_fullscreen {
459            let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland);
460            Some(Fullscreen::Borderless(current_monitor))
461        } else {
462            None
463        }
464    }
465
466    #[inline]
467    pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
468        match fullscreen {
469            Some(Fullscreen::Exclusive(_)) => {
470                warn!("`Fullscreen::Exclusive` is ignored on Wayland");
471            },
472            #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
473            Some(Fullscreen::Borderless(monitor)) => {
474                let output = monitor.and_then(|monitor| match monitor {
475                    PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
476                    #[cfg(x11_platform)]
477                    PlatformMonitorHandle::X(_) => None,
478                });
479
480                self.window.set_fullscreen(output.as_ref())
481            },
482            None => self.window.unset_fullscreen(),
483        }
484    }
485
486    #[inline]
487    pub fn set_cursor(&self, cursor: Cursor) {
488        let window_state = &mut self.window_state.lock().unwrap();
489
490        match cursor {
491            Cursor::Icon(icon) => window_state.set_cursor(icon),
492            Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
493        }
494    }
495
496    #[inline]
497    pub fn set_cursor_visible(&self, visible: bool) {
498        self.window_state.lock().unwrap().set_cursor_visible(visible);
499    }
500
501    pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
502        let xdg_activation = match self.xdg_activation.as_ref() {
503            Some(xdg_activation) => xdg_activation,
504            None => {
505                warn!("`request_user_attention` isn't supported");
506                return;
507            },
508        };
509
510        // Urgency is only removed by the compositor and there's no need to raise urgency when it
511        // was already raised.
512        if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
513            return;
514        }
515
516        self.attention_requested.store(true, Ordering::Relaxed);
517        let surface = self.surface().clone();
518        let data = XdgActivationTokenData::Attention((
519            surface.clone(),
520            Arc::downgrade(&self.attention_requested),
521        ));
522        let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
523        xdg_activation_token.set_surface(&surface);
524        xdg_activation_token.commit();
525    }
526
527    pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
528        let xdg_activation = match self.xdg_activation.as_ref() {
529            Some(xdg_activation) => xdg_activation,
530            None => return Err(NotSupportedError::new()),
531        };
532
533        let serial = AsyncRequestSerial::get();
534
535        let data = XdgActivationTokenData::Obtain((self.window_id, serial));
536        let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
537        xdg_activation_token.set_surface(self.surface());
538        xdg_activation_token.commit();
539
540        Ok(serial)
541    }
542
543    #[inline]
544    pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
545        self.window_state.lock().unwrap().set_cursor_grab(mode)
546    }
547
548    #[inline]
549    pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
550        let scale_factor = self.scale_factor();
551        let position = position.to_logical(scale_factor);
552        self.window_state
553            .lock()
554            .unwrap()
555            .set_cursor_position(position)
556            // Request redraw on success, since the state is double buffered.
557            .map(|_| self.request_redraw())
558    }
559
560    #[inline]
561    pub fn drag_window(&self) -> Result<(), ExternalError> {
562        self.window_state.lock().unwrap().drag_window()
563    }
564
565    #[inline]
566    pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
567        let surface = self.window.wl_surface();
568
569        if hittest {
570            surface.set_input_region(None);
571            Ok(())
572        } else {
573            let region = Region::new(&*self.compositor).map_err(|_| {
574                ExternalError::Os(os_error!(OsError::Misc("failed to set input region.")))
575            })?;
576            region.add(0, 0, 0, 0);
577            surface.set_input_region(Some(region.wl_region()));
578            Ok(())
579        }
580    }
581
582    #[inline]
583    pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
584        let window_state = self.window_state.lock().unwrap();
585        if window_state.ime_allowed() {
586            let scale_factor = window_state.scale_factor();
587            let position = position.to_logical(scale_factor);
588            let size = size.to_logical(scale_factor);
589            window_state.set_ime_cursor_area(position, size);
590        }
591    }
592
593    #[inline]
594    pub fn set_ime_allowed(&self, allowed: bool) {
595        let mut window_state = self.window_state.lock().unwrap();
596
597        if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
598            let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
599            self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
600            self.event_loop_awakener.ping();
601        }
602    }
603
604    #[inline]
605    pub fn set_ime_purpose(&self, purpose: ImePurpose) {
606        self.window_state.lock().unwrap().set_ime_purpose(purpose);
607    }
608
609    #[inline]
610    pub fn focus_window(&self) {}
611
612    #[inline]
613    pub fn surface(&self) -> &WlSurface {
614        self.window.wl_surface()
615    }
616
617    #[inline]
618    pub fn current_monitor(&self) -> Option<MonitorHandle> {
619        let data = self.window.wl_surface().data::<SurfaceData>()?;
620        data.outputs().next().map(MonitorHandle::new)
621    }
622
623    #[inline]
624    pub fn available_monitors(&self) -> Vec<MonitorHandle> {
625        self.monitors.lock().unwrap().clone()
626    }
627
628    #[inline]
629    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
630        // XXX there's no such concept on Wayland.
631        None
632    }
633
634    #[cfg(feature = "rwh_04")]
635    #[inline]
636    pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
637        let mut window_handle = rwh_04::WaylandHandle::empty();
638        window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
639        window_handle.display = self.display.id().as_ptr() as *mut _;
640        rwh_04::RawWindowHandle::Wayland(window_handle)
641    }
642
643    #[cfg(feature = "rwh_05")]
644    #[inline]
645    pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
646        let mut window_handle = rwh_05::WaylandWindowHandle::empty();
647        window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
648        rwh_05::RawWindowHandle::Wayland(window_handle)
649    }
650
651    #[cfg(feature = "rwh_05")]
652    #[inline]
653    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
654        let mut display_handle = rwh_05::WaylandDisplayHandle::empty();
655        display_handle.display = self.display.id().as_ptr() as *mut _;
656        rwh_05::RawDisplayHandle::Wayland(display_handle)
657    }
658
659    #[cfg(feature = "rwh_06")]
660    #[inline]
661    pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
662        Ok(rwh_06::WaylandWindowHandle::new({
663            let ptr = self.window.wl_surface().id().as_ptr();
664            std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
665        })
666        .into())
667    }
668
669    #[cfg(feature = "rwh_06")]
670    #[inline]
671    pub fn raw_display_handle_rwh_06(
672        &self,
673    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
674        Ok(rwh_06::WaylandDisplayHandle::new({
675            let ptr = self.display.id().as_ptr();
676            std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
677        })
678        .into())
679    }
680
681    #[inline]
682    pub fn set_theme(&self, theme: Option<Theme>) {
683        self.window_state.lock().unwrap().set_theme(theme)
684    }
685
686    #[inline]
687    pub fn theme(&self) -> Option<Theme> {
688        self.window_state.lock().unwrap().theme()
689    }
690
691    pub fn set_content_protected(&self, _protected: bool) {}
692
693    #[inline]
694    pub fn title(&self) -> String {
695        self.window_state.lock().unwrap().title().to_owned()
696    }
697}
698
699impl Drop for Window {
700    fn drop(&mut self) {
701        self.window_requests.closed.store(true, Ordering::Relaxed);
702        self.event_loop_awakener.ping();
703    }
704}
705
706/// The request from the window to the event loop.
707#[derive(Debug)]
708pub struct WindowRequests {
709    /// The window was closed.
710    pub closed: AtomicBool,
711
712    /// Redraw Requested.
713    pub redraw_requested: AtomicBool,
714}
715
716impl WindowRequests {
717    pub fn take_closed(&self) -> bool {
718        self.closed.swap(false, Ordering::Relaxed)
719    }
720
721    pub fn take_redraw_requested(&self) -> bool {
722        self.redraw_requested.swap(false, Ordering::Relaxed)
723    }
724}
725
726impl TryFrom<&str> for Theme {
727    type Error = ();
728
729    /// ```
730    /// use winit::window::Theme;
731    ///
732    /// assert_eq!("dark".try_into(), Ok(Theme::Dark));
733    /// assert_eq!("lIghT".try_into(), Ok(Theme::Light));
734    /// ```
735    fn try_from(theme: &str) -> Result<Self, Self::Error> {
736        if theme.eq_ignore_ascii_case("dark") {
737            Ok(Self::Dark)
738        } else if theme.eq_ignore_ascii_case("light") {
739            Ok(Self::Light)
740        } else {
741            Err(())
742        }
743    }
744}