 f607be8777
			
		
	
	
		f607be8777
		
			
		
	
	
	
	
		
			
			# Objective Fixes #13995. ## Solution Override the default `Ctrl+C` handler with one that sends `AppExit` event to every app with `TerminalCtrlCHandlerPlugin`. ## Testing Tested by running the `3d_scene` example and hitting `Ctrl+C` in the terminal. --- ## Changelog Handles `Ctrl+C` in the terminal gracefully. ## Migration Guide If you are overriding the `Ctrl+C` handler then you should call `TerminalCtrlCHandlerPlugin::gracefully_exit` from your handler. It will tell the app to exit.
		
			
				
	
	
		
			74 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			74 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::sync::atomic::{AtomicBool, Ordering};
 | |
| 
 | |
| use bevy_ecs::event::EventWriter;
 | |
| 
 | |
| use crate::{App, AppExit, Plugin, Update};
 | |
| 
 | |
| pub use ctrlc;
 | |
| 
 | |
| /// Indicates that all [`App`]'s should exit.
 | |
| static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
 | |
| 
 | |
| /// Gracefully handles `Ctrl+C` by emitting a [`AppExit`] event. This plugin is part of the `DefaultPlugins`.
 | |
| ///
 | |
| /// ```no_run
 | |
| /// # use bevy_app::{App, NoopPluginGroup as MinimalPlugins, PluginGroup, TerminalCtrlCHandlerPlugin};
 | |
| /// fn main() {
 | |
| ///     App::new()
 | |
| ///         .add_plugins(MinimalPlugins)
 | |
| ///         .add_plugins(TerminalCtrlCHandlerPlugin)
 | |
| ///         .run();
 | |
| /// }
 | |
| /// ```
 | |
| ///
 | |
| /// If you want to setup your own `Ctrl+C` handler, you should call the
 | |
| /// [`TerminalCtrlCHandlerPlugin::gracefully_exit`] function in your handler if you want bevy to gracefully exit.
 | |
| /// ```no_run
 | |
| /// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, TerminalCtrlCHandlerPlugin, ctrlc};
 | |
| /// fn main() {
 | |
| ///     // Your own `Ctrl+C` handler
 | |
| ///     ctrlc::set_handler(move || {
 | |
| ///         // Other clean up code ...
 | |
| ///
 | |
| ///         TerminalCtrlCHandlerPlugin::gracefully_exit();
 | |
| ///     });
 | |
| ///
 | |
| ///     App::new()
 | |
| ///         .add_plugins(DefaultPlugins)
 | |
| ///         .run();
 | |
| /// }
 | |
| /// ```
 | |
| #[derive(Default)]
 | |
| pub struct TerminalCtrlCHandlerPlugin;
 | |
| 
 | |
| impl TerminalCtrlCHandlerPlugin {
 | |
|     /// Sends the [`AppExit`] event to all apps using this plugin to make them gracefully exit.
 | |
|     pub fn gracefully_exit() {
 | |
|         SHOULD_EXIT.store(true, Ordering::Relaxed);
 | |
|     }
 | |
| 
 | |
|     /// Sends a [`AppExit`] event when the user presses `Ctrl+C` on the terminal.
 | |
|     fn exit_on_flag(mut events: EventWriter<AppExit>) {
 | |
|         if SHOULD_EXIT.load(Ordering::Relaxed) {
 | |
|             events.send(AppExit::from_code(130));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Plugin for TerminalCtrlCHandlerPlugin {
 | |
|     fn build(&self, app: &mut App) {
 | |
|         let result = ctrlc::try_set_handler(move || {
 | |
|             Self::gracefully_exit();
 | |
|         });
 | |
|         match result {
 | |
|             Ok(()) => {}
 | |
|             Err(ctrlc::Error::MultipleHandlers) => {
 | |
|                 bevy_utils::tracing::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`.");
 | |
|             }
 | |
|             Err(err) => bevy_utils::tracing::warn!("Failed to set `Ctrl+C` handler: {err}"),
 | |
|         }
 | |
| 
 | |
|         app.add_systems(Update, TerminalCtrlCHandlerPlugin::exit_on_flag);
 | |
|     }
 | |
| }
 |