mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-24 02:46:33 +11:00
Fix several crashes on Windows by heavily simplifying the event loop code (#1496)
This commit is contained in:
parent
26775fa0b6
commit
b4c6cdf9a3
4 changed files with 695 additions and 581 deletions
|
@ -1,5 +1,6 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`.
|
||||||
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
|
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
|
||||||
|
|
||||||
# 0.22.1 (2020-04-16)
|
# 0.22.1 (2020-04-16)
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
//! An events loop on Win32 is a background thread.
|
|
||||||
//!
|
|
||||||
//! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop.
|
|
||||||
//! Destroying the events loop stops the thread.
|
|
||||||
//!
|
|
||||||
//! You can use the `execute_in_thread` method to execute some code in the background thread.
|
|
||||||
//! Since Win32 requires you to create a window in the right thread, you must use this method
|
|
||||||
//! to create a window.
|
|
||||||
//!
|
|
||||||
//! If you create a window whose class is set to `callback`, the window's events will be
|
|
||||||
//! propagated with `run_forever` and `poll_events`.
|
|
||||||
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
|
|
||||||
//! add a `WindowState` entry to a list of window to be used by the callback.
|
|
||||||
|
|
||||||
mod runner;
|
mod runner;
|
||||||
|
|
||||||
|
@ -24,6 +11,7 @@ use std::{
|
||||||
mpsc::{self, Receiver, Sender},
|
mpsc::{self, Receiver, Sender},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
|
thread,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
|
use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
|
||||||
|
@ -41,7 +29,6 @@ use winapi::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::runner::{ELRShared, EventLoopRunnerShared};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
|
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
|
||||||
|
@ -57,6 +44,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
window::{Fullscreen, WindowId as RootWindowId},
|
window::{Fullscreen, WindowId as RootWindowId},
|
||||||
};
|
};
|
||||||
|
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||||
|
|
||||||
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
||||||
pointerId: UINT,
|
pointerId: UINT,
|
||||||
|
@ -160,9 +148,17 @@ impl<T: 'static> EventLoop<T> {
|
||||||
|
|
||||||
pub fn new_dpi_unaware_any_thread() -> EventLoop<T> {
|
pub fn new_dpi_unaware_any_thread() -> EventLoop<T> {
|
||||||
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||||
let runner_shared = Rc::new(ELRShared::new());
|
|
||||||
let (thread_msg_target, thread_msg_sender) =
|
let thread_msg_target = create_event_target_window();
|
||||||
thread_event_target_window(runner_shared.clone());
|
|
||||||
|
let send_thread_msg_target = thread_msg_target as usize;
|
||||||
|
thread::spawn(move || wait_thread(thread_id, send_thread_msg_target as HWND));
|
||||||
|
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 =
|
||||||
|
subclass_event_target_window(thread_msg_target, runner_shared.clone());
|
||||||
raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target);
|
raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target);
|
||||||
|
|
||||||
EventLoop {
|
EventLoop {
|
||||||
|
@ -200,87 +196,39 @@ impl<T: 'static> EventLoop<T> {
|
||||||
self.window_target
|
self.window_target
|
||||||
.p
|
.p
|
||||||
.runner_shared
|
.runner_shared
|
||||||
.set_runner(self, move |event, control_flow| {
|
.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 runner = &self.window_target.p.runner_shared;
|
let runner = &self.window_target.p.runner_shared;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut msg = mem::zeroed();
|
let mut msg = mem::zeroed();
|
||||||
let mut unread_message_exists = false;
|
|
||||||
|
|
||||||
|
runner.poll();
|
||||||
'main: loop {
|
'main: loop {
|
||||||
|
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
winuser::TranslateMessage(&mut msg);
|
||||||
|
winuser::DispatchMessageW(&mut msg);
|
||||||
|
|
||||||
if let Err(payload) = runner.take_panic_error() {
|
if let Err(payload) = runner.take_panic_error() {
|
||||||
runner.destroy_runner();
|
runner.reset_runner();
|
||||||
panic::resume_unwind(payload);
|
panic::resume_unwind(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.new_events();
|
if runner.control_flow() == ControlFlow::Exit && !runner.handling_events() {
|
||||||
loop {
|
break 'main;
|
||||||
if !unread_message_exists {
|
|
||||||
if 0 == winuser::PeekMessageW(
|
|
||||||
&mut msg,
|
|
||||||
ptr::null_mut(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
winuser::PM_REMOVE,
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
winuser::TranslateMessage(&mut msg);
|
|
||||||
winuser::DispatchMessageW(&mut msg);
|
|
||||||
|
|
||||||
unread_message_exists = false;
|
|
||||||
|
|
||||||
if msg.message == winuser::WM_PAINT {
|
|
||||||
// An "external" redraw was requested.
|
|
||||||
// Note that the WM_PAINT has been dispatched and
|
|
||||||
// has caused the event loop to emit the MainEventsCleared event.
|
|
||||||
// See EventLoopRunner::process_event().
|
|
||||||
// The call to main_events_cleared() below will do nothing.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure we emit the MainEventsCleared event if no WM_PAINT message was received.
|
|
||||||
runner.main_events_cleared();
|
|
||||||
// Drain eventual WM_PAINT messages sent if user called request_redraw()
|
|
||||||
// during handling of MainEventsCleared.
|
|
||||||
loop {
|
|
||||||
if 0 == winuser::PeekMessageW(
|
|
||||||
&mut msg,
|
|
||||||
ptr::null_mut(),
|
|
||||||
winuser::WM_PAINT,
|
|
||||||
winuser::WM_PAINT,
|
|
||||||
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
winuser::TranslateMessage(&mut msg);
|
|
||||||
winuser::DispatchMessageW(&mut msg);
|
|
||||||
}
|
|
||||||
runner.redraw_events_cleared();
|
|
||||||
match runner.control_flow() {
|
|
||||||
ControlFlow::Exit => break 'main,
|
|
||||||
ControlFlow::Wait => {
|
|
||||||
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
unread_message_exists = true;
|
|
||||||
}
|
|
||||||
ControlFlow::WaitUntil(resume_time) => {
|
|
||||||
wait_until_time_or_msg(resume_time);
|
|
||||||
}
|
|
||||||
ControlFlow::Poll => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.destroy_loop();
|
unsafe {
|
||||||
runner.destroy_runner();
|
runner.call_event_handler(Event::LoopDestroyed);
|
||||||
|
}
|
||||||
|
runner.reset_runner();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
|
@ -316,24 +264,83 @@ fn main_thread_id() -> DWORD {
|
||||||
unsafe { MAIN_THREAD_ID }
|
unsafe { MAIN_THREAD_ID }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn wait_until_time_or_msg(wait_until: Instant) {
|
fn get_wait_thread_id() -> DWORD {
|
||||||
let now = Instant::now();
|
unsafe {
|
||||||
if now < wait_until {
|
let mut msg = mem::zeroed();
|
||||||
// MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond
|
let result = winuser::GetMessageW(
|
||||||
// from the requested time and spinlock for the remainder to compensate for that.
|
&mut msg,
|
||||||
let resume_reason = winuser::MsgWaitForMultipleObjectsEx(
|
-1 as _,
|
||||||
|
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||||
|
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
msg.message, *SEND_WAIT_THREAD_ID_MSG_ID,
|
||||||
|
"this shouldn't be possible. please open an issue with Winit. error code: {}",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
msg.lParam as DWORD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_thread(parent_thread_id: DWORD, msg_window_id: HWND) {
|
||||||
|
unsafe {
|
||||||
|
let mut msg: winuser::MSG;
|
||||||
|
|
||||||
|
let cur_thread_id = processthreadsapi::GetCurrentThreadId();
|
||||||
|
winuser::PostThreadMessageW(
|
||||||
|
parent_thread_id,
|
||||||
|
*SEND_WAIT_THREAD_ID_MSG_ID,
|
||||||
0,
|
0,
|
||||||
ptr::null(),
|
cur_thread_id as LPARAM,
|
||||||
dur2timeout(wait_until - now).saturating_sub(1),
|
|
||||||
winuser::QS_ALLEVENTS,
|
|
||||||
winuser::MWMO_INPUTAVAILABLE,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if resume_reason == winerror::WAIT_TIMEOUT {
|
let mut wait_until_opt = None;
|
||||||
let mut msg = mem::zeroed();
|
'main: loop {
|
||||||
while Instant::now() < wait_until {
|
// Zeroing out the message ensures that the `WaitUntilInstantBox` doesn't get
|
||||||
if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
// double-freed if `MsgWaitForMultipleObjectsEx` returns early and there aren't
|
||||||
break;
|
// additional messages to process.
|
||||||
|
msg = mem::zeroed();
|
||||||
|
|
||||||
|
if wait_until_opt.is_some() {
|
||||||
|
if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, winuser::PM_REMOVE) {
|
||||||
|
winuser::TranslateMessage(&mut msg);
|
||||||
|
winuser::DispatchMessageW(&mut msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
||||||
|
break 'main;
|
||||||
|
} else {
|
||||||
|
winuser::TranslateMessage(&mut msg);
|
||||||
|
winuser::DispatchMessageW(&mut msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.message == *WAIT_UNTIL_MSG_ID {
|
||||||
|
wait_until_opt = Some(*WaitUntilInstantBox::from_raw(msg.lParam as *mut _));
|
||||||
|
} else if msg.message == *CANCEL_WAIT_UNTIL_MSG_ID {
|
||||||
|
wait_until_opt = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(wait_until) = wait_until_opt {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now < wait_until {
|
||||||
|
// MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract
|
||||||
|
// 1 millisecond from the requested time and spinlock for the remainder to
|
||||||
|
// compensate for that.
|
||||||
|
let resume_reason = winuser::MsgWaitForMultipleObjectsEx(
|
||||||
|
0,
|
||||||
|
ptr::null(),
|
||||||
|
dur2timeout(wait_until - now).saturating_sub(1),
|
||||||
|
winuser::QS_ALLEVENTS,
|
||||||
|
winuser::MWMO_INPUTAVAILABLE,
|
||||||
|
);
|
||||||
|
if resume_reason == winerror::WAIT_TIMEOUT {
|
||||||
|
winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||||
|
wait_until_opt = None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
winuser::PostMessageW(msg_window_id, *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||||
|
wait_until_opt = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,6 +468,8 @@ impl<T: 'static> EventLoopProxy<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WaitUntilInstantBox = Box<Instant>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
||||||
// WPARAM and LPARAM are unused.
|
// WPARAM and LPARAM are unused.
|
||||||
|
@ -477,6 +486,29 @@ lazy_static! {
|
||||||
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
|
winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
static ref PROCESS_NEW_EVENTS_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
winuser::RegisterWindowMessageA("Winit::ProcessNewEvents\0".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// lparam is the wait thread's message id.
|
||||||
|
static ref SEND_WAIT_THREAD_ID_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
winuser::RegisterWindowMessageA("Winit::SendWaitThreadId\0".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// lparam points to a `Box<Instant>` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should
|
||||||
|
/// be sent.
|
||||||
|
static ref WAIT_UNTIL_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
winuser::RegisterWindowMessageA("Winit::WaitUntil\0".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static ref CANCEL_WAIT_UNTIL_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
winuser::RegisterWindowMessageA("Winit::CancelWaitUntil\0".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
// 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 ref DESTROY_MSG_ID: u32 = {
|
pub static ref DESTROY_MSG_ID: u32 = {
|
||||||
|
@ -519,7 +551,7 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) -> (HWND, Sender<T>) {
|
fn create_event_target_window() -> HWND {
|
||||||
unsafe {
|
unsafe {
|
||||||
let window = winuser::CreateWindowExW(
|
let window = winuser::CreateWindowExW(
|
||||||
winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED,
|
winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED,
|
||||||
|
@ -543,7 +575,15 @@ fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) ->
|
||||||
// the LAYERED style.
|
// the LAYERED style.
|
||||||
(winuser::WS_VISIBLE | winuser::WS_POPUP) as _,
|
(winuser::WS_VISIBLE | winuser::WS_POPUP) as _,
|
||||||
);
|
);
|
||||||
|
window
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subclass_event_target_window<T>(
|
||||||
|
window: HWND,
|
||||||
|
event_loop_runner: EventLoopRunnerShared<T>,
|
||||||
|
) -> Sender<T> {
|
||||||
|
unsafe {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
let subclass_input = ThreadMsgTargetSubclassInput {
|
let subclass_input = ThreadMsgTargetSubclassInput {
|
||||||
|
@ -559,7 +599,7 @@ fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) ->
|
||||||
);
|
);
|
||||||
assert_eq!(subclass_result, 1);
|
assert_eq!(subclass_result, 1);
|
||||||
|
|
||||||
(window, tx)
|
tx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,6 +622,7 @@ unsafe fn release_mouse(window_state: &mut WindowState) {
|
||||||
const WINDOW_SUBCLASS_ID: UINT_PTR = 0;
|
const WINDOW_SUBCLASS_ID: UINT_PTR = 0;
|
||||||
const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1;
|
const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1;
|
||||||
pub(crate) fn subclass_window<T>(window: HWND, subclass_input: SubclassInput<T>) {
|
pub(crate) fn subclass_window<T>(window: HWND, subclass_input: SubclassInput<T>) {
|
||||||
|
subclass_input.event_loop_runner.register_window(window);
|
||||||
let input_ptr = Box::into_raw(Box::new(subclass_input));
|
let input_ptr = Box::into_raw(Box::new(subclass_input));
|
||||||
let subclass_result = unsafe {
|
let subclass_result = unsafe {
|
||||||
commctrl::SetWindowSubclass(
|
commctrl::SetWindowSubclass(
|
||||||
|
@ -601,6 +642,68 @@ 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 0 == winuser::PeekMessageW(
|
||||||
|
&mut msg,
|
||||||
|
redraw_window,
|
||||||
|
winuser::WM_PAINT,
|
||||||
|
winuser::WM_PAINT,
|
||||||
|
winuser::PM_REMOVE | winuser::PM_QS_PAINT,
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
winuser::TranslateMessage(&mut msg);
|
||||||
|
winuser::DispatchMessageW(&mut msg);
|
||||||
|
});
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn process_control_flow<T: 'static>(runner: &EventLoopRunner<T>) {
|
||||||
|
match runner.control_flow() {
|
||||||
|
ControlFlow::Poll => {
|
||||||
|
winuser::PostMessageW(runner.thread_msg_target(), *PROCESS_NEW_EVENTS_MSG_ID, 0, 0);
|
||||||
|
}
|
||||||
|
ControlFlow::Wait => (),
|
||||||
|
ControlFlow::WaitUntil(until) => {
|
||||||
|
winuser::PostThreadMessageW(
|
||||||
|
runner.wait_thread_id(),
|
||||||
|
*WAIT_UNTIL_MSG_ID,
|
||||||
|
0,
|
||||||
|
Box::into_raw(WaitUntilInstantBox::new(until)) as LPARAM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ControlFlow::Exit => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Emit a `ModifiersChanged` event whenever modifiers have changed.
|
/// Emit a `ModifiersChanged` event whenever modifiers have changed.
|
||||||
fn update_modifiers<T>(window: HWND, subclass_input: &SubclassInput<T>) {
|
fn update_modifiers<T>(window: HWND, subclass_input: &SubclassInput<T>) {
|
||||||
use crate::event::WindowEvent::ModifiersChanged;
|
use crate::event::WindowEvent::ModifiersChanged;
|
||||||
|
@ -639,20 +742,37 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
) -> LRESULT {
|
) -> LRESULT {
|
||||||
let subclass_input = &*(subclass_input_ptr as *const SubclassInput<T>);
|
let subclass_input = &*(subclass_input_ptr as *const SubclassInput<T>);
|
||||||
|
|
||||||
match msg {
|
winuser::RedrawWindow(
|
||||||
|
subclass_input.event_loop_runner.thread_msg_target(),
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
winuser::RDW_INTERNALPAINT,
|
||||||
|
);
|
||||||
|
|
||||||
|
// I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
|
||||||
|
// the closure to catch_unwind directly so that the match body indendation wouldn't change and
|
||||||
|
// the git blame and history would be preserved.
|
||||||
|
let callback = || match msg {
|
||||||
winuser::WM_ENTERSIZEMOVE => {
|
winuser::WM_ENTERSIZEMOVE => {
|
||||||
subclass_input.event_loop_runner.set_modal_loop(true);
|
subclass_input
|
||||||
|
.window_state
|
||||||
|
.lock()
|
||||||
|
.set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
winuser::WM_EXITSIZEMOVE => {
|
winuser::WM_EXITSIZEMOVE => {
|
||||||
subclass_input.event_loop_runner.set_modal_loop(false);
|
subclass_input
|
||||||
|
.window_state
|
||||||
|
.lock()
|
||||||
|
.set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE));
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
winuser::WM_NCCREATE => {
|
winuser::WM_NCCREATE => {
|
||||||
enable_non_client_dpi_scaling(window);
|
enable_non_client_dpi_scaling(window);
|
||||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
|
||||||
winuser::WM_NCLBUTTONDOWN => {
|
winuser::WM_NCLBUTTONDOWN => {
|
||||||
if wparam == winuser::HTCAPTION as _ {
|
if wparam == winuser::HTCAPTION as _ {
|
||||||
winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0);
|
winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0);
|
||||||
|
@ -676,6 +796,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
window_id: RootWindowId(WindowId(window)),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
event: Destroyed,
|
event: Destroyed,
|
||||||
});
|
});
|
||||||
|
subclass_input.event_loop_runner.remove_window(window);
|
||||||
|
|
||||||
drop(subclass_input);
|
drop(subclass_input);
|
||||||
Box::from_raw(subclass_input_ptr as *mut SubclassInput<T>);
|
Box::from_raw(subclass_input_ptr as *mut SubclassInput<T>);
|
||||||
|
@ -683,7 +804,25 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
}
|
}
|
||||||
|
|
||||||
winuser::WM_PAINT => {
|
winuser::WM_PAINT => {
|
||||||
subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
if subclass_input.event_loop_runner.should_buffer() {
|
||||||
|
// this branch can happen in response to `UpdateWindow`, if win32 decides to
|
||||||
|
// redraw the window outside the normal flow of the event loop.
|
||||||
|
winuser::RedrawWindow(
|
||||||
|
window,
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
winuser::RDW_INTERNALPAINT,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let managing_redraw =
|
||||||
|
flush_paint_messages(Some(window), &subclass_input.event_loop_runner);
|
||||||
|
subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
||||||
|
if managing_redraw {
|
||||||
|
subclass_input.event_loop_runner.redraw_events_cleared();
|
||||||
|
process_control_flow(&subclass_input.event_loop_runner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1583,11 +1722,19 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unset maximized if we're changing the window's size.
|
let dragging_window: bool;
|
||||||
if new_physical_inner_size != old_physical_inner_size {
|
|
||||||
WindowState::set_window_flags(subclass_input.window_state.lock(), window, |f| {
|
{
|
||||||
f.set(WindowFlags::MAXIMIZED, false)
|
let window_state = subclass_input.window_state.lock();
|
||||||
});
|
dragging_window = window_state
|
||||||
|
.window_flags()
|
||||||
|
.contains(WindowFlags::MARKER_IN_SIZE_MOVE);
|
||||||
|
// Unset maximized if we're changing the window's size.
|
||||||
|
if new_physical_inner_size != old_physical_inner_size {
|
||||||
|
WindowState::set_window_flags(window_state, window, |f| {
|
||||||
|
f.set(WindowFlags::MAXIMIZED, false)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_outer_rect: RECT;
|
let new_outer_rect: RECT;
|
||||||
|
@ -1612,9 +1759,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
)
|
)
|
||||||
.unwrap_or(conservative_rect);
|
.unwrap_or(conservative_rect);
|
||||||
|
|
||||||
// If we're not dragging the window, offset the window so that the cursor's
|
// If we're dragging the window, offset the window so that the cursor's
|
||||||
// relative horizontal position in the title bar is preserved.
|
// relative horizontal position in the title bar is preserved.
|
||||||
let dragging_window = subclass_input.event_loop_runner.in_modal_loop();
|
|
||||||
if dragging_window {
|
if dragging_window {
|
||||||
let bias = {
|
let bias = {
|
||||||
let cursor_pos = {
|
let cursor_pos = {
|
||||||
|
@ -1742,7 +1888,12 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
subclass_input
|
||||||
|
.event_loop_runner
|
||||||
|
.catch_unwind(callback)
|
||||||
|
.unwrap_or(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
|
@ -1754,7 +1905,21 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
subclass_input_ptr: DWORD_PTR,
|
subclass_input_ptr: DWORD_PTR,
|
||||||
) -> LRESULT {
|
) -> LRESULT {
|
||||||
let subclass_input = &mut *(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput<T>);
|
let subclass_input = &mut *(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput<T>);
|
||||||
match msg {
|
let runner = subclass_input.event_loop_runner.clone();
|
||||||
|
|
||||||
|
if msg != winuser::WM_PAINT {
|
||||||
|
winuser::RedrawWindow(
|
||||||
|
window,
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
winuser::RDW_INTERNALPAINT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing
|
||||||
|
// the closure to catch_unwind directly so that the match body indendation wouldn't change and
|
||||||
|
// the git blame and history would be preserved.
|
||||||
|
let callback = || match msg {
|
||||||
winuser::WM_DESTROY => {
|
winuser::WM_DESTROY => {
|
||||||
Box::from_raw(subclass_input);
|
Box::from_raw(subclass_input);
|
||||||
drop(subclass_input);
|
drop(subclass_input);
|
||||||
|
@ -1764,52 +1929,20 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
// when the event queue has been emptied. See `process_event` for more details.
|
// when the event queue has been emptied. See `process_event` for more details.
|
||||||
winuser::WM_PAINT => {
|
winuser::WM_PAINT => {
|
||||||
winuser::ValidateRect(window, ptr::null());
|
winuser::ValidateRect(window, ptr::null());
|
||||||
let queue_call_again = || {
|
// If the WM_PAINT handler in `public_window_callback` has already flushed the redraw
|
||||||
winuser::RedrawWindow(
|
// events, `handling_events` will return false and we won't emit a second
|
||||||
window,
|
// `RedrawEventsCleared` event.
|
||||||
ptr::null(),
|
if subclass_input.event_loop_runner.handling_events() {
|
||||||
ptr::null_mut(),
|
// This WM_PAINT handler will never be re-entrant because `flush_paint_messages`
|
||||||
winuser::RDW_INTERNALPAINT,
|
// doesn't call WM_PAINT for the thread event target (i.e. this window).
|
||||||
);
|
assert!(flush_paint_messages(
|
||||||
};
|
None,
|
||||||
let in_modal_loop = subclass_input.event_loop_runner.in_modal_loop();
|
&subclass_input.event_loop_runner
|
||||||
if in_modal_loop {
|
));
|
||||||
let runner = &subclass_input.event_loop_runner;
|
subclass_input.event_loop_runner.redraw_events_cleared();
|
||||||
runner.main_events_cleared();
|
process_control_flow(&subclass_input.event_loop_runner);
|
||||||
// Drain eventual WM_PAINT messages sent if user called request_redraw()
|
|
||||||
// during handling of MainEventsCleared.
|
|
||||||
let mut msg = mem::zeroed();
|
|
||||||
loop {
|
|
||||||
if 0 == winuser::PeekMessageW(
|
|
||||||
&mut msg,
|
|
||||||
ptr::null_mut(),
|
|
||||||
winuser::WM_PAINT,
|
|
||||||
winuser::WM_PAINT,
|
|
||||||
winuser::PM_QS_PAINT | winuser::PM_REMOVE,
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.hwnd != window {
|
|
||||||
winuser::TranslateMessage(&mut msg);
|
|
||||||
winuser::DispatchMessageW(&mut msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runner.redraw_events_cleared();
|
|
||||||
match runner.control_flow() {
|
|
||||||
// Waiting is handled by the modal loop.
|
|
||||||
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
|
|
||||||
ControlFlow::WaitUntil(resume_time) => {
|
|
||||||
wait_until_time_or_msg(resume_time);
|
|
||||||
runner.new_events();
|
|
||||||
queue_call_again();
|
|
||||||
}
|
|
||||||
ControlFlow::Poll => {
|
|
||||||
runner.new_events();
|
|
||||||
queue_call_again();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1940,6 +2073,49 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||||
function();
|
function();
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
_ if msg == *PROCESS_NEW_EVENTS_MSG_ID => {
|
||||||
|
winuser::PostThreadMessageW(
|
||||||
|
subclass_input.event_loop_runner.wait_thread_id(),
|
||||||
|
*CANCEL_WAIT_UNTIL_MSG_ID,
|
||||||
|
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) =
|
||||||
|
subclass_input.event_loop_runner.control_flow()
|
||||||
|
{
|
||||||
|
let mut msg = mem::zeroed();
|
||||||
|
while Instant::now() < wait_until {
|
||||||
|
if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
||||||
|
// 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 == winuser::WM_PAINT {
|
||||||
|
let mut rect = mem::zeroed();
|
||||||
|
if 0 == winuser::GetUpdateRect(msg.hwnd, &mut rect, 0) {
|
||||||
|
winuser::RedrawWindow(
|
||||||
|
msg.hwnd,
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
winuser::RDW_INTERNALPAINT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subclass_input.event_loop_runner.poll();
|
||||||
|
0
|
||||||
|
}
|
||||||
_ => commctrl::DefSubclassProc(window, msg, wparam, lparam),
|
_ => commctrl::DefSubclassProc(window, msg, wparam, lparam),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
runner.catch_unwind(callback).unwrap_or(-1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,378 @@
|
||||||
use std::{any::Any, cell::RefCell, collections::VecDeque, mem, panic, ptr, rc::Rc, time::Instant};
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::{HashSet, VecDeque},
|
||||||
|
mem, panic, ptr,
|
||||||
|
rc::Rc,
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
use winapi::{shared::windef::HWND, um::winuser};
|
use winapi::{
|
||||||
|
shared::{minwindef::DWORD, windef::HWND},
|
||||||
|
um::winuser,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, StartCause, WindowEvent},
|
event::{Event, StartCause, WindowEvent},
|
||||||
event_loop::ControlFlow,
|
event_loop::ControlFlow,
|
||||||
platform_impl::platform::event_loop::{util, EventLoop},
|
platform_impl::platform::util,
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) type EventLoopRunnerShared<T> = Rc<ELRShared<T>>;
|
pub(crate) type EventLoopRunnerShared<T> = Rc<EventLoopRunner<T>>;
|
||||||
pub(crate) struct ELRShared<T: 'static> {
|
pub(crate) struct EventLoopRunner<T: 'static> {
|
||||||
runner: RefCell<Option<EventLoopRunner<T>>>,
|
// The event loop's win32 handles
|
||||||
buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
thread_msg_target: HWND,
|
||||||
}
|
wait_thread_id: DWORD,
|
||||||
|
|
||||||
struct EventLoopRunner<T: 'static> {
|
control_flow: Cell<ControlFlow>,
|
||||||
control_flow: ControlFlow,
|
runner_state: Cell<RunnerState>,
|
||||||
runner_state: RunnerState,
|
last_events_cleared: Cell<Instant>,
|
||||||
modal_redraw_window: HWND,
|
|
||||||
in_modal_loop: bool,
|
event_handler: Cell<Option<Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>>>,
|
||||||
event_handler: Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
|
event_buffer: RefCell<VecDeque<BufferedEvent<T>>>,
|
||||||
panic_error: Option<PanicError>,
|
|
||||||
|
owned_windows: Cell<HashSet<HWND>>,
|
||||||
|
|
||||||
|
panic_error: Cell<Option<PanicError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PanicError = Box<dyn Any + Send + 'static>;
|
pub type PanicError = Box<dyn Any + Send + 'static>;
|
||||||
|
|
||||||
pub enum BufferedEvent<T: 'static> {
|
/// See `move_state_to` function for details on how the state loop works.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
enum RunnerState {
|
||||||
|
/// The event loop has just been created, and an `Init` event must be sent.
|
||||||
|
Uninitialized,
|
||||||
|
/// The event loop is idling.
|
||||||
|
Idle,
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BufferedEvent<T: 'static> {
|
||||||
Event(Event<'static, T>),
|
Event(Event<'static, T>),
|
||||||
ScaleFactorChanged(WindowId, f64, PhysicalSize<u32>),
|
ScaleFactorChanged(WindowId, f64, PhysicalSize<u32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopRunner<T> {
|
||||||
|
pub(crate) fn new(thread_msg_target: HWND, wait_thread_id: DWORD) -> EventLoopRunner<T> {
|
||||||
|
EventLoopRunner {
|
||||||
|
thread_msg_target,
|
||||||
|
wait_thread_id,
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn set_event_handler<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
let old_event_handler = self.event_handler.replace(mem::transmute::<
|
||||||
|
Option<Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>>,
|
||||||
|
Option<Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>>,
|
||||||
|
>(Some(Box::new(f))));
|
||||||
|
assert!(old_event_handler.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_runner(&self) {
|
||||||
|
let EventLoopRunner {
|
||||||
|
thread_msg_target: _,
|
||||||
|
wait_thread_id: _,
|
||||||
|
runner_state,
|
||||||
|
panic_error,
|
||||||
|
control_flow,
|
||||||
|
last_events_cleared: _,
|
||||||
|
event_handler,
|
||||||
|
event_buffer: _,
|
||||||
|
owned_windows: _,
|
||||||
|
} = self;
|
||||||
|
runner_state.set(RunnerState::Uninitialized);
|
||||||
|
panic_error.set(None);
|
||||||
|
control_flow.set(ControlFlow::Poll);
|
||||||
|
event_handler.set(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State retrieval functions.
|
||||||
|
impl<T> EventLoopRunner<T> {
|
||||||
|
pub fn thread_msg_target(&self) -> HWND {
|
||||||
|
self.thread_msg_target
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_thread_id(&self) -> DWORD {
|
||||||
|
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),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn control_flow(&self) -> ControlFlow {
|
||||||
|
self.control_flow.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handling_events(&self) -> bool {
|
||||||
|
self.runner_state.get() != RunnerState::Idle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_buffer(&self) -> bool {
|
||||||
|
let handler = self.event_handler.take();
|
||||||
|
let should_buffer = handler.is_none();
|
||||||
|
self.event_handler.set(handler);
|
||||||
|
should_buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Misc. functions
|
||||||
|
impl<T> EventLoopRunner<T> {
|
||||||
|
pub fn catch_unwind<R>(&self, f: impl FnOnce() -> R) -> Option<R> {
|
||||||
|
let panic_error = self.panic_error.take();
|
||||||
|
if panic_error.is_none() {
|
||||||
|
let result = panic::catch_unwind(panic::AssertUnwindSafe(f));
|
||||||
|
|
||||||
|
// Check to see if the panic error was set in a re-entrant call to catch_unwind inside
|
||||||
|
// of `f`. If it was, that error takes priority. If it wasn't, check if our call to
|
||||||
|
// catch_unwind caught any panics and set panic_error appropriately.
|
||||||
|
match self.panic_error.take() {
|
||||||
|
None => match result {
|
||||||
|
Ok(r) => Some(r),
|
||||||
|
Err(e) => {
|
||||||
|
self.panic_error.set(Some(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(e) => {
|
||||||
|
self.panic_error.set(Some(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.panic_error.set(panic_error);
|
||||||
|
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) {
|
||||||
|
self.move_state_to(RunnerState::HandlingMainEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe 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);
|
||||||
|
} 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.
|
||||||
|
self.event_buffer
|
||||||
|
.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 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()
|
||||||
|
.expect("either event handler is re-entrant (likely), or no event handler is registered (very unlikely)");
|
||||||
|
|
||||||
|
if control_flow != ControlFlow::Exit {
|
||||||
|
event_handler(event, &mut control_flow);
|
||||||
|
} else {
|
||||||
|
event_handler(event, &mut ControlFlow::Exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(self.event_handler.replace(Some(event_handler)).is_none());
|
||||||
|
self.control_flow.set(control_flow);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe 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
|
||||||
|
// of the loop's body and attempts to add events to the event buffer while in
|
||||||
|
// `process_event` will fail.
|
||||||
|
let buffered_event_opt = self.event_buffer.borrow_mut().pop_front();
|
||||||
|
match buffered_event_opt {
|
||||||
|
Some(e) => e.dispatch_event(|e| self.call_event_handler(e)),
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dispatch control flow events (`NewEvents`, `MainEventsCleared`, and `RedrawEventsCleared`) as
|
||||||
|
/// necessary to bring the internal `RunnerState` to the new runner state.
|
||||||
|
///
|
||||||
|
/// The state transitions are defined as follows:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// Uninitialized
|
||||||
|
/// |
|
||||||
|
/// V
|
||||||
|
/// HandlingMainEvents
|
||||||
|
/// ^ |
|
||||||
|
/// | V
|
||||||
|
/// Idle <--- HandlingRedrawEvents
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Attempting to transition back to `Uninitialized` will 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::{HandlingMainEvents, HandlingRedrawEvents, Idle, Uninitialized};
|
||||||
|
|
||||||
|
match (
|
||||||
|
self.runner_state.replace(new_runner_state),
|
||||||
|
new_runner_state,
|
||||||
|
) {
|
||||||
|
(Uninitialized, Uninitialized)
|
||||||
|
| (Idle, Idle)
|
||||||
|
| (HandlingMainEvents, HandlingMainEvents)
|
||||||
|
| (HandlingRedrawEvents, HandlingRedrawEvents) => (),
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
self.call_redraw_events_cleared();
|
||||||
|
}
|
||||||
|
(_, Uninitialized) => panic!("cannot move state to Uninitialized"),
|
||||||
|
|
||||||
|
// State transitions that start the event handling process.
|
||||||
|
(Idle, HandlingMainEvents) => {
|
||||||
|
self.call_new_events(false);
|
||||||
|
}
|
||||||
|
(Idle, HandlingRedrawEvents) => {
|
||||||
|
self.call_new_events(false);
|
||||||
|
self.call_event_handler(Event::MainEventsCleared);
|
||||||
|
}
|
||||||
|
|
||||||
|
(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call_new_events(&self, init: bool) {
|
||||||
|
let start_cause = match (init, self.control_flow()) {
|
||||||
|
(true, _) => StartCause::Init,
|
||||||
|
(false, ControlFlow::Poll) => StartCause::Poll,
|
||||||
|
(false, ControlFlow::Exit) | (false, ControlFlow::Wait) => StartCause::WaitCancelled {
|
||||||
|
requested_resume: None,
|
||||||
|
start: self.last_events_cleared.get(),
|
||||||
|
},
|
||||||
|
(false, ControlFlow::WaitUntil(requested_resume)) => {
|
||||||
|
if Instant::now() < requested_resume {
|
||||||
|
StartCause::WaitCancelled {
|
||||||
|
requested_resume: Some(requested_resume),
|
||||||
|
start: self.last_events_cleared.get(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StartCause::ResumeTimeReached {
|
||||||
|
requested_resume,
|
||||||
|
start: self.last_events_cleared.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.call_event_handler(Event::NewEvents(start_cause));
|
||||||
|
self.dispatch_buffered_events();
|
||||||
|
winuser::RedrawWindow(
|
||||||
|
self.thread_msg_target,
|
||||||
|
ptr::null(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
winuser::RDW_INTERNALPAINT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call_redraw_events_cleared(&self) {
|
||||||
|
self.call_event_handler(Event::RedrawEventsCleared);
|
||||||
|
self.last_events_cleared.set(Instant::now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> BufferedEvent<T> {
|
impl<T> BufferedEvent<T> {
|
||||||
pub fn from_event(event: Event<'_, T>) -> BufferedEvent<T> {
|
pub fn from_event(event: Event<'_, T>) -> BufferedEvent<T> {
|
||||||
match event {
|
match event {
|
||||||
|
@ -67,409 +408,3 @@ impl<T> BufferedEvent<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ELRShared<T> {
|
|
||||||
pub(crate) fn new() -> ELRShared<T> {
|
|
||||||
ELRShared {
|
|
||||||
runner: RefCell::new(None),
|
|
||||||
buffer: RefCell::new(VecDeque::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) unsafe fn set_runner<F>(&self, event_loop: &EventLoop<T>, f: F)
|
|
||||||
where
|
|
||||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
|
||||||
{
|
|
||||||
let mut runner = EventLoopRunner::new(event_loop, f);
|
|
||||||
{
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
// Dispatch any events that were buffered during the creation of the window
|
|
||||||
self.dispatch_buffered_events(&mut runner);
|
|
||||||
*runner_ref = Some(runner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn destroy_runner(&self) {
|
|
||||||
*self.runner.borrow_mut() = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new_events(&self) {
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.new_events();
|
|
||||||
// Dispatch any events that were buffered during the call `new_events`
|
|
||||||
self.dispatch_buffered_events(runner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn send_event(&self, event: Event<'_, T>) {
|
|
||||||
if let Err(event) = self.send_event_unbuffered(event) {
|
|
||||||
// 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.
|
|
||||||
if let Event::RedrawRequested(_) = event {
|
|
||||||
panic!("buffering RedrawRequested event");
|
|
||||||
}
|
|
||||||
self.buffer
|
|
||||||
.borrow_mut()
|
|
||||||
.push_back(BufferedEvent::from_event(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_event_unbuffered<'e>(&self, event: Event<'e, T>) -> Result<(), Event<'e, T>> {
|
|
||||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.process_event(event);
|
|
||||||
// Dispatch any events that were buffered during the call to `process_event`.
|
|
||||||
self.dispatch_buffered_events(runner);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_buffered_events(&self, runner: &mut EventLoopRunner<T>) {
|
|
||||||
// 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
|
|
||||||
// of the loop's body and attempts to add events to the event buffer while in
|
|
||||||
// `process_event` will fail.
|
|
||||||
loop {
|
|
||||||
let buffered_event_opt = self.buffer.borrow_mut().pop_front();
|
|
||||||
match buffered_event_opt {
|
|
||||||
Some(e) => e.dispatch_event(|e| runner.process_event(e)),
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn main_events_cleared(&self) {
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.main_events_cleared();
|
|
||||||
if !self.buffer.borrow().is_empty() {
|
|
||||||
warn!("Buffered events while dispatching MainEventsCleared");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn redraw_events_cleared(&self) {
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.redraw_events_cleared();
|
|
||||||
if !self.buffer.borrow().is_empty() {
|
|
||||||
warn!("Buffered events while dispatching RedrawEventsCleared");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn destroy_loop(&self) {
|
|
||||||
if let Ok(mut runner_ref) = self.runner.try_borrow_mut() {
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.call_event_handler(Event::LoopDestroyed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn take_panic_error(&self) -> Result<(), PanicError> {
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.take_panic_error()
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_modal_loop(&self, in_modal_loop: bool) {
|
|
||||||
let mut runner_ref = self.runner.borrow_mut();
|
|
||||||
if let Some(ref mut runner) = *runner_ref {
|
|
||||||
runner.in_modal_loop = in_modal_loop;
|
|
||||||
if in_modal_loop {
|
|
||||||
// jumpstart the modal loop
|
|
||||||
unsafe {
|
|
||||||
winuser::RedrawWindow(
|
|
||||||
runner.modal_redraw_window,
|
|
||||||
ptr::null(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
winuser::RDW_INTERNALPAINT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn in_modal_loop(&self) -> bool {
|
|
||||||
let runner = self.runner.borrow();
|
|
||||||
if let Some(ref runner) = *runner {
|
|
||||||
runner.in_modal_loop
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn control_flow(&self) -> ControlFlow {
|
|
||||||
let runner_ref = self.runner.borrow();
|
|
||||||
if let Some(ref runner) = *runner_ref {
|
|
||||||
runner.control_flow
|
|
||||||
} else {
|
|
||||||
ControlFlow::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum RunnerState {
|
|
||||||
/// The event loop has just been created, and an `Init` event must be sent.
|
|
||||||
New,
|
|
||||||
/// The event loop is idling, and began idling at the given instant.
|
|
||||||
Idle(Instant),
|
|
||||||
/// The event loop has received a signal from the OS that the loop may resume, but no winit
|
|
||||||
/// events have been generated yet. We're waiting for an event to be processed or the events
|
|
||||||
/// to be marked as cleared to send `NewEvents`, depending on the current `ControlFlow`.
|
|
||||||
DeferredNewEvents(Instant),
|
|
||||||
/// 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.
|
|
||||||
HandlingEvents,
|
|
||||||
/// The event loop is handling the redraw events and sending them to the user's callback.
|
|
||||||
/// `MainEventsCleared` has been sent, and `RedrawEventsCleared` hasn't.
|
|
||||||
HandlingRedraw,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EventLoopRunner<T> {
|
|
||||||
unsafe fn new<F>(event_loop: &EventLoop<T>, f: F) -> EventLoopRunner<T>
|
|
||||||
where
|
|
||||||
F: FnMut(Event<'_, T>, &mut ControlFlow),
|
|
||||||
{
|
|
||||||
EventLoopRunner {
|
|
||||||
control_flow: ControlFlow::default(),
|
|
||||||
runner_state: RunnerState::New,
|
|
||||||
in_modal_loop: false,
|
|
||||||
modal_redraw_window: event_loop.window_target.p.thread_msg_target,
|
|
||||||
event_handler: mem::transmute::<
|
|
||||||
Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
|
|
||||||
Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>,
|
|
||||||
>(Box::new(f)),
|
|
||||||
panic_error: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_panic_error(&mut self) -> Result<(), PanicError> {
|
|
||||||
match self.panic_error.take() {
|
|
||||||
Some(err) => Err(err),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_events(&mut self) {
|
|
||||||
self.runner_state = match self.runner_state {
|
|
||||||
// If we're already handling events or have deferred `NewEvents`, we don't need to do
|
|
||||||
// do any processing.
|
|
||||||
RunnerState::HandlingEvents
|
|
||||||
| RunnerState::HandlingRedraw
|
|
||||||
| RunnerState::DeferredNewEvents(..) => self.runner_state,
|
|
||||||
|
|
||||||
// Send the `Init` `NewEvents` and immediately move into event processing.
|
|
||||||
RunnerState::New => {
|
|
||||||
self.call_event_handler(Event::NewEvents(StartCause::Init));
|
|
||||||
RunnerState::HandlingEvents
|
|
||||||
}
|
|
||||||
|
|
||||||
// When `NewEvents` gets sent after an idle depends on the control flow...
|
|
||||||
// Some `NewEvents` are deferred because not all Windows messages trigger an event_loop event.
|
|
||||||
// So we defer the `NewEvents` to when we actually process an event.
|
|
||||||
RunnerState::Idle(wait_start) => {
|
|
||||||
match self.control_flow {
|
|
||||||
// If we're polling, send `NewEvents` and immediately move into event processing.
|
|
||||||
ControlFlow::Poll => {
|
|
||||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
|
||||||
RunnerState::HandlingEvents
|
|
||||||
},
|
|
||||||
// If the user was waiting until a specific time, the `NewEvents` call gets sent
|
|
||||||
// at varying times depending on the current time.
|
|
||||||
ControlFlow::WaitUntil(resume_time) => {
|
|
||||||
match Instant::now() >= resume_time {
|
|
||||||
// If the current time is later than the requested resume time, we can tell the
|
|
||||||
// user that the resume time has been reached with `NewEvents` and immdiately move
|
|
||||||
// into event processing.
|
|
||||||
true => {
|
|
||||||
self.call_event_handler(Event::NewEvents(StartCause::ResumeTimeReached {
|
|
||||||
start: wait_start,
|
|
||||||
requested_resume: resume_time,
|
|
||||||
}));
|
|
||||||
RunnerState::HandlingEvents
|
|
||||||
},
|
|
||||||
// However, if the current time is EARLIER than the requested resume time, we
|
|
||||||
// don't want to send the `WaitCancelled` event until we know an event is being
|
|
||||||
// sent. Defer.
|
|
||||||
false => RunnerState::DeferredNewEvents(wait_start)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// If we're waiting, `NewEvents` doesn't get sent until winit gets an event, so
|
|
||||||
// we defer.
|
|
||||||
ControlFlow::Wait |
|
|
||||||
// `Exit` shouldn't really ever get sent here, but if it does do something somewhat sane.
|
|
||||||
ControlFlow::Exit => RunnerState::DeferredNewEvents(wait_start),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_event(&mut self, event: Event<'_, T>) {
|
|
||||||
// If we're in the modal loop, we need to have some mechanism for finding when the event
|
|
||||||
// queue has been cleared so we can call `events_cleared`. Windows doesn't give any utilities
|
|
||||||
// for doing this, but it DOES guarantee that WM_PAINT will only occur after input events have
|
|
||||||
// been processed. So, we send WM_PAINT to a dummy window which calls `events_cleared` when
|
|
||||||
// the events queue has been emptied.
|
|
||||||
if self.in_modal_loop {
|
|
||||||
unsafe {
|
|
||||||
winuser::RedrawWindow(
|
|
||||||
self.modal_redraw_window,
|
|
||||||
ptr::null(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
winuser::RDW_INTERNALPAINT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If new event processing has to be done (i.e. call NewEvents or defer), do it. If we're
|
|
||||||
// already in processing nothing happens with this call.
|
|
||||||
self.new_events();
|
|
||||||
|
|
||||||
// Now that an event has been received, we have to send any `NewEvents` calls that were
|
|
||||||
// deferred.
|
|
||||||
if let RunnerState::DeferredNewEvents(wait_start) = self.runner_state {
|
|
||||||
match self.control_flow {
|
|
||||||
ControlFlow::Exit | ControlFlow::Wait => {
|
|
||||||
self.call_event_handler(Event::NewEvents(StartCause::WaitCancelled {
|
|
||||||
start: wait_start,
|
|
||||||
requested_resume: None,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
ControlFlow::WaitUntil(resume_time) => {
|
|
||||||
let start_cause = match Instant::now() >= resume_time {
|
|
||||||
// If the current time is later than the requested resume time, the resume time
|
|
||||||
// has been reached.
|
|
||||||
true => StartCause::ResumeTimeReached {
|
|
||||||
start: wait_start,
|
|
||||||
requested_resume: resume_time,
|
|
||||||
},
|
|
||||||
// Otherwise, the requested resume time HASN'T been reached and we send a WaitCancelled.
|
|
||||||
false => StartCause::WaitCancelled {
|
|
||||||
start: wait_start,
|
|
||||||
requested_resume: Some(resume_time),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
self.call_event_handler(Event::NewEvents(start_cause));
|
|
||||||
}
|
|
||||||
// This can be reached if the control flow is changed to poll during a `RedrawRequested`
|
|
||||||
// that was sent after `MainEventsCleared`.
|
|
||||||
ControlFlow::Poll => self.call_event_handler(Event::NewEvents(StartCause::Poll)),
|
|
||||||
}
|
|
||||||
self.runner_state = RunnerState::HandlingEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self.runner_state, &event) {
|
|
||||||
(RunnerState::HandlingEvents, Event::RedrawRequested(window_id)) => {
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
self.runner_state = RunnerState::HandlingRedraw;
|
|
||||||
self.call_event_handler(Event::RedrawRequested(*window_id));
|
|
||||||
}
|
|
||||||
(RunnerState::HandlingRedraw, Event::RedrawRequested(window_id)) => {
|
|
||||||
self.call_event_handler(Event::RedrawRequested(*window_id));
|
|
||||||
}
|
|
||||||
(RunnerState::HandlingRedraw, _) => {
|
|
||||||
warn!(
|
|
||||||
"non-redraw event in redraw phase: {:?}",
|
|
||||||
event.map_nonuser_event::<()>().ok()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(_, _) => {
|
|
||||||
self.runner_state = RunnerState::HandlingEvents;
|
|
||||||
self.call_event_handler(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main_events_cleared(&mut self) {
|
|
||||||
match self.runner_state {
|
|
||||||
// If we were handling events, send the MainEventsCleared message.
|
|
||||||
RunnerState::HandlingEvents => {
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
self.runner_state = RunnerState::HandlingRedraw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We already cleared the main events, we don't have to do anything.
|
|
||||||
// This happens when process_events() processed a RedrawRequested event.
|
|
||||||
RunnerState::HandlingRedraw => {}
|
|
||||||
|
|
||||||
// If we *weren't* handling events, we don't have to do anything.
|
|
||||||
RunnerState::New | RunnerState::Idle(..) => (),
|
|
||||||
|
|
||||||
// Some control flows require a NewEvents call even if no events were received. This
|
|
||||||
// branch handles those.
|
|
||||||
RunnerState::DeferredNewEvents(wait_start) => {
|
|
||||||
match self.control_flow {
|
|
||||||
// If we had deferred a Poll, send the Poll NewEvents and MainEventsCleared.
|
|
||||||
ControlFlow::Poll => {
|
|
||||||
self.call_event_handler(Event::NewEvents(StartCause::Poll));
|
|
||||||
self.runner_state = RunnerState::HandlingEvents;
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
self.runner_state = RunnerState::HandlingRedraw;
|
|
||||||
}
|
|
||||||
// If we had deferred a WaitUntil and the resume time has since been reached,
|
|
||||||
// send the resume notification and MainEventsCleared event.
|
|
||||||
ControlFlow::WaitUntil(resume_time) => {
|
|
||||||
if Instant::now() >= resume_time {
|
|
||||||
self.call_event_handler(Event::NewEvents(
|
|
||||||
StartCause::ResumeTimeReached {
|
|
||||||
start: wait_start,
|
|
||||||
requested_resume: resume_time,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
self.runner_state = RunnerState::HandlingEvents;
|
|
||||||
self.call_event_handler(Event::MainEventsCleared);
|
|
||||||
self.runner_state = RunnerState::HandlingRedraw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we deferred a wait and no events were received, the user doesn't have to
|
|
||||||
// get an event.
|
|
||||||
ControlFlow::Wait | ControlFlow::Exit => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn redraw_events_cleared(&mut self) {
|
|
||||||
match self.runner_state {
|
|
||||||
// If we were handling redraws, send the RedrawEventsCleared message.
|
|
||||||
RunnerState::HandlingRedraw => {
|
|
||||||
self.call_event_handler(Event::RedrawEventsCleared);
|
|
||||||
self.runner_state = RunnerState::Idle(Instant::now());
|
|
||||||
}
|
|
||||||
// No event was processed, we don't have to do anything.
|
|
||||||
RunnerState::DeferredNewEvents(_) => (),
|
|
||||||
// Should not happen.
|
|
||||||
_ => warn!(
|
|
||||||
"unexpected state in redraw_events_cleared: {:?}",
|
|
||||||
self.runner_state
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_event_handler(&mut self, event: Event<'_, T>) {
|
|
||||||
if self.panic_error.is_none() {
|
|
||||||
let EventLoopRunner {
|
|
||||||
ref mut panic_error,
|
|
||||||
ref mut event_handler,
|
|
||||||
ref mut control_flow,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
*panic_error = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
|
||||||
if *control_flow != ControlFlow::Exit {
|
|
||||||
(*event_handler)(event, control_flow);
|
|
||||||
} else {
|
|
||||||
(*event_handler)(event, &mut ControlFlow::Exit);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -80,7 +80,9 @@ bitflags! {
|
||||||
/// window's state to match our stored state. This controls whether to accept those changes.
|
/// window's state to match our stored state. This controls whether to accept those changes.
|
||||||
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10;
|
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10;
|
||||||
|
|
||||||
const MINIMIZED = 1 << 11;
|
const MARKER_IN_SIZE_MOVE = 1 << 11;
|
||||||
|
|
||||||
|
const MINIMIZED = 1 << 12;
|
||||||
|
|
||||||
const FULLSCREEN_AND_MASK = !(
|
const FULLSCREEN_AND_MASK = !(
|
||||||
WindowFlags::DECORATIONS.bits |
|
WindowFlags::DECORATIONS.bits |
|
||||||
|
|
Loading…
Add table
Reference in a new issue