From e648169861951d2da851c3f2924fab0bd6c5c673 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 7 Sep 2023 08:25:04 +0200 Subject: [PATCH] Move `ControlFlow` to `EventLoopWindowTarget` Fixes #3042. --- CHANGELOG.md | 2 + README.md | 6 +- examples/child_window.rs | 8 +- examples/control_flow.rs | 14 +- examples/cursor.rs | 8 +- examples/cursor_grab.rs | 10 +- examples/custom_events.rs | 8 +- examples/drag_window.rs | 4 +- examples/fullscreen.rs | 10 +- examples/handling_close.rs | 8 +- examples/ime.rs | 6 +- examples/key_binding.rs | 6 +- examples/mouse_wheel.rs | 8 +- examples/multithreaded.rs | 8 +- examples/multiwindow.rs | 10 +- examples/request_redraw.rs | 8 +- examples/request_redraw_threaded.rs | 8 +- examples/resizable.rs | 8 +- examples/startup_notification.rs | 8 +- examples/theme.rs | 6 +- examples/timer.rs | 10 +- examples/touchpad_gestures.rs | 6 +- examples/transparent.rs | 8 +- examples/web.rs | 8 +- examples/web_aspect_ratio.rs | 4 +- examples/window.rs | 8 +- examples/window_buttons.rs | 8 +- examples/window_debug.rs | 10 +- examples/window_drag_resize.rs | 6 +- examples/window_icon.rs | 8 +- examples/window_ondemand.rs | 10 +- examples/window_option_as_alt.rs | 8 +- examples/window_pump_events.rs | 6 +- examples/window_resize_increments.rs | 8 +- examples/window_tabbing.rs | 10 +- examples/x11_embed.rs | 8 +- src/event.rs | 15 +- src/event_loop.rs | 108 ++++------- src/lib.rs | 18 +- src/platform/pump_events.rs | 10 +- src/platform/run_ondemand.rs | 14 +- src/platform/web.rs | 7 +- src/platform_impl/android/mod.rs | 156 ++++++---------- src/platform_impl/ios/app_state.rs | 52 +++--- src/platform_impl/ios/event_loop.rs | 46 +++-- src/platform_impl/linux/mod.rs | 45 +++-- .../linux/wayland/event_loop/mod.rs | 130 ++++++------- src/platform_impl/linux/wayland/output.rs | 25 +++ src/platform_impl/linux/x11/mod.rs | 165 ++++++++--------- src/platform_impl/macos/app_delegate.rs | 2 +- src/platform_impl/macos/app_state.rs | 85 ++++----- src/platform_impl/macos/event_loop.rs | 51 +++-- src/platform_impl/orbital/event_loop.rs | 92 ++++----- src/platform_impl/web/event_loop/mod.rs | 14 +- src/platform_impl/web/event_loop/runner.rs | 175 +++++++++--------- src/platform_impl/web/event_loop/state.rs | 13 +- .../web/event_loop/window_target.rs | 18 +- src/platform_impl/windows/event_loop.rs | 72 ++++--- .../windows/event_loop/runner.rs | 57 +++--- src/window.rs | 8 +- 60 files changed, 799 insertions(+), 859 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd2cabb133..9002d24854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ And please only add new entries to the top of this list, right below the `# Unre - On Wayland, fix `TouchPhase::Canceled` being sent for moved events. - Mark `startup_notify` unsafe functions as safe. - Fix a bug where Wayland would be chosen on Linux even if the user specified `with_x11`. (#3058) +- **Breaking:** Moved `ControlFlow` to `EventLoopWindowTarget::set_control_flow()` and `EventLoopWindowTarget::control_flow()`. +- **Breaking:** Moved `ControlFlow::Exit` to `EventLoopWindowTarget::set_exit()` and `EventLoopWindowTarget::exiting()` and removed `ControlFlow::ExitWithCode(_)` entirely. # 0.29.1-beta diff --git a/README.md b/README.md index 7879ed784d..0f1cf7f3f4 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,14 @@ fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, - } if window_id == window.id() => *control_flow = ControlFlow::Exit, + } if window_id == window.id() => elwt.exit(), _ => (), } }); diff --git a/examples/child_window.rs b/examples/child_window.rs index 78defa8fc6..4d3110e31a 100644 --- a/examples/child_window.rs +++ b/examples/child_window.rs @@ -46,14 +46,14 @@ fn main() -> Result<(), impl std::error::Error> { println!("parent window: {parent_window:?})"); - event_loop.run(move |event: Event<()>, event_loop, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event: Event<()>, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, window_id } = event { match event { WindowEvent::CloseRequested => { windows.clear(); - *control_flow = ControlFlow::Exit; + elwt.exit(); } WindowEvent::CursorEntered { device_id: _ } => { // On x11, println when the cursor entered in a window even if the child window is created @@ -70,7 +70,7 @@ fn main() -> Result<(), impl std::error::Error> { }, .. } => { - spawn_child_window(&parent_window, event_loop, &mut windows); + spawn_child_window(&parent_window, elwt, &mut windows); } WindowEvent::RedrawRequested => { if let Some(window) = windows.get(&window_id) { diff --git a/examples/control_flow.rs b/examples/control_flow.rs index e9dc0a1ea7..b7537af048 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -9,7 +9,7 @@ use web_time as time; use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::Key, window::WindowBuilder, }; @@ -47,7 +47,7 @@ fn main() -> Result<(), impl std::error::Error> { let mut wait_cancelled = false; let mut close_requested = false; - event_loop.run(move |event, _, control_flow| { + event_loop.run(move |event, elwt| { use winit::event::StartCause; println!("{event:?}"); match event { @@ -104,20 +104,22 @@ fn main() -> Result<(), impl std::error::Error> { } match mode { - Mode::Wait => control_flow.set_wait(), + Mode::Wait => elwt.set_control_flow(ControlFlow::Wait), Mode::WaitUntil => { if !wait_cancelled { - control_flow.set_wait_until(time::Instant::now() + WAIT_TIME); + elwt.set_control_flow(ControlFlow::WaitUntil( + time::Instant::now() + WAIT_TIME, + )); } } Mode::Poll => { thread::sleep(POLL_SLEEP_TIME); - control_flow.set_poll(); + elwt.set_control_flow(ControlFlow::Poll); } }; if close_requested { - control_flow.set_exit(); + elwt.exit(); } } _ => (), diff --git a/examples/cursor.rs b/examples/cursor.rs index 1c6bbea8c3..b51d1481e0 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; @@ -19,8 +19,8 @@ fn main() -> Result<(), impl std::error::Error> { let mut cursor_idx = 0; - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { @@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> { fill::fill_window(&window); } WindowEvent::CloseRequested => { - control_flow.set_exit(); + elwt.exit(); } _ => (), } diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index b6ed5161da..ba0a2aabef 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::{Key, ModifiersState}, window::{CursorGrabMode, WindowBuilder}, }; @@ -22,12 +22,12 @@ fn main() -> Result<(), impl std::error::Error> { let mut modifiers = ModifiersState::default(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::KeyboardInput { event: KeyEvent { @@ -39,7 +39,7 @@ fn main() -> Result<(), impl std::error::Error> { } => { let result = match key { Key::Escape => { - control_flow.set_exit(); + elwt.exit(); Ok(()) } Key::Character(ch) => match ch.to_lowercase().as_str() { diff --git a/examples/custom_events.rs b/examples/custom_events.rs index 21b0d82d1a..ea23e4fc78 100644 --- a/examples/custom_events.rs +++ b/examples/custom_events.rs @@ -5,7 +5,7 @@ fn main() -> Result<(), impl std::error::Error> { use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoopBuilder, + event_loop::{ControlFlow, EventLoopBuilder}, window::WindowBuilder, }; @@ -40,15 +40,15 @@ fn main() -> Result<(), impl std::error::Error> { } }); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::UserEvent(event) => println!("user event: {event:?}"), Event::WindowEvent { event: WindowEvent::CloseRequested, .. - } => control_flow.set_exit(), + } => elwt.exit(), Event::WindowEvent { event: WindowEvent::RedrawRequested, .. diff --git a/examples/drag_window.rs b/examples/drag_window.rs index 9b644a6eff..4937329868 100644 --- a/examples/drag_window.rs +++ b/examples/drag_window.rs @@ -21,12 +21,12 @@ fn main() -> Result<(), impl std::error::Error> { let mut switched = false; let mut entered_id = window_2.id(); - event_loop.run(move |event, _, control_flow| match event { + event_loop.run(move |event, elwt| match event { Event::NewEvents(StartCause::Init) => { eprintln!("Switch which window is to be dragged by pressing \"x\".") } Event::WindowEvent { event, window_id } => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index a0b3d37c33..9af9ab813d 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::dpi::PhysicalSize; use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; -use winit::event_loop::EventLoop; +use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::Key; use winit::window::{Fullscreen, WindowBuilder}; @@ -52,12 +52,12 @@ fn main() -> Result<(), impl std::error::Error> { println!("- I\tToggle mIn size limit"); println!("- A\tToggle mAx size limit"); - event_loop.run(move |event, elwt, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::KeyboardInput { event: KeyEvent { @@ -67,7 +67,7 @@ fn main() -> Result<(), impl std::error::Error> { }, .. } => match key { - Key::Escape => control_flow.set_exit(), + Key::Escape => elwt.exit(), // WARNING: Consider using `key_without_modifers()` if available on your platform. // See the `key_binding` example Key::Character(ch) => match ch.to_lowercase().as_str() { diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 2d6e874362..c5bd37ecd3 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::Key, window::WindowBuilder, }; @@ -22,8 +22,8 @@ fn main() -> Result<(), impl std::error::Error> { let mut close_requested = false; - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { @@ -66,7 +66,7 @@ fn main() -> Result<(), impl std::error::Error> { // event loop (i.e. if it's a multi-window application), you need to // drop the window. That closes it, and results in `Destroyed` being // sent. - control_flow.set_exit(); + elwt.exit(); } } Key::Character("n") => { diff --git a/examples/ime.rs b/examples/ime.rs index a8582b6600..7313cf3a90 100644 --- a/examples/ime.rs +++ b/examples/ime.rs @@ -39,11 +39,11 @@ fn main() -> Result<(), impl std::error::Error> { let mut cursor_position = PhysicalPosition::new(0.0, 0.0); let mut ime_pos = PhysicalPosition::new(0.0, 0.0); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::CursorMoved { position, .. } => { cursor_position = position; } diff --git a/examples/key_binding.rs b/examples/key_binding.rs index 8dcb2966b7..5085cb9f30 100644 --- a/examples/key_binding.rs +++ b/examples/key_binding.rs @@ -31,12 +31,12 @@ fn main() -> Result<(), impl std::error::Error> { let mut modifiers = ModifiersState::default(); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::ModifiersChanged(new) => { modifiers = new.state(); } diff --git a/examples/mouse_wheel.rs b/examples/mouse_wheel.rs index 73e45aa678..74f89e1bcb 100644 --- a/examples/mouse_wheel.rs +++ b/examples/mouse_wheel.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -34,12 +34,12 @@ In both cases the example window should move like the content of a scroll area i In other words, the deltas indicate the direction in which to move the content (in this case the window)." ); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::MouseWheel { delta, .. } => match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { println!("mouse wheel Line Delta: ({x},{y})"); diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 290cb51a9a..379e4c37cd 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -8,7 +8,7 @@ fn main() -> Result<(), impl std::error::Error> { use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::{Key, ModifiersState}, window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel}, }; @@ -173,10 +173,10 @@ fn main() -> Result<(), impl std::error::Error> { } }); } - event_loop.run(move |event, _event_loop, control_flow| { + event_loop.run(move |event, elwt| { match !window_senders.is_empty() { - true => control_flow.set_wait(), - false => control_flow.set_exit(), + true => elwt.set_control_flow(ControlFlow::Wait), + false => elwt.exit(), }; match event { Event::WindowEvent { event, window_id } => match event { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index d153580260..3848924f32 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::Key, window::Window, }; @@ -26,8 +26,8 @@ fn main() -> Result<(), impl std::error::Error> { println!("Press N to open a new window."); - event_loop.run(move |event, event_loop, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, window_id } = event { match event { @@ -38,7 +38,7 @@ fn main() -> Result<(), impl std::error::Error> { windows.remove(&window_id); if windows.is_empty() { - control_flow.set_exit(); + elwt.exit(); } } WindowEvent::KeyboardInput { @@ -51,7 +51,7 @@ fn main() -> Result<(), impl std::error::Error> { is_synthetic: false, .. } if matches!(c.as_ref(), "n" | "N") => { - let window = Window::new(event_loop).unwrap(); + let window = Window::new(elwt).unwrap(); println!("Opened a new window: {:?}", window.id()); windows.insert(window.id(), window); } diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs index ed409f42b0..e4267b37a8 100644 --- a/examples/request_redraw.rs +++ b/examples/request_redraw.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -19,14 +19,14 @@ fn main() -> Result<(), impl std::error::Error> { .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { + event_loop.run(move |event, elwt| { println!("{event:?}"); - control_flow.set_wait(); + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::MouseInput { state: ElementState::Released, .. diff --git a/examples/request_redraw_threaded.rs b/examples/request_redraw_threaded.rs index 0e519a42d8..0d0fe46e58 100644 --- a/examples/request_redraw_threaded.rs +++ b/examples/request_redraw_threaded.rs @@ -7,7 +7,7 @@ fn main() -> Result<(), impl std::error::Error> { use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -33,16 +33,16 @@ fn main() -> Result<(), impl std::error::Error> { } }); - event_loop.run(move |event, _, control_flow| { + event_loop.run(move |event, elwt| { println!("{event:?}"); - control_flow.set_wait(); + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. - } => control_flow.set_exit(), + } => elwt.exit(), Event::WindowEvent { event: WindowEvent::RedrawRequested, .. diff --git a/examples/resizable.rs b/examples/resizable.rs index 468913e709..bf76ad2ffb 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -4,7 +4,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::KeyCode, window::WindowBuilder, }; @@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> { .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::KeyboardInput { event: KeyEvent { diff --git a/examples/startup_notification.rs b/examples/startup_notification.rs index 2eaae31111..6661d0d2b4 100644 --- a/examples/startup_notification.rs +++ b/examples/startup_notification.rs @@ -10,7 +10,7 @@ mod example { use std::rc::Rc; use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; - use winit::event_loop::EventLoop; + use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::Key; use winit::platform::startup_notify::{ EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify, @@ -32,7 +32,7 @@ mod example { let mut counter = 0; let mut create_first_window = false; - event_loop.run(move |event, elwt, flow| { + event_loop.run(move |event, elwt| { match event { Event::Resumed => create_first_window = true, @@ -61,7 +61,7 @@ mod example { // Remove the window from the map. windows.remove(&window_id); if windows.is_empty() { - flow.set_exit(); + elwt.exit(); return; } } @@ -103,7 +103,7 @@ mod example { create_first_window = false; } - flow.set_wait(); + elwt.set_control_flow(ControlFlow::Wait); }) } } diff --git a/examples/theme.rs b/examples/theme.rs index 17381e6e55..d26bdd6257 100644 --- a/examples/theme.rs +++ b/examples/theme.rs @@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> { println!(" (L) Light theme"); println!(" (D) Dark theme"); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { window_id, event } = event { match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::ThemeChanged(theme) if window_id == window.id() => { println!("Theme is changed: {theme:?}") } diff --git a/examples/timer.rs b/examples/timer.rs index 1006fef42f..62c0eee4bb 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -9,7 +9,7 @@ use web_time::Instant; use simple_logger::SimpleLogger; use winit::{ event::{Event, StartCause, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -27,21 +27,21 @@ fn main() -> Result<(), impl std::error::Error> { let timer_length = Duration::new(1, 0); - event_loop.run(move |event, _, control_flow| { + event_loop.run(move |event, elwt| { println!("{event:?}"); match event { Event::NewEvents(StartCause::Init) => { - control_flow.set_wait_until(Instant::now() + timer_length); + elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length)); } Event::NewEvents(StartCause::ResumeTimeReached { .. }) => { - control_flow.set_wait_until(Instant::now() + timer_length); + elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length)); println!("\nTimer\n"); } Event::WindowEvent { event: WindowEvent::CloseRequested, .. - } => control_flow.set_exit(), + } => elwt.exit(), Event::WindowEvent { event: WindowEvent::RedrawRequested, .. diff --git a/examples/touchpad_gestures.rs b/examples/touchpad_gestures.rs index 7c9d2dbd06..75beb224e7 100644 --- a/examples/touchpad_gestures.rs +++ b/examples/touchpad_gestures.rs @@ -19,12 +19,12 @@ fn main() -> Result<(), impl std::error::Error> { println!("Only supported on macOS at the moment."); - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::TouchpadMagnify { delta, .. } => { if delta > 0.0 { println!("Zoomed in {delta}"); diff --git a/examples/transparent.rs b/examples/transparent.rs index b5b053196d..530e1b349b 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -22,13 +22,13 @@ fn main() -> Result<(), impl std::error::Error> { window.set_title("A fantastic window!"); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); println!("{event:?}"); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::RedrawRequested => { fill::fill_window(&window); } diff --git a/examples/web.rs b/examples/web.rs index 0520979bc9..c111596dd6 100644 --- a/examples/web.rs +++ b/examples/web.rs @@ -2,7 +2,7 @@ use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::KeyCode, window::{Fullscreen, WindowBuilder}, }; @@ -21,8 +21,8 @@ pub fn main() -> Result<(), impl std::error::Error> { #[cfg(wasm_platform)] let log_list = wasm::insert_canvas_and_create_log_list(&window); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); #[cfg(wasm_platform)] wasm::log_event(&log_list, &event); @@ -31,7 +31,7 @@ pub fn main() -> Result<(), impl std::error::Error> { Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, - } if window_id == window.id() => control_flow.set_exit(), + } if window_id == window.id() => elwt.exit(), Event::AboutToWait => { window.request_redraw(); } diff --git a/examples/web_aspect_ratio.rs b/examples/web_aspect_ratio.rs index 2b19f77639..3cadcb1a5d 100644 --- a/examples/web_aspect_ratio.rs +++ b/examples/web_aspect_ratio.rs @@ -47,8 +47,8 @@ This example demonstrates the desired future functionality which will possibly b // Render once with the size info we currently have render_circle(&canvas, window.inner_size()); - let _ = event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + let _ = event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { diff --git a/examples/window.rs b/examples/window.rs index 0d466f97b3..194a3e0889 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -20,13 +20,13 @@ fn main() -> Result<(), impl std::error::Error> { .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); println!("{event:?}"); match event { Event::WindowEvent { event, window_id } if window_id == window.id() => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::RedrawRequested => { // Notify the windowing system that we'll be presenting to the window. window.pre_present_notify(); diff --git a/examples/window_buttons.rs b/examples/window_buttons.rs index b6e01c5708..ce98a7e935 100644 --- a/examples/window_buttons.rs +++ b/examples/window_buttons.rs @@ -6,7 +6,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::{DeviceEvents, EventLoop}, + event_loop::{ControlFlow, DeviceEvents, EventLoop}, keyboard::Key, window::{WindowBuilder, WindowButtons}, }; @@ -31,8 +31,8 @@ fn main() -> Result<(), impl std::error::Error> { event_loop.listen_device_events(DeviceEvents::Always); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { window_id, event } = event { match event { @@ -59,7 +59,7 @@ fn main() -> Result<(), impl std::error::Error> { } _ => (), }, - WindowEvent::CloseRequested if window_id == window.id() => control_flow.set_exit(), + WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(), WindowEvent::RedrawRequested => { fill::fill_window(&window); } diff --git a/examples/window_debug.rs b/examples/window_debug.rs index d2d39172e5..d59a07bc5c 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -6,7 +6,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, - event_loop::{DeviceEvents, EventLoop}, + event_loop::{ControlFlow, DeviceEvents, EventLoop}, keyboard::{Key, KeyCode}, window::{Fullscreen, WindowBuilder}, }; @@ -38,8 +38,8 @@ fn main() -> Result<(), impl std::error::Error> { event_loop.listen_device_events(DeviceEvents::Always); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { // This used to use the virtual key, but the new API @@ -115,7 +115,7 @@ fn main() -> Result<(), impl std::error::Error> { window.set_minimized(minimized); } "q" => { - control_flow.set_exit(); + elwt.exit(); } "v" => { visible = !visible; @@ -127,7 +127,7 @@ fn main() -> Result<(), impl std::error::Error> { } _ => (), }, - WindowEvent::CloseRequested if window_id == window.id() => control_flow.set_exit(), + WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(), WindowEvent::RedrawRequested => { fill::fill_window(&window); } diff --git a/examples/window_drag_resize.rs b/examples/window_drag_resize.rs index ead0b96566..b1d3cba43d 100644 --- a/examples/window_drag_resize.rs +++ b/examples/window_drag_resize.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event_loop::EventLoop, keyboard::Key, window::{CursorIcon, ResizeDirection, WindowBuilder}, }; @@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> { let mut border = false; let mut cursor_location = None; - event_loop.run(move |event, _, control_flow| match event { + event_loop.run(move |event, elwt| match event { Event::NewEvents(StartCause::Init) => { eprintln!("Press 'B' to toggle borderless") } Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::CursorMoved { position, .. } => { if !window.is_decorated() { let new_location = diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 5dd4c1c6a9..39473b96d1 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -5,7 +5,7 @@ use std::path::Path; use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::{Icon, WindowBuilder}, }; @@ -33,12 +33,12 @@ fn main() -> Result<(), impl std::error::Error> { .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = event { match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::DroppedFile(path) => { window.set_window_icon(Some(load_icon(&path))); } diff --git a/examples/window_ondemand.rs b/examples/window_ondemand.rs index e90b0c87f9..81d1b4e422 100644 --- a/examples/window_ondemand.rs +++ b/examples/window_ondemand.rs @@ -10,7 +10,7 @@ fn main() -> Result<(), impl std::error::Error> { use winit::{ error::EventLoopError, event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, platform::run_ondemand::EventLoopExtRunOnDemand, window::{Window, WindowBuilder, WindowId}, }; @@ -30,8 +30,8 @@ fn main() -> Result<(), impl std::error::Error> { fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> { let mut app = App::default(); - event_loop.run_ondemand(move |event, event_loop, control_flow| { - control_flow.set_wait(); + event_loop.run_ondemand(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); println!("Run {idx}: {:?}", event); if let Some(window) = &app.window { @@ -60,7 +60,7 @@ fn main() -> Result<(), impl std::error::Error> { } if id == window_id => { println!("--------------------------------------------------------- Window {idx} Destroyed"); app.window_id = None; - control_flow.set_exit(); + elwt.exit(); } _ => (), } @@ -68,7 +68,7 @@ fn main() -> Result<(), impl std::error::Error> { let window = WindowBuilder::new() .with_title("Fantastic window number one!") .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) - .build(event_loop) + .build(elwt) .unwrap(); app.window_id = Some(window.id()); app.window = Some(window); diff --git a/examples/window_option_as_alt.rs b/examples/window_option_as_alt.rs index c52070d3f6..ca919af4ab 100644 --- a/examples/window_option_as_alt.rs +++ b/examples/window_option_as_alt.rs @@ -7,7 +7,7 @@ use winit::platform::macos::{OptionAsAlt, WindowExtMacOS}; use winit::{ event::ElementState, event::{Event, MouseButton, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -31,14 +31,14 @@ fn main() -> Result<(), impl std::error::Error> { let mut option_as_alt = window.option_as_alt(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, - } if window_id == window.id() => control_flow.set_exit(), + } if window_id == window.id() => elwt.exit(), Event::WindowEvent { event, .. } => match event { WindowEvent::MouseInput { state: ElementState::Pressed, diff --git a/examples/window_pump_events.rs b/examples/window_pump_events.rs index e727da412d..0e8a5b29e8 100644 --- a/examples/window_pump_events.rs +++ b/examples/window_pump_events.rs @@ -32,8 +32,8 @@ fn main() -> std::process::ExitCode { 'main: loop { let timeout = Some(Duration::ZERO); - let status = event_loop.pump_events(timeout, |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + let status = event_loop.pump_events(timeout, |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, .. } = &event { // Print only Window events to reduce noise @@ -44,7 +44,7 @@ fn main() -> std::process::ExitCode { Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, - } if window_id == window.id() => control_flow.set_exit(), + } if window_id == window.id() => elwt.exit(), Event::AboutToWait => { window.request_redraw(); } diff --git a/examples/window_resize_increments.rs b/examples/window_resize_increments.rs index 73b045f517..2eb584504c 100644 --- a/examples/window_resize_increments.rs +++ b/examples/window_resize_increments.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::Key, window::WindowBuilder, }; @@ -24,12 +24,12 @@ fn main() -> Result<(), impl std::error::Error> { let mut has_increments = true; - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event, window_id } if window_id == window.id() => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::CloseRequested => elwt.exit(), WindowEvent::KeyboardInput { event: KeyEvent { diff --git a/examples/window_tabbing.rs b/examples/window_tabbing.rs index 050fdc0963..7a6632bdbc 100644 --- a/examples/window_tabbing.rs +++ b/examples/window_tabbing.rs @@ -8,7 +8,7 @@ use simple_logger::SimpleLogger; #[cfg(target_os = "macos")] use winit::{ event::{ElementState, Event, KeyEvent, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, keyboard::Key, platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}, window::{Window, WindowBuilder}, @@ -30,8 +30,8 @@ fn main() -> Result<(), impl std::error::Error> { println!("Press N to open a new window."); - event_loop.run(move |event, event_loop, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); if let Event::WindowEvent { event, window_id } = event { match event { @@ -42,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> { windows.remove(&window_id); if windows.is_empty() { - control_flow.set_exit(); + elwt.exit(); } } WindowEvent::Resized(_) => { @@ -64,7 +64,7 @@ fn main() -> Result<(), impl std::error::Error> { let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier(); let window = WindowBuilder::new() .with_tabbing_identifier(&tabbing_id) - .build(event_loop) + .build(elwt) .unwrap(); println!("Added a new tab: {:?}", window.id()); windows.insert(window.id(), window); diff --git a/examples/x11_embed.rs b/examples/x11_embed.rs index 29f84e7857..c852681116 100644 --- a/examples/x11_embed.rs +++ b/examples/x11_embed.rs @@ -10,7 +10,7 @@ mod imple { use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, - event_loop::EventLoop, + event_loop::{ControlFlow, EventLoop}, platform::x11::WindowBuilderExtX11, window::WindowBuilder, }; @@ -32,14 +32,14 @@ mod imple { .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); + event_loop.run(move |event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); match event { Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, - } if window_id == window.id() => control_flow.set_exit(), + } if window_id == window.id() => elwt.exit(), Event::AboutToWait => { window.request_redraw(); } diff --git a/src/event.rs b/src/event.rs index 0ca6e8753a..16c46c98d8 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,25 +7,24 @@ //! approximate the basic ordering loop of [`EventLoop::run(...)`] like this: //! //! ```rust,ignore -//! let mut control_flow = ControlFlow::Poll; //! let mut start_cause = StartCause::Init; //! -//! while control_flow != ControlFlow::Exit { -//! event_handler(NewEvents(start_cause), ..., &mut control_flow); +//! while !elwt.exiting() { +//! event_handler(NewEvents(start_cause), elwt); //! //! for e in (window events, user events, device events) { -//! event_handler(e, ..., &mut control_flow); +//! event_handler(e, elwt); //! } //! //! for w in (redraw windows) { -//! event_handler(RedrawRequested(w), ..., &mut control_flow); +//! event_handler(RedrawRequested(w), elwt); //! } //! -//! event_handler(AboutToWait, ..., &mut control_flow); -//! start_cause = wait_if_necessary(control_flow); +//! event_handler(AboutToWait, elwt); +//! start_cause = wait_if_necessary(); //! } //! -//! event_handler(LoopExiting, ..., &mut control_flow); +//! event_handler(LoopExiting, elwt); //! ``` //! //! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully diff --git a/src/event_loop.rs b/src/event_loop.rs index 417c1d4c4f..9c2c02a9b4 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -147,20 +147,12 @@ impl fmt::Debug for EventLoopWindowTarget { } } -/// Set by the user callback given to the [`EventLoop::run`] method. +/// Set through [`EventLoopWindowTarget::set_control_flow()`]. /// /// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted. /// /// Defaults to [`Poll`]. /// -/// ## Persistency -/// -/// Almost every change is persistent between multiple calls to the event loop closure within a -/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset. -/// Changes are **not** persistent between multiple calls to `run_ondemand` - issuing a new call will -/// reset the control flow to [`Poll`]. -/// -/// [`ExitWithCode`]: Self::ExitWithCode /// [`Poll`]: Self::Poll #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ControlFlow { @@ -180,81 +172,22 @@ pub enum ControlFlow { /// /// [`Poll`]: Self::Poll WaitUntil(Instant), - - /// Send a [`LoopExiting`] event and stop the event loop. This variant is *sticky* - once set, - /// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will - /// result in the `control_flow` parameter being reset to `ExitWithCode`. - /// - /// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this - /// with exit code 0. - /// - /// ## Platform-specific - /// - /// - **Android / iOS / Web:** The supplied exit code is unused. - /// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used, - /// which can cause surprises with negative exit values (`-42` would end up as `214`). See - /// [`std::process::exit`]. - /// - /// [`LoopExiting`]: Event::LoopExiting - /// [`Exit`]: ControlFlow::Exit - ExitWithCode(i32), } impl ControlFlow { - /// Alias for [`ExitWithCode`]`(0)`. - /// - /// [`ExitWithCode`]: Self::ExitWithCode - #[allow(non_upper_case_globals)] - pub const Exit: Self = Self::ExitWithCode(0); - - /// Sets this to [`Poll`]. - /// - /// [`Poll`]: Self::Poll - pub fn set_poll(&mut self) { - *self = Self::Poll; - } - - /// Sets this to [`Wait`]. - /// - /// [`Wait`]: Self::Wait - pub fn set_wait(&mut self) { - *self = Self::Wait; - } - - /// Sets this to [`WaitUntil`]`(instant)`. - /// - /// [`WaitUntil`]: Self::WaitUntil - pub fn set_wait_until(&mut self, instant: Instant) { - *self = Self::WaitUntil(instant); - } - - /// Sets this to wait until a timeout has expired. + /// Creates a [`ControlFlow`] that waits until a timeout has expired. /// /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is /// instead set to [`Wait`]. /// /// [`WaitUntil`]: Self::WaitUntil /// [`Wait`]: Self::Wait - pub fn set_wait_timeout(&mut self, timeout: Duration) { + pub fn wait_duration(timeout: Duration) -> Self { match Instant::now().checked_add(timeout) { - Some(instant) => self.set_wait_until(instant), - None => self.set_wait(), + Some(instant) => Self::WaitUntil(instant), + None => Self::Wait, } } - - /// Sets this to [`ExitWithCode`]`(code)`. - /// - /// [`ExitWithCode`]: Self::ExitWithCode - pub fn set_exit_with_code(&mut self, code: i32) { - *self = Self::ExitWithCode(code); - } - - /// Sets this to [`Exit`]. - /// - /// [`Exit`]: Self::Exit - pub fn set_exit(&mut self) { - *self = Self::Exit; - } } impl Default for ControlFlow { @@ -286,8 +219,7 @@ impl EventLoop { /// Since the closure is `'static`, it must be a `move` closure if it needs to /// access any data from the calling context. /// - /// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the - /// event loop's behavior. + /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// /// ## Platform-specific /// @@ -306,12 +238,12 @@ impl EventLoop { /// /// This function won't be available with `target_feature = "exception-handling"`. /// - /// [`ControlFlow`]: crate::event_loop::ControlFlow + /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow #[inline] #[cfg(not(all(wasm_platform, target_feature = "exception-handling")))] pub fn run(self, event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &EventLoopWindowTarget), { self.event_loop.run(event_handler) } @@ -377,6 +309,30 @@ impl EventLoopWindowTarget { pub fn listen_device_events(&self, allowed: DeviceEvents) { self.p.listen_device_events(allowed); } + + /// Sets the [`ControlFlow`]. + pub fn set_control_flow(&self, control_flow: ControlFlow) { + self.p.set_control_flow(control_flow) + } + + /// Gets the current [`ControlFlow`]. + pub fn control_flow(&self) -> ControlFlow { + self.p.control_flow() + } + + /// This exits the event loop. + /// + /// See [`LoopExiting`](Event::LoopExiting). + pub fn exit(&self) { + self.p.exit() + } + + /// Returns if the [`EventLoop`] is about to stop. + /// + /// See [`exit()`](Self::exit). + pub fn exiting(&self) -> bool { + self.p.exiting() + } } unsafe impl HasRawDisplayHandle for EventLoopWindowTarget { diff --git a/src/lib.rs b/src/lib.rs index d7f01551f2..7a2bb2d354 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,8 +30,7 @@ //! //! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and -//! will run until the `control_flow` argument given to the closure is set to -//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which +//! will run until [`exit()`] is used, at which //! point [`Event`]`::`[`LoopExiting`] is emitted and the entire program terminates. //! //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator`-based event loop @@ -44,30 +43,29 @@ //! ```no_run //! use winit::{ //! event::{Event, WindowEvent}, -//! event_loop::EventLoop, +//! event_loop::{ControlFlow, EventLoop}, //! window::WindowBuilder, //! }; //! //! let event_loop = EventLoop::new().unwrap(); //! let window = WindowBuilder::new().build(&event_loop).unwrap(); //! -//! event_loop.run(move |event, _, control_flow| { +//! event_loop.run(move |event, elwt| { //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't //! // dispatched any events. This is ideal for games and similar applications. -//! control_flow.set_poll(); +//! elwt.set_control_flow(ControlFlow::Poll); //! //! // ControlFlow::Wait pauses the event loop if no events are available to process. //! // This is ideal for non-game applications that only update in response to user //! // input, and uses significantly less power/CPU time than ControlFlow::Poll. -//! control_flow.set_wait(); -//! +//! elwt.set_control_flow(ControlFlow::Wait); //! match event { //! Event::WindowEvent { //! event: WindowEvent::CloseRequested, //! .. //! } => { //! println!("The close button was pressed; stopping"); -//! control_flow.set_exit(); +//! elwt.exit(); //! }, //! Event::AboutToWait => { //! // Application update code. @@ -115,9 +113,7 @@ //! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events //! [`EventLoop::new()`]: event_loop::EventLoop::new //! [event_loop_run]: event_loop::EventLoop::run -//! [`ControlFlow`]: event_loop::ControlFlow -//! [`Exit`]: event_loop::ControlFlow::Exit -//! [`ExitWithCode`]: event_loop::ControlFlow::ExitWithCode +//! [`exit()`]: event_loop::EventLoopWindowTarget::exit //! [`Window`]: window::Window //! [`WindowId`]: window::WindowId //! [`WindowBuilder`]: window::WindowBuilder diff --git a/src/platform/pump_events.rs b/src/platform/pump_events.rs index fa970ca1d2..1ea11af648 100644 --- a/src/platform/pump_events.rs +++ b/src/platform/pump_events.rs @@ -2,7 +2,7 @@ use std::time::Duration; use crate::{ event::Event, - event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, + event_loop::{EventLoop, EventLoopWindowTarget}, }; /// The return status for `pump_events` @@ -63,7 +63,7 @@ pub trait EventLoopExtPumpEvents { /// /// 'main: loop { /// let timeout = Some(Duration::ZERO); - /// let status = event_loop.pump_events(timeout, |event, _, control_flow| { + /// let status = event_loop.pump_events(timeout, |event, elwt| { /// # if let Event::WindowEvent { event, .. } = &event { /// # // Print only Window events to reduce noise /// # println!("{event:?}"); @@ -73,7 +73,7 @@ pub trait EventLoopExtPumpEvents { /// Event::WindowEvent { /// event: WindowEvent::CloseRequested, /// window_id, - /// } if window_id == window.id() => control_flow.set_exit(), + /// } if window_id == window.id() => elwt.exit(), /// Event::AboutToWait => { /// window.request_redraw(); /// } @@ -174,7 +174,7 @@ pub trait EventLoopExtPumpEvents { /// callback. fn pump_events(&mut self, timeout: Option, event_handler: F) -> PumpStatus where - F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow); + F: FnMut(Event, &EventLoopWindowTarget); } impl EventLoopExtPumpEvents for EventLoop { @@ -182,7 +182,7 @@ impl EventLoopExtPumpEvents for EventLoop { fn pump_events(&mut self, timeout: Option, event_handler: F) -> PumpStatus where - F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &EventLoopWindowTarget), { self.event_loop.pump_events(timeout, event_handler) } diff --git a/src/platform/run_ondemand.rs b/src/platform/run_ondemand.rs index 031b670c79..6e1c0d6ba5 100644 --- a/src/platform/run_ondemand.rs +++ b/src/platform/run_ondemand.rs @@ -1,7 +1,7 @@ use crate::{ error::EventLoopError, event::Event, - event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, + event_loop::{EventLoop, EventLoopWindowTarget}, }; #[cfg(doc)] @@ -17,7 +17,7 @@ pub trait EventLoopExtRunOnDemand { /// /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures /// and it is possible to return control back to the caller without - /// consuming the `EventLoop` (by setting the `control_flow` to [`ControlFlow::Exit`]) and + /// consuming the `EventLoop` (by using [`exit()`]) and /// so the event loop can be re-run after it has exit. /// /// It's expected that each run of the loop will be for orthogonal instantiations of your @@ -32,8 +32,7 @@ pub trait EventLoopExtRunOnDemand { /// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume /// lifecycle) - which can be used to consistently initialize application state. /// - /// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the - /// event loop's behavior. + /// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// /// # Caveats /// - This extension isn't available on all platforms, since it's not always possible to @@ -57,9 +56,12 @@ pub trait EventLoopExtRunOnDemand { /// polled to ask for new events. Events are delivered via callbacks based /// on an event loop that is internal to the browser itself. /// - **iOS:** It's not possible to stop and start an `NSApplication` repeatedly on iOS. + /// + /// [`exit()`]: EventLoopWindowTarget::exit + /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow fn run_ondemand(&mut self, event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow); + F: FnMut(Event, &EventLoopWindowTarget); } impl EventLoopExtRunOnDemand for EventLoop { @@ -67,7 +69,7 @@ impl EventLoopExtRunOnDemand for EventLoop { fn run_ondemand(&mut self, event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &EventLoopWindowTarget), { self.event_loop.run_ondemand(event_handler) } diff --git a/src/platform/web.rs b/src/platform/web.rs index 4b3d9e5287..7e5d25eb7f 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -28,7 +28,6 @@ //! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding use crate::event::Event; -use crate::event_loop::ControlFlow; use crate::event_loop::EventLoop; use crate::event_loop::EventLoopWindowTarget; use crate::window::{Window, WindowBuilder}; @@ -122,8 +121,7 @@ pub trait EventLoopExtWebSys { /// event loop when switching between tabs on a single page application. fn spawn(self, event_handler: F) where - F: 'static - + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow); + F: 'static + FnMut(Event, &EventLoopWindowTarget); } impl EventLoopExtWebSys for EventLoop { @@ -131,8 +129,7 @@ impl EventLoopExtWebSys for EventLoop { fn spawn(self, event_handler: F) where - F: 'static - + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event, &EventLoopWindowTarget), { self.event_loop.spawn(event_handler) } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 48699c7ef8..aadf5cbb37 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1,6 +1,7 @@ #![cfg(android_platform)] use std::{ + cell::Cell, collections::VecDeque, hash::Hash, sync::{ @@ -147,7 +148,6 @@ pub struct EventLoop { loop_running: bool, // Dispatched `NewEvents` running: bool, pending_redraw: bool, - control_flow: ControlFlow, cause: StartCause, ignore_volume_keys: bool, combining_accent: Option, @@ -168,23 +168,6 @@ impl Default for PlatformSpecificEventLoopAttributes { } } -fn sticky_exit_callback( - evt: event::Event, - target: &RootELW, - control_flow: &mut ControlFlow, - callback: &mut F, -) where - F: FnMut(event::Event, &RootELW, &mut ControlFlow), -{ - // make ControlFlow::ExitWithCode sticky by providing a dummy - // control flow reference if it is already ExitWithCode. - if let ControlFlow::ExitWithCode(code) = *control_flow { - callback(evt, target, &mut ControlFlow::ExitWithCode(code)) - } else { - callback(evt, target, control_flow) - } -} - impl EventLoop { pub(crate) fn new( attributes: &PlatformSpecificEventLoopAttributes, @@ -199,6 +182,8 @@ impl EventLoop { window_target: event_loop::EventLoopWindowTarget { p: EventLoopWindowTarget { app: android_app.clone(), + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(false), redraw_requester: RedrawRequester::new( &redraw_flag, android_app.create_waker(), @@ -213,7 +198,6 @@ impl EventLoop { loop_running: false, running: false, pending_redraw: false, - control_flow: Default::default(), cause: StartCause::Init, ignore_volume_keys: attributes.ignore_volume_keys, combining_accent: None, @@ -222,41 +206,25 @@ impl EventLoop { fn single_iteration(&mut self, main_event: Option>, callback: &mut F) where - F: FnMut(event::Event, &RootELW, &mut ControlFlow), + F: FnMut(event::Event, &RootELW), { trace!("Mainloop iteration"); let cause = self.cause; - let mut control_flow = self.control_flow; let mut pending_redraw = self.pending_redraw; let mut resized = false; - sticky_exit_callback( - event::Event::NewEvents(cause), - self.window_target(), - &mut control_flow, - callback, - ); + callback(event::Event::NewEvents(cause), self.window_target()); if let Some(event) = main_event { trace!("Handling main event {:?}", event); match event { MainEvent::InitWindow { .. } => { - sticky_exit_callback( - event::Event::Resumed, - self.window_target(), - &mut control_flow, - callback, - ); + callback(event::Event::Resumed, self.window_target()); } MainEvent::TerminateWindow { .. } => { - sticky_exit_callback( - event::Event::Suspended, - self.window_target(), - &mut control_flow, - callback, - ); + callback(event::Event::Suspended, self.window_target()); } MainEvent::WindowResized { .. } => resized = true, MainEvent::RedrawNeeded { .. } => pending_redraw = true, @@ -265,26 +233,22 @@ impl EventLoop { } MainEvent::GainedFocus => { *HAS_FOCUS.write().unwrap() = true; - sticky_exit_callback( + callback( event::Event::WindowEvent { window_id: window::WindowId(WindowId), event: event::WindowEvent::Focused(true), }, self.window_target(), - &mut control_flow, - callback, ); } MainEvent::LostFocus => { *HAS_FOCUS.write().unwrap() = false; - sticky_exit_callback( + callback( event::Event::WindowEvent { window_id: window::WindowId(WindowId), event: event::WindowEvent::Focused(false), }, self.window_target(), - &mut control_flow, - callback, ); } MainEvent::ConfigChanged { .. } => { @@ -304,12 +268,7 @@ impl EventLoop { scale_factor, }, }; - sticky_exit_callback( - event, - self.window_target(), - &mut control_flow, - callback, - ); + callback(event, self.window_target()); } } MainEvent::LowMemory => { @@ -363,9 +322,8 @@ impl EventLoop { // Process input events match android_app.input_events_iter() { Ok(mut input_iter) => loop { - let read_event = input_iter.next(|event| { - self.handle_input_event(&android_app, event, &mut control_flow, callback) - }); + let read_event = + input_iter.next(|event| self.handle_input_event(&android_app, event, callback)); if !read_event { break; @@ -379,12 +337,7 @@ impl EventLoop { // Empty the user event buffer { while let Ok(event) = self.user_events_receiver.try_recv() { - sticky_exit_callback( - crate::event::Event::UserEvent(event), - self.window_target(), - &mut control_flow, - callback, - ); + callback(crate::event::Event::UserEvent(event), self.window_target()); } } @@ -401,7 +354,7 @@ impl EventLoop { window_id: window::WindowId(WindowId), event: event::WindowEvent::Resized(size), }; - sticky_exit_callback(event, self.window_target(), &mut control_flow, callback); + callback(event, self.window_target()); } pending_redraw |= self.redraw_flag.get_and_reset(); @@ -411,19 +364,13 @@ impl EventLoop { window_id: window::WindowId(WindowId), event: event::WindowEvent::RedrawRequested, }; - sticky_exit_callback(event, self.window_target(), &mut control_flow, callback); + callback(event, self.window_target()); } } // This is always the last event we dispatch before poll again - sticky_exit_callback( - event::Event::AboutToWait, - self.window_target(), - &mut control_flow, - callback, - ); - - self.control_flow = control_flow; + callback(event::Event::AboutToWait, self.window_target()); + self.pending_redraw = pending_redraw; } @@ -431,11 +378,10 @@ impl EventLoop { &mut self, android_app: &AndroidApp, event: &InputEvent<'_>, - control_flow: &mut ControlFlow, callback: &mut F, ) -> InputStatus where - F: FnMut(event::Event, &RootELW, &mut ControlFlow), + F: FnMut(event::Event, &RootELW), { let mut input_status = InputStatus::Handled; match event { @@ -483,7 +429,7 @@ impl EventLoop { force: None, }), }; - sticky_exit_callback(event, self.window_target(), control_flow, callback); + callback(event, self.window_target()); } } } @@ -526,7 +472,7 @@ impl EventLoop { is_synthetic: false, }, }; - sticky_exit_callback(event, self.window_target(), control_flow, callback); + callback(event, self.window_target()); } } } @@ -540,14 +486,14 @@ impl EventLoop { pub fn run(mut self, event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(event::Event, &event_loop::EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(event::Event, &event_loop::EventLoopWindowTarget), { self.run_ondemand(event_handler) } pub fn run_ondemand(&mut self, mut event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(event::Event, &event_loop::EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(event::Event, &event_loop::EventLoopWindowTarget), { if self.loop_running { return Err(EventLoopError::AlreadyRunning); @@ -570,7 +516,7 @@ impl EventLoop { pub fn pump_events(&mut self, timeout: Option, mut callback: F) -> PumpStatus where - F: FnMut(event::Event, &RootELW, &mut ControlFlow), + F: FnMut(event::Event, &RootELW), { if !self.loop_running { self.loop_running = true; @@ -580,7 +526,7 @@ impl EventLoop { // than once self.pending_redraw = false; self.cause = StartCause::Init; - self.control_flow = ControlFlow::Poll; + self.set_control_flow(ControlFlow::default()); // run the initial loop iteration self.single_iteration(None, &mut callback); @@ -588,21 +534,15 @@ impl EventLoop { // Consider the possibility that the `StartCause::Init` iteration could // request to Exit - if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { + if !self.exiting() { self.poll_events_with_timeout(timeout, &mut callback); } - if let ControlFlow::ExitWithCode(code) = self.control_flow { + if self.exiting() { self.loop_running = false; - let mut dummy = self.control_flow; - sticky_exit_callback( - event::Event::LoopExiting, - self.window_target(), - &mut dummy, - &mut callback, - ); + callback(event::Event::LoopExiting, self.window_target()); - PumpStatus::Exit(code) + PumpStatus::Exit(0) } else { PumpStatus::Continue } @@ -610,7 +550,7 @@ impl EventLoop { fn poll_events_with_timeout(&mut self, mut timeout: Option, mut callback: F) where - F: FnMut(event::Event, &RootELW, &mut ControlFlow), + F: FnMut(event::Event, &RootELW), { let start = Instant::now(); @@ -621,14 +561,12 @@ impl EventLoop { // If we already have work to do then we don't want to block on the next poll Some(Duration::ZERO) } else { - let control_flow_timeout = match self.control_flow { + let control_flow_timeout = match self.control_flow() { ControlFlow::Wait => None, ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::WaitUntil(wait_deadline) => { Some(wait_deadline.saturating_duration_since(start)) } - // `ExitWithCode()` will be reset to `Poll` before polling - ControlFlow::ExitWithCode(_code) => unreachable!(), }; min_timeout(control_flow_timeout, timeout) @@ -663,7 +601,7 @@ impl EventLoop { } } - self.cause = match self.control_flow { + self.cause = match self.control_flow() { ControlFlow::Poll => StartCause::Poll, ControlFlow::Wait => StartCause::WaitCancelled { start, @@ -682,8 +620,6 @@ impl EventLoop { } } } - // `ExitWithCode()` will be reset to `Poll` before polling - ControlFlow::ExitWithCode(_code) => unreachable!(), }; self.single_iteration(main_event, &mut callback); @@ -700,6 +636,18 @@ impl EventLoop { waker: self.android_app.create_waker(), } } + + fn set_control_flow(&self, control_flow: ControlFlow) { + self.window_target.p.set_control_flow(control_flow) + } + + fn control_flow(&self) -> ControlFlow { + self.window_target.p.control_flow() + } + + fn exiting(&self) -> bool { + self.window_target.p.exiting() + } } pub struct EventLoopProxy { @@ -728,6 +676,8 @@ impl EventLoopProxy { pub struct EventLoopWindowTarget { app: AndroidApp, + control_flow: Cell, + exit: Cell, redraw_requester: RedrawRequester, _marker: std::marker::PhantomData, } @@ -749,6 +699,22 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::Android(AndroidDisplayHandle::empty()) } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.control_flow.set(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.control_flow.get() + } + + pub(crate) fn exit(&self) { + self.exit.set(true) + } + + pub(crate) fn exiting(&self) -> bool { + self.exit.get() + } } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index df371ecb62..6dd7bf8327 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -121,7 +121,7 @@ enum AppStateImpl { Terminated, } -struct AppState { +pub(crate) struct AppState { // This should never be `None`, except for briefly during a state transition. app_state: Option, control_flow: ControlFlow, @@ -129,7 +129,7 @@ struct AppState { } impl AppState { - fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> { + pub(crate) fn get_mut(_mtm: MainThreadMarker) -> RefMut<'static, AppState> { // basically everything in UIKit requires the main thread, so it's pointless to use the // std::sync APIs. // must be mut because plain `static` requires `Sync` @@ -290,7 +290,6 @@ impl AppState { }; (waiting_event_handler, event) } - (ControlFlow::ExitWithCode(_), _) => bug!("unexpected `ControlFlow` `Exit`"), s => bug!("`EventHandler` unexpectedly woke up {:?}", s), }; @@ -443,12 +442,6 @@ impl AppState { }); self.waker.start() } - (_, ControlFlow::ExitWithCode(_)) => { - // https://developer.apple.com/library/archive/qa/qa1561/_index.html - // it is not possible to quit an iOS app gracefully and programatically - warn!("`ControlFlow::Exit` ignored on iOS"); - self.control_flow = old - } } } @@ -458,6 +451,14 @@ impl AppState { s => bug!("`LoopExiting` happened while not processing events {:?}", s), } } + + pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) { + self.control_flow = control_flow; + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.control_flow + } } pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id) { @@ -602,7 +603,6 @@ pub(crate) fn handle_nonuser_events>( processing_redraws, } => (event_handler, active_control_flow, processing_redraws), }; - let mut control_flow = this.control_flow; drop(this); for wrapper in events { @@ -616,10 +616,10 @@ pub(crate) fn handle_nonuser_events>( event ); } - event_handler.handle_nonuser_event(event, &mut control_flow) + event_handler.handle_nonuser_event(event) } EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut event_handler, control_flow, event) + handle_hidpi_proxy(&mut event_handler, event) } } } @@ -657,7 +657,6 @@ pub(crate) fn handle_nonuser_events>( active_control_flow, } }); - this.control_flow = control_flow; break; } drop(this); @@ -673,10 +672,10 @@ pub(crate) fn handle_nonuser_events>( event ); } - event_handler.handle_nonuser_event(event, &mut control_flow) + event_handler.handle_nonuser_event(event) } EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut event_handler, control_flow, event) + handle_hidpi_proxy(&mut event_handler, event) } } } @@ -685,7 +684,6 @@ pub(crate) fn handle_nonuser_events>( fn handle_user_events(mtm: MainThreadMarker) { let mut this = AppState::get_mut(mtm); - let mut control_flow = this.control_flow; let (mut event_handler, active_control_flow, processing_redraws) = match this.try_user_callback_transition() { UserCallbackTransitionResult::ReentrancyPrevented { .. } => { @@ -702,7 +700,7 @@ fn handle_user_events(mtm: MainThreadMarker) { } drop(this); - event_handler.handle_user_events(&mut control_flow); + event_handler.handle_user_events(); loop { let mut this = AppState::get_mut(mtm); @@ -726,22 +724,19 @@ fn handle_user_events(mtm: MainThreadMarker) { queued_gpu_redraws, active_control_flow, }); - this.control_flow = control_flow; break; } drop(this); for wrapper in queued_events { match wrapper { - EventWrapper::StaticEvent(event) => { - event_handler.handle_nonuser_event(event, &mut control_flow) - } + EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event), EventWrapper::ScaleFactorChanged(event) => { - handle_hidpi_proxy(&mut event_handler, control_flow, event) + handle_hidpi_proxy(&mut event_handler, event) } } } - event_handler.handle_user_events(&mut control_flow); + event_handler.handle_user_events(); } } @@ -782,17 +777,12 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) { pub fn terminated(mtm: MainThreadMarker) { let mut this = AppState::get_mut(mtm); let mut event_handler = this.terminated_transition(); - let mut control_flow = this.control_flow; drop(this); - event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow) + event_handler.handle_nonuser_event(Event::LoopExiting) } -fn handle_hidpi_proxy( - event_handler: &mut Box, - mut control_flow: ControlFlow, - event: ScaleFactorChanged, -) { +fn handle_hidpi_proxy(event_handler: &mut Box, event: ScaleFactorChanged) { let ScaleFactorChanged { suggested_size, scale_factor, @@ -806,7 +796,7 @@ fn handle_hidpi_proxy( inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), }, }; - event_handler.handle_nonuser_event(event, &mut control_flow); + event_handler.handle_nonuser_event(event); let (view, screen_frame) = get_view_and_screen_frame(&window); let physical_size = *new_inner_size.lock().unwrap(); drop(new_inner_size); diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 0f3dcb1780..883acfc8a4 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -28,8 +28,11 @@ use crate::{ platform::ios::Idiom, }; -use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen}; use super::{app_state, monitor, view, MonitorHandle}; +use super::{ + app_state::AppState, + uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen}, +}; #[derive(Debug)] pub struct EventLoopWindowTarget { @@ -52,6 +55,24 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::UiKit(UiKitDisplayHandle::empty()) } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + AppState::get_mut(self.mtm).set_control_flow(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + AppState::get_mut(self.mtm).control_flow() + } + + pub(crate) fn exit(&self) { + // https://developer.apple.com/library/archive/qa/qa1561/_index.html + // it is not possible to quit an iOS app gracefully and programatically + warn!("`ControlFlow::Exit` ignored on iOS"); + } + + pub(crate) fn exiting(&self) -> bool { + false + } } pub struct EventLoop { @@ -102,7 +123,7 @@ impl EventLoop { pub fn run(self, event_handler: F) -> ! where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { unsafe { let application = UIApplication::shared(self.mtm); @@ -114,7 +135,7 @@ impl EventLoop { ); let event_handler = std::mem::transmute::< - Box, &RootEventLoopWindowTarget, &mut ControlFlow)>, + Box, &RootEventLoopWindowTarget)>, Box>, >(Box::new(event_handler)); @@ -314,12 +335,11 @@ fn setup_control_flow_observers() { #[derive(Debug)] pub enum Never {} -type EventHandlerCallback = - dyn FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow) + 'static; +type EventHandlerCallback = dyn FnMut(Event, &RootEventLoopWindowTarget) + 'static; pub trait EventHandler: Debug { - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow); - fn handle_user_events(&mut self, control_flow: &mut ControlFlow); + fn handle_nonuser_event(&mut self, event: Event); + fn handle_user_events(&mut self); } struct EventLoopHandler { @@ -337,17 +357,13 @@ impl Debug for EventLoopHandler { } impl EventHandler for EventLoopHandler { - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow) { - (self.f)( - event.map_nonuser_event().unwrap(), - &self.event_loop, - control_flow, - ); + fn handle_nonuser_event(&mut self, event: Event) { + (self.f)(event.map_nonuser_event().unwrap(), &self.event_loop); } - fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { + fn handle_user_events(&mut self) { for event in self.receiver.try_iter() { - (self.f)(Event::UserEvent(event), &self.event_loop, control_flow); + (self.f)(Event::UserEvent(event), &self.event_loop); } } } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 4590d5c4d7..4a52f88ce7 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -19,7 +19,7 @@ use crate::platform::x11::XlibErrorHook; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}, - event::{Event, KeyEvent}, + event::KeyEvent, event_loop::{ AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW, @@ -767,21 +767,21 @@ impl EventLoop { pub fn run(mut self, callback: F) -> Result<(), EventLoopError> where - F: FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: FnMut(crate::event::Event, &RootELW), { self.run_ondemand(callback) } pub fn run_ondemand(&mut self, callback: F) -> Result<(), EventLoopError> where - F: FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: FnMut(crate::event::Event, &RootELW), { x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_ondemand(callback)) } pub fn pump_events(&mut self, timeout: Option, callback: F) -> PumpStatus where - F: FnMut(crate::event::Event, &RootELW, &mut ControlFlow), + F: FnMut(crate::event::Event, &RootELW), { x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback)) } @@ -845,22 +845,29 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle()) } -} -fn sticky_exit_callback( - evt: Event, - target: &RootELW, - control_flow: &mut ControlFlow, - callback: &mut F, -) where - F: FnMut(Event, &RootELW, &mut ControlFlow), -{ - // make ControlFlow::ExitWithCode sticky by providing a dummy - // control flow reference if it is already ExitWithCode. - if let ControlFlow::ExitWithCode(code) = *control_flow { - callback(evt, target, &mut ControlFlow::ExitWithCode(code)) - } else { - callback(evt, target, control_flow) + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + x11_or_wayland!(match self; Self(evlp) => evlp.set_control_flow(control_flow)) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + x11_or_wayland!(match self; Self(evlp) => evlp.control_flow()) + } + + pub(crate) fn exit(&self) { + x11_or_wayland!(match self; Self(evlp) => evlp.exit()) + } + + pub(crate) fn exiting(&self) -> bool { + x11_or_wayland!(match self; Self(evlp) => evlp.exiting()) + } + + fn set_exit_code(&self, code: i32) { + x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code)) + } + + fn exit_code(&self) -> Option { + x11_or_wayland!(match self; Self(evlp) => evlp.exit_code()) } } diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 836708546d..ceb8588e92 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -1,6 +1,6 @@ //! The event-loop routines. -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::io::Result as IOResult; use std::marker::PhantomData; use std::mem; @@ -24,7 +24,6 @@ use crate::event_loop::{ }; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::min_timeout; -use crate::platform_impl::platform::sticky_exit_callback; use crate::platform_impl::{EventLoopWindowTarget as PlatformEventLoopWindowTarget, OsError}; mod proxy; @@ -44,9 +43,6 @@ pub struct EventLoop { /// Has `run` or `run_ondemand` been called or a call to `pump_events` that starts the loop loop_running: bool, - /// The application's latest control_flow state - control_flow: ControlFlow, - buffer_sink: EventSink, compositor_updates: Vec, window_ids: Vec, @@ -168,13 +164,14 @@ impl EventLoop { wayland_dispatcher: wayland_dispatcher.clone(), event_loop_awakener, queue_handle, + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(None), state: RefCell::new(winit_state), _marker: PhantomData, }; let event_loop = Self { loop_running: false, - control_flow: ControlFlow::default(), compositor_updates: Vec::new(), buffer_sink: EventSink::default(), window_ids: Vec::new(), @@ -194,7 +191,7 @@ impl EventLoop { pub fn run_ondemand(&mut self, mut event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { if self.loop_running { return Err(EventLoopError::AlreadyRunning); @@ -225,7 +222,7 @@ impl EventLoop { pub fn pump_events(&mut self, timeout: Option, mut callback: F) -> PumpStatus where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { if !self.loop_running { self.loop_running = true; @@ -233,7 +230,7 @@ impl EventLoop { // Reset the internal state for the loop as we start running to // ensure consistent behaviour in case the loop runs and exits more // than once. - self.control_flow = ControlFlow::Poll; + self.set_control_flow(ControlFlow::Poll); // Run the initial loop iteration. self.single_iteration(&mut callback, StartCause::Init); @@ -241,19 +238,13 @@ impl EventLoop { // Consider the possibility that the `StartCause::Init` iteration could // request to Exit. - if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { + if !self.exiting() { self.poll_events_with_timeout(timeout, &mut callback); } - if let ControlFlow::ExitWithCode(code) = self.control_flow { + if let Some(code) = self.exit_code() { self.loop_running = false; - let mut dummy = self.control_flow; - sticky_exit_callback( - Event::LoopExiting, - self.window_target(), - &mut dummy, - &mut callback, - ); + callback(Event::LoopExiting, self.window_target()); PumpStatus::Exit(code) } else { @@ -263,7 +254,7 @@ impl EventLoop { pub fn poll_events_with_timeout(&mut self, mut timeout: Option, mut callback: F) where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { let cause = loop { let start = Instant::now(); @@ -299,7 +290,7 @@ impl EventLoop { } Err(error) => { error!("Error dispatching wayland queue: {}", error); - self.control_flow = ControlFlow::ExitWithCode(1); + self.set_exit_code(1); return; } } @@ -308,17 +299,12 @@ impl EventLoop { timeout = if instant_wakeup { Some(Duration::ZERO) } else { - let control_flow_timeout = match self.control_flow { + let control_flow_timeout = match self.control_flow() { ControlFlow::Wait => None, ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::WaitUntil(wait_deadline) => { Some(wait_deadline.saturating_duration_since(start)) } - // This function shouldn't have to handle any requests to exit - // the application (there should be no need to poll for events - // if the application has requested to exit) so we consider - // it a bug in the backend if we ever see `ExitWithCode` here. - ControlFlow::ExitWithCode(_code) => unreachable!(), }; min_timeout(control_flow_timeout, timeout) }; @@ -336,13 +322,13 @@ impl EventLoop { // with an API to do that via some event. // Still, we set the exit code to the error's OS error code, or to 1 if not possible. let exit_code = error.raw_os_error().unwrap_or(1); - self.control_flow = ControlFlow::ExitWithCode(exit_code); + self.set_exit_code(exit_code); return; } // NB: `StartCause::Init` is handled as a special case and doesn't need // to be considered here - let cause = match self.control_flow { + let cause = match self.control_flow() { ControlFlow::Poll => StartCause::Poll, ControlFlow::Wait => StartCause::WaitCancelled { start, @@ -361,11 +347,6 @@ impl EventLoop { } } } - // This function shouldn't have to handle any requests to exit - // the application (there should be no need to poll for events - // if the application has requested to exit) so we consider - // it a bug in the backend if we ever see `ExitWithCode` here. - ControlFlow::ExitWithCode(_code) => unreachable!(), }; // Reduce spurious wake-ups. @@ -380,14 +361,12 @@ impl EventLoop { self.single_iteration(&mut callback, cause); } - fn single_iteration(&mut self, mut callback: &mut F, cause: StartCause) + fn single_iteration(&mut self, callback: &mut F, cause: StartCause) where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { // NOTE currently just indented to simplify the diff - let mut control_flow = self.control_flow; - // We retain these grow-only scratch buffers as part of the EventLoop // for the sake of avoiding lots of reallocs. We take them here to avoid // trying to mutably borrow `self` more than once and we swap them back @@ -396,33 +375,18 @@ impl EventLoop { let mut buffer_sink = std::mem::take(&mut self.buffer_sink); let mut window_ids = std::mem::take(&mut self.window_ids); - sticky_exit_callback( - Event::NewEvents(cause), - &self.window_target, - &mut control_flow, - callback, - ); + callback(Event::NewEvents(cause), &self.window_target); // NB: For consistency all platforms must emit a 'resumed' event even though Wayland // applications don't themselves have a formal suspend/resume lifecycle. if cause == StartCause::Init { - sticky_exit_callback( - Event::Resumed, - &self.window_target, - &mut control_flow, - callback, - ); + callback(Event::Resumed, &self.window_target); } // Handle pending user events. We don't need back buffer, since we can't dispatch // user events indirectly via callback to the user. for user_event in self.pending_user_events.borrow_mut().drain(..) { - sticky_exit_callback( - Event::UserEvent(user_event), - &self.window_target, - &mut control_flow, - &mut callback, - ); + callback(Event::UserEvent(user_event), &self.window_target); } // Drain the pending compositor updates. @@ -445,7 +409,7 @@ impl EventLoop { let old_physical_size = physical_size; let new_inner_size = Arc::new(Mutex::new(physical_size)); - sticky_exit_callback( + callback( Event::WindowEvent { window_id: crate::window::WindowId(window_id), event: WindowEvent::ScaleFactorChanged { @@ -456,8 +420,6 @@ impl EventLoop { }, }, &self.window_target, - &mut control_flow, - &mut callback, ); let physical_size = *new_inner_size.lock().unwrap(); @@ -499,26 +461,22 @@ impl EventLoop { physical_size }); - sticky_exit_callback( + callback( Event::WindowEvent { window_id: crate::window::WindowId(window_id), event: WindowEvent::Resized(physical_size), }, &self.window_target, - &mut control_flow, - &mut callback, ); } if compositor_update.close_window { - sticky_exit_callback( + callback( Event::WindowEvent { window_id: crate::window::WindowId(window_id), event: WindowEvent::CloseRequested, }, &self.window_target, - &mut control_flow, - &mut callback, ); } } @@ -529,7 +487,7 @@ impl EventLoop { }); for event in buffer_sink.drain() { let event = event.map_nonuser_event().unwrap(); - sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback); + callback(event, &self.window_target); } // Handle non-synthetic events. @@ -538,7 +496,7 @@ impl EventLoop { }); for event in buffer_sink.drain() { let event = event.map_nonuser_event().unwrap(); - sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback); + callback(event, &self.window_target); } // Collect the window ids @@ -581,14 +539,12 @@ impl EventLoop { }); if request_redraw { - sticky_exit_callback( + callback( Event::WindowEvent { window_id: crate::window::WindowId(window_id), event: WindowEvent::RedrawRequested, }, &self.window_target, - &mut control_flow, - &mut callback, ); } } @@ -599,14 +555,8 @@ impl EventLoop { }); // This is always the last event we dispatch before poll again - sticky_exit_callback( - Event::AboutToWait, - &self.window_target, - &mut control_flow, - &mut callback, - ); - - self.control_flow = control_flow; + callback(Event::AboutToWait, &self.window_target); + std::mem::swap(&mut self.compositor_updates, &mut compositor_updates); std::mem::swap(&mut self.buffer_sink, &mut buffer_sink); std::mem::swap(&mut self.window_ids, &mut window_ids); @@ -660,6 +610,26 @@ impl EventLoop { )))) }) } + + fn set_control_flow(&self, control_flow: ControlFlow) { + self.window_target.p.set_control_flow(control_flow) + } + + fn control_flow(&self) -> ControlFlow { + self.window_target.p.control_flow() + } + + fn exiting(&self) -> bool { + self.window_target.p.exiting() + } + + fn set_exit_code(&self, code: i32) { + self.window_target.p.set_exit_code(code) + } + + fn exit_code(&self) -> Option { + self.window_target.p.exit_code() + } } pub struct EventLoopWindowTarget { @@ -669,6 +639,12 @@ pub struct EventLoopWindowTarget { /// The main queue used by the event loop. pub queue_handle: QueueHandle, + /// The application's latest control_flow state + pub(crate) control_flow: Cell, + + /// The application's exit state. + pub(crate) exit: Cell>, + // TODO remove that RefCell once we can pass `&mut` in `Window::new`. /// Winit state. pub state: RefCell, diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 785526d979..b8547e6b0d 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -4,6 +4,7 @@ use sctk::reexports::client::Proxy; use sctk::output::OutputData; use crate::dpi::{PhysicalPosition, PhysicalSize}; +use crate::event_loop::ControlFlow; use crate::platform_impl::platform::VideoMode as PlatformVideoMode; use super::event_loop::EventLoopWindowTarget; @@ -23,6 +24,30 @@ impl EventLoopWindowTarget { // There's no primary monitor on Wayland. None } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.control_flow.set(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.control_flow.get() + } + + pub(crate) fn exit(&self) { + self.exit.set(Some(0)) + } + + pub(crate) fn exiting(&self) -> bool { + self.exit.get().is_some() + } + + pub(crate) fn set_exit_code(&self, code: i32) { + self.exit.set(Some(code)) + } + + pub(crate) fn exit_code(&self) -> Option { + self.exit.get() + } } #[derive(Clone, Debug)] diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index d1ed5ebb5f..6be647826b 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -66,14 +66,14 @@ use self::{ event_processor::EventProcessor, ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender}, }; -use super::{common::xkb_state::KbdState, OsError}; +use super::{common::xkb_state::KbdState, ControlFlow, OsError}; use crate::{ error::{EventLoopError, OsError as RootOsError}, event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW}, + event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW}, platform::pump_events::PumpStatus, platform_impl::{ - platform::{min_timeout, sticky_exit_callback, WindowId}, + platform::{min_timeout, WindowId}, PlatformSpecificWindowBuilderAttributes, }, window::WindowAttributes, @@ -148,6 +148,8 @@ pub struct EventLoopWindowTarget { wm_delete_window: xproto::Atom, net_wm_ping: xproto::Atom, ime_sender: ImeSender, + control_flow: Cell, + exit: Cell>, root: xproto::Window, ime: RefCell, windows: RefCell>>, @@ -159,7 +161,6 @@ pub struct EventLoopWindowTarget { pub struct EventLoop { loop_running: bool, - control_flow: ControlFlow, event_loop: Loop<'static, EventLoopState>, waker: calloop::ping::Ping, event_processor: EventProcessor, @@ -303,6 +304,8 @@ impl EventLoop { let window_target = EventLoopWindowTarget { ime, root, + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(None), windows: Default::default(), _marker: ::std::marker::PhantomData, ime_sender, @@ -368,7 +371,6 @@ impl EventLoop { EventLoop { loop_running: false, - control_flow: ControlFlow::default(), event_loop, waker, event_processor, @@ -398,7 +400,7 @@ impl EventLoop { pub fn run_ondemand(&mut self, mut event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { if self.loop_running { return Err(EventLoopError::AlreadyRunning); @@ -432,7 +434,7 @@ impl EventLoop { pub fn pump_events(&mut self, timeout: Option, mut callback: F) -> PumpStatus where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { if !self.loop_running { self.loop_running = true; @@ -440,7 +442,7 @@ impl EventLoop { // Reset the internal state for the loop as we start running to // ensure consistent behaviour in case the loop runs and exits more // than once. - self.control_flow = ControlFlow::Poll; + self.set_control_flow(ControlFlow::Poll); // run the initial loop iteration self.single_iteration(&mut callback, StartCause::Init); @@ -448,19 +450,13 @@ impl EventLoop { // Consider the possibility that the `StartCause::Init` iteration could // request to Exit. - if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { + if !self.exiting() { self.poll_events_with_timeout(timeout, &mut callback); } - if let ControlFlow::ExitWithCode(code) = self.control_flow { + if let Some(code) = self.exit_code() { self.loop_running = false; - let mut dummy = self.control_flow; - sticky_exit_callback( - Event::LoopExiting, - self.window_target(), - &mut dummy, - &mut callback, - ); + callback(Event::LoopExiting, self.window_target()); PumpStatus::Exit(code) } else { @@ -476,7 +472,7 @@ impl EventLoop { pub fn poll_events_with_timeout(&mut self, mut timeout: Option, mut callback: F) where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { let start = Instant::now(); @@ -486,17 +482,12 @@ impl EventLoop { // If we already have work to do then we don't want to block on the next poll. Some(Duration::ZERO) } else { - let control_flow_timeout = match self.control_flow { + let control_flow_timeout = match self.control_flow() { ControlFlow::Wait => None, ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::WaitUntil(wait_deadline) => { Some(wait_deadline.saturating_duration_since(start)) } - // This function shouldn't have to handle any requests to exit - // the application (there should be no need to poll for events - // if the application has requested to exit) so we consider - // it a bug in the backend if we ever see `ExitWithCode` here. - ControlFlow::ExitWithCode(_code) => unreachable!(), }; min_timeout(control_flow_timeout, timeout) @@ -510,7 +501,7 @@ impl EventLoop { { log::error!("Failed to poll for events: {error:?}"); let exit_code = error.raw_os_error().unwrap_or(1); - self.control_flow = ControlFlow::ExitWithCode(exit_code); + self.set_exit_code(exit_code); return; } @@ -528,7 +519,7 @@ impl EventLoop { // NB: `StartCause::Init` is handled as a special case and doesn't need // to be considered here - let cause = match self.control_flow { + let cause = match self.control_flow() { ControlFlow::Poll => StartCause::Poll, ControlFlow::Wait => StartCause::WaitCancelled { start, @@ -547,11 +538,6 @@ impl EventLoop { } } } - // This function shouldn't have to handle any requests to exit - // the application (there should be no need to poll for events - // if the application has requested to exit) so we consider - // it a bug in the backend if we ever see `ExitWithCode` here. - ControlFlow::ExitWithCode(_code) => unreachable!(), }; self.single_iteration(&mut callback, cause); @@ -559,30 +545,18 @@ impl EventLoop { fn single_iteration(&mut self, callback: &mut F, cause: StartCause) where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { - let mut control_flow = self.control_flow; - - sticky_exit_callback( - crate::event::Event::NewEvents(cause), - &self.target, - &mut control_flow, - callback, - ); + callback(crate::event::Event::NewEvents(cause), &self.target); // NB: For consistency all platforms must emit a 'resumed' event even though X11 // applications don't themselves have a formal suspend/resume lifecycle. if cause == StartCause::Init { - sticky_exit_callback( - crate::event::Event::Resumed, - &self.target, - &mut control_flow, - callback, - ); + callback(crate::event::Event::Resumed, &self.target); } // Process all pending events - self.drain_events(callback, &mut control_flow); + self.drain_events(callback); // Empty activation tokens. while let Ok((window_id, serial)) = self.activation_receiver.try_recv() { @@ -593,7 +567,7 @@ impl EventLoop { }); match token { - Some(Ok(token)) => sticky_exit_callback( + Some(Ok(token)) => callback( crate::event::Event::WindowEvent { window_id: crate::window::WindowId(window_id), event: crate::event::WindowEvent::ActivationTokenDone { @@ -602,8 +576,6 @@ impl EventLoop { }, }, &self.target, - &mut control_flow, - callback, ), Some(Err(e)) => { log::error!("Failed to get activation token: {}", e); @@ -615,12 +587,7 @@ impl EventLoop { // Empty the user event buffer { while let Ok(event) = self.user_receiver.try_recv() { - sticky_exit_callback( - crate::event::Event::UserEvent(event), - &self.target, - &mut control_flow, - callback, - ); + callback(crate::event::Event::UserEvent(event), &self.target); } } @@ -634,34 +601,25 @@ impl EventLoop { for window_id in windows { let window_id = crate::window::WindowId(window_id); - sticky_exit_callback( + callback( Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested, }, &self.target, - &mut control_flow, - callback, ); } } // This is always the last event we dispatch before poll again { - sticky_exit_callback( - crate::event::Event::AboutToWait, - &self.target, - &mut control_flow, - callback, - ); + callback(crate::event::Event::AboutToWait, &self.target); } - - self.control_flow = control_flow; } - fn drain_events(&mut self, callback: &mut F, control_flow: &mut ControlFlow) + fn drain_events(&mut self, callback: &mut F) where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { let target = &self.target; let mut xev = MaybeUninit::uninit(); @@ -670,25 +628,38 @@ impl EventLoop { while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { let mut xev = unsafe { xev.assume_init() }; self.event_processor.process_event(&mut xev, |event| { - sticky_exit_callback( - event, - target, - control_flow, - &mut |event, window_target, control_flow| { - if let Event::WindowEvent { - window_id: crate::window::WindowId(wid), - event: WindowEvent::RedrawRequested, - } = event - { - wt.redraw_sender.send(wid).unwrap(); - } else { - callback(event, window_target, control_flow); - } - }, - ); + if let Event::WindowEvent { + window_id: crate::window::WindowId(wid), + event: WindowEvent::RedrawRequested, + } = event + { + wt.redraw_sender.send(wid).unwrap(); + } else { + callback(event, target); + } }); } } + + fn set_control_flow(&self, control_flow: ControlFlow) { + self.target.p.set_control_flow(control_flow) + } + + fn control_flow(&self) -> ControlFlow { + self.target.p.control_flow() + } + + fn exiting(&self) -> bool { + self.target.p.exiting() + } + + fn set_exit_code(&self, code: i32) { + self.target.p.set_exit_code(code) + } + + fn exit_code(&self) -> Option { + self.target.p.exit_code() + } } pub(crate) fn get_xtarget(target: &RootELW) -> &EventLoopWindowTarget { @@ -743,6 +714,30 @@ impl EventLoopWindowTarget { display_handle.screen = self.xconn.default_screen_index() as c_int; RawDisplayHandle::Xlib(display_handle) } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.control_flow.set(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.control_flow.get() + } + + pub(crate) fn exit(&self) { + self.exit.set(Some(0)) + } + + pub(crate) fn exiting(&self) -> bool { + self.exit.get().is_some() + } + + pub(crate) fn set_exit_code(&self, code: i32) { + self.exit.set(Some(code)) + } + + pub(crate) fn exit_code(&self) -> Option { + self.exit.get() + } } impl EventLoopProxy { diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 4d39892962..4b0991f1e5 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -56,7 +56,7 @@ declare_class!( fn will_terminate(&self, _sender: Option<&AnyObject>) { trace_scope!("applicationWillTerminate:"); // TODO: Notify every window that it will be destroyed, like done in iOS? - AppState::exit(); + AppState::internal_exit(); } } ); diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 8dc354fc5b..bc9b04c1b8 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -40,11 +40,11 @@ impl Event { pub trait EventHandler: Debug { // Not sure probably it should accept Event<'static, Never> - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow); - fn handle_user_events(&mut self, control_flow: &mut ControlFlow); + fn handle_nonuser_event(&mut self, event: Event); + fn handle_user_events(&mut self); } -pub(crate) type Callback = RefCell, &RootWindowTarget, &mut ControlFlow)>; +pub(crate) type Callback = RefCell, &RootWindowTarget)>; struct EventLoopHandler { callback: Weak>, @@ -55,10 +55,7 @@ struct EventLoopHandler { impl EventLoopHandler { fn with_callback(&mut self, f: F) where - F: FnOnce( - &mut EventLoopHandler, - RefMut<'_, dyn FnMut(Event, &RootWindowTarget, &mut ControlFlow)>, - ), + F: FnOnce(&mut EventLoopHandler, RefMut<'_, dyn FnMut(Event, &RootWindowTarget)>), { // The `NSApp` and our `HANDLER` are global state and so it's possible that // we could get a delegate callback after the application has exit an @@ -85,28 +82,16 @@ impl Debug for EventLoopHandler { } impl EventHandler for EventLoopHandler { - fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow) { + fn handle_nonuser_event(&mut self, event: Event) { self.with_callback(|this, mut callback| { - if let ControlFlow::ExitWithCode(code) = *control_flow { - // XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode? - let dummy = &mut ControlFlow::ExitWithCode(code); - (callback)(event.userify(), &this.window_target, dummy); - } else { - (callback)(event.userify(), &this.window_target, control_flow); - } + (callback)(event.userify(), &this.window_target); }); } - fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { + fn handle_user_events(&mut self) { self.with_callback(|this, mut callback| { for event in this.receiver.try_iter() { - if let ControlFlow::ExitWithCode(code) = *control_flow { - // XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode? - let dummy = &mut ControlFlow::ExitWithCode(code); - (callback)(Event::UserEvent(event), &this.window_target, dummy); - } else { - (callback)(Event::UserEvent(event), &this.window_target, control_flow); - } + (callback)(Event::UserEvent(event), &this.window_target); } }); } @@ -132,6 +117,7 @@ struct Handler { running: AtomicBool, in_callback: AtomicBool, control_flow: Mutex, + exit: AtomicBool, start_time: Mutex>, callback: Mutex>>, pending_events: Mutex>, @@ -189,13 +175,6 @@ impl Handler { self.running.store(true, Ordering::Relaxed); } - fn should_exit(&self) -> bool { - matches!( - *self.control_flow.lock().unwrap(), - ControlFlow::ExitWithCode(_) - ) - } - /// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits /// /// Since an `EventLoop` may be run more than once we need make sure to reset the @@ -206,7 +185,7 @@ impl Handler { /// /// # Caveat /// This is only intended to be called from the main thread - fn exit(&self) { + fn internal_exit(&self) { // Relaxed ordering because we don't actually have multiple threads involved, we just want // interiour mutability // @@ -227,6 +206,14 @@ impl Handler { self.set_wait_timeout(None); } + pub fn exit(&self) { + self.exit.store(true, Ordering::Relaxed) + } + + pub fn exiting(&self) -> bool { + self.exit.load(Ordering::Relaxed) + } + pub fn request_stop_app_on_launch(&self) { // Relaxed ordering because we don't actually have multiple threads involved, we just want // interior mutability @@ -287,6 +274,10 @@ impl Handler { self.stop_app_on_redraw.load(Ordering::Relaxed) } + fn set_control_flow(&self, new_control_flow: ControlFlow) { + *self.control_flow.lock().unwrap() = new_control_flow + } + fn control_flow(&self) -> ControlFlow { *self.control_flow.lock().unwrap() } @@ -321,13 +312,13 @@ impl Handler { fn handle_nonuser_event(&self, event: Event) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { - callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap()) + callback.handle_nonuser_event(event) } } fn handle_user_events(&self) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { - callback.handle_user_events(&mut self.control_flow.lock().unwrap()); + callback.handle_user_events(); } } @@ -347,7 +338,7 @@ impl Handler { }, }; - callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap()); + callback.handle_nonuser_event(event); let physical_size = *new_inner_size.lock().unwrap(); drop(new_inner_size); @@ -418,21 +409,28 @@ impl AppState { HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw); } + pub fn set_control_flow(control_flow: ControlFlow) { + HANDLER.set_control_flow(control_flow) + } + pub fn control_flow() -> ControlFlow { HANDLER.control_flow() } - pub fn exit() -> i32 { + pub fn internal_exit() { HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(Event::LoopExiting); HANDLER.set_in_callback(false); - HANDLER.exit(); + HANDLER.internal_exit(); Self::clear_callback(); - if let ControlFlow::ExitWithCode(code) = HANDLER.control_flow() { - code - } else { - 0 - } + } + + pub fn exit() { + HANDLER.exit() + } + + pub fn exiting() -> bool { + HANDLER.exiting() } pub fn dispatch_init_events() { @@ -528,7 +526,6 @@ impl AppState { } } } - ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"), }; HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(Event::NewEvents(cause)); @@ -643,7 +640,7 @@ impl AppState { HANDLER.handle_nonuser_event(Event::AboutToWait); HANDLER.set_in_callback(false); - if HANDLER.should_exit() { + if HANDLER.exiting() { Self::stop(); } @@ -654,7 +651,7 @@ impl AppState { let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events let app_timeout = match HANDLER.control_flow() { ControlFlow::Wait => None, - ControlFlow::Poll | ControlFlow::ExitWithCode(_) => Some(Instant::now()), + ControlFlow::Poll => Some(Instant::now()), ControlFlow::WaitUntil(instant) => Some(instant), }; HANDLER diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 9e9dc64e91..422d5ca402 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -93,6 +93,22 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + AppState::set_control_flow(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + AppState::control_flow() + } + + pub(crate) fn exit(&self) { + AppState::exit() + } + + pub(crate) fn exiting(&self) -> bool { + AppState::exiting() + } } impl EventLoopWindowTarget { @@ -213,7 +229,7 @@ impl EventLoop { pub fn run(mut self, callback: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootWindowTarget), { self.run_ondemand(callback) } @@ -224,7 +240,7 @@ impl EventLoop { // redundant wake ups. pub fn run_ondemand(&mut self, callback: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootWindowTarget), { if AppState::is_running() { return Err(EventLoopError::AlreadyRunning); @@ -241,14 +257,14 @@ impl EventLoop { let callback = unsafe { mem::transmute::< - Rc, &RootWindowTarget, &mut ControlFlow)>>, - Rc, &RootWindowTarget, &mut ControlFlow)>>, + Rc, &RootWindowTarget)>>, + Rc, &RootWindowTarget)>>, >(Rc::new(RefCell::new(callback))) }; self._callback = Some(Rc::clone(&callback)); - let exit_code = autoreleasepool(|_| { + autoreleasepool(|_| { // A bit of juggling with the callback references to make sure // that `self.callback` is the only owner of the callback. let weak_cb: Weak<_> = Rc::downgrade(&callback); @@ -288,7 +304,7 @@ impl EventLoop { resume_unwind(panic); } - AppState::exit() + AppState::internal_exit() })); // # Safety @@ -298,22 +314,17 @@ impl EventLoop { drop(self._callback.take()); AppState::clear_callback(); - match catch_result { - Ok(exit_code) => exit_code, - Err(payload) => resume_unwind(payload), + if let Err(payload) = catch_result { + resume_unwind(payload) } }); - if exit_code == 0 { - Ok(()) - } else { - Err(EventLoopError::ExitFailure(exit_code)) - } + Ok(()) } pub fn pump_events(&mut self, timeout: Option, callback: F) -> PumpStatus where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootWindowTarget), { // # Safety // We are erasing the lifetime of the application callback here so that we @@ -326,8 +337,8 @@ impl EventLoop { let callback = unsafe { mem::transmute::< - Rc, &RootWindowTarget, &mut ControlFlow)>>, - Rc, &RootWindowTarget, &mut ControlFlow)>>, + Rc, &RootWindowTarget)>>, + Rc, &RootWindowTarget)>>, >(Rc::new(RefCell::new(callback))) }; @@ -407,9 +418,9 @@ impl EventLoop { resume_unwind(panic); } - if let ControlFlow::ExitWithCode(code) = AppState::control_flow() { - AppState::exit(); - PumpStatus::Exit(code) + if AppState::exiting() { + AppState::internal_exit(); + PumpStatus::Exit(0) } else { PumpStatus::Continue } diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index c62c291055..cd64a9228f 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -1,4 +1,5 @@ use std::{ + cell::Cell, collections::VecDeque, marker::PhantomData, mem, slice, @@ -301,6 +302,8 @@ impl EventLoop { windows: Vec::new(), window_target: event_loop::EventLoopWindowTarget { p: EventLoopWindowTarget { + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(false), creates: Mutex::new(VecDeque::new()), redraws: Arc::new(Mutex::new(VecDeque::new())), destroys: Arc::new(Mutex::new(VecDeque::new())), @@ -458,40 +461,20 @@ impl EventLoop { pub fn run(mut self, mut event_handler_inner: F) -> Result<(), EventLoopError> where - F: FnMut(event::Event, &event_loop::EventLoopWindowTarget, &mut ControlFlow), + F: FnMut(event::Event, &event_loop::EventLoopWindowTarget), { - // Wrapper for event handler function that prevents ExitWithCode from being unset. let mut event_handler = - move |event: event::Event, - window_target: &event_loop::EventLoopWindowTarget, - control_flow: &mut ControlFlow| { - if let ControlFlow::ExitWithCode(code) = control_flow { - event_handler_inner( - event, - window_target, - &mut ControlFlow::ExitWithCode(*code), - ); - } else { - event_handler_inner(event, window_target, control_flow); - } + move |event: event::Event, window_target: &event_loop::EventLoopWindowTarget| { + event_handler_inner(event, window_target); }; - let mut control_flow = ControlFlow::default(); let mut start_cause = StartCause::Init; - let code = loop { - event_handler( - event::Event::NewEvents(start_cause), - &self.window_target, - &mut control_flow, - ); + loop { + event_handler(event::Event::NewEvents(start_cause), &self.window_target); if start_cause == StartCause::Init { - event_handler( - event::Event::Resumed, - &self.window_target, - &mut control_flow, - ); + event_handler(event::Event::Resumed, &self.window_target); } // Handle window creates. @@ -516,7 +499,6 @@ impl EventLoop { event: event::WindowEvent::Resized((properties.w, properties.h).into()), }, &self.window_target, - &mut control_flow, ); // Send resize event on create to indicate first position. @@ -526,7 +508,6 @@ impl EventLoop { event: event::WindowEvent::Moved((properties.x, properties.y).into()), }, &self.window_target, - &mut control_flow, ); } @@ -541,7 +522,6 @@ impl EventLoop { event: event::WindowEvent::Destroyed, }, &self.window_target, - &mut control_flow, ); self.windows @@ -572,7 +552,7 @@ impl EventLoop { window_id, orbital_event.to_option(), event_state, - |event| event_handler(event, &self.window_target, &mut control_flow), + |event| event_handler(event, &self.window_target), ); } @@ -599,11 +579,7 @@ impl EventLoop { } while let Ok(event) = self.user_events_receiver.try_recv() { - event_handler( - event::Event::UserEvent(event), - &self.window_target, - &mut control_flow, - ); + event_handler(event::Event::UserEvent(event), &self.window_target); } // To avoid deadlocks the redraws lock is not held during event processing. @@ -617,24 +593,22 @@ impl EventLoop { event: event::WindowEvent::RedrawRequested, }, &self.window_target, - &mut control_flow, ); } - event_handler( - event::Event::AboutToWait, - &self.window_target, - &mut control_flow, - ); + event_handler(event::Event::AboutToWait, &self.window_target); - let requested_resume = match control_flow { + if self.window_target.p.exiting() { + break; + } + + let requested_resume = match self.window_target.p.control_flow() { ControlFlow::Poll => { start_cause = StartCause::Poll; continue; } ControlFlow::Wait => None, ControlFlow::WaitUntil(instant) => Some(instant), - ControlFlow::ExitWithCode(code) => break code, }; // Re-using wake socket caused extra wake events before because there were leftover @@ -690,19 +664,11 @@ impl EventLoop { }; } } - }; + } - event_handler( - event::Event::LoopExiting, - &self.window_target, - &mut control_flow, - ); + event_handler(event::Event::LoopExiting, &self.window_target); - if code == 0 { - Ok(()) - } else { - Err(EventLoopError::ExitFailure(code)) - } + Ok(()) } pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget { @@ -746,6 +712,8 @@ impl Clone for EventLoopProxy { impl Unpin for EventLoopProxy {} pub struct EventLoopWindowTarget { + control_flow: Cell, + exit: Cell, pub(super) creates: Mutex>>, pub(super) redraws: Arc>>, pub(super) destroys: Arc>>, @@ -771,4 +739,20 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty()) } + + pub fn set_control_flow(&self, control_flow: ControlFlow) { + self.control_flow.set(control_flow) + } + + pub fn control_flow(&self) -> ControlFlow { + self.control_flow.get() + } + + pub(crate) fn exit(&self) { + self.exit.set(true); + } + + pub(crate) fn exiting(&self) -> bool { + self.exit.get() + } } diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index a5a2886d7e..a93d160c8d 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -3,7 +3,7 @@ use std::sync::mpsc::{self, Receiver, Sender}; use crate::error::EventLoopError; use crate::event::Event; -use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget}; +use crate::event_loop::EventLoopWindowTarget as RootEventLoopWindowTarget; use super::{backend, device, window}; @@ -39,7 +39,7 @@ impl EventLoop { pub fn run(self, mut event_handler: F) -> ! where - F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: FnMut(Event, &RootEventLoopWindowTarget), { let target = RootEventLoopWindowTarget { p: self.elw.p.clone(), @@ -47,7 +47,7 @@ impl EventLoop { }; // SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`. - let handler: Box, _)> = Box::new(|event, flow| { + let handler: Box)> = Box::new(|event| { let event = match event.map_nonuser_event() { Ok(event) => event, Err(Event::UserEvent(())) => Event::UserEvent( @@ -57,7 +57,7 @@ impl EventLoop { ), Err(_) => unreachable!(), }; - event_handler(event, &target, flow) + event_handler(event, &target) }); // SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe // because this function will never return and all resources not cleaned up by the point we @@ -76,7 +76,7 @@ impl EventLoop { pub fn spawn(self, mut event_handler: F) where - F: 'static + FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), + F: 'static + FnMut(Event, &RootEventLoopWindowTarget), { let target = RootEventLoopWindowTarget { p: self.elw.p.clone(), @@ -84,7 +84,7 @@ impl EventLoop { }; self.elw.p.run( - Box::new(move |event, flow| { + Box::new(move |event| { let event = match event.map_nonuser_event() { Ok(event) => event, Err(Event::UserEvent(())) => Event::UserEvent( @@ -94,7 +94,7 @@ impl EventLoop { ), Err(_) => unreachable!(), }; - event_handler(event, &target, flow) + event_handler(event, &target) }), true, ); diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index c6dd652cd4..081458753d 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -24,7 +24,7 @@ use web_time::{Duration, Instant}; pub struct Shared(Rc); -pub(super) type EventHandler = dyn FnMut(Event<()>, &mut ControlFlow); +pub(super) type EventHandler = dyn FnMut(Event<()>); impl Clone for Shared { fn clone(&self) -> Self { @@ -35,6 +35,8 @@ impl Clone for Shared { type OnEventHandle = RefCell>>; pub struct Execution { + control_flow: Cell, + exit: Cell, runner: RefCell, suspended: Cell, event_loop_recreation: Cell, @@ -108,16 +110,9 @@ impl Runner { }) } - fn handle_single_event( - &mut self, - runner: &Shared, - event: impl Into, - control: &mut ControlFlow, - ) { - let is_closed = matches!(*control, ControlFlow::ExitWithCode(_)); - + fn handle_single_event(&mut self, runner: &Shared, event: impl Into) { match event.into() { - EventWrapper::Event(event) => (self.event_handler)(event, control), + EventWrapper::Event(event) => (self.event_handler)(event), EventWrapper::ScaleChange { canvas, size, @@ -126,18 +121,13 @@ impl Runner { if let Some(canvas) = canvas.upgrade() { canvas.borrow().handle_scale_change( runner, - |event| (self.event_handler)(event, control), + |event| (self.event_handler)(event), size, scale, ) } } } - - // Maintain closed state, even if the callback changes it - if is_closed { - *control = ControlFlow::Exit; - } } } @@ -149,6 +139,8 @@ impl Shared { let document = window.document().expect("Failed to obtain document"); Shared(Rc::new(Execution { + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(false), runner: RefCell::new(RunnerEnum::Pending), suspended: Cell::new(false), event_loop_recreation: Cell::new(false), @@ -549,19 +541,16 @@ impl Shared { // Process the destroy-pending windows. This should only be called from // `run_until_cleared`, somewhere between emitting `NewEvents` and `AboutToWait`. - fn process_destroy_pending_windows(&self, control: &mut ControlFlow) { + fn process_destroy_pending_windows(&self) { while let Some(id) = self.0.destroy_pending.borrow_mut().pop_front() { self.0 .all_canvases .borrow_mut() .retain(|&(item_id, _)| item_id != id); - self.handle_event( - Event::WindowEvent { - window_id: id, - event: crate::event::WindowEvent::Destroyed, - }, - control, - ); + self.handle_event(Event::WindowEvent { + window_id: id, + event: crate::event::WindowEvent::Destroyed, + }); self.0.redraw_pending.borrow_mut().remove(&id); } } @@ -571,52 +560,48 @@ impl Shared { // // This will also process any events that have been queued or that are queued during processing fn run_until_cleared>(&self, events: impl Iterator) { - let mut control = self.current_control_flow(); for event in events { - self.handle_event(event.into(), &mut control); + self.handle_event(event.into()); } - self.process_destroy_pending_windows(&mut control); + self.process_destroy_pending_windows(); // Collect all of the redraw events to avoid double-locking the RefCell let redraw_events: Vec = self.0.redraw_pending.borrow_mut().drain().collect(); for window_id in redraw_events { - self.handle_event( - Event::WindowEvent { - window_id, - event: WindowEvent::RedrawRequested, - }, - &mut control, - ); + self.handle_event(Event::WindowEvent { + window_id, + event: WindowEvent::RedrawRequested, + }); } - self.handle_event(Event::AboutToWait, &mut control); + self.handle_event(Event::AboutToWait); - self.apply_control_flow(control); + self.apply_control_flow(); // If the event loop is closed, it has been closed this iteration and now the closing // event should be emitted if self.is_closed() { - self.handle_loop_destroyed(&mut control); + self.handle_loop_destroyed(); } } fn handle_unload(&self) { - self.apply_control_flow(ControlFlow::Exit); - let mut control = self.current_control_flow(); + self.exit(); + self.apply_control_flow(); // We don't call `handle_loop_destroyed` here because we don't need to // perform cleanup when the web browser is going to destroy the page. - self.handle_event(Event::LoopExiting, &mut control); + self.handle_event(Event::LoopExiting); } // handle_event takes in events and either queues them or applies a callback // // It should only ever be called from `run_until_cleared`. - fn handle_event(&self, event: impl Into, control: &mut ControlFlow) { + fn handle_event(&self, event: impl Into) { if self.is_closed() { - *control = ControlFlow::Exit; + self.exit(); } match *self.0.runner.borrow_mut() { RunnerEnum::Running(ref mut runner) => { - runner.handle_single_event(self, event, control); + runner.handle_single_event(self, event); } // If an event is being handled without a runner somehow, add it to the event queue so // it will eventually be processed @@ -625,7 +610,7 @@ impl Shared { RunnerEnum::Destroyed => return, } - let is_closed = matches!(*control, ControlFlow::ExitWithCode(_)); + let is_closed = self.exiting(); // Don't take events out of the queue if the loop is closed or the runner doesn't exist // If the runner doesn't exist and this method recurses, it will recurse infinitely @@ -634,50 +619,53 @@ impl Shared { // Make sure not to let the borrow_mut live during the next handle_event let event = { self.0.events.borrow_mut().pop_front() }; if let Some(event) = event { - self.handle_event(event, control); + self.handle_event(event); } } } // Apply the new ControlFlow that has been selected by the user // Start any necessary timeouts etc - fn apply_control_flow(&self, control_flow: ControlFlow) { - let new_state = match control_flow { - ControlFlow::Poll => { - let cloned = self.clone(); - State::Poll { - request: backend::Schedule::new( - self.window().clone(), - move || cloned.poll(), - None, - ), + fn apply_control_flow(&self) { + let new_state = if self.exiting() { + State::Exit + } else { + match self.control_flow() { + ControlFlow::Poll => { + let cloned = self.clone(); + State::Poll { + request: backend::Schedule::new( + self.window().clone(), + move || cloned.poll(), + None, + ), + } } - } - ControlFlow::Wait => State::Wait { - start: Instant::now(), - }, - ControlFlow::WaitUntil(end) => { - let start = Instant::now(); + ControlFlow::Wait => State::Wait { + start: Instant::now(), + }, + ControlFlow::WaitUntil(end) => { + let start = Instant::now(); - let delay = if end <= start { - Duration::from_millis(0) - } else { - end - start - }; - - let cloned = self.clone(); - - State::WaitUntil { - start, - end, - timeout: backend::Schedule::new( - self.window().clone(), - move || cloned.resume_time_reached(start, end), - Some(delay), - ), + let delay = if end <= start { + Duration::from_millis(0) + } else { + end - start + }; + + let cloned = self.clone(); + + State::WaitUntil { + start, + end, + timeout: backend::Schedule::new( + self.window().clone(), + move || cloned.resume_time_reached(start, end), + Some(delay), + ), + } } } - ControlFlow::ExitWithCode(_) => State::Exit, }; if let RunnerEnum::Running(ref mut runner) = *self.0.runner.borrow_mut() { @@ -685,8 +673,8 @@ impl Shared { } } - fn handle_loop_destroyed(&self, control: &mut ControlFlow) { - self.handle_event(Event::LoopExiting, control); + fn handle_loop_destroyed(&self) { + self.handle_event(Event::LoopExiting); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); *self.0.page_transition_event_handle.borrow_mut() = None; *self.0.on_mouse_move.borrow_mut() = None; @@ -725,7 +713,7 @@ impl Shared { // Check if the event loop is currently closed fn is_closed(&self) -> bool { match self.0.runner.try_borrow().as_ref().map(Deref::deref) { - Ok(RunnerEnum::Running(runner)) => runner.state.is_exit(), + Ok(RunnerEnum::Running(runner)) => runner.state.exiting(), // The event loop is not closed since it is not initialized. Ok(RunnerEnum::Pending) => false, // The event loop is closed since it has been destroyed. @@ -736,15 +724,6 @@ impl Shared { } } - // Get the current control flow state - fn current_control_flow(&self) -> ControlFlow { - match *self.0.runner.borrow() { - RunnerEnum::Running(ref runner) => runner.state.control_flow(), - RunnerEnum::Pending => ControlFlow::Poll, - RunnerEnum::Destroyed => ControlFlow::Exit, - } - } - pub fn listen_device_events(&self, allowed: DeviceEvents) { self.0.device_events.set(allowed) } @@ -774,6 +753,22 @@ impl Shared { pub fn event_loop_recreation(&self, allow: bool) { self.0.event_loop_recreation.set(allow) } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.0.control_flow.get() + } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.0.control_flow.set(control_flow) + } + + pub(crate) fn exit(&self) { + self.0.exit.set(true) + } + + pub(crate) fn exiting(&self) -> bool { + self.0.exit.get() + } } pub(crate) enum EventWrapper { diff --git a/src/platform_impl/web/event_loop/state.rs b/src/platform_impl/web/event_loop/state.rs index 0dca671586..8a354c78c4 100644 --- a/src/platform_impl/web/event_loop/state.rs +++ b/src/platform_impl/web/event_loop/state.rs @@ -1,5 +1,4 @@ use super::backend; -use crate::event_loop::ControlFlow; use web_time::Instant; @@ -21,17 +20,7 @@ pub enum State { } impl State { - pub fn is_exit(&self) -> bool { + pub fn exiting(&self) -> bool { matches!(self, State::Exit) } - - pub fn control_flow(&self) -> ControlFlow { - match self { - State::Init => ControlFlow::Poll, - State::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end), - State::Wait { .. } => ControlFlow::Wait, - State::Poll { .. } => ControlFlow::Poll, - State::Exit => ControlFlow::Exit, - } - } } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index d080dabb99..82308a9659 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -19,7 +19,7 @@ use super::{ use crate::event::{ DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent, }; -use crate::event_loop::DeviceEvents; +use crate::event_loop::{ControlFlow, DeviceEvents}; use crate::keyboard::ModifiersState; use crate::window::{Theme, WindowId as RootWindowId}; @@ -678,4 +678,20 @@ impl EventLoopWindowTarget { pub fn listen_device_events(&self, allowed: DeviceEvents) { self.runner.listen_device_events(allowed) } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.runner.set_control_flow(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.runner.control_flow() + } + + pub(crate) fn exit(&self) { + self.runner.exit() + } + + pub(crate) fn exiting(&self) -> bool { + self.runner.exiting() + } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 1bf16d58e7..b5a15ba3c7 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -250,14 +250,14 @@ impl EventLoop { pub fn run(mut self, event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { self.run_ondemand(event_handler) } pub fn run_ondemand(&mut self, mut event_handler: F) -> Result<(), EventLoopError> where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { { let runner = &self.window_target.p.runner_shared; @@ -270,18 +270,20 @@ impl EventLoop { // We make sure to call runner.clear_event_handler() before // returning unsafe { - runner.set_event_handler(move |event, control_flow| { - event_handler(event, event_loop_windows_ref, control_flow) - }); + runner.set_event_handler(move |event| event_handler(event, event_loop_windows_ref)); } } let exit_code = loop { - if let ControlFlow::ExitWithCode(code) = self.wait_and_dispatch_message(None) { + self.wait_and_dispatch_message(None); + + if let Some(code) = self.exit_code() { break code; } - if let ControlFlow::ExitWithCode(code) = self.dispatch_peeked_messages() { + self.dispatch_peeked_messages(); + + if let Some(code) = self.exit_code() { break code; } }; @@ -303,7 +305,7 @@ impl EventLoop { pub fn pump_events(&mut self, timeout: Option, mut event_handler: F) -> PumpStatus where - F: FnMut(Event, &RootELW, &mut ControlFlow), + F: FnMut(Event, &RootELW), { { let runner = &self.window_target.p.runner_shared; @@ -317,23 +319,20 @@ impl EventLoop { // to leave the runner in an unsound state with an associated // event handler. unsafe { - runner.set_event_handler(move |event, control_flow| { - event_handler(event, event_loop_windows_ref, control_flow) - }); + runner.set_event_handler(move |event| event_handler(event, event_loop_windows_ref)); runner.wakeup(); } } - if !matches!( - self.wait_and_dispatch_message(timeout), - ControlFlow::ExitWithCode(_) - ) { + self.wait_and_dispatch_message(timeout); + + if self.exit_code().is_none() { self.dispatch_peeked_messages(); } let runner = &self.window_target.p.runner_shared; - let status = if let ControlFlow::ExitWithCode(code) = runner.control_flow() { + let status = if let Some(code) = runner.exit_code() { runner.loop_destroyed(); // Immediately reset the internal state for the loop to allow @@ -357,7 +356,7 @@ impl EventLoop { } /// Wait for one message and dispatch it, optionally with a timeout - fn wait_and_dispatch_message(&mut self, timeout: Option) -> ControlFlow { + fn wait_and_dispatch_message(&mut self, timeout: Option) { let start = Instant::now(); let runner = &self.window_target.p.runner_shared; @@ -368,7 +367,6 @@ impl EventLoop { ControlFlow::WaitUntil(wait_deadline) => { Some(wait_deadline.saturating_duration_since(start)) } - ControlFlow::ExitWithCode(_code) => unreachable!(), }; let timeout = min_timeout(control_flow_timeout, timeout); @@ -432,8 +430,7 @@ impl EventLoop { match msg_status { None => {} // No MSG to dispatch Some(PumpStatus::Exit(code)) => { - runner.set_exit_control_flow(code); - return runner.control_flow(); + runner.set_exit_code(code); } Some(PumpStatus::Continue) => { unsafe { @@ -454,12 +451,10 @@ impl EventLoop { } } } - - runner.control_flow() } /// Dispatch all queued messages via `PeekMessageW` - fn dispatch_peeked_messages(&mut self) -> ControlFlow { + fn dispatch_peeked_messages(&mut self) { let runner = &self.window_target.p.runner_shared; // We generally want to continue dispatching all pending messages @@ -476,8 +471,6 @@ impl EventLoop { // is the simplest way avoid unitialized memory in Rust let mut msg = unsafe { mem::zeroed() }; - let mut control_flow = runner.control_flow(); - loop { unsafe { if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) == false.into() { @@ -500,8 +493,7 @@ impl EventLoop { panic::resume_unwind(payload); } - control_flow = runner.control_flow(); - if let ControlFlow::ExitWithCode(_code) = control_flow { + if let Some(_code) = runner.exit_code() { break; } @@ -509,8 +501,6 @@ impl EventLoop { break; } } - - control_flow } pub fn create_proxy(&self) -> EventLoopProxy { @@ -519,6 +509,10 @@ impl EventLoop { event_send: self.thread_msg_sender.clone(), } } + + fn exit_code(&self) -> Option { + self.window_target.p.exit_code() + } } impl EventLoopWindowTarget { @@ -547,6 +541,26 @@ impl EventLoopWindowTarget { pub fn listen_device_events(&self, allowed: DeviceEvents) { raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed); } + + pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) { + self.runner_shared.set_control_flow(control_flow) + } + + pub(crate) fn control_flow(&self) -> ControlFlow { + self.runner_shared.control_flow() + } + + pub(crate) fn exit(&self) { + self.runner_shared.set_exit_code(0) + } + + pub(crate) fn exiting(&self) -> bool { + self.runner_shared.exit_code().is_some() + } + + fn exit_code(&self) -> Option { + self.runner_shared.exit_code() + } } /// Returns the id of the main thread. diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index e8b35cf180..d907cc3384 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -13,7 +13,6 @@ use windows_sys::Win32::Foundation::HWND; use crate::{ dpi::PhysicalSize, event::{Event, InnerSizeWriter, StartCause, WindowEvent}, - event_loop::ControlFlow, platform_impl::platform::{ event_loop::{WindowData, GWL_USERDATA}, get_window_long, @@ -21,9 +20,11 @@ use crate::{ window::WindowId, }; +use super::ControlFlow; + pub(crate) type EventLoopRunnerShared = Rc>; -type EventHandler = Cell, &mut ControlFlow)>>>; +type EventHandler = Cell)>>>; pub(crate) struct EventLoopRunner { // The event loop's win32 handles @@ -35,6 +36,7 @@ pub(crate) struct EventLoopRunner { pub(super) interrupt_msg_dispatch: Cell, control_flow: Cell, + exit: Cell>, runner_state: Cell, last_events_cleared: Cell, event_handler: EventHandler, @@ -70,7 +72,8 @@ impl EventLoopRunner { thread_msg_target, interrupt_msg_dispatch: Cell::new(false), runner_state: Cell::new(RunnerState::Uninitialized), - control_flow: Cell::new(ControlFlow::Poll), + control_flow: Cell::new(ControlFlow::default()), + exit: Cell::new(None), panic_error: Cell::new(None), last_events_cleared: Cell::new(Instant::now()), event_handler: Cell::new(None), @@ -91,11 +94,11 @@ impl EventLoopRunner { /// undefined behaviour. pub(crate) unsafe fn set_event_handler(&self, f: F) where - F: FnMut(Event, &mut ControlFlow), + F: FnMut(Event), { let old_event_handler = self.event_handler.replace(mem::transmute::< - Option, &mut ControlFlow)>>, - Option, &mut ControlFlow)>>, + Option)>>, + Option)>>, >(Some(Box::new(f)))); assert!(old_event_handler.is_none()); } @@ -111,6 +114,7 @@ impl EventLoopRunner { runner_state, panic_error, control_flow, + exit, last_events_cleared: _, event_handler, event_buffer: _, @@ -118,7 +122,8 @@ impl EventLoopRunner { interrupt_msg_dispatch.set(false); runner_state.set(RunnerState::Uninitialized); panic_error.set(None); - control_flow.set(ControlFlow::Poll); + control_flow.set(ControlFlow::default()); + exit.set(None); event_handler.set(None); } } @@ -141,14 +146,22 @@ impl EventLoopRunner { self.runner_state.get() } - pub fn set_exit_control_flow(&self, code: i32) { - self.control_flow.set(ControlFlow::ExitWithCode(code)) + pub fn set_control_flow(&self, control_flow: ControlFlow) { + self.control_flow.set(control_flow) } pub fn control_flow(&self) -> ControlFlow { self.control_flow.get() } + pub fn set_exit_code(&self, code: i32) { + self.exit.set(Some(code)) + } + + pub fn exit_code(&self) -> Option { + self.exit.get() + } + pub fn should_buffer(&self) -> bool { let handler = self.event_handler.take(); let should_buffer = handler.is_none(); @@ -226,18 +239,12 @@ impl EventLoopRunner { fn call_event_handler(&self, event: Event) { self.catch_unwind(|| { - let mut control_flow = self.control_flow.take(); let mut event_handler = self.event_handler.take() .expect("either event handler is re-entrant (likely), or no event handler is registered (very unlikely)"); - if let ControlFlow::ExitWithCode(code) = control_flow { - event_handler(event, &mut ControlFlow::ExitWithCode(code)); - } else { - event_handler(event, &mut control_flow); - } + event_handler(event); assert!(self.event_handler.replace(Some(event_handler)).is_none()); - self.control_flow.set(control_flow); }); } @@ -332,16 +339,14 @@ impl EventLoopRunner { } fn call_new_events(&self, init: bool) { - let start_cause = match (init, self.control_flow()) { - (true, _) => StartCause::Init, - (false, ControlFlow::Poll) => StartCause::Poll, - (false, ControlFlow::ExitWithCode(_)) | (false, ControlFlow::Wait) => { - StartCause::WaitCancelled { - requested_resume: None, - start: self.last_events_cleared.get(), - } - } - (false, ControlFlow::WaitUntil(requested_resume)) => { + let start_cause = match (init, self.control_flow(), self.exit.get()) { + (true, _, _) => StartCause::Init, + (false, ControlFlow::Poll, None) => StartCause::Poll, + (false, _, Some(_)) | (false, ControlFlow::Wait, None) => StartCause::WaitCancelled { + requested_resume: None, + start: self.last_events_cleared.get(), + }, + (false, ControlFlow::WaitUntil(requested_resume), None) => { if Instant::now() < requested_resume { StartCause::WaitCancelled { requested_resume: Some(requested_resume), diff --git a/src/window.rs b/src/window.rs index e00ab19e8d..27a5ba4fdb 100644 --- a/src/window.rs +++ b/src/window.rs @@ -40,21 +40,21 @@ pub use raw_window_handle; /// ```no_run /// use winit::{ /// event::{Event, WindowEvent}, -/// event_loop::EventLoop, +/// event_loop::{ControlFlow, EventLoop}, /// window::Window, /// }; /// /// let mut event_loop = EventLoop::new().unwrap(); /// let window = Window::new(&event_loop).unwrap(); /// -/// event_loop.run(move |event, _, control_flow| { -/// control_flow.set_wait(); +/// event_loop.run(move |event, elwt| { +/// elwt.set_control_flow(ControlFlow::Wait); /// /// match event { /// Event::WindowEvent { /// event: WindowEvent::CloseRequested, /// .. -/// } => control_flow.set_exit(), +/// } => elwt.exit(), /// _ => (), /// } /// });