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