Move ControlFlow to EventLoopWindowTarget

Fixes #3042.
This commit is contained in:
daxpedda
2023-09-07 08:25:04 +02:00
committed by GitHub
parent 8fdd81ecef
commit e648169861
60 changed files with 800 additions and 860 deletions

View File

@@ -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. - On Wayland, fix `TouchPhase::Canceled` being sent for moved events.
- Mark `startup_notify` unsafe functions as safe. - 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) - 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 # 0.29.1-beta

View File

@@ -43,14 +43,14 @@ fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id, window_id,
} if window_id == window.id() => *control_flow = ControlFlow::Exit, } if window_id == window.id() => elwt.exit(),
_ => (), _ => (),
} }
}); });

View File

@@ -46,14 +46,14 @@ fn main() -> Result<(), impl std::error::Error> {
println!("parent window: {parent_window:?})"); println!("parent window: {parent_window:?})");
event_loop.run(move |event: Event<()>, event_loop, control_flow| { event_loop.run(move |event: Event<()>, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, window_id } = event { if let Event::WindowEvent { event, window_id } = event {
match event { match event {
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
windows.clear(); windows.clear();
*control_flow = ControlFlow::Exit; elwt.exit();
} }
WindowEvent::CursorEntered { device_id: _ } => { WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window is created // 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 => { WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) { if let Some(window) = windows.get(&window_id) {

View File

@@ -9,7 +9,7 @@ use web_time as time;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::Key, keyboard::Key,
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -47,7 +47,7 @@ fn main() -> Result<(), impl std::error::Error> {
let mut wait_cancelled = false; let mut wait_cancelled = false;
let mut close_requested = false; let mut close_requested = false;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
use winit::event::StartCause; use winit::event::StartCause;
println!("{event:?}"); println!("{event:?}");
match event { match event {
@@ -104,20 +104,22 @@ fn main() -> Result<(), impl std::error::Error> {
} }
match mode { match mode {
Mode::Wait => control_flow.set_wait(), Mode::Wait => elwt.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => { Mode::WaitUntil => {
if !wait_cancelled { 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 => { Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME); thread::sleep(POLL_SLEEP_TIME);
control_flow.set_poll(); elwt.set_control_flow(ControlFlow::Poll);
} }
}; };
if close_requested { if close_requested {
control_flow.set_exit(); elwt.exit();
} }
} }
_ => (), _ => (),

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, WindowBuilder}, window::{CursorIcon, WindowBuilder},
}; };
@@ -19,8 +19,8 @@ fn main() -> Result<(), impl std::error::Error> {
let mut cursor_idx = 0; let mut cursor_idx = 0;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
@@ -44,7 +44,7 @@ fn main() -> Result<(), impl std::error::Error> {
fill::fill_window(&window); fill::fill_window(&window);
} }
WindowEvent::CloseRequested => { WindowEvent::CloseRequested => {
control_flow.set_exit(); elwt.exit();
} }
_ => (), _ => (),
} }

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::{Key, ModifiersState}, keyboard::{Key, ModifiersState},
window::{CursorGrabMode, WindowBuilder}, window::{CursorGrabMode, WindowBuilder},
}; };
@@ -22,12 +22,12 @@ fn main() -> Result<(), impl std::error::Error> {
let mut modifiers = ModifiersState::default(); let mut modifiers = ModifiersState::default();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {
@@ -39,7 +39,7 @@ fn main() -> Result<(), impl std::error::Error> {
} => { } => {
let result = match key { let result = match key {
Key::Escape => { Key::Escape => {
control_flow.set_exit(); elwt.exit();
Ok(()) Ok(())
} }
Key::Character(ch) => match ch.to_lowercase().as_str() { Key::Character(ch) => match ch.to_lowercase().as_str() {

View File

@@ -5,7 +5,7 @@ fn main() -> Result<(), impl std::error::Error> {
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoopBuilder, event_loop::{ControlFlow, EventLoopBuilder},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -40,15 +40,15 @@ fn main() -> Result<(), impl std::error::Error> {
} }
}); });
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::UserEvent(event) => println!("user event: {event:?}"), Event::UserEvent(event) => println!("user event: {event:?}"),
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
} => control_flow.set_exit(), } => elwt.exit(),
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
.. ..

View File

@@ -21,12 +21,12 @@ fn main() -> Result<(), impl std::error::Error> {
let mut switched = false; let mut switched = false;
let mut entered_id = window_2.id(); 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) => { Event::NewEvents(StartCause::Init) => {
eprintln!("Switch which window is to be dragged by pressing \"x\".") eprintln!("Switch which window is to be dragged by pressing \"x\".")
} }
Event::WindowEvent { event, window_id } => match event { Event::WindowEvent { event, window_id } => match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::MouseInput { WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,
button: MouseButton::Left, button: MouseButton::Left,

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop; use winit::event_loop::{ControlFlow, EventLoop};
use winit::keyboard::Key; use winit::keyboard::Key;
use winit::window::{Fullscreen, WindowBuilder}; use winit::window::{Fullscreen, WindowBuilder};
@@ -52,12 +52,12 @@ fn main() -> Result<(), impl std::error::Error> {
println!("- I\tToggle mIn size limit"); println!("- I\tToggle mIn size limit");
println!("- A\tToggle mAx size limit"); println!("- A\tToggle mAx size limit");
event_loop.run(move |event, elwt, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {
@@ -67,7 +67,7 @@ fn main() -> Result<(), impl std::error::Error> {
}, },
.. ..
} => match key { } => match key {
Key::Escape => control_flow.set_exit(), Key::Escape => elwt.exit(),
// WARNING: Consider using `key_without_modifers()` if available on your platform. // WARNING: Consider using `key_without_modifers()` if available on your platform.
// See the `key_binding` example // See the `key_binding` example
Key::Character(ch) => match ch.to_lowercase().as_str() { Key::Character(ch) => match ch.to_lowercase().as_str() {

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::Key, keyboard::Key,
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -22,8 +22,8 @@ fn main() -> Result<(), impl std::error::Error> {
let mut close_requested = false; let mut close_requested = false;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match 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 // 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 // drop the window. That closes it, and results in `Destroyed` being
// sent. // sent.
control_flow.set_exit(); elwt.exit();
} }
} }
Key::Character("n") => { Key::Character("n") => {

View File

@@ -39,11 +39,11 @@ fn main() -> Result<(), impl std::error::Error> {
let mut cursor_position = PhysicalPosition::new(0.0, 0.0); let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
let mut ime_pos = PhysicalPosition::new(0.0, 0.0); let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
cursor_position = position; cursor_position = position;
} }

View File

@@ -31,12 +31,12 @@ fn main() -> Result<(), impl std::error::Error> {
let mut modifiers = ModifiersState::default(); let mut modifiers = ModifiersState::default();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::ModifiersChanged(new) => { WindowEvent::ModifiersChanged(new) => {
modifiers = new.state(); modifiers = new.state();
} }

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, 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)." 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| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::MouseWheel { delta, .. } => match delta { WindowEvent::MouseWheel { delta, .. } => match delta {
winit::event::MouseScrollDelta::LineDelta(x, y) => { winit::event::MouseScrollDelta::LineDelta(x, y) => {
println!("mouse wheel Line Delta: ({x},{y})"); println!("mouse wheel Line Delta: ({x},{y})");

View File

@@ -8,7 +8,7 @@ fn main() -> Result<(), impl std::error::Error> {
use winit::{ use winit::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::{Key, ModifiersState}, keyboard::{Key, ModifiersState},
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel}, 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() { match !window_senders.is_empty() {
true => control_flow.set_wait(), true => elwt.set_control_flow(ControlFlow::Wait),
false => control_flow.set_exit(), false => elwt.exit(),
}; };
match event { match event {
Event::WindowEvent { event, window_id } => match event { Event::WindowEvent { event, window_id } => match event {

View File

@@ -5,7 +5,7 @@ use std::collections::HashMap;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::Key, keyboard::Key,
window::Window, window::Window,
}; };
@@ -26,8 +26,8 @@ fn main() -> Result<(), impl std::error::Error> {
println!("Press N to open a new window."); println!("Press N to open a new window.");
event_loop.run(move |event, event_loop, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, window_id } = event { if let Event::WindowEvent { event, window_id } = event {
match event { match event {
@@ -38,7 +38,7 @@ fn main() -> Result<(), impl std::error::Error> {
windows.remove(&window_id); windows.remove(&window_id);
if windows.is_empty() { if windows.is_empty() {
control_flow.set_exit(); elwt.exit();
} }
} }
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
@@ -51,7 +51,7 @@ fn main() -> Result<(), impl std::error::Error> {
is_synthetic: false, is_synthetic: false,
.. ..
} if matches!(c.as_ref(), "n" | "N") => { } 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()); println!("Opened a new window: {:?}", window.id());
windows.insert(window.id(), window); windows.insert(window.id(), window);
} }

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, WindowEvent}, event::{ElementState, Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -19,14 +19,14 @@ fn main() -> Result<(), impl std::error::Error> {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
println!("{event:?}"); println!("{event:?}");
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::MouseInput { WindowEvent::MouseInput {
state: ElementState::Released, state: ElementState::Released,
.. ..

View File

@@ -7,7 +7,7 @@ fn main() -> Result<(), impl std::error::Error> {
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, 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:?}"); println!("{event:?}");
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
} => control_flow.set_exit(), } => elwt.exit(),
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
.. ..

View File

@@ -4,7 +4,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
dpi::LogicalSize, dpi::LogicalSize,
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::KeyCode, keyboard::KeyCode,
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {

View File

@@ -10,7 +10,7 @@ mod example {
use std::rc::Rc; use std::rc::Rc;
use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop; use winit::event_loop::{ControlFlow, EventLoop};
use winit::keyboard::Key; use winit::keyboard::Key;
use winit::platform::startup_notify::{ use winit::platform::startup_notify::{
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify, EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
@@ -32,7 +32,7 @@ mod example {
let mut counter = 0; let mut counter = 0;
let mut create_first_window = false; let mut create_first_window = false;
event_loop.run(move |event, elwt, flow| { event_loop.run(move |event, elwt| {
match event { match event {
Event::Resumed => create_first_window = true, Event::Resumed => create_first_window = true,
@@ -61,7 +61,7 @@ mod example {
// Remove the window from the map. // Remove the window from the map.
windows.remove(&window_id); windows.remove(&window_id);
if windows.is_empty() { if windows.is_empty() {
flow.set_exit(); elwt.exit();
return; return;
} }
} }
@@ -103,7 +103,7 @@ mod example {
create_first_window = false; create_first_window = false;
} }
flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
}) })
} }
} }

View File

@@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> {
println!(" (L) Light theme"); println!(" (L) Light theme");
println!(" (D) Dark theme"); println!(" (D) Dark theme");
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { window_id, event } = event { if let Event::WindowEvent { window_id, event } = event {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::ThemeChanged(theme) if window_id == window.id() => { WindowEvent::ThemeChanged(theme) if window_id == window.id() => {
println!("Theme is changed: {theme:?}") println!("Theme is changed: {theme:?}")
} }

View File

@@ -9,7 +9,7 @@ use web_time::Instant;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, StartCause, WindowEvent}, event::{Event, StartCause, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -27,21 +27,21 @@ fn main() -> Result<(), impl std::error::Error> {
let timer_length = Duration::new(1, 0); let timer_length = Duration::new(1, 0);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
println!("{event:?}"); println!("{event:?}");
match event { match event {
Event::NewEvents(StartCause::Init) => { 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 { .. }) => { 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"); println!("\nTimer\n");
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
.. ..
} => control_flow.set_exit(), } => elwt.exit(),
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
.. ..

View File

@@ -19,12 +19,12 @@ fn main() -> Result<(), impl std::error::Error> {
println!("Only supported on macOS at the moment."); println!("Only supported on macOS at the moment.");
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::TouchpadMagnify { delta, .. } => { WindowEvent::TouchpadMagnify { delta, .. } => {
if delta > 0.0 { if delta > 0.0 {
println!("Zoomed in {delta}"); println!("Zoomed in {delta}");

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -22,13 +22,13 @@ fn main() -> Result<(), impl std::error::Error> {
window.set_title("A fantastic window!"); window.set_title("A fantastic window!");
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
println!("{event:?}"); println!("{event:?}");
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
fill::fill_window(&window); fill::fill_window(&window);
} }

View File

@@ -2,7 +2,7 @@
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::KeyCode, keyboard::KeyCode,
window::{Fullscreen, WindowBuilder}, window::{Fullscreen, WindowBuilder},
}; };
@@ -21,8 +21,8 @@ pub fn main() -> Result<(), impl std::error::Error> {
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
let log_list = wasm::insert_canvas_and_create_log_list(&window); let log_list = wasm::insert_canvas_and_create_log_list(&window);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
#[cfg(wasm_platform)] #[cfg(wasm_platform)]
wasm::log_event(&log_list, &event); wasm::log_event(&log_list, &event);
@@ -31,7 +31,7 @@ pub fn main() -> Result<(), impl std::error::Error> {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id, window_id,
} if window_id == window.id() => control_flow.set_exit(), } if window_id == window.id() => elwt.exit(),
Event::AboutToWait => { Event::AboutToWait => {
window.request_redraw(); window.request_redraw();
} }

View File

@@ -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 once with the size info we currently have
render_circle(&canvas, window.inner_size()); render_circle(&canvas, window.inner_size());
let _ = event_loop.run(move |event, _, control_flow| { let _ = event_loop.run(move |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { Event::WindowEvent {

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -20,13 +20,13 @@ fn main() -> Result<(), impl std::error::Error> {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
println!("{event:?}"); println!("{event:?}");
match event { match event {
Event::WindowEvent { event, window_id } if window_id == window.id() => 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 => { WindowEvent::RedrawRequested => {
// Notify the windowing system that we'll be presenting to the window. // Notify the windowing system that we'll be presenting to the window.
window.pre_present_notify(); window.pre_present_notify();

View File

@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
dpi::LogicalSize, dpi::LogicalSize,
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{DeviceEvents, EventLoop}, event_loop::{ControlFlow, DeviceEvents, EventLoop},
keyboard::Key, keyboard::Key,
window::{WindowBuilder, WindowButtons}, window::{WindowBuilder, WindowButtons},
}; };
@@ -31,8 +31,8 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.listen_device_events(DeviceEvents::Always); event_loop.listen_device_events(DeviceEvents::Always);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { window_id, event } = event { if let Event::WindowEvent { window_id, event } = event {
match 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 => { WindowEvent::RedrawRequested => {
fill::fill_window(&window); fill::fill_window(&window);
} }

View File

@@ -6,7 +6,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
dpi::{LogicalSize, PhysicalSize}, dpi::{LogicalSize, PhysicalSize},
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
event_loop::{DeviceEvents, EventLoop}, event_loop::{ControlFlow, DeviceEvents, EventLoop},
keyboard::{Key, KeyCode}, keyboard::{Key, KeyCode},
window::{Fullscreen, WindowBuilder}, window::{Fullscreen, WindowBuilder},
}; };
@@ -38,8 +38,8 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.listen_device_events(DeviceEvents::Always); event_loop.listen_device_events(DeviceEvents::Always);
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
// This used to use the virtual key, but the new API // 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); window.set_minimized(minimized);
} }
"q" => { "q" => {
control_flow.set_exit(); elwt.exit();
} }
"v" => { "v" => {
visible = !visible; 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 => { WindowEvent::RedrawRequested => {
fill::fill_window(&window); fill::fill_window(&window);
} }

View File

@@ -3,7 +3,7 @@
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop}, event_loop::EventLoop,
keyboard::Key, keyboard::Key,
window::{CursorIcon, ResizeDirection, WindowBuilder}, window::{CursorIcon, ResizeDirection, WindowBuilder},
}; };
@@ -27,12 +27,12 @@ fn main() -> Result<(), impl std::error::Error> {
let mut border = false; let mut border = false;
let mut cursor_location = None; 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) => { Event::NewEvents(StartCause::Init) => {
eprintln!("Press 'B' to toggle borderless") eprintln!("Press 'B' to toggle borderless")
} }
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::CursorMoved { position, .. } => { WindowEvent::CursorMoved { position, .. } => {
if !window.is_decorated() { if !window.is_decorated() {
let new_location = let new_location =

View File

@@ -5,7 +5,7 @@ use std::path::Path;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::{Icon, WindowBuilder}, window::{Icon, WindowBuilder},
}; };
@@ -33,12 +33,12 @@ fn main() -> Result<(), impl std::error::Error> {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = event { if let Event::WindowEvent { event, .. } = event {
match event { match event {
WindowEvent::CloseRequested => control_flow.set_exit(), WindowEvent::CloseRequested => elwt.exit(),
WindowEvent::DroppedFile(path) => { WindowEvent::DroppedFile(path) => {
window.set_window_icon(Some(load_icon(&path))); window.set_window_icon(Some(load_icon(&path)));
} }

View File

@@ -10,7 +10,7 @@ fn main() -> Result<(), impl std::error::Error> {
use winit::{ use winit::{
error::EventLoopError, error::EventLoopError,
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
platform::run_ondemand::EventLoopExtRunOnDemand, platform::run_ondemand::EventLoopExtRunOnDemand,
window::{Window, WindowBuilder, WindowId}, 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> { fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> {
let mut app = App::default(); let mut app = App::default();
event_loop.run_ondemand(move |event, event_loop, control_flow| { event_loop.run_ondemand(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
println!("Run {idx}: {:?}", event); println!("Run {idx}: {:?}", event);
if let Some(window) = &app.window { if let Some(window) = &app.window {
@@ -60,7 +60,7 @@ fn main() -> Result<(), impl std::error::Error> {
} if id == window_id => { } if id == window_id => {
println!("--------------------------------------------------------- Window {idx} Destroyed"); println!("--------------------------------------------------------- Window {idx} Destroyed");
app.window_id = None; 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() let window = WindowBuilder::new()
.with_title("Fantastic window number one!") .with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.build(event_loop) .build(elwt)
.unwrap(); .unwrap();
app.window_id = Some(window.id()); app.window_id = Some(window.id());
app.window = Some(window); app.window = Some(window);

View File

@@ -7,7 +7,7 @@ use winit::platform::macos::{OptionAsAlt, WindowExtMacOS};
use winit::{ use winit::{
event::ElementState, event::ElementState,
event::{Event, MouseButton, WindowEvent}, event::{Event, MouseButton, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -31,14 +31,14 @@ fn main() -> Result<(), impl std::error::Error> {
let mut option_as_alt = window.option_as_alt(); let mut option_as_alt = window.option_as_alt();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id, window_id,
} if window_id == window.id() => control_flow.set_exit(), } if window_id == window.id() => elwt.exit(),
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => match event {
WindowEvent::MouseInput { WindowEvent::MouseInput {
state: ElementState::Pressed, state: ElementState::Pressed,

View File

@@ -32,8 +32,8 @@ fn main() -> std::process::ExitCode {
'main: loop { 'main: loop {
let timeout = Some(Duration::ZERO); let timeout = Some(Duration::ZERO);
let status = event_loop.pump_events(timeout, |event, _, control_flow| { let status = event_loop.pump_events(timeout, |event, elwt| {
*control_flow = ControlFlow::Wait; elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, .. } = &event { if let Event::WindowEvent { event, .. } = &event {
// Print only Window events to reduce noise // Print only Window events to reduce noise
@@ -44,7 +44,7 @@ fn main() -> std::process::ExitCode {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id, window_id,
} if window_id == window.id() => control_flow.set_exit(), } if window_id == window.id() => elwt.exit(),
Event::AboutToWait => { Event::AboutToWait => {
window.request_redraw(); window.request_redraw();
} }

View File

@@ -3,7 +3,7 @@ use simple_logger::SimpleLogger;
use winit::{ use winit::{
dpi::LogicalSize, dpi::LogicalSize,
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::Key, keyboard::Key,
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -24,12 +24,12 @@ fn main() -> Result<(), impl std::error::Error> {
let mut has_increments = true; let mut has_increments = true;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { event, window_id } if window_id == window.id() => 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 { WindowEvent::KeyboardInput {
event: event:
KeyEvent { KeyEvent {

View File

@@ -8,7 +8,7 @@ use simple_logger::SimpleLogger;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use winit::{ use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
keyboard::Key, keyboard::Key,
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS}, platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
@@ -30,8 +30,8 @@ fn main() -> Result<(), impl std::error::Error> {
println!("Press N to open a new window."); println!("Press N to open a new window.");
event_loop.run(move |event, event_loop, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
if let Event::WindowEvent { event, window_id } = event { if let Event::WindowEvent { event, window_id } = event {
match event { match event {
@@ -42,7 +42,7 @@ fn main() -> Result<(), impl std::error::Error> {
windows.remove(&window_id); windows.remove(&window_id);
if windows.is_empty() { if windows.is_empty() {
control_flow.set_exit(); elwt.exit();
} }
} }
WindowEvent::Resized(_) => { WindowEvent::Resized(_) => {
@@ -64,7 +64,7 @@ fn main() -> Result<(), impl std::error::Error> {
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier(); let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
let window = WindowBuilder::new() let window = WindowBuilder::new()
.with_tabbing_identifier(&tabbing_id) .with_tabbing_identifier(&tabbing_id)
.build(event_loop) .build(elwt)
.unwrap(); .unwrap();
println!("Added a new tab: {:?}", window.id()); println!("Added a new tab: {:?}", window.id());
windows.insert(window.id(), window); windows.insert(window.id(), window);

View File

@@ -10,7 +10,7 @@ mod imple {
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use winit::{ use winit::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
event_loop::EventLoop, event_loop::{ControlFlow, EventLoop},
platform::x11::WindowBuilderExtX11, platform::x11::WindowBuilderExtX11,
window::WindowBuilder, window::WindowBuilder,
}; };
@@ -32,14 +32,14 @@ mod imple {
.build(&event_loop) .build(&event_loop)
.unwrap(); .unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, elwt| {
control_flow.set_wait(); elwt.set_control_flow(ControlFlow::Wait);
match event { match event {
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
window_id, window_id,
} if window_id == window.id() => control_flow.set_exit(), } if window_id == window.id() => elwt.exit(),
Event::AboutToWait => { Event::AboutToWait => {
window.request_redraw(); window.request_redraw();
} }

View File

@@ -7,25 +7,24 @@
//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this: //! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
//! //!
//! ```rust,ignore //! ```rust,ignore
//! let mut control_flow = ControlFlow::Poll;
//! let mut start_cause = StartCause::Init; //! let mut start_cause = StartCause::Init;
//! //!
//! while control_flow != ControlFlow::Exit { //! while !elwt.exiting() {
//! event_handler(NewEvents(start_cause), ..., &mut control_flow); //! event_handler(NewEvents(start_cause), elwt);
//! //!
//! for e in (window events, user events, device events) { //! for e in (window events, user events, device events) {
//! event_handler(e, ..., &mut control_flow); //! event_handler(e, elwt);
//! } //! }
//! //!
//! for w in (redraw windows) { //! for w in (redraw windows) {
//! event_handler(RedrawRequested(w), ..., &mut control_flow); //! event_handler(RedrawRequested(w), elwt);
//! } //! }
//! //!
//! event_handler(AboutToWait, ..., &mut control_flow); //! event_handler(AboutToWait, elwt);
//! start_cause = wait_if_necessary(control_flow); //! 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 //! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully

View File

@@ -147,20 +147,12 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
} }
} }
/// 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. /// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
/// ///
/// Defaults to [`Poll`]. /// 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 /// [`Poll`]: Self::Poll
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControlFlow { pub enum ControlFlow {
@@ -180,81 +172,22 @@ pub enum ControlFlow {
/// ///
/// [`Poll`]: Self::Poll /// [`Poll`]: Self::Poll
WaitUntil(Instant), 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 { impl ControlFlow {
/// Alias for [`ExitWithCode`]`(0)`. /// Creates a [`ControlFlow`] that waits until a timeout has expired.
///
/// [`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.
/// ///
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
/// instead set to [`Wait`]. /// instead set to [`Wait`].
/// ///
/// [`WaitUntil`]: Self::WaitUntil /// [`WaitUntil`]: Self::WaitUntil
/// [`Wait`]: Self::Wait /// [`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) { match Instant::now().checked_add(timeout) {
Some(instant) => self.set_wait_until(instant), Some(instant) => Self::WaitUntil(instant),
None => self.set_wait(), 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 { impl Default for ControlFlow {
@@ -286,8 +219,7 @@ impl<T> EventLoop<T> {
/// Since the closure is `'static`, it must be a `move` closure if it needs to /// Since the closure is `'static`, it must be a `move` closure if it needs to
/// access any data from the calling context. /// access any data from the calling context.
/// ///
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// event loop's behavior.
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
@@ -306,12 +238,12 @@ impl<T> EventLoop<T> {
/// ///
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [`ControlFlow`]: crate::event_loop::ControlFlow /// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow
#[inline] #[inline]
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))] #[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &EventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
{ {
self.event_loop.run(event_handler) self.event_loop.run(event_handler)
} }
@@ -377,6 +309,30 @@ impl<T> EventLoopWindowTarget<T> {
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.p.listen_device_events(allowed); 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<T> HasRawDisplayHandle for EventLoopWindowTarget<T> { unsafe impl<T> HasRawDisplayHandle for EventLoopWindowTarget<T> {

View File

@@ -30,8 +30,7 @@
//! //!
//! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will //! 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 //! 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 //! will run until [`exit()`] is used, at which
//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which
//! point [`Event`]`::`[`LoopExiting`] is emitted and the entire program terminates. //! point [`Event`]`::`[`LoopExiting`] is emitted and the entire program terminates.
//! //!
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop //! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
@@ -44,30 +43,29 @@
//! ```no_run //! ```no_run
//! use winit::{ //! use winit::{
//! event::{Event, WindowEvent}, //! event::{Event, WindowEvent},
//! event_loop::EventLoop, //! event_loop::{ControlFlow, EventLoop},
//! window::WindowBuilder, //! window::WindowBuilder,
//! }; //! };
//! //!
//! let event_loop = EventLoop::new().unwrap(); //! let event_loop = EventLoop::new().unwrap();
//! let window = WindowBuilder::new().build(&event_loop).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 //! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
//! // dispatched any events. This is ideal for games and similar applications. //! // 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. //! // 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 //! // 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. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! control_flow.set_wait(); //! elwt.set_control_flow(ControlFlow::Wait);
//!
//! match event { //! match event {
//! Event::WindowEvent { //! Event::WindowEvent {
//! event: WindowEvent::CloseRequested, //! event: WindowEvent::CloseRequested,
//! .. //! ..
//! } => { //! } => {
//! println!("The close button was pressed; stopping"); //! println!("The close button was pressed; stopping");
//! control_flow.set_exit(); //! elwt.exit();
//! }, //! },
//! Event::AboutToWait => { //! Event::AboutToWait => {
//! // Application update code. //! // Application update code.
@@ -115,9 +113,7 @@
//! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events //! [`EventLoopExtPumpEvents::pump_events`]: ./platform/pump_events/trait.EventLoopExtPumpEvents.html#tymethod.pump_events
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [event_loop_run]: event_loop::EventLoop::run //! [event_loop_run]: event_loop::EventLoop::run
//! [`ControlFlow`]: event_loop::ControlFlow //! [`exit()`]: event_loop::EventLoopWindowTarget::exit
//! [`Exit`]: event_loop::ControlFlow::Exit
//! [`ExitWithCode`]: event_loop::ControlFlow::ExitWithCode
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
//! [`WindowBuilder`]: window::WindowBuilder //! [`WindowBuilder`]: window::WindowBuilder

View File

@@ -2,7 +2,7 @@ use std::time::Duration;
use crate::{ use crate::{
event::Event, event::Event,
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
}; };
/// The return status for `pump_events` /// The return status for `pump_events`
@@ -63,7 +63,7 @@ pub trait EventLoopExtPumpEvents {
/// ///
/// 'main: loop { /// 'main: loop {
/// let timeout = Some(Duration::ZERO); /// 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 { /// # if let Event::WindowEvent { event, .. } = &event {
/// # // Print only Window events to reduce noise /// # // Print only Window events to reduce noise
/// # println!("{event:?}"); /// # println!("{event:?}");
@@ -73,7 +73,7 @@ pub trait EventLoopExtPumpEvents {
/// Event::WindowEvent { /// Event::WindowEvent {
/// event: WindowEvent::CloseRequested, /// event: WindowEvent::CloseRequested,
/// window_id, /// window_id,
/// } if window_id == window.id() => control_flow.set_exit(), /// } if window_id == window.id() => elwt.exit(),
/// Event::AboutToWait => { /// Event::AboutToWait => {
/// window.request_redraw(); /// window.request_redraw();
/// } /// }
@@ -174,7 +174,7 @@ pub trait EventLoopExtPumpEvents {
/// callback. /// callback.
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
} }
impl<T> EventLoopExtPumpEvents for EventLoop<T> { impl<T> EventLoopExtPumpEvents for EventLoop<T> {
@@ -182,7 +182,7 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{ {
self.event_loop.pump_events(timeout, event_handler) self.event_loop.pump_events(timeout, event_handler)
} }

View File

@@ -1,7 +1,7 @@
use crate::{ use crate::{
error::EventLoopError, error::EventLoopError,
event::Event, event::Event,
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
}; };
#[cfg(doc)] #[cfg(doc)]
@@ -17,7 +17,7 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures /// 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 /// 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. /// 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 /// 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 /// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume
/// lifecycle) - which can be used to consistently initialize application state. /// lifecycle) - which can be used to consistently initialize application state.
/// ///
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// event loop's behavior.
/// ///
/// # Caveats /// # Caveats
/// - This extension isn't available on all platforms, since it's not always possible to /// - 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 /// polled to ask for new events. Events are delivered via callbacks based
/// on an event loop that is internal to the browser itself. /// 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. /// - **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<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow); F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
} }
impl<T> EventLoopExtRunOnDemand for EventLoop<T> { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
@@ -67,7 +69,7 @@ impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError> fn run_ondemand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow), F: FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
{ {
self.event_loop.run_ondemand(event_handler) self.event_loop.run_ondemand(event_handler)
} }

View File

@@ -28,7 +28,6 @@
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding //! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
use crate::event::Event; use crate::event::Event;
use crate::event_loop::ControlFlow;
use crate::event_loop::EventLoop; use crate::event_loop::EventLoop;
use crate::event_loop::EventLoopWindowTarget; use crate::event_loop::EventLoopWindowTarget;
use crate::window::{Window, WindowBuilder}; use crate::window::{Window, WindowBuilder};
@@ -122,8 +121,7 @@ pub trait EventLoopExtWebSys {
/// event loop when switching between tabs on a single page application. /// event loop when switching between tabs on a single page application.
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>);
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow);
} }
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
@@ -131,8 +129,7 @@ impl<T> EventLoopExtWebSys for EventLoop<T> {
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static F: 'static + FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>),
+ FnMut(Event<Self::UserEvent>, &EventLoopWindowTarget<Self::UserEvent>, &mut ControlFlow),
{ {
self.event_loop.spawn(event_handler) self.event_loop.spawn(event_handler)
} }

View File

@@ -1,6 +1,7 @@
#![cfg(android_platform)] #![cfg(android_platform)]
use std::{ use std::{
cell::Cell,
collections::VecDeque, collections::VecDeque,
hash::Hash, hash::Hash,
sync::{ sync::{
@@ -147,7 +148,6 @@ pub struct EventLoop<T: 'static> {
loop_running: bool, // Dispatched `NewEvents<Init>` loop_running: bool, // Dispatched `NewEvents<Init>`
running: bool, running: bool,
pending_redraw: bool, pending_redraw: bool,
control_flow: ControlFlow,
cause: StartCause, cause: StartCause,
ignore_volume_keys: bool, ignore_volume_keys: bool,
combining_accent: Option<char>, combining_accent: Option<char>,
@@ -168,23 +168,6 @@ impl Default for PlatformSpecificEventLoopAttributes {
} }
} }
fn sticky_exit_callback<T, F>(
evt: event::Event<T>,
target: &RootELW<T>,
control_flow: &mut ControlFlow,
callback: &mut F,
) where
F: FnMut(event::Event<T>, &RootELW<T>, &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<T: 'static> EventLoop<T> { impl<T: 'static> EventLoop<T> {
pub(crate) fn new( pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes, attributes: &PlatformSpecificEventLoopAttributes,
@@ -199,6 +182,8 @@ impl<T: 'static> EventLoop<T> {
window_target: event_loop::EventLoopWindowTarget { window_target: event_loop::EventLoopWindowTarget {
p: EventLoopWindowTarget { p: EventLoopWindowTarget {
app: android_app.clone(), app: android_app.clone(),
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(false),
redraw_requester: RedrawRequester::new( redraw_requester: RedrawRequester::new(
&redraw_flag, &redraw_flag,
android_app.create_waker(), android_app.create_waker(),
@@ -213,7 +198,6 @@ impl<T: 'static> EventLoop<T> {
loop_running: false, loop_running: false,
running: false, running: false,
pending_redraw: false, pending_redraw: false,
control_flow: Default::default(),
cause: StartCause::Init, cause: StartCause::Init,
ignore_volume_keys: attributes.ignore_volume_keys, ignore_volume_keys: attributes.ignore_volume_keys,
combining_accent: None, combining_accent: None,
@@ -222,41 +206,25 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F) fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
where where
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
trace!("Mainloop iteration"); trace!("Mainloop iteration");
let cause = self.cause; let cause = self.cause;
let mut control_flow = self.control_flow;
let mut pending_redraw = self.pending_redraw; let mut pending_redraw = self.pending_redraw;
let mut resized = false; let mut resized = false;
sticky_exit_callback( callback(event::Event::NewEvents(cause), self.window_target());
event::Event::NewEvents(cause),
self.window_target(),
&mut control_flow,
callback,
);
if let Some(event) = main_event { if let Some(event) = main_event {
trace!("Handling main event {:?}", event); trace!("Handling main event {:?}", event);
match event { match event {
MainEvent::InitWindow { .. } => { MainEvent::InitWindow { .. } => {
sticky_exit_callback( callback(event::Event::Resumed, self.window_target());
event::Event::Resumed,
self.window_target(),
&mut control_flow,
callback,
);
} }
MainEvent::TerminateWindow { .. } => { MainEvent::TerminateWindow { .. } => {
sticky_exit_callback( callback(event::Event::Suspended, self.window_target());
event::Event::Suspended,
self.window_target(),
&mut control_flow,
callback,
);
} }
MainEvent::WindowResized { .. } => resized = true, MainEvent::WindowResized { .. } => resized = true,
MainEvent::RedrawNeeded { .. } => pending_redraw = true, MainEvent::RedrawNeeded { .. } => pending_redraw = true,
@@ -265,26 +233,22 @@ impl<T: 'static> EventLoop<T> {
} }
MainEvent::GainedFocus => { MainEvent::GainedFocus => {
*HAS_FOCUS.write().unwrap() = true; *HAS_FOCUS.write().unwrap() = true;
sticky_exit_callback( callback(
event::Event::WindowEvent { event::Event::WindowEvent {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(true), event: event::WindowEvent::Focused(true),
}, },
self.window_target(), self.window_target(),
&mut control_flow,
callback,
); );
} }
MainEvent::LostFocus => { MainEvent::LostFocus => {
*HAS_FOCUS.write().unwrap() = false; *HAS_FOCUS.write().unwrap() = false;
sticky_exit_callback( callback(
event::Event::WindowEvent { event::Event::WindowEvent {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(false), event: event::WindowEvent::Focused(false),
}, },
self.window_target(), self.window_target(),
&mut control_flow,
callback,
); );
} }
MainEvent::ConfigChanged { .. } => { MainEvent::ConfigChanged { .. } => {
@@ -304,12 +268,7 @@ impl<T: 'static> EventLoop<T> {
scale_factor, scale_factor,
}, },
}; };
sticky_exit_callback( callback(event, self.window_target());
event,
self.window_target(),
&mut control_flow,
callback,
);
} }
} }
MainEvent::LowMemory => { MainEvent::LowMemory => {
@@ -363,9 +322,8 @@ impl<T: 'static> EventLoop<T> {
// Process input events // Process input events
match android_app.input_events_iter() { match android_app.input_events_iter() {
Ok(mut input_iter) => loop { Ok(mut input_iter) => loop {
let read_event = input_iter.next(|event| { let read_event =
self.handle_input_event(&android_app, event, &mut control_flow, callback) input_iter.next(|event| self.handle_input_event(&android_app, event, callback));
});
if !read_event { if !read_event {
break; break;
@@ -379,12 +337,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer // Empty the user event buffer
{ {
while let Ok(event) = self.user_events_receiver.try_recv() { while let Ok(event) = self.user_events_receiver.try_recv() {
sticky_exit_callback( callback(crate::event::Event::UserEvent(event), self.window_target());
crate::event::Event::UserEvent(event),
self.window_target(),
&mut control_flow,
callback,
);
} }
} }
@@ -401,7 +354,7 @@ impl<T: 'static> EventLoop<T> {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size), 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(); pending_redraw |= self.redraw_flag.get_and_reset();
@@ -411,19 +364,13 @@ impl<T: 'static> EventLoop<T> {
window_id: window::WindowId(WindowId), window_id: window::WindowId(WindowId),
event: event::WindowEvent::RedrawRequested, 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 // This is always the last event we dispatch before poll again
sticky_exit_callback( callback(event::Event::AboutToWait, self.window_target());
event::Event::AboutToWait,
self.window_target(),
&mut control_flow,
callback,
);
self.control_flow = control_flow;
self.pending_redraw = pending_redraw; self.pending_redraw = pending_redraw;
} }
@@ -431,11 +378,10 @@ impl<T: 'static> EventLoop<T> {
&mut self, &mut self,
android_app: &AndroidApp, android_app: &AndroidApp,
event: &InputEvent<'_>, event: &InputEvent<'_>,
control_flow: &mut ControlFlow,
callback: &mut F, callback: &mut F,
) -> InputStatus ) -> InputStatus
where where
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
let mut input_status = InputStatus::Handled; let mut input_status = InputStatus::Handled;
match event { match event {
@@ -483,7 +429,7 @@ impl<T: 'static> EventLoop<T> {
force: None, force: None,
}), }),
}; };
sticky_exit_callback(event, self.window_target(), control_flow, callback); callback(event, self.window_target());
} }
} }
} }
@@ -526,7 +472,7 @@ impl<T: 'static> EventLoop<T> {
is_synthetic: false, is_synthetic: false,
}, },
}; };
sticky_exit_callback(event, self.window_target(), control_flow, callback); callback(event, self.window_target());
} }
} }
} }
@@ -540,14 +486,14 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{ {
self.run_ondemand(event_handler) self.run_ondemand(event_handler)
} }
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{ {
if self.loop_running { if self.loop_running {
return Err(EventLoopError::AlreadyRunning); return Err(EventLoopError::AlreadyRunning);
@@ -570,7 +516,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -580,7 +526,7 @@ impl<T: 'static> EventLoop<T> {
// than once // than once
self.pending_redraw = false; self.pending_redraw = false;
self.cause = StartCause::Init; self.cause = StartCause::Init;
self.control_flow = ControlFlow::Poll; self.set_control_flow(ControlFlow::default());
// run the initial loop iteration // run the initial loop iteration
self.single_iteration(None, &mut callback); self.single_iteration(None, &mut callback);
@@ -588,21 +534,15 @@ impl<T: 'static> EventLoop<T> {
// Consider the possibility that the `StartCause::Init` iteration could // Consider the possibility that the `StartCause::Init` iteration could
// request to Exit // request to Exit
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { if !self.exiting() {
self.poll_events_with_timeout(timeout, &mut callback); self.poll_events_with_timeout(timeout, &mut callback);
} }
if let ControlFlow::ExitWithCode(code) = self.control_flow { if self.exiting() {
self.loop_running = false; self.loop_running = false;
let mut dummy = self.control_flow; callback(event::Event::LoopExiting, self.window_target());
sticky_exit_callback(
event::Event::LoopExiting,
self.window_target(),
&mut dummy,
&mut callback,
);
PumpStatus::Exit(code) PumpStatus::Exit(0)
} else { } else {
PumpStatus::Continue PumpStatus::Continue
} }
@@ -610,7 +550,7 @@ impl<T: 'static> EventLoop<T> {
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &RootELW<T>),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -621,14 +561,12 @@ impl<T: 'static> EventLoop<T> {
// If we already have work to do then we don't want to block on the next poll // If we already have work to do then we don't want to block on the next poll
Some(Duration::ZERO) Some(Duration::ZERO)
} else { } else {
let control_flow_timeout = match self.control_flow { let control_flow_timeout = match self.control_flow() {
ControlFlow::Wait => None, ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => { ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start)) 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) min_timeout(control_flow_timeout, timeout)
@@ -663,7 +601,7 @@ impl<T: 'static> EventLoop<T> {
} }
} }
self.cause = match self.control_flow { self.cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { ControlFlow::Wait => StartCause::WaitCancelled {
start, start,
@@ -682,8 +620,6 @@ impl<T: 'static> EventLoop<T> {
} }
} }
} }
// `ExitWithCode()` will be reset to `Poll` before polling
ControlFlow::ExitWithCode(_code) => unreachable!(),
}; };
self.single_iteration(main_event, &mut callback); self.single_iteration(main_event, &mut callback);
@@ -700,6 +636,18 @@ impl<T: 'static> EventLoop<T> {
waker: self.android_app.create_waker(), 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<T: 'static> { pub struct EventLoopProxy<T: 'static> {
@@ -728,6 +676,8 @@ impl<T> EventLoopProxy<T> {
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget<T: 'static> {
app: AndroidApp, app: AndroidApp,
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,
_marker: std::marker::PhantomData<T>, _marker: std::marker::PhantomData<T>,
} }
@@ -749,6 +699,22 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle { pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Android(AndroidDisplayHandle::empty()) 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)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]

View File

@@ -121,7 +121,7 @@ enum AppStateImpl {
Terminated, Terminated,
} }
struct AppState { pub(crate) struct AppState {
// This should never be `None`, except for briefly during a state transition. // This should never be `None`, except for briefly during a state transition.
app_state: Option<AppStateImpl>, app_state: Option<AppStateImpl>,
control_flow: ControlFlow, control_flow: ControlFlow,
@@ -129,7 +129,7 @@ struct AppState {
} }
impl 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 // basically everything in UIKit requires the main thread, so it's pointless to use the
// std::sync APIs. // std::sync APIs.
// must be mut because plain `static` requires `Sync` // must be mut because plain `static` requires `Sync`
@@ -290,7 +290,6 @@ impl AppState {
}; };
(waiting_event_handler, event) (waiting_event_handler, event)
} }
(ControlFlow::ExitWithCode(_), _) => bug!("unexpected `ControlFlow` `Exit`"),
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
}; };
@@ -443,12 +442,6 @@ impl AppState {
}); });
self.waker.start() 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), 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<WinitUIWindow>) { pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
@@ -602,7 +603,6 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
processing_redraws, processing_redraws,
} => (event_handler, active_control_flow, processing_redraws), } => (event_handler, active_control_flow, processing_redraws),
}; };
let mut control_flow = this.control_flow;
drop(this); drop(this);
for wrapper in events { for wrapper in events {
@@ -616,10 +616,10 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event, &mut control_flow) event_handler.handle_nonuser_event(event)
} }
EventWrapper::ScaleFactorChanged(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<I: IntoIterator<Item = EventWrapper>>(
active_control_flow, active_control_flow,
} }
}); });
this.control_flow = control_flow;
break; break;
} }
drop(this); drop(this);
@@ -673,10 +672,10 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event, &mut control_flow) event_handler.handle_nonuser_event(event)
} }
EventWrapper::ScaleFactorChanged(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<I: IntoIterator<Item = EventWrapper>>(
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut control_flow = this.control_flow;
let (mut event_handler, active_control_flow, processing_redraws) = let (mut event_handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => { UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
@@ -702,7 +700,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
} }
drop(this); drop(this);
event_handler.handle_user_events(&mut control_flow); event_handler.handle_user_events();
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
@@ -726,22 +724,19 @@ fn handle_user_events(mtm: MainThreadMarker) {
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
}); });
this.control_flow = control_flow;
break; break;
} }
drop(this); drop(this);
for wrapper in queued_events { for wrapper in queued_events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => { EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event),
event_handler.handle_nonuser_event(event, &mut control_flow)
}
EventWrapper::ScaleFactorChanged(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) { pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut event_handler = this.terminated_transition(); let mut event_handler = this.terminated_transition();
let mut control_flow = this.control_flow;
drop(this); drop(this);
event_handler.handle_nonuser_event(Event::LoopExiting, &mut control_flow) event_handler.handle_nonuser_event(Event::LoopExiting)
} }
fn handle_hidpi_proxy( fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) {
event_handler: &mut Box<dyn EventHandler>,
mut control_flow: ControlFlow,
event: ScaleFactorChanged,
) {
let ScaleFactorChanged { let ScaleFactorChanged {
suggested_size, suggested_size,
scale_factor, scale_factor,
@@ -806,7 +796,7 @@ fn handle_hidpi_proxy(
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), 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 (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);

View File

@@ -28,8 +28,11 @@ use crate::{
platform::ios::Idiom, platform::ios::Idiom,
}; };
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
use super::{app_state, monitor, view, MonitorHandle}; use super::{app_state, monitor, view, MonitorHandle};
use super::{
app_state::AppState,
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
};
#[derive(Debug)] #[derive(Debug)]
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget<T: 'static> {
@@ -52,6 +55,24 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle { pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty()) 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<T: 'static> { pub struct EventLoop<T: 'static> {
@@ -102,7 +123,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(self, event_handler: F) -> ! pub fn run<F>(self, event_handler: F) -> !
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
unsafe { unsafe {
let application = UIApplication::shared(self.mtm); let application = UIApplication::shared(self.mtm);
@@ -114,7 +135,7 @@ impl<T: 'static> EventLoop<T> {
); );
let event_handler = std::mem::transmute::< let event_handler = std::mem::transmute::<
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow)>, Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>)>,
Box<EventHandlerCallback<T>>, Box<EventHandlerCallback<T>>,
>(Box::new(event_handler)); >(Box::new(event_handler));
@@ -314,12 +335,11 @@ fn setup_control_flow_observers() {
#[derive(Debug)] #[derive(Debug)]
pub enum Never {} pub enum Never {}
type EventHandlerCallback<T> = type EventHandlerCallback<T> = dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>) + 'static;
dyn FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow) + 'static;
pub trait EventHandler: Debug { pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow); fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow); fn handle_user_events(&mut self);
} }
struct EventLoopHandler<T: 'static> { struct EventLoopHandler<T: 'static> {
@@ -337,17 +357,13 @@ impl<T: 'static> Debug for EventLoopHandler<T> {
} }
impl<T: 'static> EventHandler for EventLoopHandler<T> { impl<T: 'static> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) { fn handle_nonuser_event(&mut self, event: Event<Never>) {
(self.f)( (self.f)(event.map_nonuser_event().unwrap(), &self.event_loop);
event.map_nonuser_event().unwrap(),
&self.event_loop,
control_flow,
);
} }
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { fn handle_user_events(&mut self) {
for event in self.receiver.try_iter() { 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);
} }
} }
} }

View File

@@ -19,7 +19,7 @@ use crate::platform::x11::XlibErrorHook;
use crate::{ use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError}, error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, KeyEvent}, event::KeyEvent,
event_loop::{ event_loop::{
AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
EventLoopWindowTarget as RootELW, EventLoopWindowTarget as RootELW,
@@ -767,21 +767,21 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
self.run_ondemand(callback) self.run_ondemand(callback)
} }
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_ondemand(callback)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_ondemand(callback))
} }
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(crate::event::Event<T>, &RootELW<T>),
{ {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback)) x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
} }
@@ -845,22 +845,29 @@ impl<T> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle()) x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle())
} }
}
fn sticky_exit_callback<T, F>( pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
evt: Event<T>, x11_or_wayland!(match self; Self(evlp) => evlp.set_control_flow(control_flow))
target: &RootELW<T>, }
control_flow: &mut ControlFlow,
callback: &mut F, pub(crate) fn control_flow(&self) -> ControlFlow {
) where x11_or_wayland!(match self; Self(evlp) => evlp.control_flow())
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), }
{
// make ControlFlow::ExitWithCode sticky by providing a dummy pub(crate) fn exit(&self) {
// control flow reference if it is already ExitWithCode. x11_or_wayland!(match self; Self(evlp) => evlp.exit())
if let ControlFlow::ExitWithCode(code) = *control_flow { }
callback(evt, target, &mut ControlFlow::ExitWithCode(code))
} else { pub(crate) fn exiting(&self) -> bool {
callback(evt, target, control_flow) 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<i32> {
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
} }
} }

View File

@@ -1,6 +1,6 @@
//! The event-loop routines. //! The event-loop routines.
use std::cell::RefCell; use std::cell::{Cell, RefCell};
use std::io::Result as IOResult; use std::io::Result as IOResult;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
@@ -24,7 +24,6 @@ use crate::event_loop::{
}; };
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::platform::min_timeout; use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::platform::sticky_exit_callback;
use crate::platform_impl::{EventLoopWindowTarget as PlatformEventLoopWindowTarget, OsError}; use crate::platform_impl::{EventLoopWindowTarget as PlatformEventLoopWindowTarget, OsError};
mod proxy; mod proxy;
@@ -44,9 +43,6 @@ pub struct EventLoop<T: 'static> {
/// Has `run` or `run_ondemand` been called or a call to `pump_events` that starts the loop /// Has `run` or `run_ondemand` been called or a call to `pump_events` that starts the loop
loop_running: bool, loop_running: bool,
/// The application's latest control_flow state
control_flow: ControlFlow,
buffer_sink: EventSink, buffer_sink: EventSink,
compositor_updates: Vec<WindowCompositorUpdate>, compositor_updates: Vec<WindowCompositorUpdate>,
window_ids: Vec<WindowId>, window_ids: Vec<WindowId>,
@@ -168,13 +164,14 @@ impl<T: 'static> EventLoop<T> {
wayland_dispatcher: wayland_dispatcher.clone(), wayland_dispatcher: wayland_dispatcher.clone(),
event_loop_awakener, event_loop_awakener,
queue_handle, queue_handle,
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
state: RefCell::new(winit_state), state: RefCell::new(winit_state),
_marker: PhantomData, _marker: PhantomData,
}; };
let event_loop = Self { let event_loop = Self {
loop_running: false, loop_running: false,
control_flow: ControlFlow::default(),
compositor_updates: Vec::new(), compositor_updates: Vec::new(),
buffer_sink: EventSink::default(), buffer_sink: EventSink::default(),
window_ids: Vec::new(), window_ids: Vec::new(),
@@ -194,7 +191,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
if self.loop_running { if self.loop_running {
return Err(EventLoopError::AlreadyRunning); return Err(EventLoopError::AlreadyRunning);
@@ -225,7 +222,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -233,7 +230,7 @@ impl<T: 'static> EventLoop<T> {
// Reset the internal state for the loop as we start running to // Reset the internal state for the loop as we start running to
// ensure consistent behaviour in case the loop runs and exits more // ensure consistent behaviour in case the loop runs and exits more
// than once. // than once.
self.control_flow = ControlFlow::Poll; self.set_control_flow(ControlFlow::Poll);
// Run the initial loop iteration. // Run the initial loop iteration.
self.single_iteration(&mut callback, StartCause::Init); self.single_iteration(&mut callback, StartCause::Init);
@@ -241,19 +238,13 @@ impl<T: 'static> EventLoop<T> {
// Consider the possibility that the `StartCause::Init` iteration could // Consider the possibility that the `StartCause::Init` iteration could
// request to Exit. // request to Exit.
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { if !self.exiting() {
self.poll_events_with_timeout(timeout, &mut callback); 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; self.loop_running = false;
let mut dummy = self.control_flow; callback(Event::LoopExiting, self.window_target());
sticky_exit_callback(
Event::LoopExiting,
self.window_target(),
&mut dummy,
&mut callback,
);
PumpStatus::Exit(code) PumpStatus::Exit(code)
} else { } else {
@@ -263,7 +254,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
let cause = loop { let cause = loop {
let start = Instant::now(); let start = Instant::now();
@@ -299,7 +290,7 @@ impl<T: 'static> EventLoop<T> {
} }
Err(error) => { Err(error) => {
error!("Error dispatching wayland queue: {}", error); error!("Error dispatching wayland queue: {}", error);
self.control_flow = ControlFlow::ExitWithCode(1); self.set_exit_code(1);
return; return;
} }
} }
@@ -308,17 +299,12 @@ impl<T: 'static> EventLoop<T> {
timeout = if instant_wakeup { timeout = if instant_wakeup {
Some(Duration::ZERO) Some(Duration::ZERO)
} else { } else {
let control_flow_timeout = match self.control_flow { let control_flow_timeout = match self.control_flow() {
ControlFlow::Wait => None, ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => { ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start)) 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) min_timeout(control_flow_timeout, timeout)
}; };
@@ -336,13 +322,13 @@ impl<T: 'static> EventLoop<T> {
// with an API to do that via some event. // 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. // 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); let exit_code = error.raw_os_error().unwrap_or(1);
self.control_flow = ControlFlow::ExitWithCode(exit_code); self.set_exit_code(exit_code);
return; return;
} }
// NB: `StartCause::Init` is handled as a special case and doesn't need // NB: `StartCause::Init` is handled as a special case and doesn't need
// to be considered here // to be considered here
let cause = match self.control_flow { let cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { ControlFlow::Wait => StartCause::WaitCancelled {
start, start,
@@ -361,11 +347,6 @@ impl<T: 'static> EventLoop<T> {
} }
} }
} }
// 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. // Reduce spurious wake-ups.
@@ -380,14 +361,12 @@ impl<T: 'static> EventLoop<T> {
self.single_iteration(&mut callback, cause); self.single_iteration(&mut callback, cause);
} }
fn single_iteration<F>(&mut self, mut callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
// NOTE currently just indented to simplify the diff // 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 // 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 // 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 // trying to mutably borrow `self` more than once and we swap them back
@@ -396,33 +375,18 @@ impl<T: 'static> EventLoop<T> {
let mut buffer_sink = std::mem::take(&mut self.buffer_sink); let mut buffer_sink = std::mem::take(&mut self.buffer_sink);
let mut window_ids = std::mem::take(&mut self.window_ids); let mut window_ids = std::mem::take(&mut self.window_ids);
sticky_exit_callback( callback(Event::NewEvents(cause), &self.window_target);
Event::NewEvents(cause),
&self.window_target,
&mut control_flow,
callback,
);
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland // NB: For consistency all platforms must emit a 'resumed' event even though Wayland
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
if cause == StartCause::Init { if cause == StartCause::Init {
sticky_exit_callback( callback(Event::Resumed, &self.window_target);
Event::Resumed,
&self.window_target,
&mut control_flow,
callback,
);
} }
// Handle pending user events. We don't need back buffer, since we can't dispatch // Handle pending user events. We don't need back buffer, since we can't dispatch
// user events indirectly via callback to the user. // user events indirectly via callback to the user.
for user_event in self.pending_user_events.borrow_mut().drain(..) { for user_event in self.pending_user_events.borrow_mut().drain(..) {
sticky_exit_callback( callback(Event::UserEvent(user_event), &self.window_target);
Event::UserEvent(user_event),
&self.window_target,
&mut control_flow,
&mut callback,
);
} }
// Drain the pending compositor updates. // Drain the pending compositor updates.
@@ -445,7 +409,7 @@ impl<T: 'static> EventLoop<T> {
let old_physical_size = physical_size; let old_physical_size = physical_size;
let new_inner_size = Arc::new(Mutex::new(physical_size)); let new_inner_size = Arc::new(Mutex::new(physical_size));
sticky_exit_callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: WindowEvent::ScaleFactorChanged { event: WindowEvent::ScaleFactorChanged {
@@ -456,8 +420,6 @@ impl<T: 'static> EventLoop<T> {
}, },
}, },
&self.window_target, &self.window_target,
&mut control_flow,
&mut callback,
); );
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
@@ -499,26 +461,22 @@ impl<T: 'static> EventLoop<T> {
physical_size physical_size
}); });
sticky_exit_callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: WindowEvent::Resized(physical_size), event: WindowEvent::Resized(physical_size),
}, },
&self.window_target, &self.window_target,
&mut control_flow,
&mut callback,
); );
} }
if compositor_update.close_window { if compositor_update.close_window {
sticky_exit_callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: WindowEvent::CloseRequested, event: WindowEvent::CloseRequested,
}, },
&self.window_target, &self.window_target,
&mut control_flow,
&mut callback,
); );
} }
} }
@@ -529,7 +487,7 @@ impl<T: 'static> EventLoop<T> {
}); });
for event in buffer_sink.drain() { for event in buffer_sink.drain() {
let event = event.map_nonuser_event().unwrap(); 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. // Handle non-synthetic events.
@@ -538,7 +496,7 @@ impl<T: 'static> EventLoop<T> {
}); });
for event in buffer_sink.drain() { for event in buffer_sink.drain() {
let event = event.map_nonuser_event().unwrap(); 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 // Collect the window ids
@@ -581,14 +539,12 @@ impl<T: 'static> EventLoop<T> {
}); });
if request_redraw { if request_redraw {
sticky_exit_callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, },
&self.window_target, &self.window_target,
&mut control_flow,
&mut callback,
); );
} }
} }
@@ -599,14 +555,8 @@ impl<T: 'static> EventLoop<T> {
}); });
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
sticky_exit_callback( callback(Event::AboutToWait, &self.window_target);
Event::AboutToWait,
&self.window_target,
&mut control_flow,
&mut callback,
);
self.control_flow = control_flow;
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates); 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.buffer_sink, &mut buffer_sink);
std::mem::swap(&mut self.window_ids, &mut window_ids); std::mem::swap(&mut self.window_ids, &mut window_ids);
@@ -660,6 +610,26 @@ impl<T: 'static> EventLoop<T> {
)))) ))))
}) })
} }
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<i32> {
self.window_target.p.exit_code()
}
} }
pub struct EventLoopWindowTarget<T> { pub struct EventLoopWindowTarget<T> {
@@ -669,6 +639,12 @@ pub struct EventLoopWindowTarget<T> {
/// The main queue used by the event loop. /// The main queue used by the event loop.
pub queue_handle: QueueHandle<WinitState>, pub queue_handle: QueueHandle<WinitState>,
/// The application's latest control_flow state
pub(crate) control_flow: Cell<ControlFlow>,
/// The application's exit state.
pub(crate) exit: Cell<Option<i32>>,
// TODO remove that RefCell once we can pass `&mut` in `Window::new`. // TODO remove that RefCell once we can pass `&mut` in `Window::new`.
/// Winit state. /// Winit state.
pub state: RefCell<WinitState>, pub state: RefCell<WinitState>,

View File

@@ -4,6 +4,7 @@ use sctk::reexports::client::Proxy;
use sctk::output::OutputData; use sctk::output::OutputData;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::event_loop::ControlFlow;
use crate::platform_impl::platform::VideoMode as PlatformVideoMode; use crate::platform_impl::platform::VideoMode as PlatformVideoMode;
use super::event_loop::EventLoopWindowTarget; use super::event_loop::EventLoopWindowTarget;
@@ -23,6 +24,30 @@ impl<T> EventLoopWindowTarget<T> {
// There's no primary monitor on Wayland. // There's no primary monitor on Wayland.
None 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<i32> {
self.exit.get()
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

@@ -66,14 +66,14 @@ use self::{
event_processor::EventProcessor, event_processor::EventProcessor,
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender}, ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
}; };
use super::{common::xkb_state::KbdState, OsError}; use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{ use crate::{
error::{EventLoopError, OsError as RootOsError}, error::{EventLoopError, OsError as RootOsError},
event::{Event, StartCause, WindowEvent}, event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW}, event_loop::{DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform::pump_events::PumpStatus, platform::pump_events::PumpStatus,
platform_impl::{ platform_impl::{
platform::{min_timeout, sticky_exit_callback, WindowId}, platform::{min_timeout, WindowId},
PlatformSpecificWindowBuilderAttributes, PlatformSpecificWindowBuilderAttributes,
}, },
window::WindowAttributes, window::WindowAttributes,
@@ -148,6 +148,8 @@ pub struct EventLoopWindowTarget<T> {
wm_delete_window: xproto::Atom, wm_delete_window: xproto::Atom,
net_wm_ping: xproto::Atom, net_wm_ping: xproto::Atom,
ime_sender: ImeSender, ime_sender: ImeSender,
control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>,
root: xproto::Window, root: xproto::Window,
ime: RefCell<Ime>, ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
@@ -159,7 +161,6 @@ pub struct EventLoopWindowTarget<T> {
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
loop_running: bool, loop_running: bool,
control_flow: ControlFlow,
event_loop: Loop<'static, EventLoopState>, event_loop: Loop<'static, EventLoopState>,
waker: calloop::ping::Ping, waker: calloop::ping::Ping,
event_processor: EventProcessor<T>, event_processor: EventProcessor<T>,
@@ -303,6 +304,8 @@ impl<T: 'static> EventLoop<T> {
let window_target = EventLoopWindowTarget { let window_target = EventLoopWindowTarget {
ime, ime,
root, root,
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(None),
windows: Default::default(), windows: Default::default(),
_marker: ::std::marker::PhantomData, _marker: ::std::marker::PhantomData,
ime_sender, ime_sender,
@@ -368,7 +371,6 @@ impl<T: 'static> EventLoop<T> {
EventLoop { EventLoop {
loop_running: false, loop_running: false,
control_flow: ControlFlow::default(),
event_loop, event_loop,
waker, waker,
event_processor, event_processor,
@@ -398,7 +400,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
if self.loop_running { if self.loop_running {
return Err(EventLoopError::AlreadyRunning); return Err(EventLoopError::AlreadyRunning);
@@ -432,7 +434,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
if !self.loop_running { if !self.loop_running {
self.loop_running = true; self.loop_running = true;
@@ -440,7 +442,7 @@ impl<T: 'static> EventLoop<T> {
// Reset the internal state for the loop as we start running to // Reset the internal state for the loop as we start running to
// ensure consistent behaviour in case the loop runs and exits more // ensure consistent behaviour in case the loop runs and exits more
// than once. // than once.
self.control_flow = ControlFlow::Poll; self.set_control_flow(ControlFlow::Poll);
// run the initial loop iteration // run the initial loop iteration
self.single_iteration(&mut callback, StartCause::Init); self.single_iteration(&mut callback, StartCause::Init);
@@ -448,19 +450,13 @@ impl<T: 'static> EventLoop<T> {
// Consider the possibility that the `StartCause::Init` iteration could // Consider the possibility that the `StartCause::Init` iteration could
// request to Exit. // request to Exit.
if !matches!(self.control_flow, ControlFlow::ExitWithCode(_)) { if !self.exiting() {
self.poll_events_with_timeout(timeout, &mut callback); 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; self.loop_running = false;
let mut dummy = self.control_flow; callback(Event::LoopExiting, self.window_target());
sticky_exit_callback(
Event::LoopExiting,
self.window_target(),
&mut dummy,
&mut callback,
);
PumpStatus::Exit(code) PumpStatus::Exit(code)
} else { } else {
@@ -476,7 +472,7 @@ impl<T: 'static> EventLoop<T> {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F) pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
let start = Instant::now(); let start = Instant::now();
@@ -486,17 +482,12 @@ impl<T: 'static> EventLoop<T> {
// If we already have work to do then we don't want to block on the next poll. // If we already have work to do then we don't want to block on the next poll.
Some(Duration::ZERO) Some(Duration::ZERO)
} else { } else {
let control_flow_timeout = match self.control_flow { let control_flow_timeout = match self.control_flow() {
ControlFlow::Wait => None, ControlFlow::Wait => None,
ControlFlow::Poll => Some(Duration::ZERO), ControlFlow::Poll => Some(Duration::ZERO),
ControlFlow::WaitUntil(wait_deadline) => { ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start)) 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) min_timeout(control_flow_timeout, timeout)
@@ -510,7 +501,7 @@ impl<T: 'static> EventLoop<T> {
{ {
log::error!("Failed to poll for events: {error:?}"); log::error!("Failed to poll for events: {error:?}");
let exit_code = error.raw_os_error().unwrap_or(1); let exit_code = error.raw_os_error().unwrap_or(1);
self.control_flow = ControlFlow::ExitWithCode(exit_code); self.set_exit_code(exit_code);
return; return;
} }
@@ -528,7 +519,7 @@ impl<T: 'static> EventLoop<T> {
// NB: `StartCause::Init` is handled as a special case and doesn't need // NB: `StartCause::Init` is handled as a special case and doesn't need
// to be considered here // to be considered here
let cause = match self.control_flow { let cause = match self.control_flow() {
ControlFlow::Poll => StartCause::Poll, ControlFlow::Poll => StartCause::Poll,
ControlFlow::Wait => StartCause::WaitCancelled { ControlFlow::Wait => StartCause::WaitCancelled {
start, start,
@@ -547,11 +538,6 @@ impl<T: 'static> EventLoop<T> {
} }
} }
} }
// 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); self.single_iteration(&mut callback, cause);
@@ -559,30 +545,18 @@ impl<T: 'static> EventLoop<T> {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause) fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
let mut control_flow = self.control_flow; callback(crate::event::Event::NewEvents(cause), &self.target);
sticky_exit_callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
callback,
);
// NB: For consistency all platforms must emit a 'resumed' event even though X11 // NB: For consistency all platforms must emit a 'resumed' event even though X11
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
if cause == StartCause::Init { if cause == StartCause::Init {
sticky_exit_callback( callback(crate::event::Event::Resumed, &self.target);
crate::event::Event::Resumed,
&self.target,
&mut control_flow,
callback,
);
} }
// Process all pending events // Process all pending events
self.drain_events(callback, &mut control_flow); self.drain_events(callback);
// Empty activation tokens. // Empty activation tokens.
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() { while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
@@ -593,7 +567,7 @@ impl<T: 'static> EventLoop<T> {
}); });
match token { match token {
Some(Ok(token)) => sticky_exit_callback( Some(Ok(token)) => callback(
crate::event::Event::WindowEvent { crate::event::Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: crate::event::WindowEvent::ActivationTokenDone { event: crate::event::WindowEvent::ActivationTokenDone {
@@ -602,8 +576,6 @@ impl<T: 'static> EventLoop<T> {
}, },
}, },
&self.target, &self.target,
&mut control_flow,
callback,
), ),
Some(Err(e)) => { Some(Err(e)) => {
log::error!("Failed to get activation token: {}", e); log::error!("Failed to get activation token: {}", e);
@@ -615,12 +587,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer // Empty the user event buffer
{ {
while let Ok(event) = self.user_receiver.try_recv() { while let Ok(event) = self.user_receiver.try_recv() {
sticky_exit_callback( callback(crate::event::Event::UserEvent(event), &self.target);
crate::event::Event::UserEvent(event),
&self.target,
&mut control_flow,
callback,
);
} }
} }
@@ -634,34 +601,25 @@ impl<T: 'static> EventLoop<T> {
for window_id in windows { for window_id in windows {
let window_id = crate::window::WindowId(window_id); let window_id = crate::window::WindowId(window_id);
sticky_exit_callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id, window_id,
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, },
&self.target, &self.target,
&mut control_flow,
callback,
); );
} }
} }
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
{ {
sticky_exit_callback( callback(crate::event::Event::AboutToWait, &self.target);
crate::event::Event::AboutToWait,
&self.target,
&mut control_flow,
callback,
);
} }
self.control_flow = control_flow;
} }
fn drain_events<F>(&mut self, callback: &mut F, control_flow: &mut ControlFlow) fn drain_events<F>(&mut self, callback: &mut F)
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
let target = &self.target; let target = &self.target;
let mut xev = MaybeUninit::uninit(); let mut xev = MaybeUninit::uninit();
@@ -670,25 +628,38 @@ impl<T: 'static> EventLoop<T> {
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() }; let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |event| { self.event_processor.process_event(&mut xev, |event| {
sticky_exit_callback( if let Event::WindowEvent {
event, window_id: crate::window::WindowId(wid),
target, event: WindowEvent::RedrawRequested,
control_flow, } = event
&mut |event, window_target, control_flow| { {
if let Event::WindowEvent { wt.redraw_sender.send(wid).unwrap();
window_id: crate::window::WindowId(wid), } else {
event: WindowEvent::RedrawRequested, callback(event, target);
} = event }
{
wt.redraw_sender.send(wid).unwrap();
} else {
callback(event, window_target, control_flow);
}
},
);
}); });
} }
} }
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<i32> {
self.target.p.exit_code()
}
} }
pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> { pub(crate) fn get_xtarget<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
@@ -743,6 +714,30 @@ impl<T> EventLoopWindowTarget<T> {
display_handle.screen = self.xconn.default_screen_index() as c_int; display_handle.screen = self.xconn.default_screen_index() as c_int;
RawDisplayHandle::Xlib(display_handle) 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<i32> {
self.exit.get()
}
} }
impl<T: 'static> EventLoopProxy<T> { impl<T: 'static> EventLoopProxy<T> {

View File

@@ -56,7 +56,7 @@ declare_class!(
fn will_terminate(&self, _sender: Option<&AnyObject>) { fn will_terminate(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationWillTerminate:"); trace_scope!("applicationWillTerminate:");
// TODO: Notify every window that it will be destroyed, like done in iOS? // TODO: Notify every window that it will be destroyed, like done in iOS?
AppState::exit(); AppState::internal_exit();
} }
} }
); );

View File

@@ -40,11 +40,11 @@ impl<Never> Event<Never> {
pub trait EventHandler: Debug { pub trait EventHandler: Debug {
// Not sure probably it should accept Event<'static, Never> // Not sure probably it should accept Event<'static, Never>
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow); fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow); fn handle_user_events(&mut self);
} }
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>; pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>;
struct EventLoopHandler<T: 'static> { struct EventLoopHandler<T: 'static> {
callback: Weak<Callback<T>>, callback: Weak<Callback<T>>,
@@ -55,10 +55,7 @@ struct EventLoopHandler<T: 'static> {
impl<T> EventLoopHandler<T> { impl<T> EventLoopHandler<T> {
fn with_callback<F>(&mut self, f: F) fn with_callback<F>(&mut self, f: F)
where where
F: FnOnce( F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
&mut EventLoopHandler<T>,
RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
),
{ {
// The `NSApp` and our `HANDLER` are global state and so it's possible that // 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 // we could get a delegate callback after the application has exit an
@@ -85,28 +82,16 @@ impl<T> Debug for EventLoopHandler<T> {
} }
impl<T> EventHandler for EventLoopHandler<T> { impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) { fn handle_nonuser_event(&mut self, event: Event<Never>) {
self.with_callback(|this, mut callback| { self.with_callback(|this, mut callback| {
if let ControlFlow::ExitWithCode(code) = *control_flow { (callback)(event.userify(), &this.window_target);
// 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);
}
}); });
} }
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) { fn handle_user_events(&mut self) {
self.with_callback(|this, mut callback| { self.with_callback(|this, mut callback| {
for event in this.receiver.try_iter() { for event in this.receiver.try_iter() {
if let ControlFlow::ExitWithCode(code) = *control_flow { (callback)(Event::UserEvent(event), &this.window_target);
// 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);
}
} }
}); });
} }
@@ -132,6 +117,7 @@ struct Handler {
running: AtomicBool, running: AtomicBool,
in_callback: AtomicBool, in_callback: AtomicBool,
control_flow: Mutex<ControlFlow>, control_flow: Mutex<ControlFlow>,
exit: AtomicBool,
start_time: Mutex<Option<Instant>>, start_time: Mutex<Option<Instant>>,
callback: Mutex<Option<Box<dyn EventHandler>>>, callback: Mutex<Option<Box<dyn EventHandler>>>,
pending_events: Mutex<VecDeque<EventWrapper>>, pending_events: Mutex<VecDeque<EventWrapper>>,
@@ -189,13 +175,6 @@ impl Handler {
self.running.store(true, Ordering::Relaxed); 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 /// 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 /// Since an `EventLoop` may be run more than once we need make sure to reset the
@@ -206,7 +185,7 @@ impl Handler {
/// ///
/// # Caveat /// # Caveat
/// This is only intended to be called from the main thread /// 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 // Relaxed ordering because we don't actually have multiple threads involved, we just want
// interiour mutability // interiour mutability
// //
@@ -227,6 +206,14 @@ impl Handler {
self.set_wait_timeout(None); 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) { pub fn request_stop_app_on_launch(&self) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want // Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability // interior mutability
@@ -287,6 +274,10 @@ impl Handler {
self.stop_app_on_redraw.load(Ordering::Relaxed) 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 { fn control_flow(&self) -> ControlFlow {
*self.control_flow.lock().unwrap() *self.control_flow.lock().unwrap()
} }
@@ -321,13 +312,13 @@ impl Handler {
fn handle_nonuser_event(&self, event: Event<Never>) { fn handle_nonuser_event(&self, event: Event<Never>) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() { 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) { fn handle_user_events(&self) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() { 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(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);
@@ -418,21 +409,28 @@ impl AppState {
HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw); 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 { pub fn control_flow() -> ControlFlow {
HANDLER.control_flow() HANDLER.control_flow()
} }
pub fn exit() -> i32 { pub fn internal_exit() {
HANDLER.set_in_callback(true); HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::LoopExiting); HANDLER.handle_nonuser_event(Event::LoopExiting);
HANDLER.set_in_callback(false); HANDLER.set_in_callback(false);
HANDLER.exit(); HANDLER.internal_exit();
Self::clear_callback(); Self::clear_callback();
if let ControlFlow::ExitWithCode(code) = HANDLER.control_flow() { }
code
} else { pub fn exit() {
0 HANDLER.exit()
} }
pub fn exiting() -> bool {
HANDLER.exiting()
} }
pub fn dispatch_init_events() { 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.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause)); HANDLER.handle_nonuser_event(Event::NewEvents(cause));
@@ -643,7 +640,7 @@ impl AppState {
HANDLER.handle_nonuser_event(Event::AboutToWait); HANDLER.handle_nonuser_event(Event::AboutToWait);
HANDLER.set_in_callback(false); HANDLER.set_in_callback(false);
if HANDLER.should_exit() { if HANDLER.exiting() {
Self::stop(); Self::stop();
} }
@@ -654,7 +651,7 @@ impl AppState {
let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events
let app_timeout = match HANDLER.control_flow() { let app_timeout = match HANDLER.control_flow() {
ControlFlow::Wait => None, ControlFlow::Wait => None,
ControlFlow::Poll | ControlFlow::ExitWithCode(_) => Some(Instant::now()), ControlFlow::Poll => Some(Instant::now()),
ControlFlow::WaitUntil(instant) => Some(instant), ControlFlow::WaitUntil(instant) => Some(instant),
}; };
HANDLER HANDLER

View File

@@ -93,6 +93,22 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle { pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) 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<T> EventLoopWindowTarget<T> { impl<T> EventLoopWindowTarget<T> {
@@ -213,7 +229,7 @@ impl<T> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootWindowTarget<T>),
{ {
self.run_ondemand(callback) self.run_ondemand(callback)
} }
@@ -224,7 +240,7 @@ impl<T> EventLoop<T> {
// redundant wake ups. // redundant wake ups.
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootWindowTarget<T>),
{ {
if AppState::is_running() { if AppState::is_running() {
return Err(EventLoopError::AlreadyRunning); return Err(EventLoopError::AlreadyRunning);
@@ -241,14 +257,14 @@ impl<T> EventLoop<T> {
let callback = unsafe { let callback = unsafe {
mem::transmute::< mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>, Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>, Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
>(Rc::new(RefCell::new(callback))) >(Rc::new(RefCell::new(callback)))
}; };
self._callback = Some(Rc::clone(&callback)); self._callback = Some(Rc::clone(&callback));
let exit_code = autoreleasepool(|_| { autoreleasepool(|_| {
// A bit of juggling with the callback references to make sure // A bit of juggling with the callback references to make sure
// that `self.callback` is the only owner of the callback. // that `self.callback` is the only owner of the callback.
let weak_cb: Weak<_> = Rc::downgrade(&callback); let weak_cb: Weak<_> = Rc::downgrade(&callback);
@@ -288,7 +304,7 @@ impl<T> EventLoop<T> {
resume_unwind(panic); resume_unwind(panic);
} }
AppState::exit() AppState::internal_exit()
})); }));
// # Safety // # Safety
@@ -298,22 +314,17 @@ impl<T> EventLoop<T> {
drop(self._callback.take()); drop(self._callback.take());
AppState::clear_callback(); AppState::clear_callback();
match catch_result { if let Err(payload) = catch_result {
Ok(exit_code) => exit_code, resume_unwind(payload)
Err(payload) => resume_unwind(payload),
} }
}); });
if exit_code == 0 { Ok(())
Ok(())
} else {
Err(EventLoopError::ExitFailure(exit_code))
}
} }
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootWindowTarget<T>),
{ {
// # Safety // # Safety
// We are erasing the lifetime of the application callback here so that we // We are erasing the lifetime of the application callback here so that we
@@ -326,8 +337,8 @@ impl<T> EventLoop<T> {
let callback = unsafe { let callback = unsafe {
mem::transmute::< mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>, Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>, Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
>(Rc::new(RefCell::new(callback))) >(Rc::new(RefCell::new(callback)))
}; };
@@ -407,9 +418,9 @@ impl<T> EventLoop<T> {
resume_unwind(panic); resume_unwind(panic);
} }
if let ControlFlow::ExitWithCode(code) = AppState::control_flow() { if AppState::exiting() {
AppState::exit(); AppState::internal_exit();
PumpStatus::Exit(code) PumpStatus::Exit(0)
} else { } else {
PumpStatus::Continue PumpStatus::Continue
} }

View File

@@ -1,4 +1,5 @@
use std::{ use std::{
cell::Cell,
collections::VecDeque, collections::VecDeque,
marker::PhantomData, marker::PhantomData,
mem, slice, mem, slice,
@@ -301,6 +302,8 @@ impl<T: 'static> EventLoop<T> {
windows: Vec::new(), windows: Vec::new(),
window_target: event_loop::EventLoopWindowTarget { window_target: event_loop::EventLoopWindowTarget {
p: EventLoopWindowTarget { p: EventLoopWindowTarget {
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(false),
creates: Mutex::new(VecDeque::new()), creates: Mutex::new(VecDeque::new()),
redraws: Arc::new(Mutex::new(VecDeque::new())), redraws: Arc::new(Mutex::new(VecDeque::new())),
destroys: Arc::new(Mutex::new(VecDeque::new())), destroys: Arc::new(Mutex::new(VecDeque::new())),
@@ -458,40 +461,20 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, mut event_handler_inner: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, mut event_handler_inner: F) -> Result<(), EventLoopError>
where where
F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(event::Event<T>, &event_loop::EventLoopWindowTarget<T>),
{ {
// Wrapper for event handler function that prevents ExitWithCode from being unset.
let mut event_handler = let mut event_handler =
move |event: event::Event<T>, move |event: event::Event<T>, window_target: &event_loop::EventLoopWindowTarget<T>| {
window_target: &event_loop::EventLoopWindowTarget<T>, event_handler_inner(event, window_target);
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);
}
}; };
let mut control_flow = ControlFlow::default();
let mut start_cause = StartCause::Init; let mut start_cause = StartCause::Init;
let code = loop { loop {
event_handler( event_handler(event::Event::NewEvents(start_cause), &self.window_target);
event::Event::NewEvents(start_cause),
&self.window_target,
&mut control_flow,
);
if start_cause == StartCause::Init { if start_cause == StartCause::Init {
event_handler( event_handler(event::Event::Resumed, &self.window_target);
event::Event::Resumed,
&self.window_target,
&mut control_flow,
);
} }
// Handle window creates. // Handle window creates.
@@ -516,7 +499,6 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::Resized((properties.w, properties.h).into()), event: event::WindowEvent::Resized((properties.w, properties.h).into()),
}, },
&self.window_target, &self.window_target,
&mut control_flow,
); );
// Send resize event on create to indicate first position. // Send resize event on create to indicate first position.
@@ -526,7 +508,6 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::Moved((properties.x, properties.y).into()), event: event::WindowEvent::Moved((properties.x, properties.y).into()),
}, },
&self.window_target, &self.window_target,
&mut control_flow,
); );
} }
@@ -541,7 +522,6 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::Destroyed, event: event::WindowEvent::Destroyed,
}, },
&self.window_target, &self.window_target,
&mut control_flow,
); );
self.windows self.windows
@@ -572,7 +552,7 @@ impl<T: 'static> EventLoop<T> {
window_id, window_id,
orbital_event.to_option(), orbital_event.to_option(),
event_state, event_state,
|event| event_handler(event, &self.window_target, &mut control_flow), |event| event_handler(event, &self.window_target),
); );
} }
@@ -599,11 +579,7 @@ impl<T: 'static> EventLoop<T> {
} }
while let Ok(event) = self.user_events_receiver.try_recv() { while let Ok(event) = self.user_events_receiver.try_recv() {
event_handler( event_handler(event::Event::UserEvent(event), &self.window_target);
event::Event::UserEvent(event),
&self.window_target,
&mut control_flow,
);
} }
// To avoid deadlocks the redraws lock is not held during event processing. // To avoid deadlocks the redraws lock is not held during event processing.
@@ -617,24 +593,22 @@ impl<T: 'static> EventLoop<T> {
event: event::WindowEvent::RedrawRequested, event: event::WindowEvent::RedrawRequested,
}, },
&self.window_target, &self.window_target,
&mut control_flow,
); );
} }
event_handler( event_handler(event::Event::AboutToWait, &self.window_target);
event::Event::AboutToWait,
&self.window_target,
&mut control_flow,
);
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 => { ControlFlow::Poll => {
start_cause = StartCause::Poll; start_cause = StartCause::Poll;
continue; continue;
} }
ControlFlow::Wait => None, ControlFlow::Wait => None,
ControlFlow::WaitUntil(instant) => Some(instant), ControlFlow::WaitUntil(instant) => Some(instant),
ControlFlow::ExitWithCode(code) => break code,
}; };
// Re-using wake socket caused extra wake events before because there were leftover // Re-using wake socket caused extra wake events before because there were leftover
@@ -690,19 +664,11 @@ impl<T: 'static> EventLoop<T> {
}; };
} }
} }
};
event_handler(
event::Event::LoopExiting,
&self.window_target,
&mut control_flow,
);
if code == 0 {
Ok(())
} else {
Err(EventLoopError::ExitFailure(code))
} }
event_handler(event::Event::LoopExiting, &self.window_target);
Ok(())
} }
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> { pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
@@ -746,6 +712,8 @@ impl<T> Clone for EventLoopProxy<T> {
impl<T> Unpin for EventLoopProxy<T> {} impl<T> Unpin for EventLoopProxy<T> {}
pub struct EventLoopWindowTarget<T: 'static> { pub struct EventLoopWindowTarget<T: 'static> {
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
pub(super) creates: Mutex<VecDeque<Arc<RedoxSocket>>>, pub(super) creates: Mutex<VecDeque<Arc<RedoxSocket>>>,
pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>, pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>, pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
@@ -771,4 +739,20 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle { pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty()) 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()
}
} }

View File

@@ -3,7 +3,7 @@ use std::sync::mpsc::{self, Receiver, Sender};
use crate::error::EventLoopError; use crate::error::EventLoopError;
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget}; use crate::event_loop::EventLoopWindowTarget as RootEventLoopWindowTarget;
use super::{backend, device, window}; use super::{backend, device, window};
@@ -39,7 +39,7 @@ impl<T> EventLoop<T> {
pub fn run<F>(self, mut event_handler: F) -> ! pub fn run<F>(self, mut event_handler: F) -> !
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
let target = RootEventLoopWindowTarget { let target = RootEventLoopWindowTarget {
p: self.elw.p.clone(), p: self.elw.p.clone(),
@@ -47,7 +47,7 @@ impl<T> EventLoop<T> {
}; };
// SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`. // SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`.
let handler: Box<dyn FnMut(Event<()>, _)> = Box::new(|event, flow| { let handler: Box<dyn FnMut(Event<()>)> = Box::new(|event| {
let event = match event.map_nonuser_event() { let event = match event.map_nonuser_event() {
Ok(event) => event, Ok(event) => event,
Err(Event::UserEvent(())) => Event::UserEvent( Err(Event::UserEvent(())) => Event::UserEvent(
@@ -57,7 +57,7 @@ impl<T> EventLoop<T> {
), ),
Err(_) => unreachable!(), Err(_) => unreachable!(),
}; };
event_handler(event, &target, flow) event_handler(event, &target)
}); });
// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe // 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 // because this function will never return and all resources not cleaned up by the point we
@@ -76,7 +76,7 @@ impl<T> EventLoop<T> {
pub fn spawn<F>(self, mut event_handler: F) pub fn spawn<F>(self, mut event_handler: F)
where where
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow), F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>),
{ {
let target = RootEventLoopWindowTarget { let target = RootEventLoopWindowTarget {
p: self.elw.p.clone(), p: self.elw.p.clone(),
@@ -84,7 +84,7 @@ impl<T> EventLoop<T> {
}; };
self.elw.p.run( self.elw.p.run(
Box::new(move |event, flow| { Box::new(move |event| {
let event = match event.map_nonuser_event() { let event = match event.map_nonuser_event() {
Ok(event) => event, Ok(event) => event,
Err(Event::UserEvent(())) => Event::UserEvent( Err(Event::UserEvent(())) => Event::UserEvent(
@@ -94,7 +94,7 @@ impl<T> EventLoop<T> {
), ),
Err(_) => unreachable!(), Err(_) => unreachable!(),
}; };
event_handler(event, &target, flow) event_handler(event, &target)
}), }),
true, true,
); );

View File

@@ -24,7 +24,7 @@ use web_time::{Duration, Instant};
pub struct Shared(Rc<Execution>); pub struct Shared(Rc<Execution>);
pub(super) type EventHandler = dyn FnMut(Event<()>, &mut ControlFlow); pub(super) type EventHandler = dyn FnMut(Event<()>);
impl Clone for Shared { impl Clone for Shared {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@@ -35,6 +35,8 @@ impl Clone for Shared {
type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>; type OnEventHandle<T> = RefCell<Option<EventListenerHandle<dyn FnMut(T)>>>;
pub struct Execution { pub struct Execution {
control_flow: Cell<ControlFlow>,
exit: Cell<bool>,
runner: RefCell<RunnerEnum>, runner: RefCell<RunnerEnum>,
suspended: Cell<bool>, suspended: Cell<bool>,
event_loop_recreation: Cell<bool>, event_loop_recreation: Cell<bool>,
@@ -108,16 +110,9 @@ impl Runner {
}) })
} }
fn handle_single_event( fn handle_single_event(&mut self, runner: &Shared, event: impl Into<EventWrapper>) {
&mut self,
runner: &Shared,
event: impl Into<EventWrapper>,
control: &mut ControlFlow,
) {
let is_closed = matches!(*control, ControlFlow::ExitWithCode(_));
match event.into() { match event.into() {
EventWrapper::Event(event) => (self.event_handler)(event, control), EventWrapper::Event(event) => (self.event_handler)(event),
EventWrapper::ScaleChange { EventWrapper::ScaleChange {
canvas, canvas,
size, size,
@@ -126,18 +121,13 @@ impl Runner {
if let Some(canvas) = canvas.upgrade() { if let Some(canvas) = canvas.upgrade() {
canvas.borrow().handle_scale_change( canvas.borrow().handle_scale_change(
runner, runner,
|event| (self.event_handler)(event, control), |event| (self.event_handler)(event),
size, size,
scale, 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"); let document = window.document().expect("Failed to obtain document");
Shared(Rc::new(Execution { Shared(Rc::new(Execution {
control_flow: Cell::new(ControlFlow::default()),
exit: Cell::new(false),
runner: RefCell::new(RunnerEnum::Pending), runner: RefCell::new(RunnerEnum::Pending),
suspended: Cell::new(false), suspended: Cell::new(false),
event_loop_recreation: 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 // Process the destroy-pending windows. This should only be called from
// `run_until_cleared`, somewhere between emitting `NewEvents` and `AboutToWait`. // `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() { while let Some(id) = self.0.destroy_pending.borrow_mut().pop_front() {
self.0 self.0
.all_canvases .all_canvases
.borrow_mut() .borrow_mut()
.retain(|&(item_id, _)| item_id != id); .retain(|&(item_id, _)| item_id != id);
self.handle_event( self.handle_event(Event::WindowEvent {
Event::WindowEvent { window_id: id,
window_id: id, event: crate::event::WindowEvent::Destroyed,
event: crate::event::WindowEvent::Destroyed, });
},
control,
);
self.0.redraw_pending.borrow_mut().remove(&id); 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 // This will also process any events that have been queued or that are queued during processing
fn run_until_cleared<E: Into<EventWrapper>>(&self, events: impl Iterator<Item = E>) { fn run_until_cleared<E: Into<EventWrapper>>(&self, events: impl Iterator<Item = E>) {
let mut control = self.current_control_flow();
for event in events { 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 // Collect all of the redraw events to avoid double-locking the RefCell
let redraw_events: Vec<WindowId> = self.0.redraw_pending.borrow_mut().drain().collect(); let redraw_events: Vec<WindowId> = self.0.redraw_pending.borrow_mut().drain().collect();
for window_id in redraw_events { for window_id in redraw_events {
self.handle_event( self.handle_event(Event::WindowEvent {
Event::WindowEvent { window_id,
window_id, event: WindowEvent::RedrawRequested,
event: WindowEvent::RedrawRequested, });
},
&mut control,
);
} }
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 // If the event loop is closed, it has been closed this iteration and now the closing
// event should be emitted // event should be emitted
if self.is_closed() { if self.is_closed() {
self.handle_loop_destroyed(&mut control); self.handle_loop_destroyed();
} }
} }
fn handle_unload(&self) { fn handle_unload(&self) {
self.apply_control_flow(ControlFlow::Exit); self.exit();
let mut control = self.current_control_flow(); self.apply_control_flow();
// We don't call `handle_loop_destroyed` here because we don't need to // 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. // 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 // handle_event takes in events and either queues them or applies a callback
// //
// It should only ever be called from `run_until_cleared`. // It should only ever be called from `run_until_cleared`.
fn handle_event(&self, event: impl Into<EventWrapper>, control: &mut ControlFlow) { fn handle_event(&self, event: impl Into<EventWrapper>) {
if self.is_closed() { if self.is_closed() {
*control = ControlFlow::Exit; self.exit();
} }
match *self.0.runner.borrow_mut() { match *self.0.runner.borrow_mut() {
RunnerEnum::Running(ref mut runner) => { 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 // If an event is being handled without a runner somehow, add it to the event queue so
// it will eventually be processed // it will eventually be processed
@@ -625,7 +610,7 @@ impl Shared {
RunnerEnum::Destroyed => return, 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 // 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 // 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 // Make sure not to let the borrow_mut live during the next handle_event
let event = { self.0.events.borrow_mut().pop_front() }; let event = { self.0.events.borrow_mut().pop_front() };
if let Some(event) = event { 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 // Apply the new ControlFlow that has been selected by the user
// Start any necessary timeouts etc // Start any necessary timeouts etc
fn apply_control_flow(&self, control_flow: ControlFlow) { fn apply_control_flow(&self) {
let new_state = match control_flow { let new_state = if self.exiting() {
ControlFlow::Poll => { State::Exit
let cloned = self.clone(); } else {
State::Poll { match self.control_flow() {
request: backend::Schedule::new( ControlFlow::Poll => {
self.window().clone(), let cloned = self.clone();
move || cloned.poll(), State::Poll {
None, 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();
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::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),
),
}
}
ControlFlow::ExitWithCode(_) => State::Exit,
}; };
if let RunnerEnum::Running(ref mut runner) = *self.0.runner.borrow_mut() { 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) { fn handle_loop_destroyed(&self) {
self.handle_event(Event::LoopExiting, control); self.handle_event(Event::LoopExiting);
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.page_transition_event_handle.borrow_mut() = None; *self.0.page_transition_event_handle.borrow_mut() = None;
*self.0.on_mouse_move.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 // Check if the event loop is currently closed
fn is_closed(&self) -> bool { fn is_closed(&self) -> bool {
match self.0.runner.try_borrow().as_ref().map(Deref::deref) { 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. // The event loop is not closed since it is not initialized.
Ok(RunnerEnum::Pending) => false, Ok(RunnerEnum::Pending) => false,
// The event loop is closed since it has been destroyed. // 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) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.0.device_events.set(allowed) self.0.device_events.set(allowed)
} }
@@ -774,6 +753,22 @@ impl Shared {
pub fn event_loop_recreation(&self, allow: bool) { pub fn event_loop_recreation(&self, allow: bool) {
self.0.event_loop_recreation.set(allow) 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 { pub(crate) enum EventWrapper {

View File

@@ -1,5 +1,4 @@
use super::backend; use super::backend;
use crate::event_loop::ControlFlow;
use web_time::Instant; use web_time::Instant;
@@ -21,17 +20,7 @@ pub enum State {
} }
impl State { impl State {
pub fn is_exit(&self) -> bool { pub fn exiting(&self) -> bool {
matches!(self, State::Exit) 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,
}
}
} }

View File

@@ -19,7 +19,7 @@ use super::{
use crate::event::{ use crate::event::{
DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent, 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::keyboard::ModifiersState;
use crate::window::{Theme, WindowId as RootWindowId}; use crate::window::{Theme, WindowId as RootWindowId};
@@ -678,4 +678,20 @@ impl<T> EventLoopWindowTarget<T> {
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
self.runner.listen_device_events(allowed) 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()
}
} }

View File

@@ -250,14 +250,14 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError> pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
self.run_ondemand(event_handler) self.run_ondemand(event_handler)
} }
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
{ {
let runner = &self.window_target.p.runner_shared; let runner = &self.window_target.p.runner_shared;
@@ -270,18 +270,20 @@ impl<T: 'static> EventLoop<T> {
// We make sure to call runner.clear_event_handler() before // We make sure to call runner.clear_event_handler() before
// returning // returning
unsafe { unsafe {
runner.set_event_handler(move |event, control_flow| { runner.set_event_handler(move |event| event_handler(event, event_loop_windows_ref));
event_handler(event, event_loop_windows_ref, control_flow)
});
} }
} }
let exit_code = loop { 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; break code;
} }
if let ControlFlow::ExitWithCode(code) = self.dispatch_peeked_messages() { self.dispatch_peeked_messages();
if let Some(code) = self.exit_code() {
break code; break code;
} }
}; };
@@ -303,7 +305,7 @@ impl<T: 'static> EventLoop<T> {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus
where where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow), F: FnMut(Event<T>, &RootELW<T>),
{ {
{ {
let runner = &self.window_target.p.runner_shared; let runner = &self.window_target.p.runner_shared;
@@ -317,23 +319,20 @@ impl<T: 'static> EventLoop<T> {
// to leave the runner in an unsound state with an associated // to leave the runner in an unsound state with an associated
// event handler. // event handler.
unsafe { unsafe {
runner.set_event_handler(move |event, control_flow| { runner.set_event_handler(move |event| event_handler(event, event_loop_windows_ref));
event_handler(event, event_loop_windows_ref, control_flow)
});
runner.wakeup(); runner.wakeup();
} }
} }
if !matches!( self.wait_and_dispatch_message(timeout);
self.wait_and_dispatch_message(timeout),
ControlFlow::ExitWithCode(_) if self.exit_code().is_none() {
) {
self.dispatch_peeked_messages(); self.dispatch_peeked_messages();
} }
let runner = &self.window_target.p.runner_shared; 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(); runner.loop_destroyed();
// Immediately reset the internal state for the loop to allow // Immediately reset the internal state for the loop to allow
@@ -357,7 +356,7 @@ impl<T: 'static> EventLoop<T> {
} }
/// Wait for one message and dispatch it, optionally with a timeout /// Wait for one message and dispatch it, optionally with a timeout
fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) -> ControlFlow { fn wait_and_dispatch_message(&mut self, timeout: Option<Duration>) {
let start = Instant::now(); let start = Instant::now();
let runner = &self.window_target.p.runner_shared; let runner = &self.window_target.p.runner_shared;
@@ -368,7 +367,6 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::WaitUntil(wait_deadline) => { ControlFlow::WaitUntil(wait_deadline) => {
Some(wait_deadline.saturating_duration_since(start)) Some(wait_deadline.saturating_duration_since(start))
} }
ControlFlow::ExitWithCode(_code) => unreachable!(),
}; };
let timeout = min_timeout(control_flow_timeout, timeout); let timeout = min_timeout(control_flow_timeout, timeout);
@@ -432,8 +430,7 @@ impl<T: 'static> EventLoop<T> {
match msg_status { match msg_status {
None => {} // No MSG to dispatch None => {} // No MSG to dispatch
Some(PumpStatus::Exit(code)) => { Some(PumpStatus::Exit(code)) => {
runner.set_exit_control_flow(code); runner.set_exit_code(code);
return runner.control_flow();
} }
Some(PumpStatus::Continue) => { Some(PumpStatus::Continue) => {
unsafe { unsafe {
@@ -454,12 +451,10 @@ impl<T: 'static> EventLoop<T> {
} }
} }
} }
runner.control_flow()
} }
/// Dispatch all queued messages via `PeekMessageW` /// 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; let runner = &self.window_target.p.runner_shared;
// We generally want to continue dispatching all pending messages // We generally want to continue dispatching all pending messages
@@ -476,8 +471,6 @@ impl<T: 'static> EventLoop<T> {
// is the simplest way avoid unitialized memory in Rust // is the simplest way avoid unitialized memory in Rust
let mut msg = unsafe { mem::zeroed() }; let mut msg = unsafe { mem::zeroed() };
let mut control_flow = runner.control_flow();
loop { loop {
unsafe { unsafe {
if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) == false.into() { if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) == false.into() {
@@ -500,8 +493,7 @@ impl<T: 'static> EventLoop<T> {
panic::resume_unwind(payload); panic::resume_unwind(payload);
} }
control_flow = runner.control_flow(); if let Some(_code) = runner.exit_code() {
if let ControlFlow::ExitWithCode(_code) = control_flow {
break; break;
} }
@@ -509,8 +501,6 @@ impl<T: 'static> EventLoop<T> {
break; break;
} }
} }
control_flow
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
@@ -519,6 +509,10 @@ impl<T: 'static> EventLoop<T> {
event_send: self.thread_msg_sender.clone(), event_send: self.thread_msg_sender.clone(),
} }
} }
fn exit_code(&self) -> Option<i32> {
self.window_target.p.exit_code()
}
} }
impl<T> EventLoopWindowTarget<T> { impl<T> EventLoopWindowTarget<T> {
@@ -547,6 +541,26 @@ impl<T> EventLoopWindowTarget<T> {
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed); 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<i32> {
self.runner_shared.exit_code()
}
} }
/// Returns the id of the main thread. /// Returns the id of the main thread.

View File

@@ -13,7 +13,6 @@ use windows_sys::Win32::Foundation::HWND;
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent}, event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::ControlFlow,
platform_impl::platform::{ platform_impl::platform::{
event_loop::{WindowData, GWL_USERDATA}, event_loop::{WindowData, GWL_USERDATA},
get_window_long, get_window_long,
@@ -21,9 +20,11 @@ use crate::{
window::WindowId, window::WindowId,
}; };
use super::ControlFlow;
pub(crate) type EventLoopRunnerShared<T> = Rc<EventLoopRunner<T>>; pub(crate) type EventLoopRunnerShared<T> = Rc<EventLoopRunner<T>>;
type EventHandler<T> = Cell<Option<Box<dyn FnMut(Event<T>, &mut ControlFlow)>>>; type EventHandler<T> = Cell<Option<Box<dyn FnMut(Event<T>)>>>;
pub(crate) struct EventLoopRunner<T: 'static> { pub(crate) struct EventLoopRunner<T: 'static> {
// The event loop's win32 handles // The event loop's win32 handles
@@ -35,6 +36,7 @@ pub(crate) struct EventLoopRunner<T: 'static> {
pub(super) interrupt_msg_dispatch: Cell<bool>, pub(super) interrupt_msg_dispatch: Cell<bool>,
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>,
runner_state: Cell<RunnerState>, runner_state: Cell<RunnerState>,
last_events_cleared: Cell<Instant>, last_events_cleared: Cell<Instant>,
event_handler: EventHandler<T>, event_handler: EventHandler<T>,
@@ -70,7 +72,8 @@ impl<T> EventLoopRunner<T> {
thread_msg_target, thread_msg_target,
interrupt_msg_dispatch: Cell::new(false), interrupt_msg_dispatch: Cell::new(false),
runner_state: Cell::new(RunnerState::Uninitialized), 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), panic_error: Cell::new(None),
last_events_cleared: Cell::new(Instant::now()), last_events_cleared: Cell::new(Instant::now()),
event_handler: Cell::new(None), event_handler: Cell::new(None),
@@ -91,11 +94,11 @@ impl<T> EventLoopRunner<T> {
/// undefined behaviour. /// undefined behaviour.
pub(crate) unsafe fn set_event_handler<F>(&self, f: F) pub(crate) unsafe fn set_event_handler<F>(&self, f: F)
where where
F: FnMut(Event<T>, &mut ControlFlow), F: FnMut(Event<T>),
{ {
let old_event_handler = self.event_handler.replace(mem::transmute::< let old_event_handler = self.event_handler.replace(mem::transmute::<
Option<Box<dyn FnMut(Event<T>, &mut ControlFlow)>>, Option<Box<dyn FnMut(Event<T>)>>,
Option<Box<dyn FnMut(Event<T>, &mut ControlFlow)>>, Option<Box<dyn FnMut(Event<T>)>>,
>(Some(Box::new(f)))); >(Some(Box::new(f))));
assert!(old_event_handler.is_none()); assert!(old_event_handler.is_none());
} }
@@ -111,6 +114,7 @@ impl<T> EventLoopRunner<T> {
runner_state, runner_state,
panic_error, panic_error,
control_flow, control_flow,
exit,
last_events_cleared: _, last_events_cleared: _,
event_handler, event_handler,
event_buffer: _, event_buffer: _,
@@ -118,7 +122,8 @@ impl<T> EventLoopRunner<T> {
interrupt_msg_dispatch.set(false); interrupt_msg_dispatch.set(false);
runner_state.set(RunnerState::Uninitialized); runner_state.set(RunnerState::Uninitialized);
panic_error.set(None); panic_error.set(None);
control_flow.set(ControlFlow::Poll); control_flow.set(ControlFlow::default());
exit.set(None);
event_handler.set(None); event_handler.set(None);
} }
} }
@@ -141,14 +146,22 @@ impl<T> EventLoopRunner<T> {
self.runner_state.get() self.runner_state.get()
} }
pub fn set_exit_control_flow(&self, code: i32) { pub fn set_control_flow(&self, control_flow: ControlFlow) {
self.control_flow.set(ControlFlow::ExitWithCode(code)) self.control_flow.set(control_flow)
} }
pub fn control_flow(&self) -> ControlFlow { pub fn control_flow(&self) -> ControlFlow {
self.control_flow.get() self.control_flow.get()
} }
pub fn set_exit_code(&self, code: i32) {
self.exit.set(Some(code))
}
pub fn exit_code(&self) -> Option<i32> {
self.exit.get()
}
pub fn should_buffer(&self) -> bool { pub fn should_buffer(&self) -> bool {
let handler = self.event_handler.take(); let handler = self.event_handler.take();
let should_buffer = handler.is_none(); let should_buffer = handler.is_none();
@@ -226,18 +239,12 @@ impl<T> EventLoopRunner<T> {
fn call_event_handler(&self, event: Event<T>) { fn call_event_handler(&self, event: Event<T>) {
self.catch_unwind(|| { self.catch_unwind(|| {
let mut control_flow = self.control_flow.take();
let mut event_handler = self.event_handler.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)"); .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);
event_handler(event, &mut ControlFlow::ExitWithCode(code));
} else {
event_handler(event, &mut control_flow);
}
assert!(self.event_handler.replace(Some(event_handler)).is_none()); assert!(self.event_handler.replace(Some(event_handler)).is_none());
self.control_flow.set(control_flow);
}); });
} }
@@ -332,16 +339,14 @@ impl<T> EventLoopRunner<T> {
} }
fn call_new_events(&self, init: bool) { fn call_new_events(&self, init: bool) {
let start_cause = match (init, self.control_flow()) { let start_cause = match (init, self.control_flow(), self.exit.get()) {
(true, _) => StartCause::Init, (true, _, _) => StartCause::Init,
(false, ControlFlow::Poll) => StartCause::Poll, (false, ControlFlow::Poll, None) => StartCause::Poll,
(false, ControlFlow::ExitWithCode(_)) | (false, ControlFlow::Wait) => { (false, _, Some(_)) | (false, ControlFlow::Wait, None) => StartCause::WaitCancelled {
StartCause::WaitCancelled { requested_resume: None,
requested_resume: None, start: self.last_events_cleared.get(),
start: self.last_events_cleared.get(), },
} (false, ControlFlow::WaitUntil(requested_resume), None) => {
}
(false, ControlFlow::WaitUntil(requested_resume)) => {
if Instant::now() < requested_resume { if Instant::now() < requested_resume {
StartCause::WaitCancelled { StartCause::WaitCancelled {
requested_resume: Some(requested_resume), requested_resume: Some(requested_resume),

View File

@@ -40,21 +40,21 @@ pub use raw_window_handle;
/// ```no_run /// ```no_run
/// use winit::{ /// use winit::{
/// event::{Event, WindowEvent}, /// event::{Event, WindowEvent},
/// event_loop::EventLoop, /// event_loop::{ControlFlow, EventLoop},
/// window::Window, /// window::Window,
/// }; /// };
/// ///
/// let mut event_loop = EventLoop::new().unwrap(); /// let mut event_loop = EventLoop::new().unwrap();
/// let window = Window::new(&event_loop).unwrap(); /// let window = Window::new(&event_loop).unwrap();
/// ///
/// event_loop.run(move |event, _, control_flow| { /// event_loop.run(move |event, elwt| {
/// control_flow.set_wait(); /// elwt.set_control_flow(ControlFlow::Wait);
/// ///
/// match event { /// match event {
/// Event::WindowEvent { /// Event::WindowEvent {
/// event: WindowEvent::CloseRequested, /// event: WindowEvent::CloseRequested,
/// .. /// ..
/// } => control_flow.set_exit(), /// } => elwt.exit(),
/// _ => (), /// _ => (),
/// } /// }
/// }); /// });