Skip to content

Commit 8fffc44

Browse files
committed
Add resource to control winit runner
1 parent 3f2cc38 commit 8fffc44

File tree

4 files changed

+156
-18
lines changed

4 files changed

+156
-18
lines changed

crates/bevy_winit/src/lib.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod system;
1313
#[cfg(target_arch = "wasm32")]
1414
mod web_resize;
1515
mod winit_config;
16+
mod winit_handler;
1617
mod winit_windows;
1718

1819
use bevy_a11y::AccessibilityRequested;
@@ -22,6 +23,7 @@ use bevy_tasks::tick_global_task_pools_on_main_thread;
2223
use system::{changed_window, create_window, despawn_window, CachedWindow};
2324

2425
pub use winit_config::*;
26+
pub use winit_handler::*;
2527
pub use winit_windows::*;
2628

2729
use bevy_app::{App, AppExit, Last, Plugin};
@@ -75,7 +77,7 @@ pub struct WinitPlugin;
7577

7678
impl Plugin for WinitPlugin {
7779
fn build(&self, app: &mut App) {
78-
let mut event_loop_builder = EventLoopBuilder::<()>::with_user_event();
80+
let mut event_loop_builder = EventLoopBuilder::<HandleEvent>::with_user_event();
7981

8082
#[cfg(target_os = "android")]
8183
{
@@ -113,7 +115,7 @@ impl Plugin for WinitPlugin {
113115
#[cfg(not(target_arch = "wasm32"))]
114116
let mut create_window_system_state: SystemState<(
115117
Commands,
116-
NonSendMut<EventLoop<()>>,
118+
NonSendMut<EventLoop<HandleEvent>>,
117119
Query<(Entity, &mut Window)>,
118120
EventWriter<WindowCreated>,
119121
NonSendMut<WinitWindows>,
@@ -125,7 +127,7 @@ impl Plugin for WinitPlugin {
125127
#[cfg(target_arch = "wasm32")]
126128
let mut create_window_system_state: SystemState<(
127129
Commands,
128-
NonSendMut<EventLoop<()>>,
130+
NonSendMut<EventLoop<HandleEvent>>,
129131
Query<(Entity, &mut Window)>,
130132
EventWriter<WindowCreated>,
131133
NonSendMut<WinitWindows>,
@@ -185,9 +187,10 @@ impl Plugin for WinitPlugin {
185187
}
186188
}
187189

188-
fn run<F>(event_loop: EventLoop<()>, event_handler: F) -> !
190+
fn run<F>(event_loop: EventLoop<HandleEvent>, event_handler: F) -> !
189191
where
190-
F: 'static + FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
192+
F: 'static
193+
+ FnMut(Event<'_, HandleEvent>, &EventLoopWindowTarget<HandleEvent>, &mut ControlFlow),
191194
{
192195
event_loop.run(event_handler)
193196
}
@@ -204,9 +207,9 @@ where
204207
target_os = "netbsd",
205208
target_os = "openbsd"
206209
))]
207-
fn run_return<F>(event_loop: &mut EventLoop<()>, event_handler: F)
210+
fn run_return<F>(event_loop: &mut EventLoop<HandleEvent>, event_handler: F)
208211
where
209-
F: FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
212+
F: FnMut(Event<'_, HandleEvent>, &EventLoopWindowTarget<HandleEvent>, &mut ControlFlow),
210213
{
211214
use winit::platform::run_return::EventLoopExtRunReturn;
212215
event_loop.run_return(event_handler);
@@ -221,9 +224,9 @@ where
221224
target_os = "netbsd",
222225
target_os = "openbsd"
223226
)))]
224-
fn run_return<F>(_event_loop: &mut EventLoop<()>, _event_handler: F)
227+
fn run_return<F>(_event_loop: &mut EventLoop<HandleEvent>, _event_handler: F)
225228
where
226-
F: FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
229+
F: FnMut(Event<'_, HandleEvent>, &EventLoopWindowTarget<HandleEvent>, &mut ControlFlow),
227230
{
228231
panic!("Run return is not supported on this platform!")
229232
}
@@ -304,10 +307,9 @@ pub fn winit_runner(mut app: App) {
304307
// We remove this so that we have ownership over it.
305308
let mut event_loop = app
306309
.world
307-
.remove_non_send_resource::<EventLoop<()>>()
310+
.remove_non_send_resource::<EventLoop<HandleEvent>>()
308311
.unwrap();
309-
app.world
310-
.insert_non_send_resource(event_loop.create_proxy());
312+
app.world.insert_resource(WinitHandler::new(&event_loop));
311313

312314
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
313315
let mut redraw_event_reader = ManualEventReader::<RequestRedraw>::default();
@@ -364,8 +366,8 @@ pub fn winit_runner(mut app: App) {
364366
let mut next_frame = Instant::now();
365367
let mut frame_semaphore = 0u8;
366368

367-
let event_handler = move |event: Event<()>,
368-
event_loop: &EventLoopWindowTarget<()>,
369+
let event_handler = move |event: Event<HandleEvent>,
370+
event_loop: &EventLoopWindowTarget<HandleEvent>,
369371
control_flow: &mut ControlFlow| {
370372
#[cfg(feature = "trace")]
371373
let _span = bevy_utils::tracing::info_span!("winit event_handler").entered();
@@ -670,6 +672,44 @@ pub fn winit_runner(mut app: App) {
670672
delta: Vec2::new(x as f32, y as f32),
671673
});
672674
}
675+
Event::UserEvent(event) => match event {
676+
HandleEvent::Run(rate_multiplier) => {
677+
tick_mode = if let TickMode::Periodic { next_tick, .. } = tick_mode {
678+
TickMode::Periodic {
679+
next_tick,
680+
rate_multiplier,
681+
}
682+
} else {
683+
TickMode::Periodic {
684+
next_tick: Instant::now(),
685+
rate_multiplier,
686+
}
687+
};
688+
}
689+
HandleEvent::RunFullThrottle => {
690+
tick_mode = TickMode::Continuous;
691+
}
692+
HandleEvent::Pause => {
693+
tick_mode = TickMode::Manual { request_steps: 0 };
694+
}
695+
HandleEvent::Step(additional_steps) => {
696+
tick_mode = if let TickMode::Manual { request_steps } = tick_mode {
697+
TickMode::Manual {
698+
request_steps: request_steps + additional_steps,
699+
}
700+
} else {
701+
TickMode::Manual {
702+
request_steps: additional_steps,
703+
}
704+
};
705+
}
706+
HandleEvent::Redraw => {
707+
request_redraw = true;
708+
}
709+
HandleEvent::Exit(code) => {
710+
*control_flow = ControlFlow::ExitWithCode(code);
711+
}
712+
},
673713
Event::Suspended => {
674714
app_active = false;
675715
#[cfg(target_os = "android")]

crates/bevy_winit/src/system.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ use crate::{
3232
///
3333
/// This will default any necessary components if they are not already added.
3434
#[allow(clippy::too_many_arguments)]
35-
pub(crate) fn create_window<'a>(
35+
pub(crate) fn create_window<'a, T: 'static>(
3636
mut commands: Commands,
37-
event_loop: &EventLoopWindowTarget<()>,
37+
event_loop: &EventLoopWindowTarget<T>,
3838
created_windows: impl Iterator<Item = (Entity, Mut<'a, Window>)>,
3939
mut event_writer: EventWriter<WindowCreated>,
4040
mut winit_windows: NonSendMut<WinitWindows>,
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use bevy_ecs::system::Resource;
2+
use bevy_utils::{synccell::SyncCell, tracing::warn};
3+
use winit::event_loop::{EventLoop, EventLoopProxy};
4+
5+
#[derive(Clone, Copy, Debug, PartialEq)]
6+
pub(crate) enum HandleEvent {
7+
Run(f64),
8+
RunFullThrottle,
9+
Pause,
10+
Step(u64),
11+
Redraw,
12+
Exit(i32),
13+
}
14+
15+
/// Controls the operation of the winit runner.
16+
#[derive(Resource)]
17+
pub struct WinitHandler {
18+
proxy: SyncCell<EventLoopProxy<HandleEvent>>,
19+
is_running: bool,
20+
}
21+
22+
impl WinitHandler {
23+
pub(crate) fn new(event_loop: &EventLoop<HandleEvent>) -> Self {
24+
Self {
25+
proxy: SyncCell::new(event_loop.create_proxy()),
26+
is_running: false,
27+
}
28+
}
29+
30+
/// Whether the ticks are automatically run or not.
31+
pub fn is_running(&self) -> bool {
32+
self.is_running
33+
}
34+
35+
/// Run game ticks in sync with real time.
36+
pub fn run(&mut self) {
37+
self.run_at(1.);
38+
}
39+
40+
/// Run game ticks in sync with real time with a specified speed.
41+
pub fn run_at(&mut self, rate_multiplier: f64) {
42+
self.is_running = true;
43+
if let Err(e) = self
44+
.proxy
45+
.get()
46+
.send_event(HandleEvent::Run(rate_multiplier))
47+
{
48+
warn!(%e);
49+
}
50+
}
51+
52+
/// Run game ticks as fast as possible.
53+
pub fn run_full_throttle(&mut self) {
54+
self.is_running = true;
55+
if let Err(e) = self.proxy.get().send_event(HandleEvent::RunFullThrottle) {
56+
warn!(%e);
57+
}
58+
}
59+
60+
/// Stop automatic running of game ticks.
61+
pub fn pause(&mut self) {
62+
self.is_running = false;
63+
if let Err(e) = self.proxy.get().send_event(HandleEvent::Pause) {
64+
warn!(%e);
65+
}
66+
}
67+
68+
/// Run a game tick only once.
69+
pub fn step(&mut self) {
70+
self.step_at(1);
71+
}
72+
73+
/// Run game ticks a specified number of times.
74+
pub fn step_at(&mut self, request_steps: u64) {
75+
self.is_running = false;
76+
if let Err(e) = self
77+
.proxy
78+
.get()
79+
.send_event(HandleEvent::Step(request_steps))
80+
{
81+
warn!(%e);
82+
}
83+
}
84+
85+
/// Requests frame redraw.
86+
pub fn redraw(&mut self) {
87+
if let Err(e) = self.proxy.get().send_event(HandleEvent::Redraw) {
88+
warn!(%e);
89+
}
90+
}
91+
92+
/// Exit the application.
93+
pub fn exit(&mut self, code: i32) {
94+
if let Err(e) = self.proxy.get().send_event(HandleEvent::Exit(code)) {
95+
warn!(%e);
96+
}
97+
}
98+
}

crates/bevy_winit/src/winit_windows.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ pub struct WinitWindows {
3939

4040
impl WinitWindows {
4141
/// Creates a `winit` window and associates it with our entity.
42-
pub fn create_window(
42+
pub fn create_window<T: 'static>(
4343
&mut self,
44-
event_loop: &winit::event_loop::EventLoopWindowTarget<()>,
44+
event_loop: &winit::event_loop::EventLoopWindowTarget<T>,
4545
entity: Entity,
4646
window: &Window,
4747
adapters: &mut AccessKitAdapters,

0 commit comments

Comments
 (0)