Remove second thread from win32 backend

This commit is contained in:
Osspial
2018-07-12 23:43:58 -04:00
parent 64b8a9c6a5
commit 2e83bac99c
5 changed files with 296 additions and 405 deletions

View File

@@ -39,6 +39,7 @@ core-graphics = "0.17.3"
version = "0.3.6"
features = [
"combaseapi",
"commctrl",
"dwmapi",
"errhandlingapi",
"hidusage",

View File

@@ -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<F>(&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.

View File

@@ -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<RefCell<Vec<Event>>>,
}
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<RefCell<Vec<Event>>>) -> 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 {

View File

@@ -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<Mutex<WindowState>>,
pub event_queue: Rc<RefCell<Vec<Event>>>,
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<Mutex<WindowState>>) {
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<Event>,
pub(crate) event_queue: Rc<RefCell<Vec<Event>>>
}
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<FnMut(Inserter)>> = 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<F>(&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<F>(&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 flow = callback(event);
match flow {
ControlFlow::Continue => continue,
ControlFlow::Break => break,
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;
}
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<FnMut()>> = 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<F>(&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<F>(&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<F>(&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<FnMut(_)>);
let raw = Box::into_raw(double_box);
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<FnMut()>;
let boxed2 = Box::new(boxed);
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?");
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<Option<ThreadLocalData>> = RefCell::new(None));
struct ThreadLocalData {
sender: mpsc::Sender<Event>,
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
file_drop_handlers: HashMap<HWND, FileDropHandler>, // 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 {
let subclass_input = &mut*(subclass_input_ptr as *mut SubclassInput);
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
},
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();
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 };
}
}
}
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)
}
}
}

View File

@@ -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<Window, CreationError> {
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<Window, CreationError> {
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::<winuser::WNDCLASSEXW>() 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()),