From e26b831f23702b1c5ecfe8071d96254cf5b35d9d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 1 Mar 2025 12:09:59 +0100 Subject: [PATCH] Windows: Use `ApplicationHandler` (#4141) * Make ActiveEventLoop a thin wrapper over EventLoopRunner * Use ApplicationHandler instead of Event --- src/platform_impl/windows/drop_handler.rs | 33 +- src/platform_impl/windows/event_loop.rs | 715 +++++++----------- .../windows/event_loop/runner.rs | 182 +++-- src/platform_impl/windows/window.rs | 28 +- 4 files changed, 391 insertions(+), 567 deletions(-) diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 94af20ec..4b32f699 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -13,18 +13,17 @@ use windows_sys::Win32::System::Ole::{CF_HDROP, DROPEFFECT_COPY, DROPEFFECT_NONE use windows_sys::Win32::UI::Shell::{DragFinish, DragQueryFileW, HDROP}; use crate::dpi::PhysicalPosition; -use crate::event::{Event, WindowEvent}; +use crate::event::WindowEvent; use crate::platform_impl::platform::definitions::{ IDataObject, IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknown, IUnknownVtbl, }; -use crate::window::WindowId; #[repr(C)] pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, - send_event: Box, + send_event: Box, cursor_effect: u32, valid: bool, /* If the currently hovered item is not valid there must not be any * `DragLeft` emitted */ @@ -36,7 +35,7 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub(crate) fn new(window: HWND, send_event: Box) -> FileDropHandler { + pub(crate) fn new(window: HWND, send_event: Box) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl }, refcount: AtomicUsize::new(1), @@ -92,10 +91,7 @@ impl FileDropHandler { let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; drop_handler.valid = hdrop.is_some(); if drop_handler.valid { - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: WindowEvent::DragEntered { paths, position }, - }); + (drop_handler.send_event)(WindowEvent::DragEntered { paths, position }); } drop_handler.cursor_effect = if drop_handler.valid { DROPEFFECT_COPY } else { DROPEFFECT_NONE }; @@ -119,10 +115,7 @@ impl FileDropHandler { ScreenToClient(drop_handler.window, &mut pt); } let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: WindowEvent::DragMoved { position }, - }); + (drop_handler.send_event)(WindowEvent::DragMoved { position }); } unsafe { *pdwEffect = drop_handler.cursor_effect; @@ -134,10 +127,7 @@ impl FileDropHandler { pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { let drop_handler = unsafe { Self::from_interface(this) }; if drop_handler.valid { - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: WindowEvent::DragLeft { position: None }, - }); + (drop_handler.send_event)(WindowEvent::DragLeft { position: None }); } S_OK @@ -159,10 +149,7 @@ impl FileDropHandler { let position = PhysicalPosition::new(pt.x as f64, pt.y as f64); let mut paths = Vec::new(); let hdrop = unsafe { Self::iterate_filenames(pDataObj, |path| paths.push(path)) }; - drop_handler.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(drop_handler.window as usize), - event: WindowEvent::DragDropped { paths, position }, - }); + (drop_handler.send_event)(WindowEvent::DragDropped { paths, position }); if let Some(hdrop) = hdrop { unsafe { DragFinish(hdrop); @@ -232,12 +219,6 @@ impl FileDropHandler { } } -impl FileDropHandlerData { - fn send_event(&self, event: Event) { - (self.send_event)(event); - } -} - impl Drop for FileDropHandler { fn drop(&mut self) { unsafe { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 6e94e6aa..3a5778c0 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -11,7 +11,6 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::time::{Duration, Instant}; use std::{mem, panic, ptr}; -use runner::EventLoopRunner; use windows_sys::Win32::Foundation::{ GetLastError, FALSE, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_FAILED, WPARAM, }; @@ -60,13 +59,15 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE, }; +pub(super) use self::runner::{Event, EventLoopRunner}; use super::window::set_skip_taskbar; use super::SelectedCursor; use crate::application::ApplicationHandler; use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::error::{EventLoopError, RequestError}; use crate::event::{ - Event, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent, + DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, + WindowEvent, }; use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, @@ -106,8 +107,9 @@ pub(crate) struct WindowData { } impl WindowData { - fn send_event(&self, event: Event) { - self.event_loop_runner.send_event(event); + fn send_window_event(&self, window: HWND, event: WindowEvent) { + let window_id = WindowId::from_raw(window as usize); + self.event_loop_runner.send_event(Event::Window { window_id, event }); } fn window_state_lock(&self) -> MutexGuard<'_, WindowState> { @@ -120,8 +122,12 @@ struct ThreadMsgTargetData { } impl ThreadMsgTargetData { - fn send_event(&self, event: Event) { - self.event_loop_runner.send_event(event); + fn send_wakeup(&self) { + self.event_loop_runner.send_event(Event::WakeUp); + } + + fn send_device_event(&self, device_id: DeviceId, event: DeviceEvent) { + self.event_loop_runner.send_event(Event::Device { device_id, event }); } } @@ -133,7 +139,7 @@ pub(crate) enum ProcResult { } pub struct EventLoop { - window_target: ActiveEventLoop, + runner: Rc, msg_hook: Option bool + 'static>>, // It is a timer used on timed waits. // It is created lazily in case if we have `ControlFlow::WaitUntil`. @@ -175,12 +181,6 @@ impl std::hash::Hash for PlatformSpecificEventLoopAttributes { } } -pub struct ActiveEventLoop { - thread_id: u32, - thread_msg_target: HWND, - pub(crate) runner_shared: Rc, -} - impl EventLoop { pub(crate) fn new( attributes: &mut PlatformSpecificEventLoopAttributes, @@ -202,7 +202,7 @@ impl EventLoop { let thread_msg_target = create_event_target_window(); - let runner_shared = Rc::new(EventLoopRunner::new(thread_msg_target)); + let runner_shared = Rc::new(EventLoopRunner::new(thread_id, thread_msg_target)); insert_event_target_window_data(thread_msg_target, runner_shared.clone()); raw_input::register_all_mice_and_keyboards_for_raw_input( @@ -211,14 +211,14 @@ impl EventLoop { ); Ok(EventLoop { - window_target: ActiveEventLoop { thread_id, thread_msg_target, runner_shared }, + runner: runner_shared, msg_hook: attributes.msg_hook.take(), high_resolution_timer: None, }) } pub fn window_target(&self) -> &dyn RootActiveEventLoop { - &self.window_target + ActiveEventLoop::from_ref(&self.runner) } pub fn run_app(mut self, app: A) -> Result<(), EventLoopError> { @@ -229,33 +229,10 @@ impl EventLoop { &mut self, mut app: A, ) -> Result<(), EventLoopError> { - self.window_target.clear_exit(); - { - let runner = &self.window_target.runner_shared; + self.runner.clear_exit(); - let event_loop_windows_ref = &self.window_target; - // # Safety - // We make sure to call runner.clear_event_handler() before - // returning - unsafe { - runner.set_event_handler(move |event| match event { - Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), - Event::WindowEvent { window_id, event } => { - app.window_event(event_loop_windows_ref, window_id, event) - }, - Event::DeviceEvent { device_id, event } => { - app.device_event(event_loop_windows_ref, device_id, event) - }, - Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), - Event::Suspended => app.suspended(event_loop_windows_ref), - Event::Resumed => app.resumed(event_loop_windows_ref), - Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), - Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), - Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), - }); - } - } + // SAFETY: The resetter is not leaked. + let _app_resetter = unsafe { self.runner.set_app(&mut app) }; let exit_code = loop { self.wait_for_messages(None); @@ -272,13 +249,9 @@ impl EventLoop { } }; - let runner = &self.window_target.runner_shared; - runner.loop_destroyed(); + self.runner.loop_destroyed(); - // # Safety - // We assume that this will effectively call `runner.clear_event_handler()` - // to meet the safety requirements for calling `runner.set_event_handler()` above. - runner.reset_runner(); + self.runner.reset_runner(); if exit_code == 0 { Ok(()) @@ -292,39 +265,10 @@ impl EventLoop { timeout: Option, mut app: A, ) -> PumpStatus { - { - let runner = &self.window_target.runner_shared; - let event_loop_windows_ref = &self.window_target; - // let user_event_receiver = &self.user_event_receiver; + // SAFETY: The resetter is not leaked. + let _app_resetter = unsafe { self.runner.set_app(&mut app) }; - // # Safety - // We make sure to call runner.clear_event_handler() before - // returning - // - // Note: we're currently assuming nothing can panic and unwind - // to leave the runner in an unsound state with an associated - // event handler. - unsafe { - runner.set_event_handler(move |event| match event { - Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause), - Event::WindowEvent { window_id, event } => { - app.window_event(event_loop_windows_ref, window_id, event) - }, - Event::DeviceEvent { device_id, event } => { - app.device_event(event_loop_windows_ref, device_id, event) - }, - Event::UserWakeUp => app.proxy_wake_up(event_loop_windows_ref), - Event::Suspended => app.suspended(event_loop_windows_ref), - Event::Resumed => app.resumed(event_loop_windows_ref), - Event::CreateSurfaces => app.can_create_surfaces(event_loop_windows_ref), - Event::AboutToWait => app.about_to_wait(event_loop_windows_ref), - Event::LoopExiting => app.exiting(event_loop_windows_ref), - Event::MemoryWarning => app.memory_warning(event_loop_windows_ref), - }); - - runner.wakeup(); - } - } + self.runner.wakeup(); if self.exit_code().is_none() { self.wait_for_messages(timeout); @@ -335,29 +279,17 @@ impl EventLoop { self.dispatch_peeked_messages(); } - let runner = &self.window_target.runner_shared; - - let status = if let Some(code) = runner.exit_code() { - runner.loop_destroyed(); + if let Some(code) = self.runner.exit_code() { + self.runner.loop_destroyed(); // Immediately reset the internal state for the loop to allow // the loop to be run more than once. - runner.reset_runner(); + self.runner.reset_runner(); PumpStatus::Exit(code) } else { - runner.prepare_wait(); + self.runner.prepare_wait(); PumpStatus::Continue - }; - - // We wait until we've checked for an exit status before clearing the - // application callback, in case we need to dispatch a LoopExiting event - // - // # Safety - // This pairs up with our call to `runner.set_event_handler` and ensures - // the application's callback can't be held beyond its lifetime. - runner.clear_event_handler(); - - status + } } /// Waits until new event messages arrive to be peeked. @@ -366,8 +298,6 @@ impl EventLoop { /// Parameter timeout is optional. This method would wait for the smaller timeout /// between the argument and a timeout from control flow. fn wait_for_messages(&mut self, timeout: Option) { - let runner = &self.window_target.runner_shared; - // We aim to be consistent with the MacOS backend which has a RunLoop // observer that will dispatch AboutToWait when about to wait for // events, and NewEvents after the RunLoop wakes up. @@ -377,22 +307,24 @@ impl EventLoop { // pending messages via `PeekMessage` until we come back to "wait" via // `MsgWaitForMultipleObjectsEx`. // - runner.prepare_wait(); - wait_for_messages_impl(&mut self.high_resolution_timer, runner.control_flow(), timeout); + self.runner.prepare_wait(); + wait_for_messages_impl( + &mut self.high_resolution_timer, + self.runner.control_flow(), + timeout, + ); // Before we potentially exit, make sure to consistently emit an event for the wake up - runner.wakeup(); + self.runner.wakeup(); } /// Dispatch all queued messages via `PeekMessageW` fn dispatch_peeked_messages(&mut self) { - let runner = &self.window_target.runner_shared; - // We generally want to continue dispatching all pending messages // but we also allow dispatching to be interrupted as a means to // ensure the `pump_events` won't indefinitely block an external // event loop if there are too many pending events. This interrupt // flag will be set after dispatching `RedrawRequested` events. - runner.interrupt_msg_dispatch.set(false); + self.runner.interrupt_msg_dispatch.set(false); // # Safety // The Windows API has no documented requirement for bitwise @@ -418,52 +350,48 @@ impl EventLoop { } } - if let Err(payload) = runner.take_panic_error() { - runner.reset_runner(); + if let Err(payload) = self.runner.take_panic_error() { + self.runner.reset_runner(); panic::resume_unwind(payload); } - if let Some(_code) = runner.exit_code() { + if let Some(_code) = self.runner.exit_code() { break; } - if runner.interrupt_msg_dispatch.get() { + if self.runner.interrupt_msg_dispatch.get() { break; } } } fn exit_code(&self) -> Option { - self.window_target.exit_code() + self.runner.exit_code() } } impl Drop for EventLoop { fn drop(&mut self) { unsafe { - DestroyWindow(self.window_target.thread_msg_target); + DestroyWindow(self.runner.thread_msg_target); } } } +#[repr(transparent)] +pub(crate) struct ActiveEventLoop(pub Rc); + impl ActiveEventLoop { - #[inline(always)] - pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { - EventLoopThreadExecutor { thread_id: self.thread_id, target_window: self.thread_msg_target } - } - - pub(crate) fn clear_exit(&self) { - self.runner_shared.clear_exit(); - } - - fn exit_code(&self) -> Option { - self.runner_shared.exit_code() + fn from_ref(shared_runner: &Rc) -> &Self { + // SAFETY: `ActiveEventLoop` is `#[repr(transparent)]` over `Rc`. + // FIXME(madsmtm): Implement `ActiveEventLoop` for `Rc` directly. + unsafe { mem::transmute::<&Rc, &Self>(shared_runner) } } } impl RootActiveEventLoop for ActiveEventLoop { fn create_proxy(&self) -> RootEventLoopProxy { - let event_loop_proxy = EventLoopProxy { target_window: self.thread_msg_target }; + let event_loop_proxy = EventLoopProxy { target_window: self.0.thread_msg_target }; RootEventLoopProxy::new(Arc::new(event_loop_proxy)) } @@ -494,7 +422,7 @@ impl RootActiveEventLoop for ActiveEventLoop { } fn exiting(&self) -> bool { - self.runner_shared.exit_code().is_some() + self.0.exit_code().is_some() } fn system_theme(&self) -> Option { @@ -502,19 +430,19 @@ impl RootActiveEventLoop for ActiveEventLoop { } fn listen_device_events(&self, allowed: DeviceEvents) { - raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, allowed); + raw_input::register_all_mice_and_keyboards_for_raw_input(self.0.thread_msg_target, allowed); } fn set_control_flow(&self, control_flow: ControlFlow) { - self.runner_shared.set_control_flow(control_flow) + self.0.set_control_flow(control_flow) } fn control_flow(&self) -> ControlFlow { - self.runner_shared.control_flow() + self.0.control_flow() } fn exit(&self) { - self.runner_shared.set_exit_code(0) + self.0.set_exit_code(0) } fn owned_display_handle(&self) -> CoreOwnedDisplayHandle { @@ -994,10 +922,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { // Drop lock drop(window_state); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: ModifiersChanged(modifiers.into()), - }); + userdata.send_window_event(window, ModifiersChanged(modifiers.into())); } } @@ -1006,25 +931,16 @@ unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) { update_modifiers(window, userdata); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: Focused(true), - }); + userdata.send_window_event(window, Focused(true)); } unsafe fn lose_active_focus(window: HWND, userdata: &WindowData) { use crate::event::WindowEvent::{Focused, ModifiersChanged}; userdata.window_state_lock().modifiers_state = ModifiersState::empty(); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: ModifiersChanged(ModifiersState::empty().into()), - }); + userdata.send_window_event(window, ModifiersChanged(ModifiersState::empty().into())); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: Focused(false), - }); + userdata.send_window_event(window, Focused(false)); } /// Any window whose callback is configured to this function will have its events propagated @@ -1119,13 +1035,10 @@ unsafe fn public_window_callback_inner( let events = userdata.key_event_builder.process_message(window, msg, wparam, lparam, &mut result); for event in events { - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: KeyboardInput { - device_id: None, - event: event.event, - is_synthetic: event.is_synthetic, - }, + userdata.send_window_event(window, KeyboardInput { + device_id: None, + event: event.event, + is_synthetic: event.is_synthetic, }); } }; @@ -1204,20 +1117,14 @@ unsafe fn public_window_callback_inner( WM_CLOSE => { use crate::event::WindowEvent::CloseRequested; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: CloseRequested, - }); + userdata.send_window_event(window, CloseRequested); result = ProcResult::Value(0); }, WM_DESTROY => { use crate::event::WindowEvent::Destroyed; unsafe { RevokeDragDrop(window) }; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: Destroyed, - }); + userdata.send_window_event(window, Destroyed); result = ProcResult::Value(0); }, @@ -1235,10 +1142,7 @@ unsafe fn public_window_callback_inner( // window outside the normal flow of the event loop. This way mark event as handled // and request a normal redraw with `RedrawWindow`. if !userdata.event_loop_runner.should_buffer() { - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::RedrawRequested, - }); + userdata.send_window_event(window, WindowEvent::RedrawRequested); } // NOTE: calling `RedrawWindow` during `WM_PAINT` does nothing, since to mark @@ -1338,10 +1242,7 @@ unsafe fn public_window_callback_inner( if unsafe { (*windowpos).flags & SWP_NOMOVE != SWP_NOMOVE } { let physical_position = unsafe { PhysicalPosition::new((*windowpos).x, (*windowpos).y) }; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: Moved(physical_position), - }); + userdata.send_window_event(window, Moved(physical_position)); } // This is necessary for us to still get sent WM_SIZE. @@ -1354,10 +1255,6 @@ unsafe fn public_window_callback_inner( let h = super::hiword(lparam as u32) as u32; let physical_size = PhysicalSize::new(w, h); - let event = Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: SurfaceResized(physical_size), - }; { let mut w = userdata.window_state_lock(); @@ -1368,7 +1265,7 @@ unsafe fn public_window_callback_inner( w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized)); } } - userdata.send_event(event); + userdata.send_window_event(window, SurfaceResized(physical_size)); result = ProcResult::Value(0); }, @@ -1469,10 +1366,7 @@ unsafe fn public_window_callback_inner( if ime_allowed { userdata.window_state_lock().ime_state = ImeState::Enabled; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Enabled), - }); + userdata.send_window_event(window, WindowEvent::Ime(Ime::Enabled)); } result = ProcResult::DefWindowProc(wparam); @@ -1489,10 +1383,10 @@ unsafe fn public_window_callback_inner( let ime_context = unsafe { ImeContext::current(window) }; if lparam == 0 { - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - }); + userdata.send_window_event( + window, + WindowEvent::Ime(Ime::Preedit(String::new(), None)), + ); } // Google Japanese Input and ATOK have both flags, so @@ -1501,14 +1395,11 @@ unsafe fn public_window_callback_inner( if let Some(text) = unsafe { ime_context.get_composed_text() } { userdata.window_state_lock().ime_state = ImeState::Enabled; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - }); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Commit(text)), - }); + userdata.send_window_event( + window, + WindowEvent::Ime(Ime::Preedit(String::new(), None)), + ); + userdata.send_window_event(window, WindowEvent::Ime(Ime::Commit(text))); } } @@ -1520,10 +1411,10 @@ unsafe fn public_window_callback_inner( userdata.window_state_lock().ime_state = ImeState::Preedit; let cursor_range = first.map(|f| (f, last.unwrap_or(f))); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Preedit(text, cursor_range)), - }); + userdata.send_window_event( + window, + WindowEvent::Ime(Ime::Preedit(text, cursor_range)), + ); } } } @@ -1543,23 +1434,17 @@ unsafe fn public_window_callback_inner( // trying receiving composing result and commit if exists. let ime_context = unsafe { ImeContext::current(window) }; if let Some(text) = unsafe { ime_context.get_composed_text() } { - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - }); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Commit(text)), - }); + userdata.send_window_event( + window, + WindowEvent::Ime(Ime::Preedit(String::new(), None)), + ); + userdata.send_window_event(window, WindowEvent::Ime(Ime::Commit(text))); } } userdata.window_state_lock().ime_state = ImeState::Disabled; - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::Ime(Ime::Disabled), - }); + userdata.send_window_event(window, WindowEvent::Ime(Ime::Disabled)); } result = ProcResult::DefWindowProc(wparam); @@ -1615,14 +1500,11 @@ unsafe fn public_window_callback_inner( .ok(); drop(w); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerEntered { - device_id: None, - primary: true, - position, - kind: PointerKind::Mouse, - }, + userdata.send_window_event(window, PointerEntered { + device_id: None, + primary: true, + position, + kind: PointerKind::Mouse, }); // Calling TrackMouseEvent in order to receive mouse leave events. @@ -1641,14 +1523,11 @@ unsafe fn public_window_callback_inner( .ok(); drop(w); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerLeft { - device_id: None, - primary: true, - position: Some(position), - kind: PointerKind::Mouse, - }, + userdata.send_window_event(window, PointerLeft { + device_id: None, + primary: true, + position: Some(position), + kind: PointerKind::Mouse, }); }, PointerMoveKind::None => drop(w), @@ -1665,14 +1544,11 @@ unsafe fn public_window_callback_inner( if cursor_moved { update_modifiers(window, userdata); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerMoved { - device_id: None, - primary: true, - position, - source: PointerSource::Mouse, - }, + userdata.send_window_event(window, PointerMoved { + device_id: None, + primary: true, + position, + source: PointerSource::Mouse, }); } @@ -1688,9 +1564,11 @@ unsafe fn public_window_callback_inner( w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok(); } - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerLeft { device_id: None, primary: true, position: None, kind: Mouse }, + userdata.send_window_event(window, PointerLeft { + device_id: None, + primary: true, + position: None, + kind: Mouse, }); result = ProcResult::Value(0); @@ -1704,13 +1582,10 @@ unsafe fn public_window_callback_inner( update_modifiers(window, userdata); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::MouseWheel { - device_id: None, - delta: LineDelta(0.0, value), - phase: TouchPhase::Moved, - }, + userdata.send_window_event(window, WindowEvent::MouseWheel { + device_id: None, + delta: LineDelta(0.0, value), + phase: TouchPhase::Moved, }); result = ProcResult::Value(0); @@ -1724,13 +1599,10 @@ unsafe fn public_window_callback_inner( update_modifiers(window, userdata); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::MouseWheel { - device_id: None, - delta: LineDelta(value, 0.0), - phase: TouchPhase::Moved, - }, + userdata.send_window_event(window, WindowEvent::MouseWheel { + device_id: None, + delta: LineDelta(value, 0.0), + phase: TouchPhase::Moved, }); result = ProcResult::Value(0); @@ -1763,15 +1635,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Pressed, - position, - button: Left.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Pressed, + position, + button: Left.into(), }); result = ProcResult::Value(0); }, @@ -1789,15 +1658,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Released, - position, - button: Left.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Released, + position, + button: Left.into(), }); result = ProcResult::Value(0); }, @@ -1815,15 +1681,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Pressed, - position, - button: Right.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Pressed, + position, + button: Right.into(), }); result = ProcResult::Value(0); }, @@ -1841,15 +1704,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Released, - position, - button: Right.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Released, + position, + button: Right.into(), }); result = ProcResult::Value(0); }, @@ -1867,15 +1727,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Pressed, - position, - button: Middle.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Pressed, + position, + button: Middle.into(), }); result = ProcResult::Value(0); }, @@ -1893,15 +1750,12 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Released, - position, - button: Middle.into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Released, + position, + button: Middle.into(), }); result = ProcResult::Value(0); }, @@ -1920,20 +1774,17 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Pressed, - position, - button: match xbutton { - 1 => Back, - 2 => Forward, - _ => Other(xbutton), - } - .into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Pressed, + position, + button: match xbutton { + 1 => Back, + 2 => Forward, + _ => Other(xbutton), + } + .into(), }); result = ProcResult::Value(0); }, @@ -1952,20 +1803,17 @@ unsafe fn public_window_callback_inner( let y = super::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: PointerButton { - device_id: None, - primary: true, - state: Released, - position, - button: match xbutton { - 1 => Back, - 2 => Forward, - _ => Other(xbutton), - } - .into(), - }, + userdata.send_window_event(window, PointerButton { + device_id: None, + primary: true, + state: Released, + position, + button: match xbutton { + 1 => Back, + 2 => Forward, + _ => Other(xbutton), + } + .into(), }); result = ProcResult::Value(0); }, @@ -2009,59 +1857,43 @@ unsafe fn public_window_callback_inner( let y = position.y as f64 + (input.y % 100) as f64 / 100f64; let position = PhysicalPosition::new(x, y); - let window_id = WindowId::from_raw(window as usize); let finger_id = FingerId::from_raw(input.dwID as usize); let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY); if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerEntered { - device_id: None, - primary, - position, - kind: PointerKind::Touch(finger_id), - }, + userdata.send_window_event(window, WindowEvent::PointerEntered { + device_id: None, + primary, + position, + kind: PointerKind::Touch(finger_id), }); - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerButton { - device_id: None, - primary, - state: Pressed, - position, - button: Touch { finger_id, force: None }, - }, + userdata.send_window_event(window, WindowEvent::PointerButton { + device_id: None, + primary, + state: Pressed, + position, + button: Touch { finger_id, force: None }, }); } else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerButton { - device_id: None, - primary, - state: Released, - position, - button: Touch { finger_id, force: None }, - }, + userdata.send_window_event(window, WindowEvent::PointerButton { + device_id: None, + primary, + state: Released, + position, + button: Touch { finger_id, force: None }, }); - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerLeft { - device_id: None, - primary, - position: Some(position), - kind: PointerKind::Touch(finger_id), - }, + userdata.send_window_event(window, WindowEvent::PointerLeft { + device_id: None, + primary, + position: Some(position), + kind: PointerKind::Touch(finger_id), }); } else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerMoved { - device_id: None, - primary, - position, - source: PointerSource::Touch { finger_id, force: None }, - }, + userdata.send_window_event(window, WindowEvent::PointerMoved { + device_id: None, + primary, + position, + source: PointerSource::Touch { finger_id, force: None }, }); } else { continue; @@ -2180,78 +2012,62 @@ unsafe fn public_window_callback_inner( let y = location.y as f64 + y.fract(); let position = PhysicalPosition::new(x, y); - let window_id = WindowId::from_raw(window as usize); let finger_id = FingerId::from_raw(pointer_info.pointerId as usize); let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY); if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerEntered { - device_id: None, - primary, - position, - kind: if let PT_TOUCH = pointer_info.pointerType { - PointerKind::Touch(finger_id) - } else { - PointerKind::Unknown - }, + userdata.send_window_event(window, WindowEvent::PointerEntered { + device_id: None, + primary, + position, + kind: if let PT_TOUCH = pointer_info.pointerType { + PointerKind::Touch(finger_id) + } else { + PointerKind::Unknown }, }); - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerButton { - device_id: None, - primary, - state: Pressed, - position, - button: if let PT_TOUCH = pointer_info.pointerType { - ButtonSource::Touch { finger_id, force } - } else { - ButtonSource::Unknown(0) - }, + userdata.send_window_event(window, WindowEvent::PointerButton { + device_id: None, + primary, + state: Pressed, + position, + button: if let PT_TOUCH = pointer_info.pointerType { + ButtonSource::Touch { finger_id, force } + } else { + ButtonSource::Unknown(0) }, }); } else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerButton { - device_id: None, - primary, - state: Released, - position, - button: if let PT_TOUCH = pointer_info.pointerType { - ButtonSource::Touch { finger_id, force } - } else { - ButtonSource::Unknown(0) - }, + userdata.send_window_event(window, WindowEvent::PointerButton { + device_id: None, + primary, + state: Released, + position, + button: if let PT_TOUCH = pointer_info.pointerType { + ButtonSource::Touch { finger_id, force } + } else { + ButtonSource::Unknown(0) }, }); - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerLeft { - device_id: None, - primary, - position: Some(position), - kind: if let PT_TOUCH = pointer_info.pointerType { - PointerKind::Touch(finger_id) - } else { - PointerKind::Unknown - }, + userdata.send_window_event(window, WindowEvent::PointerLeft { + device_id: None, + primary, + position: Some(position), + kind: if let PT_TOUCH = pointer_info.pointerType { + PointerKind::Touch(finger_id) + } else { + PointerKind::Unknown }, }); } else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE) { - userdata.send_event(Event::WindowEvent { - window_id, - event: WindowEvent::PointerMoved { - device_id: None, - primary, - position, - source: if let PT_TOUCH = pointer_info.pointerType { - PointerSource::Touch { finger_id, force } - } else { - PointerSource::Unknown - }, + userdata.send_window_event(window, WindowEvent::PointerMoved { + device_id: None, + primary, + position, + source: if let PT_TOUCH = pointer_info.pointerType { + PointerSource::Touch { finger_id, force } + } else { + PointerSource::Unknown }, }); } else { @@ -2415,12 +2231,9 @@ unsafe fn public_window_callback_inner( }; let new_surface_size = Arc::new(Mutex::new(new_physical_surface_size)); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: ScaleFactorChanged { - scale_factor: new_scale_factor, - surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), - }, + userdata.send_window_event(window, ScaleFactorChanged { + scale_factor: new_scale_factor, + surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), }); let new_physical_surface_size = *new_surface_size.lock().unwrap(); @@ -2567,10 +2380,7 @@ unsafe fn public_window_callback_inner( if window_state.current_theme != new_theme { window_state.current_theme = new_theme; drop(window_state); - userdata.send_event(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: ThemeChanged(new_theme), - }); + userdata.send_window_event(window, ThemeChanged(new_theme)); } } result = ProcResult::DefWindowProc(wparam); @@ -2657,7 +2467,7 @@ unsafe extern "system" fn thread_event_target_callback( // user event is still in the mpsc channel and will be pulled // once the placeholder event is delivered to the wrapper // `event_handler` - userdata.send_event(Event::UserWakeUp); + userdata.send_wakeup(); 0 }, _ if msg == EXEC_MSG_ID.get() => { @@ -2682,7 +2492,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { use crate::event::ElementState::{Pressed, Released}; use crate::event::MouseScrollDelta::LineDelta; - let device_id = Some(wrap_device_id(data.header.hDevice as _)); + let device_id = wrap_device_id(data.header.hDevice as _); if data.header.dwType == RIM_TYPEMOUSE { let mouse = unsafe { data.data.mouse }; @@ -2692,10 +2502,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { let y = mouse.lLastY as f64; if x != 0.0 || y != 0.0 { - userdata.send_event(Event::DeviceEvent { - device_id, - event: PointerMotion { delta: (x, y) }, - }); + userdata.send_device_event(device_id, PointerMotion { delta: (x, y) }); } } @@ -2703,27 +2510,18 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { if util::has_flag(button_flags as u32, RI_MOUSE_WHEEL) { let button_data = unsafe { mouse.Anonymous.Anonymous.usButtonData } as i16; let delta = button_data as f32 / WHEEL_DELTA as f32; - userdata.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { delta: LineDelta(0.0, delta) }, - }); + userdata.send_device_event(device_id, MouseWheel { delta: LineDelta(0.0, delta) }); } if util::has_flag(button_flags as u32, RI_MOUSE_HWHEEL) { let button_data = unsafe { mouse.Anonymous.Anonymous.usButtonData } as i16; let delta = -button_data as f32 / WHEEL_DELTA as f32; - userdata.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { delta: LineDelta(delta, 0.0) }, - }); + userdata.send_device_event(device_id, MouseWheel { delta: LineDelta(delta, 0.0) }); } let button_state = raw_input::get_raw_mouse_button_state(button_flags as u32); for (button, state) in button_state.iter().enumerate() { if let Some(state) = *state { - userdata.send_event(Event::DeviceEvent { - device_id, - event: Button { button: button as _, state }, - }); + userdata.send_device_event(device_id, Button { button: button as _, state }); } } } else if data.header.dwType == RIM_TYPEKEYBOARD { @@ -2739,10 +2537,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { if let Some(physical_key) = raw_input::get_keyboard_physical_key(keyboard) { let state = if pressed { Pressed } else { Released }; - userdata.send_event(Event::DeviceEvent { - device_id, - event: Key(RawKeyEvent { physical_key, state }), - }); + userdata.send_device_event(device_id, Key(RawKeyEvent { physical_key, state })); } } } diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 3b712167..e6183d6d 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -1,22 +1,27 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::VecDeque; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::Instant; use std::{mem, panic}; use windows_sys::Win32::Foundation::HWND; -use super::ControlFlow; +use super::{ActiveEventLoop, ControlFlow, EventLoopThreadExecutor}; +use crate::application::ApplicationHandler; use crate::dpi::PhysicalSize; -use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent}; +use crate::event::{DeviceEvent, DeviceId, StartCause, SurfaceSizeWriter, WindowEvent}; +use crate::event_loop::ActiveEventLoop as RootActiveEventLoop; use crate::platform_impl::platform::event_loop::{WindowData, GWL_USERDATA}; use crate::platform_impl::platform::get_window_long; use crate::window::WindowId; -type EventHandler = Cell>>; +type EventHandler = Cell>; pub(crate) struct EventLoopRunner { + pub(super) thread_id: u32, + // The event loop's win32 handles pub(super) thread_msg_target: HWND, @@ -29,8 +34,8 @@ pub(crate) struct EventLoopRunner { exit: Cell>, runner_state: Cell, last_events_cleared: Cell, - event_handler: EventHandler, - event_buffer: RefCell>, + event_handler: Rc, + event_buffer: RefCell>, panic_error: Cell>, } @@ -51,14 +56,20 @@ pub(crate) enum RunnerState { Destroyed, } -enum BufferedEvent { - Event(Event), - ScaleFactorChanged(HWND, f64, PhysicalSize), +#[derive(Debug, Clone)] +pub(crate) enum Event { + Device { device_id: DeviceId, event: DeviceEvent }, + Window { window_id: WindowId, event: WindowEvent }, + BufferedScaleFactorChanged(HWND, f64, PhysicalSize), + // FIXME(madsmtm): Coalesce these into a flag (or similar) instead of handling them as events. + // https://github.com/rust-windowing/winit/pull/3687 + WakeUp, } impl EventLoopRunner { - pub(crate) fn new(thread_msg_target: HWND) -> EventLoopRunner { - EventLoopRunner { + pub(crate) fn new(thread_id: u32, thread_msg_target: HWND) -> Self { + Self { + thread_id, thread_msg_target, interrupt_msg_dispatch: Cell::new(false), runner_state: Cell::new(RunnerState::Uninitialized), @@ -66,40 +77,50 @@ impl EventLoopRunner { exit: Cell::new(None), panic_error: Cell::new(None), last_events_cleared: Cell::new(Instant::now()), - event_handler: Cell::new(None), + event_handler: Rc::new(Cell::new(None)), event_buffer: RefCell::new(VecDeque::new()), } } - /// Associate the application's event handler with the runner + /// Associate the application's event handler with the runner. /// /// # Safety - /// This is ignoring the lifetime of the application handler (which may not - /// outlive the EventLoopRunner) and can lead to undefined behaviour if - /// the handler is not cleared before the end of real lifetime. /// - /// All public APIs that take an event handler (`run`, `run_on_demand`, - /// `pump_events`) _must_ pair a call to `set_event_handler` with - /// a call to `clear_event_handler` before returning to avoid - /// undefined behaviour. - pub(crate) unsafe fn set_event_handler(&self, f: F) - where - F: FnMut(Event), - { - // Erase closure lifetime. - // SAFETY: Caller upholds that the lifetime of the closure is upheld. - let f = - unsafe { mem::transmute::, Box>(Box::new(f)) }; - let old_event_handler = self.event_handler.replace(Some(f)); - assert!(old_event_handler.is_none()); - } + /// The returned type must not be leaked (as that would allow the application to be associated + /// with the runner for too long). + pub(crate) unsafe fn set_app<'app>( + &self, + app: &'app mut (dyn ApplicationHandler + 'app), + ) -> impl Drop + 'app { + // Erase app lifetime, to allow storing on the event loop runner. + // + // SAFETY: Caller upholds that the lifetime of the closure is upheld, by not dropping the + // return type which resets it. + let f = unsafe { + mem::transmute::< + &'app mut (dyn ApplicationHandler + 'app), + &'static mut (dyn ApplicationHandler + 'static), + >(app) + }; - pub(crate) fn clear_event_handler(&self) { - self.event_handler.set(None); + let old_event_handler = self.event_handler.replace(Some(f)); + + assert!(old_event_handler.is_none()); + + struct Resetter(Rc); + + impl Drop for Resetter { + fn drop(&mut self) { + self.0.set(None); + } + } + + Resetter(self.event_handler.clone()) } pub(crate) fn reset_runner(&self) { - let EventLoopRunner { + let Self { + thread_id: _, thread_msg_target: _, interrupt_msg_dispatch, runner_state, @@ -188,21 +209,26 @@ impl EventLoopRunner { None } } + + #[inline(always)] + pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { + EventLoopThreadExecutor { thread_id: self.thread_id, target_window: self.thread_msg_target } + } } /// Event dispatch functions. impl EventLoopRunner { - pub(crate) fn prepare_wait(&self) { + pub(crate) fn prepare_wait(self: &Rc) { self.move_state_to(RunnerState::Idle); } - pub(crate) fn wakeup(&self) { + pub(crate) fn wakeup(self: &Rc) { self.move_state_to(RunnerState::HandlingMainEvents); } - pub(crate) fn send_event(&self, event: Event) { - if let Event::WindowEvent { event: WindowEvent::RedrawRequested, .. } = event { - self.call_event_handler(event); + pub(crate) fn send_event(self: &Rc, event: Event) { + if let Event::Window { event: WindowEvent::RedrawRequested, .. } = event { + self.call_event_handler(|app, event_loop| event.dispatch_event(app, event_loop)); // As a rule, to ensure that `pump_events` can't block an external event loop // for too long, we always guarantee that `pump_events` will return control to // the external loop asap after a `RedrawRequested` event is dispatched. @@ -210,31 +236,34 @@ impl EventLoopRunner { } 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)) + self.event_buffer.borrow_mut().push_back(event.buffer_scale_factor()) } else { - self.call_event_handler(event); + self.call_event_handler(|app, event_loop| event.dispatch_event(app, event_loop)); self.dispatch_buffered_events(); } } - pub(crate) fn loop_destroyed(&self) { + pub(crate) fn loop_destroyed(self: &Rc) { self.move_state_to(RunnerState::Destroyed); } - fn call_event_handler(&self, event: Event) { + fn call_event_handler( + self: &Rc, + closure: impl FnOnce(&mut dyn ApplicationHandler, &dyn RootActiveEventLoop), + ) { self.catch_unwind(|| { - let mut event_handler = self.event_handler.take().expect( + let event_handler = self.event_handler.take().expect( "either event handler is re-entrant (likely), or no event handler is registered \ (very unlikely)", ); - event_handler(event); + closure(event_handler, ActiveEventLoop::from_ref(self)); assert!(self.event_handler.replace(Some(event_handler)).is_none()); }); } - fn dispatch_buffered_events(&self) { + fn dispatch_buffered_events(self: &Rc) { 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 @@ -242,7 +271,9 @@ impl EventLoopRunner { // `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)), + Some(e) => { + self.call_event_handler(|app, event_loop| e.dispatch_event(app, event_loop)) + }, None => break, } } @@ -272,7 +303,7 @@ impl EventLoopRunner { /// 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. - fn move_state_to(&self, new_runner_state: RunnerState) { + fn move_state_to(self: &Rc, new_runner_state: RunnerState) { use RunnerState::{Destroyed, HandlingMainEvents, Idle, Uninitialized}; match (self.runner_state.replace(new_runner_state), new_runner_state) { @@ -287,14 +318,14 @@ impl EventLoopRunner { }, (Uninitialized, Idle) => { self.call_new_events(true); - self.call_event_handler(Event::AboutToWait); + self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); }, (Uninitialized, Destroyed) => { self.call_new_events(true); - self.call_event_handler(Event::AboutToWait); + self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); - self.call_event_handler(Event::LoopExiting); + self.call_event_handler(|app, event_loop| app.exiting(event_loop)); }, (_, Uninitialized) => panic!("cannot move state to Uninitialized"), @@ -303,25 +334,25 @@ impl EventLoopRunner { self.call_new_events(false); }, (Idle, Destroyed) => { - self.call_event_handler(Event::LoopExiting); + self.call_event_handler(|app, event_loop| app.exiting(event_loop)); }, (HandlingMainEvents, Idle) => { // This is always the last event we dispatch before waiting for new events - self.call_event_handler(Event::AboutToWait); + self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); }, (HandlingMainEvents, Destroyed) => { - self.call_event_handler(Event::AboutToWait); + self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); - self.call_event_handler(Event::LoopExiting); + self.call_event_handler(|app, event_loop| app.exiting(event_loop)); }, (Destroyed, _) => panic!("cannot move state from Destroyed"), } } - fn call_new_events(&self, init: bool) { + fn call_new_events(self: &Rc, init: bool) { let start_cause = match (init, self.control_flow(), self.exit.get()) { (true, ..) => StartCause::Init, (false, ControlFlow::Poll, None) => StartCause::Poll, @@ -343,45 +374,55 @@ impl EventLoopRunner { } }, }; - self.call_event_handler(Event::NewEvents(start_cause)); + self.call_event_handler(|app, event_loop| app.new_events(event_loop, start_cause)); // NB: For consistency all platforms must call `can_create_surfaces` even though Windows // applications don't themselves have a formal surface destroy/create lifecycle. if init { - self.call_event_handler(Event::CreateSurfaces); + self.call_event_handler(|app, event_loop| app.can_create_surfaces(event_loop)); } self.dispatch_buffered_events(); } } -impl BufferedEvent { - pub fn from_event(event: Event) -> BufferedEvent { - match event { - Event::WindowEvent { +impl Event { + /// Mark ScaleFactorChanged as being buffered (which forces us to re-handle when the user set a + /// new size). + pub fn buffer_scale_factor(self) -> Self { + match self { + Self::Window { event: WindowEvent::ScaleFactorChanged { scale_factor, surface_size_writer }, window_id, - } => BufferedEvent::ScaleFactorChanged( + } => Event::BufferedScaleFactorChanged( window_id.into_raw() as HWND, scale_factor, *surface_size_writer.new_surface_size.upgrade().unwrap().lock().unwrap(), ), - event => BufferedEvent::Event(event), + event => event, } } - pub fn dispatch_event(self, dispatch: impl FnOnce(Event)) { + pub fn dispatch_event( + self, + app: &mut dyn ApplicationHandler, + event_loop: &dyn RootActiveEventLoop, + ) { match self { - Self::Event(event) => dispatch(event), - Self::ScaleFactorChanged(window, scale_factor, new_surface_size) => { + Self::Window { window_id, event } => app.window_event(event_loop, window_id, event), + Self::Device { device_id, event } => { + app.device_event(event_loop, Some(device_id), event) + }, + Self::BufferedScaleFactorChanged(window, scale_factor, new_surface_size) => { let user_new_surface_size = Arc::new(Mutex::new(new_surface_size)); - dispatch(Event::WindowEvent { - window_id: WindowId::from_raw(window as usize), - event: WindowEvent::ScaleFactorChanged { + app.window_event( + event_loop, + WindowId::from_raw(window as usize), + WindowEvent::ScaleFactorChanged { scale_factor, surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade( &user_new_surface_size, )), }, - }); + ); let surface_size = *user_new_surface_size.lock().unwrap(); drop(user_new_surface_size); @@ -395,6 +436,7 @@ impl BufferedEvent { window_flags.set_size(window, surface_size); } }, + Self::WakeUp => app.proxy_wake_up(event_loop), } } } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index a2d00613..a38370c3 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -3,6 +3,7 @@ use std::cell::Cell; use std::ffi::c_void; use std::mem::{self, MaybeUninit}; +use std::rc::Rc; use std::sync::mpsc::channel; use std::sync::{Arc, Mutex, MutexGuard}; use std::{io, panic, ptr}; @@ -59,7 +60,9 @@ use crate::platform_impl::platform::dpi::{ dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi, }; use crate::platform_impl::platform::drop_handler::FileDropHandler; -use crate::platform_impl::platform::event_loop::{self, ActiveEventLoop, DESTROY_MSG_ID}; +use crate::platform_impl::platform::event_loop::{ + self, ActiveEventLoop, Event, EventLoopRunner, DESTROY_MSG_ID, +}; use crate::platform_impl::platform::icon::{self, IconType}; use crate::platform_impl::platform::ime::ImeContext; use crate::platform_impl::platform::keyboard::KeyEventBuilder; @@ -109,7 +112,7 @@ impl Window { // First person to remove the need for cloning here gets a cookie! // // done. you owe me -- ossi - unsafe { init(w_attr, event_loop) } + unsafe { init(w_attr, &event_loop.0) } } fn window_state_lock(&self) -> MutexGuard<'_, WindowState> { @@ -1082,7 +1085,7 @@ impl CoreWindow for Window { pub(super) struct InitData<'a> { // inputs - pub event_loop: &'a ActiveEventLoop, + pub runner: &'a Rc, pub attributes: WindowAttributes, pub window_flags: WindowFlags, // outputs @@ -1128,7 +1131,7 @@ impl InitData<'_> { Window { window: SyncWindowHandle(window), window_state, - thread_executor: self.event_loop.create_thread_executor(), + thread_executor: self.runner.create_thread_executor(), } } @@ -1147,10 +1150,13 @@ impl InitData<'_> { ); } - let file_drop_runner = self.event_loop.runner_shared.clone(); + let file_drop_runner = self.runner.clone(); + let window_id = win.id(); let file_drop_handler = FileDropHandler::new( win.window.hwnd(), - Box::new(move |event| file_drop_runner.send_event(event)), + Box::new(move |event| { + file_drop_runner.send_event(Event::Window { window_id, event }) + }), ); let handler_interface_ptr = @@ -1164,7 +1170,7 @@ impl InitData<'_> { event_loop::WindowData { window_state: win.window_state.clone(), - event_loop_runner: self.event_loop.runner_shared.clone(), + event_loop_runner: self.runner.clone(), key_event_builder: KeyEventBuilder::default(), _file_drop_handler: file_drop_handler, userdata_removed: Cell::new(false), @@ -1176,7 +1182,7 @@ impl InitData<'_> { // The user data will be registered for the window and can be accessed within the window event // callback. pub unsafe fn on_nccreate(&mut self, window: HWND) -> Option { - let runner = self.event_loop.runner_shared.clone(); + let runner = self.runner.clone(); let result = runner.catch_unwind(|| { let window = unsafe { self.create_window(window) }; let window_data = unsafe { self.create_window_data(&window) }; @@ -1268,7 +1274,7 @@ impl InitData<'_> { } unsafe fn init( attributes: WindowAttributes, - event_loop: &ActiveEventLoop, + runner: &Rc, ) -> Result { let title = util::encode_wide(&attributes.title); @@ -1322,7 +1328,7 @@ unsafe fn init( let menu = attributes.platform_specific.menu; let fullscreen = attributes.fullscreen.clone(); let maximized = attributes.maximized; - let mut initdata = InitData { event_loop, attributes, window_flags, window: None }; + let mut initdata = InitData { runner, attributes, window_flags, window: None }; let (style, ex_style) = window_flags.to_window_styles(); let handle = unsafe { @@ -1343,7 +1349,7 @@ unsafe fn init( }; // If the window creation in `InitData` panicked, then should resume panicking here - if let Err(panic_error) = event_loop.runner_shared.take_panic_error() { + if let Err(panic_error) = runner.take_panic_error() { panic::resume_unwind(panic_error) }