bevy_window/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc(
3 html_logo_url = "https://bevy.org/assets/icon.png",
4 html_favicon_url = "https://bevy.org/assets/icon.png"
5)]
6#![no_std]
7
8//! `bevy_window` provides a platform-agnostic interface for windowing in Bevy.
9//!
10//! This crate contains types for window management and events,
11//! used by windowing implementors such as `bevy_winit`.
12//! The [`WindowPlugin`] sets up some global window-related parameters and
13//! is part of the [`DefaultPlugins`](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html).
14
15#[cfg(feature = "std")]
16extern crate std;
17
18extern crate alloc;
19
20mod cursor;
21mod event;
22mod monitor;
23mod raw_handle;
24mod system;
25mod window;
26
27pub use crate::raw_handle::*;
28
29pub use cursor::*;
30pub use event::*;
31pub use monitor::*;
32pub use system::*;
33pub use window::*;
34
35/// The windowing prelude.
36///
37/// This includes the most common types in this crate, re-exported for your convenience.
38pub mod prelude {
39 #[doc(hidden)]
40 pub use crate::{
41 CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, Ime, MonitorSelection,
42 VideoModeSelection, Window, WindowMoved, WindowPlugin, WindowPosition,
43 WindowResizeConstraints,
44 };
45}
46
47use alloc::sync::Arc;
48use bevy_app::prelude::*;
49use bevy_platform::sync::Mutex;
50
51impl Default for WindowPlugin {
52 fn default() -> Self {
53 WindowPlugin {
54 primary_window: Some(Window::default()),
55 primary_cursor_options: Some(CursorOptions::default()),
56 exit_condition: ExitCondition::OnAllClosed,
57 close_when_requested: true,
58 }
59 }
60}
61
62/// A [`Plugin`] that defines an interface for windowing support in Bevy.
63pub struct WindowPlugin {
64 /// Settings for the primary window.
65 ///
66 /// `Some(custom_window)` will spawn an entity with `custom_window` and [`PrimaryWindow`] as components.
67 /// `None` will not spawn a primary window.
68 ///
69 /// Defaults to `Some(Window::default())`.
70 ///
71 /// Note that if there are no windows the App will exit (by default) due to
72 /// [`exit_on_all_closed`].
73 pub primary_window: Option<Window>,
74
75 /// Settings for the cursor on the primary window.
76 ///
77 /// Defaults to `Some(CursorOptions::default())`.
78 ///
79 /// Has no effect if [`WindowPlugin::primary_window`] is `None`.
80 pub primary_cursor_options: Option<CursorOptions>,
81
82 /// Whether to exit the app when there are no open windows.
83 ///
84 /// If disabling this, ensure that you send the [`bevy_app::AppExit`]
85 /// event when the app should exit. If this does not occur, you will
86 /// create 'headless' processes (processes without windows), which may
87 /// surprise your users. It is recommended to leave this setting to
88 /// either [`ExitCondition::OnAllClosed`] or [`ExitCondition::OnPrimaryClosed`].
89 ///
90 /// [`ExitCondition::OnAllClosed`] will add [`exit_on_all_closed`] to [`Update`].
91 /// [`ExitCondition::OnPrimaryClosed`] will add [`exit_on_primary_closed`] to [`Update`].
92 pub exit_condition: ExitCondition,
93
94 /// Whether to close windows when they are requested to be closed (i.e.
95 /// when the close button is pressed).
96 ///
97 /// If true, this plugin will add [`close_when_requested`] to [`Update`].
98 /// If this system (or a replacement) is not running, the close button will have no effect.
99 /// This may surprise your users. It is recommended to leave this setting as `true`.
100 pub close_when_requested: bool,
101}
102
103impl Plugin for WindowPlugin {
104 fn build(&self, app: &mut App) {
105 // User convenience events
106 app.add_message::<WindowEvent>()
107 .add_message::<WindowResized>()
108 .add_message::<WindowCreated>()
109 .add_message::<WindowClosing>()
110 .add_message::<WindowClosed>()
111 .add_message::<WindowCloseRequested>()
112 .add_message::<WindowDestroyed>()
113 .add_message::<RequestRedraw>()
114 .add_message::<CursorMoved>()
115 .add_message::<CursorEntered>()
116 .add_message::<CursorLeft>()
117 .add_message::<Ime>()
118 .add_message::<WindowFocused>()
119 .add_message::<WindowOccluded>()
120 .add_message::<WindowScaleFactorChanged>()
121 .add_message::<WindowBackendScaleFactorChanged>()
122 .add_message::<FileDragAndDrop>()
123 .add_message::<WindowMoved>()
124 .add_message::<WindowThemeChanged>()
125 .add_message::<AppLifecycle>();
126
127 if let Some(primary_window) = &self.primary_window {
128 let mut entity_commands = app.world_mut().spawn(primary_window.clone());
129 entity_commands.insert((
130 PrimaryWindow,
131 RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
132 ));
133 if let Some(primary_cursor_options) = &self.primary_cursor_options {
134 entity_commands.insert(primary_cursor_options.clone());
135 }
136 }
137
138 match self.exit_condition {
139 ExitCondition::OnPrimaryClosed => {
140 app.add_systems(PostUpdate, exit_on_primary_closed);
141 }
142 ExitCondition::OnAllClosed => {
143 app.add_systems(PostUpdate, exit_on_all_closed);
144 }
145 ExitCondition::DontExit => {}
146 }
147
148 if self.close_when_requested {
149 // Need to run before `exit_on_*` systems
150 app.add_systems(Update, close_when_requested);
151 }
152 }
153}
154
155/// Defines the specific conditions the application should exit on
156#[derive(Clone)]
157pub enum ExitCondition {
158 /// Close application when the primary window is closed
159 ///
160 /// The plugin will add [`exit_on_primary_closed`] to [`PostUpdate`].
161 OnPrimaryClosed,
162 /// Close application when all windows are closed
163 ///
164 /// The plugin will add [`exit_on_all_closed`] to [`PostUpdate`].
165 OnAllClosed,
166 /// Keep application running headless even after closing all windows
167 ///
168 /// If selecting this, ensure that you send the [`bevy_app::AppExit`]
169 /// event when the app should exit. If this does not occur, you will
170 /// create 'headless' processes (processes without windows), which may
171 /// surprise your users.
172 DontExit,
173}