mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
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
|
@ -11,8 +11,8 @@
|
|||
//!
|
||||
//! And the following platform-specific modules:
|
||||
//!
|
||||
//! - `run_ondemand` (available on `android`)
|
||||
//! - `pump_events` (available on `android`)
|
||||
//! - `run_ondemand` (available on `windows`, `android`)
|
||||
//! - `pump_events` (available on `windows`, `android`)
|
||||
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
|
||||
//!
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
|
@ -36,10 +36,10 @@ pub mod windows;
|
|||
#[cfg(x11_platform)]
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(any(android_platform))]
|
||||
#[cfg(any(windows_platform, android_platform))]
|
||||
pub mod run_ondemand;
|
||||
|
||||
#[cfg(any(android_platform,))]
|
||||
#[cfg(any(windows_platform, android_platform,))]
|
||||
pub mod pump_events;
|
||||
|
||||
#[cfg(any(
|
||||
|
|
|
@ -14,7 +14,6 @@ use std::{
|
|||
mpsc::{self, Receiver, Sender},
|
||||
Arc, Mutex, MutexGuard,
|
||||
},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
|
@ -23,13 +22,11 @@ use raw_window_handle::{RawDisplayHandle, WindowsDisplayHandle};
|
|||
|
||||
use windows_sys::Win32::{
|
||||
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
|
||||
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_TIMEOUT, WPARAM},
|
||||
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WPARAM},
|
||||
Graphics::Gdi::{
|
||||
GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow, RedrawWindow,
|
||||
ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT,
|
||||
SC_SCREENSAVE,
|
||||
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
|
||||
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
|
||||
},
|
||||
Media::{timeBeginPeriod, timeEndPeriod, timeGetDevCaps, TIMECAPS, TIMERR_NOERROR},
|
||||
System::{
|
||||
Ole::RevokeDragDrop,
|
||||
Threading::{GetCurrentThreadId, INFINITE},
|
||||
|
@ -54,34 +51,35 @@ use windows_sys::Win32::{
|
|||
},
|
||||
WindowsAndMessaging::{
|
||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
|
||||
GetMenu, GetMessageW, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW,
|
||||
PostMessageW, PostThreadMessageW, RegisterClassExW, RegisterWindowMessageA, SetCursor,
|
||||
SetWindowPos, TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE,
|
||||
GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE,
|
||||
NCCALCSIZE_PARAMS, PM_NOREMOVE, PM_QS_PAINT, PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS,
|
||||
RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
|
||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE,
|
||||
WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
|
||||
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
||||
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP,
|
||||
WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT,
|
||||
WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW,
|
||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos,
|
||||
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
|
||||
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
||||
PT_TOUCH, RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
|
||||
SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA,
|
||||
WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
|
||||
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
|
||||
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
|
||||
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
|
||||
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
|
||||
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
|
||||
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
|
||||
WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
|
||||
WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
|
||||
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
error::RunLoopError,
|
||||
event::{DeviceEvent, Event, Force, Ime, RawKeyEvent, Touch, TouchPhase, WindowEvent},
|
||||
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
keyboard::{KeyCode, ModifiersState},
|
||||
platform::scancode::KeyCodeExtScancode,
|
||||
platform::{pump_events::PumpStatus, scancode::KeyCodeExtScancode},
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_theme,
|
||||
dpi::{become_dpi_aware, dpi_to_scale_factor},
|
||||
|
@ -99,6 +97,8 @@ use crate::{
|
|||
};
|
||||
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||
|
||||
use self::runner::RunnerState;
|
||||
|
||||
use super::window::set_skip_taskbar;
|
||||
|
||||
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
||||
|
@ -216,13 +216,7 @@ impl<T: 'static> EventLoop<T> {
|
|||
|
||||
let thread_msg_target = create_event_target_window::<T>();
|
||||
|
||||
thread::Builder::new()
|
||||
.name("winit wait thread".to_string())
|
||||
.spawn(move || wait_thread(thread_id, thread_msg_target))
|
||||
.expect("Failed to spawn winit wait thread");
|
||||
let wait_thread_id = get_wait_thread_id();
|
||||
|
||||
let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target, wait_thread_id));
|
||||
let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target));
|
||||
|
||||
let thread_msg_sender =
|
||||
insert_event_target_window_data::<T>(thread_msg_target, runner_shared.clone());
|
||||
|
@ -253,34 +247,246 @@ impl<T: 'static> EventLoop<T> {
|
|||
where
|
||||
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
let exit_code = self.run_return(event_handler);
|
||||
let exit_code = match self.run_ondemand(event_handler) {
|
||||
Err(RunLoopError::ExitFailure(code)) => code,
|
||||
Err(_err) => 1,
|
||||
Ok(_) => 0,
|
||||
};
|
||||
::std::process::exit(exit_code);
|
||||
}
|
||||
|
||||
pub fn run_return<F>(&mut self, mut event_handler: F) -> i32
|
||||
pub fn run_return<F>(&mut self, event_handler: F) -> i32
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
match self.run_ondemand(event_handler) {
|
||||
Err(RunLoopError::ExitFailure(code)) => code,
|
||||
Err(_err) => 1,
|
||||
Ok(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.window_target
|
||||
.p
|
||||
.runner_shared
|
||||
.set_event_handler(move |event, control_flow| {
|
||||
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
if runner.state() != RunnerState::Uninitialized {
|
||||
return Err(RunLoopError::AlreadyRunning);
|
||||
}
|
||||
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
// # Safety
|
||||
// We make sure to call runner.clear_event_handler() before
|
||||
// returning
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event, control_flow| {
|
||||
event_handler(event, event_loop_windows_ref, control_flow)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let exit_code = loop {
|
||||
if let ControlFlow::ExitWithCode(code) = self.wait_and_dispatch_message() {
|
||||
break code;
|
||||
}
|
||||
|
||||
if let ControlFlow::ExitWithCode(code) = self.dispatch_peeked_messages() {
|
||||
break code;
|
||||
}
|
||||
};
|
||||
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
runner.loop_destroyed();
|
||||
|
||||
// # Safety
|
||||
// We assume that this will effectively call `runner.clear_event_handler()`
|
||||
// to meet the safety requirements for calling `runner.set_event_handler()` above.
|
||||
runner.reset_runner();
|
||||
|
||||
if exit_code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RunLoopError::ExitFailure(exit_code))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, mut event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
||||
{
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
|
||||
// # Safety
|
||||
// We make sure to call runner.clear_event_handler() before
|
||||
// returning
|
||||
//
|
||||
// Note: we're currently assuming nothing can panic and unwind
|
||||
// to leave the runner in an unsound state with an associated
|
||||
// event handler.
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event, control_flow| {
|
||||
event_handler(event, event_loop_windows_ref, control_flow)
|
||||
});
|
||||
runner.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
self.dispatch_peeked_messages();
|
||||
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
let exit_code = unsafe {
|
||||
let mut msg = mem::zeroed();
|
||||
let status = if let ControlFlow::ExitWithCode(code) = runner.control_flow() {
|
||||
runner.loop_destroyed();
|
||||
|
||||
runner.poll();
|
||||
'main: loop {
|
||||
if GetMessageW(&mut msg, 0, 0, 0) == false.into() {
|
||||
break 'main 0;
|
||||
// Immediately reset the internal state for the loop to allow
|
||||
// the loop to be run more than once.
|
||||
runner.reset_runner();
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
runner.prepare_wait();
|
||||
PumpStatus::Continue
|
||||
};
|
||||
|
||||
// We wait until we've checked for an exit status before clearing the
|
||||
// application callback, in case we need to dispatch a LoopDestroyed event
|
||||
//
|
||||
// # Safety
|
||||
// This pairs up with our call to `runner.set_event_handler` and ensures
|
||||
// the application's callback can't be held beyond its lifetime.
|
||||
runner.clear_event_handler();
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
/// Wait for one message and dispatch it, optionally with a timeout if control_flow is `WaitUntil`
|
||||
fn wait_and_dispatch_message(&mut self) -> ControlFlow {
|
||||
let start = Instant::now();
|
||||
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
let timeout = match runner.control_flow() {
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
Some(wait_deadline.saturating_duration_since(start))
|
||||
}
|
||||
ControlFlow::ExitWithCode(_code) => unreachable!(),
|
||||
};
|
||||
|
||||
fn get_msg_with_timeout(msg: &mut MSG, timeout: Option<Duration>) -> PumpStatus {
|
||||
unsafe {
|
||||
// A timeout of None means wait indefinitely (so we don't need to call SetTimer)
|
||||
let timer_id = timeout.map(|timeout| SetTimer(0, 0, dur2timeout(timeout), None));
|
||||
let get_status = GetMessageW(msg, 0, 0, 0);
|
||||
if let Some(timer_id) = timer_id {
|
||||
KillTimer(0, timer_id);
|
||||
}
|
||||
// A return value of 0 implies `WM_QUIT`
|
||||
if get_status == 0 {
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
PumpStatus::Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the next MSG either via PeekMessage or GetMessage depending on whether the
|
||||
/// requested timeout is `ZERO` (and so we don't want to block)
|
||||
///
|
||||
/// Returns `None` if if no MSG was read, else a `Continue` or `Exit` status
|
||||
fn wait_for_msg(msg: &mut MSG, timeout: Option<Duration>) -> Option<PumpStatus> {
|
||||
if timeout == Some(Duration::ZERO) {
|
||||
unsafe {
|
||||
if PeekMessageW(msg, 0, 0, 0, PM_REMOVE) != 0 {
|
||||
Some(PumpStatus::Continue)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(get_msg_with_timeout(msg, timeout))
|
||||
}
|
||||
}
|
||||
|
||||
// We aim to be consistent with the MacOS backend which has a RunLoop
|
||||
// observer that will dispatch MainEventsCleared when about to wait for
|
||||
// events, and NewEvents after the RunLoop wakes up.
|
||||
//
|
||||
// We emulate similar behaviour by treating `GetMessage` as our wait
|
||||
// point and wake up point (when it returns) and we drain all other
|
||||
// pending messages via `PeekMessage` until we come back to "wait" via
|
||||
// `GetMessage`
|
||||
//
|
||||
runner.prepare_wait();
|
||||
|
||||
// # Safety
|
||||
// The Windows API has no documented requirement for bitwise
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid unitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
let msg_status = wait_for_msg(&mut msg, timeout);
|
||||
|
||||
// Before we potentially exit, make sure to consistently emit an event for the wake up
|
||||
runner.wakeup();
|
||||
|
||||
match msg_status {
|
||||
None => {} // No MSG to dispatch
|
||||
Some(PumpStatus::Exit(code)) => {
|
||||
runner.set_exit_control_flow(code);
|
||||
return runner.control_flow();
|
||||
}
|
||||
Some(PumpStatus::Continue) => {
|
||||
unsafe {
|
||||
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
|
||||
callback(&mut msg as *mut _ as *mut _)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !handled {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(payload) = runner.take_panic_error() {
|
||||
runner.reset_runner();
|
||||
panic::resume_unwind(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runner.control_flow()
|
||||
}
|
||||
|
||||
/// Dispatch all queued messages via `PeekMessageW`
|
||||
fn dispatch_peeked_messages(&mut self) -> ControlFlow {
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
// We generally want to continue dispatching all pending messages
|
||||
// but we also allow dispatching to be interrupted as a means to
|
||||
// ensure the `pump_events` won't indefinitely block an external
|
||||
// event loop if there are too many pending events. This interrupt
|
||||
// flag will be set after dispatching `RedrawRequested` events.
|
||||
runner.interrupt_msg_dispatch.set(false);
|
||||
|
||||
// # Safety
|
||||
// The Windows API has no documented requirement for bitwise
|
||||
// initializing a `MSG` struct (it can be uninitialized memory for the C
|
||||
// API) and there's no API to construct or initialize a `MSG`. This
|
||||
// is the simplest way avoid unitialized memory in Rust
|
||||
let mut msg = unsafe { mem::zeroed() };
|
||||
|
||||
let mut control_flow = runner.control_flow();
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) == false.into() {
|
||||
break;
|
||||
}
|
||||
|
||||
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
|
||||
|
@ -292,26 +498,24 @@ impl<T: 'static> EventLoop<T> {
|
|||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
if let Err(payload) = runner.take_panic_error() {
|
||||
runner.reset_runner();
|
||||
panic::resume_unwind(payload);
|
||||
}
|
||||
|
||||
if let ControlFlow::ExitWithCode(code) = runner.control_flow() {
|
||||
if !runner.handling_events() {
|
||||
break 'main code;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
runner.loop_destroyed();
|
||||
if let Err(payload) = runner.take_panic_error() {
|
||||
runner.reset_runner();
|
||||
panic::resume_unwind(payload);
|
||||
}
|
||||
|
||||
control_flow = runner.control_flow();
|
||||
if let ControlFlow::ExitWithCode(_code) = control_flow {
|
||||
break;
|
||||
}
|
||||
|
||||
if runner.interrupt_msg_dispatch.get() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
runner.reset_runner();
|
||||
exit_code
|
||||
control_flow
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
|
@ -391,109 +595,6 @@ fn main_thread_id() -> u32 {
|
|||
unsafe { MAIN_THREAD_ID }
|
||||
}
|
||||
|
||||
fn get_wait_thread_id() -> u32 {
|
||||
unsafe {
|
||||
let mut msg = mem::zeroed();
|
||||
let result = GetMessageW(
|
||||
&mut msg,
|
||||
-1,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
);
|
||||
assert_eq!(
|
||||
msg.message,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
"this shouldn't be possible. please open an issue with Winit. error code: {result}"
|
||||
);
|
||||
msg.lParam as u32
|
||||
}
|
||||
}
|
||||
|
||||
static WAIT_PERIOD_MIN: Lazy<Option<u32>> = Lazy::new(|| unsafe {
|
||||
let mut caps = TIMECAPS {
|
||||
wPeriodMin: 0,
|
||||
wPeriodMax: 0,
|
||||
};
|
||||
if timeGetDevCaps(&mut caps, mem::size_of::<TIMECAPS>() as u32) == TIMERR_NOERROR {
|
||||
Some(caps.wPeriodMin)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) {
|
||||
unsafe {
|
||||
let mut msg: MSG;
|
||||
|
||||
let cur_thread_id = GetCurrentThreadId();
|
||||
PostThreadMessageW(
|
||||
parent_thread_id,
|
||||
SEND_WAIT_THREAD_ID_MSG_ID.get(),
|
||||
0,
|
||||
cur_thread_id as LPARAM,
|
||||
);
|
||||
|
||||
let mut wait_until_opt = None;
|
||||
'main: loop {
|
||||
// Zeroing out the message ensures that the `WaitUntilInstantBox` doesn't get
|
||||
// double-freed if `MsgWaitForMultipleObjectsEx` returns early and there aren't
|
||||
// additional messages to process.
|
||||
msg = mem::zeroed();
|
||||
|
||||
if wait_until_opt.is_some() {
|
||||
if PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != false.into() {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
} else if GetMessageW(&mut msg, 0, 0, 0) == false.into() {
|
||||
break 'main;
|
||||
} else {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
if msg.message == WAIT_UNTIL_MSG_ID.get() {
|
||||
wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam as *mut _));
|
||||
} else if msg.message == CANCEL_WAIT_UNTIL_MSG_ID.get() {
|
||||
wait_until_opt = None;
|
||||
}
|
||||
|
||||
if let Some(wait_until) = wait_until_opt {
|
||||
let now = Instant::now();
|
||||
if now < wait_until {
|
||||
// Windows' scheduler has a default accuracy of several ms. This isn't good enough for
|
||||
// `WaitUntil`, so we request the Windows scheduler to use a higher accuracy if possible.
|
||||
// If we couldn't query the timer capabilities, then we use the default resolution.
|
||||
if let Some(period) = *WAIT_PERIOD_MIN {
|
||||
timeBeginPeriod(period);
|
||||
}
|
||||
// `MsgWaitForMultipleObjects` is bound by the granularity of the scheduler period.
|
||||
// Because of this, we try to reduce the requested time just enough to undershoot `wait_until`
|
||||
// by the smallest amount possible, and then we busy loop for the remaining time inside the
|
||||
// NewEvents message handler.
|
||||
let resume_reason = MsgWaitForMultipleObjectsEx(
|
||||
0,
|
||||
ptr::null(),
|
||||
dur2timeout(wait_until - now).saturating_sub(WAIT_PERIOD_MIN.unwrap_or(1)),
|
||||
QS_ALLEVENTS,
|
||||
MWMO_INPUTAVAILABLE,
|
||||
);
|
||||
if let Some(period) = *WAIT_PERIOD_MIN {
|
||||
timeEndPeriod(period);
|
||||
}
|
||||
if resume_reason == WAIT_TIMEOUT {
|
||||
PostMessageW(msg_window_id, PROCESS_NEW_EVENTS_MSG_ID.get(), 0, 0);
|
||||
wait_until_opt = None;
|
||||
}
|
||||
} else {
|
||||
PostMessageW(msg_window_id, PROCESS_NEW_EVENTS_MSG_ID.get(), 0, 0);
|
||||
wait_until_opt = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
|
||||
fn dur2timeout(dur: Duration) -> u32 {
|
||||
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
|
||||
|
@ -612,8 +713,6 @@ impl<T: 'static> EventLoopProxy<T> {
|
|||
}
|
||||
}
|
||||
|
||||
type WaitUntilInstantBox = Box<Instant>;
|
||||
|
||||
/// A lazily-initialized window message ID.
|
||||
pub struct LazyMessageId {
|
||||
/// The ID.
|
||||
|
@ -673,13 +772,6 @@ static USER_EVENT_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WakeupMsg\0
|
|||
// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
|
||||
// and LPARAM is unused.
|
||||
static EXEC_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ExecMsg\0");
|
||||
static PROCESS_NEW_EVENTS_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ProcessNewEvents\0");
|
||||
/// lparam is the wait thread's message id.
|
||||
static SEND_WAIT_THREAD_ID_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::SendWaitThreadId\0");
|
||||
/// lparam points to a `Box<Instant>` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should
|
||||
/// be sent.
|
||||
static WAIT_UNTIL_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::WaitUntil\0");
|
||||
static CANCEL_WAIT_UNTIL_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::CancelWaitUntil\0");
|
||||
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
||||
// WPARAM and LPARAM are unused.
|
||||
pub static DESTROY_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::DestroyMsg\0");
|
||||
|
@ -795,74 +887,6 @@ fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Flush redraw events for Winit's windows.
|
||||
///
|
||||
/// Winit's API guarantees that all redraw events will be clustered together and dispatched all at
|
||||
/// once, but the standard Windows message loop doesn't always exhibit that behavior. If multiple
|
||||
/// windows have had redraws scheduled, but an input event is pushed to the message queue between
|
||||
/// the `WM_PAINT` call for the first window and the `WM_PAINT` call for the second window, Windows
|
||||
/// will dispatch the input event immediately instead of flushing all the redraw events. This
|
||||
/// function explicitly pulls all of Winit's redraw events out of the event queue so that they
|
||||
/// always all get processed in one fell swoop.
|
||||
///
|
||||
/// Returns `true` if this invocation flushed all the redraw events. If this function is re-entrant,
|
||||
/// it won't flush the redraw events and will return `false`.
|
||||
#[must_use]
|
||||
unsafe fn flush_paint_messages<T: 'static>(
|
||||
except: Option<HWND>,
|
||||
runner: &EventLoopRunner<T>,
|
||||
) -> bool {
|
||||
if !runner.redrawing() {
|
||||
runner.main_events_cleared();
|
||||
let mut msg = mem::zeroed();
|
||||
runner.owned_windows(|redraw_window| {
|
||||
if Some(redraw_window) == except {
|
||||
return;
|
||||
}
|
||||
|
||||
if PeekMessageW(
|
||||
&mut msg,
|
||||
redraw_window,
|
||||
WM_PAINT,
|
||||
WM_PAINT,
|
||||
PM_REMOVE | PM_QS_PAINT,
|
||||
) == false.into()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn process_control_flow<T: 'static>(runner: &EventLoopRunner<T>) {
|
||||
match runner.control_flow() {
|
||||
ControlFlow::Poll => {
|
||||
PostMessageW(
|
||||
runner.thread_msg_target(),
|
||||
PROCESS_NEW_EVENTS_MSG_ID.get(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
ControlFlow::Wait => (),
|
||||
ControlFlow::WaitUntil(until) => {
|
||||
PostThreadMessageW(
|
||||
runner.wait_thread_id(),
|
||||
WAIT_UNTIL_MSG_ID.get(),
|
||||
0,
|
||||
Box::into_raw(WaitUntilInstantBox::new(until)) as isize,
|
||||
);
|
||||
}
|
||||
ControlFlow::ExitWithCode(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a `ModifiersChanged` event whenever modifiers have changed.
|
||||
/// Returns the current modifier state
|
||||
fn update_modifiers<T>(window: HWND, userdata: &WindowData<T>) {
|
||||
|
@ -988,13 +1012,6 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||
lparam: LPARAM,
|
||||
userdata: &WindowData<T>,
|
||||
) -> LRESULT {
|
||||
RedrawWindow(
|
||||
userdata.event_loop_runner.thread_msg_target(),
|
||||
ptr::null(),
|
||||
0,
|
||||
RDW_INTERNALPAINT,
|
||||
);
|
||||
|
||||
let mut result = ProcResult::DefWindowProc(wparam);
|
||||
|
||||
// Send new modifiers before sending key events.
|
||||
|
@ -1116,7 +1133,6 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||
window_id: RootWindowId(WindowId(window)),
|
||||
event: Destroyed,
|
||||
});
|
||||
userdata.event_loop_runner.remove_window(window);
|
||||
result = ProcResult::Value(0);
|
||||
}
|
||||
|
||||
|
@ -1132,13 +1148,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||
// redraw the window outside the normal flow of the event loop.
|
||||
RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT);
|
||||
} else {
|
||||
let managing_redraw =
|
||||
flush_paint_messages(Some(window), &userdata.event_loop_runner);
|
||||
userdata.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
||||
if managing_redraw {
|
||||
userdata.event_loop_runner.redraw_events_cleared();
|
||||
process_control_flow(&userdata.event_loop_runner);
|
||||
}
|
||||
}
|
||||
result = ProcResult::DefWindowProc(wparam);
|
||||
}
|
||||
|
@ -2270,27 +2280,8 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
|||
userdata_removed = true;
|
||||
0
|
||||
}
|
||||
// Because WM_PAINT comes after all other messages, we use it during modal loops to detect
|
||||
// when the event queue has been emptied. See `process_event` for more details.
|
||||
WM_PAINT => {
|
||||
ValidateRect(window, ptr::null());
|
||||
// If the WM_PAINT handler in `public_window_callback` has already flushed the redraw
|
||||
// events, `handling_events` will return false and we won't emit a second
|
||||
// `RedrawEventsCleared` event.
|
||||
if userdata.event_loop_runner.handling_events() {
|
||||
if userdata.event_loop_runner.should_buffer() {
|
||||
// This branch can be triggered when a nested win32 event loop is triggered
|
||||
// inside of the `event_handler` callback.
|
||||
RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT);
|
||||
} else {
|
||||
// This WM_PAINT handler will never be re-entrant because `flush_paint_messages`
|
||||
// doesn't call WM_PAINT for the thread event target (i.e. this window).
|
||||
assert!(flush_paint_messages(None, &userdata.event_loop_runner));
|
||||
userdata.event_loop_runner.redraw_events_cleared();
|
||||
process_control_flow(&userdata.event_loop_runner);
|
||||
}
|
||||
}
|
||||
|
||||
// Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them.
|
||||
DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
|
@ -2329,40 +2320,6 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
|||
function();
|
||||
0
|
||||
}
|
||||
_ if msg == PROCESS_NEW_EVENTS_MSG_ID.get() => {
|
||||
PostThreadMessageW(
|
||||
userdata.event_loop_runner.wait_thread_id(),
|
||||
CANCEL_WAIT_UNTIL_MSG_ID.get(),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
// if the control_flow is WaitUntil, make sure the given moment has actually passed
|
||||
// before emitting NewEvents
|
||||
if let ControlFlow::WaitUntil(wait_until) = userdata.event_loop_runner.control_flow() {
|
||||
let mut msg = mem::zeroed();
|
||||
while Instant::now() < wait_until {
|
||||
if PeekMessageW(&mut msg, 0, 0, 0, PM_NOREMOVE) != false.into() {
|
||||
// This works around a "feature" in PeekMessageW. If the message PeekMessageW
|
||||
// gets is a WM_PAINT message that had RDW_INTERNALPAINT set (i.e. doesn't
|
||||
// have an update region), PeekMessageW will remove that window from the
|
||||
// redraw queue even though we told it not to remove messages from the
|
||||
// queue. We fix it by re-dispatching an internal paint message to that
|
||||
// window.
|
||||
if msg.message == WM_PAINT {
|
||||
let mut rect = mem::zeroed();
|
||||
if GetUpdateRect(msg.hwnd, &mut rect, false.into()) == false.into() {
|
||||
RedrawWindow(msg.hwnd, ptr::null(), 0, RDW_INTERNALPAINT);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
userdata.event_loop_runner.poll();
|
||||
0
|
||||
}
|
||||
_ => DefWindowProcW(window, msg, wparam, lparam),
|
||||
};
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -987,8 +987,6 @@ impl<'a, T: 'static> InitData<'a, T> {
|
|||
None
|
||||
};
|
||||
|
||||
self.event_loop.runner_shared.register_window(win.window.0);
|
||||
|
||||
event_loop::WindowData {
|
||||
window_state: win.window_state.clone(),
|
||||
event_loop_runner: self.event_loop.runner_shared.clone(),
|
||||
|
|
|
@ -524,25 +524,25 @@ impl Window {
|
|||
self.window.scale_factor()
|
||||
}
|
||||
|
||||
/// Emits a [`Event::RedrawRequested`] event in the associated event loop after all OS
|
||||
/// events have been processed by the event loop.
|
||||
/// Requests a future [`Event::RedrawRequested`] event to be emitted in a way that is
|
||||
/// synchronized and / or throttled by the windowing system.
|
||||
///
|
||||
/// This is the **strongly encouraged** method of redrawing windows, as it can integrate with
|
||||
/// OS-requested redraws (e.g. when a window gets resized).
|
||||
///
|
||||
/// This function can cause `RedrawRequested` events to be emitted after [`Event::MainEventsCleared`]
|
||||
/// but before `Event::NewEvents` if called in the following circumstances:
|
||||
/// * While processing `MainEventsCleared`.
|
||||
/// * While processing a `RedrawRequested` event that was sent during `MainEventsCleared` or any
|
||||
/// directly subsequent `RedrawRequested` event.
|
||||
/// Applications should always aim to redraw whenever they receive a `RedrawRequested` event.
|
||||
///
|
||||
/// There are no strong guarantees about when exactly a `RedrawRequest` event will be emitted
|
||||
/// with respect to other events, since the requirements can vary significantly between
|
||||
/// windowing systems.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Windows** This API uses `RedrawWindow` to request a `WM_PAINT` message and `RedrawRequested`
|
||||
/// is emitted in sync with any `WM_PAINT` messages
|
||||
/// - **iOS:** Can only be called on the main thread.
|
||||
/// - **Android:** Subsequent calls after `MainEventsCleared` are not handled.
|
||||
///
|
||||
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
|
||||
/// [`Event::MainEventsCleared`]: crate::event::Event::MainEventsCleared
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
self.window.request_redraw()
|
||||
|
|
Loading…
Reference in a new issue