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
14extern crate alloc;
15
16use alloc::sync::Arc;
17use std::sync::Mutex;
18
19use bevy_a11y::Focus;
20
21mod event;
22mod monitor;
23mod raw_handle;
24mod system;
25mod system_cursor;
26mod window;
27
28pub use crate::raw_handle::*;
29
30#[cfg(target_os = "android")]
31pub use android_activity;
32
33pub use event::*;
34pub use monitor::*;
35pub use system::*;
36pub use system_cursor::*;
37pub use window::*;
38
39/// The windowing prelude.
40///
41/// This includes the most common types in this crate, re-exported for your convenience.
42pub mod prelude {
43 #[doc(hidden)]
44 pub use crate::{
45 CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, Ime, MonitorSelection, Window,
46 WindowMoved, WindowPlugin, WindowPosition, WindowResizeConstraints,
47 };
48}
49
50use bevy_app::prelude::*;
51
52impl Default for WindowPlugin {
53 fn default() -> Self {
54 WindowPlugin {
55 primary_window: Some(Window::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 /// Whether to exit the app when there are no open windows.
76 ///
77 /// If disabling this, ensure that you send the [`bevy_app::AppExit`]
78 /// event when the app should exit. If this does not occur, you will
79 /// create 'headless' processes (processes without windows), which may
80 /// surprise your users. It is recommended to leave this setting to
81 /// either [`ExitCondition::OnAllClosed`] or [`ExitCondition::OnPrimaryClosed`].
82 ///
83 /// [`ExitCondition::OnAllClosed`] will add [`exit_on_all_closed`] to [`Update`].
84 /// [`ExitCondition::OnPrimaryClosed`] will add [`exit_on_primary_closed`] to [`Update`].
85 pub exit_condition: ExitCondition,
86
87 /// Whether to close windows when they are requested to be closed (i.e.
88 /// when the close button is pressed).
89 ///
90 /// If true, this plugin will add [`close_when_requested`] to [`Update`].
91 /// If this system (or a replacement) is not running, the close button will have no effect.
92 /// This may surprise your users. It is recommended to leave this setting as `true`.
93 pub close_when_requested: bool,
94}
95
96impl Plugin for WindowPlugin {
97 fn build(&self, app: &mut App) {
98 // User convenience events
99 app.add_event::<WindowEvent>()
100 .add_event::<WindowResized>()
101 .add_event::<WindowCreated>()
102 .add_event::<WindowClosing>()
103 .add_event::<WindowClosed>()
104 .add_event::<WindowCloseRequested>()
105 .add_event::<WindowDestroyed>()
106 .add_event::<RequestRedraw>()
107 .add_event::<CursorMoved>()
108 .add_event::<CursorEntered>()
109 .add_event::<CursorLeft>()
110 .add_event::<Ime>()
111 .add_event::<WindowFocused>()
112 .add_event::<WindowOccluded>()
113 .add_event::<WindowScaleFactorChanged>()
114 .add_event::<WindowBackendScaleFactorChanged>()
115 .add_event::<FileDragAndDrop>()
116 .add_event::<WindowMoved>()
117 .add_event::<WindowThemeChanged>()
118 .add_event::<AppLifecycle>();
119
120 if let Some(primary_window) = &self.primary_window {
121 let initial_focus = app
122 .world_mut()
123 .spawn(primary_window.clone())
124 .insert((
125 PrimaryWindow,
126 RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
127 ))
128 .id();
129 if let Some(mut focus) = app.world_mut().get_resource_mut::<Focus>() {
130 **focus = Some(initial_focus);
131 }
132 }
133
134 match self.exit_condition {
135 ExitCondition::OnPrimaryClosed => {
136 app.add_systems(PostUpdate, exit_on_primary_closed);
137 }
138 ExitCondition::OnAllClosed => {
139 app.add_systems(PostUpdate, exit_on_all_closed);
140 }
141 ExitCondition::DontExit => {}
142 }
143
144 if self.close_when_requested {
145 // Need to run before `exit_on_*` systems
146 app.add_systems(Update, close_when_requested);
147 }
148
149 // Register event types
150 app.register_type::<WindowEvent>()
151 .register_type::<WindowResized>()
152 .register_type::<RequestRedraw>()
153 .register_type::<WindowCreated>()
154 .register_type::<WindowCloseRequested>()
155 .register_type::<WindowClosing>()
156 .register_type::<WindowClosed>()
157 .register_type::<CursorMoved>()
158 .register_type::<CursorEntered>()
159 .register_type::<CursorLeft>()
160 .register_type::<WindowFocused>()
161 .register_type::<WindowOccluded>()
162 .register_type::<WindowScaleFactorChanged>()
163 .register_type::<WindowBackendScaleFactorChanged>()
164 .register_type::<FileDragAndDrop>()
165 .register_type::<WindowMoved>()
166 .register_type::<WindowThemeChanged>()
167 .register_type::<AppLifecycle>()
168 .register_type::<Monitor>();
169
170 // Register window descriptor and related types
171 app.register_type::<Window>()
172 .register_type::<PrimaryWindow>();
173 }
174}
175
176/// Defines the specific conditions the application should exit on
177#[derive(Clone)]
178pub enum ExitCondition {
179 /// Close application when the primary window is closed
180 ///
181 /// The plugin will add [`exit_on_primary_closed`] to [`Update`].
182 OnPrimaryClosed,
183 /// Close application when all windows are closed
184 ///
185 /// The plugin will add [`exit_on_all_closed`] to [`Update`].
186 OnAllClosed,
187 /// Keep application running headless even after closing all windows
188 ///
189 /// If selecting this, ensure that you send the [`bevy_app::AppExit`]
190 /// event when the app should exit. If this does not occur, you will
191 /// create 'headless' processes (processes without windows), which may
192 /// surprise your users.
193 DontExit,
194}
195
196/// [`AndroidApp`] provides an interface to query the application state as well as monitor events
197/// (for example lifecycle and input events).
198#[cfg(target_os = "android")]
199pub static ANDROID_APP: std::sync::OnceLock<android_activity::AndroidApp> =
200 std::sync::OnceLock::new();