diff --git a/Cargo.toml b/Cargo.toml index 1e06f5e862..3ae8aa2d75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ core-graphics = "0.17.3" version = "0.3.6" features = [ "combaseapi", + "commctrl", "dwmapi", "errhandlingapi", "hidusage", diff --git a/src/lib.rs b/src/lib.rs index 78874f2b45..9e60e78d66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,15 +240,6 @@ impl EventLoop { MonitorId { inner: self.events_loop.get_primary_monitor() } } - /// Fetches all the events that are pending, calls the callback function for each of them, - /// and returns. - #[inline] - pub fn poll_events(&mut self, callback: F) - where F: FnMut(Event) - { - self.events_loop.poll_events(callback) - } - /// Calls `callback` every time an event is received. If no event is available, sleeps the /// current thread and waits for an event. If the callback returns `ControlFlow::Break` then /// `run_forever` will immediately return. diff --git a/src/platform/windows/drop_handler.rs b/src/platform/windows/drop_handler.rs index e7bd12d026..d3f28a9b36 100644 --- a/src/platform/windows/drop_handler.rs +++ b/src/platform/windows/drop_handler.rs @@ -3,6 +3,8 @@ use std::os::windows::ffi::OsStringExt; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{mem, ptr}; +use std::rc::Rc; +use std::cell::RefCell; use winapi::ctypes::c_void; use winapi::shared::guiddef::REFIID; @@ -14,7 +16,6 @@ use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl}; use winapi::um::winnt::HRESULT; use winapi::um::{shellapi, unknwnbase}; -use platform::platform::events_loop::send_event; use platform::platform::WindowId; use {Event, WindowId as SuperWindowId}; @@ -24,6 +25,7 @@ pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, + event_queue: Rc>>, } pub struct FileDropHandler { @@ -32,13 +34,14 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub fn new(window: HWND) -> FileDropHandler { + pub fn new(window: HWND, event_queue: Rc>>) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, }, refcount: AtomicUsize::new(1), window, + event_queue, }); FileDropHandler { data: Box::into_raw(data), @@ -82,7 +85,7 @@ impl FileDropHandler { use events::WindowEvent::HoveredFile; let drop_handler = Self::from_interface(this); Self::iterate_filenames(pDataObj, |filename| { - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: HoveredFile(filename), }); @@ -103,7 +106,7 @@ impl FileDropHandler { pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { use events::WindowEvent::HoveredFileCancelled; let drop_handler = Self::from_interface(this); - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: HoveredFileCancelled, }); @@ -121,7 +124,7 @@ impl FileDropHandler { use events::WindowEvent::DroppedFile; let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: DroppedFile(filename), }); @@ -182,6 +185,12 @@ impl FileDropHandler { } } +impl FileDropHandlerData { + fn send_event(&self, event: Event) { + self.event_queue.borrow_mut().push(event); + } +} + impl Drop for FileDropHandler { fn drop(&mut self) { unsafe { diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 0fdce07f99..17d6c526b1 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -12,11 +12,14 @@ //! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to //! add a `WindowState` entry to a list of window to be used by the callback. -use std::{mem, ptr, thread}; +use winapi::shared::basetsd::DWORD_PTR; +use winapi::shared::basetsd::UINT_PTR; +use std::rc::Rc; +use std::{mem, ptr}; use std::cell::RefCell; -use std::collections::HashMap; -use std::os::windows::io::AsRawHandle; -use std::sync::{Arc, Barrier, mpsc, Mutex}; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; +use std::sync::{Arc, Mutex}; use winapi::ctypes::c_int; use winapi::shared::minwindef::{ @@ -33,7 +36,7 @@ use winapi::shared::minwindef::{ use winapi::shared::windef::{HWND, POINT, RECT}; use winapi::shared::windowsx; use winapi::shared::winerror::S_OK; -use winapi::um::{winuser, processthreadsapi, ole2}; +use winapi::um::{winuser, processthreadsapi, ole2, shellapi, commctrl}; use winapi::um::oleidl::LPDROPTARGET; use winapi::um::winnt::{LONG, LPCSTR, SHORT}; @@ -102,6 +105,19 @@ pub struct WindowState { pub always_on_top: bool, pub maximized: bool, pub resizable: bool, + pub mouse_buttons_down: u32 +} + +pub(crate) struct SubclassInput { + pub window_state: Arc>, + pub event_queue: Rc>>, + pub file_drop_handler: FileDropHandler +} + +impl SubclassInput { + fn send_event(&self, event: Event) { + self.event_queue.borrow_mut().push(event); + } } impl WindowState { @@ -117,27 +133,10 @@ impl WindowState { } } -/// Dummy object that allows inserting a window's state. -// We store a pointer in order to !impl Send and Sync. -pub struct Inserter(*mut u8); - -impl Inserter { - /// Inserts a window's state for the callback to use. The state is removed automatically if the - /// callback receives a `WM_CLOSE` message for the window. - pub fn insert(&self, window: HWND, state: Arc>) { - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - let was_in = context_stash.as_mut().unwrap().windows.insert(window, state); - assert!(was_in.is_none()); - }); - } -} - pub struct EventLoop { // Id of the background thread from the Win32 API. thread_id: DWORD, - // Receiver for the events. The sender is in the background thread. - receiver: mpsc::Receiver, + pub(crate) event_queue: Rc>> } impl EventLoop { @@ -148,101 +147,55 @@ impl EventLoop { pub fn with_dpi_awareness(dpi_aware: bool) -> EventLoop { become_dpi_aware(dpi_aware); - // The main events transfer channel. - let (tx, rx) = mpsc::channel(); - - // Local barrier in order to block the `new()` function until the background thread has - // an events queue. - let barrier = Arc::new(Barrier::new(2)); - let barrier_clone = barrier.clone(); - - let thread = thread::spawn(move || { - CONTEXT_STASH.with(|context_stash| { - *context_stash.borrow_mut() = Some(ThreadLocalData { - sender: tx, - windows: HashMap::with_capacity(4), - file_drop_handlers: HashMap::with_capacity(4), - mouse_buttons_down: 0, - }); - }); - - unsafe { - // Calling `PostThreadMessageA` on a thread that does not have an events queue yet - // will fail. In order to avoid this situation, we call `IsGuiThread` to initialize - // it. - winuser::IsGUIThread(1); - // Then only we unblock the `new()` function. We are sure that we don't call - // `PostThreadMessageA()` before `new()` returns. - barrier_clone.wait(); - drop(barrier_clone); - - let mut msg = mem::uninitialized(); - - loop { - if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 { - // Only happens if the message is `WM_QUIT`. - debug_assert_eq!(msg.message, winuser::WM_QUIT); - break; - } - - match msg.message { - x if x == *EXEC_MSG_ID => { - let mut function: Box> = Box::from_raw(msg.wParam as usize as *mut _); - function(Inserter(ptr::null_mut())); - }, - x if x == *WAKEUP_MSG_ID => { - send_event(Event::Awakened); - }, - _ => { - // Calls `callback` below. - winuser::TranslateMessage(&msg); - winuser::DispatchMessageW(&msg); - } - } - } - } - }); - - // Blocks this function until the background thread has an events loop. See other comments. - barrier.wait(); - - let thread_id = unsafe { - let handle = mem::transmute(thread.as_raw_handle()); - processthreadsapi::GetThreadId(handle) - }; + let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; EventLoop { thread_id, - receiver: rx, - } - } - - pub fn poll_events(&mut self, mut callback: F) - where F: FnMut(Event) - { - loop { - let event = match self.receiver.try_recv() { - Ok(e) => e, - Err(_) => return - }; - - callback(event); + event_queue: Rc::new(RefCell::new(Vec::new())) } } pub fn run_forever(&mut self, mut callback: F) where F: FnMut(Event) -> ControlFlow { - loop { - let event = match self.receiver.recv() { - Ok(e) => e, - Err(_) => return - }; + unsafe { + // Calling `PostThreadMessageA` on a thread that does not have an events queue yet + // will fail. In order to avoid this situation, we call `IsGuiThread` to initialize + // it. + winuser::IsGUIThread(1); + + let mut msg = mem::uninitialized(); + + loop { + if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 { + // Only happens if the message is `WM_QUIT`. + debug_assert_eq!(msg.message, winuser::WM_QUIT); + return; + } - let flow = callback(event); - match flow { - ControlFlow::Continue => continue, - ControlFlow::Break => break, + match msg.message { + x if x == *WAKEUP_MSG_ID => { + if ControlFlow::Break == callback(Event::Awakened) { + return; + } + }, + x if x == *EXEC_MSG_ID => { + let mut function: Box> = Box::from_raw(msg.wParam as usize as *mut _); + function() + } + _ => { + // Calls `callback` below. + winuser::TranslateMessage(&msg); + winuser::DispatchMessageW(&msg); + } + } + + let mut event_queue = self.event_queue.borrow_mut(); + for event in event_queue.drain(..) { + if ControlFlow::Break == callback(event) { + return; + } + } } } } @@ -252,19 +205,6 @@ impl EventLoop { thread_id: self.thread_id, } } - - /// Executes a function in the background thread. - /// - /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent - /// to the unstable FnBox. - /// - /// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is - /// removed automatically if the callback receives a `WM_CLOSE` message for the window. - pub(super) fn execute_in_thread(&self, function: F) - where F: FnMut(Inserter) + Send + 'static - { - self.create_proxy().execute_in_thread(function) - } } impl Drop for EventLoop { @@ -299,11 +239,14 @@ impl EventLoopProxy { } } - /// Executes a function in the background thread. - /// - /// Note that we use FnMut instead of FnOnce because boxing FnOnce won't work on stable Rust - /// until 2030 when the design of Box is finally complete. - /// https://github.com/rust-lang/rust/issues/28796 + /// Check to see if we're in the parent event loop's thread. + pub(super) fn in_event_loop_thread(&self) -> bool { + let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + self.thread_id == cur_thread_id + } + + /// Executes a function in the event loop thread. If we're already in the event loop thread, + /// we just call the function directly. /// /// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is /// removed automatically if the callback receives a `WM_CLOSE` message for the window. @@ -312,25 +255,29 @@ impl EventLoopProxy { /// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the /// events may be sent to the other thread in different order to the one in which you set /// `WindowState`, leaving them out of sync. - pub fn execute_in_thread(&self, function: F) - where - F: FnMut(Inserter) + Send + 'static, + /// + /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent + /// to the unstable FnBox. + pub(super) fn execute_in_thread(&self, mut function: F) + where F: FnMut() + Send + 'static { - // We are using double-boxing here because it make casting back much easier - let double_box = Box::new(Box::new(function) as Box); - let raw = Box::into_raw(double_box); - - let res = unsafe { - winuser::PostThreadMessageA( - self.thread_id, - *EXEC_MSG_ID, - raw as *mut () as usize as WPARAM, - 0, - ) - }; - // PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen as - // the events loop is still alive) or if the queue is full. - assert!(res != 0, "PostThreadMessage failed; is the messages queue full?"); + unsafe { + if self.in_event_loop_thread() { + function(); + } else { + // We double-box because the first box is a fat pointer. + let boxed = Box::new(function) as Box; + let boxed2 = Box::new(boxed); + + let raw = Box::into_raw(boxed2); + + let res = winuser::PostThreadMessageA(self.thread_id, *EXEC_MSG_ID, + raw as *mut () as usize as WPARAM, 0); + // PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen + // as the events loop is still alive) or if the queue is full. + assert!(res != 0, "PostThreadMessage failed ; is the messages queue full?"); + } + } } } @@ -347,7 +294,7 @@ lazy_static! { // and LPARAM is unused. static ref EXEC_MSG_ID: u32 = { unsafe { - winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as LPCSTR) + winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8) } }; // Message sent by a `Window` when it wants to be destroyed by the main thread. @@ -366,49 +313,32 @@ lazy_static! { }; } -// There's no parameters passed to the callback function, so it needs to get its context stashed -// in a thread-local variable. -thread_local!(static CONTEXT_STASH: RefCell> = RefCell::new(None)); -struct ThreadLocalData { - sender: mpsc::Sender, - windows: HashMap>>, - file_drop_handlers: HashMap, // Each window has its own drop handler. - mouse_buttons_down: u32, -} - -// Utility function that dispatches an event on the current thread. -pub fn send_event(event: Event) { - CONTEXT_STASH.with(|context_stash| { - let context_stash = context_stash.borrow(); - - let _ = context_stash.as_ref().unwrap().sender.send(event); // Ignoring if closed - }); -} - /// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of /// the window. -unsafe fn capture_mouse(window: HWND) { - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - context_stash.mouse_buttons_down += 1; - winuser::SetCapture(window); - } - }); +unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) { + window_state.mouse_buttons_down += 1; + winuser::SetCapture(window); } /// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor /// is outside the window. -unsafe fn release_mouse() { - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - context_stash.mouse_buttons_down = context_stash.mouse_buttons_down.saturating_sub(1); - if context_stash.mouse_buttons_down == 0 { - winuser::ReleaseCapture(); - } - } - }); +unsafe fn release_mouse(window_state: &mut WindowState) { + window_state.mouse_buttons_down = window_state.mouse_buttons_down.saturating_sub(1); + if window_state.mouse_buttons_down == 0 { + winuser::ReleaseCapture(); + } +} + +const WINDOW_SUBCLASS_ID: UINT_PTR = 0; +pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) { + let input_ptr = Box::into_raw(Box::new(subclass_input)); + let subclass_result = unsafe{ commctrl::SetWindowSubclass( + window, + Some(callback), + WINDOW_SUBCLASS_ID, + input_ptr as DWORD_PTR + ) }; + assert_eq!(subclass_result, 1); } /// Any window whose callback is configured to this function will have its events propagated @@ -423,40 +353,20 @@ pub unsafe extern "system" fn callback( msg: UINT, wparam: WPARAM, lparam: LPARAM, + _: UINT_PTR, + subclass_input_ptr: DWORD_PTR ) -> LRESULT { - match msg { - winuser::WM_CREATE => { - use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE}; - let ole_init_result = ole2::OleInitialize(ptr::null_mut()); - // It is ok if the initialize result is `S_FALSE` because it might happen that - // multiple windows are created on the same thread. - if ole_init_result == OLE_E_WRONGCOMPOBJ { - panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); - } else if ole_init_result == RPC_E_CHANGED_MODE { - panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); - } - - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - - let drop_handlers = &mut context_stash.as_mut().unwrap().file_drop_handlers; - let new_handler = FileDropHandler::new(window); - let handler_interface_ptr = &mut (*new_handler.data).interface as LPDROPTARGET; - drop_handlers.insert(window, new_handler); - - assert_eq!(ole2::RegisterDragDrop(window, handler_interface_ptr), S_OK); - }); - 0 - }, + let subclass_input = &mut*(subclass_input_ptr as *mut SubclassInput); + match msg { winuser::WM_NCCREATE => { enable_non_client_dpi_scaling(window); - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, winuser::WM_CLOSE => { use events::WindowEvent::CloseRequested; - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CloseRequested }); @@ -465,27 +375,24 @@ pub unsafe extern "system" fn callback( winuser::WM_DESTROY => { use events::WindowEvent::Destroyed; - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - ole2::RevokeDragDrop(window); - let context_stash_mut = context_stash.as_mut().unwrap(); - context_stash_mut.file_drop_handlers.remove(&window); - context_stash_mut.windows.remove(&window); - }); - send_event(Event::WindowEvent { + ole2::RevokeDragDrop(window); + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Destroyed }); + + Box::from_raw(subclass_input); + drop(subclass_input); 0 }, winuser::WM_PAINT => { use events::WindowEvent::Redraw; - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Redraw, }); - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, // WM_MOVE supplies client area positions, so we send Moved here instead. @@ -499,14 +406,14 @@ pub unsafe extern "system" fn callback( ((*windowpos).x, (*windowpos).y), dpi_factor, ); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Moved(logical_position), }); } // This is necessary for us to still get sent WM_SIZE. - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, winuser::WM_SIZE => { @@ -514,21 +421,14 @@ pub unsafe extern "system" fn callback( let w = LOWORD(lparam as DWORD) as u32; let h = HIWORD(lparam as DWORD) as u32; - // Wait for the parent thread to process the resize event before returning from the - // callback. - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - let cstash = context_stash.as_mut().unwrap(); - - let dpi_factor = get_hwnd_scale_factor(window); - let logical_size = LogicalSize::from_physical((w, h), dpi_factor); - let event = Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Resized(logical_size), - }; + let dpi_factor = get_hwnd_scale_factor(window); + let logical_size = LogicalSize::from_physical((w, h), dpi_factor); + let event = Event::WindowEvent { + window_id: SuperWindowId(WindowId(window)), + event: Resized(logical_size), + }; - cstash.sender.send(event).ok(); - }); + subclass_input.send_event(event); 0 }, @@ -536,7 +436,7 @@ pub unsafe extern "system" fn callback( use std::mem; use events::WindowEvent::ReceivedCharacter; let chr: char = mem::transmute(wparam as u32); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: ReceivedCharacter(chr), }); @@ -553,23 +453,18 @@ pub unsafe extern "system" fn callback( winuser::WM_MOUSEMOVE => { use events::WindowEvent::{CursorEntered, CursorMoved}; - let mouse_outside_window = CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - if let Some(w) = context_stash.windows.get_mut(&window) { - let mut w = w.lock().unwrap(); - if !w.mouse_in_window { - w.mouse_in_window = true; - return true; - } - } + let mouse_outside_window = { + let mut window = subclass_input.window_state.lock().unwrap(); + if !window.mouse_in_window { + window.mouse_in_window = true; + true + } else { + false } - - false - }); + }; if mouse_outside_window { - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorEntered { device_id: DEVICE_ID }, }); @@ -588,7 +483,7 @@ pub unsafe extern "system" fn callback( let dpi_factor = get_hwnd_scale_factor(window); let position = LogicalPosition::from_physical((x, y), dpi_factor); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, }); @@ -598,23 +493,18 @@ pub unsafe extern "system" fn callback( winuser::WM_MOUSELEAVE => { use events::WindowEvent::CursorLeft; - let mouse_in_window = CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - if let Some(w) = context_stash.windows.get_mut(&window) { - let mut w = w.lock().unwrap(); - if w.mouse_in_window { - w.mouse_in_window = false; - return true; - } - } + let mouse_in_window = { + let mut window = subclass_input.window_state.lock().unwrap(); + if window.mouse_in_window { + window.mouse_in_window = false; + true + } else { + false } - - false - }); + }; if mouse_in_window { - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorLeft { device_id: DEVICE_ID } }); @@ -631,7 +521,7 @@ pub unsafe extern "system" fn callback( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, }); @@ -643,10 +533,10 @@ pub unsafe extern "system" fn callback( use events::ElementState::Pressed; use events::VirtualKeyCode; if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -661,7 +551,7 @@ pub unsafe extern "system" fn callback( // 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) { - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::ReceivedCharacter('\u{7F}'), }); @@ -674,7 +564,7 @@ pub unsafe extern "system" fn callback( winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { use events::ElementState::Released; if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -695,9 +585,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Left; use events::ElementState::Pressed; - capture_mouse(window); + capture_mouse(window, &mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() } }); @@ -709,9 +599,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Left; use events::ElementState::Released; - release_mouse(); + release_mouse(&mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() } }); @@ -723,9 +613,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Right; use events::ElementState::Pressed; - capture_mouse(window); + capture_mouse(window, &mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() } }); @@ -737,9 +627,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Right; use events::ElementState::Released; - release_mouse(); + release_mouse(&mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() } }); @@ -751,9 +641,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Middle; use events::ElementState::Pressed; - capture_mouse(window); + capture_mouse(window, &mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() } }); @@ -765,9 +655,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Middle; use events::ElementState::Released; - release_mouse(); + release_mouse(&mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() } }); @@ -780,9 +670,9 @@ pub unsafe extern "system" fn callback( use events::ElementState::Pressed; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - capture_mouse(window); + capture_mouse(window, &mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), modifiers: event::get_key_mods() } }); @@ -795,9 +685,9 @@ pub unsafe extern "system" fn callback( use events::ElementState::Released; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - release_mouse(); + release_mouse(&mut *subclass_input.window_state.lock().unwrap()); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), modifiers: event::get_key_mods() } }); @@ -811,12 +701,12 @@ pub unsafe extern "system" fn callback( _ => unreachable!(), }; - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id: wrap_device_id(lparam as _), event, }); - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, winuser::WM_INPUT => { @@ -835,21 +725,21 @@ pub unsafe extern "system" fn callback( let y = mouse.lLastY as f64; if x != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: Motion { axis: 0, value: x } }); } if y != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: Motion { axis: 1, value: y } }); } if x != 0.0 || y != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: MouseMotion { delta: (x, y) } }); @@ -858,7 +748,7 @@ pub unsafe extern "system" fn callback( if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: MouseWheel { delta: LineDelta(0.0, delta as f32) } }); @@ -872,7 +762,7 @@ pub unsafe extern "system" fn callback( // seem to be anything else reasonable to do for a mouse // button ID. let button = (index + 1) as _; - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: Button { button, @@ -906,7 +796,7 @@ pub unsafe extern "system" fn callback( ) { let virtual_keycode = vkey_to_winit_vkey(vkey); - send_event(Event::DeviceEvent { + subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(KeyboardInput { scancode, @@ -920,7 +810,7 @@ pub unsafe extern "system" fn callback( } } - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, winuser::WM_TOUCH => { @@ -939,7 +829,7 @@ pub unsafe extern "system" fn callback( let x = (input.x as f64) / 100f64; let y = (input.y as f64) / 100f64; let location = LogicalPosition::from_physical((x, y), dpi_factor); - send_event( Event::WindowEvent { + subclass_input.send_event( Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::Touch(Touch { phase: @@ -965,7 +855,7 @@ pub unsafe extern "system" fn callback( winuser::WM_SETFOCUS => { use events::WindowEvent::{Focused, CursorMoved}; - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Focused(true) }); @@ -975,7 +865,7 @@ pub unsafe extern "system" fn callback( let dpi_factor = get_hwnd_scale_factor(window); let position = LogicalPosition::from_physical((x, y), dpi_factor); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, }); @@ -985,7 +875,7 @@ pub unsafe extern "system" fn callback( winuser::WM_KILLFOCUS => { use events::WindowEvent::Focused; - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Focused(false) }); @@ -993,29 +883,22 @@ pub unsafe extern "system" fn callback( }, winuser::WM_SETCURSOR => { - let call_def_window_proc = CONTEXT_STASH.with(|context_stash| { - context_stash - .borrow() - .as_ref() - .and_then(|cstash| cstash.windows.get(&window)) - .map(|window_state_mutex| { - let window_state = window_state_mutex.lock().unwrap(); - if window_state.mouse_in_window { - let cursor = winuser::LoadCursorW( - ptr::null_mut(), - window_state.cursor.0, - ); - winuser::SetCursor(cursor); - false - } else { - true - } - }) - .unwrap_or(true) - }); + let call_def_window_proc = { + let window_state = subclass_input.window_state.lock().unwrap(); + if window_state.mouse_in_window { + let cursor = winuser::LoadCursorW( + ptr::null_mut(), + window_state.cursor.0, + ); + winuser::SetCursor(cursor); + false + } else { + true + } + }; if call_def_window_proc { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { 0 } @@ -1028,29 +911,21 @@ pub unsafe extern "system" fn callback( winuser::WM_GETMINMAXINFO => { let mmi = lparam as *mut winuser::MINMAXINFO; - //(*mmi).max_position = winapi::shared::windef::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor. - //(*mmi).max_size = winapi::shared::windef::POINT { x: .., y: .. }; // The dimensions of the primary monitor. - - CONTEXT_STASH.with(|context_stash| { - if let Some(cstash) = context_stash.borrow().as_ref() { - if let Some(wstash) = cstash.windows.get(&window) { - let window_state = wstash.lock().unwrap(); - - if window_state.min_size.is_some() || window_state.max_size.is_some() { - let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD; - let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD; - if let Some(min_size) = window_state.min_size { - let (width, height) = adjust_size(min_size, style, ex_style); - (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 }; - } - if let Some(max_size) = window_state.max_size { - let (width, height) = adjust_size(max_size, style, ex_style); - (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 }; - } - } - } + + let window_state = subclass_input.window_state.lock().unwrap(); + + if window_state.min_size.is_some() || window_state.max_size.is_some() { + let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD; + let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD; + if let Some(min_size) = window_state.min_size { + let (width, height) = adjust_size(min_size, style, ex_style); + (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 }; } - }); + if let Some(max_size) = window_state.max_size { + let (width, height) = adjust_size(max_size, style, ex_style); + (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 }; + } + } 0 }, @@ -1067,34 +942,27 @@ pub unsafe extern "system" fn callback( let new_dpi_x = u32::from(LOWORD(wparam as DWORD)); let new_dpi_factor = dpi_to_scale_factor(new_dpi_x); - let suppress_resize = CONTEXT_STASH.with(|context_stash| { - context_stash - .borrow() - .as_ref() - .and_then(|cstash| cstash.windows.get(&window)) - .map(|window_state_mutex| { - let mut window_state = window_state_mutex.lock().unwrap(); - let suppress_resize = window_state.saved_window_info - .as_mut() - .map(|saved_window_info| { - let dpi_changed = if !saved_window_info.is_fullscreen { - saved_window_info.dpi_factor.take() != Some(new_dpi_factor) - } else { - false - }; - !dpi_changed || saved_window_info.is_fullscreen - }) - .unwrap_or(false); - // Now we adjust the min/max dimensions for the new DPI. - if !suppress_resize { - let old_dpi_factor = window_state.dpi_factor; - window_state.update_min_max(old_dpi_factor, new_dpi_factor); - } - window_state.dpi_factor = new_dpi_factor; - suppress_resize + let suppress_resize = { + let mut window_state = subclass_input.window_state.lock().unwrap(); + let suppress_resize = window_state.saved_window_info + .as_mut() + .map(|saved_window_info| { + let dpi_changed = if !saved_window_info.is_fullscreen { + saved_window_info.dpi_factor.take() != Some(new_dpi_factor) + } else { + false + }; + !dpi_changed || saved_window_info.is_fullscreen }) - .unwrap_or(false) - }); + .unwrap_or(false); + // Now we adjust the min/max dimensions for the new DPI. + if !suppress_resize { + let old_dpi_factor = window_state.dpi_factor; + window_state.update_min_max(old_dpi_factor, new_dpi_factor); + } + window_state.dpi_factor = new_dpi_factor; + suppress_resize + }; // This prevents us from re-applying DPI adjustment to the restored size after exiting // fullscreen (the restored size is already DPI adjusted). @@ -1112,7 +980,7 @@ pub unsafe extern "system" fn callback( ); } - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: HiDpiFactorChanged(new_dpi_factor), }); @@ -1127,7 +995,7 @@ pub unsafe extern "system" fn callback( } else if msg == *INITIAL_DPI_MSG_ID { use events::WindowEvent::HiDpiFactorChanged; let scale_factor = dpi_to_scale_factor(wparam as u32); - send_event(Event::WindowEvent { + subclass_input.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: HiDpiFactorChanged(scale_factor), }); @@ -1165,7 +1033,7 @@ pub unsafe extern "system" fn callback( ); 0 } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } } } diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index 95c821e7a7..899e461077 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -10,10 +10,11 @@ use std::sync::mpsc::channel; use winapi::ctypes::c_int; use winapi::shared::minwindef::{BOOL, DWORD, FALSE, LPARAM, TRUE, UINT, WORD, WPARAM}; use winapi::shared::windef::{HWND, LPPOINT, POINT, RECT}; -use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser}; +use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser, ole2}; use winapi::um::objbase::COINIT_MULTITHREADED; use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2}; use winapi::um::wingdi::{CreateRectRgn, DeleteObject}; +use winapi::um::oleidl::LPDROPTARGET; use winapi::um::winnt::{LONG, LPCWSTR}; use { @@ -32,6 +33,7 @@ use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventLoop, INITIAL_D use platform::platform::icon::{self, IconType, WinIcon}; use platform::platform::monitor::get_available_monitors; use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; +use platform::platform::drop_handler::FileDropHandler; use platform::platform::util; const WS_RESIZABLE: DWORD = winuser::WS_SIZEBOX | winuser::WS_MAXIMIZEBOX; @@ -76,15 +78,11 @@ impl Window { w_attr: WindowAttributes, pl_attr: PlatformSpecificWindowBuilderAttributes, ) -> Result { - let (tx, rx) = channel(); - let proxy = events_loop.create_proxy(); - events_loop.execute_in_thread(move |inserter| { - // We dispatch an `init` function because of code style. - // First person to remove the need for cloning here gets a cookie! - let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) }; - let _ = tx.send(win); - }); - rx.recv().unwrap() + // We dispatch an `init` function because of code style. + // First person to remove the need for cloning here gets a cookie! + // + // done. you owe me -- ossi + unsafe { init(w_attr, pl_attr, events_loop) } } pub fn set_title(&self, text: &str) { @@ -323,7 +321,7 @@ impl Window { _ => winuser::IDC_ARROW, // use arrow for the missing cases. }); self.window_state.lock().unwrap().cursor = cursor_id; - self.events_loop_proxy.execute_in_thread(move |_| unsafe { + self.events_loop_proxy.execute_in_thread(move || unsafe { let cursor = winuser::LoadCursorW( ptr::null_mut(), cursor_id.0, @@ -385,7 +383,7 @@ impl Window { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { let result = unsafe { Self::grab_cursor_inner(&window, grab) }; if result.is_ok() { window_state.lock().unwrap().cursor_grabbed = grab; @@ -411,7 +409,7 @@ impl Window { if hide == window_state_lock.cursor_hidden { return; } let (tx, rx) = channel(); let window_state = Arc::clone(&self.window_state); - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { unsafe { Self::hide_cursor_inner(hide) }; window_state.lock().unwrap().cursor_hidden = hide; let _ = tx.send(()); @@ -459,7 +457,7 @@ impl Window { let window = self.window.clone(); unsafe { // `ShowWindow` resizes the window, so it must be called from the main thread. - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { winuser::ShowWindow( window.0, if maximized { @@ -524,7 +522,7 @@ impl Window { // We're restoring the window to its size and position from before being fullscreened. // `ShowWindow` resizes the window, so it must be called from the main thread. - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { let _ = Self::grab_cursor_inner(&window, false); if resizable { @@ -577,7 +575,7 @@ impl Window { let window_state = Arc::clone(&self.window_state); let (style, ex_style) = self.set_fullscreen_style(&mut window_state_lock); - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { let _ = Self::grab_cursor_inner(&window, false); winuser::SetWindowLongW( @@ -674,7 +672,7 @@ impl Window { let window = self.window.clone(); - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style); winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style); winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _); @@ -702,7 +700,7 @@ impl Window { let mut window_state = self.window_state.lock().unwrap(); if mem::replace(&mut window_state.always_on_top, always_on_top) != always_on_top { let window = self.window.clone(); - self.events_loop_proxy.execute_in_thread(move |_| { + self.events_loop_proxy.execute_in_thread(move || { let insert_after = if always_on_top { winuser::HWND_TOPMOST } else { @@ -805,8 +803,7 @@ pub unsafe fn adjust_size( unsafe fn init( mut attributes: WindowAttributes, mut pl_attribs: PlatformSpecificWindowBuilderAttributes, - inserter: events_loop::Inserter, - events_loop_proxy: events_loop::EventLoopProxy, + event_loop: &events_loop::EventLoop, ) -> Result { let title = OsStr::new(&attributes.title) .encode_wide() @@ -1013,6 +1010,7 @@ unsafe fn init( maximized: attributes.maximized, resizable: attributes.resizable, always_on_top: attributes.always_on_top, + mouse_buttons_down: 0 }; // Creating a mutex to track the current window state Arc::new(Mutex::new(window_state)) @@ -1048,7 +1046,7 @@ unsafe fn init( let win = Window { window: real_window, window_state, - events_loop_proxy, + events_loop_proxy: event_loop.create_proxy(), }; win.set_maximized(attributes.maximized); @@ -1057,7 +1055,31 @@ unsafe fn init( force_window_active(win.window.0); } - inserter.insert(win.window.0, win.window_state.clone()); + let file_drop_handler = { + use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK}; + let ole_init_result = ole2::OleInitialize(ptr::null_mut()); + // It is ok if the initialize result is `S_FALSE` because it might happen that + // multiple windows are created on the same thread. + if ole_init_result == OLE_E_WRONGCOMPOBJ { + panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); + } else if ole_init_result == RPC_E_CHANGED_MODE { + panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); + } + + let file_drop_handler = FileDropHandler::new(win.window.0, event_loop.event_queue.clone()); + let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET; + + assert_eq!(ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK); + file_drop_handler + }; + + let subclass_input = events_loop::SubclassInput { + window_state: win.window_state.clone(), + event_queue: event_loop.event_queue.clone(), + file_drop_handler + }; + + events_loop::subclass_window(win.window.0, subclass_input); Ok(win) } @@ -1083,7 +1105,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(events_loop::callback), + lpfnWndProc: Some(winuser::DefWindowProcW), cbClsExtra: 0, cbWndExtra: 0, hInstance: libloaderapi::GetModuleHandleW(ptr::null()),