pix_engine/
engine.rs

1//! Primary [`PiEngine`] trait and functions which drive your application.
2//!
3//! This is the core module of the `pix-engine` crate and is responsible for building and running
4//! any application using it.
5//!
6//! [`EngineBuilder`] allows you to customize various engine features and, once built, can
7//! [run][`Engine::run`] your application which must implement [`PixEngine::on_update`].
8//!
9//!
10//!
11//! # Example
12//!
13//! ```no_run
14//! use pix_engine::prelude::*;
15//!
16//! struct MyApp;
17//!
18//! impl PixEngine for MyApp {
19//!     fn on_start(&mut self, s: &mut PixState) -> PixResult<()> {
20//!         // Setup App state. `PixState` contains engine specific state and
21//!         // utility functions for things like getting mouse coordinates,
22//!         // drawing shapes, etc.
23//!         s.background(220);
24//!         s.font_family(Font::NOTO)?;
25//!         s.font_size(16);
26//!         Ok(())
27//!     }
28//!
29//!     fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
30//!         // Main render loop. Called as often as possible, or based on `target frame rate`.
31//!         if s.mouse_pressed() {
32//!             s.fill(color!(0));
33//!         } else {
34//!             s.fill(color!(255));
35//!         }
36//!         let m = s.mouse_pos();
37//!         s.circle([m.x(), m.y(), 80])?;
38//!         Ok(())
39//!     }
40//!
41//!     fn on_stop(&mut self, s: &mut PixState) -> PixResult<()> {
42//!         // Teardown any state or resources before exiting.
43//!         Ok(())
44//!     }
45//! }
46//!
47//! fn main() -> PixResult<()> {
48//!     let mut app = MyApp;
49//!     let mut engine = Engine::builder()
50//!       .dimensions(800, 600)
51//!       .title("MyApp")
52//!       .build()?;
53//!     engine.run(&mut app)
54//! }
55//! ```
56
57use crate::{image::Icon, prelude::*, renderer::RendererSettings};
58use log::{debug, error, info};
59use std::{
60    num::NonZeroUsize,
61    thread,
62    time::{Duration, Instant},
63};
64
65/// Trait for allowing the [`Engine`] to drive your application and send notification of events,
66/// passing along a [`&mut PixState`](PixState) to allow interacting with the [`Engine`].
67///
68/// Please see the [module-level documentation] for more examples.
69///
70/// [module-level documentation]: crate::appstate
71#[allow(unused_variables)]
72pub trait PixEngine {
73    /// Called once upon engine start when [`Engine::run`] is called.
74    ///
75    /// This can be used to set up initial state like creating objects, loading files or [Image]s, or
76    /// any additional application state that's either dynamic or relies on runtime values from
77    /// [`PixState`].
78    ///
79    /// # Errors
80    ///
81    /// Returning an error will immediately exit the application and call [`PixEngine::on_stop`].
82    /// [`Engine::run`] will return the original error or any error returned from
83    /// [`PixEngine::on_stop`].
84    ///
85    /// # Example
86    ///
87    /// ```
88    /// # use pix_engine::prelude::*;
89    /// # struct App;
90    /// # impl PixEngine for App {
91    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
92    /// fn on_start(&mut self, s: &mut PixState) -> PixResult<()> {
93    ///     s.background(220);
94    ///     s.font_family(Font::NOTO)?;
95    ///     s.font_size(16);
96    ///     Ok(())
97    /// }
98    /// # }
99    /// ```
100    fn on_start(&mut self, s: &mut PixState) -> PixResult<()> {
101        Ok(())
102    }
103
104    /// Called after [`PixEngine::on_start`], every frame based on the [target frame rate].
105    ///
106    /// By default, this is called as often as possible but can be controlled by changing the
107    /// [target frame rate]. It will continue to be executed until the application is terminated,
108    /// or [`PixState::run(false)`] is called.
109    ///
110    /// After [`PixState::run(false)`] is called, you can call [`PixState::redraw`] or
111    /// [`PixState::run_times`] to control the execution.
112    ///
113    /// [target frame rate]: PixState::frame_rate
114    ///
115    /// # Errors
116    ///
117    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`].
118    /// [`Engine::run`] will return the original error or any error returned from
119    /// [`PixEngine::on_stop`]. Calling [`PixState::abort_quit`] during [`PixEngine::on_stop`] will
120    /// allow program execution to resume. Care should be taken as this could result in a loop if
121    /// the cause of the original error is not resolved.
122    ///
123    /// Any errors encountered from methods on [`PixState`] can either be handled by the
124    /// implementor, or propagated with the [?] operator.
125    ///
126    /// # Example
127    ///
128    /// ```
129    /// # use pix_engine::prelude::*;
130    /// # struct App;
131    /// # impl PixEngine for App {
132    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
133    ///     if s.mouse_pressed() {
134    ///         s.fill(color!(0));
135    ///     } else {
136    ///         s.fill(color!(255));
137    ///     }
138    ///     let m = s.mouse_pos();
139    ///     s.circle([m.x(), m.y(), 80])?;
140    ///     Ok(())
141    /// }
142    /// # }
143    /// ```
144    fn on_update(&mut self, s: &mut PixState) -> PixResult<()>;
145
146    /// Called when the engine detects a close/exit event such as calling [`PixState::quit`] or if an
147    /// error is returned during program execution by any [`PixEngine`] methods.
148    ///
149    /// This can be used to clean up files or resources on appliation quit.
150    ///
151    /// # Errors
152    ///
153    /// Returning an error will immediately exit the application by propagating the error and returning from
154    /// [`Engine::run`]. Calling [`PixState::abort_quit`] will allow program execution to
155    /// resume. Care should be taken as this could result in a loop if the cause of the original
156    /// error is not resolved.
157    ///
158    /// Any errors encountered from methods on [`PixState`] can either be handled by the
159    /// implementor, or propagated with the [?] operator.
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// # use pix_engine::prelude::*;
165    /// # struct App { resources: std::path::PathBuf }
166    /// # impl PixEngine for App {
167    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
168    /// fn on_stop(&mut self, s: &mut PixState) -> PixResult<()> {
169    ///     std::fs::remove_file(&self.resources)?;
170    ///     Ok(())
171    /// }
172    /// # }
173    /// ```
174    fn on_stop(&mut self, s: &mut PixState) -> PixResult<()> {
175        Ok(())
176    }
177
178    /// Called each time a [`Key`] is pressed with the [`KeyEvent`] indicating which key and modifiers
179    /// are pressed as well as whether this is a repeat event where the key is being held down.
180    ///
181    /// Returning `true` consumes this event, preventing any further event triggering.
182    ///
183    /// # Errors
184    ///
185    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
186    /// the `Errors` section in [`PixEngine::on_update`] for more details.
187    ///
188    /// # Example
189    ///
190    /// ```
191    /// # use pix_engine::prelude::*;
192    /// # struct App;
193    /// # impl PixEngine for App {
194    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
195    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
196    ///     match event.key {
197    ///         Key::Return if event.keymod == KeyMod::CTRL => {
198    ///             s.fullscreen(true);
199    ///             Ok(true)
200    ///         },
201    ///         _ => Ok(false),
202    ///     }
203    /// }
204    /// # }
205    /// ```
206    fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
207        Ok(false)
208    }
209
210    /// Called each time a [`Key`] is pressed with the [`KeyEvent`] indicating which key and modifiers
211    /// are released.
212    ///
213    /// Returning `true` consumes this event, preventing any further event triggering.
214    ///
215    /// # Errors
216    ///
217    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
218    /// the `Errors` section in [`PixEngine::on_update`] for more details.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// # use pix_engine::prelude::*;
224    /// # struct App;
225    /// # impl App { fn fire_bullet(&mut self, s: &mut PixState) {} }
226    /// # impl PixEngine for App {
227    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
228    /// fn on_key_released(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
229    ///     match event.key {
230    ///         Key::Space => {
231    ///             self.fire_bullet(s);
232    ///             Ok(true)
233    ///         }
234    ///         _ => Ok(false),
235    ///     }
236    /// }
237    /// # }
238    /// ```
239    fn on_key_released(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
240        Ok(false)
241    }
242
243    /// Called each time text input is received.
244    ///
245    /// Returning `true` consumes this event, preventing any further event triggering.
246    ///
247    /// # Errors
248    ///
249    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
250    /// the `Errors` section in [`PixEngine::on_update`] for more details.
251    ///
252    /// # Example
253    ///
254    /// ```
255    /// # use pix_engine::prelude::*;
256    /// # struct App { text: String };
257    /// # impl PixEngine for App {
258    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
259    /// fn on_key_typed(&mut self, s: &mut PixState, text: &str) -> PixResult<bool> {
260    ///     self.text.push_str(text);
261    ///     Ok(true)
262    /// }
263    /// # }
264    /// ```
265    fn on_key_typed(&mut self, s: &mut PixState, text: &str) -> PixResult<bool> {
266        Ok(false)
267    }
268
269    /// Called each time the [`Mouse`] is moved while any mouse button is being held.
270    ///
271    /// You can inspect which button is being held by calling [`PixState::mouse_down`] with the desired
272    /// [Mouse] button. See also: [`PixEngine::on_mouse_motion`].
273    ///
274    /// Returning `true` consumes this event, preventing any further event triggering.
275    ///
276    /// # Errors
277    ///
278    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
279    /// the `Errors` section in [`PixEngine::on_update`] for more details.
280    ///
281    /// # Example
282    ///
283    /// ```
284    /// # use pix_engine::prelude::*;
285    /// # struct App { pos: Point<i32> };
286    /// # impl PixEngine for App {
287    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
288    /// fn on_mouse_dragged(
289    ///     &mut self,
290    ///     s: &mut PixState,
291    ///     pos: Point<i32>,
292    ///     rel_pos: Point<i32>,
293    /// ) -> PixResult<bool> {
294    ///     self.pos = pos;
295    ///     Ok(true)
296    /// }
297    /// # }
298    /// ```
299    fn on_mouse_dragged(
300        &mut self,
301        s: &mut PixState,
302        pos: Point<i32>,
303        rel_pos: Point<i32>,
304    ) -> PixResult<bool> {
305        Ok(false)
306    }
307
308    /// Called each time a [`ControllerButton`] is pressed with the [`ControllerEvent`] indicating
309    /// which button is pressed.
310    ///
311    /// Returning `true` consumes this event, preventing any further event triggering.
312    ///
313    /// # Errors
314    ///
315    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
316    /// the `Errors` section in [`PixEngine::on_update`] for more details.
317    ///
318    /// # Example
319    ///
320    /// ```
321    /// # use pix_engine::prelude::*;
322    /// # struct App;
323    /// # impl App { fn pause(&mut self) {} }
324    /// # impl PixEngine for App {
325    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
326    /// fn on_controller_pressed(&mut self, s: &mut PixState, event: ControllerEvent) -> PixResult<bool> {
327    ///     match event.button {
328    ///         ControllerButton::Start => {
329    ///             self.pause();
330    ///             Ok(true)
331    ///         },
332    ///         _ => Ok(false),
333    ///     }
334    /// }
335    /// # }
336    /// ```
337    fn on_controller_pressed(
338        &mut self,
339        s: &mut PixState,
340        event: ControllerEvent,
341    ) -> PixResult<bool> {
342        Ok(false)
343    }
344
345    /// Called each time a [`ControllerButton`] is pressed with the [`ControllerEvent`] indicating
346    /// which key and modifiers are released.
347    ///
348    /// Returning `true` consumes this event, preventing any further event triggering.
349    ///
350    /// # Errors
351    ///
352    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
353    /// the `Errors` section in [`PixEngine::on_update`] for more details.
354    ///
355    /// # Example
356    ///
357    /// ```
358    /// # use pix_engine::prelude::*;
359    /// # struct App;
360    /// # impl App { fn fire_bullet(&mut self, s: &mut PixState) {} }
361    /// # impl PixEngine for App {
362    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
363    /// fn on_controller_released(&mut self, s: &mut PixState, event: ControllerEvent) -> PixResult<bool> {
364    ///     match event.button {
365    ///         ControllerButton::X => {
366    ///             self.fire_bullet(s);
367    ///             Ok(true)
368    ///         }
369    ///         _ => Ok(false),
370    ///     }
371    /// }
372    /// # }
373    /// ```
374    fn on_controller_released(
375        &mut self,
376        s: &mut PixState,
377        event: ControllerEvent,
378    ) -> PixResult<bool> {
379        Ok(false)
380    }
381
382    /// Called each time a `Controller` `Axis` is moved with the delta since last frame.
383    ///
384    /// Returning `true` consumes this event, preventing any further event triggering.
385    ///
386    /// # Errors
387    ///
388    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
389    /// the `Errors` section in [`PixEngine::on_update`] for more details.
390    ///
391    /// # Example
392    ///
393    /// ```
394    /// # use pix_engine::prelude::*;
395    /// # struct App { player1: ControllerId }
396    /// # impl App {
397    /// #   fn move_right(&self) {}
398    /// #   fn move_left(&self) {}
399    /// #   fn move_up(&self) {}
400    /// #   fn move_down(&self) {}
401    /// # }
402    /// # impl PixEngine for App {
403    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
404    /// fn on_controller_axis_motion(
405    ///     &mut self,
406    ///     s: &mut PixState,
407    ///     controller_id: ControllerId,
408    ///     axis: Axis,
409    ///     value: i32,
410    /// ) -> PixResult<bool> {
411    ///     if controller_id == self.player1 {
412    ///         match axis {
413    ///             Axis::LeftX => {
414    ///                 if value > 0 {
415    ///                     self.move_right();
416    ///                 } else if value < 0 {
417    ///                     self.move_left();
418    ///                 }
419    ///                 Ok(true)
420    ///             }
421    ///             Axis::LeftY => {
422    ///                 if value > 0 {
423    ///                     self.move_up();
424    ///                 } else if value < 0 {
425    ///                     self.move_down();
426    ///                 }
427    ///                 Ok(true)
428    ///             }
429    ///             _ => Ok(false)
430    ///         }
431    ///     } else {
432    ///         Ok(false)
433    ///     }
434    /// }
435    /// # }
436    /// ```
437    fn on_controller_axis_motion(
438        &mut self,
439        s: &mut PixState,
440        controller_id: ControllerId,
441        axis: Axis,
442        value: i32,
443    ) -> PixResult<bool> {
444        Ok(false)
445    }
446
447    /// Called each time a `Controller` is added, removed, or remapped.
448    ///
449    /// Returning `true` consumes this event, preventing any further event triggering.
450    ///
451    /// # Errors
452    ///
453    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
454    /// the `Errors` section in [`PixEngine::on_update`] for more details.
455    ///
456    /// # Example
457    ///
458    /// ```
459    /// # use pix_engine::prelude::*;
460    /// # struct App;
461    /// # impl App {
462    /// # fn add_gamepad(&mut self, _: ControllerId) {}
463    /// # fn remove_gamepad(&mut self, _: ControllerId) {}
464    /// # }
465    /// # impl PixEngine for App {
466    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
467    /// fn on_controller_update(
468    ///     &mut self,
469    ///     s: &mut PixState,
470    ///     controller_id: ControllerId,
471    ///     update: ControllerUpdate,
472    /// ) -> PixResult<bool> {
473    ///     match update {
474    ///         ControllerUpdate::Added => {
475    ///             self.add_gamepad(controller_id);
476    ///             Ok(true)
477    ///         }
478    ///         ControllerUpdate::Removed => {
479    ///             self.remove_gamepad(controller_id);
480    ///             Ok(true)
481    ///         }
482    ///         _ => Ok(false),
483    ///     }
484    /// }
485    /// # }
486    /// ```
487    fn on_controller_update(
488        &mut self,
489        s: &mut PixState,
490        controller_id: ControllerId,
491        update: ControllerUpdate,
492    ) -> PixResult<bool> {
493        Ok(false)
494    }
495
496    /// Called each time a [`Mouse`] button is pressed.
497    ///
498    /// Returning `true` consumes this event, preventing any further event triggering.
499    ///
500    /// # Errors
501    ///
502    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
503    /// the `Errors` section in [`PixEngine::on_update`] for more details.
504    ///
505    /// # Example
506    ///
507    /// ```
508    /// # use pix_engine::prelude::*;
509    /// # struct App { canvas: Rect<i32>, drawing: bool };
510    /// # impl PixEngine for App {
511    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
512    /// fn on_mouse_pressed(
513    ///     &mut self,
514    ///     s: &mut PixState,
515    ///     btn: Mouse,
516    ///     pos: Point<i32>,
517    /// ) -> PixResult<bool> {
518    ///     if let Mouse::Left = btn {
519    ///         if self.canvas.contains(pos) {
520    ///             self.drawing = true;
521    ///         }
522    ///     }
523    ///     Ok(true)
524    /// }
525    /// # }
526    /// ```
527    fn on_mouse_pressed(
528        &mut self,
529        s: &mut PixState,
530        btn: Mouse,
531        pos: Point<i32>,
532    ) -> PixResult<bool> {
533        Ok(false)
534    }
535
536    /// Called each time a [`Mouse`] button is released.
537    ///
538    /// Returning `true` consumes this event, preventing any further event triggering.
539    ///
540    /// # Errors
541    ///
542    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
543    /// the `Errors` section in [`PixEngine::on_update`] for more details.
544    ///
545    /// # Example
546    ///
547    /// ```
548    /// # use pix_engine::prelude::*;
549    /// # struct App { drawing: bool, canvas: Rect<i32> };
550    /// # impl PixEngine for App {
551    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
552    /// fn on_mouse_released(
553    ///     &mut self,
554    ///     s: &mut PixState,
555    ///     btn: Mouse,
556    ///     pos: Point<i32>,
557    /// ) -> PixResult<bool> {
558    ///     if let Mouse::Left = btn {
559    ///         if self.canvas.contains(pos) {
560    ///             self.drawing = false;
561    ///         }
562    ///     }
563    ///     Ok(true)
564    /// }
565    /// # }
566    /// ```
567    fn on_mouse_released(
568        &mut self,
569        s: &mut PixState,
570        btn: Mouse,
571        pos: Point<i32>,
572    ) -> PixResult<bool> {
573        Ok(false)
574    }
575
576    /// Called each time a [`Mouse`] button is clicked (a press followed by a release).
577    ///
578    /// Returning `true` consumes this event, preventing any further event triggering.
579    ///
580    /// # Errors
581    ///
582    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
583    /// the `Errors` section in [`PixEngine::on_update`] for more details.
584    ///
585    /// # Example
586    ///
587    /// ```
588    /// # use pix_engine::prelude::*;
589    /// # struct App { item: Rect<i32>, selected: bool };
590    /// # impl PixEngine for App {
591    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
592    /// fn on_mouse_clicked(
593    ///     &mut self,
594    ///     s: &mut PixState,
595    ///     btn: Mouse,
596    ///     pos: Point<i32>,
597    /// ) -> PixResult<bool> {
598    ///     if let Mouse::Left = btn {
599    ///         if self.item.contains(pos) {
600    ///             self.selected = true;
601    ///         }
602    ///     }
603    ///     Ok(true)
604    /// }
605    /// # }
606    /// ```
607    fn on_mouse_clicked(
608        &mut self,
609        s: &mut PixState,
610        btn: Mouse,
611        pos: Point<i32>,
612    ) -> PixResult<bool> {
613        Ok(false)
614    }
615
616    /// Called each time a [`Mouse`] button is clicked twice within 500ms.
617    ///
618    /// Returning `true` consumes this event, preventing any further event triggering.
619    ///
620    /// # Errors
621    ///
622    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
623    /// the `Errors` section in [`PixEngine::on_update`] for more details.
624    ///
625    /// # Example
626    ///
627    /// ```
628    /// # use pix_engine::prelude::*;
629    /// # struct App { item: Rect<i32> };
630    /// # impl App { fn execute_item(&mut self) {} }
631    /// # impl PixEngine for App {
632    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
633    /// fn on_mouse_dbl_clicked(
634    ///     &mut self,
635    ///     s: &mut PixState,
636    ///     btn: Mouse,
637    ///     pos: Point<i32>,
638    /// ) -> PixResult<bool> {
639    ///     if let Mouse::Left = btn {
640    ///         if self.item.contains(pos) {
641    ///             self.execute_item()
642    ///         }
643    ///     }
644    ///     Ok(true)
645    /// }
646    /// # }
647    /// ```
648    fn on_mouse_dbl_clicked(
649        &mut self,
650        s: &mut PixState,
651        btn: Mouse,
652        pos: Point<i32>,
653    ) -> PixResult<bool> {
654        Ok(false)
655    }
656
657    /// Called each time the [`Mouse`] is moved with the `(x, y)` screen coordinates and relative
658    /// `(xrel, yrel)` positions since last frame.
659    ///
660    /// Returning `true` consumes this event, preventing any further event triggering.
661    ///
662    /// # Errors
663    ///
664    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
665    /// the `Errors` section in [`PixEngine::on_update`] for more details.
666    ///
667    /// # Example
668    ///
669    /// ```
670    /// # use pix_engine::prelude::*;
671    /// # struct App { pos: Point<i32> };
672    /// # impl PixEngine for App {
673    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
674    /// fn on_mouse_motion(
675    ///     &mut self,
676    ///     s: &mut PixState,
677    ///     pos: Point<i32>,
678    ///     rel_pos: Point<i32>,
679    /// ) -> PixResult<bool> {
680    ///     self.pos = pos;
681    ///     Ok(true)
682    /// }
683    /// # }
684    /// ```
685    fn on_mouse_motion(
686        &mut self,
687        s: &mut PixState,
688        pos: Point<i32>,
689        rel_pos: Point<i32>,
690    ) -> PixResult<bool> {
691        Ok(false)
692    }
693
694    /// Called each time the [`Mouse`] wheel is scrolled with the `(x, y)` delta since last frame.
695    ///
696    /// Returning `true` consumes this event, preventing any further event triggering.
697    ///
698    /// # Errors
699    ///
700    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
701    /// the `Errors` section in [`PixEngine::on_update`] for more details.
702    ///
703    /// # Example
704    ///
705    /// ```
706    /// # use pix_engine::prelude::*;
707    /// # struct App { scroll: Point<i32> };
708    /// # impl PixEngine for App {
709    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
710    /// fn on_mouse_wheel(&mut self, s: &mut PixState, pos: Point<i32>) -> PixResult<bool> {
711    ///     self.scroll += pos;
712    ///     Ok(true)
713    /// }
714    /// # }
715    /// ```
716    fn on_mouse_wheel(&mut self, s: &mut PixState, pos: Point<i32>) -> PixResult<bool> {
717        Ok(false)
718    }
719
720    /// Called each time a window event occurs.
721    ///
722    /// # Errors
723    ///
724    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
725    /// the `Errors` section in [`PixEngine::on_update`] for more details.
726    ///
727    /// # Example
728    ///
729    /// ```
730    /// # use pix_engine::prelude::*;
731    /// # struct App { window_id: WindowId };
732    /// # impl App { fn pause(&mut self) {} fn unpause(&mut self) {} }
733    /// # impl PixEngine for App {
734    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
735    /// fn on_window_event(
736    ///     &mut self,
737    ///     s: &mut PixState,
738    ///     window_id: WindowId,
739    ///     event: WindowEvent,
740    /// ) -> PixResult<()> {
741    ///     if window_id == self.window_id {
742    ///         match event {
743    ///             WindowEvent::Minimized => self.pause(),
744    ///             WindowEvent::Restored => self.unpause(),
745    ///             _ => (),
746    ///         }
747    ///     }
748    ///     Ok(())
749    /// }
750    /// # }
751    /// ```
752    fn on_window_event(
753        &mut self,
754        s: &mut PixState,
755        window_id: WindowId,
756        event: WindowEvent,
757    ) -> PixResult<()> {
758        Ok(())
759    }
760
761    /// Called for any system or user event. This is a catch-all for handling any events not
762    /// covered by other [`PixEngine`] methods.
763    ///
764    /// Returning `true` consumes this event, preventing any further event triggering.
765    ///
766    /// # Errors
767    ///
768    /// Returning an error will start exiting the application and call [`PixEngine::on_stop`]. See
769    /// the `Errors` section in [`PixEngine::on_update`] for more details.
770    ///
771    /// # Example
772    ///
773    /// ```
774    /// # use pix_engine::prelude::*;
775    /// # struct App;
776    /// # impl PixEngine for App {
777    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
778    /// fn on_event(
779    ///     &mut self,
780    ///     s: &mut PixState,
781    ///     event: &Event,
782    /// ) -> PixResult<bool> {
783    ///     match event {
784    ///         Event::ControllerDown { controller_id, button } => {
785    ///             // Handle controller down event
786    ///             Ok(true)
787    ///         }
788    ///         Event::ControllerUp { controller_id, button } => {
789    ///             // Handle controller up event
790    ///             Ok(true)
791    ///         }
792    ///         _ => Ok(false),
793    ///     }
794    /// }
795    /// # }
796    /// ```
797    fn on_event(&mut self, s: &mut PixState, event: &Event) -> PixResult<bool> {
798        Ok(false)
799    }
800}
801
802/// Builds a [`Engine`] instance by providing several configration functions.
803///
804/// # Example
805///
806/// ```no_run
807/// # use pix_engine::prelude::*;
808/// # struct MyApp;
809/// # impl PixEngine for MyApp {
810/// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
811/// # }
812/// fn main() -> PixResult<()> {
813///     let mut engine = Engine::builder()
814///         .title("My App")
815///         .position(10, 10)
816///         .resizable()
817///         .show_frame_rate()
818///         .icon("myapp.png")
819///         .build()?;
820///     let mut app = MyApp;
821///     engine.run(&mut app)
822/// }
823/// ```
824#[must_use]
825#[derive(Debug)]
826pub struct EngineBuilder {
827    settings: RendererSettings,
828    theme: Theme,
829    joystick_deadzone: i32,
830}
831
832impl Default for EngineBuilder {
833    fn default() -> Self {
834        Self {
835            settings: RendererSettings::default(),
836            theme: Theme::default(),
837            joystick_deadzone: 8000,
838        }
839    }
840}
841
842impl EngineBuilder {
843    /// Constructs a `EngineBuilder`.
844    pub fn new() -> Self {
845        Self::default()
846    }
847
848    /// Set a window title.
849    pub fn title<S>(&mut self, title: S) -> &mut Self
850    where
851        S: Into<String>,
852    {
853        self.settings.title = title.into();
854        self
855    }
856
857    /// Set font for text rendering.
858    pub fn font(&mut self, font: Font) -> &mut Self {
859        self.theme.fonts.body = font;
860        self
861    }
862
863    /// Set font size for text rendering.
864    pub fn font_size(&mut self, size: u32) -> &mut Self {
865        self.theme.font_size = size;
866        self
867    }
868
869    /// Set theme for UI rendering.
870    pub fn theme(&mut self, theme: Theme) -> &mut Self {
871        self.theme = theme;
872        self
873    }
874
875    /// Set a window icon.
876    pub fn icon<I>(&mut self, icon: I) -> &mut Self
877    where
878        I: Into<Icon>,
879    {
880        self.settings.icon = Some(icon.into());
881        self
882    }
883
884    /// Position the window at the given `(x, y)` coordinates of the display.
885    pub fn position(&mut self, x: i32, y: i32) -> &mut Self {
886        self.settings.x = Position::Positioned(x);
887        self.settings.y = Position::Positioned(y);
888        self
889    }
890
891    /// Position the window in the center of the display. This is the default.
892    pub fn position_centered(&mut self) -> &mut Self {
893        self.settings.x = Position::Centered;
894        self.settings.y = Position::Centered;
895        self
896    }
897
898    /// Set window dimensions.
899    pub fn dimensions(&mut self, width: u32, height: u32) -> &mut Self {
900        self.settings.width = width;
901        self.settings.height = height;
902        self
903    }
904
905    /// Set the rendering scale of the current canvas. Drawing coordinates are scaled by x/y
906    /// factors before being drawn to the canvas.
907    pub fn scale(&mut self, x: f32, y: f32) -> &mut Self {
908        self.settings.scale_x = x;
909        self.settings.scale_y = y;
910        self
911    }
912
913    /// Set audio sample rate in Hz (samples per second). Defaults to device fallback sample rate.
914    pub fn audio_sample_rate(&mut self, sample_rate: i32) -> &mut Self {
915        self.settings.audio_sample_rate = Some(sample_rate);
916        self
917    }
918
919    /// Set number of audio channels (1 for Mono, 2 for Stereo, etc). Defaults to device fallback
920    /// number of channels.
921    pub fn audio_channels(&mut self, channels: u8) -> &mut Self {
922        self.settings.audio_channels = Some(channels);
923        self
924    }
925
926    /// Set audio buffer size in samples. Defaults to device fallback sample size.
927    pub fn audio_buffer_size(&mut self, buffer_size: u16) -> &mut Self {
928        self.settings.audio_buffer_size = Some(buffer_size);
929        self
930    }
931
932    /// Start window in fullscreen mode.
933    pub fn fullscreen(&mut self) -> &mut Self {
934        self.settings.fullscreen = true;
935        self
936    }
937
938    /// Set the window to synchronize frame rate to the screens refresh rate ([`VSync`]).
939    ///
940    /// [`VSync`]: https://en.wikipedia.org/wiki/Screen_tearing#Vertical_synchronization
941    pub fn vsync_enabled(&mut self) -> &mut Self {
942        self.settings.vsync = true;
943        self
944    }
945
946    /// Allow window resizing.
947    pub fn resizable(&mut self) -> &mut Self {
948        self.settings.resizable = true;
949        self
950    }
951
952    /// Removes the window decoration.
953    pub fn borderless(&mut self) -> &mut Self {
954        self.settings.borderless = true;
955        self
956    }
957
958    /// Alter the joystick axis deadzone.
959    pub fn deadzone(&mut self, value: i32) -> &mut Self {
960        self.joystick_deadzone = value;
961        self
962    }
963
964    /// Enables high-DPI on displays that support it.
965    pub fn allow_highdpi(&mut self) -> &mut Self {
966        self.settings.allow_highdpi = true;
967        self
968    }
969
970    /// Starts engine with window hidden.
971    pub fn hidden(&mut self) -> &mut Self {
972        self.settings.hidden = true;
973        self
974    }
975
976    /// Enable average frame rate (FPS) in title.
977    pub fn show_frame_rate(&mut self) -> &mut Self {
978        self.settings.show_frame_rate = true;
979        self
980    }
981
982    /// Set a target frame rate to render at, controls how often
983    /// [`Engine::on_update`] is called.
984    pub fn target_frame_rate(&mut self, rate: usize) -> &mut Self {
985        self.settings.target_frame_rate = Some(rate);
986        self
987    }
988
989    /// Set a custom texture cache size other than the default of `20`.
990    /// Affects font family and image rendering caching operations.
991    pub fn texture_cache(&mut self, size: NonZeroUsize) -> &mut Self {
992        self.settings.texture_cache_size = size;
993        self
994    }
995
996    /// Set a custom text cache size other than the default of `500`.
997    /// Affects text rendering caching operations.
998    pub fn text_cache(&mut self, size: NonZeroUsize) -> &mut Self {
999        self.settings.text_cache_size = size;
1000        self
1001    }
1002
1003    /// Convert [EngineBuilder] to a [`Engine`] instance.
1004    ///
1005    /// # Errors
1006    ///
1007    /// If the engine fails to create a new renderer, then an error is returned.
1008    ///
1009    /// Possible errors include the title containing a `nul` character, the position or dimensions
1010    /// being invalid values or overlowing and an internal renderer error such as running out of
1011    /// memory or a software driver issue.
1012    pub fn build(&self) -> PixResult<Engine> {
1013        Ok(Engine {
1014            state: PixState::new(self.settings.clone(), self.theme.clone())?,
1015            joystick_deadzone: self.joystick_deadzone,
1016        })
1017    }
1018}
1019
1020/// The core engine that maintains the render loop, state, drawing functions, event handling, etc.
1021#[must_use]
1022#[derive(Debug)]
1023pub struct Engine {
1024    state: PixState,
1025    joystick_deadzone: i32,
1026}
1027
1028impl Engine {
1029    /// Constructs a default [EngineBuilder] which can build a `Engine` instance.
1030    ///
1031    /// See [EngineBuilder] for examples.
1032    pub fn builder() -> EngineBuilder {
1033        EngineBuilder::default()
1034    }
1035
1036    /// Starts the `Engine` application and begins executing the frame loop on a given
1037    /// application which must implement [`Engine`]. The only required method of which is
1038    /// [`Engine::on_update`].
1039    ///
1040    /// # Errors
1041    ///
1042    /// Any error in the entire library can propagate here and terminate the program. See the
1043    /// [error](crate::error) module for details. Also see [`Engine::on_stop`].
1044    ///
1045    /// # Example
1046    ///
1047    /// ```no_run
1048    /// # use pix_engine::prelude::*;
1049    /// # struct MyApp;
1050    /// # impl MyApp { fn new() -> Self { Self } }
1051    /// # impl PixEngine for MyApp {
1052    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
1053    /// # }
1054    /// fn main() -> PixResult<()> {
1055    ///     let mut engine = Engine::builder().build()?;
1056    ///     let mut app = MyApp::new(); // MyApp implements `Engine`
1057    ///     engine.run(&mut app)
1058    /// }
1059    /// ```
1060    pub fn run<A>(&mut self, app: &mut A) -> PixResult<()>
1061    where
1062        A: PixEngine,
1063    {
1064        info!("Starting `Engine`...");
1065
1066        // Handle events before on_start to initialize window
1067        self.handle_events(app)?;
1068
1069        debug!("Starting with `Engine::on_start`");
1070        self.state.clear()?;
1071        let on_start = app.on_start(&mut self.state);
1072        if on_start.is_err() || self.state.should_quit() {
1073            debug!("Quitting during startup with `Engine::on_stop`");
1074            if let Err(ref err) = on_start {
1075                error!("Error: {}", err);
1076            }
1077            return app.on_stop(&mut self.state).and(on_start);
1078        }
1079        self.state.present();
1080
1081        // on_stop loop enables on_stop to prevent application close if necessary
1082        'on_stop: loop {
1083            debug!("Starting `Engine::on_update` loop.");
1084            // running loop continues until an event or on_update returns false or errors
1085            let result = 'running: loop {
1086                let start_time = Instant::now();
1087                let time_since_last = start_time - self.state.last_frame_time();
1088
1089                self.handle_events(app)?;
1090                if self.state.should_quit() {
1091                    break 'running Ok(());
1092                }
1093
1094                if self.state.is_running() {
1095                    self.state.pre_update();
1096                    let on_update = app.on_update(&mut self.state);
1097                    if on_update.is_err() {
1098                        self.state.quit();
1099                        break 'running on_update;
1100                    }
1101                    self.state.on_update()?;
1102                    self.state.post_update();
1103                    self.state.present();
1104                    self.state.set_delta_time(start_time, time_since_last);
1105                    self.state.increment_frame(time_since_last)?;
1106                }
1107
1108                if !self.state.vsync_enabled() {
1109                    if let Some(target_delta_time) = self.state.target_delta_time() {
1110                        let time_to_next_frame = start_time + target_delta_time;
1111                        let now = Instant::now();
1112                        if time_to_next_frame > now {
1113                            thread::sleep(time_to_next_frame - now);
1114                        }
1115                    }
1116                }
1117            };
1118
1119            debug!("Quitting with `Engine::on_stop`");
1120            let on_stop = app.on_stop(&mut self.state);
1121            if self.state.should_quit() {
1122                info!("Quitting `Engine`...");
1123                break 'on_stop on_stop.and(result);
1124            }
1125        }
1126    }
1127}
1128
1129impl Engine {
1130    /// Handle user and system events.
1131    #[inline]
1132    fn handle_events<A>(&mut self, app: &mut A) -> PixResult<()>
1133    where
1134        A: PixEngine,
1135    {
1136        let state = &mut self.state;
1137        while let Some(event) = state.poll_event() {
1138            if let Event::ControllerAxisMotion { .. }
1139            | Event::JoyAxisMotion { .. }
1140            | Event::MouseMotion { .. }
1141            | Event::MouseWheel { .. }
1142            | Event::KeyDown { repeat: true, .. }
1143            | Event::KeyUp { repeat: true, .. } = event
1144            {
1145                // Ignore noisy events
1146            } else {
1147                debug!("Polling event {:?}", event);
1148            }
1149            let handled = app.on_event(state, &event)?;
1150            if !handled {
1151                match event {
1152                    Event::Quit { .. } | Event::AppTerminating { .. } => state.quit(),
1153                    Event::Window {
1154                        window_id,
1155                        win_event,
1156                    } => {
1157                        let window_id = WindowId(window_id);
1158                        match win_event {
1159                            WindowEvent::FocusGained => state.focus_window(Some(window_id)),
1160                            WindowEvent::FocusLost => state.focus_window(None),
1161                            WindowEvent::Close => state.close_window(window_id)?,
1162                            _ => (),
1163                        }
1164                        app.on_window_event(state, window_id, win_event)?;
1165                    }
1166                    Event::KeyDown {
1167                        key: Some(key),
1168                        keymod,
1169                        repeat,
1170                    } => {
1171                        let evt = KeyEvent::new(key, keymod, repeat);
1172                        if !app.on_key_pressed(state, evt)? {
1173                            state.ui.keys.press(key, keymod);
1174                        }
1175                    }
1176                    Event::KeyUp {
1177                        key: Some(key),
1178                        keymod,
1179                        repeat,
1180                    } => {
1181                        let evt = KeyEvent::new(key, keymod, repeat);
1182                        if !app.on_key_released(state, evt)? {
1183                            state.ui.keys.release(key, keymod);
1184                        }
1185                    }
1186                    Event::ControllerDown {
1187                        controller_id,
1188                        button,
1189                    } => {
1190                        let evt = ControllerEvent::new(controller_id, button);
1191                        app.on_controller_pressed(state, evt)?;
1192                    }
1193                    Event::ControllerUp {
1194                        controller_id,
1195                        button,
1196                    } => {
1197                        let evt = ControllerEvent::new(controller_id, button);
1198                        app.on_controller_released(state, evt)?;
1199                    }
1200                    Event::ControllerAxisMotion {
1201                        controller_id,
1202                        axis,
1203                        value,
1204                    } => {
1205                        let value = i32::from(value);
1206                        let value =
1207                            if (-self.joystick_deadzone..self.joystick_deadzone).contains(&value) {
1208                                0
1209                            } else {
1210                                value
1211                            };
1212                        let id = ControllerId(controller_id);
1213                        app.on_controller_axis_motion(state, id, axis, value)?;
1214                    }
1215                    Event::ControllerAdded { controller_id } => {
1216                        let id = ControllerId(controller_id);
1217                        if !app.on_controller_update(state, id, ControllerUpdate::Added)? {
1218                            state.open_controller(id)?;
1219                        }
1220                    }
1221                    Event::JoyDeviceAdded { joy_id } => {
1222                        let id = ControllerId(joy_id);
1223                        if !app.on_controller_update(state, id, ControllerUpdate::Added)? {
1224                            state.open_controller(id)?;
1225                        }
1226                    }
1227                    Event::ControllerRemoved { controller_id } => {
1228                        let id = ControllerId(controller_id);
1229                        if !app.on_controller_update(state, id, ControllerUpdate::Removed)? {
1230                            state.close_controller(id);
1231                        }
1232                    }
1233                    Event::JoyDeviceRemoved { joy_id } => {
1234                        let id = ControllerId(joy_id);
1235                        if !app.on_controller_update(state, id, ControllerUpdate::Removed)? {
1236                            state.open_controller(id)?;
1237                        }
1238                    }
1239                    Event::ControllerRemapped { controller_id } => {
1240                        let id = ControllerId(controller_id);
1241                        app.on_controller_update(state, id, ControllerUpdate::Remapped)?;
1242                    }
1243                    Event::TextInput { text, .. } => {
1244                        if !app.on_key_typed(state, &text)? {
1245                            state.ui.keys.typed(text);
1246                        }
1247                    }
1248                    Event::MouseMotion { x, y, xrel, yrel } => {
1249                        let pos = point!(x, y);
1250                        let rel_pos = point!(xrel, yrel);
1251                        if state.ui.mouse.is_pressed() {
1252                            app.on_mouse_dragged(state, pos, rel_pos)?;
1253                        }
1254                        if !app.on_mouse_motion(state, pos, rel_pos)? {
1255                            state.on_mouse_motion(pos);
1256                        }
1257                    }
1258                    Event::MouseDown { button, x, y } => {
1259                        if !app.on_mouse_pressed(state, button, point!(x, y))? {
1260                            state.on_mouse_pressed(button);
1261                        }
1262                    }
1263                    Event::MouseUp { button, x, y } => {
1264                        if state.ui.mouse.is_down(button) {
1265                            let now = Instant::now();
1266                            if let Some(clicked) = state.ui.mouse.last_clicked(button) {
1267                                if now - *clicked < Duration::from_millis(500)
1268                                    && !app.on_mouse_dbl_clicked(state, button, point!(x, y))?
1269                                {
1270                                    state.on_mouse_dbl_click(button, now);
1271                                }
1272                            }
1273                            if !app.on_mouse_clicked(state, button, point!(x, y))? {
1274                                state.on_mouse_click(button, now);
1275                            }
1276                        }
1277                        if !app.on_mouse_released(state, button, point!(x, y))? {
1278                            state.on_mouse_released(button);
1279                        }
1280                    }
1281                    Event::MouseWheel { x, y, .. } => {
1282                        if !app.on_mouse_wheel(state, point!(x, y))? {
1283                            state.on_mouse_wheel(x, y);
1284                        }
1285                    }
1286                    _ => (),
1287                }
1288            }
1289        }
1290        Ok(())
1291    }
1292}