diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a7deac50c5f..e5a4d4fce8b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1,4 +1,17 @@ #![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; @@ -11,7 +24,6 @@ use std::{ mpsc::{self, Receiver, Sender}, Arc, }, - thread, time::{Duration, Instant}, }; use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; @@ -29,6 +41,7 @@ use winapi::{ }, }; +use self::runner::{ELRShared, EventLoopRunnerShared}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, @@ -44,7 +57,6 @@ use crate::{ }, window::{Fullscreen, WindowId as RootWindowId}, }; -use runner::{EventLoopRunner, EventLoopRunnerShared}; type GetPointerFrameInfoHistory = unsafe extern "system" fn( pointerId: UINT, @@ -148,17 +160,9 @@ impl EventLoop { pub fn new_dpi_unaware_any_thread() -> EventLoop { let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; - - 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()); + let runner_shared = Rc::new(ELRShared::new()); + let (thread_msg_target, thread_msg_sender) = + thread_event_target_window(runner_shared.clone()); raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target); EventLoop { @@ -196,39 +200,87 @@ impl EventLoop { self.window_target .p .runner_shared - .set_event_handler(move |event, control_flow| { + .set_runner(self, 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 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() { - runner.reset_runner(); + runner.destroy_runner(); panic::resume_unwind(payload); } - if runner.control_flow() == ControlFlow::Exit && !runner.handling_events() { - break 'main; + 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; + } + } + // 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 => (), } } } - unsafe { - runner.call_event_handler(Event::LoopDestroyed); - } - runner.reset_runner(); + runner.destroy_loop(); + runner.destroy_runner(); } pub fn create_proxy(&self) -> EventLoopProxy { @@ -264,83 +316,24 @@ fn main_thread_id() -> DWORD { unsafe { MAIN_THREAD_ID } } -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, +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( 0, - cur_thread_id as LPARAM, + ptr::null(), + dur2timeout(wait_until - now).saturating_sub(1), + winuser::QS_ALLEVENTS, + winuser::MWMO_INPUTAVAILABLE, ); - 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; + 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; } } } @@ -468,8 +461,6 @@ 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. @@ -486,29 +477,6 @@ 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 = { @@ -551,7 +519,7 @@ lazy_static! { }; } -fn create_event_target_window() -> HWND { +fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> (HWND, Sender) { unsafe { let window = winuser::CreateWindowExW( winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, @@ -575,15 +543,7 @@ fn create_event_target_window() -> HWND { // 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 { @@ -599,7 +559,7 @@ fn subclass_event_target_window( ); assert_eq!(subclass_result, 1); - tx + (window, tx) } } @@ -622,7 +582,6 @@ 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( @@ -642,68 +601,6 @@ 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; @@ -742,37 +639,20 @@ unsafe extern "system" fn public_window_callback( ) -> LRESULT { let subclass_input = &*(subclass_input_ptr as *const SubclassInput); - 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 { + match msg { winuser::WM_ENTERSIZEMOVE => { - subclass_input - .window_state - .lock() - .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE)); + subclass_input.event_loop_runner.set_modal_loop(true); 0 } - winuser::WM_EXITSIZEMOVE => { - subclass_input - .window_state - .lock() - .set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE)); + subclass_input.event_loop_runner.set_modal_loop(false); 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); @@ -796,7 +676,6 @@ 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); @@ -804,25 +683,7 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_PAINT => { - 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); - } - } - + subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window)))); commctrl::DefSubclassProc(window, msg, wparam, lparam) } @@ -1722,19 +1583,11 @@ unsafe extern "system" fn public_window_callback( }, }); - 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) - }); - } + // 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 new_outer_rect: RECT; @@ -1759,8 +1612,9 @@ unsafe extern "system" fn public_window_callback( ) .unwrap_or(conservative_rect); - // If we're dragging the window, offset the window so that the cursor's + // If we're not 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 = { @@ -1888,12 +1742,7 @@ 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( @@ -1905,21 +1754,7 @@ unsafe extern "system" fn thread_event_target_callback( subclass_input_ptr: DWORD_PTR, ) -> LRESULT { let subclass_input = &mut *(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput); - 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 { + match msg { winuser::WM_DESTROY => { Box::from_raw(subclass_input); drop(subclass_input); @@ -1929,20 +1764,52 @@ 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()); - // 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); - } + 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(); + } + } + } 0 } @@ -2073,49 +1940,6 @@ 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 258a40c08c2..e5c062b035a 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -1,410 +1,475 @@ -use std::{ - any::Any, - cell::{Cell, RefCell}, - collections::{HashSet, VecDeque}, - mem, panic, ptr, - rc::Rc, - time::Instant, -}; +use std::{any::Any, cell::RefCell, collections::VecDeque, mem, panic, ptr, rc::Rc, time::Instant}; -use winapi::{ - shared::{minwindef::DWORD, windef::HWND}, - um::winuser, -}; +use winapi::{shared::windef::HWND, um::winuser}; use crate::{ dpi::PhysicalSize, event::{Event, StartCause, WindowEvent}, event_loop::ControlFlow, - platform_impl::platform::util, + platform_impl::platform::event_loop::{util, EventLoop}, window::WindowId, }; -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>, +pub(crate) type EventLoopRunnerShared = Rc>; +pub(crate) struct ELRShared { + runner: RefCell>>, + buffer: RefCell>>, +} - panic_error: 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, } pub type PanicError = Box; -/// 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 { +pub enum BufferedEvent { Event(Event<'static, T>), ScaleFactorChanged(WindowId, f64, PhysicalSize), } -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()), +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()), } } - 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); + 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 _, + ); + } + } } } -/// State retrieval functions. -impl EventLoopRunner { - pub fn thread_msg_target(&self) -> HWND { - self.thread_msg_target +impl ELRShared { + pub(crate) fn new() -> ELRShared { + ELRShared { + runner: RefCell::new(None), + buffer: RefCell::new(VecDeque::new()), + } } - pub fn wait_thread_id(&self) -> DWORD { - self.wait_thread_id + 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 redrawing(&self) -> bool { - self.runner_state.get() == RunnerState::HandlingRedrawEvents + pub(crate) fn destroy_runner(&self) { + *self.runner.borrow_mut() = None; } - pub fn take_panic_error(&self) -> Result<(), PanicError> { - match self.panic_error.take() { - Some(err) => Err(err), - None => Ok(()), + 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 control_flow(&self) -> ControlFlow { - self.control_flow.get() + 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 handling_events(&self) -> bool { - self.runner_state.get() != RunnerState::Idle + 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 should_buffer(&self) -> bool { - let handler = self.event_handler.take(); - let should_buffer = handler.is_none(); - self.event_handler.set(handler); - should_buffer + 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, + } + } } -} -/// 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 - } + 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"); } - } 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(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 owned_windows(&self, mut f: impl FnMut(HWND)) { - let mut owned_windows = self.owned_windows.take(); - for hwnd in &owned_windows { - f(*hwnd); + 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); + } } - let new_owned_windows = self.owned_windows.take(); - owned_windows.extend(&new_owned_windows); - self.owned_windows.set(owned_windows); } -} -/// Event dispatch functions. -impl EventLoopRunner { - pub(crate) unsafe fn poll(&self) { - self.move_state_to(RunnerState::HandlingMainEvents); + 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) 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 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 main_events_cleared(&self) { - self.move_state_to(RunnerState::HandlingRedrawEvents); + 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 redraw_events_cleared(&self) { - self.move_state_to(RunnerState::Idle); + 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 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); - } +#[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, +} - assert!(self.event_handler.replace(Some(event_handler)).is_none()); - self.control_flow.set(control_flow); - }); +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, + } } - 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 take_panic_error(&mut self) -> Result<(), PanicError> { + match self.panic_error.take() { + Some(err) => Err(err), + None => Ok(()), } } - /// 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); + 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 } - (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(); + + // 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) => panic!("cannot move state to Uninitialized"), + }; + } - // State transitions that start the event handling process. - (Idle, HandlingMainEvents) => { - self.call_new_events(false); + 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, + ); } - (Idle, HandlingRedrawEvents) => { - self.call_new_events(false); - self.call_event_handler(Event::MainEventsCleared); + } + + // 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; + } - (HandlingMainEvents, HandlingRedrawEvents) => { + 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)); } - (HandlingMainEvents, Idle) => { - warn!("RedrawEventsCleared emitted without explicit MainEventsCleared"); - self.call_event_handler(Event::MainEventsCleared); - self.call_redraw_events_cleared(); + (RunnerState::HandlingRedraw, Event::RedrawRequested(window_id)) => { + self.call_event_handler(Event::RedrawRequested(*window_id)); } - - (HandlingRedrawEvents, Idle) => { - self.call_redraw_events_cleared(); + (RunnerState::HandlingRedraw, _) => { + warn!( + "non-redraw event in redraw phase: {:?}", + event.map_nonuser_event::<()>().ok() + ); } - (HandlingRedrawEvents, HandlingMainEvents) => { - warn!("NewEvents emitted without explicit RedrawEventsCleared"); - self.call_redraw_events_cleared(); - self.call_new_events(false); + (_, _) => { + self.runner_state = RunnerState::HandlingEvents; + self.call_event_handler(event); } } } - 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(), + 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; } - } else { - StartCause::ResumeTimeReached { - 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; + } } + // 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, - ); - } - - 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 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 + ), } } - 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 _, - ); - } + 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(); } } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 124090813c7..11794821ade 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -80,9 +80,7 @@ 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 MARKER_IN_SIZE_MOVE = 1 << 11; - - const MINIMIZED = 1 << 12; + const MINIMIZED = 1 << 11; const FULLSCREEN_AND_MASK = !( WindowFlags::DECORATIONS.bits |