Skip to main content

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}