plinth_core/
app.rs

1use crate::graphics::{create_graphics, Graphics, Rc};
2use crate::plinth_app::PlinthApp;
3use std::cell::RefCell;
4use winit::{
5    application::ApplicationHandler,
6    dpi::PhysicalSize,
7    event::WindowEvent,
8    event_loop::{ActiveEventLoop, ControlFlow, EventLoop, EventLoopProxy},
9    window::{Window, WindowId},
10};
11
12enum State {
13    Ready(Graphics),
14    Init(Option<EventLoopProxy<Graphics>>),
15}
16
17pub struct App {
18    _title: String,
19    state: State,
20    user_app: Rc<RefCell<dyn PlinthApp>>,
21}
22
23impl App {
24    pub fn new(event_loop: &EventLoop<Graphics>, user_app: Rc<RefCell<dyn PlinthApp>>) -> Self {
25        Self {
26            _title: "WebGPU Example".to_string(),
27            state: State::Init(Some(event_loop.create_proxy())),
28            user_app,
29        }
30    }
31
32    fn draw(&mut self) {
33        if let State::Ready(gfx) = &mut self.state {
34            self.user_app.borrow_mut().before_render();
35            self.user_app.borrow_mut().render(gfx);
36            self.user_app.borrow_mut().after_render();
37        }
38    }
39
40    fn resized(&mut self, size: PhysicalSize<u32>) {
41        if let State::Ready(gfx) = &mut self.state {
42            gfx.resize(size);
43            self.user_app.borrow_mut().render(gfx);
44        }
45    }
46
47    pub fn _set_title(&mut self, title: &str) {
48        self._title = title.to_string();
49    }
50}
51
52impl ApplicationHandler<Graphics> for App {
53    fn window_event(
54        &mut self,
55        event_loop: &ActiveEventLoop,
56        _window_id: WindowId,
57        event: WindowEvent,
58    ) {
59        match event {
60            WindowEvent::Resized(size) => self.resized(size),
61            WindowEvent::RedrawRequested => self.draw(),
62
63            WindowEvent::CloseRequested => {
64                self.user_app.borrow_mut().on_close();
65                event_loop.exit()
66            }
67            _ => {}
68        }
69        self.user_app
70            .borrow_mut()
71            .event_handler(event_loop, _window_id, &event);
72    }
73
74    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
75        if let State::Init(proxy) = &mut self.state {
76            if let Some(proxy) = proxy.take() {
77                let mut win_attr = Window::default_attributes();
78
79                #[cfg(not(target_arch = "wasm32"))]
80                {
81                    win_attr = win_attr.with_title(self._title.as_str());
82                }
83
84                #[cfg(target_arch = "wasm32")]
85                {
86                    use winit::platform::web::WindowAttributesExtWebSys;
87                    win_attr = win_attr.with_append(true);
88                }
89
90                let window = Rc::new(
91                    event_loop
92                        .create_window(win_attr)
93                        .expect("create window err."),
94                );
95
96                let user_app = Rc::clone(&self.user_app);
97
98                #[cfg(target_arch = "wasm32")]
99                wasm_bindgen_futures::spawn_local(create_graphics(window, proxy, user_app));
100
101                #[cfg(not(target_arch = "wasm32"))]
102                pollster::block_on(create_graphics(window, proxy, user_app));
103            }
104        }
105    }
106
107    fn user_event(&mut self, _event_loop: &ActiveEventLoop, graphics: Graphics) {
108        self.state = State::Ready(graphics);
109        if let State::Ready(gfx) = &mut self.state {
110            let scale_factor = gfx.window.scale_factor();
111            let logical_size = gfx.window.inner_size();
112            let physical_size = winit::dpi::PhysicalSize::new(
113                (logical_size.width as f64 * scale_factor) as u32,
114                (logical_size.height as f64 * scale_factor) as u32,
115            );
116            self.resized(physical_size);
117        }
118        self.user_app.borrow_mut().init();
119    }
120}
121
122pub fn start_app(user_app: Rc<RefCell<dyn PlinthApp>>) {
123    let event_loop = EventLoop::<Graphics>::with_user_event().build().unwrap();
124    event_loop.set_control_flow(ControlFlow::Poll);
125    let app = App::new(&event_loop, user_app);
126    run_app(event_loop, app);
127}
128
129#[cfg(target_arch = "wasm32")]
130fn run_app(event_loop: EventLoop<Graphics>, app: App) {
131    // Sets up panics to go to the console.error in browser environments
132    std::panic::set_hook(Box::new(console_error_panic_hook::hook));
133    console_log::init_with_level(log::Level::Error).expect("Couldn't initialize logger");
134
135    // Runs the app async via the browsers event loop
136    use winit::platform::web::EventLoopExtWebSys;
137    wasm_bindgen_futures::spawn_local(async move {
138        event_loop.spawn_app(app);
139    });
140}
141
142#[cfg(not(target_arch = "wasm32"))]
143fn run_app(event_loop: EventLoop<Graphics>, mut app: App) {
144    // Allows the setting of the log level through RUST_LOG env var.
145    // It also allows wgpu logs to be seen.
146    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("error")).init();
147
148    // Runs the app on the current thread.
149    let _ = event_loop.run_app(&mut app);
150}