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        // Apply resize increments.
170        if let Some(increments) = attributes.resize_increments {
171            let increments = increments.to_logical(window_state.scale_factor());
172            window_state.set_resize_increments(Some(increments));
173        }
174
175        // Activate the window when the token is passed.
176        if let (Some(xdg_activation), Some(token)) =
177            (xdg_activation.as_ref(), attributes.platform_specific.activation_token)
178        {
179            xdg_activation.activate(token.token, &surface);
180        }
181
182        // XXX Do initial commit.
183        window.commit();
184
185        // Add the window and window requests into the state.
186        let window_state = Arc::new(Mutex::new(window_state));
187        let window_id = super::make_wid(&surface);
188        state.windows.get_mut().insert(window_id, window_state.clone());
189
190        let window_requests = WindowRequests {
191            redraw_requested: AtomicBool::new(true),
192            closed: AtomicBool::new(false),
193        };
194        let window_requests = Arc::new(window_requests);
195        state.window_requests.get_mut().insert(window_id, window_requests.clone());
196
197        // Setup the event sync to insert `WindowEvents` right from the window.
198        let window_events_sink = state.window_events_sink.clone();
199
200        let mut wayland_source = event_loop_window_target.wayland_dispatcher.as_source_mut();
201        let event_queue = wayland_source.queue();
202
203        // Do a roundtrip.
204        event_queue.roundtrip(&mut state).map_err(|error| {
205            os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
206        })?;
207
208        // XXX Wait for the initial configure to arrive.
209        while !window_state.lock().unwrap().is_configured() {
210            event_queue.blocking_dispatch(&mut state).map_err(|error| {
211                os_error!(OsError::WaylandError(Arc::new(WaylandError::Dispatch(error))))
212            })?;
213        }
214
215        // Wake-up event loop, so it'll send initial redraw requested.
216        let event_loop_awakener = event_loop_window_target.event_loop_awakener.clone();
217        event_loop_awakener.ping();
218
219        Ok(Self {
220            window,
221            display,
222            monitors,
223            window_id,
224            compositor,
225            window_state,
226            queue_handle,
227            xdg_activation,
228            attention_requested: Arc::new(AtomicBool::new(false)),
229            event_loop_awakener,
230            window_requests,
231            window_events_sink,
232        })
233    }
234
235    pub(crate) fn xdg_toplevel(&self) -> Option<NonNull<c_void>> {
236        NonNull::new(self.window.xdg_toplevel().id().as_ptr().cast())
237    }
238}
239
240impl Window {
241    #[inline]
242    pub fn id(&self) -> WindowId {
243        self.window_id
244    }
245
246    #[inline]
247    pub fn set_title(&self, title: impl ToString) {
248        let new_title = title.to_string();
249        self.window_state.lock().unwrap().set_title(new_title);
250    }
251
252    #[inline]
253    pub fn set_visible(&self, _visible: bool) {
254        // Not possible on Wayland.
255    }
256
257    #[inline]
258    pub fn is_visible(&self) -> Option<bool> {
259        None
260    }
261
262    #[inline]
263    pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
264        Err(NotSupportedError::new())
265    }
266
267    #[inline]
268    pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
269        Err(NotSupportedError::new())
270    }
271
272    #[inline]
273    pub fn set_outer_position(&self, _: Position) {
274        // Not possible on Wayland.
275    }
276
277    #[inline]
278    pub fn inner_size(&self) -> PhysicalSize<u32> {
279        let window_state = self.window_state.lock().unwrap();
280        let scale_factor = window_state.scale_factor();
281        super::logical_to_physical_rounded(window_state.inner_size(), scale_factor)
282    }
283
284    #[inline]
285    pub fn request_redraw(&self) {
286        // NOTE: try to not wake up the loop when the event was already scheduled and not yet
287        // processed by the loop, because if at this point the value was `true` it could only
288        // mean that the loop still haven't dispatched the value to the client and will do
289        // eventually, resetting it to `false`.
290        if self
291            .window_requests
292            .redraw_requested
293            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
294            .is_ok()
295        {
296            self.event_loop_awakener.ping();
297        }
298    }
299
300    #[inline]
301    pub fn pre_present_notify(&self) {
302        self.window_state.lock().unwrap().request_frame_callback();
303    }
304
305    #[inline]
306    pub fn outer_size(&self) -> PhysicalSize<u32> {
307        let window_state = self.window_state.lock().unwrap();
308        let scale_factor = window_state.scale_factor();
309        super::logical_to_physical_rounded(window_state.outer_size(), scale_factor)
310    }
311
312    #[inline]
313    pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
314        let mut window_state = self.window_state.lock().unwrap();
315        let new_size = window_state.request_inner_size(size);
316        self.request_redraw();
317        Some(new_size)
318    }
319
320    /// Set the minimum inner size for the window.
321    #[inline]
322    pub fn set_min_inner_size(&self, min_size: Option<Size>) {
323        let scale_factor = self.scale_factor();
324        let min_size = min_size.map(|size| size.to_logical(scale_factor));
325        self.window_state.lock().unwrap().set_min_inner_size(min_size);
326        // NOTE: Requires commit to be applied.
327        self.request_redraw();
328    }
329
330    /// Set the maximum inner size for the window.
331    #[inline]
332    pub fn set_max_inner_size(&self, max_size: Option<Size>) {
333        let scale_factor = self.scale_factor();
334        let max_size = max_size.map(|size| size.to_logical(scale_factor));
335        self.window_state.lock().unwrap().set_max_inner_size(max_size);
336        // NOTE: Requires commit to be applied.
337        self.request_redraw();
338    }
339
340    #[inline]
341    pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
342        let window_state = self.window_state.lock().unwrap();
343        let scale_factor = window_state.scale_factor();
344        window_state
345            .resize_increments()
346            .map(|size| super::logical_to_physical_rounded(size, scale_factor))
347    }
348
349    #[inline]
350    pub fn set_resize_increments(&self, increments: Option<Size>) {
351        let mut window_state = self.window_state.lock().unwrap();
352        let scale_factor = window_state.scale_factor();
353        let increments = increments.map(|size| size.to_logical(scale_factor));
354        window_state.set_resize_increments(increments);
355    }
356
357    #[inline]
358    pub fn set_transparent(&self, transparent: bool) {
359        self.window_state.lock().unwrap().set_transparent(transparent);
360    }
361
362    #[inline]
363    pub fn has_focus(&self) -> bool {
364        self.window_state.lock().unwrap().has_focus()
365    }
366
367    #[inline]
368    pub fn is_minimized(&self) -> Option<bool> {
369        // XXX clients don't know whether they are minimized or not.
370        None
371    }
372
373    #[inline]
374    pub fn show_window_menu(&self, position: Position) {
375        let scale_factor = self.scale_factor();
376        let position = position.to_logical(scale_factor);
377        self.window_state.lock().unwrap().show_window_menu(position);
378    }
379
380    #[inline]
381    pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
382        self.window_state.lock().unwrap().drag_resize_window(direction)
383    }
384
385    #[inline]
386    pub fn set_resizable(&self, resizable: bool) {
387        if self.window_state.lock().unwrap().set_resizable(resizable) {
388            // NOTE: Requires commit to be applied.
389            self.request_redraw();
390        }
391    }
392
393    #[inline]
394    pub fn is_resizable(&self) -> bool {
395        self.window_state.lock().unwrap().resizable()
396    }
397
398    #[inline]
399    pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {
400        // TODO(kchibisov) v5 of the xdg_shell allows that.
401    }
402
403    #[inline]
404    pub fn enabled_buttons(&self) -> WindowButtons {
405        // TODO(kchibisov) v5 of the xdg_shell allows that.
406        WindowButtons::all()
407    }
408
409    #[inline]
410    pub fn scale_factor(&self) -> f64 {
411        self.window_state.lock().unwrap().scale_factor()
412    }
413
414    #[inline]
415    pub fn set_blur(&self, blur: bool) {
416        self.window_state.lock().unwrap().set_blur(blur);
417    }
418
419    #[inline]
420    pub fn set_decorations(&self, decorate: bool) {
421        self.window_state.lock().unwrap().set_decorate(decorate)
422    }
423
424    #[inline]
425    pub fn is_decorated(&self) -> bool {
426        self.window_state.lock().unwrap().is_decorated()
427    }
428
429    #[inline]
430    pub fn set_window_level(&self, _level: WindowLevel) {}
431
432    #[inline]
433    pub(crate) fn set_window_icon(&self, _window_icon: Option<PlatformIcon>) {}
434
435    #[inline]
436    pub fn set_minimized(&self, minimized: bool) {
437        // You can't unminimize the window on Wayland.
438        if !minimized {
439            warn!("Unminimizing is ignored on Wayland.");
440            return;
441        }
442
443        self.window.set_minimized();
444    }
445
446    #[inline]
447    pub fn is_maximized(&self) -> bool {
448        self.window_state
449            .lock()
450            .unwrap()
451            .last_configure
452            .as_ref()
453            .map(|last_configure| last_configure.is_maximized())
454            .unwrap_or_default()
455    }
456
457    #[inline]
458    pub fn set_maximized(&self, maximized: bool) {
459        if maximized {
460            self.window.set_maximized()
461        } else {
462            self.window.unset_maximized()
463        }
464    }
465
466    #[inline]
467    pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
468        let is_fullscreen = self
469            .window_state
470            .lock()
471            .unwrap()
472            .last_configure
473            .as_ref()
474            .map(|last_configure| last_configure.is_fullscreen())
475            .unwrap_or_default();
476
477        if is_fullscreen {
478            let current_monitor = self.current_monitor().map(PlatformMonitorHandle::Wayland);
479            Some(Fullscreen::Borderless(current_monitor))
480        } else {
481            None
482        }
483    }
484
485    #[inline]
486    pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
487        match fullscreen {
488            Some(Fullscreen::Exclusive(_)) => {
489                warn!("`Fullscreen::Exclusive` is ignored on Wayland");
490            },
491            #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))]
492            Some(Fullscreen::Borderless(monitor)) => {
493                let output = monitor.and_then(|monitor| match monitor {
494                    PlatformMonitorHandle::Wayland(monitor) => Some(monitor.proxy),
495                    #[cfg(x11_platform)]
496                    PlatformMonitorHandle::X(_) => None,
497                });
498
499                self.window.set_fullscreen(output.as_ref())
500            },
501            None => self.window.unset_fullscreen(),
502        }
503    }
504
505    #[inline]
506    pub fn set_cursor(&self, cursor: Cursor) {
507        let window_state = &mut self.window_state.lock().unwrap();
508
509        match cursor {
510            Cursor::Icon(icon) => window_state.set_cursor(icon),
511            Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
512        }
513    }
514
515    #[inline]
516    pub fn set_cursor_visible(&self, visible: bool) {
517        self.window_state.lock().unwrap().set_cursor_visible(visible);
518    }
519
520    pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
521        let xdg_activation = match self.xdg_activation.as_ref() {
522            Some(xdg_activation) => xdg_activation,
523            None => {
524                warn!("`request_user_attention` isn't supported");
525                return;
526            },
527        };
528
529        // Urgency is only removed by the compositor and there's no need to raise urgency when it
530        // was already raised.
531        if request_type.is_none() || self.attention_requested.load(Ordering::Relaxed) {
532            return;
533        }
534
535        self.attention_requested.store(true, Ordering::Relaxed);
536        let surface = self.surface().clone();
537        let data = XdgActivationTokenData::Attention((
538            surface.clone(),
539            Arc::downgrade(&self.attention_requested),
540        ));
541        let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
542        xdg_activation_token.set_surface(&surface);
543        xdg_activation_token.commit();
544    }
545
546    pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
547        let xdg_activation = match self.xdg_activation.as_ref() {
548            Some(xdg_activation) => xdg_activation,
549            None => return Err(NotSupportedError::new()),
550        };
551
552        let serial = AsyncRequestSerial::get();
553
554        let data = XdgActivationTokenData::Obtain((self.window_id, serial));
555        let xdg_activation_token = xdg_activation.get_activation_token(&self.queue_handle, data);
556        xdg_activation_token.set_surface(self.surface());
557        xdg_activation_token.commit();
558
559        Ok(serial)
560    }
561
562    #[inline]
563    pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
564        self.window_state.lock().unwrap().set_cursor_grab(mode)
565    }
566
567    #[inline]
568    pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
569        let scale_factor = self.scale_factor();
570        let position = position.to_logical(scale_factor);
571        self.window_state
572            .lock()
573            .unwrap()
574            .set_cursor_position(position)
575            // Request redraw on success, since the state is double buffered.
576            .map(|_| self.request_redraw())
577    }
578
579    #[inline]
580    pub fn drag_window(&self) -> Result<(), ExternalError> {
581        self.window_state.lock().unwrap().drag_window()
582    }
583
584    #[inline]
585    pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
586        let surface = self.window.wl_surface();
587
588        if hittest {
589            surface.set_input_region(None);
590            Ok(())
591        } else {
592            let region = Region::new(&*self.compositor).map_err(|_| {
593                ExternalError::Os(os_error!(OsError::Misc("failed to set input region.")))
594            })?;
595            region.add(0, 0, 0, 0);
596            surface.set_input_region(Some(region.wl_region()));
597            Ok(())
598        }
599    }
600
601    #[inline]
602    pub fn set_ime_cursor_area(&self, position: Position, size: Size) {
603        let window_state = self.window_state.lock().unwrap();
604        if window_state.ime_allowed() {
605            let scale_factor = window_state.scale_factor();
606            let position = position.to_logical(scale_factor);
607            let size = size.to_logical(scale_factor);
608            window_state.set_ime_cursor_area(position, size);
609        }
610    }
611
612    #[inline]
613    pub fn set_ime_allowed(&self, allowed: bool) {
614        let mut window_state = self.window_state.lock().unwrap();
615
616        if window_state.ime_allowed() != allowed && window_state.set_ime_allowed(allowed) {
617            let event = WindowEvent::Ime(if allowed { Ime::Enabled } else { Ime::Disabled });
618            self.window_events_sink.lock().unwrap().push_window_event(event, self.window_id);
619            self.event_loop_awakener.ping();
620        }
621    }
622
623    #[inline]
624    pub fn set_ime_purpose(&self, purpose: ImePurpose) {
625        self.window_state.lock().unwrap().set_ime_purpose(purpose);
626    }
627
628    #[inline]
629    pub fn focus_window(&self) {}
630
631    #[inline]
632    pub fn surface(&self) -> &WlSurface {
633        self.window.wl_surface()
634    }
635
636    #[inline]
637    pub fn current_monitor(&self) -> Option<MonitorHandle> {
638        let data = self.window.wl_surface().data::<SurfaceData>()?;
639        data.outputs().next().map(MonitorHandle::new)
640    }
641
642    #[inline]
643    pub fn available_monitors(&self) -> Vec<MonitorHandle> {
644        self.monitors.lock().unwrap().clone()
645    }
646
647    #[inline]
648    pub fn primary_monitor(&self) -> Option<MonitorHandle> {
649        // XXX there's no such concept on Wayland.
650        None
651    }
652
653    #[cfg(feature = "rwh_04")]
654    #[inline]
655    pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
656        let mut window_handle = rwh_04::WaylandHandle::empty();
657        window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
658        window_handle.display = self.display.id().as_ptr() as *mut _;
659        rwh_04::RawWindowHandle::Wayland(window_handle)
660    }
661
662    #[cfg(feature = "rwh_05")]
663    #[inline]
664    pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
665        let mut window_handle = rwh_05::WaylandWindowHandle::empty();
666        window_handle.surface = self.window.wl_surface().id().as_ptr() as *mut _;
667        rwh_05::RawWindowHandle::Wayland(window_handle)
668    }
669
670    #[cfg(feature = "rwh_05")]
671    #[inline]
672    pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
673        let mut display_handle = rwh_05::WaylandDisplayHandle::empty();
674        display_handle.display = self.display.id().as_ptr() as *mut _;
675        rwh_05::RawDisplayHandle::Wayland(display_handle)
676    }
677
678    #[cfg(feature = "rwh_06")]
679    #[inline]
680    pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
681        Ok(rwh_06::WaylandWindowHandle::new({
682            let ptr = self.window.wl_surface().id().as_ptr();
683            std::ptr::NonNull::new(ptr as *mut _).expect("wl_surface will never be null")
684        })
685        .into())
686    }
687
688    #[cfg(feature = "rwh_06")]
689    #[inline]
690    pub fn raw_display_handle_rwh_06(
691        &self,
692    ) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
693        Ok(rwh_06::WaylandDisplayHandle::new({
694            let ptr = self.display.id().as_ptr();
695            std::ptr::NonNull::new(ptr as *mut _).expect("wl_proxy should never be null")
696        })
697        .into())
698    }
699
700    #[inline]
701    pub fn set_theme(&self, theme: Option<Theme>) {
702        self.window_state.lock().unwrap().set_theme(theme)
703    }
704
705    #[inline]
706    pub fn theme(&self) -> Option<Theme> {
707        self.window_state.lock().unwrap().theme()
708    }
709
710    pub fn set_content_protected(&self, _protected: bool) {}
711
712    #[inline]
713    pub fn title(&self) -> String {
714        self.window_state.lock().unwrap().title().to_owned()
715    }
716}
717
718impl Drop for Window {
719    fn drop(&mut self) {
720        self.window_requests.closed.store(true, Ordering::Relaxed);
721        self.event_loop_awakener.ping();
722    }
723}
724
725/// The request from the window to the event loop.
726#[derive(Debug)]
727pub struct WindowRequests {
728    /// The window was closed.
729    pub closed: AtomicBool,
730
731    /// Redraw Requested.
732    pub redraw_requested: AtomicBool,
733}
734
735impl WindowRequests {
736    pub fn take_closed(&self) -> bool {
737        self.closed.swap(false, Ordering::Relaxed)
738    }
739
740    pub fn take_redraw_requested(&self) -> bool {
741        self.redraw_requested.swap(false, Ordering::Relaxed)
742    }
743}
744
745impl TryFrom<&str> for Theme {
746    type Error = ();
747
748    /// ```
749    /// use winit::window::Theme;
750    ///
751    /// assert_eq!("dark".try_into(), Ok(Theme::Dark));
752    /// assert_eq!("lIghT".try_into(), Ok(Theme::Light));
753    /// ```
754    fn try_from(theme: &str) -> Result<Self, Self::Error> {
755        if theme.eq_ignore_ascii_case("dark") {
756            Ok(Self::Dark)
757        } else if theme.eq_ignore_ascii_case("light") {
758            Ok(Self::Light)
759        } else {
760            Err(())
761        }
762    }
763}