Skip to main content

complex_text/
common.rs

1use glfw::*;
2use glow::HasContext;
3use impellers::*;
4
5#[allow(unused)]
6pub struct SdlGlImpellerFrameWork {
7    pub ttx: TypographyContext,
8    pub style: ParagraphStyle,
9    pub itx: impellers::Context,
10    pub glow_ctx: glow::Context,
11    pub receiver: GlfwReceiver<(f64, WindowEvent)>,
12    pub window: PWindow,
13    pub gtx: Glfw,
14    pub events: Vec<WindowEvent>,
15}
16impl Default for SdlGlImpellerFrameWork {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21#[allow(unused)]
22pub const DOG_BYTES: &[u8] = include_bytes!("dog.jpg");
23
24type UserCallback = Box<dyn FnMut(&mut SdlGlImpellerFrameWork) -> Option<DisplayList>>;
25impl SdlGlImpellerFrameWork {
26    pub fn new() -> Self {
27        // initialize window
28        let mut gtx = init(fail_on_errors).expect("failed to init glfw");
29        gtx.window_hint(WindowHint::ContextVersionMajor(4));
30        gtx.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
31        gtx.window_hint(WindowHint::SRgbCapable(true));
32        gtx.window_hint(WindowHint::StencilBits(Some(8)));
33        gtx.window_hint(WindowHint::ScaleToMonitor(true));
34
35        let (mut window, ev_receiver) = gtx
36            .create_window(800, 600, "glfw opengl impeller", WindowMode::Windowed)
37            .unwrap();
38        window.make_current();
39        window.set_all_polling(true);
40
41        // initialize impeller context using opengl fn pointers
42        let itx = unsafe {
43            impellers::Context::new_opengl_es(|s| {
44                window
45                    .get_proc_address(s)
46                    .map(|f| f as _)
47                    .unwrap_or(std::ptr::null_mut())
48            })
49        }
50        .unwrap();
51        let glow_ctx = unsafe {
52            glow::Context::from_loader_function(|s| {
53                window
54                    .get_proc_address(s)
55                    .map(|f| f as _)
56                    .unwrap_or(std::ptr::null_mut())
57            })
58        };
59        unsafe {
60            let (width, height) = window.get_framebuffer_size();
61            glow_ctx.viewport(0, 0, width, height);
62        }
63        let ttx = TypographyContext::default();
64        let mut style = ParagraphStyle::default();
65        style.set_font_size(24.0);
66        style.set_font_family("Roboto");
67        style.set_font_weight(FontWeight::Bold);
68        let mut paint = Paint::default();
69        paint.set_color(Color::LIGHT_SKY_BLUE);
70        style.set_foreground(&paint);
71        Self {
72            ttx,
73            style,
74            glow_ctx,
75            itx,
76            window,
77            receiver: ev_receiver,
78            gtx,
79            events: vec![],
80        }
81    }
82    /// Enters event loop and continues until window is closed or escape is pressed
83    /// # Arguments
84    /// - dl: If specified, this display list will be drawn on each frame
85    /// - user_callback: If specified, this callback will be called on each frame
86    ///   - If it returns a DisplayList, we will draw that on surface after callback.
87    ///   - The callback has access to everything, so it can draw to surface on its own if it needs to.
88    pub fn enter_event_loop(
89        mut self,
90        dl: Option<DisplayList>,
91        mut user_callback: Option<UserCallback>,
92    ) {
93        let mut previous_instant = std::time::Instant::now();
94        let mut current_frame = 0;
95        let mut fps = 0;
96        let mut vsync = true;
97        // enter event loop
98        while !self.window.should_close() {
99            {
100                // record fps
101                if previous_instant.elapsed().as_secs_f64() >= 1.0 {
102                    fps = current_frame;
103                    current_frame = 0;
104                    previous_instant = std::time::Instant::now();
105                }
106                current_frame += 1;
107            }
108            self.events.clear();
109            // check events
110            self.gtx.poll_events();
111            for (_, event) in flush_messages(&self.receiver) {
112                match &event {
113                    WindowEvent::Close => {
114                        self.window.set_should_close(true);
115                    }
116                    WindowEvent::FramebufferSize(w, h) => {
117                        unsafe {
118                            self.glow_ctx.viewport(0, 0, *w, *h);
119                        }
120                        println!("window resized to {}x{}", w, h);
121                    }
122                    WindowEvent::Key(Key::Space, _, Action::Release, _) => {
123                        vsync = !vsync;
124                        println!("setting vsync to {vsync}");
125
126                        self.gtx.set_swap_interval(if vsync {
127                            SwapInterval::Sync(1)
128                        } else {
129                            SwapInterval::None
130                        });
131                    }
132                    _ => {}
133                }
134                self.events.push(event);
135            }
136
137            let (width, height) = self.window.get_framebuffer_size();
138            // init surface by wrapping default framebuffer (fbo = 0)
139            let mut surface = unsafe {
140                self.itx.wrap_fbo(
141                    0,
142                    PixelFormat::RGBA8888,
143                    ISize::new(width.into(), height.into()),
144                )
145            }
146            .expect("failed to wrap window's framebuffer");
147            let mut dl_builder = DisplayListBuilder::new(Some(&Rect::from_size(
148                [width as f32, height as f32].into(),
149            )));
150
151            if let Some(dl) = dl.as_ref() {
152                dl_builder.draw_display_list(dl, 1.0);
153            }
154            // call user callback
155            if let Some(cb) = user_callback.as_mut() {
156                if let Some(display_list) = cb(&mut self) {
157                    dl_builder.draw_display_list(&display_list, 1.0);
158                }
159            }
160            {
161                let mut para_builder = ParagraphBuilder::new(&self.ttx).unwrap();
162                para_builder.push_style(&self.style);
163                para_builder.add_text(&format!("avg fps: {fps}"));
164                let para = para_builder.build(1000.0).unwrap();
165                dl_builder.draw_paragraph(&para, Point::origin());
166            }
167            surface
168                .draw_display_list(&dl_builder.build().unwrap())
169                .unwrap();
170
171            // submit frame and wait for vsync
172            self.window.swap_buffers();
173        }
174    }
175
176    // drop the window/sdl or whatever
177}
178
179#[allow(unused)]
180pub fn main() {
181    unimplemented!("this is a common module for other examples to use")
182}