nannou/app.rs
1//! Items related to the `App` type and the application context in general.
2//!
3//! See here for items relating to the event loop, device access, creating and managing windows,
4//! streams and more.
5//!
6//! - [**App**](./struct.App.html) - provides a context and API for windowing, devices, etc.
7//! - [**Proxy**](./struct.Proxy.html) - a handle to an **App** that may be used from a non-main
8//! thread.
9//! - [**LoopMode**](./enum.LoopMode.html) - describes the behaviour of the application event loop.
10
11use crate::draw;
12use crate::event::{self, Event, Key, LoopEvent, Update};
13use crate::frame::{Frame, RawFrame};
14use crate::geom;
15use crate::state;
16use crate::time::DurationF64;
17use crate::wgpu;
18use crate::window::{self, Window};
19use find_folder;
20use instant::Instant;
21use std::cell::{RefCell, RefMut};
22use std::collections::HashMap;
23use std::future::Future;
24use std::path::PathBuf;
25use std::pin::Pin;
26use std::sync::atomic::{self, AtomicBool};
27use std::sync::Arc;
28use std::time::Duration;
29use std::{self, future};
30use wgpu_upstream::InstanceDescriptor;
31use winit;
32use winit::event_loop::ControlFlow;
33
34/// The user function type for initialising their model.
35pub type ModelFn<Model> = fn(&App) -> Model;
36
37/// The user function type for updating their model in accordance with some event.
38pub type EventFn<Model, Event> = fn(&App, &mut Model, Event);
39
40/// The user function type for updating the user model within the application loop.
41pub type UpdateFn<Model> = fn(&App, &mut Model, Update);
42
43/// The user function type for drawing their model to the surface of a single window.
44pub type ViewFn<Model> = fn(&App, &Model, Frame);
45
46/// A shorthand version of `ViewFn` for sketches where the user does not need a model.
47pub type SketchViewFn = fn(&App, Frame);
48
49/// The user function type allowing them to consume the `model` when the application exits.
50pub type ExitFn<Model> = fn(&App, Model);
51
52/// The **App**'s view function.
53enum View<Model = ()> {
54 /// A view function allows for viewing the user's model.
55 WithModel(ViewFn<Model>),
56 /// A **Simple** view function does not require a user **Model**. Simpler to get started.
57 Sketch(SketchViewFn),
58}
59
60/// A nannou `App` builder.
61pub struct Builder<M = (), E = Event> {
62 model: Box<dyn FnOnce(&App) -> Box<dyn Future<Output = M> + '_>>,
63 config: Config,
64 event: Option<EventFn<M, E>>,
65 update: Option<UpdateFn<M>>,
66 default_view: Option<View<M>>,
67 exit: Option<ExitFn<M>>,
68 create_default_window: bool,
69 default_window_size: Option<DefaultWindowSize>,
70 capture_frame_timeout: Option<Option<Duration>>,
71 max_capture_frame_jobs: Option<u32>,
72 backends: wgpu::Backends,
73}
74
75/// A nannou `Sketch` builder.
76pub struct SketchBuilder<E = Event> {
77 builder: Builder<(), E>,
78}
79
80enum DefaultWindowSize {
81 /// Default window size in logical coordinates.
82 Logical(winit::dpi::LogicalSize<u32>),
83 /// Fullscreen on whatever the primary monitor is at the time of window creation.
84 Fullscreen,
85}
86
87/// The default `model` function used when none is specified by the user.
88fn default_model(_: &App) -> () {
89 ()
90}
91
92/// Each nannou application has a single **App** instance. This **App** represents the entire
93/// context of the application.
94///
95/// The **App** provides access to most application, windowing and "IO" related APIs. In other
96/// words, if you need access to windowing, the active wgpu devices, etc, the **App** will provide
97/// access to this.
98///
99/// The **App** owns and manages:
100///
101/// - The **window and input event loop** used to drive the application forward.
102/// - **All windows** for graphics and user input. Windows can be referenced via their IDs.
103/// - The sharing of wgpu devices between windows.
104/// - A default **Draw** instance for ease of use.
105/// - A map of channels for submitting user input updates to active **Ui**s.
106pub struct App {
107 config: RefCell<Config>,
108 default_window_size: Option<DefaultWindowSize>,
109 max_capture_frame_jobs: u32,
110 capture_frame_timeout: Option<Duration>,
111 pub(crate) event_loop_window_target: Option<EventLoopWindowTarget>,
112 pub(crate) event_loop_proxy: Proxy,
113 pub(crate) windows: RefCell<HashMap<window::Id, Window>>,
114 /// The wgpu backends to choose between.
115 backends: wgpu::Backends,
116 /// The main wgpu instance.
117 instance: wgpu::Instance,
118 /// A map of active wgpu physical device adapters.
119 adapters: wgpu::AdapterMap,
120 draw_state: DrawState,
121 /// The window that is currently in focus.
122 pub(crate) focused_window: RefCell<Option<window::Id>>,
123 /// The current state of the `Mouse`.
124 pub mouse: state::Mouse,
125 /// State of the keyboard keys.
126 ///
127 /// `mods` provides state of each of the modifier keys: `shift`, `ctrl`, `alt`, `logo`.
128 ///
129 /// `down` is the set of keys that are currently pressed.
130 ///
131 /// NOTE: `down` this is tracked by the nannou `App` so issues might occur if e.g. a key is
132 /// pressed while the app is in focus and then released when out of focus. Eventually we should
133 /// change this to query the OS somehow, but I don't think `winit` provides a way to do this
134 /// yet.
135 pub keys: state::Keys,
136 /// Key time measurements tracked by the App.
137 ///
138 /// `duration.since_start` specifies the duration since the app started running.
139 ///
140 /// `duration.since_prev_update` specifies the duration since the previous update event.
141 pub duration: state::Time,
142 /// The time in seconds since the `App` started running.
143 ///
144 /// Primarily, this field is a convenience that removes the need to call
145 /// `app.duration.since_start.secs()`. Normally we would try to avoid using such an ambiguous
146 /// field name, however due to the sheer amount of use that this value has we feel it is
147 /// beneficial to provide easier access.
148 ///
149 /// This value is of the same type as the scalar value used for describing space in animations.
150 /// This makes it very easy to animate graphics and create changes over time without having to
151 /// cast values or repeatedly calculate it from a `Duration` type. A small example might be
152 /// `app.time.sin()` for simple oscillation behaviour.
153 ///
154 /// **Note:** This is suitable for use in short sketches, however should be avoided in long
155 /// running installations. This is because the "resolution" of floating point values reduces as
156 /// the number becomes higher. Instead, we recommend using `app.duration.since_start` or
157 /// `app.duration.since_prev_update` to access a more precise form of app time.
158 pub time: f32,
159}
160
161/// Miscellaneous app configuration parameters.
162#[derive(Debug)]
163struct Config {
164 loop_mode: LoopMode,
165 exit_on_escape: bool,
166 fullscreen_on_shortcut: bool,
167}
168
169// Draw state managed by the **App**.
170#[derive(Debug)]
171struct DrawState {
172 draw: RefCell<draw::Draw>,
173 renderers: RefCell<HashMap<window::Id, RefCell<draw::Renderer>>>,
174}
175
176/// A handle to the **App** that can be shared across threads. This may be used to "wake up" the
177/// **App**'s inner event loop.
178#[derive(Clone)]
179pub struct Proxy {
180 event_loop_proxy: winit::event_loop::EventLoopProxy<()>,
181 // Whether or not a wakeup is already queued.
182 //
183 // Used to avoid spuriously calling `EventLoopProxy::send_event` as this can be expensive on
184 // some platforms.
185 wakeup_queued: Arc<AtomicBool>,
186}
187
188// State related specifically to the application loop, shared between loop modes.
189struct LoopState {
190 updates_since_event: u64,
191 loop_start: Instant,
192 last_update: Instant,
193 total_updates: u64,
194}
195
196/// The mode in which the **App** is currently running the event loop and emitting `Update` events.
197#[derive(Clone, Debug, PartialEq)]
198pub enum LoopMode {
199 /// Synchronises `Update` events with requests for a new frame for the surface.
200 ///
201 /// The result of using this loop mode is similar to using vsync in traditional applications.
202 /// E.g. if you have one window running on a monitor with a 60hz refresh rate, your update will
203 /// get called at a fairly consistent interval that is close to 60 times per second.
204 RefreshSync,
205
206 /// Specifies that the application is continuously looping at a consistent rate.
207 ///
208 /// **NOTE:** This currently behaves the same as `RefreshSync`. Need to upate this to handled a
209 /// fix step properly in the future. See #456.
210 Rate {
211 /// The minimum interval between emitted updates.
212 update_interval: Duration,
213 },
214
215 /// Waits for user input, window, device and wake-up events to occur before producing `Update`
216 /// events.
217 ///
218 /// This is particularly useful for low-energy GUIs that only need to update when some sort of
219 /// input has occurred. The benefit of using this mode is that you don't waste CPU cycles
220 /// looping or updating when you know nothing is changing in your model or view.
221 Wait,
222
223 /// Loops for the given number of updates and then finishes.
224 ///
225 /// This is similar to the **Wait** loop mode, except that windowing, application and input
226 /// events will not cause the loop to update or view again after the initial
227 /// `number_of_updates` have already been applied.
228 ///
229 /// This is useful for sketches where you only want to draw one frame, or if you know exactly
230 /// how many updates you require for an animation, etc.
231 NTimes {
232 /// The number of updates that must be emited regardless of non-update events
233 number_of_updates: usize,
234 },
235}
236
237impl<M> Builder<M, Event>
238where
239 M: 'static,
240{
241 /// The default set of backends requested.
242 pub const DEFAULT_BACKENDS: wgpu::Backends = wgpu::DEFAULT_BACKENDS;
243
244 /// Begin building the `App`.
245 ///
246 /// The `model` argument is the function that the App will call to initialise your Model.
247 ///
248 /// The Model can be thought of as the state that you would like to track throughout the
249 /// lifetime of your nannou program from start to exit.
250 ///
251 /// The given function is called before any event processing begins within the application.
252 ///
253 /// The Model that is returned by the function is the same model that will be passed to the
254 /// given event and view functions.
255 pub fn new(model: ModelFn<M>) -> Self {
256 Self::new_async(move |app| Box::new(future::ready(model(app))))
257 }
258
259 pub fn new_async(
260 model: impl FnOnce(&App) -> Box<dyn Future<Output = M> + '_> + 'static,
261 ) -> Self {
262 Builder {
263 model: Box::new(model),
264 config: Config::default(),
265 event: None,
266 update: None,
267 default_view: None,
268 exit: None,
269 create_default_window: false,
270 default_window_size: None,
271 max_capture_frame_jobs: None,
272 capture_frame_timeout: None,
273 backends: Self::DEFAULT_BACKENDS,
274 }
275 }
276
277 /// The function that the app will call to allow you to update your Model on events.
278 ///
279 /// The `event` function allows you to expect any event type that implements `LoopEvent`,
280 /// however nannou also provides a default `Event` type that should cover most use cases. This
281 /// event type is an `enum` that describes all the different kinds of I/O events that might
282 /// occur during the life of the program. These include things like `Update`s and
283 /// `WindowEvent`s such as `KeyPressed`, `MouseMoved`, and so on.
284 #[cfg_attr(rustfmt, rustfmt_skip)]
285 pub fn event<E>(self, event: EventFn<M, E>) -> Builder<M, E>
286 where
287 E: LoopEvent,
288 {
289 let Builder {
290 model,
291 config,
292 update,
293 default_view,
294 exit,
295 create_default_window,
296 default_window_size,
297 max_capture_frame_jobs,
298 capture_frame_timeout,
299 backends,
300 ..
301 } = self;
302 Builder {
303 model,
304 config,
305 event: Some(event),
306 update,
307 default_view,
308 exit,
309 create_default_window,
310 default_window_size,
311 max_capture_frame_jobs,
312 capture_frame_timeout,
313 backends
314 }
315 }
316}
317
318impl<M, E> Builder<M, E>
319where
320 M: 'static,
321 E: LoopEvent,
322{
323 /// By default, we timeout if waiting for a frame capture job takes longer than 5 seconds. This
324 /// is to avoid hanging forever in the case the frame writing process encounters an
325 /// unrecoverable error.
326 pub const DEFAULT_CAPTURE_FRAME_TIMEOUT: Duration = Duration::from_secs(10);
327
328 /// The default `view` function that the app will call to allow you to present your Model to
329 /// the surface of a window on your display.
330 ///
331 /// This function will be used in the case that a window-specific view function has not been
332 /// provided, e.g. via `window::Builder::view` or `window::Builder::sketch`.
333 ///
334 /// Note that when working with more than one window, you can use `frame.window_id()` to
335 /// determine which window the current call is associated with.
336 pub fn view(mut self, view: ViewFn<M>) -> Self {
337 self.default_view = Some(View::WithModel(view));
338 self
339 }
340
341 /// A function for updating the model within the application loop.
342 ///
343 /// See the `LoopMode` documentation for more information about the different kinds of
344 /// application loop modes available in nannou and how they behave.
345 ///
346 /// Update events are also emitted as a variant of the `event` function. Note that if you
347 /// specify both an `event` function and an `update` function, the `event` function will always
348 /// be called with an update event prior to this `update` function.
349 pub fn update(mut self, update: UpdateFn<M>) -> Self {
350 self.update = Some(update);
351 self
352 }
353
354 /// Tell the app that you would like it to create a single, simple, default window just before
355 /// it calls your model function.
356 ///
357 /// The given `view` function will play the same role as if passed to the `view` builder
358 /// method. Note that the `view` function passed to this method will overwrite any pre-existing
359 /// view function specified by any preceding call to the `view`
360 ///
361 /// Note that calling this multiple times will not give you multiple windows, but instead will
362 /// simply overwrite pre-existing calls to the method. If you would like to create multiple
363 /// windows or would like more flexibility in your window creation process, please see the
364 /// `App::new_window` method. The role of this `simple_window` method is to provide a
365 /// quick-and-easy way to start with a simple window. This can be very useful for quick ideas,
366 /// small single-window applications and examples.
367 pub fn simple_window(mut self, view: ViewFn<M>) -> Self {
368 self.default_view = Some(View::WithModel(view));
369 self.create_default_window = true;
370 self
371 }
372
373 /// Specify an `exit` function to be called when the application exits.
374 ///
375 /// The exit function gives ownership of the model back to you for any cleanup that might be
376 /// necessary.
377 pub fn exit(mut self, exit: ExitFn<M>) -> Self {
378 self.exit = Some(exit);
379 self
380 }
381
382 /// Specify the default window size in points.
383 ///
384 /// If a window is created and its size is not specified, this size will be used.
385 pub fn size(mut self, width: u32, height: u32) -> Self {
386 let size = winit::dpi::LogicalSize { width, height };
387 self.default_window_size = Some(DefaultWindowSize::Logical(size));
388 self
389 }
390
391 /// Specify that windows should be created on the primary monitor by default.
392 pub fn fullscreen(mut self) -> Self {
393 self.default_window_size = Some(DefaultWindowSize::Fullscreen);
394 self
395 }
396
397 /// Specify the default initial loop mode for this app.
398 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
399 self.config.loop_mode = mode;
400 self
401 }
402
403 /// The maximum number of simultaneous capture frame jobs that can be run per window before we
404 /// block and wait for the existing jobs to complete.
405 ///
406 /// A "capture frame job" refers to the combind process of waiting to read a frame from the GPU
407 /// and then writing that frame to an image file on the disk. Each call to
408 /// `window.capture_frame(path)` spawns a new "capture frame job" on an internal thread pool.
409 ///
410 /// By default, this value is equal to the number of physical cpu threads available on the
411 /// system. However, keep in mind that this means there must be room in both RAM and VRAM for
412 /// this number of textures to exist per window at any moment in time. If you run into an "out
413 /// of memory" error, try reducing the number of max jobs to a lower value, though never lower
414 /// than `1`.
415 ///
416 /// **Panics** if the specified value is less than `1`.
417 pub fn max_capture_frame_jobs(mut self, max_jobs: u32) -> Self {
418 assert!(
419 max_jobs >= 1,
420 "must allow for at least one capture frame job at a time"
421 );
422 self.max_capture_frame_jobs = Some(max_jobs);
423 self
424 }
425
426 /// In the case that `max_capture_frame_jobs` is reached and the main thread must block, this
427 /// specifies how long to wait for a running capture job to complete. See the
428 /// `max_capture_frame_jobs` docs for more details.
429 ///
430 /// By default, the timeout used is equal to `app::Builder::DEFAULT_CAPTURE_FRAME_TIMEOUT`.
431 ///
432 /// If `None` is specified, the capture process will never time out. This may be necessary on
433 /// extremely low-powered machines that take a long time to write each frame to disk.
434 pub fn capture_frame_timeout(mut self, timeout: Option<std::time::Duration>) -> Self {
435 self.capture_frame_timeout = Some(timeout);
436 self
437 }
438
439 /// Specify the set of preferred WGPU backends.
440 ///
441 /// By default, this is `wgpu::Backends::PRIMARY | wgpu::Backends::GL`.
442 pub fn backends(mut self, backends: wgpu::Backends) -> Self {
443 self.backends = backends;
444 self
445 }
446
447 /// Build and run an `App` with the specified parameters.
448 ///
449 /// This function will not return until the application has exited.
450 ///
451 /// If you wish to remain cross-platform friendly, we recommend that you call this on the main
452 /// thread as some platforms require that their application event loop and windows are
453 /// initialised on the main thread.
454 pub fn run(self) {
455 let rt = Self::build_runtime();
456 rt.block_on(self.run_async())
457 }
458
459 #[cfg(not(target_arch = "wasm32"))]
460 fn build_runtime() -> tokio::runtime::Runtime {
461 tokio::runtime::Builder::new_multi_thread()
462 .enable_all()
463 .build()
464 .expect("failed to create tokio runtime")
465 }
466
467 #[cfg(target_arch = "wasm32")]
468 fn build_runtime() -> tokio::runtime::Runtime {
469 tokio::runtime::Builder::new_current_thread()
470 .enable_all()
471 .build()
472 .expect("failed to create tokio runtime")
473 }
474
475 pub async fn run_async(self) {
476 // Start the winit window event loop.
477 let event_loop = winit::event_loop::EventLoop::new();
478
479 // Create the proxy used to awaken the event loop.
480 let event_loop_proxy = event_loop.create_proxy();
481 let wakeup_queued = Arc::new(AtomicBool::new(false));
482 let event_loop_proxy = Proxy {
483 event_loop_proxy,
484 wakeup_queued,
485 };
486
487 // Initialise the app.
488 let max_capture_frame_jobs = self
489 .max_capture_frame_jobs
490 .unwrap_or(num_cpus::get() as u32);
491 let capture_frame_timeout = self
492 .capture_frame_timeout
493 .unwrap_or(Some(Self::DEFAULT_CAPTURE_FRAME_TIMEOUT));
494 let event_loop_window_target = Some(EventLoopWindowTarget::Owned(event_loop));
495 let app = App::new(
496 self.config,
497 event_loop_proxy,
498 event_loop_window_target,
499 self.default_window_size,
500 max_capture_frame_jobs,
501 capture_frame_timeout,
502 self.backends,
503 );
504
505 // Create the default window if necessary
506 if self.create_default_window {
507 let window_id = app
508 .new_window()
509 .build_async()
510 .await
511 .expect("could not build default app window");
512 *app.focused_window.borrow_mut() = Some(window_id);
513 }
514
515 // Call the user's model function.
516 let model = Pin::from((self.model)(&app)).await;
517
518 // If there is not yet some default window in "focus" check to see if one has been created.
519 if app.focused_window.borrow().is_none() {
520 if let Some(id) = app.windows.borrow().keys().next() {
521 *app.focused_window.borrow_mut() = Some(id.clone());
522 }
523 }
524
525 run_loop(
526 app,
527 model,
528 self.event,
529 self.update,
530 self.default_view,
531 self.exit,
532 );
533 }
534}
535
536impl<E> SketchBuilder<E>
537where
538 E: LoopEvent,
539{
540 /// Specify the default initial loop mode for this sketch.
541 ///
542 /// This method delegates to `Builder::loop_mode`.
543 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
544 self.builder = self.builder.loop_mode(mode);
545 self
546 }
547
548 /// The size of the sketch window.
549 pub fn size(mut self, width: u32, height: u32) -> Self {
550 self.builder = self.builder.size(width, height);
551 self
552 }
553
554 /// Build and run a `Sketch` with the specified parameters.
555 ///
556 /// This calls `App::run` internally. See that method for details!
557 pub fn run(self) {
558 self.builder.run()
559 }
560}
561
562impl Builder<(), Event> {
563 /// Shorthand for building a simple app that has no model, handles no events and simply draws
564 /// to a single window.
565 ///
566 /// This is useful for late night hack sessions where you just don't care about all that other
567 /// stuff, you just want to play around with some ideas or make something pretty.
568 pub fn sketch(view: SketchViewFn) -> SketchBuilder<Event> {
569 let mut builder = Builder::new(default_model);
570 builder.default_view = Some(View::Sketch(view));
571 builder.create_default_window = true;
572 SketchBuilder { builder }
573 }
574}
575
576/// Given some "frames per second", return the interval between frames as a `Duration`.
577fn update_interval(fps: f64) -> Duration {
578 assert!(fps > 0.0);
579 const NANOSEC_PER_SEC: f64 = 1_000_000_000.0;
580 let interval_nanosecs = NANOSEC_PER_SEC / fps;
581 let secs = (interval_nanosecs / NANOSEC_PER_SEC) as u64;
582 let nanosecs = (interval_nanosecs % NANOSEC_PER_SEC) as u32;
583 Duration::new(secs, nanosecs)
584}
585
586impl LoopMode {
587 pub const DEFAULT_RATE_FPS: f64 = 60.0;
588 /// The minimum number of updates that will be emitted after an event is triggered in Wait
589 /// mode.
590 pub const UPDATES_PER_WAIT_EVENT: u32 = 3;
591
592 /// A simplified constructor for the default `RefreshSync` loop mode.
593 ///
594 /// Assumes a display refresh rate of ~60hz and in turn specifies a `minimum_update_latency` of
595 /// ~8.33ms. The `windows` field is set to `None`.
596 pub fn refresh_sync() -> Self {
597 LoopMode::RefreshSync
598 }
599
600 /// Specify the **Rate** mode with the given frames-per-second.
601 pub fn rate_fps(fps: f64) -> Self {
602 let update_interval = update_interval(fps);
603 LoopMode::Rate { update_interval }
604 }
605
606 /// Specify the **Wait** mode.
607 pub fn wait() -> Self {
608 LoopMode::Wait
609 }
610
611 /// Specify the **Ntimes** mode with one update
612 ///
613 /// Waits long enough to ensure loop iteration never occurs faster than the given `max_fps`.
614 pub fn loop_ntimes(number_of_updates: usize) -> Self {
615 LoopMode::NTimes { number_of_updates }
616 }
617
618 /// Specify the **Ntimes** mode with one update
619 pub fn loop_once() -> Self {
620 Self::loop_ntimes(1)
621 }
622}
623
624impl Default for LoopMode {
625 fn default() -> Self {
626 LoopMode::refresh_sync()
627 }
628}
629
630impl Default for Config {
631 fn default() -> Self {
632 let loop_mode = Default::default();
633 let exit_on_escape = App::DEFAULT_EXIT_ON_ESCAPE;
634 let fullscreen_on_shortcut = App::DEFAULT_FULLSCREEN_ON_SHORTCUT;
635 Config {
636 loop_mode,
637 exit_on_escape,
638 fullscreen_on_shortcut,
639 }
640 }
641}
642
643impl App {
644 pub const ASSETS_DIRECTORY_NAME: &'static str = "assets";
645 pub const DEFAULT_EXIT_ON_ESCAPE: bool = true;
646 pub const DEFAULT_FULLSCREEN_ON_SHORTCUT: bool = true;
647
648 // Create a new `App`.
649 fn new(
650 config: Config,
651 event_loop_proxy: Proxy,
652 event_loop_window_target: Option<EventLoopWindowTarget>,
653 default_window_size: Option<DefaultWindowSize>,
654 max_capture_frame_jobs: u32,
655 capture_frame_timeout: Option<Duration>,
656 backends: wgpu::Backends,
657 ) -> Self {
658 let instance = wgpu::Instance::new(InstanceDescriptor {
659 backends,
660 ..Default::default()
661 });
662 let adapters = Default::default();
663 let windows = RefCell::new(HashMap::new());
664 let draw = RefCell::new(draw::Draw::default());
665 let config = RefCell::new(config);
666 let renderers = RefCell::new(Default::default());
667 let draw_state = DrawState { draw, renderers };
668 let focused_window = RefCell::new(None);
669 let mouse = state::Mouse::new();
670 let keys = state::Keys::default();
671 let duration = state::Time::default();
672 let time = duration.since_start.secs() as _;
673 let app = App {
674 event_loop_proxy,
675 event_loop_window_target,
676 default_window_size,
677 max_capture_frame_jobs,
678 capture_frame_timeout,
679 focused_window,
680 backends,
681 instance,
682 adapters,
683 windows,
684 config,
685 draw_state,
686 mouse,
687 keys,
688 duration,
689 time,
690 };
691 app
692 }
693
694 /// Returns the list of all the monitors available on the system.
695 pub fn available_monitors(&self) -> Vec<winit::monitor::MonitorHandle> {
696 match self.event_loop_window_target {
697 Some(EventLoopWindowTarget::Owned(ref event_loop)) => {
698 event_loop.available_monitors().collect()
699 }
700 _ => {
701 let windows = self.windows.borrow();
702 match windows.values().next() {
703 None => vec![],
704 Some(window) => window.window.available_monitors().collect(),
705 }
706 }
707 }
708 }
709
710 /// Returns the primary monitor of the system.
711 /// May return None if none can be detected. For example, this can happen when running on Linux
712 /// with Wayland.
713 pub fn primary_monitor(&self) -> Option<winit::monitor::MonitorHandle> {
714 match self.event_loop_window_target {
715 Some(EventLoopWindowTarget::Owned(ref event_loop)) => event_loop.primary_monitor(),
716 _ => {
717 let windows = self.windows.borrow();
718 match windows.values().next() {
719 None => unimplemented!(
720 "yet to implement a way to get `primary_monitor` if neither \
721 event loop or window can be safely accessed"
722 ),
723 Some(window) => window.window.primary_monitor(),
724 }
725 }
726 }
727 }
728
729 /// Find and return the absolute path to the project's `assets` directory.
730 ///
731 /// This method looks for the assets directory in the following order:
732 ///
733 /// 1. Checks the same directory as the executable.
734 /// 2. Recursively checks exe's parent directories (to a max depth of 5).
735 /// 3. Recursively checks exe's children directories (to a max depth of 3).
736 pub fn assets_path(&self) -> Result<PathBuf, find_folder::Error> {
737 find_assets_path()
738 }
739
740 /// The path to the current project directory.
741 ///
742 /// The current project directory is considered to be the directory containing the cargo
743 /// manifest (aka the `Cargo.toml` file).
744 ///
745 /// **Note:** Be careful not to rely on this directory for apps or sketches that you wish to
746 /// distribute! This directory is mostly useful for local sketches, experiments and testing.
747 pub fn project_path(&self) -> Result<PathBuf, find_folder::Error> {
748 find_project_path()
749 }
750
751 /// Begin building a new window.
752 pub fn new_window(&self) -> window::Builder {
753 let builder = window::Builder::new(self);
754 let builder = match self.default_window_size {
755 Some(DefaultWindowSize::Fullscreen) => builder.fullscreen(),
756 Some(DefaultWindowSize::Logical(size)) => builder.size(size.width, size.height),
757 None => builder,
758 };
759 builder
760 .max_capture_frame_jobs(self.max_capture_frame_jobs)
761 .capture_frame_timeout(self.capture_frame_timeout)
762 }
763
764 /// The number of windows currently in the application.
765 pub fn window_count(&self) -> usize {
766 self.windows.borrow().len()
767 }
768
769 /// A reference to the window with the given `Id`.
770 pub fn window(&self, id: window::Id) -> Option<std::cell::Ref<Window>> {
771 let windows = self.windows.borrow();
772 if !windows.contains_key(&id) {
773 None
774 } else {
775 Some(std::cell::Ref::map(windows, |ws| &ws[&id]))
776 }
777 }
778
779 /// Return the **Id** of the currently focused window.
780 ///
781 /// **Panics** if there are no windows or if no window is in focus.
782 pub fn window_id(&self) -> window::Id {
783 self.focused_window
784 .borrow()
785 .expect("called `App::window_id` but there is no window currently in focus")
786 }
787
788 /// Return a `Vec` containing a unique `window::Id` for each currently open window managed by
789 /// the `App`.
790 pub fn window_ids(&self) -> Vec<window::Id> {
791 let windows = self.windows.borrow();
792 windows.keys().cloned().collect()
793 }
794
795 /// Return the **Rect** for the currently focused window.
796 ///
797 /// The **Rect** coords are described in "points" (pixels divided by the hidpi factor).
798 ///
799 /// **Panics** if there are no windows or if no window is in focus.
800 pub fn window_rect(&self) -> geom::Rect<f32> {
801 self.main_window().rect()
802 }
803
804 /// A reference to the window currently in focus.
805 ///
806 /// **Panics** if their are no windows open in the **App**.
807 ///
808 /// Uses the **App::window** method internally.
809 ///
810 /// TODO: Currently this produces a reference to the *focused* window, but this behaviour
811 /// should be changed to track the "main" window (the first window created?).
812 pub fn main_window(&self) -> std::cell::Ref<Window> {
813 self.window(self.window_id())
814 .expect("no window for focused id")
815 }
816
817 /// Return the wgpu `Backends` in use.
818 pub fn backends(&self) -> wgpu::Backends {
819 self.backends
820 }
821
822 /// Return the main wgpu `Instance` in use.
823 ///
824 /// This must be passed into the various methods on `AdapterMap`.
825 pub fn instance(&self) -> &wgpu::Instance {
826 &self.instance
827 }
828
829 /// Access to the **App**'s inner map of wgpu adapters representing access to physical GPU
830 /// devices.
831 ///
832 /// By maintaining a map of active adapters and their established devices, nannou allows for
833 /// devices to be shared based on the desired `RequestAdapterOptions` and `DeviceDescriptor`s.
834 ///
835 /// For example, when creating new windows with the same set of `RequestAdapterOptions` and
836 /// `DeviceDescriptor`s, nannou will automatically share devices between windows where
837 /// possible. This allows for sharing GPU resources like **Texture**s and **Buffer**s between
838 /// windows.
839 ///
840 /// All methods on `AdapterMap` that take a `wgpu::Instance` must be passed the main instance
841 /// in use by the app, accessed via `App::instance()`.
842 pub fn wgpu_adapters(&self) -> &wgpu::AdapterMap {
843 &self.adapters
844 }
845
846 /// Return whether or not the `App` is currently set to exit when the `Escape` key is pressed.
847 pub fn exit_on_escape(&self) -> bool {
848 self.config.borrow().exit_on_escape
849 }
850
851 /// Specify whether or not the app should close when the `Escape` key is pressed.
852 ///
853 /// By default this is `true`.
854 pub fn set_exit_on_escape(&self, b: bool) {
855 self.config.borrow_mut().exit_on_escape = b;
856 }
857
858 /// Returns whether or not the `App` is currently allows the focused window to enter or exit
859 /// fullscreen via typical platform-specific shortcuts.
860 ///
861 /// - Linux uses F11.
862 /// - macOS uses apple key + f.
863 /// - Windows uses windows key + f.
864 pub fn fullscreen_on_shortcut(&self) -> bool {
865 self.config.borrow().fullscreen_on_shortcut
866 }
867
868 /// Set whether or not the `App` should allow the focused window to enter or exit fullscreen
869 /// via typical platform-specific shortcuts.
870 ///
871 /// - Linux uses F11.
872 /// - macOS uses apple key + f.
873 /// - Windows uses windows key + f.
874 pub fn set_fullscreen_on_shortcut(&self, b: bool) {
875 self.config.borrow_mut().fullscreen_on_shortcut = b;
876 }
877
878 /// Returns the **App**'s current **LoopMode**.
879 ///
880 /// The default loop mode is `LoopMode::RefreshSync`.
881 pub fn loop_mode(&self) -> LoopMode {
882 self.config.borrow().loop_mode.clone()
883 }
884
885 /// Sets the loop mode of the **App**.
886 ///
887 /// Note: Setting the loop mode will not affect anything until the end of the current loop
888 /// iteration. The behaviour of a single loop iteration is described under each of the
889 /// **LoopMode** variants.
890 pub fn set_loop_mode(&self, mode: LoopMode) {
891 self.config.borrow_mut().loop_mode = mode;
892 }
893
894 /// A handle to the **App** that can be shared across threads.
895 ///
896 /// This can be used to "wake up" the **App**'s inner event loop.
897 pub fn create_proxy(&self) -> Proxy {
898 self.event_loop_proxy.clone()
899 }
900
901 /// Produce the **App**'s **Draw** API for drawing geometry and text with colors and textures.
902 ///
903 /// **Note:** You can also create your own **Draw** instances via `Draw::new()`! This method
904 /// makes it a tiny bit easier as the **App** stores the **Draw** instance for you and
905 /// automatically resets the state on each call to `app.draw()`.
906 pub fn draw(&self) -> draw::Draw {
907 let draw = self.draw_state.draw.borrow_mut();
908 draw.reset();
909 draw.clone()
910 }
911
912 /// The number of times the focused window's **view** function has been called since the start
913 /// of the program.
914 pub fn elapsed_frames(&self) -> u64 {
915 self.main_window().frame_count
916 }
917
918 /// The number of frames that can currently be displayed a second
919 pub fn fps(&self) -> f32 {
920 self.duration.updates_per_second()
921 }
922
923 /// The name of the nannou executable that is currently running.
924 pub fn exe_name(&self) -> std::io::Result<String> {
925 let string = std::env::current_exe()?
926 .file_stem()
927 .expect("exe path contained no file stem")
928 .to_string_lossy()
929 .to_string();
930 Ok(string)
931 }
932
933 /// Quits the currently running application.
934 pub fn quit(&self) {
935 self.windows.borrow_mut().clear();
936 }
937}
938
939impl Proxy {
940 /// Wake up the application!
941 ///
942 /// This wakes up the **App**'s inner event loop and causes a user event to be emitted by the
943 /// event loop.
944 ///
945 /// The `app::Proxy` stores a flag in order to track whether or not the `EventLoop` is
946 /// currently blocking and waiting for events. This method will only call the underlying
947 /// `winit::event_loop::EventLoopProxy::send_event` method if this flag is set to true and will
948 /// immediately set the flag to false afterwards. This makes it safe to call the `wakeup`
949 /// method as frequently as necessary across methods without causing any underlying OS methods
950 /// to be called more than necessary.
951 pub fn wakeup(&self) -> Result<(), winit::event_loop::EventLoopClosed<()>> {
952 if !self.wakeup_queued.load(atomic::Ordering::SeqCst) {
953 self.event_loop_proxy.send_event(())?;
954 self.wakeup_queued.store(true, atomic::Ordering::SeqCst);
955 }
956 Ok(())
957 }
958}
959
960impl draw::Draw {
961 /// Render the **Draw**'s inner list of commands to the texture associated with the **Frame**.
962 ///
963 /// The **App** stores a unique render.
964 pub fn to_frame(&self, app: &App, frame: &Frame) -> Result<(), draw::renderer::DrawError> {
965 let window_id = frame.window_id();
966 let window = app
967 .window(window_id)
968 .expect("no window to draw to for `Draw`'s window_id");
969
970 // Retrieve a renderer for this window.
971 let renderers = app.draw_state.renderers.borrow_mut();
972 let renderer = RefMut::map(renderers, |renderers| {
973 renderers.entry(window_id).or_insert_with(|| {
974 let device = window.device();
975 let frame_dims: [u32; 2] = window.tracked_state.physical_size.into();
976 let scale_factor = window.tracked_state.scale_factor as f32;
977 let msaa_samples = window.msaa_samples();
978 let target_format = crate::frame::Frame::TEXTURE_FORMAT;
979 let renderer = draw::RendererBuilder::new().build(
980 device,
981 frame_dims,
982 scale_factor,
983 msaa_samples,
984 target_format,
985 );
986 RefCell::new(renderer)
987 })
988 });
989
990 let scale_factor = window.tracked_state.scale_factor as _;
991 let mut renderer = renderer.borrow_mut();
992 renderer.render_to_frame(window.device(), self, scale_factor, frame);
993 Ok(())
994 }
995}
996
997impl<'a> wgpu::WithDeviceQueuePair for &'a crate::app::App {
998 fn with_device_queue_pair<F, O>(self, f: F) -> O
999 where
1000 F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O,
1001 {
1002 self.main_window().with_device_queue_pair(f)
1003 }
1004}
1005
1006/// Attempt to find the assets directory path relative to the executable location.
1007pub fn find_assets_path() -> Result<PathBuf, find_folder::Error> {
1008 let exe_path = std::env::current_exe()?;
1009 find_folder::Search::ParentsThenKids(5, 3)
1010 .of(exe_path
1011 .parent()
1012 .expect("executable has no parent directory to search")
1013 .into())
1014 .for_folder(App::ASSETS_DIRECTORY_NAME)
1015}
1016
1017/// Attempt to find the assets directory path relative to the executable location.
1018pub fn find_project_path() -> Result<PathBuf, find_folder::Error> {
1019 let exe_path = std::env::current_exe()?;
1020 let mut path = exe_path.parent().expect("exe has no parent directory");
1021 while let Some(parent) = path.parent() {
1022 path = parent;
1023 if path.join("Cargo").with_extension("toml").exists() {
1024 return Ok(path.to_path_buf());
1025 }
1026 }
1027 Err(find_folder::Error::NotFound)
1028}
1029
1030// This type allows the `App` to provide an API for creating new windows.
1031//
1032// During the `setup` before the
1033pub(crate) enum EventLoopWindowTarget {
1034 // Ownership over the event loop.
1035 //
1036 // This is the state before the `EventLoop::run` begins.
1037 Owned(winit::event_loop::EventLoop<()>),
1038 // A pointer to the target for building windows.
1039 //
1040 // This is the state during `EventLoop::run`. This pointer becomes invalid following
1041 // `EventLoop::run`, so it is essential to take care that we are in the correct state when
1042 // using this pointer.
1043 Pointer(*const winit::event_loop::EventLoopWindowTarget<()>),
1044}
1045
1046impl EventLoopWindowTarget {
1047 // Take a reference to the inner event loop window target.
1048 //
1049 // This method is solely used during `window::Builder::build` to allow for
1050 pub(crate) fn as_ref(&self) -> &winit::event_loop::EventLoopWindowTarget<()> {
1051 match *self {
1052 EventLoopWindowTarget::Owned(ref event_loop) => &**event_loop,
1053 EventLoopWindowTarget::Pointer(ptr) => {
1054 // This cast is safe, assuming that the `App`'s `EventLoopWindowTarget` will only
1055 // ever be in the `Pointer` state while the pointer is valid - that is, during the
1056 // call to `EventLoop::run`. Great care is taken to ensure that the
1057 // `EventLoopWindowTarget` is dropped immediately after `EventLoop::run` completes.
1058 // This allows us to take care of abiding by the `EventLoopWindowTarget` lifetime
1059 // manually while avoiding having the lifetime propagate up through the `App` type.
1060 unsafe { &*ptr as &winit::event_loop::EventLoopWindowTarget<()> }
1061 }
1062 }
1063 }
1064}
1065
1066// Application Loop.
1067//
1068// Beyond this point lies the master function for running the main application loop!
1069//
1070// This is undoubtedly the hairiest part of nannou's code base. This is largely due to the fact
1071// that it is the part of nannou where we marry application and user input events, loop timing,
1072// updating the model, platform-specific quirks and warts, the various possible `LoopMode`s and
1073// wgpu interop.
1074//
1075// If you would like to contribute but are unsure about any of the following, feel free to open an
1076// issue and ask!
1077fn run_loop<M, E>(
1078 mut app: App,
1079 model: M,
1080 event_fn: Option<EventFn<M, E>>,
1081 update_fn: Option<UpdateFn<M>>,
1082 default_view: Option<View<M>>,
1083 exit_fn: Option<ExitFn<M>>,
1084) where
1085 M: 'static,
1086 E: LoopEvent,
1087{
1088 // Track the moment the loop starts.
1089 let loop_start = Instant::now();
1090
1091 // Wrap the `model` in an `Option`, allowing us to take full ownership within the `event_loop`
1092 // on `exit`.
1093 let mut model = Some(model);
1094
1095 // Take ownership of the `EventLoop` from the `App`.
1096 let event_loop = match app.event_loop_window_target.take() {
1097 Some(EventLoopWindowTarget::Owned(event_loop)) => event_loop,
1098 _ => unreachable!("the app should always own the event loop at this point"),
1099 };
1100
1101 // Keep track of state related to the loop mode itself.
1102 let mut loop_state = LoopState {
1103 updates_since_event: 0,
1104 loop_start,
1105 last_update: loop_start,
1106 total_updates: 0,
1107 };
1108
1109 // Run the event loop.
1110 event_loop.run(move |mut event, event_loop_window_target, control_flow| {
1111 // Set the event loop window target pointer to allow for building windows.
1112 app.event_loop_window_target = Some(EventLoopWindowTarget::Pointer(
1113 event_loop_window_target as *const _,
1114 ));
1115
1116 let mut exit = false;
1117
1118 match event {
1119 // Check to see if we need to emit an update and request a redraw.
1120 winit::event::Event::MainEventsCleared => {
1121 if let Some(model) = model.as_mut() {
1122 let loop_mode = app.loop_mode();
1123 let now = Instant::now();
1124 let mut do_update = |loop_state: &mut LoopState| {
1125 apply_update(&mut app, model, event_fn, update_fn, loop_state, now);
1126 };
1127 match loop_mode {
1128 LoopMode::NTimes { number_of_updates }
1129 if loop_state.total_updates >= number_of_updates as u64 => {}
1130 // Sometimes winit interrupts ControlFlow::Wait for no good reason, so we
1131 // make sure that there were some events in order to do an update when
1132 // LoopMode::Wait is used.
1133 LoopMode::Wait if loop_state.updates_since_event > 0 => {}
1134 // TODO: Consider allowing for a custom number of updates like so:
1135 // LoopMode::Wait { updates_before_waiting } =>
1136 // if loop_state.updates_since_event > updates_before_waiting as u64 => {}
1137 _ => {
1138 do_update(&mut loop_state);
1139 },
1140 }
1141 }
1142 }
1143
1144 // Request a frame from the user for the specified window.
1145 //
1146 // TODO: Only request a frame from the user if this redraw was requested following an
1147 // update. Otherwise, just use the existing intermediary frame.
1148 winit::event::Event::RedrawRequested(window_id) => {
1149 if let Some(model) = model.as_mut() {
1150 // Retrieve the surface frame and the number of this frame.
1151 // NOTE: We avoid mutably borrowing `windows` map any longer than necessary to
1152 // avoid restricting users from accessing `windows` during `view`.
1153 let (mut surface_tex_result, nth_frame) = {
1154 let mut windows = app.windows.borrow_mut();
1155 let window = windows
1156 .get_mut(&window_id)
1157 .expect("no window for `RedrawRequest`");
1158 let texture = window.surface.get_current_texture();
1159 let nth_frame = window.frame_count;
1160 (texture, nth_frame)
1161 };
1162
1163 if let Err(e) = &surface_tex_result {
1164 match e {
1165 // Sometimes redraws get delivered before resizes on x11 for unclear reasons.
1166 // It goes all the way down to the API: if you ask x11 about the window size
1167 // at this time, it'll tell you that it hasn't changed. So... we skip
1168 // this frame. The resize will show up in a bit and then we can get on
1169 // with our lives.
1170 // If you turn on debug logging this does occasionally cause some vulkan
1171 // validation errors... that's not great.
1172 // TODO find a better long-term fix than ignoring.
1173 wgpu::SurfaceError::Lost => {
1174 // Attempt to reconfigure the surface.
1175 let mut windows = app.windows.borrow_mut();
1176 let window = windows
1177 .get_mut(&window_id)
1178 .expect("no window for `RedrawRequest`");
1179 window
1180 .reconfigure_surface(window.tracked_state.physical_size.into());
1181 surface_tex_result = window.surface.get_current_texture();
1182 }
1183 wgpu::SurfaceError::Outdated => {} // skip frame
1184 wgpu::SurfaceError::Timeout => {} // skip frame
1185 wgpu::SurfaceError::OutOfMemory => {
1186 panic!("out of memory acquiring the surface frame: {}", e);
1187 }
1188 }
1189 }
1190
1191 if let Ok(surface_tex) = surface_tex_result {
1192 let surface_texture = &surface_tex
1193 .texture
1194 .create_view(&wgpu::TextureViewDescriptor::default());
1195
1196 // Borrow the window now that we don't need it mutably until setting the render
1197 // data back.
1198 let windows = app.windows.borrow();
1199 let window = windows
1200 .get(&window_id)
1201 .expect("failed to find window for redraw request");
1202 let frame_data = &window.frame_data;
1203
1204 // Construct and emit a frame via `view` for receiving the user's graphics commands.
1205 let sf = window.tracked_state.scale_factor;
1206 let (w, h) = window
1207 .tracked_state
1208 .physical_size
1209 .to_logical::<f32>(sf)
1210 .into();
1211 let window_rect = geom::Rect::from_w_h(w, h);
1212 let raw_frame = RawFrame::new_empty(
1213 window.device_queue_pair().clone(),
1214 window_id,
1215 nth_frame,
1216 surface_texture,
1217 window.surface_conf.format,
1218 window_rect,
1219 );
1220
1221 // Clear the raw frame immediately once the window is invalidated
1222 if window.is_invalidated {
1223 if let Some(data) = frame_data {
1224 raw_frame.clear(&data.render.texture_view(), window.clear_color);
1225 }
1226 }
1227
1228 // If the user specified a view function specifically for this window, use it.
1229 // Otherwise, use the fallback, default view passed to the app if there was one.
1230 let window_view = window.user_functions.view.clone();
1231
1232 match window_view {
1233 Some(window::View::Sketch(view)) => {
1234 let data = frame_data.as_ref().expect("missing `frame_data`");
1235 let frame =
1236 Frame::new_empty(raw_frame, &data.render, &data.capture);
1237 view(&app, frame);
1238 }
1239 Some(window::View::WithModel(view)) => {
1240 let data = frame_data.as_ref().expect("missing `frame_data`");
1241 let frame =
1242 Frame::new_empty(raw_frame, &data.render, &data.capture);
1243 let view = view.to_fn_ptr::<M>().expect(
1244 "unexpected model argument given to window view function",
1245 );
1246 (*view)(&app, model, frame);
1247 }
1248 Some(window::View::WithModelRaw(raw_view)) => {
1249 let raw_view = raw_view.to_fn_ptr::<M>().expect(
1250 "unexpected model argument given to window raw_view function",
1251 );
1252 (*raw_view)(&app, &model, raw_frame);
1253 }
1254 None => match default_view {
1255 Some(View::Sketch(view)) => {
1256 let data = frame_data.as_ref().expect("missing `frame_data`");
1257 let frame =
1258 Frame::new_empty(raw_frame, &data.render, &data.capture);
1259 view(&app, frame);
1260 }
1261 Some(View::WithModel(view)) => {
1262 let data = frame_data.as_ref().expect("missing `frame_data`");
1263 let frame =
1264 Frame::new_empty(raw_frame, &data.render, &data.capture);
1265 view(&app, &model, frame);
1266 }
1267 None => raw_frame.submit(),
1268 },
1269 }
1270
1271 // Queue has been submitted by now, time to present.
1272 surface_tex.present();
1273
1274 // Release immutable lock
1275 drop(windows);
1276
1277 // Increment the window's frame count.
1278 let mut windows = app.windows.borrow_mut();
1279 let window = windows
1280 .get_mut(&window_id)
1281 .expect("no window for redraw request ID");
1282
1283 // Assume invalidated window was cleared above before `view()`
1284 window.is_invalidated = false;
1285 window.frame_count += 1;
1286 }
1287 }
1288 }
1289
1290 // Clear any inactive adapters and devices and poll those remaining.
1291 winit::event::Event::RedrawEventsCleared => {
1292 app.wgpu_adapters().clear_inactive_adapters_and_devices();
1293 // TODO: This seems to cause some glitching and slows down macOS drastically.
1294 // While not necessary, this would be nice to have to automatically process async
1295 // read/write callbacks submitted by users who aren't aware that they need to poll
1296 // their devices in order to make them do work. Perhaps as a workaround we could
1297 // only poll devices that aren't already associated with a window?
1298 //app.wgpu_adapters().poll_all_devices(false);
1299 }
1300
1301 // For all window, device and user (app proxy) events reset the `updates_since_event`
1302 // count which is used to improve behaviour for the `Wait` loop mode.
1303 // TODO: Document this set of events under `LoopMode::Wait`.
1304 winit::event::Event::WindowEvent { .. }
1305 | winit::event::Event::DeviceEvent { .. }
1306 | winit::event::Event::UserEvent(_)
1307 | winit::event::Event::Suspended
1308 | winit::event::Event::Resumed => {
1309 loop_state.updates_since_event = 0;
1310
1311 // `UserEvent` is emitted on `wakeup`.
1312 if let winit::event::Event::UserEvent(_) = event {
1313 app.event_loop_proxy.wakeup_queued.store(false, atomic::Ordering::SeqCst);
1314 }
1315 }
1316
1317 // Ignore `NewEvents`.
1318 winit::event::Event::NewEvents(_)
1319 // `LoopDestroyed` is handled later in `process_and_emit_winit_event` so ignore it here.
1320 | winit::event::Event::LoopDestroyed => {}
1321 }
1322
1323 // We must reconfigure the wgpu surface if the window was resized.
1324 if let winit::event::Event::WindowEvent {
1325 ref mut event,
1326 window_id,
1327 } = event
1328 {
1329 match event {
1330 winit::event::WindowEvent::Resized(new_inner_size) => {
1331 let mut windows = app.windows.borrow_mut();
1332 if let Some(window) = windows.get_mut(&window_id) {
1333 window.reconfigure_surface(new_inner_size.clone().into());
1334 }
1335 }
1336
1337 winit::event::WindowEvent::ScaleFactorChanged {
1338 scale_factor,
1339 new_inner_size,
1340 } => {
1341 let mut windows = app.windows.borrow_mut();
1342 if let Some(window) = windows.get_mut(&window_id) {
1343 window.tracked_state.scale_factor = *scale_factor;
1344 window.reconfigure_surface(new_inner_size.clone().into());
1345 }
1346 }
1347
1348 _ => (),
1349 }
1350 }
1351
1352 // Process the event with the user's functions and see if we need to exit.
1353 if let Some(model) = model.as_mut() {
1354 exit |= process_and_emit_winit_event::<M, E>(&mut app, model, event_fn, &event);
1355 }
1356
1357 // Set the control flow based on the loop mode.
1358 let loop_mode = app.loop_mode();
1359 *control_flow = match loop_mode {
1360 LoopMode::Wait => ControlFlow::Wait,
1361 LoopMode::NTimes { number_of_updates }
1362 if loop_state.total_updates >= number_of_updates as u64 =>
1363 {
1364 ControlFlow::Wait
1365 }
1366 _ => ControlFlow::Poll,
1367 };
1368
1369 // If we need to exit, call the user's function and update control flow.
1370 if exit {
1371 if let Some(model) = model.take() {
1372 if let Some(exit_fn) = exit_fn {
1373 exit_fn(&app, model);
1374 }
1375 }
1376
1377 *control_flow = ControlFlow::Exit;
1378 return;
1379 }
1380 });
1381
1382 // Ensure the app no longer points to the window target now that `run` has completed.
1383 // TODO: Right now `event_loop.run` can't return. This is just a reminder in case one day the
1384 // API is changed so that it does return.
1385 #[allow(unreachable_code)]
1386 {
1387 app.event_loop_window_target.take();
1388 }
1389}
1390
1391// Apply an update to the model via the user's function and update the app and loop state
1392// accordingly.
1393fn apply_update<M, E>(
1394 app: &mut App,
1395 model: &mut M,
1396 event_fn: Option<EventFn<M, E>>,
1397 update_fn: Option<UpdateFn<M>>,
1398 loop_state: &mut LoopState,
1399 now: Instant,
1400) where
1401 M: 'static,
1402 E: LoopEvent,
1403{
1404 // Update the app's durations.
1405 let since_last = now.duration_since(loop_state.last_update);
1406 let since_start = now.duration_since(loop_state.loop_start);
1407 app.duration.since_prev_update = since_last;
1408 app.duration.since_start = since_start;
1409 app.time = since_start.secs() as _;
1410 let update = crate::event::Update {
1411 since_start,
1412 since_last,
1413 };
1414 // User event function.
1415 if let Some(event_fn) = event_fn {
1416 let event = E::from(update.clone());
1417 event_fn(app, model, event);
1418 }
1419 // User update function.
1420 if let Some(update_fn) = update_fn {
1421 update_fn(app, model, update);
1422 }
1423 loop_state.last_update = now;
1424 loop_state.total_updates += 1;
1425 loop_state.updates_since_event += 1;
1426 // Request redraw from windows.
1427 let windows = app.windows.borrow();
1428 for window in windows.values() {
1429 window.window.request_redraw();
1430 }
1431}
1432
1433// Whether or not the given event should toggle fullscreen.
1434fn should_toggle_fullscreen(
1435 winit_event: &winit::event::WindowEvent,
1436 mods: &winit::event::ModifiersState,
1437) -> bool {
1438 let input = match *winit_event {
1439 winit::event::WindowEvent::KeyboardInput { ref input, .. } => match input.state {
1440 event::ElementState::Pressed => input,
1441 _ => return false,
1442 },
1443 _ => return false,
1444 };
1445
1446 let key = match input.virtual_keycode {
1447 None => return false,
1448 Some(k) => k,
1449 };
1450
1451 // On linux, check for the F11 key (with no modifiers down).
1452 //
1453 // TODO: Somehow add special case for KDE?
1454 if cfg!(target_os = "linux") {
1455 if *mods == winit::event::ModifiersState::empty() {
1456 if let Key::F11 = key {
1457 return true;
1458 }
1459 }
1460
1461 // On macos and windows check for the logo key plus `f` with no other modifiers.
1462 } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1463 if mods.logo() {
1464 if let Key::F = key {
1465 return true;
1466 }
1467 }
1468 }
1469
1470 false
1471}
1472
1473// Event handling boilerplate shared between the loop modes.
1474//
1475// 1. Checks for exit on escape.
1476// 2. Removes closed windows from app.
1477// 3. Emits event via `event_fn`.
1478// 4. Returns whether or not we should break from the loop.
1479fn process_and_emit_winit_event<'a, M, E>(
1480 app: &mut App,
1481 model: &mut M,
1482 event_fn: Option<EventFn<M, E>>,
1483 winit_event: &winit::event::Event<'a, ()>,
1484) -> bool
1485where
1486 M: 'static,
1487 E: LoopEvent,
1488{
1489 // Inspect the event to see if it would require closing the App.
1490 let mut exit_on_escape = false;
1491 let mut removed_window = None;
1492 if let winit::event::Event::WindowEvent {
1493 window_id,
1494 ref event,
1495 } = *winit_event
1496 {
1497 // If we should exit the app on escape, check for the escape key.
1498 if app.exit_on_escape() {
1499 if let winit::event::WindowEvent::KeyboardInput { input, .. } = *event {
1500 if let Some(Key::Escape) = input.virtual_keycode {
1501 exit_on_escape = true;
1502 }
1503 }
1504 }
1505
1506 // When a window has been closed, this function is called to remove any state associated
1507 // with that window so that the state doesn't leak.
1508 //
1509 // Returns the `Window` that was removed.
1510 fn remove_related_window_state(app: &App, window_id: &window::Id) -> Option<Window> {
1511 app.draw_state.renderers.borrow_mut().remove(window_id);
1512 app.windows.borrow_mut().remove(window_id)
1513 }
1514
1515 if let winit::event::WindowEvent::Destroyed = *event {
1516 removed_window = remove_related_window_state(app, &window_id);
1517 // TODO: We should allow the user to handle this case. E.g. allow for doing things like
1518 // "would you like to save". We currently do this with the app exit function, but maybe a
1519 // window `close` function would be useful?
1520 } else if let winit::event::WindowEvent::CloseRequested = *event {
1521 removed_window = remove_related_window_state(app, &window_id);
1522 } else {
1523 // Get the size of the window for translating coords and dimensions.
1524 let (win_w, win_h, scale_factor) = match app.window(window_id) {
1525 Some(win) => {
1526 // If we should toggle fullscreen for this window, do so.
1527 if app.fullscreen_on_shortcut() {
1528 if should_toggle_fullscreen(event, &app.keys.mods) {
1529 if win.is_fullscreen() {
1530 win.set_fullscreen(false);
1531 } else {
1532 win.set_fullscreen(true);
1533 }
1534 }
1535 }
1536
1537 let sf = win.tracked_state.scale_factor;
1538 let (w, h) = win.tracked_state.physical_size.to_logical::<f32>(sf).into();
1539 (w, h, sf)
1540 }
1541 None => (0.0, 0.0, 1.0),
1542 };
1543
1544 // Translate the coordinates from top-left-origin-with-y-down to centre-origin-with-y-up.
1545 let tx = |x: geom::scalar::Default| x - win_w as geom::scalar::Default / 2.0;
1546 let ty = |y: geom::scalar::Default| -(y - win_h as geom::scalar::Default / 2.0);
1547
1548 // If the window ID has changed, ensure the dimensions are up to date.
1549 if *app.focused_window.borrow() != Some(window_id) {
1550 if app.window(window_id).is_some() {
1551 *app.focused_window.borrow_mut() = Some(window_id);
1552 }
1553 }
1554
1555 // Check for events that would update either mouse, keyboard or window state.
1556 match *event {
1557 winit::event::WindowEvent::CursorMoved { position, .. } => {
1558 let (x, y) = position.to_logical::<f32>(scale_factor).into();
1559 let x = tx(x);
1560 let y = ty(y);
1561 app.mouse.x = x;
1562 app.mouse.y = y;
1563 app.mouse.window = Some(window_id);
1564 }
1565
1566 winit::event::WindowEvent::MouseInput { state, button, .. } => {
1567 match state {
1568 event::ElementState::Pressed => {
1569 let p = app.mouse.position();
1570 app.mouse.buttons.press(button, p);
1571 }
1572 event::ElementState::Released => {
1573 app.mouse.buttons.release(button);
1574 }
1575 }
1576 app.mouse.window = Some(window_id);
1577 }
1578
1579 winit::event::WindowEvent::KeyboardInput { input, .. } => {
1580 if let Some(key) = input.virtual_keycode {
1581 match input.state {
1582 event::ElementState::Pressed => {
1583 app.keys.down.keys.insert(key);
1584 }
1585 event::ElementState::Released => {
1586 app.keys.down.keys.remove(&key);
1587 }
1588 }
1589 }
1590 }
1591
1592 _ => (),
1593 }
1594 }
1595 }
1596
1597 // Update the modifier keys within the app if necessary.
1598 if let winit::event::Event::WindowEvent { event, .. } = winit_event {
1599 if let winit::event::WindowEvent::ModifiersChanged(new_mods) = event {
1600 app.keys.mods = new_mods.clone();
1601 }
1602 }
1603
1604 // If the user provided an event function and winit::event::Event could be interpreted as some event
1605 // `E`, use it to update the model.
1606 if let Some(event_fn) = event_fn {
1607 if let Some(event) = E::from_winit_event(winit_event, app) {
1608 event_fn(&app, model, event);
1609 }
1610 }
1611
1612 // If the event was a window event, and the user specified an event function for this window,
1613 // call it.
1614 if let winit::event::Event::WindowEvent {
1615 window_id,
1616 ref event,
1617 } = *winit_event
1618 {
1619 // Raw window events.
1620 if let Some(raw_window_event_fn) = {
1621 let windows = app.windows.borrow();
1622 windows
1623 .get(&window_id)
1624 .and_then(|w| w.user_functions.raw_event.clone())
1625 .or_else(|| {
1626 removed_window
1627 .as_ref()
1628 .and_then(|w| w.user_functions.raw_event.clone())
1629 })
1630 } {
1631 let raw_window_event_fn = raw_window_event_fn
1632 .to_fn_ptr::<M>()
1633 .expect("unexpected model argument given to window event function");
1634 (*raw_window_event_fn)(&app, model, event);
1635 }
1636
1637 let (win_w, win_h, scale_factor) = {
1638 let windows = app.windows.borrow();
1639 windows
1640 .get(&window_id)
1641 .map(|w| {
1642 let sf = w.tracked_state.scale_factor;
1643 let (w, h) = w.tracked_state.physical_size.to_logical::<f64>(sf).into();
1644 (w, h, sf)
1645 })
1646 .unwrap_or((0.0, 0.0, 1.0))
1647 };
1648
1649 // If the event can be represented by a simplified nannou event, check for relevant user
1650 // functions to be called.
1651 if let Some(simple) =
1652 event::WindowEvent::from_winit_window_event(event, win_w, win_h, scale_factor)
1653 {
1654 // Nannou window events.
1655 if let Some(window_event_fn) = {
1656 let windows = app.windows.borrow();
1657 windows
1658 .get(&window_id)
1659 .and_then(|w| w.user_functions.event.clone())
1660 .or_else(|| {
1661 removed_window
1662 .as_ref()
1663 .and_then(|w| w.user_functions.event.clone())
1664 })
1665 } {
1666 let window_event_fn = window_event_fn
1667 .to_fn_ptr::<M>()
1668 .expect("unexpected model argument given to window event function");
1669 (*window_event_fn)(&app, model, simple.clone());
1670 }
1671
1672 // A macro to simplify calling event-specific user functions.
1673 macro_rules! call_user_function {
1674 ($fn_name:ident $(,$arg:expr)*) => {{
1675 if let Some(event_fn) = {
1676 let windows = app.windows.borrow();
1677 windows
1678 .get(&window_id)
1679 .and_then(|w| w.user_functions.$fn_name.clone())
1680 .or_else(|| {
1681 removed_window
1682 .as_ref()
1683 .and_then(|w| w.user_functions.$fn_name.clone())
1684 })
1685 } {
1686 let event_fn = event_fn
1687 .to_fn_ptr::<M>()
1688 .unwrap_or_else(|| {
1689 panic!(
1690 "unexpected model argument given to {} function",
1691 stringify!($fn_name),
1692 );
1693 });
1694 (*event_fn)(&app, model, $($arg),*);
1695 }
1696 }};
1697 }
1698
1699 // Check for more specific event functions.
1700 match simple {
1701 event::WindowEvent::KeyPressed(key) => call_user_function!(key_pressed, key),
1702 event::WindowEvent::KeyReleased(key) => call_user_function!(key_released, key),
1703 event::WindowEvent::ReceivedCharacter(char) => {
1704 call_user_function!(received_character, char)
1705 }
1706 event::WindowEvent::MouseMoved(pos) => call_user_function!(mouse_moved, pos),
1707 event::WindowEvent::MousePressed(button) => {
1708 call_user_function!(mouse_pressed, button)
1709 }
1710 event::WindowEvent::MouseReleased(button) => {
1711 call_user_function!(mouse_released, button)
1712 }
1713 event::WindowEvent::MouseEntered => call_user_function!(mouse_entered),
1714 event::WindowEvent::MouseExited => call_user_function!(mouse_exited),
1715 event::WindowEvent::MouseWheel(amount, phase) => {
1716 call_user_function!(mouse_wheel, amount, phase)
1717 }
1718 event::WindowEvent::Moved(pos) => call_user_function!(moved, pos),
1719 event::WindowEvent::Resized(size) => call_user_function!(resized, size),
1720 event::WindowEvent::Touch(touch) => call_user_function!(touch, touch),
1721 event::WindowEvent::TouchPressure(pressure) => {
1722 call_user_function!(touchpad_pressure, pressure)
1723 }
1724 event::WindowEvent::HoveredFile(path) => call_user_function!(hovered_file, path),
1725 event::WindowEvent::HoveredFileCancelled => {
1726 call_user_function!(hovered_file_cancelled)
1727 }
1728 event::WindowEvent::DroppedFile(path) => call_user_function!(dropped_file, path),
1729 event::WindowEvent::Focused => call_user_function!(focused),
1730 event::WindowEvent::Unfocused => call_user_function!(unfocused),
1731 event::WindowEvent::Closed => call_user_function!(closed),
1732 }
1733 }
1734 }
1735
1736 // If the loop was destroyed, we'll need to exit.
1737 let loop_destroyed = match winit_event {
1738 winit::event::Event::LoopDestroyed => true,
1739 _ => false,
1740 };
1741
1742 // If any exist conditions were triggered, indicate so.
1743 let exit = if loop_destroyed || exit_on_escape || app.windows.borrow().is_empty() {
1744 true
1745 } else {
1746 false
1747 };
1748
1749 exit
1750}