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}