diff --git a/CHANGELOG.md b/CHANGELOG.md index dc79991608..f9259fd55b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased +- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`. - On Windows, fix `WindowBuilder::with_maximized` being ignored. # 0.22.1 (2020-04-16) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index e5a4d4fce8..a7deac50c5 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1,17 +1,4 @@ #![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; @@ -24,6 +11,7 @@ use std::{ mpsc::{self, Receiver, Sender}, Arc, }, + thread, time::{Duration, Instant}, }; use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; @@ -41,7 +29,6 @@ use winapi::{ }, }; -use self::runner::{ELRShared, EventLoopRunnerShared}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, @@ -57,6 +44,7 @@ use crate::{ }, window::{Fullscreen, WindowId as RootWindowId}, }; +use runner::{EventLoopRunner, EventLoopRunnerShared}; type GetPointerFrameInfoHistory = unsafe extern "system" fn( pointerId: UINT, @@ -160,9 +148,17 @@ impl EventLoop { pub fn new_dpi_unaware_any_thread() -> EventLoop { let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; - let runner_shared = Rc::new(ELRShared::new()); - let (thread_msg_target, thread_msg_sender) = - thread_event_target_window(runner_shared.clone()); + + let thread_msg_target = create_event_target_window(); + + 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); EventLoop { @@ -200,87 +196,39 @@ impl EventLoop { self.window_target .p .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) - }) + }); } let runner = &self.window_target.p.runner_shared; unsafe { let mut msg = mem::zeroed(); - let mut unread_message_exists = false; + runner.poll(); 'main: loop { - if let Err(payload) = runner.take_panic_error() { - runner.destroy_runner(); - panic::resume_unwind(payload); + if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { + break 'main; } + winuser::TranslateMessage(&mut msg); + winuser::DispatchMessageW(&mut msg); - runner.new_events(); - loop { - 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; - } + if let Err(payload) = runner.take_panic_error() { + runner.reset_runner(); + panic::resume_unwind(payload); } - // 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 => (), + if runner.control_flow() == ControlFlow::Exit && !runner.handling_events() { + break 'main; } } } - runner.destroy_loop(); - runner.destroy_runner(); + unsafe { + runner.call_event_handler(Event::LoopDestroyed); + } + runner.reset_runner(); } pub fn create_proxy(&self) -> EventLoopProxy { @@ -316,24 +264,83 @@ fn main_thread_id() -> DWORD { unsafe { MAIN_THREAD_ID } } -unsafe fn wait_until_time_or_msg(wait_until: Instant) { - 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( +fn get_wait_thread_id() -> DWORD { + unsafe { + let mut msg = mem::zeroed(); + let result = winuser::GetMessageW( + &mut msg, + -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, - ptr::null(), - dur2timeout(wait_until - now).saturating_sub(1), - winuser::QS_ALLEVENTS, - winuser::MWMO_INPUTAVAILABLE, + cur_thread_id as LPARAM, ); - if resume_reason == winerror::WAIT_TIMEOUT { - let mut msg = mem::zeroed(); - while Instant::now() < wait_until { - if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) { - break; + 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 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 EventLoopProxy { } } +type WaitUntilInstantBox = Box; + lazy_static! { // Message sent by the `EventLoopProxy` when we want to wake up the thread. // WPARAM and LPARAM are unused. @@ -477,6 +486,29 @@ lazy_static! { 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` 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. // WPARAM and LPARAM are unused. pub static ref DESTROY_MSG_ID: u32 = { @@ -519,7 +551,7 @@ lazy_static! { }; } -fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> (HWND, Sender) { +fn create_event_target_window() -> HWND { unsafe { let window = winuser::CreateWindowExW( winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, @@ -543,7 +575,15 @@ fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> // the LAYERED style. (winuser::WS_VISIBLE | winuser::WS_POPUP) as _, ); + window + } +} +fn subclass_event_target_window( + window: HWND, + event_loop_runner: EventLoopRunnerShared, +) -> Sender { + unsafe { let (tx, rx) = mpsc::channel(); let subclass_input = ThreadMsgTargetSubclassInput { @@ -559,7 +599,7 @@ fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> ); 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 THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1; pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) { + subclass_input.event_loop_runner.register_window(window); let input_ptr = Box::into_raw(Box::new(subclass_input)); let subclass_result = unsafe { commctrl::SetWindowSubclass( @@ -601,6 +642,68 @@ fn normalize_pointer_pressure(pressure: u32) -> Option { } } +/// 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( + except: Option, + runner: &EventLoopRunner, +) -> 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(runner: &EventLoopRunner) { + 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. fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { use crate::event::WindowEvent::ModifiersChanged; @@ -639,20 +742,37 @@ unsafe extern "system" fn public_window_callback( ) -> LRESULT { let subclass_input = &*(subclass_input_ptr as *const SubclassInput); - 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 => { - 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 } + 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 } + winuser::WM_NCCREATE => { enable_non_client_dpi_scaling(window); commctrl::DefSubclassProc(window, msg, wparam, lparam) } - winuser::WM_NCLBUTTONDOWN => { if wparam == winuser::HTCAPTION as _ { winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0); @@ -676,6 +796,7 @@ unsafe extern "system" fn public_window_callback( window_id: RootWindowId(WindowId(window)), event: Destroyed, }); + subclass_input.event_loop_runner.remove_window(window); drop(subclass_input); Box::from_raw(subclass_input_ptr as *mut SubclassInput); @@ -683,7 +804,25 @@ unsafe extern "system" fn public_window_callback( } 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) } @@ -1583,11 +1722,19 @@ unsafe extern "system" fn public_window_callback( }, }); - // Unset maximized if we're changing the window's size. - 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 dragging_window: bool; + + { + 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; @@ -1612,9 +1759,8 @@ unsafe extern "system" fn public_window_callback( ) .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. - let dragging_window = subclass_input.event_loop_runner.in_modal_loop(); if dragging_window { let bias = { let cursor_pos = { @@ -1742,7 +1888,12 @@ unsafe extern "system" fn public_window_callback( 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( @@ -1754,7 +1905,21 @@ unsafe extern "system" fn thread_event_target_callback( subclass_input_ptr: DWORD_PTR, ) -> LRESULT { let subclass_input = &mut *(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput); - 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 => { Box::from_raw(subclass_input); drop(subclass_input); @@ -1764,52 +1929,20 @@ unsafe extern "system" fn thread_event_target_callback( // when the event queue has been emptied. See `process_event` for more details. winuser::WM_PAINT => { winuser::ValidateRect(window, ptr::null()); - let queue_call_again = || { - winuser::RedrawWindow( - window, - ptr::null(), - ptr::null_mut(), - winuser::RDW_INTERNALPAINT, - ); - }; - let in_modal_loop = subclass_input.event_loop_runner.in_modal_loop(); - if in_modal_loop { - let runner = &subclass_input.event_loop_runner; - runner.main_events_cleared(); - // 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(); - } - } + // 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 subclass_input.event_loop_runner.handling_events() { + // 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, + &subclass_input.event_loop_runner + )); + subclass_input.event_loop_runner.redraw_events_cleared(); + process_control_flow(&subclass_input.event_loop_runner); } + 0 } @@ -1940,6 +2073,49 @@ unsafe extern "system" fn thread_event_target_callback( function(); 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), - } + }; + + runner.catch_unwind(callback).unwrap_or(-1) } diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index e5c062b035..258a40c08c 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -1,475 +1,410 @@ -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::{ dpi::PhysicalSize, event::{Event, StartCause, WindowEvent}, event_loop::ControlFlow, - platform_impl::platform::event_loop::{util, EventLoop}, + platform_impl::platform::util, window::WindowId, }; -pub(crate) type EventLoopRunnerShared = Rc>; -pub(crate) struct ELRShared { - runner: RefCell>>, - buffer: RefCell>>, -} +pub(crate) type EventLoopRunnerShared = Rc>; +pub(crate) struct EventLoopRunner { + // The event loop's win32 handles + thread_msg_target: HWND, + wait_thread_id: DWORD, + + control_flow: Cell, + runner_state: Cell, + last_events_cleared: Cell, + + event_handler: Cell, &mut ControlFlow)>>>, + event_buffer: RefCell>>, + + owned_windows: Cell>, -struct EventLoopRunner { - control_flow: ControlFlow, - runner_state: RunnerState, - modal_redraw_window: HWND, - in_modal_loop: bool, - event_handler: Box, &mut ControlFlow)>, - panic_error: Option, + panic_error: Cell>, } pub type PanicError = Box; -pub enum BufferedEvent { +/// 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 { Event(Event<'static, T>), ScaleFactorChanged(WindowId, f64, PhysicalSize), } -impl BufferedEvent { - pub fn from_event(event: Event<'_, T>) -> BufferedEvent { - match event { - Event::WindowEvent { - event: - WindowEvent::ScaleFactorChanged { - scale_factor, - new_inner_size, - }, - window_id, - } => BufferedEvent::ScaleFactorChanged(window_id, scale_factor, *new_inner_size), - event => BufferedEvent::Event(event.to_static().unwrap()), +impl EventLoopRunner { + pub(crate) fn new(thread_msg_target: HWND, wait_thread_id: DWORD) -> EventLoopRunner { + 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 fn dispatch_event(self, dispatch: impl FnOnce(Event<'_, T>)) { - match self { - Self::Event(event) => dispatch(event), - Self::ScaleFactorChanged(window_id, scale_factor, mut new_inner_size) => { - dispatch(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); - util::set_inner_size_physical( - (window_id.0).0, - new_inner_size.width as _, - new_inner_size.height as _, - ); - } - } + pub(crate) unsafe fn set_event_handler(&self, f: F) + where + F: FnMut(Event<'_, T>, &mut ControlFlow), + { + let old_event_handler = self.event_handler.replace(mem::transmute::< + Option, &mut ControlFlow)>>, + Option, &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); } } -impl ELRShared { - pub(crate) fn new() -> ELRShared { - ELRShared { - runner: RefCell::new(None), - buffer: RefCell::new(VecDeque::new()), - } +/// State retrieval functions. +impl EventLoopRunner { + pub fn thread_msg_target(&self) -> HWND { + self.thread_msg_target } - pub(crate) unsafe fn set_runner(&self, event_loop: &EventLoop, 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 fn wait_thread_id(&self) -> DWORD { + self.wait_thread_id } - pub(crate) fn destroy_runner(&self) { - *self.runner.borrow_mut() = None; + pub fn redrawing(&self) -> bool { + self.runner_state.get() == RunnerState::HandlingRedrawEvents } - 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 fn take_panic_error(&self) -> Result<(), PanicError> { + match self.panic_error.take() { + Some(err) => Err(err), + None => Ok(()), } } - 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)); - } + pub fn control_flow(&self) -> ControlFlow { + self.control_flow.get() } - 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) + pub fn handling_events(&self) -> bool { + self.runner_state.get() != RunnerState::Idle } - fn dispatch_buffered_events(&self, runner: &mut EventLoopRunner) { - // 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 fn should_buffer(&self) -> bool { + let handler = self.event_handler.take(); + let should_buffer = handler.is_none(); + self.event_handler.set(handler); + should_buffer } +} - 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"); +/// Misc. functions +impl EventLoopRunner { + pub fn catch_unwind(&self, f: impl FnOnce() -> R) -> Option { + 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(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 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(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 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); } +} - 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(()) - } +/// Event dispatch functions. +impl EventLoopRunner { + pub(crate) unsafe fn poll(&self) { + self.move_state_to(RunnerState::HandlingMainEvents); } - 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) 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) fn in_modal_loop(&self) -> bool { - let runner = self.runner.borrow(); - if let Some(ref runner) = *runner { - runner.in_modal_loop - } else { - false - } + pub(crate) unsafe fn main_events_cleared(&self) { + self.move_state_to(RunnerState::HandlingRedrawEvents); } - 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 - } + pub(crate) unsafe fn redraw_events_cleared(&self) { + self.move_state_to(RunnerState::Idle); } -} -#[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, -} + 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)"); -impl EventLoopRunner { - unsafe fn new(event_loop: &EventLoop, f: F) -> EventLoopRunner - 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, &mut ControlFlow)>, - Box, &mut ControlFlow)>, - >(Box::new(f)), - panic_error: None, - } + 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); + }); } - fn take_panic_error(&mut self) -> Result<(), PanicError> { - match self.panic_error.take() { - Some(err) => Err(err), - None => Ok(()), + 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, + } } } - 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 + /// 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); } - - // 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), - } + (Uninitialized, HandlingRedrawEvents) => { + self.call_new_events(true); + self.call_event_handler(Event::MainEventsCleared); } - }; - } - - 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, - ); + (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"), - // 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)), + // 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); } - self.runner_state = RunnerState::HandlingEvents; - } - match (self.runner_state, &event) { - (RunnerState::HandlingEvents, Event::RedrawRequested(window_id)) => { + (HandlingMainEvents, HandlingRedrawEvents) => { 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)); + (HandlingMainEvents, Idle) => { + warn!("RedrawEventsCleared emitted without explicit MainEventsCleared"); + self.call_event_handler(Event::MainEventsCleared); + self.call_redraw_events_cleared(); } - (RunnerState::HandlingRedraw, _) => { - warn!( - "non-redraw event in redraw phase: {:?}", - event.map_nonuser_event::<()>().ok() - ); + + (HandlingRedrawEvents, Idle) => { + self.call_redraw_events_cleared(); } - (_, _) => { - self.runner_state = RunnerState::HandlingEvents; - self.call_event_handler(event); + (HandlingRedrawEvents, HandlingMainEvents) => { + warn!("NewEvents emitted without explicit RedrawEventsCleared"); + self.call_redraw_events_cleared(); + self.call_new_events(false); } } } - 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; + 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(), } - // 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; - } + } else { + StartCause::ResumeTimeReached { + requested_resume, + start: self.last_events_cleared.get(), } - // If we deferred a wait and no events were received, the user doesn't have to - // get an event. - ControlFlow::Wait | ControlFlow::Exit => (), } } - } + }; + 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, + ); } - 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 - ), + unsafe fn call_redraw_events_cleared(&self) { + self.call_event_handler(Event::RedrawEventsCleared); + self.last_events_cleared.set(Instant::now()); + } +} + +impl BufferedEvent { + pub fn from_event(event: Event<'_, T>) -> BufferedEvent { + match event { + Event::WindowEvent { + event: + WindowEvent::ScaleFactorChanged { + scale_factor, + new_inner_size, + }, + window_id, + } => BufferedEvent::ScaleFactorChanged(window_id, scale_factor, *new_inner_size), + event => BufferedEvent::Event(event.to_static().unwrap()), } } - 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(); + pub fn dispatch_event(self, dispatch: impl FnOnce(Event<'_, T>)) { + match self { + Self::Event(event) => dispatch(event), + Self::ScaleFactorChanged(window_id, scale_factor, mut new_inner_size) => { + dispatch(Event::WindowEvent { + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor, + new_inner_size: &mut new_inner_size, + }, + }); + util::set_inner_size_physical( + (window_id.0).0, + new_inner_size.width as _, + new_inner_size.height as _, + ); + } } } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 11794821ad..124090813c 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -80,7 +80,9 @@ bitflags! { /// window's state to match our stored state. This controls whether to accept those changes. 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 = !( WindowFlags::DECORATIONS.bits |