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