control_flow/util/
fill.rs

1//! Fill the window buffer with a solid color.
2//!
3//! Launching a window without drawing to it has unpredictable results varying from platform to
4//! platform. In order to have well-defined examples, this module provides an easy way to
5//! fill the window buffer with a solid color.
6//!
7//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
8//! also be used to fill the window buffer, but they are more complicated to use.
9
10#[allow(unused_imports)]
11pub use platform::cleanup_window;
12pub use platform::fill_window;
13
14#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
15mod platform {
16    use std::cell::RefCell;
17    use std::collections::HashMap;
18    use std::mem;
19    use std::mem::ManuallyDrop;
20    use std::num::NonZeroU32;
21
22    use softbuffer::{Context, Surface};
23    use winit::window::{Window, WindowId};
24
25    thread_local! {
26        // NOTE: You should never do things like that, create context and drop it before
27        // you drop the event loop. We do this for brevity to not blow up examples. We use
28        // ManuallyDrop to prevent destructors from running.
29        //
30        // A static, thread-local map of graphics contexts to open windows.
31        static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
32    }
33
34    /// The graphics context used to draw to a window.
35    struct GraphicsContext {
36        /// The global softbuffer context.
37        context: RefCell<Context<&'static Window>>,
38
39        /// The hash map of window IDs to surfaces.
40        surfaces: HashMap<WindowId, Surface<&'static Window, &'static Window>>,
41    }
42
43    impl GraphicsContext {
44        fn new(w: &Window) -> Self {
45            Self {
46                context: RefCell::new(
47                    Context::new(unsafe { mem::transmute::<&'_ Window, &'static Window>(w) })
48                        .expect("Failed to create a softbuffer context"),
49                ),
50                surfaces: HashMap::new(),
51            }
52        }
53
54        fn create_surface(
55            &mut self,
56            window: &Window,
57        ) -> &mut Surface<&'static Window, &'static Window> {
58            self.surfaces.entry(window.id()).or_insert_with(|| {
59                Surface::new(&self.context.borrow(), unsafe {
60                    mem::transmute::<&'_ Window, &'static Window>(window)
61                })
62                .expect("Failed to create a softbuffer surface")
63            })
64        }
65
66        fn destroy_surface(&mut self, window: &Window) {
67            self.surfaces.remove(&window.id());
68        }
69    }
70
71    pub fn fill_window(window: &Window) {
72        GC.with(|gc| {
73            let size = window.inner_size();
74            let (Some(width), Some(height)) =
75                (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
76            else {
77                return;
78            };
79
80            // Either get the last context used or create a new one.
81            let mut gc = gc.borrow_mut();
82            let surface =
83                gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window);
84
85            // Fill a buffer with a solid color.
86            const DARK_GRAY: u32 = 0xff181818;
87
88            surface.resize(width, height).expect("Failed to resize the softbuffer surface");
89
90            let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
91            buffer.fill(DARK_GRAY);
92            buffer.present().expect("Failed to present the softbuffer buffer");
93        })
94    }
95
96    #[allow(dead_code)]
97    pub fn cleanup_window(window: &Window) {
98        GC.with(|gc| {
99            let mut gc = gc.borrow_mut();
100            if let Some(context) = gc.as_mut() {
101                context.destroy_surface(window);
102            }
103        });
104    }
105}
106
107#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
108mod platform {
109    pub fn fill_window(_window: &winit::window::Window) {
110        // No-op on mobile platforms.
111    }
112
113    #[allow(dead_code)]
114    pub fn cleanup_window(_window: &winit::window::Window) {
115        // No-op on mobile platforms.
116    }
117}