repame_shell/quit.rs
1//! Quitting: the one thing a game cannot do the same way on every platform.
2//!
3//! `std::process::exit` is `unreachable` on `wasm32-unknown-unknown`: it traps
4//! the instance and strands every winit `RefCell` guard, so the next event
5//! panics with "RefCell already borrowed". A browser has no window to close
6//! and no exit to call, so a quit there latches instead: the shell keeps
7//! servicing frames while the app stops stepping and paints [`Quit::parked`],
8//! leaving the tab to be closed. A parked root still says so on screen, and
9//! the game should cut its audio (`repame_audio::Audio::silence`) so a
10//! looping track does not play on over that message.
11
12use repose_core::View;
13use repose_core::prelude::Modifier;
14use repose_ui::{Center, Text, ViewExt};
15
16/// A quit request, latched so a browser shell can park instead of exiting.
17#[derive(Default)]
18pub struct Quit {
19 latched: bool,
20}
21
22impl Quit {
23 /// Ask to quit. Does not return off the web, where the process exits
24 /// here.
25 pub fn request(&mut self) {
26 self.latched = true;
27 #[cfg(not(target_arch = "wasm32"))]
28 std::process::exit(0);
29 }
30
31 /// Whether the quit is latched, and the app is parked.
32 pub fn latched(&self) -> bool {
33 self.latched
34 }
35
36 /// The root a parked shell paints: one centered line saying the game
37 /// closed, so a blank tab is not mistaken for a hung frame.
38 pub fn parked() -> View {
39 Center(Modifier::new().fill_max_size()).child(Text("The game has been closed."))
40 }
41}