Path: blob/main/crates/bevy_app/src/terminal_ctrl_c_handler.rs
9366 views
use core::sync::atomic::{AtomicU8, Ordering};12use bevy_ecs::message::MessageWriter;34use crate::{App, AppExit, Plugin, Update};56pub use ctrlc;78/// Indicates that all [`App`]'s should exit.9static SHOULD_EXIT: AtomicU8 = AtomicU8::new(0);1011/// Gracefully handles `Ctrl+C` by emitting a [`AppExit`] event. This plugin is part of the `DefaultPlugins`.12///13/// ```no_run14/// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, PluginGroup, TerminalCtrlCHandlerPlugin};15/// fn main() {16/// App::new()17/// .add_plugins(MinimalPlugins)18/// .add_plugins(TerminalCtrlCHandlerPlugin)19/// .run();20/// }21/// ```22///23/// If you want to setup your own `Ctrl+C` handler, you should call the24/// [`TerminalCtrlCHandlerPlugin::gracefully_exit`] function in your handler if you want bevy to gracefully exit.25/// ```no_run26/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, TerminalCtrlCHandlerPlugin, ctrlc};27/// fn main() {28/// // Your own `Ctrl+C` handler29/// ctrlc::set_handler(move || {30/// // Other clean up code ...31///32/// TerminalCtrlCHandlerPlugin::gracefully_exit();33/// });34///35/// App::new()36/// .add_plugins(DefaultPlugins)37/// .run();38/// }39/// ```40#[derive(Default)]41pub struct TerminalCtrlCHandlerPlugin;4243impl TerminalCtrlCHandlerPlugin {44/// When called the first time, it sends the [`AppExit`] event to all apps using45/// this plugin to make them gracefully exit.46///47/// If called more than once, it exits immediately.48pub fn gracefully_exit() {49if SHOULD_EXIT.fetch_add(1, Ordering::SeqCst) > 0 {50log::error!("Received more than one ctrl+c. Skipping graceful shutdown.");51std::process::exit(Self::EXIT_CODE.into());52};53}5455/// Sends a [`AppExit`] event when the user presses `Ctrl+C` on the terminal.56pub fn exit_on_flag(mut app_exit_writer: MessageWriter<AppExit>) {57if SHOULD_EXIT.load(Ordering::Relaxed) > 0 {58app_exit_writer.write(AppExit::from_code(Self::EXIT_CODE));59}60}6162const EXIT_CODE: u8 = 130;63}6465impl Plugin for TerminalCtrlCHandlerPlugin {66fn build(&self, app: &mut App) {67let result = ctrlc::try_set_handler(move || {68Self::gracefully_exit();69});70match result {71Ok(()) => {}72Err(ctrlc::Error::MultipleHandlers) => {73log::info!("Skipping installing `Ctrl+C` handler as one was already installed. Please call `TerminalCtrlCHandlerPlugin::gracefully_exit` in your own `Ctrl+C` handler if you want Bevy to gracefully exit on `Ctrl+C`.");74}75Err(err) => log::warn!("Failed to set `Ctrl+C` handler: {err}"),76}7778app.add_systems(Update, TerminalCtrlCHandlerPlugin::exit_on_flag);79}80}818283