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(not(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 rio_window::window::{Window, WindowId};
23    use softbuffer::{Context, Surface};
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 {
48                        mem::transmute::<&'_ Window, &'static Window>(w)
49                    })
50                    .expect("Failed to create a softbuffer context"),
51                ),
52                surfaces: HashMap::new(),
53            }
54        }
55
56        fn create_surface(
57            &mut self,
58            window: &Window,
59        ) -> &mut Surface<&'static Window, &'static Window> {
60            self.surfaces.entry(window.id()).or_insert_with(|| {
61                Surface::new(&self.context.borrow(), unsafe {
62                    mem::transmute::<&'_ Window, &'static Window>(window)
63                })
64                .expect("Failed to create a softbuffer surface")
65            })
66        }
67
68        fn destroy_surface(&mut self, window: &Window) {
69            self.surfaces.remove(&window.id());
70        }
71    }
72
73    pub fn fill_window(window: &Window) {
74        GC.with(|gc| {
75            let size = window.inner_size();
76            let (Some(width), Some(height)) =
77                (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
78            else {
79                return;
80            };
81
82            // Either get the last context used or create a new one.
83            let mut gc = gc.borrow_mut();
84            let surface = gc
85                .get_or_insert_with(|| GraphicsContext::new(window))
86                .create_surface(window);
87
88            // Fill a buffer with a solid color.
89            const DARK_GRAY: u32 = 0xff181818;
90
91            surface
92                .resize(width, height)
93                .expect("Failed to resize the softbuffer surface");
94
95            let mut buffer = surface
96                .buffer_mut()
97                .expect("Failed to get the softbuffer buffer");
98            buffer.fill(DARK_GRAY);
99            buffer
100                .present()
101                .expect("Failed to present the softbuffer buffer");
102        })
103    }
104
105    #[allow(dead_code)]
106    pub fn cleanup_window(window: &Window) {
107        GC.with(|gc| {
108            let mut gc = gc.borrow_mut();
109            if let Some(context) = gc.as_mut() {
110                context.destroy_surface(window);
111            }
112        });
113    }
114}