Merge pull request #250 from Osspial/master

Fix window contents not resizing in lockstep with window resize on win32
This commit is contained in:
tomaka 2017-08-05 09:17:30 +02:00 committed by GitHub
commit 4202c35786

View file

@ -1,12 +1,12 @@
//! An events loop on Win32 is a background thread. //! An events loop on Win32 is a background thread.
//! //!
//! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop. //! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop.
//! Destroying the events loop stops the thread. //! Destroying the events loop stops the thread.
//! //!
//! You can use the `execute_in_thread` method to execute some code in the background thread. //! You can use the `execute_in_thread` method to execute some code in the background thread.
//! Since Win32 requires you to create a window in the right thread, you must use this method //! Since Win32 requires you to create a window in the right thread, you must use this method
//! to create a window. //! to create a window.
//! //!
//! If you create a window whose class is set to `callback`, the window's events will be //! If you create a window whose class is set to `callback`, the window's events will be
//! propagated with `run_forever` and `poll_events`. //! propagated with `run_forever` and `poll_events`.
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to //! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
@ -22,6 +22,7 @@ use std::ptr;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::Condvar;
use std::thread; use std::thread;
use kernel32; use kernel32;
@ -78,22 +79,29 @@ pub struct EventsLoop {
thread_id: winapi::DWORD, thread_id: winapi::DWORD,
// Receiver for the events. The sender is in the background thread. // Receiver for the events. The sender is in the background thread.
receiver: mpsc::Receiver<Event>, receiver: mpsc::Receiver<Event>,
// 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<bool>, Condvar)>
} }
impl EventsLoop { impl EventsLoop {
pub fn new() -> EventsLoop { pub fn new() -> EventsLoop {
// The main events transfer channel. // The main events transfer channel.
let (tx, rx) = mpsc::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 channel in order to block the `new()` function until the background thread has // Local channel in order to block the `new()` function until the background thread has
// an events queue. // an events queue.
let (local_block_tx, local_block_rx) = mpsc::channel(); let (local_block_tx, local_block_rx) = mpsc::channel();
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
CONTEXT_STASH.with(|context_stash| { CONTEXT_STASH.with(|context_stash| {
*context_stash.borrow_mut() = Some(ThreadLocalData { *context_stash.borrow_mut() = Some(ThreadLocalData {
sender: tx, sender: tx,
windows: HashMap::with_capacity(4), windows: HashMap::with_capacity(4),
win32_block_loop: win32_block_loop_child
}); });
}); });
@ -139,6 +147,7 @@ impl EventsLoop {
EventsLoop { EventsLoop {
thread_id: unsafe { kernel32::GetThreadId(thread.as_raw_handle()) }, thread_id: unsafe { kernel32::GetThreadId(thread.as_raw_handle()) },
receiver: rx, receiver: rx,
win32_block_loop
} }
} }
@ -150,8 +159,18 @@ impl EventsLoop {
Ok(e) => e, Ok(e) => e,
Err(_) => return Err(_) => return
}; };
let is_resize = match event {
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
_ => false
};
callback(event); 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();
}
} }
} }
@ -163,8 +182,18 @@ impl EventsLoop {
Ok(e) => e, Ok(e) => e,
Err(_) => return Err(_) => return
}; };
let is_resize = match event {
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
_ => false
};
let flow = callback(event); 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 { match flow {
ControlFlow::Continue => continue, ControlFlow::Continue => continue,
ControlFlow::Break => break, ControlFlow::Break => break,
@ -240,7 +269,7 @@ lazy_static! {
// WPARAM and LPARAM are unused. // WPARAM and LPARAM are unused.
static ref WAKEUP_MSG_ID: u32 = { static ref WAKEUP_MSG_ID: u32 = {
unsafe { unsafe {
user32::RegisterWindowMessageA("Winit::WakeupMsg".as_ptr() as *const i8) user32::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as *const i8)
} }
}; };
// Message sent when we want to execute a closure in the thread. // Message sent when we want to execute a closure in the thread.
@ -248,7 +277,7 @@ lazy_static! {
// and LPARAM is unused. // and LPARAM is unused.
static ref EXEC_MSG_ID: u32 = { static ref EXEC_MSG_ID: u32 = {
unsafe { unsafe {
user32::RegisterWindowMessageA("Winit::ExecMsg".as_ptr() as *const i8) user32::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8)
} }
}; };
} }
@ -259,12 +288,14 @@ thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::
struct ThreadLocalData { struct ThreadLocalData {
sender: mpsc::Sender<Event>, sender: mpsc::Sender<Event>,
windows: HashMap<winapi::HWND, Arc<Mutex<WindowState>>>, windows: HashMap<winapi::HWND, Arc<Mutex<WindowState>>>,
win32_block_loop: Arc<(Mutex<bool>, Condvar)>
} }
// Utility function that dispatches an event on the current thread. // Utility function that dispatches an event on the current thread.
fn send_event(event: Event) { fn send_event(event: Event) {
CONTEXT_STASH.with(|context_stash| { CONTEXT_STASH.with(|context_stash| {
let context_stash = context_stash.borrow(); let context_stash = context_stash.borrow();
let _ = context_stash.as_ref().unwrap().sender.send(event); // Ignoring if closed let _ = context_stash.as_ref().unwrap().sender.send(event); // Ignoring if closed
}); });
} }
@ -302,9 +333,36 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
use events::WindowEvent::Resized; use events::WindowEvent::Resized;
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32; let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32; let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)), // Wait for the parent thread to process the resize event before returning from the
event: Resized(w, h), // 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();
}
}); });
0 0
}, },
@ -361,7 +419,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
window_id: SuperWindowId(WindowId(window)), window_id: SuperWindowId(WindowId(window)),
event: MouseEntered { device_id: DEVICE_ID }, event: MouseEntered { device_id: DEVICE_ID },
}); });
// Calling TrackMouseEvent in order to receive mouse leave events. // Calling TrackMouseEvent in order to receive mouse leave events.
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT { user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD, cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD,
@ -548,7 +606,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
use events::WindowEvent::MouseInput; use events::WindowEvent::MouseInput;
use events::MouseButton::Other; use events::MouseButton::Other;
use events::ElementState::Released; use events::ElementState::Released;
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
send_event(Event::WindowEvent { send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)), window_id: SuperWindowId(WindowId(window)),
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) } event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) }
@ -638,7 +696,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
if call_def_window_proc { if call_def_window_proc {
user32::DefWindowProcW(window, msg, wparam, lparam) user32::DefWindowProcW(window, msg, wparam, lparam)
} else { } else {
0 0
} }
}, },