From b02c03b4c02e162bc06f85b47fe8a2509088d852 Mon Sep 17 00:00:00 2001 From: Osspial Date: Wed, 21 Feb 2018 20:08:14 -0500 Subject: [PATCH 1/4] On win32, bound window size upon window creation --- src/platform/windows/window.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index 2211e76390..e78790d8cf 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -371,8 +371,12 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild // building a RECT object with coordinates let mut rect = RECT { - left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as LONG, - top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as LONG, + left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 + .max(window.min_dimensions.map(|m| m.0).unwrap_or(0)) + .min(window.max_dimensions.map(|m| m.0).unwrap_or(u32::max_value())) as LONG, + top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 + .max(window.min_dimensions.map(|m| m.1).unwrap_or(0)) + .min(window.max_dimensions.map(|m| m.1).unwrap_or(u32::max_value())) as LONG, }; // switching to fullscreen if necessary From 8ec3ef9dc84d7f8b16f33ebe8e5471f983ac2708 Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 1 Mar 2018 14:37:26 -0500 Subject: [PATCH 2/4] Begin work moving win32 backend to single thread --- Cargo.toml | 1 + src/platform/windows/events_loop.rs | 282 +++++++++------------------- src/platform/windows/window.rs | 32 ++-- 3 files changed, 99 insertions(+), 216 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 655b78b9a5..a60e20b2b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ features = [ "libloaderapi", "windowsx", "hidusage", + "commctrl" ] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 1a62c6abad..12e92f7ef7 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -12,24 +12,20 @@ //! 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::{ptr, mem}; +use std::rc::Rc; use std::cell::RefCell; use std::collections::HashMap; use std::ffi::OsString; -use std::mem; use std::os::windows::ffi::OsStringExt; use std::os::windows::io::AsRawHandle; -use std::ptr; use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Barrier; -use std::sync::Mutex; -use std::sync::Condvar; -use std::thread; use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, UINT, LRESULT, MAX_PATH}; use winapi::shared::windef::{HWND, POINT}; +use winapi::shared::basetsd::{UINT_PTR, DWORD_PTR}; use winapi::shared::windowsx; -use winapi::um::{winuser, shellapi, processthreadsapi}; +use winapi::um::{winuser, shellapi, processthreadsapi, commctrl}; use platform::platform::event; use platform::platform::Cursor; @@ -58,153 +54,91 @@ pub struct WindowState { pub mouse_in_window: bool, } -/// 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(crate) struct SubclassInput { + pub window_state: Rc } pub struct EventsLoop { // 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, - // Variable that contains the block state of the win32 event loop thread during a WM_SIZE event. - // The mutex's value is `true` when it's blocked, and should be set to false when it's done - // blocking. That's done by the parent thread when it receives a Resized event. - win32_block_loop: Arc<(Mutex, Condvar)> + receiver: mpsc::Receiver } impl EventsLoop { pub fn new() -> EventsLoop { // The main events transfer channel. let (tx, rx) = mpsc::channel(); - let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new())); - let win32_block_loop_child = win32_block_loop.clone(); - - // 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), - win32_block_loop: win32_block_loop_child, - 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() }; EventsLoop { thread_id, - receiver: rx, - win32_block_loop + 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 - }; - let is_resize = match event { - Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true, - _ => false - }; - - callback(event); - if is_resize { - let (ref mutex, ref cvar) = *self.win32_block_loop; - let mut block_thread = mutex.lock().unwrap(); - *block_thread = false; - cvar.notify_all(); - } - } + unimplemented!() + // 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::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) == 0 { + // // Only happens if the message is `WM_QUIT`. + // debug_assert_eq!(msg.message, winuser::WM_QUIT); + // break; + // } + + // match msg.message { + // x if x == *WAKEUP_MSG_ID => { + // callback(Event::Awakened); + // }, + // _ => { + // // Calls `callback` below. + // winuser::TranslateMessage(&msg); + // winuser::DispatchMessageW(&msg); + // } + // } + // } + // } } 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 - }; - let is_resize = match event { - Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true, - _ => false - }; - - let flow = callback(event); - if is_resize { - let (ref mutex, ref cvar) = *self.win32_block_loop; - let mut block_thread = mutex.lock().unwrap(); - *block_thread = false; - cvar.notify_all(); - } - match flow { - ControlFlow::Continue => continue, - ControlFlow::Break => break, + 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); + break; + } + + match msg.message { + x if x == *WAKEUP_MSG_ID => { + callback(Event::Awakened); + }, + _ => { + // Calls `callback` below. + winuser::TranslateMessage(&msg); + winuser::DispatchMessageW(&msg); + } + } } } } @@ -214,39 +148,6 @@ impl EventsLoop { 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 - { - unsafe { - 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?"); - } - } -} - -impl Drop for EventsLoop { - fn drop(&mut self) { - unsafe { - // Posting `WM_QUIT` will cause `GetMessage` to stop. - winuser::PostThreadMessageA(self.thread_id, winuser::WM_QUIT, 0, 0); - } - } } #[derive(Clone)] @@ -281,23 +182,22 @@ lazy_static! { winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8) } }; - // Message sent when we want to execute a closure in the thread. - // WPARAM contains a Box> that must be retreived with `Box::from_raw`, - // and LPARAM is unused. - static ref EXEC_MSG_ID: u32 = { - unsafe { - winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8) - } - }; } -// 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)); +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 = commctrl::SetWindowSubclass( + window, + Some(callback), + WINDOW_SUBCLASS_ID, + input_ptr as DWORD_PTR + ); + assert_eq!(subclass_result, 1); +} struct ThreadLocalData { sender: mpsc::Sender, - windows: HashMap>>, - win32_block_loop: Arc<(Mutex, Condvar)>, + windows: HashMap>>, mouse_buttons_down: u32 } @@ -343,10 +243,12 @@ unsafe fn release_mouse() { // // Returning 0 tells the Win32 API that the message has been processed. // FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary -pub unsafe extern "system" fn callback(window: HWND, msg: UINT, - wparam: WPARAM, lparam: LPARAM) - -> LRESULT -{ +pub unsafe extern "system" fn callback( + window: HWND, msg: UINT, + wparam: WPARAM, lparam: LPARAM, + _: UINT_PTR, subclass_input_ptr: DWORD_PTR +) -> LRESULT { + let subclass_input = unsafe{ &mut*(subclass_input_ptr as *mut SubclassInput) }; match msg { winuser::WM_CLOSE => { use events::WindowEvent::Closed; @@ -360,6 +262,11 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, }); winuser::DefWindowProcW(window, msg, wparam, lparam) }, + winuser::WM_DESTROY => { + Box::from_raw(subclass_input); + drop(subclass_input); + 0 + } winuser::WM_ERASEBKGND => { 1 @@ -381,24 +288,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, event: Resized(w, h), }; - // If this window has been inserted into the window map, the resize event happened - // during the event loop. If it hasn't, the event happened on window creation and - // should be ignored. - if cstash.windows.get(&window).is_some() { - let (ref mutex, ref cvar) = *cstash.win32_block_loop; - let mut block_thread = mutex.lock().unwrap(); - *block_thread = true; - - // The event needs to be sent after the lock to ensure that `notify_all` is - // called after `wait`. - cstash.sender.send(event).ok(); - - while *block_thread { - block_thread = cvar.wait(block_thread).unwrap(); - } - } else { - cstash.sender.send(event).ok(); - } + cstash.sender.send(event).ok(); }); 0 }, diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index e78790d8cf..16bee21741 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -6,9 +6,9 @@ use std::mem; use std::os::raw; use std::os::windows::ffi::OsStrExt; use std::ptr; -use std::sync::Arc; -use std::sync::Mutex; use std::sync::mpsc::channel; +use std::rc::Rc; +use std::cell::RefCell; use platform::platform::events_loop; use platform::platform::EventsLoop; @@ -34,7 +34,7 @@ pub struct Window { window: WindowWrapper, /// The current window state. - window_state: Arc>, + window_state: Rc>, } unsafe impl Send for Window {} @@ -49,13 +49,7 @@ impl Window { let (tx, rx) = channel(); - events_loop.execute_in_thread(move |inserter| { - // We dispatch an `init` function because of code style. - let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter) }; - let _ = tx.send(win); - }); - - rx.recv().unwrap() + unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap()) } } pub fn set_title(&self, text: &str) { @@ -346,13 +340,11 @@ impl Window { } } -impl Drop for Window { +impl Drop for WindowWrapper { #[inline] fn drop(&mut self) { unsafe { - // We are sending WM_CLOSE, and our callback will process this by calling DefWindowProcW, - // which in turn will send a WM_DESTROY. - winuser::PostMessageW(self.window.0, winuser::WM_CLOSE, 0, 0); + winuser::DestroyWindow(self.0); } } } @@ -361,8 +353,10 @@ impl Drop for Window { #[doc(hidden)] pub struct WindowWrapper(HWND, HDC); -unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, - inserter: events_loop::Inserter) -> Result { +unsafe fn init( + window: WindowAttributes, + pl_attribs: PlatformSpecificWindowBuilderAttributes, +) -> Result { let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter()) .collect::>(); @@ -478,15 +472,13 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild } // Creating a mutex to track the current window state - let window_state = Arc::new(Mutex::new(events_loop::WindowState { + let window_state = Rc::new(RefCell::new(events_loop::WindowState { cursor: winuser::IDC_ARROW, // use arrow by default cursor_state: CursorState::Normal, attributes: window.clone(), mouse_in_window: false, })); - inserter.insert(real_window.0, window_state.clone()); - // making the window transparent if window.transparent { let bb = dwmapi::DWM_BLURBEHIND { @@ -518,7 +510,7 @@ unsafe fn register_window_class() -> Vec { 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()), From 3873f15216add7f117aae5753c9a6f4ab4a571b9 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 2 Apr 2018 14:36:47 -0400 Subject: [PATCH 3/4] Continue port work --- src/platform/windows/events_loop.rs | 333 +++++++++++----------------- src/platform/windows/window.rs | 19 +- 2 files changed, 144 insertions(+), 208 deletions(-) diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 12e92f7ef7..cb152c2c57 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -15,11 +15,9 @@ use std::{ptr, mem}; use std::rc::Rc; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::VecDeque; use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; -use std::os::windows::io::AsRawHandle; -use std::sync::mpsc; use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, UINT, LRESULT, MAX_PATH}; use winapi::shared::windef::{HWND, POINT}; @@ -52,63 +50,63 @@ pub struct WindowState { pub attributes: WindowAttributes, /// Will contain `true` if the mouse is hovering the window. pub mouse_in_window: bool, + pub mouse_buttons_down: u8 } pub(crate) struct SubclassInput { - pub window_state: Rc + pub window_state: Rc>, + pub event_queue: Rc>> } pub struct EventsLoop { // 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 EventsLoop { pub fn new() -> EventsLoop { - // The main events transfer channel. - let (tx, rx) = mpsc::channel(); - let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; EventsLoop { thread_id, - receiver: rx + event_queue: Rc::new(RefCell::new(VecDeque::new())) } } pub fn poll_events(&mut self, mut callback: F) where F: FnMut(Event) { - unimplemented!() - // 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::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) == 0 { - // // Only happens if the message is `WM_QUIT`. - // debug_assert_eq!(msg.message, winuser::WM_QUIT); - // break; - // } - - // match msg.message { - // x if x == *WAKEUP_MSG_ID => { - // callback(Event::Awakened); - // }, - // _ => { - // // Calls `callback` below. - // winuser::TranslateMessage(&msg); - // winuser::DispatchMessageW(&msg); - // } - // } - // } - // } + 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::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) == 0 { + return; + } + + match msg.message { + x if x == *WAKEUP_MSG_ID => { + callback(Event::Awakened); + }, + _ => { + // Calls `callback` below. + winuser::TranslateMessage(&msg); + winuser::DispatchMessageW(&msg); + } + } + + let mut event_queue = self.event_queue.borrow_mut(); + for event in event_queue.drain(..) { + callback(event); + } + } + } } pub fn run_forever(&mut self, mut callback: F) @@ -126,12 +124,14 @@ impl EventsLoop { 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; + return; } match msg.message { x if x == *WAKEUP_MSG_ID => { - callback(Event::Awakened); + if ControlFlow::Break == callback(Event::Awakened) { + return; + } }, _ => { // Calls `callback` below. @@ -139,6 +139,13 @@ impl EventsLoop { winuser::DispatchMessageW(&msg); } } + + let mut event_queue = self.event_queue.borrow_mut(); + for event in event_queue.drain(..) { + if ControlFlow::Break == callback(event) { + return; + } + } } } } @@ -187,54 +194,14 @@ lazy_static! { 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 = commctrl::SetWindowSubclass( + let subclass_result = unsafe{ commctrl::SetWindowSubclass( window, Some(callback), WINDOW_SUBCLASS_ID, input_ptr as DWORD_PTR - ); + ) }; assert_eq!(subclass_result, 1); } -struct ThreadLocalData { - sender: mpsc::Sender, - windows: HashMap>>, - mouse_buttons_down: u32 -} - -// Utility function that dispatches an event on the current thread. -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); - } - }); -} - -/// 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(); - } - } - }); -} /// 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. @@ -248,18 +215,15 @@ pub unsafe extern "system" fn callback( wparam: WPARAM, lparam: LPARAM, _: UINT_PTR, subclass_input_ptr: DWORD_PTR ) -> LRESULT { - let subclass_input = unsafe{ &mut*(subclass_input_ptr as *mut SubclassInput) }; + let subclass_input = &mut*(subclass_input_ptr as *mut SubclassInput); + match msg { winuser::WM_CLOSE => { use events::WindowEvent::Closed; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Closed }); - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - context_stash.as_mut().unwrap().windows.remove(&window); - }); winuser::DefWindowProcW(window, msg, wparam, lparam) }, winuser::WM_DESTROY => { @@ -277,18 +241,9 @@ 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 event = Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Resized(w, h), - }; - - cstash.sender.send(event).ok(); + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { + window_id: SuperWindowId(WindowId(window)), + event: Resized(w, h), }); 0 }, @@ -297,7 +252,7 @@ pub unsafe extern "system" fn callback( use events::WindowEvent::Moved; let x = LOWORD(lparam as DWORD) as i32; let y = HIWORD(lparam as DWORD) as i32; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Moved(x, y), }); @@ -308,7 +263,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.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: ReceivedCharacter(chr), }); @@ -325,23 +280,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_state = subclass_input.window_state.borrow_mut(); + if !window_state.mouse_in_window { + window_state.mouse_in_window = true; + true + } else { + false } - - false - }); + }; if mouse_outside_window { - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorEntered { device_id: DEVICE_ID }, }); @@ -358,7 +308,7 @@ pub unsafe extern "system" fn callback( let x = windowsx::GET_X_LPARAM(lparam) as f64; let y = windowsx::GET_Y_LPARAM(lparam) as f64; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() }, }); @@ -368,23 +318,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_state = subclass_input.window_state.borrow_mut(); + if window_state.mouse_in_window { + window_state.mouse_in_window = false; + true + } else { + false } - - false - }); + }; if mouse_in_window { - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorLeft { device_id: DEVICE_ID } }); @@ -402,12 +347,12 @@ 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.event_queue.borrow_mut().push_back(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() }, }); - send_event(Event::DeviceEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::DeviceEvent { device_id: DEVICE_ID, event: DeviceEvent::MouseWheel { delta: LineDelta(0.0, value) }, }); @@ -422,7 +367,7 @@ pub unsafe extern "system" fn callback( winuser::DefWindowProcW(window, msg, wparam, lparam) } else { let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam); - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -437,7 +382,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.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::ReceivedCharacter('\u{7F}'), }); @@ -449,7 +394,7 @@ pub unsafe extern "system" fn callback( winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { use events::ElementState::Released; let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam); - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -469,9 +414,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Left; use events::ElementState::Pressed; - capture_mouse(window); + subclass_input.window_state.borrow_mut().mouse_buttons_down += 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() } }); @@ -483,9 +428,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Left; use events::ElementState::Released; - release_mouse(); + subclass_input.window_state.borrow_mut().mouse_buttons_down -= 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() } }); @@ -497,9 +442,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Right; use events::ElementState::Pressed; - capture_mouse(window); + subclass_input.window_state.borrow_mut().mouse_buttons_down += 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() } }); @@ -511,9 +456,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Right; use events::ElementState::Released; - release_mouse(); + subclass_input.window_state.borrow_mut().mouse_buttons_down -= 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() } }); @@ -525,9 +470,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Middle; use events::ElementState::Pressed; - capture_mouse(window); + subclass_input.window_state.borrow_mut().mouse_buttons_down += 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() } }); @@ -539,9 +484,9 @@ pub unsafe extern "system" fn callback( use events::MouseButton::Middle; use events::ElementState::Released; - release_mouse(); + subclass_input.window_state.borrow_mut().mouse_buttons_down -= 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() } }); @@ -554,9 +499,9 @@ pub unsafe extern "system" fn callback( use events::ElementState::Pressed; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - capture_mouse(window); + subclass_input.window_state.borrow_mut().mouse_buttons_down += 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(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() } }); @@ -569,9 +514,9 @@ pub unsafe extern "system" fn callback( use events::ElementState::Released; let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - release_mouse(); + subclass_input.window_state.borrow_mut().mouse_buttons_down -= 1; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(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() } }); @@ -593,21 +538,21 @@ pub unsafe extern "system" fn callback( let y = mouse.lLastY as f64; if x != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::DeviceEvent { device_id: DEVICE_ID, event: Motion { axis: 0, value: x } }); } if y != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::DeviceEvent { device_id: DEVICE_ID, event: Motion { axis: 1, value: y } }); } if x != 0.0 || y != 0.0 { - send_event(Event::DeviceEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::DeviceEvent { device_id: DEVICE_ID, event: MouseMotion { delta: (x, y) } }); @@ -622,7 +567,7 @@ pub unsafe extern "system" fn callback( winuser::WM_SETFOCUS => { use events::WindowEvent::{Focused, CursorMoved}; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Focused(true) }); @@ -630,7 +575,7 @@ pub unsafe extern "system" fn callback( let x = windowsx::GET_X_LPARAM(lparam) as f64; let y = windowsx::GET_Y_LPARAM(lparam) as f64; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() }, }); @@ -639,7 +584,7 @@ pub unsafe extern "system" fn callback( winuser::WM_KILLFOCUS => { use events::WindowEvent::Focused; - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: Focused(false) }); @@ -647,32 +592,24 @@ pub unsafe extern "system" fn callback( }, winuser::WM_SETCURSOR => { - let call_def_window_proc = CONTEXT_STASH.with(|context_stash| { - let cstash = context_stash.borrow(); - let mut call_def_window_proc = false; - if let Some(cstash) = cstash.as_ref() { - if let Some(w_stash) = cstash.windows.get(&window) { - if let Ok(window_state) = w_stash.lock() { - if window_state.mouse_in_window { - match window_state.cursor_state { - CursorState::Normal => { - winuser::SetCursor(winuser::LoadCursorW( - ptr::null_mut(), - window_state.cursor)); - }, - CursorState::Grab | CursorState::Hide => { - winuser::SetCursor(ptr::null_mut()); - } - } - } else { - call_def_window_proc = true; - } + let call_def_window_proc = { + let window_state = subclass_input.window_state.borrow_mut(); + if window_state.mouse_in_window { + match window_state.cursor_state { + CursorState::Normal => { + winuser::SetCursor(winuser::LoadCursorW( + ptr::null_mut(), + window_state.cursor)); + }, + CursorState::Grab | CursorState::Hide => { + winuser::SetCursor(ptr::null_mut()); } } + false + } else { + true } - - call_def_window_proc - }); + }; if call_def_window_proc { winuser::DefWindowProcW(window, msg, wparam, lparam) @@ -692,7 +629,7 @@ pub unsafe extern "system" fn callback( let nch = shellapi::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as u32) as usize; if nch > 0 { - send_event(Event::WindowEvent { + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()) }); @@ -708,27 +645,21 @@ pub unsafe extern "system" fn callback( //(*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(); + let window_state = subclass_input.window_state.borrow(); - match window_state.attributes.min_dimensions { - Some((width, height)) => { - (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 }; - }, - None => { } - } + match window_state.attributes.min_dimensions { + Some((width, height)) => { + (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 }; + }, + None => { } + } - match window_state.attributes.max_dimensions { - Some((width, height)) => { - (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 }; - }, - None => { } - } - } - } - }); + match window_state.attributes.max_dimensions { + Some((width, height)) => { + (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 }; + }, + None => { } + } 0 }, diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index 16bee21741..7a9e586a78 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -6,11 +6,10 @@ use std::mem; use std::os::raw; use std::os::windows::ffi::OsStrExt; use std::ptr; -use std::sync::mpsc::channel; use std::rc::Rc; use std::cell::RefCell; -use platform::platform::events_loop; +use platform::platform::events_loop::{self, SubclassInput}; use platform::platform::EventsLoop; use platform::platform::PlatformSpecificWindowBuilderAttributes; use platform::platform::MonitorId; @@ -47,9 +46,15 @@ impl Window { let mut w_attr = Some(w_attr.clone()); let mut pl_attr = Some(pl_attr.clone()); - let (tx, rx) = channel(); + let window = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap()) }; + if let Ok(ref window) = window { + events_loop::subclass_window(window.window.0, SubclassInput { + window_state: window.window_state.clone(), + event_queue: events_loop.event_queue.clone() + }); + } - unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap()) } + window } pub fn set_title(&self, text: &str) { @@ -227,14 +232,13 @@ impl Window { _ => winuser::IDC_ARROW, // use arrow for the missing cases. }; - let mut cur = self.window_state.lock().unwrap(); - cur.cursor = cursor_id; + self.window_state.borrow_mut().cursor = cursor_id; } // TODO: it should be possible to rework this function by using the `execute_in_thread` method // of the events loop. pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { - let mut current_state = self.window_state.lock().unwrap(); + let mut current_state = self.window_state.borrow_mut(); let foreground_thread_id = unsafe { winuser::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) }; let current_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; @@ -477,6 +481,7 @@ unsafe fn init( cursor_state: CursorState::Normal, attributes: window.clone(), mouse_in_window: false, + mouse_buttons_down: 0 })); // making the window transparent From f3103eaae9967bcf752ff584fe612a04f70809f6 Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 2 Apr 2018 18:43:44 -0400 Subject: [PATCH 4/4] Change DefWindowProcW calls to DefSubclassProc --- src/platform/windows/events_loop.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index cb152c2c57..b40c3d4247 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -224,7 +224,7 @@ pub unsafe extern "system" fn callback( window_id: SuperWindowId(WindowId(window)), event: Closed }); - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, winuser::WM_DESTROY => { Box::from_raw(subclass_input); @@ -364,7 +364,7 @@ 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 { let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam); subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { @@ -561,7 +561,7 @@ pub unsafe extern "system" fn callback( 0 } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } }, @@ -612,7 +612,7 @@ pub unsafe extern "system" fn callback( }; if call_def_window_proc { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { 0 } @@ -665,7 +665,7 @@ pub unsafe extern "system" fn callback( }, _ => { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } } }