1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![forbid(unsafe_code)]
3#![doc(
4 html_logo_url = "https://bevy.org/assets/icon.png",
5 html_favicon_url = "https://bevy.org/assets/icon.png"
6)]
7
8extern crate alloc;
16
17use bevy_derive::Deref;
18use bevy_reflect::Reflect;
19use bevy_window::{ExitSystems, RawHandleWrapperHolder, WindowEvent};
20use core::cell::RefCell;
21use winit::{event_loop::EventLoop, window::WindowId};
22
23use bevy_a11y::AccessibilityRequested;
24use bevy_app::{App, Last, OnAppExitSystems, Plugin};
25use bevy_ecs::prelude::*;
26use bevy_window::{CursorOptions, Window, WindowCreated};
27use system::{changed_cursor_options, changed_windows, check_keyboard_focus_lost, despawn_windows};
28pub use system::{create_monitors, create_windows};
29#[cfg(all(target_family = "wasm", target_os = "unknown"))]
30pub use winit::platform::web::CustomCursorExtWebSys;
31pub use winit::{
32 event_loop::EventLoopProxy,
33 window::{CustomCursor as WinitCustomCursor, CustomCursorSource},
34};
35pub use winit_config::*;
36pub use winit_monitors::*;
37pub use winit_windows::*;
38
39use crate::{
40 accessibility::{AccessKitPlugin, WinitActionRequestHandlers},
41 state::winit_runner,
42};
43
44pub mod accessibility;
45pub mod converters;
46mod cursor;
47mod state;
48mod system;
49mod winit_config;
50mod winit_monitors;
51mod winit_windows;
52
53thread_local! {
54 pub static WINIT_WINDOWS: RefCell<WinitWindows> = const { RefCell::new(WinitWindows::new()) };
57}
58
59#[derive(Default)]
66pub struct WinitPlugin {
67 pub run_on_any_thread: bool,
77}
78
79impl Plugin for WinitPlugin {
80 fn name(&self) -> &str {
81 "bevy_winit::WinitPlugin"
82 }
83
84 fn build(&self, app: &mut App) {
85 let mut event_loop_builder = EventLoop::<WinitUserEvent>::with_user_event();
86
87 #[cfg(all(target_os = "linux", feature = "x11"))]
89 {
90 use winit::platform::x11::EventLoopBuilderExtX11;
91
92 event_loop_builder.with_any_thread(self.run_on_any_thread);
97 }
98
99 #[cfg(all(target_os = "linux", feature = "wayland"))]
101 {
102 use winit::platform::wayland::EventLoopBuilderExtWayland;
103 event_loop_builder.with_any_thread(self.run_on_any_thread);
104 }
105
106 #[cfg(target_os = "macos")]
107 {
108 use bevy_ecs::system::SystemState;
109 use winit::platform::macos::EventLoopBuilderExtMacOS;
110
111 let mut initial_windows_state =
115 SystemState::<Query<(Entity, &Window)>>::new(app.world_mut());
116 let initial_windows = initial_windows_state.get(app.world()).unwrap();
117 let initially_focused = initial_windows.iter().any(|(_, window)| window.focused);
118 event_loop_builder.with_activate_ignoring_other_apps(initially_focused);
119 }
120
121 #[cfg(target_os = "windows")]
122 {
123 use winit::platform::windows::EventLoopBuilderExtWindows;
124 event_loop_builder.with_any_thread(self.run_on_any_thread);
125 }
126
127 #[cfg(target_os = "android")]
128 {
129 use winit::platform::android::EventLoopBuilderExtAndroid;
130 let msg = "Bevy must be setup with the #[bevy_main] macro on Android";
131 event_loop_builder
132 .with_android_app(bevy_android::ANDROID_APP.get().expect(msg).clone());
133 }
134
135 let event_loop = event_loop_builder
136 .build()
137 .expect("Failed to build event loop");
138
139 let event_loop_proxy = event_loop.create_proxy();
140
141 #[cfg(any(all(unix, not(target_os = "horizon")), windows))]
144 {
145 let event_loop_proxy = event_loop_proxy.clone();
146 bevy_app::TerminalCtrlCHandlerPlugin::register_exit_handler(move || {
147 let _ = event_loop_proxy.send_event(WinitUserEvent::WakeUp);
148 });
149 }
150
151 app.init_resource::<WinitMonitors>()
152 .init_resource::<WinitSettings>()
153 .insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle()))
154 .insert_resource(EventLoopProxyWrapper(event_loop_proxy))
155 .add_message::<RawWinitWindowEvent>()
156 .set_runner(|app| winit_runner(app, event_loop))
157 .add_systems(
158 Last,
159 (
160 changed_windows,
161 changed_cursor_options,
162 despawn_windows.after(ExitSystems).after(OnAppExitSystems),
163 check_keyboard_focus_lost,
164 )
165 .chain(),
166 );
167
168 app.add_plugins(AccessKitPlugin);
169 app.add_plugins(cursor::WinitCursorPlugin);
170
171 app.add_observer(
172 |_window: On<Add<Window>>, event_loop_proxy: Res<EventLoopProxyWrapper>| -> Result {
173 event_loop_proxy.send_event(WinitUserEvent::WindowAdded)?;
174
175 Ok(())
176 },
177 );
178 }
179}
180
181#[derive(Debug, Clone, Copy, Reflect)]
197#[reflect(Debug, Clone)]
198pub enum WinitUserEvent {
199 WakeUp,
201 WindowAdded,
203}
204
205#[derive(Debug, Clone, Message)]
213pub struct RawWinitWindowEvent {
214 pub window_id: WindowId,
216 pub event: winit::event::WindowEvent,
218}
219
220#[derive(Resource, Deref)]
227pub struct EventLoopProxyWrapper(EventLoopProxy<WinitUserEvent>);
228
229#[derive(Resource, Deref)]
236pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle);
237
238trait AppSendEvent {
239 fn send(&mut self, event: impl Into<WindowEvent>);
240}
241
242impl AppSendEvent for Vec<WindowEvent> {
243 fn send(&mut self, event: impl Into<WindowEvent>) {
244 self.push(Into::<WindowEvent>::into(event));
245 }
246}
247
248pub type CreateWindowParams<'w, 's> = (
250 Commands<'w, 's>,
251 Query<
252 'w,
253 's,
254 (
255 Entity,
256 &'static mut Window,
257 &'static CursorOptions,
258 Option<&'static RawHandleWrapperHolder>,
259 ),
260 Added<Window>,
261 >,
262 MessageWriter<'w, WindowCreated>,
263 ResMut<'w, WinitActionRequestHandlers>,
264 Res<'w, AccessibilityRequested>,
265 Res<'w, WinitMonitors>,
266);
267
268pub type CreateMonitorParams<'w, 's> = (Commands<'w, 's>, ResMut<'w, WinitMonitors>);