Skip to main content

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_ecs::schedule::IntoScheduleConfigs;
50use bevy_platform::sync::Mutex;
51
52impl Default for WindowPlugin {
53    fn default() -> Self {
54        WindowPlugin {
55            primary_window: Some(Window::default()),
56            primary_cursor_options: Some(CursorOptions::default()),
57            exit_condition: ExitCondition::OnAllClosed,
58            close_when_requested: true,
59        }
60    }
61}
62
63/// A [`Plugin`] that defines an interface for windowing support in Bevy.
64pub struct WindowPlugin {
65    /// Settings for the primary window.
66    ///
67    /// `Some(custom_window)` will spawn an entity with `custom_window` and [`PrimaryWindow`] as components.
68    /// `None` will not spawn a primary window.
69    ///
70    /// Defaults to `Some(Window::default())`.
71    ///
72    /// Note that if there are no windows the App will exit (by default) due to
73    /// [`exit_on_all_closed`].
74    pub primary_window: Option<Window>,
75
76    /// Settings for the cursor on the primary window.
77    ///
78    /// Defaults to `Some(CursorOptions::default())`.
79    ///
80    /// Has no effect if [`WindowPlugin::primary_window`] is `None`.
81    pub primary_cursor_options: Option<CursorOptions>,
82
83    /// Whether to exit the app when there are no open windows.
84    ///
85    /// If disabling this, ensure that you send the [`bevy_app::AppExit`]
86    /// event when the app should exit. If this does not occur, you will
87    /// create 'headless' processes (processes without windows), which may
88    /// surprise your users. It is recommended to leave this setting to
89    /// either [`ExitCondition::OnAllClosed`] or [`ExitCondition::OnPrimaryClosed`].
90    ///
91    /// [`ExitCondition::OnAllClosed`] will add [`exit_on_all_closed`] to [`Last`].
92    /// [`ExitCondition::OnPrimaryClosed`] will add [`exit_on_primary_closed`] to [`Last`].
93    pub exit_condition: ExitCondition,
94
95    /// Whether to close windows when they are requested to be closed (i.e.
96    /// when the close button is pressed).
97    ///
98    /// If true, this plugin will add [`close_when_requested`] to [`Last`].
99    /// If this system (or a replacement) is not running, the close button will have no effect.
100    /// This may surprise your users. It is recommended to leave this setting as `true`.
101    pub close_when_requested: bool,
102}
103
104impl Plugin for WindowPlugin {
105    fn build(&self, app: &mut App) {
106        // User convenience events
107        app.add_message::<WindowEvent>()
108            .add_message::<WindowResized>()
109            .add_message::<WindowCreated>()
110            .add_message::<WindowClosing>()
111            .add_message::<WindowClosed>()
112            .add_message::<WindowCloseRequested>()
113            .add_message::<WindowDestroyed>()
114            .add_message::<RequestRedraw>()
115            .add_message::<CursorMoved>()
116            .add_message::<CursorEntered>()
117            .add_message::<CursorLeft>()
118            .add_message::<Ime>()
119            .add_message::<WindowFocused>()
120            .add_message::<WindowOccluded>()
121            .add_message::<WindowScaleFactorChanged>()
122            .add_message::<WindowBackendScaleFactorChanged>()
123            .add_message::<FileDragAndDrop>()
124            .add_message::<WindowMoved>()
125            .add_message::<WindowThemeChanged>()
126            .add_message::<AppLifecycle>();
127
128        if let Some(primary_window) = &self.primary_window {
129            let mut entity_commands = app.world_mut().spawn(primary_window.clone());
130            entity_commands.insert((
131                PrimaryWindow,
132                RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
133            ));
134            if let Some(primary_cursor_options) = &self.primary_cursor_options {
135                entity_commands.insert(primary_cursor_options.clone());
136            }
137        }
138
139        match self.exit_condition {
140            ExitCondition::OnPrimaryClosed => {
141                app.add_systems(Last, exit_on_primary_closed.in_set(ExitSystems));
142            }
143            ExitCondition::OnAllClosed => {
144                app.add_systems(Last, exit_on_all_closed.in_set(ExitSystems));
145            }
146            ExitCondition::DontExit => {}
147        }
148
149        if self.close_when_requested {
150            // Need to run before `exit_on_*` systems
151            app.add_systems(Last, close_when_requested.before(ExitSystems));
152        }
153    }
154}
155
156/// Defines the specific conditions the application should exit on
157#[derive(Clone)]
158pub enum ExitCondition {
159    /// Close application when the primary window is closed
160    ///
161    /// The plugin will add [`exit_on_primary_closed`] to [`Last`].
162    OnPrimaryClosed,
163    /// Close application when all windows are closed
164    ///
165    /// The plugin will add [`exit_on_all_closed`] to [`Last`].
166    OnAllClosed,
167    /// Keep application running headless even after closing all windows
168    ///
169    /// If selecting this, ensure that you send the [`bevy_app::AppExit`]
170    /// event when the app should exit. If this does not occur, you will
171    /// create 'headless' processes (processes without windows), which may
172    /// surprise your users.
173    DontExit,
174}