1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Provides structures for running a stateful single-window GLFW application.

use {anyhow::Result, glfw::WindowEvent};

mod glfw_window;
mod logging;

pub use self::glfw_window::GlfwWindow;

/// Application state can be any type which implements the State trait.
///
/// State is created after the GLFW window is created, but is allowed to
/// configure the window for things like resizability and event polling.
pub trait State {
    /// Create a new instance of this state.
    ///
    /// # Params
    ///
    /// * `window` - A fully constructed application window. The implementation
    ///   can use this handle to resize the window, apply GLFW window hints,
    ///   toggle fullscren, and construct a Vulkan instance which can present
    ///   surfaces to the window.
    fn new(window: &mut GlfwWindow) -> Result<Self>
    where
        Self: Sized;

    /// Handle a GLFW event and update the application state.
    ///
    /// # Params
    ///
    /// * `window` - The fully constructed application window. The application
    ///   can exit by calling `set_should_close` on the window.
    /// * `window_event` - The event currently being processed by the window.
    fn handle_event(
        &mut self,
        _window: &mut GlfwWindow,
        _window_event: glfw::WindowEvent,
    ) -> Result<()> {
        Ok(())
    }

    /// Called each time through the main application loop after all events
    /// have been processed.
    ///
    /// Update is not called while an application is paused while minimized.
    ///
    /// # Params
    ///
    /// * `window` - The fully constructed application window. The application
    ///   can exit by calling `set_should_close` on the window.
    fn update(&mut self, _window: &mut GlfwWindow) -> Result<()> {
        Ok(())
    }
}

/// Every application is comprised of a State type and a GLFW window.
/// Applications automatically pause if they are minimized or the window is
/// resized such that there is no drawing area.
pub struct Application<S: State> {
    state: S,
    paused: bool,
    window: GlfwWindow,
}

// Public API

impl<S> Application<S>
where
    S: Sized + State,
{
    /// Create and run the Application until the window is closed.
    ///
    /// The window title is just the Application state struct's type name.
    pub fn run() -> Result<()> {
        let window_title = std::any::type_name::<S>();
        Self::new(window_title)?.main_loop()
    }
}

// Private API

impl<S> Application<S>
where
    S: Sized + State,
{
    /// Create a new running application.
    fn new(window_title: impl AsRef<str>) -> Result<Self> {
        self::logging::setup();

        let mut window = GlfwWindow::new(window_title)?;

        // Framebuffer polling is required for detecting when the app should be
        // paused.
        window.set_framebuffer_size_polling(true);

        Ok(Self {
            state: S::new(&mut window)?,
            paused: false,
            window,
        })
    }

    /// Run the application until until the window is closed.
    fn main_loop(mut self) -> Result<()> {
        let event_receiver = self.window.event_receiver.take().unwrap();
        while !self.window.should_close() {
            self.window.glfw.poll_events();
            for (_, window_event) in glfw::flush_messages(&event_receiver) {
                self.handle_event(window_event)?;
            }
            if !self.paused {
                self.state.update(&mut self.window)?;
            }
        }
        Ok(())
    }

    /// Handle a GLFW window event.
    fn handle_event(&mut self, window_event: WindowEvent) -> Result<()> {
        match window_event {
            WindowEvent::Close => {
                self.window.set_should_close(true);
            }
            WindowEvent::FramebufferSize(width, height) => {
                self.paused = width == 0 || height == 0;
            }
            _ => (),
        }

        self.state.handle_event(&mut self.window, window_event)
    }
}