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:
|
//! And the following platform-specific modules:
|
||||||
//!
|
//!
|
||||||
//! - `run_ondemand` (available on `android`)
|
//! - `run_ondemand` (available on `windows`, `android`)
|
||||||
//! - `pump_events` (available on `android`)
|
//! - `pump_events` (available on `windows`, `android`)
|
||||||
//! - `run_return` (available on `windows`, `unix`, `macos`, and `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.
|
//! 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)]
|
#[cfg(x11_platform)]
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
|
|
||||||
#[cfg(any(android_platform))]
|
#[cfg(any(windows_platform, android_platform))]
|
||||||
pub mod run_ondemand;
|
pub mod run_ondemand;
|
||||||
|
|
||||||
#[cfg(any(android_platform,))]
|
#[cfg(any(windows_platform, android_platform,))]
|
||||||
pub mod pump_events;
|
pub mod pump_events;
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
|
|
@ -14,7 +14,6 @@ use std::{
|
||||||
mpsc::{self, Receiver, Sender},
|
mpsc::{self, Receiver, Sender},
|
||||||
Arc, Mutex, MutexGuard,
|
Arc, Mutex, MutexGuard,
|
||||||
},
|
},
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,13 +22,11 @@ use raw_window_handle::{RawDisplayHandle, WindowsDisplayHandle};
|
||||||
|
|
||||||
use windows_sys::Win32::{
|
use windows_sys::Win32::{
|
||||||
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
|
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::{
|
Graphics::Gdi::{
|
||||||
GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow, RedrawWindow,
|
GetMonitorInfoW, MonitorFromRect, MonitorFromWindow, RedrawWindow, ScreenToClient,
|
||||||
ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT,
|
ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT, SC_SCREENSAVE,
|
||||||
SC_SCREENSAVE,
|
|
||||||
},
|
},
|
||||||
Media::{timeBeginPeriod, timeEndPeriod, timeGetDevCaps, TIMECAPS, TIMERR_NOERROR},
|
|
||||||
System::{
|
System::{
|
||||||
Ole::RevokeDragDrop,
|
Ole::RevokeDragDrop,
|
||||||
Threading::{GetCurrentThreadId, INFINITE},
|
Threading::{GetCurrentThreadId, INFINITE},
|
||||||
|
@ -54,34 +51,35 @@ use windows_sys::Win32::{
|
||||||
},
|
},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
|
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
|
||||||
GetMenu, GetMessageW, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW,
|
GetMenu, GetMessageW, KillTimer, LoadCursorW, PeekMessageW, PostMessageW,
|
||||||
PostMessageW, PostThreadMessageW, RegisterClassExW, RegisterWindowMessageA, SetCursor,
|
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetTimer, SetWindowPos,
|
||||||
SetWindowPos, TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE,
|
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
|
||||||
GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE,
|
HTCAPTION, HTCLIENT, MINMAXINFO, MNC_CLOSE, MSG, NCCALCSIZE_PARAMS, PM_REMOVE, PT_PEN,
|
||||||
NCCALCSIZE_PARAMS, PM_NOREMOVE, PM_QS_PAINT, PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS,
|
PT_TOUCH, RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
|
||||||
RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA,
|
||||||
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
|
WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
|
||||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
|
||||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
|
||||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE,
|
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
|
||||||
WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
|
||||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
|
||||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR,
|
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
|
||||||
WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP,
|
WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
|
||||||
WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP,
|
WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
|
||||||
WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT,
|
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
|
||||||
WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
WS_VISIBLE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
|
error::RunLoopError,
|
||||||
event::{DeviceEvent, Event, Force, Ime, RawKeyEvent, Touch, TouchPhase, WindowEvent},
|
event::{DeviceEvent, Event, Force, Ime, RawKeyEvent, Touch, TouchPhase, WindowEvent},
|
||||||
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
event_loop::{ControlFlow, DeviceEvents, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||||
keyboard::{KeyCode, ModifiersState},
|
keyboard::{KeyCode, ModifiersState},
|
||||||
platform::scancode::KeyCodeExtScancode,
|
platform::{pump_events::PumpStatus, scancode::KeyCodeExtScancode},
|
||||||
platform_impl::platform::{
|
platform_impl::platform::{
|
||||||
dark_mode::try_theme,
|
dark_mode::try_theme,
|
||||||
dpi::{become_dpi_aware, dpi_to_scale_factor},
|
dpi::{become_dpi_aware, dpi_to_scale_factor},
|
||||||
|
@ -99,6 +97,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||||
|
|
||||||
|
use self::runner::RunnerState;
|
||||||
|
|
||||||
use super::window::set_skip_taskbar;
|
use super::window::set_skip_taskbar;
|
||||||
|
|
||||||
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
||||||
|
@ -216,13 +216,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
|
|
||||||
let thread_msg_target = create_event_target_window::<T>();
|
let thread_msg_target = create_event_target_window::<T>();
|
||||||
|
|
||||||
thread::Builder::new()
|
let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target));
|
||||||
.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 thread_msg_sender =
|
let thread_msg_sender =
|
||||||
insert_event_target_window_data::<T>(thread_msg_target, runner_shared.clone());
|
insert_event_target_window_data::<T>(thread_msg_target, runner_shared.clone());
|
||||||
|
@ -253,34 +247,246 @@ impl<T: 'static> EventLoop<T> {
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
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);
|
::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
|
where
|
||||||
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
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 {
|
pub fn run_ondemand<F>(&mut self, mut event_handler: F) -> Result<(), RunLoopError>
|
||||||
self.window_target
|
where
|
||||||
.p
|
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
|
||||||
.runner_shared
|
{
|
||||||
.set_event_handler(move |event, control_flow| {
|
{
|
||||||
|
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)
|
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 runner = &self.window_target.p.runner_shared;
|
||||||
|
|
||||||
let exit_code = unsafe {
|
let status = if let ControlFlow::ExitWithCode(code) = runner.control_flow() {
|
||||||
let mut msg = mem::zeroed();
|
runner.loop_destroyed();
|
||||||
|
|
||||||
runner.poll();
|
// Immediately reset the internal state for the loop to allow
|
||||||
'main: loop {
|
// the loop to be run more than once.
|
||||||
if GetMessageW(&mut msg, 0, 0, 0) == false.into() {
|
runner.reset_runner();
|
||||||
break 'main 0;
|
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() {
|
let handled = if let Some(callback) = self.msg_hook.as_deref_mut() {
|
||||||
|
@ -292,26 +498,24 @@ impl<T: 'static> EventLoop<T> {
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&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 {
|
if let Err(payload) = runner.take_panic_error() {
|
||||||
runner.loop_destroyed();
|
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();
|
control_flow
|
||||||
exit_code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
|
@ -391,109 +595,6 @@ fn main_thread_id() -> u32 {
|
||||||
unsafe { MAIN_THREAD_ID }
|
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
|
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
|
||||||
fn dur2timeout(dur: Duration) -> u32 {
|
fn dur2timeout(dur: Duration) -> u32 {
|
||||||
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
|
// 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.
|
/// A lazily-initialized window message ID.
|
||||||
pub struct LazyMessageId {
|
pub struct LazyMessageId {
|
||||||
/// The ID.
|
/// 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`,
|
// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
|
||||||
// and LPARAM is unused.
|
// and LPARAM is unused.
|
||||||
static EXEC_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::ExecMsg\0");
|
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.
|
// Message sent by a `Window` when it wants to be destroyed by the main thread.
|
||||||
// WPARAM and LPARAM are unused.
|
// WPARAM and LPARAM are unused.
|
||||||
pub static DESTROY_MSG_ID: LazyMessageId = LazyMessageId::new("Winit::DestroyMsg\0");
|
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.
|
/// Emit a `ModifiersChanged` event whenever modifiers have changed.
|
||||||
/// Returns the current modifier state
|
/// Returns the current modifier state
|
||||||
fn update_modifiers<T>(window: HWND, userdata: &WindowData<T>) {
|
fn update_modifiers<T>(window: HWND, userdata: &WindowData<T>) {
|
||||||
|
@ -988,13 +1012,6 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
||||||
lparam: LPARAM,
|
lparam: LPARAM,
|
||||||
userdata: &WindowData<T>,
|
userdata: &WindowData<T>,
|
||||||
) -> LRESULT {
|
) -> LRESULT {
|
||||||
RedrawWindow(
|
|
||||||
userdata.event_loop_runner.thread_msg_target(),
|
|
||||||
ptr::null(),
|
|
||||||
0,
|
|
||||||
RDW_INTERNALPAINT,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut result = ProcResult::DefWindowProc(wparam);
|
let mut result = ProcResult::DefWindowProc(wparam);
|
||||||
|
|
||||||
// Send new modifiers before sending key events.
|
// Send new modifiers before sending key events.
|
||||||
|
@ -1116,7 +1133,6 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
||||||
window_id: RootWindowId(WindowId(window)),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
event: Destroyed,
|
event: Destroyed,
|
||||||
});
|
});
|
||||||
userdata.event_loop_runner.remove_window(window);
|
|
||||||
result = ProcResult::Value(0);
|
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.
|
// redraw the window outside the normal flow of the event loop.
|
||||||
RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT);
|
RedrawWindow(window, ptr::null(), 0, RDW_INTERNALPAINT);
|
||||||
} else {
|
} else {
|
||||||
let managing_redraw =
|
|
||||||
flush_paint_messages(Some(window), &userdata.event_loop_runner);
|
|
||||||
userdata.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
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);
|
result = ProcResult::DefWindowProc(wparam);
|
||||||
}
|
}
|
||||||
|
@ -2270,27 +2280,8 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
userdata_removed = true;
|
userdata_removed = true;
|
||||||
0
|
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 => {
|
WM_PAINT => {
|
||||||
ValidateRect(window, ptr::null());
|
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.
|
// Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them.
|
||||||
DefWindowProcW(window, msg, wparam, lparam)
|
DefWindowProcW(window, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
@ -2329,40 +2320,6 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
function();
|
function();
|
||||||
0
|
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),
|
_ => DefWindowProcW(window, msg, wparam, lparam),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::{HashSet, VecDeque},
|
collections::VecDeque,
|
||||||
mem, panic, ptr,
|
mem, panic,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use windows_sys::Win32::{
|
use windows_sys::Win32::Foundation::HWND;
|
||||||
Foundation::HWND,
|
|
||||||
Graphics::Gdi::{RedrawWindow, RDW_INTERNALPAINT},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
|
@ -30,7 +27,11 @@ type EventHandler<T> = Cell<Option<Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)
|
||||||
pub(crate) struct EventLoopRunner<T: 'static> {
|
pub(crate) struct EventLoopRunner<T: 'static> {
|
||||||
// The event loop's win32 handles
|
// The event loop's win32 handles
|
||||||
pub(super) thread_msg_target: HWND,
|
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>,
|
control_flow: Cell<ControlFlow>,
|
||||||
runner_state: Cell<RunnerState>,
|
runner_state: Cell<RunnerState>,
|
||||||
|
@ -38,8 +39,6 @@ pub(crate) struct EventLoopRunner<T: 'static> {
|
||||||
event_handler: EventHandler<T>,
|
event_handler: EventHandler<T>,
|
||||||
event_buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
event_buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
||||||
|
|
||||||
owned_windows: Cell<HashSet<HWND>>,
|
|
||||||
|
|
||||||
panic_error: Cell<Option<PanicError>>,
|
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.
|
/// See `move_state_to` function for details on how the state loop works.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[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.
|
/// The event loop has just been created, and an `Init` event must be sent.
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
/// The event loop is idling.
|
/// 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.
|
/// 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.
|
/// `NewEvents` has been sent, and `MainEventsCleared` hasn't.
|
||||||
HandlingMainEvents,
|
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.
|
/// The event loop has been destroyed. No other events will be emitted.
|
||||||
Destroyed,
|
Destroyed,
|
||||||
}
|
}
|
||||||
|
@ -68,20 +64,30 @@ enum BufferedEvent<T: 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> EventLoopRunner<T> {
|
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 {
|
EventLoopRunner {
|
||||||
thread_msg_target,
|
thread_msg_target,
|
||||||
wait_thread_id,
|
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::Poll),
|
||||||
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),
|
||||||
event_buffer: RefCell::new(VecDeque::new()),
|
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)
|
pub(crate) unsafe fn set_event_handler<F>(&self, f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||||
|
@ -93,18 +99,22 @@ impl<T> EventLoopRunner<T> {
|
||||||
assert!(old_event_handler.is_none());
|
assert!(old_event_handler.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_event_handler(&self) {
|
||||||
|
self.event_handler.set(None);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_runner(&self) {
|
pub(crate) fn reset_runner(&self) {
|
||||||
let EventLoopRunner {
|
let EventLoopRunner {
|
||||||
thread_msg_target: _,
|
thread_msg_target: _,
|
||||||
wait_thread_id: _,
|
interrupt_msg_dispatch,
|
||||||
runner_state,
|
runner_state,
|
||||||
panic_error,
|
panic_error,
|
||||||
control_flow,
|
control_flow,
|
||||||
last_events_cleared: _,
|
last_events_cleared: _,
|
||||||
event_handler,
|
event_handler,
|
||||||
event_buffer: _,
|
event_buffer: _,
|
||||||
owned_windows: _,
|
|
||||||
} = self;
|
} = self;
|
||||||
|
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::Poll);
|
||||||
|
@ -114,18 +124,11 @@ impl<T> EventLoopRunner<T> {
|
||||||
|
|
||||||
/// State retrieval functions.
|
/// State retrieval functions.
|
||||||
impl<T> EventLoopRunner<T> {
|
impl<T> EventLoopRunner<T> {
|
||||||
|
#[allow(unused)]
|
||||||
pub fn thread_msg_target(&self) -> HWND {
|
pub fn thread_msg_target(&self) -> HWND {
|
||||||
self.thread_msg_target
|
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> {
|
pub fn take_panic_error(&self) -> Result<(), PanicError> {
|
||||||
match self.panic_error.take() {
|
match self.panic_error.take() {
|
||||||
Some(err) => Err(err),
|
Some(err) => Err(err),
|
||||||
|
@ -133,12 +136,16 @@ impl<T> EventLoopRunner<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn control_flow(&self) -> ControlFlow {
|
pub fn state(&self) -> RunnerState {
|
||||||
self.control_flow.get()
|
self.runner_state.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handling_events(&self) -> bool {
|
pub fn set_exit_control_flow(&self, code: i32) {
|
||||||
self.runner_state.get() != RunnerState::Idle
|
self.control_flow.set(ControlFlow::ExitWithCode(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn control_flow(&self) -> ControlFlow {
|
||||||
|
self.control_flow.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_buffer(&self) -> bool {
|
pub fn should_buffer(&self) -> bool {
|
||||||
|
@ -177,42 +184,25 @@ impl<T> EventLoopRunner<T> {
|
||||||
None
|
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.
|
/// Event dispatch functions.
|
||||||
impl<T> EventLoopRunner<T> {
|
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);
|
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 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);
|
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() {
|
} else if self.should_buffer() {
|
||||||
// If the runner is already borrowed, we're in the middle of an event loop invocation. Add
|
// 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.
|
// the event to a buffer to be processed later.
|
||||||
|
@ -220,25 +210,16 @@ impl<T> EventLoopRunner<T> {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push_back(BufferedEvent::from_event(event))
|
.push_back(BufferedEvent::from_event(event))
|
||||||
} else {
|
} else {
|
||||||
self.move_state_to(RunnerState::HandlingMainEvents);
|
|
||||||
self.call_event_handler(event);
|
self.call_event_handler(event);
|
||||||
self.dispatch_buffered_events();
|
self.dispatch_buffered_events();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn main_events_cleared(&self) {
|
pub(crate) fn loop_destroyed(&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) {
|
|
||||||
self.move_state_to(RunnerState::Destroyed);
|
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(|| {
|
self.catch_unwind(|| {
|
||||||
let mut control_flow = self.control_flow.take();
|
let mut control_flow = self.control_flow.take();
|
||||||
let mut event_handler = self.event_handler.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 {
|
loop {
|
||||||
// We do this instead of using a `while let` loop because if we use a `while let`
|
// 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
|
// loop the reference returned `borrow_mut()` doesn't get dropped until the end
|
||||||
|
@ -278,24 +259,22 @@ impl<T> EventLoopRunner<T> {
|
||||||
/// Uninitialized
|
/// Uninitialized
|
||||||
/// |
|
/// |
|
||||||
/// V
|
/// V
|
||||||
/// HandlingMainEvents
|
/// Idle
|
||||||
/// ^ |
|
/// ^ |
|
||||||
/// | V
|
/// | V
|
||||||
/// Idle <--- HandlingRedrawEvents
|
/// HandlingMainEvents
|
||||||
/// |
|
/// |
|
||||||
/// V
|
/// V
|
||||||
/// Destroyed
|
/// Destroyed
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Attempting to transition back to `Uninitialized` will result in a panic. Attempting to
|
/// 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
|
/// 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
|
/// runner state machine (e.g. `self.runner_state == HandlingMainEvents` and
|
||||||
/// `new_runner_state == Idle`), the intermediate state transitions will still be executed.
|
/// `new_runner_state == Idle`), the intermediate state transitions will still be executed.
|
||||||
unsafe fn move_state_to(&self, new_runner_state: RunnerState) {
|
fn move_state_to(&self, new_runner_state: RunnerState) {
|
||||||
use RunnerState::{
|
use RunnerState::{Destroyed, HandlingMainEvents, Idle, Uninitialized};
|
||||||
Destroyed, HandlingMainEvents, HandlingRedrawEvents, Idle, Uninitialized,
|
|
||||||
};
|
|
||||||
|
|
||||||
match (
|
match (
|
||||||
self.runner_state.replace(new_runner_state),
|
self.runner_state.replace(new_runner_state),
|
||||||
|
@ -304,17 +283,12 @@ impl<T> EventLoopRunner<T> {
|
||||||
(Uninitialized, Uninitialized)
|
(Uninitialized, Uninitialized)
|
||||||
| (Idle, Idle)
|
| (Idle, Idle)
|
||||||
| (HandlingMainEvents, HandlingMainEvents)
|
| (HandlingMainEvents, HandlingMainEvents)
|
||||||
| (HandlingRedrawEvents, HandlingRedrawEvents)
|
|
||||||
| (Destroyed, Destroyed) => (),
|
| (Destroyed, Destroyed) => (),
|
||||||
|
|
||||||
// State transitions that initialize the event loop.
|
// State transitions that initialize the event loop.
|
||||||
(Uninitialized, HandlingMainEvents) => {
|
(Uninitialized, HandlingMainEvents) => {
|
||||||
self.call_new_events(true);
|
self.call_new_events(true);
|
||||||
}
|
}
|
||||||
(Uninitialized, HandlingRedrawEvents) => {
|
|
||||||
self.call_new_events(true);
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
}
|
|
||||||
(Uninitialized, Idle) => {
|
(Uninitialized, Idle) => {
|
||||||
self.call_new_events(true);
|
self.call_new_events(true);
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
self.call_event_handler(Event::MainEventsCleared);
|
||||||
|
@ -332,19 +306,11 @@ impl<T> EventLoopRunner<T> {
|
||||||
(Idle, HandlingMainEvents) => {
|
(Idle, HandlingMainEvents) => {
|
||||||
self.call_new_events(false);
|
self.call_new_events(false);
|
||||||
}
|
}
|
||||||
(Idle, HandlingRedrawEvents) => {
|
|
||||||
self.call_new_events(false);
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
}
|
|
||||||
(Idle, Destroyed) => {
|
(Idle, Destroyed) => {
|
||||||
self.call_event_handler(Event::LoopDestroyed);
|
self.call_event_handler(Event::LoopDestroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
(HandlingMainEvents, HandlingRedrawEvents) => {
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
}
|
|
||||||
(HandlingMainEvents, Idle) => {
|
(HandlingMainEvents, Idle) => {
|
||||||
warn!("RedrawEventsCleared emitted without explicit MainEventsCleared");
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
self.call_event_handler(Event::MainEventsCleared);
|
||||||
self.call_redraw_events_cleared();
|
self.call_redraw_events_cleared();
|
||||||
}
|
}
|
||||||
|
@ -354,24 +320,11 @@ impl<T> EventLoopRunner<T> {
|
||||||
self.call_event_handler(Event::LoopDestroyed);
|
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"),
|
(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()) {
|
let start_cause = match (init, self.control_flow()) {
|
||||||
(true, _) => StartCause::Init,
|
(true, _) => StartCause::Init,
|
||||||
(false, ControlFlow::Poll) => StartCause::Poll,
|
(false, ControlFlow::Poll) => StartCause::Poll,
|
||||||
|
@ -402,10 +355,9 @@ impl<T> EventLoopRunner<T> {
|
||||||
self.call_event_handler(Event::Resumed);
|
self.call_event_handler(Event::Resumed);
|
||||||
}
|
}
|
||||||
self.dispatch_buffered_events();
|
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.call_event_handler(Event::RedrawEventsCleared);
|
||||||
self.last_events_cleared.set(Instant::now());
|
self.last_events_cleared.set(Instant::now());
|
||||||
}
|
}
|
||||||
|
|
|
@ -987,8 +987,6 @@ impl<'a, T: 'static> InitData<'a, T> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
self.event_loop.runner_shared.register_window(win.window.0);
|
|
||||||
|
|
||||||
event_loop::WindowData {
|
event_loop::WindowData {
|
||||||
window_state: win.window_state.clone(),
|
window_state: win.window_state.clone(),
|
||||||
event_loop_runner: self.event_loop.runner_shared.clone(),
|
event_loop_runner: self.event_loop.runner_shared.clone(),
|
||||||
|
|
|
@ -524,25 +524,25 @@ impl Window {
|
||||||
self.window.scale_factor()
|
self.window.scale_factor()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits a [`Event::RedrawRequested`] event in the associated event loop after all OS
|
/// Requests a future [`Event::RedrawRequested`] event to be emitted in a way that is
|
||||||
/// events have been processed by the event loop.
|
/// synchronized and / or throttled by the windowing system.
|
||||||
///
|
///
|
||||||
/// This is the **strongly encouraged** method of redrawing windows, as it can integrate with
|
/// This is the **strongly encouraged** method of redrawing windows, as it can integrate with
|
||||||
/// OS-requested redraws (e.g. when a window gets resized).
|
/// OS-requested redraws (e.g. when a window gets resized).
|
||||||
///
|
///
|
||||||
/// This function can cause `RedrawRequested` events to be emitted after [`Event::MainEventsCleared`]
|
/// Applications should always aim to redraw whenever they receive a `RedrawRequested` event.
|
||||||
/// but before `Event::NewEvents` if called in the following circumstances:
|
///
|
||||||
/// * While processing `MainEventsCleared`.
|
/// There are no strong guarantees about when exactly a `RedrawRequest` event will be emitted
|
||||||
/// * While processing a `RedrawRequested` event that was sent during `MainEventsCleared` or any
|
/// with respect to other events, since the requirements can vary significantly between
|
||||||
/// directly subsequent `RedrawRequested` event.
|
/// windowing systems.
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## 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.
|
/// - **iOS:** Can only be called on the main thread.
|
||||||
/// - **Android:** Subsequent calls after `MainEventsCleared` are not handled.
|
|
||||||
///
|
///
|
||||||
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
|
/// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested
|
||||||
/// [`Event::MainEventsCleared`]: crate::event::Event::MainEventsCleared
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn request_redraw(&self) {
|
pub fn request_redraw(&self) {
|
||||||
self.window.request_redraw()
|
self.window.request_redraw()
|
||||||
|
|
Loading…
Reference in a new issue