bevy_winit/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![forbid(unsafe_code)]
3#![doc(
4    html_logo_url = "https://bevyengine.org/assets/icon.png",
5    html_favicon_url = "https://bevyengine.org/assets/icon.png"
6)]
7
8//! `bevy_winit` provides utilities to handle window creation and the eventloop through [`winit`]
9//!
10//! Most commonly, the [`WinitPlugin`] is used as part of
11//! [`DefaultPlugins`](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html).
12//! The app's [runner](bevy_app::App::runner) is set by `WinitPlugin` and handles the `winit` [`EventLoop`].
13//! See `winit_runner` for details.
14
15extern crate alloc;
16
17use bevy_derive::Deref;
18use bevy_reflect::prelude::ReflectDefault;
19use bevy_reflect::Reflect;
20use bevy_window::{RawHandleWrapperHolder, WindowEvent};
21use core::marker::PhantomData;
22use winit::{event_loop::EventLoop, window::WindowId};
23
24use bevy_a11y::AccessibilityRequested;
25use bevy_app::{App, Last, Plugin};
26use bevy_ecs::prelude::*;
27use bevy_window::{exit_on_all_closed, Window, WindowCreated};
28use system::{changed_windows, check_keyboard_focus_lost, despawn_windows};
29pub use system::{create_monitors, create_windows};
30#[cfg(all(target_family = "wasm", target_os = "unknown"))]
31pub use winit::platform::web::CustomCursorExtWebSys;
32pub use winit::{
33    event_loop::EventLoopProxy,
34    window::{CustomCursor as WinitCustomCursor, CustomCursorSource},
35};
36pub use winit_config::*;
37pub use winit_windows::*;
38
39use crate::{
40    accessibility::{AccessKitAdapters, AccessKitPlugin, WinitActionRequestHandlers},
41    state::winit_runner,
42    winit_monitors::WinitMonitors,
43};
44
45pub mod accessibility;
46mod converters;
47pub mod cursor;
48#[cfg(feature = "custom_cursor")]
49mod custom_cursor;
50mod state;
51mod system;
52mod winit_config;
53mod winit_monitors;
54mod winit_windows;
55
56/// A [`Plugin`] that uses `winit` to create and manage windows, and receive window and input
57/// events.
58///
59/// This plugin will add systems and resources that sync with the `winit` backend and also
60/// replace the existing [`App`] runner with one that constructs an [event loop](EventLoop) to
61/// receive window and input events from the OS.
62///
63/// The `T` event type can be used to pass custom events to the `winit`'s loop, and handled as events
64/// in systems.
65///
66/// When using eg. `MinimalPlugins` you can add this using `WinitPlugin::<WakeUp>::default()`, where
67/// `WakeUp` is the default `Event` that bevy uses.
68#[derive(Default)]
69pub struct WinitPlugin<T: Event = WakeUp> {
70    /// Allows the window (and the event loop) to be created on any thread
71    /// instead of only the main thread.
72    ///
73    /// See [`EventLoopBuilder::build`](winit::event_loop::EventLoopBuilder::build) for more information on this.
74    ///
75    /// # Supported platforms
76    ///
77    /// Only works on Linux (X11/Wayland) and Windows.
78    /// This field is ignored on other platforms.
79    pub run_on_any_thread: bool,
80    marker: PhantomData<T>,
81}
82
83impl<T: Event> Plugin for WinitPlugin<T> {
84    fn name(&self) -> &str {
85        "bevy_winit::WinitPlugin"
86    }
87
88    fn build(&self, app: &mut App) {
89        let mut event_loop_builder = EventLoop::<T>::with_user_event();
90
91        // linux check is needed because x11 might be enabled on other platforms.
92        #[cfg(all(target_os = "linux", feature = "x11"))]
93        {
94            use winit::platform::x11::EventLoopBuilderExtX11;
95
96            // This allows a Bevy app to be started and ran outside the main thread.
97            // A use case for this is to allow external applications to spawn a thread
98            // which runs a Bevy app without requiring the Bevy app to need to reside on
99            // the main thread, which can be problematic.
100            event_loop_builder.with_any_thread(self.run_on_any_thread);
101        }
102
103        // linux check is needed because wayland might be enabled on other platforms.
104        #[cfg(all(target_os = "linux", feature = "wayland"))]
105        {
106            use winit::platform::wayland::EventLoopBuilderExtWayland;
107            event_loop_builder.with_any_thread(self.run_on_any_thread);
108        }
109
110        #[cfg(target_os = "windows")]
111        {
112            use winit::platform::windows::EventLoopBuilderExtWindows;
113            event_loop_builder.with_any_thread(self.run_on_any_thread);
114        }
115
116        #[cfg(target_os = "android")]
117        {
118            use winit::platform::android::EventLoopBuilderExtAndroid;
119            let msg = "Bevy must be setup with the #[bevy_main] macro on Android";
120            event_loop_builder.with_android_app(bevy_window::ANDROID_APP.get().expect(msg).clone());
121        }
122
123        let event_loop = event_loop_builder
124            .build()
125            .expect("Failed to build event loop");
126
127        app.init_non_send_resource::<WinitWindows>()
128            .init_resource::<WinitMonitors>()
129            .init_resource::<WinitSettings>()
130            .insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle()))
131            .add_event::<RawWinitWindowEvent>()
132            .set_runner(|app| winit_runner(app, event_loop))
133            .add_systems(
134                Last,
135                (
136                    // `exit_on_all_closed` only checks if windows exist but doesn't access data,
137                    // so we don't need to care about its ordering relative to `changed_windows`
138                    changed_windows.ambiguous_with(exit_on_all_closed),
139                    despawn_windows,
140                    check_keyboard_focus_lost,
141                )
142                    .chain(),
143            );
144
145        app.add_plugins(AccessKitPlugin);
146        app.add_plugins(cursor::CursorPlugin);
147    }
148}
149
150/// The default event that can be used to wake the window loop
151/// Wakes up the loop if in wait state
152#[derive(Debug, Default, Clone, Copy, Event, Reflect)]
153#[reflect(Debug, Default, Clone)]
154pub struct WakeUp;
155
156/// The original window event as produced by Winit. This is meant as an escape
157/// hatch for power users that wish to add custom Winit integrations.
158/// If you want to process events for your app or game, you should instead use
159/// `bevy::window::WindowEvent`, or one of its sub-events.
160///
161/// When you receive this event it has already been handled by Bevy's main loop.
162/// Sending these events will NOT cause them to be processed by Bevy.
163#[derive(Debug, Clone, Event)]
164pub struct RawWinitWindowEvent {
165    /// The window for which the event was fired.
166    pub window_id: WindowId,
167    /// The raw winit window event.
168    pub event: winit::event::WindowEvent,
169}
170
171/// A wrapper type around [`winit::event_loop::EventLoopProxy`] with the specific
172/// [`winit::event::Event::UserEvent`] used in the [`WinitPlugin`].
173///
174/// The `EventLoopProxy` can be used to request a redraw from outside bevy.
175///
176/// Use `Res<EventLoopProxy>` to receive this resource.
177#[derive(Resource, Deref)]
178pub struct EventLoopProxyWrapper<T: 'static>(EventLoopProxy<T>);
179
180/// A wrapper around [`winit::event_loop::OwnedDisplayHandle`]
181///
182/// The `DisplayHandleWrapper` can be used to build integrations that rely on direct
183/// access to the display handle
184///
185/// Use `Res<DisplayHandleWrapper>` to receive this resource.
186#[derive(Resource, Deref)]
187pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle);
188
189trait AppSendEvent {
190    fn send(&mut self, event: impl Into<WindowEvent>);
191}
192
193impl AppSendEvent for Vec<WindowEvent> {
194    fn send(&mut self, event: impl Into<WindowEvent>) {
195        self.push(Into::<WindowEvent>::into(event));
196    }
197}
198
199/// The parameters of the [`create_windows`] system.
200pub type CreateWindowParams<'w, 's, F = ()> = (
201    Commands<'w, 's>,
202    Query<
203        'w,
204        's,
205        (
206            Entity,
207            &'static mut Window,
208            Option<&'static RawHandleWrapperHolder>,
209        ),
210        F,
211    >,
212    EventWriter<'w, WindowCreated>,
213    NonSendMut<'w, WinitWindows>,
214    NonSendMut<'w, AccessKitAdapters>,
215    ResMut<'w, WinitActionRequestHandlers>,
216    Res<'w, AccessibilityRequested>,
217    Res<'w, WinitMonitors>,
218);
219
220/// The parameters of the [`create_monitors`] system.
221pub type CreateMonitorParams<'w, 's> = (Commands<'w, 's>, ResMut<'w, WinitMonitors>);