diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index e202281b48..20cdac913b 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -40,26 +40,26 @@ use windows_sys::Win32::UI::Input::{ MOUSE_MOVE_RELATIVE, RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE, }; use windows_sys::Win32::UI::WindowsAndMessaging::{ - CREATESTRUCTW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GWL_STYLE, - GWL_USERDATA, GetClientRect, GetCursorPos, GetMenu, HTCAPTION, HTCLIENT, LoadCursorW, - MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, MsgWaitForMultipleObjectsEx, - NCCALCSIZE_PARAMS, PEN_FLAG_BARREL, PEN_FLAG_ERASER, PEN_MASK_PRESSURE, PEN_MASK_ROTATION, - PEN_MASK_TILT_X, PEN_MASK_TILT_Y, PM_REMOVE, PT_PEN, PT_TOUCH, PeekMessageW, PostMessageW, - QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, RegisterClassExW, RegisterWindowMessageA, - SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, - SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetCursor, SetWindowPos, - SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, - WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, - WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, - WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, - WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, - WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, - WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, - WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, - WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WMSZ_BOTTOM, - WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, - WMSZ_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, - WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE, + CREATESTRUCTW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, + ENDSESSION_CLOSEAPP, GWL_STYLE, GWL_USERDATA, GetClientRect, GetCursorPos, GetMenu, HTCAPTION, + HTCLIENT, LoadCursorW, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, + MsgWaitForMultipleObjectsEx, NCCALCSIZE_PARAMS, PEN_FLAG_BARREL, PEN_FLAG_ERASER, + PEN_MASK_PRESSURE, PEN_MASK_ROTATION, PEN_MASK_TILT_X, PEN_MASK_TILT_Y, PM_REMOVE, PT_PEN, + PT_TOUCH, PeekMessageW, PostMessageW, QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, + RegisterClassExW, RegisterWindowMessageA, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, + SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, + SWP_NOZORDER, SetCursor, SetWindowPos, SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, + WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENDSESSION, + WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, + WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, + WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, + WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, + WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, + WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, + WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, + WM_XBUTTONDOWN, WM_XBUTTONUP, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT, WMSZ_LEFT, + WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WNDCLASSEXW, WS_EX_LAYERED, + WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE, }; use winit_core::application::ApplicationHandler; use winit_core::cursor::{CustomCursor, CustomCursorSource}; @@ -249,12 +249,12 @@ impl EventLoop { pub fn run_app_on_demand( &mut self, - mut app: A, + app: A, ) -> Result<(), EventLoopError> { self.runner.clear_exit(); // SAFETY: The resetter is not leaked. - let _app_resetter = unsafe { self.runner.set_app(&mut app) }; + let _app_resetter = unsafe { self.runner.set_app(app) }; let exit_code = loop { self.wait_for_messages(None); @@ -281,10 +281,10 @@ impl EventLoop { pub fn pump_app_events( &mut self, timeout: Option, - mut app: A, + app: A, ) -> PumpStatus { // SAFETY: The resetter is not leaked. - let _app_resetter = unsafe { self.runner.set_app(&mut app) }; + let _app_resetter = unsafe { self.runner.set_app(app) }; self.runner.wakeup(); @@ -2384,6 +2384,28 @@ unsafe extern "system" fn thread_event_target_callback( unsafe { DefWindowProcW(window, msg, wparam, lparam) } }, + WM_ENDSESSION => { + // `wParam` is `FALSE` is for if the shutdown gets canceled, + // and we don't need to handle that case since we didn't do anything prior in response + // to `WM_QUERYENDSESSION` + if wparam == true as usize { + // Sent from Restart Manager + // > https://learn.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications + if lparam == ENDSESSION_CLOSEAPP as isize { + // Mark the event loop to exit is enough + // since Restart Manager doesn't terminate us right after returning here + userdata.event_loop_runner.set_exit_code(0); + } + // Treat everything else as system shutdown + else { + userdata.event_loop_runner.loop_destroyed(); + // Windows will terminate us anytime after we return `0` here, + // just do that ourselves here + std::process::exit(0) + } + } + 0 + }, _ if msg == USER_EVENT_MSG_ID.get() => { // synthesis a placeholder UserEvent, so that if the callback is diff --git a/winit-win32/src/event_loop/runner.rs b/winit-win32/src/event_loop/runner.rs index 65e6082761..38af6d7c5a 100644 --- a/winit-win32/src/event_loop/runner.rs +++ b/winit-win32/src/event_loop/runner.rs @@ -17,7 +17,7 @@ use super::{ActiveEventLoop, ControlFlow, EventLoopThreadExecutor}; use crate::event_loop::{GWL_USERDATA, WindowData}; use crate::util::get_window_long; -type EventHandler = Cell>; +type EventHandler = Cell>>; pub(crate) struct EventLoopRunner { pub(super) thread_id: u32, @@ -96,19 +96,16 @@ impl EventLoopRunner { /// /// 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 { + pub(crate) unsafe fn set_app(&self, app: A) -> impl Drop + use { + let app = Box::new(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) + mem::transmute::, Box>( + app, + ) }; let old_event_handler = self.event_handler.replace(Some(f)); @@ -260,12 +257,12 @@ impl EventLoopRunner { closure: impl FnOnce(&mut dyn ApplicationHandler, &dyn RootActiveEventLoop), ) { self.catch_unwind(|| { - let event_handler = self.event_handler.take().expect( + let mut event_handler = self.event_handler.take().expect( "either event handler is re-entrant (likely), or no event handler is registered \ (very unlikely)", ); - closure(event_handler, ActiveEventLoop::from_ref(self)); + closure(&mut event_handler, ActiveEventLoop::from_ref(self)); assert!(self.event_handler.replace(Some(event_handler)).is_none()); }); @@ -333,6 +330,7 @@ impl EventLoopRunner { self.call_new_events(true); self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); + drop(self.event_handler.take()); }, (_, Uninitialized) => panic!("cannot move state to Uninitialized"), @@ -340,7 +338,9 @@ impl EventLoopRunner { (Idle, HandlingMainEvents) => { self.call_new_events(false); }, - (Idle, Destroyed) => {}, + (Idle, Destroyed) => { + drop(self.event_handler.take()); + }, (HandlingMainEvents, Idle) => { // This is always the last event we dispatch before waiting for new events @@ -350,6 +350,7 @@ impl EventLoopRunner { (HandlingMainEvents, Destroyed) => { self.call_event_handler(|app, event_loop| app.about_to_wait(event_loop)); self.last_events_cleared.set(Instant::now()); + drop(self.event_handler.take()); }, (Destroyed, _) => panic!("cannot move state from Destroyed"), diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index b3a435f9d2..45769f4586 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -47,3 +47,9 @@ changelog entry. ### Fixed - On X11, fix `set_hittest` not working on some window managers. + + +### Changed + +- On Windows, the `ApplicationHandler` passed to `EventLoop::run_app` is now dropped on receiving `WM_ENDSESSION` message in system shutdown +- On Windows, the event loop will now exit on receiving `WM_ENDSESSION` in reaction to Restart Manager diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index 6882ebf163..1b43d973c8 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -219,7 +219,6 @@ impl EventLoop { mut app: A, ) -> Result<(), EventLoopError> { #[cfg(any( - windows_platform, macos_platform, android_platform, orbital_platform, @@ -232,6 +231,10 @@ impl EventLoop { drop(app); result } + #[cfg(windows_platform)] + { + self.event_loop.run_app_on_demand(app) + } #[cfg(web_platform)] { self.event_loop.register_app(app);