diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a213fa26..d8f635be 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -16,16 +16,16 @@ use std::{ thread, time::{Duration, Instant}, }; -use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; use winapi::{ shared::{ + basetsd::LONG_PTR, minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WORD, WPARAM}, windef::{HWND, POINT, RECT}, windowsx, winerror, }, um::{ - commctrl, libloaderapi, ole2, processthreadsapi, winbase, + libloaderapi, ole2, processthreadsapi, winbase, winnt::{HANDLE, LONG, LPCSTR, SHORT}, winuser, }, @@ -43,6 +43,7 @@ use crate::{ event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, monitor::{self, MonitorHandle}, raw_input, util, + window::InitData, window_state::{CursorFlags, WindowFlags, WindowState}, wrap_device_id, WindowId, DEVICE_ID, }, @@ -82,27 +83,26 @@ lazy_static! { static ref GET_POINTER_PEN_INFO: Option = get_function!("user32.dll", GetPointerPenInfo); } - -pub(crate) struct SubclassInput { +pub(crate) struct WindowData { pub window_state: Arc>, pub event_loop_runner: EventLoopRunnerShared, pub file_drop_handler: Option, - pub subclass_removed: Cell, + pub userdata_removed: Cell, pub recurse_depth: Cell, } -impl SubclassInput { +impl WindowData { unsafe fn send_event(&self, event: Event<'_, T>) { self.event_loop_runner.send_event(event); } } -struct ThreadMsgTargetSubclassInput { +struct ThreadMsgTargetData { event_loop_runner: EventLoopRunnerShared, user_event_receiver: Receiver, } -impl ThreadMsgTargetSubclassInput { +impl ThreadMsgTargetData { unsafe fn send_event(&self, event: Event<'_, T>) { self.event_loop_runner.send_event(event); } @@ -155,7 +155,7 @@ 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 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)); @@ -164,7 +164,7 @@ impl EventLoop { 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()); + insert_event_target_window_data::(thread_msg_target, runner_shared.clone()); raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target); EventLoop { @@ -563,19 +563,23 @@ lazy_static! { pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { winuser::RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr() as LPCSTR) }; - static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = unsafe { + static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; - let class_name: Vec<_> = OsStr::new("Winit Thread Event Target") + OsStr::new("Winit Thread Event Target") .encode_wide() .chain(Some(0).into_iter()) - .collect(); + .collect() + }; +} +fn create_event_target_window() -> HWND { + unsafe { let class = winuser::WNDCLASSEXW { cbSize: mem::size_of::() as UINT, style: 0, - lpfnWndProc: Some(winuser::DefWindowProcW), + lpfnWndProc: Some(thread_event_target_callback::), cbClsExtra: 0, cbWndExtra: 0, hInstance: libloaderapi::GetModuleHandleW(ptr::null()), @@ -583,17 +587,13 @@ lazy_static! { hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly hbrBackground: ptr::null_mut(), lpszMenuName: ptr::null(), - lpszClassName: class_name.as_ptr(), + lpszClassName: THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(), hIconSm: ptr::null_mut(), }; winuser::RegisterClassExW(&class); + } - class_name - }; -} - -fn create_event_target_window() -> HWND { unsafe { let window = winuser::CreateWindowExW( winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, @@ -615,45 +615,33 @@ fn create_event_target_window() -> HWND { // The window technically has to be visible to receive WM_PAINT messages (which are used // for delivering events during resizes), but it isn't displayed to the user because of // the LAYERED style. - (winuser::WS_VISIBLE | winuser::WS_POPUP) as _, + (winuser::WS_VISIBLE | winuser::WS_POPUP) as WindowLongPtr, ); window } } -fn subclass_event_target_window( - window: HWND, +fn insert_event_target_window_data( + thread_msg_target: HWND, event_loop_runner: EventLoopRunnerShared, ) -> Sender { + let (tx, rx) = mpsc::channel(); + + let userdata = ThreadMsgTargetData { + event_loop_runner, + user_event_receiver: rx, + }; + let input_ptr = Box::into_raw(Box::new(userdata)); + unsafe { - let (tx, rx) = mpsc::channel(); - - let subclass_input = ThreadMsgTargetSubclassInput { - event_loop_runner, - user_event_receiver: rx, - }; - let input_ptr = Box::into_raw(Box::new(subclass_input)); - let subclass_result = commctrl::SetWindowSubclass( - window, - Some(thread_event_target_callback::), - THREAD_EVENT_TARGET_SUBCLASS_ID, - input_ptr as DWORD_PTR, - ); - assert_eq!(subclass_result, 1); - - tx - } -} - -fn remove_event_target_window_subclass(window: HWND) { - let removal_result = unsafe { - commctrl::RemoveWindowSubclass( - window, - Some(thread_event_target_callback::), - THREAD_EVENT_TARGET_SUBCLASS_ID, + winuser::SetWindowLongPtrW( + thread_msg_target, + winuser::GWL_USERDATA, + input_ptr as WindowLongPtr, ) }; - assert_eq!(removal_result, 1); + + tx } /// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of @@ -674,33 +662,6 @@ unsafe fn release_mouse(mut window_state: parking_lot::MutexGuard<'_, WindowStat } } -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( - window, - Some(public_window_callback::), - WINDOW_SUBCLASS_ID, - input_ptr as DWORD_PTR, - ) - }; - assert_eq!(subclass_result, 1); -} - -fn remove_window_subclass(window: HWND) { - let removal_result = unsafe { - commctrl::RemoveWindowSubclass( - window, - Some(public_window_callback::), - WINDOW_SUBCLASS_ID, - ) - }; - assert_eq!(removal_result, 1); -} - fn normalize_pointer_pressure(pressure: u32) -> Option { match pressure { 1..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)), @@ -771,11 +732,11 @@ unsafe fn process_control_flow(runner: &EventLoopRunner) { } /// Emit a `ModifiersChanged` event whenever modifiers have changed. -fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { +fn update_modifiers(window: HWND, userdata: &WindowData) { use crate::event::WindowEvent::ModifiersChanged; let modifiers = event::get_key_mods(); - let mut window_state = subclass_input.window_state.lock(); + let mut window_state = userdata.window_state.lock(); if window_state.modifiers_state != modifiers { window_state.modifiers_state = modifiers; @@ -783,7 +744,7 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { drop(window_state); unsafe { - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ModifiersChanged(modifiers), }); @@ -791,6 +752,11 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { } } +#[cfg(target_arch = "x86_64")] +type WindowLongPtr = LONG_PTR; +#[cfg(target_arch = "x86")] +type WindowLongPtr = LONG; + /// Any window whose callback is configured to this function will have its events propagated /// through the events loop of the thread the window was created in. // @@ -798,33 +764,56 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { // // Returning 0 tells the Win32 API that the message has been processed. // FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary -unsafe extern "system" fn public_window_callback( +pub(super) unsafe extern "system" fn public_window_callback( window: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, - uidsubclass: UINT_PTR, - subclass_input_ptr: DWORD_PTR, ) -> LRESULT { - let subclass_input_ptr = subclass_input_ptr as *mut SubclassInput; - let (result, subclass_removed, recurse_depth) = { - let subclass_input = &*subclass_input_ptr; - subclass_input - .recurse_depth - .set(subclass_input.recurse_depth.get() + 1); + let userdata = winuser::GetWindowLongPtrW(window, winuser::GWL_USERDATA); + let userdata_ptr = match (userdata, msg) { + (0, winuser::WM_NCCREATE) => { + let createstruct = &mut *(lparam as *mut winuser::CREATESTRUCTW); + let initdata = createstruct.lpCreateParams as LONG_PTR; + let initdata = &mut *(initdata as *mut InitData<'_, T>); - let result = - public_window_callback_inner(window, msg, wparam, lparam, uidsubclass, subclass_input); - - let subclass_removed = subclass_input.subclass_removed.get(); - let recurse_depth = subclass_input.recurse_depth.get() - 1; - subclass_input.recurse_depth.set(recurse_depth); - - (result, subclass_removed, recurse_depth) + let runner = initdata.event_loop.runner_shared.clone(); + if let Some((win, userdata)) = runner.catch_unwind(|| (initdata.post_init)(window)) { + initdata.window = Some(win); + let userdata = Box::into_raw(Box::new(userdata)); + winuser::SetWindowLongPtrW( + window, + winuser::GWL_USERDATA, + userdata as WindowLongPtr, + ); + userdata + } else { + return -1; + } + } + // Getting here should quite frankly be impossible, + // but we'll make window creation fail here just in case. + (0, winuser::WM_CREATE) => return -1, + (0, _) => return winuser::DefWindowProcW(window, msg, wparam, lparam), + _ => userdata as *mut WindowData, }; - if subclass_removed && recurse_depth == 0 { - Box::from_raw(subclass_input_ptr); + let (result, userdata_removed, recurse_depth) = { + let userdata = &*(userdata_ptr); + + userdata.recurse_depth.set(userdata.recurse_depth.get() + 1); + + let result = public_window_callback_inner(window, msg, wparam, lparam, &userdata); + + let userdata_removed = userdata.userdata_removed.get(); + let recurse_depth = userdata.recurse_depth.get() - 1; + userdata.recurse_depth.set(recurse_depth); + + (result, userdata_removed, recurse_depth) + }; + + if userdata_removed && recurse_depth == 0 { + Box::from_raw(userdata_ptr); } result @@ -835,11 +824,10 @@ unsafe fn public_window_callback_inner( msg: UINT, wparam: WPARAM, lparam: LPARAM, - _: UINT_PTR, - subclass_input: &SubclassInput, + userdata: &WindowData, ) -> LRESULT { winuser::RedrawWindow( - subclass_input.event_loop_runner.thread_msg_target(), + userdata.event_loop_runner.thread_msg_target(), ptr::null(), ptr::null_mut(), winuser::RDW_INTERNALPAINT, @@ -850,7 +838,7 @@ unsafe fn public_window_callback_inner( // the git blame and history would be preserved. let callback = || match msg { winuser::WM_ENTERSIZEMOVE => { - subclass_input + userdata .window_state .lock() .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE)); @@ -858,7 +846,7 @@ unsafe fn public_window_callback_inner( } winuser::WM_EXITSIZEMOVE => { - subclass_input + userdata .window_state .lock() .set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE)); @@ -867,18 +855,19 @@ unsafe fn public_window_callback_inner( winuser::WM_NCCREATE => { enable_non_client_dpi_scaling(window); - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } + winuser::WM_NCLBUTTONDOWN => { if wparam == winuser::HTCAPTION as _ { winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, lparam); } - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } winuser::WM_CLOSE => { use crate::event::WindowEvent::CloseRequested; - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CloseRequested, }); @@ -888,22 +877,22 @@ unsafe fn public_window_callback_inner( winuser::WM_DESTROY => { use crate::event::WindowEvent::Destroyed; ole2::RevokeDragDrop(window); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Destroyed, }); - subclass_input.event_loop_runner.remove_window(window); + userdata.event_loop_runner.remove_window(window); 0 } winuser::WM_NCDESTROY => { - remove_window_subclass::(window); - subclass_input.subclass_removed.set(true); + winuser::SetWindowLongPtrW(window, winuser::GWL_USERDATA, 0); + userdata.userdata_removed.set(true); 0 } winuser::WM_PAINT => { - if subclass_input.event_loop_runner.should_buffer() { + if userdata.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( @@ -914,19 +903,19 @@ unsafe fn public_window_callback_inner( ); } else { let managing_redraw = - flush_paint_messages(Some(window), &subclass_input.event_loop_runner); - subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window)))); + flush_paint_messages(Some(window), &userdata.event_loop_runner); + userdata.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); + userdata.event_loop_runner.redraw_events_cleared(); + process_control_flow(&userdata.event_loop_runner); } } - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } winuser::WM_WINDOWPOSCHANGING => { - let mut window_state = subclass_input.window_state.lock(); + let mut window_state = userdata.window_state.lock(); if let Some(ref mut fullscreen) = window_state.fullscreen { let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS); let new_rect = RECT { @@ -981,14 +970,14 @@ unsafe fn public_window_callback_inner( if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE { let physical_position = PhysicalPosition::new((*windowpos).x as i32, (*windowpos).y as i32); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Moved(physical_position), }); } // This is necessary for us to still get sent WM_SIZE. - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } winuser::WM_SIZE => { @@ -1003,7 +992,7 @@ unsafe fn public_window_callback_inner( }; { - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); // See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists. if !w .window_flags() @@ -1014,7 +1003,7 @@ unsafe fn public_window_callback_inner( } } - subclass_input.send_event(event); + userdata.send_event(event); 0 } @@ -1025,24 +1014,24 @@ unsafe fn public_window_callback_inner( let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; if is_high_surrogate { - subclass_input.window_state.lock().high_surrogate = Some(wparam as u16); + userdata.window_state.lock().high_surrogate = Some(wparam as u16); } else if is_low_surrogate { - let high_surrogate = subclass_input.window_state.lock().high_surrogate.take(); + let high_surrogate = userdata.window_state.lock().high_surrogate.take(); if let Some(high_surrogate) = high_surrogate { let pair = [high_surrogate, wparam as u16]; if let Some(Ok(chr)) = char::decode_utf16(pair.iter().copied()).next() { - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ReceivedCharacter(chr), }); } } } else { - subclass_input.window_state.lock().high_surrogate = None; + userdata.window_state.lock().high_surrogate = None; if let Some(chr) = char::from_u32(wparam as u32) { - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ReceivedCharacter(chr), }); @@ -1054,17 +1043,17 @@ unsafe fn public_window_callback_inner( // this is necessary for us to maintain minimize/restore state winuser::WM_SYSCOMMAND => { if wparam == winuser::SC_RESTORE { - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, false)); } if wparam == winuser::SC_MINIMIZE { - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, true)); } // Send `WindowEvent::Minimized` here if we decide to implement one if wparam == winuser::SC_SCREENSAVE { - let window_state = subclass_input.window_state.lock(); + let window_state = userdata.window_state.lock(); if window_state.fullscreen.is_some() { return 0; } @@ -1076,7 +1065,7 @@ unsafe fn public_window_callback_inner( winuser::WM_MOUSEMOVE => { use crate::event::WindowEvent::{CursorEntered, CursorMoved}; let mouse_was_outside_window = { - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW); w.mouse @@ -1086,7 +1075,7 @@ unsafe fn public_window_callback_inner( }; if mouse_was_outside_window { - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CursorEntered { device_id: DEVICE_ID, @@ -1110,14 +1099,14 @@ unsafe fn public_window_callback_inner( // handle spurious WM_MOUSEMOVE messages // see https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343 // and http://debugandconquer.blogspot.com/2015/08/the-cause-of-spurious-mouse-move.html - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); cursor_moved = w.mouse.last_position != Some(position); w.mouse.last_position = Some(position); } if cursor_moved { - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, @@ -1133,13 +1122,13 @@ unsafe fn public_window_callback_inner( winuser::WM_MOUSELEAVE => { use crate::event::WindowEvent::CursorLeft; { - let mut w = subclass_input.window_state.lock(); + let mut w = userdata.window_state.lock(); w.mouse .set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)) .ok(); } - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CursorLeft { device_id: DEVICE_ID, @@ -1156,9 +1145,9 @@ unsafe fn public_window_callback_inner( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, @@ -1178,9 +1167,9 @@ unsafe fn public_window_callback_inner( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, @@ -1196,13 +1185,13 @@ unsafe fn public_window_callback_inner( winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { use crate::event::{ElementState::Pressed, VirtualKeyCode}; if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } else { if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -1218,7 +1207,7 @@ unsafe fn public_window_callback_inner( // Windows doesn't emit a delete character by default, but in order to make it // consistent with the other platforms we'll emit a delete character here. if vkey == Some(VirtualKeyCode::Delete) { - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::ReceivedCharacter('\u{7F}'), }); @@ -1231,10 +1220,10 @@ unsafe fn public_window_callback_inner( winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { use crate::event::ElementState::Released; if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -1254,11 +1243,11 @@ unsafe fn public_window_callback_inner( winuser::WM_LBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput}; - capture_mouse(window, &mut *subclass_input.window_state.lock()); + capture_mouse(window, &mut *userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1275,11 +1264,11 @@ unsafe fn public_window_callback_inner( ElementState::Released, MouseButton::Left, WindowEvent::MouseInput, }; - release_mouse(subclass_input.window_state.lock()); + release_mouse(userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1296,11 +1285,11 @@ unsafe fn public_window_callback_inner( ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput, }; - capture_mouse(window, &mut *subclass_input.window_state.lock()); + capture_mouse(window, &mut *userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1317,11 +1306,11 @@ unsafe fn public_window_callback_inner( ElementState::Released, MouseButton::Right, WindowEvent::MouseInput, }; - release_mouse(subclass_input.window_state.lock()); + release_mouse(userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1338,11 +1327,11 @@ unsafe fn public_window_callback_inner( ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput, }; - capture_mouse(window, &mut *subclass_input.window_state.lock()); + capture_mouse(window, &mut *userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1359,11 +1348,11 @@ unsafe fn public_window_callback_inner( ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput, }; - release_mouse(subclass_input.window_state.lock()); + release_mouse(userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1381,11 +1370,11 @@ unsafe fn public_window_callback_inner( }; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - capture_mouse(window, &mut *subclass_input.window_state.lock()); + capture_mouse(window, &mut *userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1403,11 +1392,11 @@ unsafe fn public_window_callback_inner( }; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - release_mouse(subclass_input.window_state.lock()); + release_mouse(userdata.window_state.lock()); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, @@ -1425,7 +1414,7 @@ unsafe fn public_window_callback_inner( // can happen if `SetCapture` is called on our window when it already has the mouse // capture. if lparam != window as isize { - subclass_input.window_state.lock().mouse.capture_count = 0; + userdata.window_state.lock().mouse.capture_count = 0; } 0 } @@ -1455,7 +1444,7 @@ unsafe fn public_window_callback_inner( let x = location.x as f64 + (input.x % 100) as f64 / 100f64; let y = location.y as f64 + (input.y % 100) as f64 / 100f64; let location = PhysicalPosition::new(x, y); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::Touch(Touch { phase: if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 { @@ -1593,7 +1582,7 @@ unsafe fn public_window_callback_inner( let x = location.x as f64 + x.fract(); let y = location.y as f64 + y.fract(); let location = PhysicalPosition::new(x, y); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::Touch(Touch { phase: if pointer_info.pointerFlags & winuser::POINTER_FLAG_DOWN != 0 { @@ -1626,10 +1615,10 @@ unsafe fn public_window_callback_inner( winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); - update_modifiers(window, subclass_input); + update_modifiers(window, userdata); #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -1644,7 +1633,7 @@ unsafe fn public_window_callback_inner( }) } - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Focused(true), }); @@ -1664,7 +1653,7 @@ unsafe fn public_window_callback_inner( let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -1679,13 +1668,13 @@ unsafe fn public_window_callback_inner( }) } - subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); - subclass_input.send_event(Event::WindowEvent { + userdata.window_state.lock().modifiers_state = ModifiersState::empty(); + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ModifiersChanged(ModifiersState::empty()), }); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Focused(false), }); @@ -1694,7 +1683,7 @@ unsafe fn public_window_callback_inner( winuser::WM_SETCURSOR => { let set_cursor_to = { - let window_state = subclass_input.window_state.lock(); + let window_state = userdata.window_state.lock(); // The return value for the preceding `WM_NCHITTEST` message is conveniently // provided through the low-order word of lParam. We use that here since // `WM_MOUSEMOVE` seems to come after `WM_SETCURSOR` for a given cursor movement. @@ -1724,7 +1713,7 @@ unsafe fn public_window_callback_inner( winuser::WM_GETMINMAXINFO => { let mmi = lparam as *mut winuser::MINMAXINFO; - let window_state = subclass_input.window_state.lock(); + let window_state = userdata.window_state.lock(); if window_state.min_size.is_some() || window_state.max_size.is_some() { if let Some(min_size) = window_state.min_size { @@ -1762,7 +1751,7 @@ unsafe fn public_window_callback_inner( let old_scale_factor: f64; let allow_resize = { - let mut window_state = subclass_input.window_state.lock(); + let mut window_state = userdata.window_state.lock(); old_scale_factor = window_state.scale_factor; window_state.scale_factor = new_scale_factor; @@ -1827,7 +1816,7 @@ unsafe fn public_window_callback_inner( false => old_physical_inner_size, }; - let _ = subclass_input.send_event(Event::WindowEvent { + let _ = userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ScaleFactorChanged { scale_factor: new_scale_factor, @@ -1838,7 +1827,7 @@ unsafe fn public_window_callback_inner( let dragging_window: bool; { - let window_state = subclass_input.window_state.lock(); + let window_state = userdata.window_state.lock(); dragging_window = window_state .window_flags() .contains(WindowFlags::MARKER_IN_SIZE_MOVE); @@ -1968,23 +1957,23 @@ unsafe fn public_window_callback_inner( winuser::WM_SETTINGCHANGE => { use crate::event::WindowEvent::ThemeChanged; - let preferred_theme = subclass_input.window_state.lock().preferred_theme; + let preferred_theme = userdata.window_state.lock().preferred_theme; if preferred_theme == None { let new_theme = try_theme(window, preferred_theme); - let mut window_state = subclass_input.window_state.lock(); + let mut window_state = userdata.window_state.lock(); if window_state.current_theme != new_theme { window_state.current_theme = new_theme; mem::drop(window_state); - subclass_input.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ThemeChanged(new_theme), }); } } - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } _ => { @@ -1992,18 +1981,18 @@ unsafe fn public_window_callback_inner( winuser::DestroyWindow(window); 0 } else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID { - let mut window_state = subclass_input.window_state.lock(); + let mut window_state = userdata.window_state.lock(); window_state.set_window_flags_in_place(|f| { f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0) }); 0 } else { - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } } }; - subclass_input + userdata .event_loop_runner .catch_unwind(callback) .unwrap_or(-1) @@ -2014,10 +2003,15 @@ unsafe extern "system" fn thread_event_target_callback( msg: UINT, wparam: WPARAM, lparam: LPARAM, - _: UINT_PTR, - subclass_input_ptr: DWORD_PTR, ) -> LRESULT { - let subclass_input = Box::from_raw(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput); + let userdata_ptr = + winuser::GetWindowLongPtrW(window, winuser::GWL_USERDATA) as *mut ThreadMsgTargetData; + if userdata_ptr.is_null() { + // `userdata_ptr` will always be null for the first `WM_GETMINMAXINFO`, as well as `WM_NCCREATE` and + // `WM_CREATE`. + return winuser::DefWindowProcW(window, msg, wparam, lparam); + } + let userdata = Box::from_raw(userdata_ptr); if msg != winuser::WM_PAINT { winuser::RedrawWindow( @@ -2028,15 +2022,15 @@ unsafe extern "system" fn thread_event_target_callback( ); } - let mut subclass_removed = false; + let mut userdata_removed = false; // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and // the git blame and history would be preserved. let callback = || match msg { winuser::WM_NCDESTROY => { - remove_event_target_window_subclass::(window); - subclass_removed = true; + winuser::SetWindowLongPtrW(window, winuser::GWL_USERDATA, 0); + userdata_removed = true; 0 } // Because WM_PAINT comes after all other messages, we use it during modal loops to detect @@ -2046,8 +2040,8 @@ unsafe extern "system" fn thread_event_target_callback( // 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() { - if subclass_input.event_loop_runner.should_buffer() { + if userdata.event_loop_runner.handling_events() { + if userdata.event_loop_runner.should_buffer() { // This branch can be triggered when a nested win32 event loop is triggered // inside of the `event_handler` callback. winuser::RedrawWindow( @@ -2059,17 +2053,14 @@ unsafe extern "system" fn thread_event_target_callback( } else { // 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); + assert!(flush_paint_messages(None, &userdata.event_loop_runner)); + userdata.event_loop_runner.redraw_events_cleared(); + process_control_flow(&userdata.event_loop_runner); } } // Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them. - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } winuser::WM_INPUT_DEVICE_CHANGE => { @@ -2079,7 +2070,7 @@ unsafe extern "system" fn thread_event_target_callback( _ => unreachable!(), }; - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id: wrap_device_id(lparam as _), event, }); @@ -2105,21 +2096,21 @@ unsafe extern "system" fn thread_event_target_callback( let y = mouse.lLastY as f64; if x != 0.0 { - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: Motion { axis: 0, value: x }, }); } if y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: Motion { axis: 1, value: y }, }); } if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: MouseMotion { delta: (x, y) }, }); @@ -2129,7 +2120,7 @@ unsafe extern "system" fn thread_event_target_callback( if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { let delta = mouse.usButtonData as SHORT as f32 / winuser::WHEEL_DELTA as f32; - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: MouseWheel { delta: LineDelta(0.0, delta), @@ -2145,7 +2136,7 @@ unsafe extern "system" fn thread_event_target_callback( // seem to be anything else reasonable to do for a mouse // button ID. let button = (index + 1) as _; - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: Button { button, state }, }); @@ -2172,7 +2163,7 @@ unsafe extern "system" fn thread_event_target_callback( let virtual_keycode = vkey_to_winit_vkey(vkey); #[allow(deprecated)] - subclass_input.send_event(Event::DeviceEvent { + userdata.send_event(Event::DeviceEvent { device_id, event: Key(KeyboardInput { scancode, @@ -2186,12 +2177,12 @@ unsafe extern "system" fn thread_event_target_callback( } } - commctrl::DefSubclassProc(window, msg, wparam, lparam) + winuser::DefWindowProcW(window, msg, wparam, lparam) } _ if msg == *USER_EVENT_MSG_ID => { - if let Ok(event) = subclass_input.user_event_receiver.recv() { - subclass_input.send_event(Event::UserEvent(event)); + if let Ok(event) = userdata.user_event_receiver.recv() { + userdata.send_event(Event::UserEvent(event)); } 0 } @@ -2202,7 +2193,7 @@ unsafe extern "system" fn thread_event_target_callback( } _ if msg == *PROCESS_NEW_EVENTS_MSG_ID => { winuser::PostThreadMessageW( - subclass_input.event_loop_runner.wait_thread_id(), + userdata.event_loop_runner.wait_thread_id(), *CANCEL_WAIT_UNTIL_MSG_ID, 0, 0, @@ -2210,9 +2201,7 @@ unsafe extern "system" fn thread_event_target_callback( // 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() - { + if let ControlFlow::WaitUntil(wait_until) = userdata.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) { @@ -2238,20 +2227,20 @@ unsafe extern "system" fn thread_event_target_callback( } } } - subclass_input.event_loop_runner.poll(); + userdata.event_loop_runner.poll(); 0 } - _ => commctrl::DefSubclassProc(window, msg, wparam, lparam), + _ => winuser::DefWindowProcW(window, msg, wparam, lparam), }; - let result = subclass_input + let result = userdata .event_loop_runner .catch_unwind(callback) .unwrap_or(-1); - if subclass_removed { - mem::drop(subclass_input); + if userdata_removed { + mem::drop(userdata); } else { - Box::into_raw(subclass_input); + Box::into_raw(userdata); } result } diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 4f90966c..cf327674 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -23,7 +23,7 @@ use crate::{ pub(crate) type EventLoopRunnerShared = Rc>; pub(crate) struct EventLoopRunner { // The event loop's win32 handles - thread_msg_target: HWND, + pub(super) thread_msg_target: HWND, wait_thread_id: DWORD, control_flow: Cell, diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 081009dc..0a866eb9 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -7,7 +7,7 @@ use std::{ ffi::OsStr, io, mem, os::windows::ffi::OsStrExt, - ptr, + panic, ptr, sync::{mpsc::channel, Arc}, }; @@ -40,7 +40,7 @@ use crate::{ dark_mode::try_theme, dpi::{dpi_to_scale_factor, hwnd_dpi}, drop_handler::FileDropHandler, - event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID}, + event_loop::{self, EventLoopWindowTarget, WindowData, DESTROY_MSG_ID}, icon::{self, IconType}, monitor, util, window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, @@ -73,7 +73,7 @@ impl Window { // done. you owe me -- ossi unsafe { let drag_and_drop = pl_attr.drag_and_drop; - init(w_attr, pl_attr, event_loop).map(|win| { + init(w_attr, pl_attr, event_loop, |win| { let file_drop_handler = if drag_and_drop { use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK}; @@ -111,16 +111,15 @@ impl Window { None }; - let subclass_input = event_loop::SubclassInput { + event_loop.runner_shared.register_window(win.window.0); + + event_loop::WindowData { window_state: win.window_state.clone(), event_loop_runner: event_loop.runner_shared.clone(), file_drop_handler, - subclass_removed: Cell::new(false), + userdata_removed: Cell::new(false), recurse_depth: Cell::new(0), - }; - - event_loop::subclass_window(win.window.0, subclass_input); - win + } }) } } @@ -724,18 +723,31 @@ pub struct WindowWrapper(HWND); unsafe impl Sync for WindowWrapper {} unsafe impl Send for WindowWrapper {} -unsafe fn init( +pub(super) struct InitData<'a, T: 'static> { + // inputs + pub event_loop: &'a EventLoopWindowTarget, + pub post_init: &'a dyn Fn(HWND) -> (Window, WindowData), + // outputs + pub window: Option, +} + +unsafe fn init( attributes: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, event_loop: &EventLoopWindowTarget, -) -> Result { + create_window_data: F, +) -> Result +where + T: 'static, + F: Fn(&mut Window) -> WindowData, +{ let title = OsStr::new(&attributes.title) .encode_wide() .chain(Some(0).into_iter()) .collect::>(); // registering the window class - let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon); + let class_name = register_window_class::(&attributes.window_icon, &pl_attribs.taskbar_icon); let mut window_flags = WindowFlags::empty(); window_flags.set(WindowFlags::DECORATIONS, attributes.decorations); @@ -766,31 +778,59 @@ unsafe fn init( } }; - // creating the real window this time, by using the functions in `extra_functions` - let real_window = { - let (style, ex_style) = window_flags.to_window_styles(); - let handle = winuser::CreateWindowExW( - ex_style, - class_name.as_ptr(), - title.as_ptr() as LPCWSTR, - style, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - winuser::CW_USEDEFAULT, - parent.unwrap_or(ptr::null_mut()), - pl_attribs.menu.unwrap_or(ptr::null_mut()), - libloaderapi::GetModuleHandleW(ptr::null()), - ptr::null_mut(), - ); - - if handle.is_null() { - return Err(os_error!(io::Error::last_os_error())); - } - - WindowWrapper(handle) + let mut initdata = InitData { + event_loop, + post_init: &|hwnd| { + let mut window = post_init( + WindowWrapper(hwnd), + attributes.clone(), + pl_attribs.clone(), + window_flags, + event_loop, + ); + let window_data = create_window_data(&mut window); + (window, window_data) + }, + window: None, }; + let (style, ex_style) = window_flags.to_window_styles(); + let handle = winuser::CreateWindowExW( + ex_style, + class_name.as_ptr(), + title.as_ptr() as LPCWSTR, + style, + winuser::CW_USEDEFAULT, + winuser::CW_USEDEFAULT, + winuser::CW_USEDEFAULT, + winuser::CW_USEDEFAULT, + parent.unwrap_or(ptr::null_mut()), + pl_attribs.menu.unwrap_or(ptr::null_mut()), + libloaderapi::GetModuleHandleW(ptr::null()), + &mut initdata as *mut _ as *mut _, + ); + + // If the `post_init` callback in `InitData` panicked, then should resume panicking here + if let Err(panic_error) = event_loop.runner_shared.take_panic_error() { + panic::resume_unwind(panic_error) + } + + if handle.is_null() { + return Err(os_error!(io::Error::last_os_error())); + } + + // If the handle is non-null, then window creation must have succeeded, which means + // that we *must* have populated the `InitData.window` field. + Ok(initdata.window.unwrap()) +} + +unsafe fn post_init( + real_window: WindowWrapper, + attributes: WindowAttributes, + pl_attribs: PlatformSpecificWindowBuilderAttributes, + window_flags: WindowFlags, + event_loop: &EventLoopWindowTarget, +) -> Window { // Register for touch events if applicable { let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32; @@ -862,10 +902,10 @@ unsafe fn init( win.set_outer_position(position); } - Ok(win) + win } -unsafe fn register_window_class( +unsafe fn register_window_class( window_icon: &Option, taskbar_icon: &Option, ) -> Vec { @@ -886,7 +926,7 @@ unsafe fn register_window_class( let class = winuser::WNDCLASSEXW { cbSize: mem::size_of::() as UINT, style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC, - lpfnWndProc: Some(winuser::DefWindowProcW), + lpfnWndProc: Some(super::event_loop::public_window_callback::), cbClsExtra: 0, cbWndExtra: 0, hInstance: libloaderapi::GetModuleHandleW(ptr::null()),