Windows: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand
A surprising amount of work was required to enable these extensions on Windows. I had originally assumed that pump_events was going to be very similar to run except would use PeekMessageW instead of GetMessageW to avoid blocking the external loop but I found the Windows backend broke several assumptions I had. Overall I think these changes can hopefully be considered a quite a significant simplification (I think it's a net deletion of a fair amount of code) and I think it also helps bring it into slightly closer alignment with other backends too Key changes: - I have removed the `wait_thread` that was a fairly fiddly way of handling `ControlFlow::WaitUntil` timeouts in favor of using `SetTimer` which works with the same messages picked up by `GetMessage` and `PeekMessage`. - I have removed the ordering guarantees between `MainEventsCleared`, `RedrawRequested` and `RedrawEventsCleared` events due to the complexity in maintaining this artificial ordering, which is already not supported consistently across backends anyway (in particular this ordering already isn't compatible with how MacOS / iOS work). - `RedrawRequested` events are now directly dispatched via `WM_PAINT` messages - comparable to how `RedrawRequested` is dispatched via `drawRect` in the MacOS backend. - I have re-worked how `NewEvents`, `MainEventsCleared`, and `RedrawEventsCleared` get dispatched to be more in line with the MacOS backend and also more in line with how we have recently discussed defining them for all platforms. `NewEvents` is conceptually delivered when the event loop "wakes up" and `MainEventsCleared` gets dispatched when the event loop is about to ask the OS to wait for new events. This is a more portable model, and is already how these events work in the MacOS backend. `RedrawEventsCleared` are just delivered after `MainEventsCleared` but this event no longer has a useful meaning. Probably the most controversial thing here is that this "breaks" the ordering rules for redraw event handling, but since my changes interacted with how the order is maintained I was very reluctant to figure out how to continue maintaining something that we have recently been discussing changing: https://github.com/rust-windowing/winit/issues/2640. Additionally, since the MacOS backend already doesn't strictly maintain this order it's somewhat academic to see this as a breakage if Winit applications can't really rely on it already. This updates the documentation for `request_redraw()` to reflect that we no longer guarantee that `RedrawRequested` events must be dispatched after `MainEventsCleared`.
This commit is contained in:
parent
f5e73b0af4
commit
420840278b
5 changed files with 341 additions and 434 deletions
|
|
@ -1,16 +1,13 @@
|
|||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
collections::{HashSet, VecDeque},
|
||||
mem, panic, ptr,
|
||||
collections::VecDeque,
|
||||
mem, panic,
|
||||
rc::Rc,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use windows_sys::Win32::{
|
||||
Foundation::HWND,
|
||||
Graphics::Gdi::{RedrawWindow, RDW_INTERNALPAINT},
|
||||
};
|
||||
use windows_sys::Win32::Foundation::HWND;
|
||||
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
|
|
@ -30,7 +27,11 @@ type EventHandler<T> = Cell<Option<Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)
|
|||
pub(crate) struct EventLoopRunner<T: 'static> {
|
||||
// The event loop's win32 handles
|
||||
pub(super) thread_msg_target: HWND,
|
||||
wait_thread_id: u32,
|
||||
|
||||
// Setting this will ensure pump_events will return to the external
|
||||
// loop asap. E.g. set after each RedrawRequested to ensure pump_events
|
||||
// can't stall an external loop beyond a frame
|
||||
pub(super) interrupt_msg_dispatch: Cell<bool>,
|
||||
|
||||
control_flow: Cell<ControlFlow>,
|
||||
runner_state: Cell<RunnerState>,
|
||||
|
|
@ -38,8 +39,6 @@ pub(crate) struct EventLoopRunner<T: 'static> {
|
|||
event_handler: EventHandler<T>,
|
||||
event_buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
||||
|
||||
owned_windows: Cell<HashSet<HWND>>,
|
||||
|
||||
panic_error: Cell<Option<PanicError>>,
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +46,7 @@ pub type PanicError = Box<dyn Any + Send + 'static>;
|
|||
|
||||
/// See `move_state_to` function for details on how the state loop works.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum RunnerState {
|
||||
pub(crate) enum RunnerState {
|
||||
/// The event loop has just been created, and an `Init` event must be sent.
|
||||
Uninitialized,
|
||||
/// The event loop is idling.
|
||||
|
|
@ -55,9 +54,6 @@ enum RunnerState {
|
|||
/// The event loop is handling the OS's events and sending them to the user's callback.
|
||||
/// `NewEvents` has been sent, and `MainEventsCleared` hasn't.
|
||||
HandlingMainEvents,
|
||||
/// The event loop is handling the redraw events and sending them to the user's callback.
|
||||
/// `MainEventsCleared` has been sent, and `RedrawEventsCleared` hasn't.
|
||||
HandlingRedrawEvents,
|
||||
/// The event loop has been destroyed. No other events will be emitted.
|
||||
Destroyed,
|
||||
}
|
||||
|
|
@ -68,20 +64,30 @@ enum BufferedEvent<T: 'static> {
|
|||
}
|
||||
|
||||
impl<T> EventLoopRunner<T> {
|
||||
pub(crate) fn new(thread_msg_target: HWND, wait_thread_id: u32) -> EventLoopRunner<T> {
|
||||
pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner<T> {
|
||||
EventLoopRunner {
|
||||
thread_msg_target,
|
||||
wait_thread_id,
|
||||
interrupt_msg_dispatch: Cell::new(false),
|
||||
runner_state: Cell::new(RunnerState::Uninitialized),
|
||||
control_flow: Cell::new(ControlFlow::Poll),
|
||||
panic_error: Cell::new(None),
|
||||
last_events_cleared: Cell::new(Instant::now()),
|
||||
event_handler: Cell::new(None),
|
||||
event_buffer: RefCell::new(VecDeque::new()),
|
||||
owned_windows: Cell::new(HashSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Associate the application's event handler with the runner
|
||||
///
|
||||
/// # Safety
|
||||
/// This is ignoring the lifetime of the application handler (which may not
|
||||
/// outlive the EventLoopRunner) and can lead to undefined behaviour if
|
||||
/// the handler is not cleared before the end of real lifetime.
|
||||
///
|
||||
/// All public APIs that take an event handler (`run`, `run_ondemand`,
|
||||
/// `pump_events`) _must_ pair a call to `set_event_handler` with
|
||||
/// a call to `clear_event_handler` before returning to avoid
|
||||
/// undefined behaviour.
|
||||
pub(crate) unsafe fn set_event_handler<F>(&self, f: F)
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||
|
|
@ -93,18 +99,22 @@ impl<T> EventLoopRunner<T> {
|
|||
assert!(old_event_handler.is_none());
|
||||
}
|
||||
|
||||
pub(crate) fn clear_event_handler(&self) {
|
||||
self.event_handler.set(None);
|
||||
}
|
||||
|
||||
pub(crate) fn reset_runner(&self) {
|
||||
let EventLoopRunner {
|
||||
thread_msg_target: _,
|
||||
wait_thread_id: _,
|
||||
interrupt_msg_dispatch,
|
||||
runner_state,
|
||||
panic_error,
|
||||
control_flow,
|
||||
last_events_cleared: _,
|
||||
event_handler,
|
||||
event_buffer: _,
|
||||
owned_windows: _,
|
||||
} = self;
|
||||
interrupt_msg_dispatch.set(false);
|
||||
runner_state.set(RunnerState::Uninitialized);
|
||||
panic_error.set(None);
|
||||
control_flow.set(ControlFlow::Poll);
|
||||
|
|
@ -114,18 +124,11 @@ impl<T> EventLoopRunner<T> {
|
|||
|
||||
/// State retrieval functions.
|
||||
impl<T> EventLoopRunner<T> {
|
||||
#[allow(unused)]
|
||||
pub fn thread_msg_target(&self) -> HWND {
|
||||
self.thread_msg_target
|
||||
}
|
||||
|
||||
pub fn wait_thread_id(&self) -> u32 {
|
||||
self.wait_thread_id
|
||||
}
|
||||
|
||||
pub fn redrawing(&self) -> bool {
|
||||
self.runner_state.get() == RunnerState::HandlingRedrawEvents
|
||||
}
|
||||
|
||||
pub fn take_panic_error(&self) -> Result<(), PanicError> {
|
||||
match self.panic_error.take() {
|
||||
Some(err) => Err(err),
|
||||
|
|
@ -133,12 +136,16 @@ impl<T> EventLoopRunner<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
pub fn state(&self) -> RunnerState {
|
||||
self.runner_state.get()
|
||||
}
|
||||
|
||||
pub fn handling_events(&self) -> bool {
|
||||
self.runner_state.get() != RunnerState::Idle
|
||||
pub fn set_exit_control_flow(&self, code: i32) {
|
||||
self.control_flow.set(ControlFlow::ExitWithCode(code))
|
||||
}
|
||||
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
pub fn should_buffer(&self) -> bool {
|
||||
|
|
@ -177,42 +184,25 @@ impl<T> EventLoopRunner<T> {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn register_window(&self, window: HWND) {
|
||||
let mut owned_windows = self.owned_windows.take();
|
||||
owned_windows.insert(window);
|
||||
self.owned_windows.set(owned_windows);
|
||||
}
|
||||
|
||||
pub fn remove_window(&self, window: HWND) {
|
||||
let mut owned_windows = self.owned_windows.take();
|
||||
owned_windows.remove(&window);
|
||||
self.owned_windows.set(owned_windows);
|
||||
}
|
||||
|
||||
pub fn owned_windows(&self, mut f: impl FnMut(HWND)) {
|
||||
let mut owned_windows = self.owned_windows.take();
|
||||
for hwnd in &owned_windows {
|
||||
f(*hwnd);
|
||||
}
|
||||
let new_owned_windows = self.owned_windows.take();
|
||||
owned_windows.extend(&new_owned_windows);
|
||||
self.owned_windows.set(owned_windows);
|
||||
}
|
||||
}
|
||||
|
||||
/// Event dispatch functions.
|
||||
impl<T> EventLoopRunner<T> {
|
||||
pub(crate) unsafe fn poll(&self) {
|
||||
pub(crate) fn prepare_wait(&self) {
|
||||
self.move_state_to(RunnerState::Idle);
|
||||
}
|
||||
|
||||
pub(crate) fn wakeup(&self) {
|
||||
self.move_state_to(RunnerState::HandlingMainEvents);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn send_event(&self, event: Event<'_, T>) {
|
||||
pub(crate) fn send_event(&self, event: Event<'_, T>) {
|
||||
if let Event::RedrawRequested(_) = event {
|
||||
if self.runner_state.get() != RunnerState::HandlingRedrawEvents {
|
||||
warn!("RedrawRequested dispatched without explicit MainEventsCleared");
|
||||
self.move_state_to(RunnerState::HandlingRedrawEvents);
|
||||
}
|
||||
self.call_event_handler(event);
|
||||
// As a rule, to ensure that `pump_events` can't block an external event loop
|
||||
// for too long, we always guarantee that `pump_events` will return control to
|
||||
// the external loop asap after a `RedrawRequested` event is dispatched.
|
||||
self.interrupt_msg_dispatch.set(true);
|
||||
} else if self.should_buffer() {
|
||||
// If the runner is already borrowed, we're in the middle of an event loop invocation. Add
|
||||
// the event to a buffer to be processed later.
|
||||
|
|
@ -220,25 +210,16 @@ impl<T> EventLoopRunner<T> {
|
|||
.borrow_mut()
|
||||
.push_back(BufferedEvent::from_event(event))
|
||||
} else {
|
||||
self.move_state_to(RunnerState::HandlingMainEvents);
|
||||
self.call_event_handler(event);
|
||||
self.dispatch_buffered_events();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn main_events_cleared(&self) {
|
||||
self.move_state_to(RunnerState::HandlingRedrawEvents);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn redraw_events_cleared(&self) {
|
||||
self.move_state_to(RunnerState::Idle);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn loop_destroyed(&self) {
|
||||
pub(crate) fn loop_destroyed(&self) {
|
||||
self.move_state_to(RunnerState::Destroyed);
|
||||
}
|
||||
|
||||
unsafe fn call_event_handler(&self, event: Event<'_, T>) {
|
||||
fn call_event_handler(&self, event: Event<'_, T>) {
|
||||
self.catch_unwind(|| {
|
||||
let mut control_flow = self.control_flow.take();
|
||||
let mut event_handler = self.event_handler.take()
|
||||
|
|
@ -255,7 +236,7 @@ impl<T> EventLoopRunner<T> {
|
|||
});
|
||||
}
|
||||
|
||||
unsafe fn dispatch_buffered_events(&self) {
|
||||
fn dispatch_buffered_events(&self) {
|
||||
loop {
|
||||
// We do this instead of using a `while let` loop because if we use a `while let`
|
||||
// loop the reference returned `borrow_mut()` doesn't get dropped until the end
|
||||
|
|
@ -278,24 +259,22 @@ impl<T> EventLoopRunner<T> {
|
|||
/// Uninitialized
|
||||
/// |
|
||||
/// V
|
||||
/// HandlingMainEvents
|
||||
/// ^ |
|
||||
/// | V
|
||||
/// Idle <--- HandlingRedrawEvents
|
||||
/// |
|
||||
/// V
|
||||
/// Destroyed
|
||||
/// Idle
|
||||
/// ^ |
|
||||
/// | V
|
||||
/// HandlingMainEvents
|
||||
/// |
|
||||
/// V
|
||||
/// Destroyed
|
||||
/// ```
|
||||
///
|
||||
/// Attempting to transition back to `Uninitialized` will result in a panic. Attempting to
|
||||
/// transition *from* `Destroyed` will also reuslt in a panic. Transitioning to the current
|
||||
/// transition *from* `Destroyed` will also result in a panic. Transitioning to the current
|
||||
/// state is a no-op. Even if the `new_runner_state` isn't the immediate next state in the
|
||||
/// runner state machine (e.g. `self.runner_state == HandlingMainEvents` and
|
||||
/// `new_runner_state == Idle`), the intermediate state transitions will still be executed.
|
||||
unsafe fn move_state_to(&self, new_runner_state: RunnerState) {
|
||||
use RunnerState::{
|
||||
Destroyed, HandlingMainEvents, HandlingRedrawEvents, Idle, Uninitialized,
|
||||
};
|
||||
fn move_state_to(&self, new_runner_state: RunnerState) {
|
||||
use RunnerState::{Destroyed, HandlingMainEvents, Idle, Uninitialized};
|
||||
|
||||
match (
|
||||
self.runner_state.replace(new_runner_state),
|
||||
|
|
@ -304,17 +283,12 @@ impl<T> EventLoopRunner<T> {
|
|||
(Uninitialized, Uninitialized)
|
||||
| (Idle, Idle)
|
||||
| (HandlingMainEvents, HandlingMainEvents)
|
||||
| (HandlingRedrawEvents, HandlingRedrawEvents)
|
||||
| (Destroyed, Destroyed) => (),
|
||||
|
||||
// State transitions that initialize the event loop.
|
||||
(Uninitialized, HandlingMainEvents) => {
|
||||
self.call_new_events(true);
|
||||
}
|
||||
(Uninitialized, HandlingRedrawEvents) => {
|
||||
self.call_new_events(true);
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
}
|
||||
(Uninitialized, Idle) => {
|
||||
self.call_new_events(true);
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
|
|
@ -332,19 +306,11 @@ impl<T> EventLoopRunner<T> {
|
|||
(Idle, HandlingMainEvents) => {
|
||||
self.call_new_events(false);
|
||||
}
|
||||
(Idle, HandlingRedrawEvents) => {
|
||||
self.call_new_events(false);
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
}
|
||||
(Idle, Destroyed) => {
|
||||
self.call_event_handler(Event::LoopDestroyed);
|
||||
}
|
||||
|
||||
(HandlingMainEvents, HandlingRedrawEvents) => {
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
}
|
||||
(HandlingMainEvents, Idle) => {
|
||||
warn!("RedrawEventsCleared emitted without explicit MainEventsCleared");
|
||||
self.call_event_handler(Event::MainEventsCleared);
|
||||
self.call_redraw_events_cleared();
|
||||
}
|
||||
|
|
@ -354,24 +320,11 @@ impl<T> EventLoopRunner<T> {
|
|||
self.call_event_handler(Event::LoopDestroyed);
|
||||
}
|
||||
|
||||
(HandlingRedrawEvents, Idle) => {
|
||||
self.call_redraw_events_cleared();
|
||||
}
|
||||
(HandlingRedrawEvents, HandlingMainEvents) => {
|
||||
warn!("NewEvents emitted without explicit RedrawEventsCleared");
|
||||
self.call_redraw_events_cleared();
|
||||
self.call_new_events(false);
|
||||
}
|
||||
(HandlingRedrawEvents, Destroyed) => {
|
||||
self.call_redraw_events_cleared();
|
||||
self.call_event_handler(Event::LoopDestroyed);
|
||||
}
|
||||
|
||||
(Destroyed, _) => panic!("cannot move state from Destroyed"),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn call_new_events(&self, init: bool) {
|
||||
fn call_new_events(&self, init: bool) {
|
||||
let start_cause = match (init, self.control_flow()) {
|
||||
(true, _) => StartCause::Init,
|
||||
(false, ControlFlow::Poll) => StartCause::Poll,
|
||||
|
|
@ -402,10 +355,9 @@ impl<T> EventLoopRunner<T> {
|
|||
self.call_event_handler(Event::Resumed);
|
||||
}
|
||||
self.dispatch_buffered_events();
|
||||
RedrawWindow(self.thread_msg_target, ptr::null(), 0, RDW_INTERNALPAINT);
|
||||
}
|
||||
|
||||
unsafe fn call_redraw_events_cleared(&self) {
|
||||
fn call_redraw_events_cleared(&self) {
|
||||
self.call_event_handler(Event::RedrawEventsCleared);
|
||||
self.last_events_cleared.set(Instant::now());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue