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