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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! The application structure and supporting traits. //! //! # Example //! //! ```rust,no_run //! use agents::app::{App, State}; //! use anyhow::{Context, Result}; //! use draw2d::graphics::{ //! Graphics, //! layer::{LayerHandle, Batch}, //! texture_atlas::TextureHandle, //! vertex::Vertex2d //! }; //! use glfw::Window; //! use std::time::Duration; //! //! /// Every binary will have a structure of some sort. //! struct Example { //! layer: LayerHandle //! } //! //! /// The structure can have whatever implementation functions it needs. //! impl Example { //! //! /// The constructor has access to both the window and graphics. //! pub fn create( //! window: &mut glfw::Window, //! graphics: &mut Graphics //! ) -> Result<Self> { //! Ok(Self { //! layer: graphics.add_layer_to_top() //! }) //! } //! } //! //! /// The example must implement State to be used with an application. //! /// All methods have defaults, so none are *required* for a bare minimum //! /// working example. //! impl State for Example { //! fn update( //! &mut self, //! _: &mut Window, //! g: &mut Graphics, //! _dt: Duration //! ) -> Result<()> { //! let mut batch = Batch::empty(); //! batch.vertices.extend_from_slice(&[ //! Vertex2d { //! pos: [-100.0, -100.0], //! ..Default::default() //! }, //! Vertex2d { //! pos: [100.0, -100.0], //! ..Default::default() //! }, //! Vertex2d { //! pos: [0.0, 100.0], //! rgba: [0.1, 0.2, 0.8, 1.0], //! ..Default::default() //! }, //! ]); //! g.get_layer_mut(&self.layer).push_batch(batch); //! Ok(()) //! } //! } //! //! /// Finally, start the application and let it run until the user quits. //! fn main() -> Result<()> { //! App::new(Example::create)?.main_loop() //! } //! ``` mod app; mod update_timer; use anyhow::Result; use draw2d::{graphics::Graphics, GlfwWindow}; use glfw::{Action, Key, WindowEvent}; use std::time::{Duration, Instant}; /// Each application maintains an instance of State which controls the actual /// behavior and rendering. pub trait State { /// Invoked by the application once just before the first update/render. /// /// This can be useful if any initialization needs to be done before /// rendering but *after* constructing the instance for some reason. Or if /// the application has need to setup some visuals which never /// change. #[allow(unused)] fn init( &mut self, window: &mut glfw::Window, graphics: &mut Graphics, ) -> Result<()> { Ok(()) } /// Called once each frame before presenting the framebuffer. #[allow(unused)] fn update( &mut self, window: &mut glfw::Window, graphics: &mut Graphics, update_duration: Duration, ) -> Result<()> { Ok(()) } /// Handle a glfw window event. /// /// The application can be stopped by calling `window.set_should_close`. /// By default, the application will close when `esc` is pressed. #[allow(unused)] fn handle_event( &mut self, window_event: &glfw::WindowEvent, window: &mut glfw::Window, graphics: &mut Graphics, ) -> Result<()> { match window_event { WindowEvent::Key(Key::Escape, _, Action::Press, _) => { window.set_should_close(true); Ok(()) } _ => Ok(()), } } } /// The main application. /// /// This struct owns the window, the graphics subsystem, and the state. It is /// responsible for the main application loop, rebuilding the swapchain, and /// invoking functions on the State. pub struct App<S: State> { graphics: Graphics, window_surface: GlfwWindow, update_timer: UpdateTimer, state: S, } /// The timer is used to compute the time to update and the average update /// duration. /// /// The timer prints the average update duration to the terminal. It only does /// this every few seconds to prevent terminal IO from becoming a bottleneck. pub struct UpdateTimer { /// The timer's display name display_name: String, /// The timestamp recorded for the last update. last_update: Instant, /// The timestamp recorded last time the average update duration was /// checkpointed. last_checkpoint: Instant, /// The number of updates since the last checkpoint. updates_since_checkpoint: i32, }