demo_vk/app/
mod.rs

1//! The Winit ApplicationHandler implementation.
2//!
3//! This module defines the traits and functions required for managing the
4//! lifecycle of a Winit application with a single Vulkan-enabled window.
5
6mod 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
28/// Implementations of this trait can be run with app_main to manage a Winit
29/// window.
30pub trait App {
31    type Args: Sized + Parser;
32
33    /// Creates a new instance of the application.
34    /// The application is allowed to modify the window based on its own
35    /// requirements. This includes modifying the polling state, fullscreen
36    /// status, size, etc...
37    ///
38    /// Note: the window is not visible when initially created, the app must
39    /// choose when to make it visible for the first time.
40    fn new(window: &mut Window, args: &Self::Args) -> Result<Self>
41    where
42        Self: Sized;
43
44    /// Handles a single WindowEvent.
45    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    /// Handles a single DeviceEvent.
54    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    /// Called in a loop when no other events are pending or when the OS
63    /// requests a new frame for the window.
64    ///
65    /// This is a good place for rendering logic. This method blocks event
66    /// processing, so it should be kept as responsive as possible.
67    fn update(
68        &mut self,
69        #[allow(unused_variables)] window: &mut Window,
70    ) -> Result<AppState> {
71        Ok(AppState::Continue)
72    }
73}
74
75/// The entrypoint for implementations of the App trait.
76///
77/// Initializes logging and the application event loop. Any errors that cause
78/// the application to exit are reported with a stacktrace if available.
79pub 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            // All setup logic is one-time only, so if the window already
131            // exists then do nothing.
132            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            // Quit if the app doesn't exist, this can happen when initial
169            // setup failed but an event is still queued.
170            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}