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..b40c3d4247 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -12,24 +12,18 @@ //! 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::collections::VecDeque; 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; @@ -56,128 +50,61 @@ 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 } -/// 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 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, - // 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)> + pub(crate) event_queue: Rc>> } 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 + event_queue: Rc::new(RefCell::new(VecDeque::new())) } } 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 - }; + 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); + } + } - 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(); + let mut event_queue = self.event_queue.borrow_mut(); + for event in event_queue.drain(..) { + callback(event); + } } } } @@ -185,26 +112,40 @@ impl EventsLoop { 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 - }; + 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); - 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, + match msg.message { + x if x == *WAKEUP_MSG_ID => { + if ControlFlow::Break == callback(Event::Awakened) { + return; + } + }, + _ => { + // 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; + } + } } } } @@ -214,39 +155,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,59 +189,18 @@ 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)); -struct ThreadLocalData { - sender: mpsc::Sender, - windows: HashMap>>, - win32_block_loop: Arc<(Mutex, Condvar)>, - 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(); - } - } - }); +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 @@ -343,23 +210,27 @@ 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 = &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) + commctrl::DefSubclassProc(window, msg, wparam, lparam) }, + winuser::WM_DESTROY => { + Box::from_raw(subclass_input); + drop(subclass_input); + 0 + } winuser::WM_ERASEBKGND => { 1 @@ -370,35 +241,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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), - }; - - // 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(); - } + subclass_input.event_queue.borrow_mut().push_back(Event::WindowEvent { + window_id: SuperWindowId(WindowId(window)), + event: Resized(w, h), }); 0 }, @@ -407,7 +252,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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), }); @@ -418,7 +263,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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), }); @@ -435,23 +280,18 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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 }, }); @@ -468,7 +308,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() }, }); @@ -478,23 +318,18 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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 } }); @@ -512,12 +347,12 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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) }, }); @@ -529,10 +364,10 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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); - 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, @@ -547,7 +382,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, // 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}'), }); @@ -559,7 +394,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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, @@ -579,9 +414,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -593,9 +428,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -607,9 +442,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -621,9 +456,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -635,9 +470,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -649,9 +484,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -664,9 +499,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -679,9 +514,9 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() } }); @@ -703,21 +538,21 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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) } }); @@ -726,13 +561,13 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 0 } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } }, 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) }); @@ -740,7 +575,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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() }, }); @@ -749,7 +584,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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) }); @@ -757,35 +592,27 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, }, 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) + commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { 0 } @@ -802,7 +629,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, 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()) }); @@ -818,33 +645,27 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, //(*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 }, _ => { - 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 2211e76390..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::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::events_loop::{self, SubclassInput}; use platform::platform::EventsLoop; use platform::platform::PlatformSpecificWindowBuilderAttributes; use platform::platform::MonitorId; @@ -34,7 +33,7 @@ pub struct Window { window: WindowWrapper, /// The current window state. - window_state: Arc>, + window_state: Rc>, } unsafe impl Send for Window {} @@ -47,15 +46,15 @@ impl Window { let mut w_attr = Some(w_attr.clone()); let mut pl_attr = Some(pl_attr.clone()); - 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); - }); + 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() + }); + } - rx.recv().unwrap() + window } pub fn set_title(&self, text: &str) { @@ -233,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() }; @@ -346,13 +344,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 +357,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::>(); @@ -371,8 +369,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 @@ -474,15 +476,14 @@ 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, + mouse_buttons_down: 0 })); - inserter.insert(real_window.0, window_state.clone()); - // making the window transparent if window.transparent { let bb = dwmapi::DWM_BLURBEHIND { @@ -514,7 +515,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()),