1mod logging;
7
8use {
9 crate::unwrap_here,
10 anyhow::{Context, Result},
11 clap::Parser,
12 winit::{
13 application::ApplicationHandler,
14 event::{DeviceEvent, WindowEvent},
15 event_loop::{ActiveEventLoop, EventLoop},
16 window::{Window, WindowAttributes},
17 },
18};
19
20pub use self::logging::{ErrorLocationMessage, Location};
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23pub enum AppState {
24 Continue,
25 Exit,
26}
27
28pub trait App {
31 type Args: Sized + Parser;
32
33 fn new(window: &mut Window, args: &Self::Args) -> Result<Self>
41 where
42 Self: Sized;
43
44 fn handle_window_event(
46 &mut self,
47 #[allow(unused_variables)] window: &mut Window,
48 #[allow(unused_variables)] event: WindowEvent,
49 ) -> Result<AppState> {
50 Ok(AppState::Continue)
51 }
52
53 fn handle_device_event(
55 &mut self,
56 #[allow(unused_variables)] window: &mut Window,
57 #[allow(unused_variables)] event: DeviceEvent,
58 ) -> Result<AppState> {
59 Ok(AppState::Continue)
60 }
61
62 fn update(
68 &mut self,
69 #[allow(unused_variables)] window: &mut Window,
70 ) -> Result<AppState> {
71 Ok(AppState::Continue)
72 }
73}
74
75pub fn app_main<A>() -> Result<()>
80where
81 A: App + 'static,
82{
83 let exit_result = try_app_main::<A>();
84 if let Some(err) = exit_result.as_ref().err() {
85 let result: String = err
86 .chain()
87 .skip(1)
88 .enumerate()
89 .map(|(index, err)| format!(" {}| {}\n\n", index, err))
90 .to_owned()
91 .collect();
92 log::error!(
93 "{}\n\n{}\n\nCaused by:\n{}\n\nBacktrace:\n{}",
94 "Application exited with an error!",
95 err,
96 result,
97 err.backtrace()
98 );
99 };
100 exit_result
101}
102
103struct WinitAppHandler<A: App + 'static> {
104 args: Option<A::Args>,
105 app: Option<A>,
106 window: Option<Window>,
107 exit_result: Result<()>,
108}
109
110impl<A: App + 'static> WinitAppHandler<A> {
111 fn process_app_state(
112 &mut self,
113 app_state: Result<AppState>,
114 event_loop: &ActiveEventLoop,
115 ) {
116 if let Ok(AppState::Exit) = app_state {
117 event_loop.exit();
118 }
119 if let Err(err) = app_state {
120 self.exit_result =
121 Err(err).context("Unexpected application error!");
122 event_loop.exit();
123 }
124 }
125}
126
127impl<A: App + 'static> ApplicationHandler for WinitAppHandler<A> {
128 fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
129 if self.window.is_some() {
130 return;
133 }
134
135 self.window = match event_loop
136 .create_window(WindowAttributes::default().with_visible(false))
137 {
138 Ok(window) => Some(window),
139 Err(error) => {
140 self.exit_result =
141 Err(error).context("Unable to create window");
142 event_loop.exit();
143 return;
144 }
145 };
146
147 self.app = match A::new(
148 self.window.as_mut().unwrap(),
149 &self.args.take().unwrap(),
150 ) {
151 Ok(app) => Some(app),
152 Err(error) => {
153 self.exit_result =
154 Err(error).context("Unable to initialize app");
155 event_loop.exit();
156 return;
157 }
158 };
159 }
160
161 fn window_event(
162 &mut self,
163 event_loop: &winit::event_loop::ActiveEventLoop,
164 _window_id: winit::window::WindowId,
165 event: WindowEvent,
166 ) {
167 if self.app.is_none() {
168 self.process_app_state(Ok(AppState::Exit), event_loop);
171 return;
172 }
173
174 let app_state = match event {
175 WindowEvent::CloseRequested => Ok(AppState::Exit),
176 WindowEvent::RedrawRequested => {
177 self.window.as_mut().unwrap().pre_present_notify();
178 let state = self
179 .app
180 .as_mut()
181 .unwrap()
182 .update(self.window.as_mut().unwrap())
183 .context("Unexpected error in App::update()!");
184 self.window.as_mut().unwrap().request_redraw();
185 state
186 }
187 _ => self
188 .app
189 .as_mut()
190 .unwrap()
191 .handle_window_event(self.window.as_mut().unwrap(), event),
192 };
193 self.process_app_state(app_state, event_loop);
194 }
195
196 fn device_event(
197 &mut self,
198 event_loop: &winit::event_loop::ActiveEventLoop,
199 _device_id: winit::event::DeviceId,
200 event: DeviceEvent,
201 ) {
202 let app_state = self
203 .app
204 .as_mut()
205 .unwrap()
206 .handle_device_event(self.window.as_mut().unwrap(), event);
207 self.process_app_state(app_state, event_loop);
208 }
209}
210
211fn try_app_main<A>() -> Result<()>
212where
213 A: App + 'static,
214{
215 logging::setup();
216
217 let args = unwrap_here!(
218 "Parse CLI args",
219 argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
220 );
221
222 let mut winit_app = WinitAppHandler::<A> {
223 args: Some(A::Args::parse_from(args)),
224 app: None,
225 window: None,
226 exit_result: Ok(()),
227 };
228
229 let event_loop = unwrap_here!("Create event loop.", EventLoop::new());
230 event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
231 unwrap_here!(
232 "Run main application loop.",
233 event_loop.run_app(&mut winit_app)
234 );
235
236 winit_app.exit_result
237}