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}