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