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();