demo_vk/app/
mod.rs

1//! The GLFW application implementation.
2//!
3//! This module defines the traits and functions required for managing the
4//! lifecycle of a GLFW application with a single Vulkan-enabled window.
5
6mod fullscreen_toggle;
7mod logging;
8use {
9    crate::trace,
10    anyhow::{Context, Result},
11    clap::Parser,
12    glfw::fail_on_errors,
13    std::sync::{atomic::AtomicBool, Arc},
14};
15
16pub use self::fullscreen_toggle::FullscreenToggle;
17
18/// Implementations of this trait can be run with app_main to manage a GLFW
19/// window.
20pub trait App {
21    type Args: Sized + Parser;
22
23    /// Creates a new instance of the application.
24    /// The application is allowed to modify the window based on its own
25    /// requirements. This includes modifying the polling state, fullscreen
26    /// status, size, etc...
27    fn new(window: &mut glfw::Window, args: Self::Args) -> Result<Self>
28    where
29        Self: Sized;
30
31    /// Handles a single GLFW event.
32    ///
33    /// This function is called in a loop to consume any pending events before
34    /// every call to update().
35    fn handle_event(
36        &mut self,
37        #[allow(unused_variables)] window: &mut glfw::Window,
38        #[allow(unused_variables)] event: glfw::WindowEvent,
39    ) -> Result<()> {
40        Ok(())
41    }
42
43    /// Called in a loop after all pending events have been processed.
44    ///
45    /// This is a good place for rendering logic. This method blocks event
46    /// processing, so it should be kept as responsive as possible.
47    fn update(
48        &mut self,
49        #[allow(unused_variables)] window: &mut glfw::Window,
50    ) -> Result<()> {
51        Ok(())
52    }
53}
54
55/// The entrypoint for implementations of the App trait.
56///
57/// Initializes logging and the GLFW library. Any errors that cause the
58/// application to exit are reported with a stacktrace if available.
59pub fn app_main<A>() -> Result<()>
60where
61    A: App + 'static,
62{
63    let exit_result = try_app_main::<A>();
64    if let Some(err) = exit_result.as_ref().err() {
65        let result: String = err
66            .chain()
67            .skip(1)
68            .enumerate()
69            .map(|(index, err)| format!("  {}| {}\n\n", index, err))
70            .to_owned()
71            .collect();
72        log::error!(
73            "{}\n\n{}\n\nCaused by:\n{}\n\nBacktrace:\n{}",
74            "Application exited with an error!",
75            err,
76            result,
77            err.backtrace()
78        );
79    };
80    exit_result
81}
82
83fn try_app_main<A>() -> Result<()>
84where
85    A: App + 'static,
86{
87    logging::setup();
88
89    let args = argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
90        .with_context(trace!("Error while expanding argfiles!"))?;
91    let args = A::Args::parse_from(args);
92
93    let mut glfw = glfw::init(fail_on_errors!())
94        .with_context(trace!("Unable to initalize GLFW!"))?;
95    glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
96    glfw.window_hint(glfw::WindowHint::ScaleToMonitor(true));
97    glfw.window_hint(glfw::WindowHint::Visible(false));
98
99    let interrupted = {
100        let interrupted = Arc::new(AtomicBool::new(false));
101        let cloned = interrupted.clone();
102        ctrlc::set_handler(move || {
103            interrupted.store(true, std::sync::atomic::Ordering::SeqCst);
104        })
105        .with_context(trace!("Unable to set the ctrl-c signal handler!"))?;
106        cloned
107    };
108
109    let (mut window, events) = glfw
110        .create_window(
111            640,
112            480,
113            "Shader Toy - Slang",
114            glfw::WindowMode::Windowed,
115        )
116        .with_context(trace!("Unable to create window!"))?;
117
118    let mut app = A::new(&mut window, args)
119        .with_context(trace!("Error while initializing the app!"))?;
120
121    window.show();
122    while !window.should_close() {
123        if interrupted.load(std::sync::atomic::Ordering::Relaxed) {
124            window.set_should_close(true);
125        };
126
127        glfw.poll_events();
128        for (_, event) in glfw::flush_messages(&events) {
129            app.handle_event(&mut window, event)?;
130        }
131
132        app.update(&mut window)?;
133    }
134
135    Ok(())
136}