2019-07-06 02:37:25 +10:00
|
|
|
#![allow(non_snake_case)]
|
2019-02-06 02:30:33 +11:00
|
|
|
//! 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.
|
|
|
|
//! Destroying the events loop stops the 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
|
|
|
|
//! to create a window.
|
|
|
|
//!
|
|
|
|
//! If you create a window whose class is set to `callback`, the window's events will be
|
|
|
|
//! propagated with `run_forever` and `poll_events`.
|
|
|
|
//! 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.
|
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
mod runner;
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
use parking_lot::Mutex;
|
2019-06-22 01:33:15 +10:00
|
|
|
use std::{
|
|
|
|
marker::PhantomData,
|
|
|
|
mem, panic, ptr,
|
|
|
|
rc::Rc,
|
|
|
|
sync::{
|
|
|
|
mpsc::{self, Receiver, Sender},
|
|
|
|
Arc,
|
|
|
|
},
|
|
|
|
time::{Duration, Instant},
|
|
|
|
};
|
|
|
|
use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
|
|
|
|
|
|
|
|
use winapi::{
|
|
|
|
ctypes::c_int,
|
|
|
|
shared::{
|
|
|
|
minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WPARAM},
|
|
|
|
windef::{HWND, POINT, RECT},
|
|
|
|
windowsx, winerror,
|
|
|
|
},
|
|
|
|
um::{
|
|
|
|
commctrl, libloaderapi, ole2, processthreadsapi, winbase,
|
2019-07-26 17:12:06 +10:00
|
|
|
winnt::{HANDLE, LONG, LPCSTR, SHORT},
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser,
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
use self::runner::{ELRShared, EventLoopRunnerShared};
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::{
|
|
|
|
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
2019-08-27 12:05:42 +10:00
|
|
|
event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent},
|
2019-06-22 01:33:15 +10:00
|
|
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
|
|
|
platform_impl::platform::{
|
2019-12-23 06:04:09 +11:00
|
|
|
dark_mode::try_dark_mode,
|
2019-06-22 01:33:15 +10:00
|
|
|
dpi::{
|
|
|
|
become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_scale_factor,
|
|
|
|
},
|
|
|
|
drop_handler::FileDropHandler,
|
2019-12-31 06:11:11 +11:00
|
|
|
event::{
|
|
|
|
self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, ModifiersStateSide,
|
|
|
|
},
|
|
|
|
monitor, raw_input, util,
|
2019-06-22 01:33:15 +10:00
|
|
|
window::adjust_size,
|
|
|
|
window_state::{CursorFlags, WindowFlags, WindowState},
|
|
|
|
wrap_device_id, WindowId, DEVICE_ID,
|
|
|
|
},
|
2019-10-06 07:23:30 +11:00
|
|
|
window::{Fullscreen, WindowId as RootWindowId},
|
2019-02-06 02:30:33 +11:00
|
|
|
};
|
|
|
|
|
2019-07-06 02:37:25 +10:00
|
|
|
type GetPointerFrameInfoHistory = unsafe extern "system" fn(
|
|
|
|
pointerId: UINT,
|
|
|
|
entriesCount: *mut UINT,
|
|
|
|
pointerCount: *mut UINT,
|
|
|
|
pointerInfo: *mut winuser::POINTER_INFO,
|
|
|
|
) -> BOOL;
|
|
|
|
|
|
|
|
type SkipPointerFrameMessages = unsafe extern "system" fn(pointerId: UINT) -> BOOL;
|
2019-07-26 17:12:06 +10:00
|
|
|
type GetPointerDeviceRects = unsafe extern "system" fn(
|
|
|
|
device: HANDLE,
|
|
|
|
pointerDeviceRect: *mut RECT,
|
|
|
|
displayRect: *mut RECT,
|
|
|
|
) -> BOOL;
|
2019-07-06 02:37:25 +10:00
|
|
|
|
2019-09-10 04:15:49 +10:00
|
|
|
type GetPointerTouchInfo =
|
|
|
|
unsafe extern "system" fn(pointerId: UINT, touchInfo: *mut winuser::POINTER_TOUCH_INFO) -> BOOL;
|
|
|
|
|
|
|
|
type GetPointerPenInfo =
|
|
|
|
unsafe extern "system" fn(pointId: UINT, penInfo: *mut winuser::POINTER_PEN_INFO) -> BOOL;
|
|
|
|
|
2019-07-06 02:37:25 +10:00
|
|
|
lazy_static! {
|
|
|
|
static ref GET_POINTER_FRAME_INFO_HISTORY: Option<GetPointerFrameInfoHistory> =
|
|
|
|
get_function!("user32.dll", GetPointerFrameInfoHistory);
|
|
|
|
static ref SKIP_POINTER_FRAME_MESSAGES: Option<SkipPointerFrameMessages> =
|
|
|
|
get_function!("user32.dll", SkipPointerFrameMessages);
|
2019-07-26 17:12:06 +10:00
|
|
|
static ref GET_POINTER_DEVICE_RECTS: Option<GetPointerDeviceRects> =
|
|
|
|
get_function!("user32.dll", GetPointerDeviceRects);
|
2019-09-10 04:15:49 +10:00
|
|
|
static ref GET_POINTER_TOUCH_INFO: Option<GetPointerTouchInfo> =
|
|
|
|
get_function!("user32.dll", GetPointerTouchInfo);
|
|
|
|
static ref GET_POINTER_PEN_INFO: Option<GetPointerPenInfo> =
|
|
|
|
get_function!("user32.dll", GetPointerPenInfo);
|
2019-07-06 02:37:25 +10:00
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
pub(crate) struct SubclassInput<T> {
|
|
|
|
pub window_state: Arc<Mutex<WindowState>>,
|
|
|
|
pub event_loop_runner: EventLoopRunnerShared<T>,
|
|
|
|
pub file_drop_handler: FileDropHandler,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> SubclassInput<T> {
|
|
|
|
unsafe fn send_event(&self, event: Event<T>) {
|
|
|
|
self.event_loop_runner.send_event(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ThreadMsgTargetSubclassInput<T> {
|
|
|
|
event_loop_runner: EventLoopRunnerShared<T>,
|
|
|
|
user_event_receiver: Receiver<T>,
|
2019-12-31 06:11:11 +11:00
|
|
|
modifiers_state: ModifiersStateSide,
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> ThreadMsgTargetSubclassInput<T> {
|
|
|
|
unsafe fn send_event(&self, event: Event<T>) {
|
|
|
|
self.event_loop_runner.send_event(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventLoop<T: 'static> {
|
|
|
|
thread_msg_sender: Sender<T>,
|
|
|
|
window_target: RootELW<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EventLoopWindowTarget<T> {
|
|
|
|
thread_id: DWORD,
|
|
|
|
thread_msg_target: HWND,
|
|
|
|
pub(crate) runner_shared: EventLoopRunnerShared<T>,
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
macro_rules! main_thread_check {
|
|
|
|
($fn_name:literal) => {{
|
|
|
|
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
|
|
|
if thread_id != main_thread_id() {
|
|
|
|
panic!(concat!(
|
|
|
|
"Initializing the event loop outside of the main thread is a significant \
|
|
|
|
cross-platform compatibility hazard. If you really, absolutely need to create an \
|
|
|
|
EventLoop on a different thread, please use the `EventLoopExtWindows::",
|
|
|
|
$fn_name,
|
|
|
|
"` function."
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
impl<T: 'static> EventLoop<T> {
|
|
|
|
pub fn new() -> EventLoop<T> {
|
2019-10-19 02:51:06 +11:00
|
|
|
main_thread_check!("new_any_thread");
|
|
|
|
|
|
|
|
Self::new_any_thread()
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
pub fn new_any_thread() -> EventLoop<T> {
|
|
|
|
become_dpi_aware();
|
|
|
|
Self::new_dpi_unaware_any_thread()
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
pub fn new_dpi_unaware() -> EventLoop<T> {
|
|
|
|
main_thread_check!("new_dpi_unaware_any_thread");
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
Self::new_dpi_unaware_any_thread()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_dpi_unaware_any_thread() -> EventLoop<T> {
|
2019-02-06 02:30:33 +11:00
|
|
|
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
2019-08-27 12:05:42 +10:00
|
|
|
let runner_shared = Rc::new(ELRShared::new());
|
2019-06-22 01:33:15 +10:00
|
|
|
let (thread_msg_target, thread_msg_sender) =
|
|
|
|
thread_event_target_window(runner_shared.clone());
|
2019-12-31 06:11:11 +11:00
|
|
|
raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target);
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
EventLoop {
|
|
|
|
thread_msg_sender,
|
|
|
|
window_target: RootELW {
|
|
|
|
p: EventLoopWindowTarget {
|
|
|
|
thread_id,
|
|
|
|
thread_msg_target,
|
|
|
|
runner_shared,
|
|
|
|
},
|
|
|
|
_marker: PhantomData,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
pub fn window_target(&self) -> &RootELW<T> {
|
|
|
|
&self.window_target
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
pub fn run<F>(mut self, event_handler: F) -> !
|
2019-06-22 01:33:15 +10:00
|
|
|
where
|
|
|
|
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
2019-02-06 02:30:33 +11:00
|
|
|
{
|
|
|
|
self.run_return(event_handler);
|
|
|
|
::std::process::exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_return<F>(&mut self, mut event_handler: F)
|
2019-06-22 01:33:15 +10:00
|
|
|
where
|
|
|
|
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
|
2019-02-06 02:30:33 +11:00
|
|
|
{
|
|
|
|
let event_loop_windows_ref = &self.window_target;
|
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
unsafe {
|
|
|
|
self.window_target
|
|
|
|
.p
|
|
|
|
.runner_shared
|
|
|
|
.set_runner(self, move |event, control_flow| {
|
|
|
|
event_handler(event, event_loop_windows_ref, control_flow)
|
|
|
|
})
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
let runner = &self.window_target.p.runner_shared;
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
unsafe {
|
2019-06-29 08:07:36 +10:00
|
|
|
let mut msg = mem::zeroed();
|
2019-02-06 02:30:33 +11:00
|
|
|
let mut msg_unprocessed = false;
|
|
|
|
|
|
|
|
'main: loop {
|
2019-08-27 12:05:42 +10:00
|
|
|
runner.new_events();
|
2019-02-06 02:30:33 +11:00
|
|
|
loop {
|
|
|
|
if !msg_unprocessed {
|
|
|
|
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
winuser::TranslateMessage(&mut msg);
|
|
|
|
winuser::DispatchMessageW(&mut msg);
|
2019-07-05 06:14:15 +10:00
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
msg_unprocessed = false;
|
|
|
|
}
|
2019-08-27 12:05:42 +10:00
|
|
|
runner.events_cleared();
|
|
|
|
if let Err(payload) = runner.take_panic_error() {
|
|
|
|
runner.destroy_runner();
|
2019-02-06 02:30:33 +11:00
|
|
|
panic::resume_unwind(payload);
|
|
|
|
}
|
|
|
|
|
2019-07-05 06:14:15 +10:00
|
|
|
if !msg_unprocessed {
|
2019-08-27 12:05:42 +10:00
|
|
|
let control_flow = runner.control_flow();
|
2019-07-05 06:14:15 +10:00
|
|
|
match control_flow {
|
|
|
|
ControlFlow::Exit => break 'main,
|
|
|
|
ControlFlow::Wait => {
|
|
|
|
if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) {
|
|
|
|
break 'main;
|
|
|
|
}
|
|
|
|
msg_unprocessed = true;
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
2019-07-05 06:14:15 +10:00
|
|
|
ControlFlow::WaitUntil(resume_time) => {
|
|
|
|
wait_until_time_or_msg(resume_time);
|
|
|
|
}
|
|
|
|
ControlFlow::Poll => (),
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
unsafe {
|
|
|
|
runner.call_event_handler(Event::LoopDestroyed);
|
|
|
|
}
|
|
|
|
runner.destroy_runner();
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
|
|
|
EventLoopProxy {
|
|
|
|
target_window: self.window_target.p.thread_msg_target,
|
|
|
|
event_send: self.thread_msg_sender.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> EventLoopWindowTarget<T> {
|
|
|
|
#[inline(always)]
|
|
|
|
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
|
|
|
|
EventLoopThreadExecutor {
|
|
|
|
thread_id: self.thread_id,
|
|
|
|
target_window: self.thread_msg_target,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-19 02:51:06 +11:00
|
|
|
fn main_thread_id() -> DWORD {
|
|
|
|
static mut MAIN_THREAD_ID: DWORD = 0;
|
|
|
|
#[used]
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
#[link_section = ".CRT$XCU"]
|
|
|
|
static INIT_MAIN_THREAD_ID: unsafe fn() = {
|
|
|
|
unsafe fn initer() {
|
|
|
|
MAIN_THREAD_ID = processthreadsapi::GetCurrentThreadId();
|
|
|
|
}
|
|
|
|
initer
|
|
|
|
};
|
|
|
|
|
|
|
|
unsafe { MAIN_THREAD_ID }
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
// Returns true if the wait time was reached, and false if a message must be processed.
|
|
|
|
unsafe fn wait_until_time_or_msg(wait_until: Instant) -> bool {
|
2019-06-29 08:07:36 +10:00
|
|
|
let mut msg = mem::zeroed();
|
2019-02-06 02:30:33 +11:00
|
|
|
let now = Instant::now();
|
|
|
|
if now <= wait_until {
|
|
|
|
// MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond
|
|
|
|
// from the requested time and spinlock for the remainder to compensate for that.
|
|
|
|
let resume_reason = winuser::MsgWaitForMultipleObjectsEx(
|
|
|
|
0,
|
|
|
|
ptr::null(),
|
|
|
|
dur2timeout(wait_until - now).saturating_sub(1),
|
|
|
|
winuser::QS_ALLEVENTS,
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser::MWMO_INPUTAVAILABLE,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
|
|
|
|
if resume_reason == winerror::WAIT_TIMEOUT {
|
|
|
|
while Instant::now() < wait_until {
|
|
|
|
if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs
|
|
|
|
fn dur2timeout(dur: Duration) -> DWORD {
|
|
|
|
// Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
|
|
|
|
// timeouts in windows APIs are typically u32 milliseconds. To translate, we
|
|
|
|
// have two pieces to take care of:
|
|
|
|
//
|
|
|
|
// * Nanosecond precision is rounded up
|
|
|
|
// * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
|
|
|
|
// (never time out).
|
2019-06-22 01:33:15 +10:00
|
|
|
dur.as_secs()
|
|
|
|
.checked_mul(1000)
|
|
|
|
.and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
|
|
|
|
.and_then(|ms| {
|
|
|
|
ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.map(|ms| {
|
|
|
|
if ms > DWORD::max_value() as u64 {
|
|
|
|
winbase::INFINITE
|
|
|
|
} else {
|
|
|
|
ms as DWORD
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.unwrap_or(winbase::INFINITE)
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Drop for EventLoop<T> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
winuser::DestroyWindow(self.window_target.p.thread_msg_target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) struct EventLoopThreadExecutor {
|
|
|
|
thread_id: DWORD,
|
|
|
|
target_window: HWND,
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for EventLoopThreadExecutor {}
|
|
|
|
unsafe impl Sync for EventLoopThreadExecutor {}
|
|
|
|
|
|
|
|
impl EventLoopThreadExecutor {
|
|
|
|
/// 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.
|
|
|
|
///
|
|
|
|
/// Note that if you are using this to change some property of a window and updating
|
|
|
|
/// `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.
|
|
|
|
///
|
|
|
|
/// 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)
|
2019-06-22 01:33:15 +10:00
|
|
|
where
|
|
|
|
F: FnMut() + Send + 'static,
|
2019-02-06 02:30:33 +11:00
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
if self.in_event_loop_thread() {
|
|
|
|
function();
|
|
|
|
} else {
|
|
|
|
// We double-box because the first box is a fat pointer.
|
2019-06-18 04:27:00 +10:00
|
|
|
let boxed = Box::new(function) as Box<dyn FnMut()>;
|
2019-02-06 02:30:33 +11:00
|
|
|
let boxed2: ThreadExecFn = Box::new(boxed);
|
|
|
|
|
|
|
|
let raw = Box::into_raw(boxed2);
|
|
|
|
|
|
|
|
let res = winuser::PostMessageW(
|
2019-06-22 01:33:15 +10:00
|
|
|
self.target_window,
|
|
|
|
*EXEC_MSG_ID,
|
|
|
|
raw as *mut () as usize as WPARAM,
|
|
|
|
0,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
assert!(res != 0, "PostMessage failed ; is the messages queue full?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 04:27:00 +10:00
|
|
|
type ThreadExecFn = Box<Box<dyn FnMut()>>;
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-02-21 20:51:43 +11:00
|
|
|
pub struct EventLoopProxy<T: 'static> {
|
2019-02-06 02:30:33 +11:00
|
|
|
target_window: HWND,
|
|
|
|
event_send: Sender<T>,
|
|
|
|
}
|
2019-02-21 20:51:43 +11:00
|
|
|
unsafe impl<T: Send + 'static> Send for EventLoopProxy<T> {}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-08-06 06:51:42 +10:00
|
|
|
impl<T: 'static> Clone for EventLoopProxy<T> {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
target_window: self.target_window,
|
|
|
|
event_send: self.event_send.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 20:51:43 +11:00
|
|
|
impl<T: 'static> EventLoopProxy<T> {
|
2019-12-08 04:22:03 +11:00
|
|
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
2019-02-06 02:30:33 +11:00
|
|
|
unsafe {
|
|
|
|
if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 {
|
|
|
|
self.event_send.send(event).ok();
|
|
|
|
Ok(())
|
|
|
|
} else {
|
2019-12-08 04:22:03 +11:00
|
|
|
Err(EventLoopClosed(event))
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
|
|
|
// WPARAM and LPARAM are unused.
|
|
|
|
static ref USER_EVENT_MSG_ID: u32 = {
|
|
|
|
unsafe {
|
|
|
|
winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Message sent when we want to execute a closure in the thread.
|
2019-06-18 04:27:00 +10:00
|
|
|
// WPARAM contains a Box<Box<dyn FnMut()>> that must be retrieved with `Box::from_raw`,
|
2019-02-06 02:30:33 +11:00
|
|
|
// and LPARAM is unused.
|
|
|
|
static ref EXEC_MSG_ID: u32 = {
|
|
|
|
unsafe {
|
|
|
|
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.
|
|
|
|
// WPARAM and LPARAM are unused.
|
|
|
|
pub static ref DESTROY_MSG_ID: u32 = {
|
|
|
|
unsafe {
|
|
|
|
winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Message sent by a `Window` after creation if it has a DPI != 96.
|
|
|
|
// WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height.
|
|
|
|
pub static ref INITIAL_DPI_MSG_ID: u32 = {
|
|
|
|
unsafe {
|
|
|
|
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
|
|
|
|
// documentation in the `window_state` module for more information.
|
|
|
|
pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe {
|
|
|
|
winuser::RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr() as LPCSTR)
|
|
|
|
};
|
|
|
|
static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec<u16> = unsafe {
|
|
|
|
use std::ffi::OsStr;
|
|
|
|
use std::os::windows::ffi::OsStrExt;
|
|
|
|
|
|
|
|
let class_name: Vec<_> = OsStr::new("Winit Thread Event Target")
|
|
|
|
.encode_wide()
|
|
|
|
.chain(Some(0).into_iter())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let class = winuser::WNDCLASSEXW {
|
|
|
|
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
|
|
|
style: 0,
|
|
|
|
lpfnWndProc: Some(winuser::DefWindowProcW),
|
|
|
|
cbClsExtra: 0,
|
|
|
|
cbWndExtra: 0,
|
|
|
|
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
|
|
|
hIcon: ptr::null_mut(),
|
|
|
|
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
|
|
|
hbrBackground: ptr::null_mut(),
|
|
|
|
lpszMenuName: ptr::null(),
|
|
|
|
lpszClassName: class_name.as_ptr(),
|
|
|
|
hIconSm: ptr::null_mut(),
|
|
|
|
};
|
|
|
|
|
|
|
|
winuser::RegisterClassExW(&class);
|
|
|
|
|
|
|
|
class_name
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn thread_event_target_window<T>(event_loop_runner: EventLoopRunnerShared<T>) -> (HWND, Sender<T>) {
|
|
|
|
unsafe {
|
|
|
|
let window = winuser::CreateWindowExW(
|
|
|
|
winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED,
|
|
|
|
THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
0,
|
2019-06-22 01:33:15 +10:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2019-02-06 02:30:33 +11:00
|
|
|
ptr::null_mut(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
libloaderapi::GetModuleHandleW(ptr::null()),
|
2019-06-22 01:33:15 +10:00
|
|
|
ptr::null_mut(),
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
winuser::SetWindowLongPtrW(
|
|
|
|
window,
|
|
|
|
winuser::GWL_STYLE,
|
|
|
|
// The window technically has to be visible to receive WM_PAINT messages (which are used
|
|
|
|
// for delivering events during resizes), but it isn't displayed to the user because of
|
|
|
|
// the LAYERED style.
|
2019-06-22 01:33:15 +10:00
|
|
|
(winuser::WS_VISIBLE | winuser::WS_POPUP) as _,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
|
|
|
|
|
|
|
let subclass_input = ThreadMsgTargetSubclassInput {
|
|
|
|
event_loop_runner,
|
|
|
|
user_event_receiver: rx,
|
2019-12-31 06:11:11 +11:00
|
|
|
modifiers_state: ModifiersStateSide::default(),
|
2019-02-06 02:30:33 +11:00
|
|
|
};
|
|
|
|
let input_ptr = Box::into_raw(Box::new(subclass_input));
|
|
|
|
let subclass_result = commctrl::SetWindowSubclass(
|
|
|
|
window,
|
|
|
|
Some(thread_event_target_callback::<T>),
|
|
|
|
THREAD_EVENT_TARGET_SUBCLASS_ID,
|
2019-06-22 01:33:15 +10:00
|
|
|
input_ptr as DWORD_PTR,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
assert_eq!(subclass_result, 1);
|
|
|
|
|
|
|
|
(window, tx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of
|
|
|
|
/// the 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(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;
|
|
|
|
const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1;
|
|
|
|
pub(crate) fn subclass_window<T>(window: HWND, subclass_input: SubclassInput<T>) {
|
|
|
|
let input_ptr = Box::into_raw(Box::new(subclass_input));
|
2019-06-22 01:33:15 +10:00
|
|
|
let subclass_result = unsafe {
|
|
|
|
commctrl::SetWindowSubclass(
|
|
|
|
window,
|
|
|
|
Some(public_window_callback::<T>),
|
|
|
|
WINDOW_SUBCLASS_ID,
|
|
|
|
input_ptr as DWORD_PTR,
|
|
|
|
)
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
assert_eq!(subclass_result, 1);
|
|
|
|
}
|
|
|
|
|
2019-09-10 04:15:49 +10:00
|
|
|
fn normalize_pointer_pressure(pressure: u32) -> Option<Force> {
|
|
|
|
match pressure {
|
|
|
|
1..=1024 => Some(Force::Normalized(pressure as f64 / 1024.0)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
/// 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.
|
|
|
|
//
|
|
|
|
// This is the callback that is called by `DispatchMessage` in the events loop.
|
|
|
|
//
|
|
|
|
// Returning 0 tells the Win32 API that the message has been processed.
|
|
|
|
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
|
|
|
unsafe extern "system" fn public_window_callback<T>(
|
|
|
|
window: HWND,
|
|
|
|
msg: UINT,
|
|
|
|
wparam: WPARAM,
|
|
|
|
lparam: LPARAM,
|
|
|
|
_: UINT_PTR,
|
2019-06-22 01:33:15 +10:00
|
|
|
subclass_input_ptr: DWORD_PTR,
|
2019-02-06 02:30:33 +11:00
|
|
|
) -> LRESULT {
|
2019-08-27 12:05:42 +10:00
|
|
|
let subclass_input = &*(subclass_input_ptr as *const SubclassInput<T>);
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
match msg {
|
|
|
|
winuser::WM_ENTERSIZEMOVE => {
|
2019-08-27 12:05:42 +10:00
|
|
|
subclass_input.event_loop_runner.set_modal_loop(true);
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_EXITSIZEMOVE => {
|
2019-08-27 12:05:42 +10:00
|
|
|
subclass_input.event_loop_runner.set_modal_loop(false);
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_NCCREATE => {
|
|
|
|
enable_non_client_dpi_scaling(window);
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-04-15 01:48:31 +10:00
|
|
|
winuser::WM_NCLBUTTONDOWN => {
|
|
|
|
// jumpstart the modal loop
|
|
|
|
winuser::RedrawWindow(
|
|
|
|
window,
|
|
|
|
ptr::null(),
|
|
|
|
ptr::null_mut(),
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser::RDW_INTERNALPAINT,
|
2019-04-15 01:48:31 +10:00
|
|
|
);
|
|
|
|
if wparam == winuser::HTCAPTION as _ {
|
|
|
|
winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, 0);
|
|
|
|
}
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-04-15 01:48:31 +10:00
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_CLOSE => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::CloseRequested;
|
2019-02-06 02:30:33 +11:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: CloseRequested,
|
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_DESTROY => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::Destroyed;
|
2019-02-06 02:30:33 +11:00
|
|
|
ole2::RevokeDragDrop(window);
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: Destroyed,
|
|
|
|
});
|
|
|
|
|
|
|
|
drop(subclass_input);
|
2019-08-27 12:05:42 +10:00
|
|
|
Box::from_raw(subclass_input_ptr as *mut SubclassInput<T>);
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_PAINT => {
|
2019-08-27 12:05:42 +10:00
|
|
|
subclass_input.send_event(Event::RedrawRequested(RootWindowId(WindowId(window))));
|
2019-02-06 02:30:33 +11:00
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-10-06 07:23:30 +11:00
|
|
|
winuser::WM_WINDOWPOSCHANGING => {
|
|
|
|
let mut window_state = subclass_input.window_state.lock();
|
|
|
|
if let Some(ref mut fullscreen) = window_state.fullscreen {
|
|
|
|
let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS);
|
|
|
|
let new_rect = RECT {
|
|
|
|
left: window_pos.x,
|
|
|
|
top: window_pos.y,
|
|
|
|
right: window_pos.x + window_pos.cx,
|
|
|
|
bottom: window_pos.y + window_pos.cy,
|
|
|
|
};
|
|
|
|
let new_monitor =
|
|
|
|
winuser::MonitorFromRect(&new_rect, winuser::MONITOR_DEFAULTTONULL);
|
|
|
|
match fullscreen {
|
|
|
|
Fullscreen::Borderless(ref mut fullscreen_monitor) => {
|
|
|
|
if new_monitor != fullscreen_monitor.inner.hmonitor()
|
|
|
|
&& new_monitor != ptr::null_mut()
|
|
|
|
{
|
|
|
|
if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) {
|
|
|
|
let new_monitor_rect = new_monitor_info.rcMonitor;
|
|
|
|
window_pos.x = new_monitor_rect.left;
|
|
|
|
window_pos.y = new_monitor_rect.top;
|
|
|
|
window_pos.cx = new_monitor_rect.right - new_monitor_rect.left;
|
|
|
|
window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top;
|
|
|
|
}
|
|
|
|
*fullscreen_monitor = crate::monitor::MonitorHandle {
|
|
|
|
inner: monitor::MonitorHandle::new(new_monitor),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Fullscreen::Exclusive(ref video_mode) => {
|
|
|
|
let old_monitor = video_mode.video_mode.monitor.hmonitor();
|
|
|
|
if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) {
|
|
|
|
let old_monitor_rect = old_monitor_info.rcMonitor;
|
|
|
|
window_pos.x = old_monitor_rect.left;
|
|
|
|
window_pos.y = old_monitor_rect.top;
|
|
|
|
window_pos.cx = old_monitor_rect.right - old_monitor_rect.left;
|
|
|
|
window_pos.cy = old_monitor_rect.bottom - old_monitor_rect.top;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
// WM_MOVE supplies client area positions, so we send Moved here instead.
|
|
|
|
winuser::WM_WINDOWPOSCHANGED => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::Moved;
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
let windowpos = lparam as *const winuser::WINDOWPOS;
|
|
|
|
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
2019-05-30 11:29:54 +10:00
|
|
|
let dpi_factor = hwnd_scale_factor(window);
|
2019-06-22 01:33:15 +10:00
|
|
|
let logical_position =
|
|
|
|
LogicalPosition::from_physical(((*windowpos).x, (*windowpos).y), dpi_factor);
|
2019-02-06 02:30:33 +11:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: Moved(logical_position),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is necessary for us to still get sent WM_SIZE.
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_SIZE => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::Resized;
|
2019-02-06 02:30:33 +11:00
|
|
|
let w = LOWORD(lparam as DWORD) as u32;
|
|
|
|
let h = HIWORD(lparam as DWORD) as u32;
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
let dpi_factor = hwnd_scale_factor(window);
|
2019-02-06 02:30:33 +11:00
|
|
|
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
|
|
|
|
let event = Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: Resized(logical_size),
|
|
|
|
};
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut w = subclass_input.window_state.lock();
|
|
|
|
// See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists.
|
2019-06-22 01:33:15 +10:00
|
|
|
if !w
|
|
|
|
.window_flags()
|
|
|
|
.contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE)
|
|
|
|
{
|
2019-02-06 02:30:33 +11:00
|
|
|
let maximized = wparam == winuser::SIZE_MAXIMIZED;
|
|
|
|
w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
subclass_input.send_event(event);
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-11-30 07:49:43 +11:00
|
|
|
winuser::WM_CHAR | winuser::WM_SYSCHAR => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::ReceivedCharacter;
|
2019-09-17 04:26:56 +10:00
|
|
|
use std::char;
|
|
|
|
let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF;
|
|
|
|
let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF;
|
2019-09-16 12:09:08 +10:00
|
|
|
|
2019-09-17 04:26:56 +10:00
|
|
|
if is_high_surrogate {
|
2019-09-16 12:09:08 +10:00
|
|
|
subclass_input.window_state.lock().high_surrogate = Some(wparam as u16);
|
2019-09-17 04:26:56 +10:00
|
|
|
} else if is_low_surrogate {
|
|
|
|
let high_surrogate = subclass_input.window_state.lock().high_surrogate.take();
|
|
|
|
|
|
|
|
if let Some(high_surrogate) = high_surrogate {
|
2019-09-16 12:09:08 +10:00
|
|
|
let pair = [high_surrogate, wparam as u16];
|
2019-09-17 04:26:56 +10:00
|
|
|
if let Some(Ok(chr)) = char::decode_utf16(pair.iter().copied()).next() {
|
2019-09-16 12:09:08 +10:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: ReceivedCharacter(chr),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-09-17 04:26:56 +10:00
|
|
|
subclass_input.window_state.lock().high_surrogate = None;
|
|
|
|
|
|
|
|
if let Some(chr) = char::from_u32(wparam as u32) {
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: ReceivedCharacter(chr),
|
|
|
|
});
|
|
|
|
}
|
2019-09-16 12:09:08 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-12-22 17:04:11 +11:00
|
|
|
// this is necessary for us to maintain minimize/restore state
|
2019-07-29 19:18:23 +10:00
|
|
|
winuser::WM_SYSCOMMAND => {
|
2019-12-22 17:04:11 +11:00
|
|
|
if wparam == winuser::SC_RESTORE {
|
|
|
|
let mut w = subclass_input.window_state.lock();
|
|
|
|
w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, false));
|
|
|
|
}
|
|
|
|
if wparam == winuser::SC_MINIMIZE {
|
|
|
|
let mut w = subclass_input.window_state.lock();
|
|
|
|
w.set_window_flags_in_place(|f| f.set(WindowFlags::MINIMIZED, true));
|
|
|
|
}
|
|
|
|
// Send `WindowEvent::Minimized` here if we decide to implement one
|
|
|
|
|
2019-07-29 19:18:23 +10:00
|
|
|
if wparam == winuser::SC_SCREENSAVE {
|
|
|
|
let window_state = subclass_input.window_state.lock();
|
|
|
|
if window_state.fullscreen.is_some() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_MOUSEMOVE => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::{CursorEntered, CursorMoved};
|
2019-02-06 02:30:33 +11:00
|
|
|
let mouse_was_outside_window = {
|
|
|
|
let mut w = subclass_input.window_state.lock();
|
|
|
|
|
|
|
|
let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW);
|
2019-06-22 01:33:15 +10:00
|
|
|
w.mouse
|
|
|
|
.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true))
|
|
|
|
.ok();
|
2019-02-06 02:30:33 +11:00
|
|
|
was_outside_window
|
|
|
|
};
|
|
|
|
|
|
|
|
if mouse_was_outside_window {
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: CursorEntered {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
// Calling TrackMouseEvent in order to receive mouse leave events.
|
|
|
|
winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT {
|
|
|
|
cbSize: mem::size_of::<winuser::TRACKMOUSEEVENT>() as DWORD,
|
|
|
|
dwFlags: winuser::TME_LEAVE,
|
|
|
|
hwndTrack: window,
|
|
|
|
dwHoverTime: winuser::HOVER_DEFAULT,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
|
|
|
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
2019-05-30 11:29:54 +10:00
|
|
|
let dpi_factor = hwnd_scale_factor(window);
|
2019-02-06 02:30:33 +11:00
|
|
|
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: CursorMoved {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
position,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_MOUSELEAVE => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::CursorLeft;
|
2019-02-06 02:30:33 +11:00
|
|
|
{
|
|
|
|
let mut w = subclass_input.window_state.lock();
|
2019-06-22 01:33:15 +10:00
|
|
|
w.mouse
|
|
|
|
.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false))
|
|
|
|
.ok();
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: CursorLeft {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_MOUSEWHEEL => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::MouseScrollDelta::LineDelta;
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
let value = (wparam >> 16) as i16;
|
|
|
|
let value = value as i32;
|
|
|
|
let value = value as f32 / winuser::WHEEL_DELTA as f32;
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: WindowEvent::MouseWheel {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
delta: LineDelta(0.0, value),
|
|
|
|
phase: TouchPhase::Moved,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-02-23 01:31:16 +11:00
|
|
|
winuser::WM_MOUSEHWHEEL => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::MouseScrollDelta::LineDelta;
|
2019-02-23 01:31:16 +11:00
|
|
|
|
|
|
|
let value = (wparam >> 16) as i16;
|
|
|
|
let value = value as i32;
|
|
|
|
let value = value as f32 / winuser::WHEEL_DELTA as f32;
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: WindowEvent::MouseWheel {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
delta: LineDelta(value, 0.0),
|
|
|
|
phase: TouchPhase::Moved,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-23 01:31:16 +11:00
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-23 01:31:16 +11:00
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{ElementState::Pressed, VirtualKeyCode};
|
2019-02-06 02:30:33 +11:00
|
|
|
if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 {
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
|
|
|
} else {
|
|
|
|
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::KeyboardInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
input: KeyboardInput {
|
|
|
|
state: Pressed,
|
2019-06-22 01:33:15 +10:00
|
|
|
scancode,
|
2019-02-06 02:30:33 +11:00
|
|
|
virtual_keycode: vkey,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-12-08 09:51:37 +11:00
|
|
|
is_synthetic: false,
|
2019-02-06 02:30:33 +11:00
|
|
|
},
|
|
|
|
});
|
|
|
|
// 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) {
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::ReceivedCharacter('\u{7F}'),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
|
|
|
}
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_KEYUP | winuser::WM_SYSKEYUP => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::ElementState::Released;
|
2019-02-06 02:30:33 +11:00
|
|
|
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::KeyboardInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
input: KeyboardInput {
|
|
|
|
state: Released,
|
2019-06-22 01:33:15 +10:00
|
|
|
scancode,
|
2019-02-06 02:30:33 +11:00
|
|
|
virtual_keycode: vkey,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-12-08 09:51:37 +11:00
|
|
|
is_synthetic: false,
|
2019-02-06 02:30:33 +11:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_LBUTTONDOWN => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Pressed,
|
|
|
|
button: Left,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_LBUTTONUP => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Released, MouseButton::Left, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
release_mouse(&mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Released,
|
|
|
|
button: Left,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_RBUTTONDOWN => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Pressed, MouseButton::Right, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Pressed,
|
|
|
|
button: Right,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_RBUTTONUP => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Released, MouseButton::Right, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
release_mouse(&mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Released,
|
|
|
|
button: Right,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_MBUTTONDOWN => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Pressed, MouseButton::Middle, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Pressed,
|
|
|
|
button: Middle,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_MBUTTONUP => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Released, MouseButton::Middle, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
release_mouse(&mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Released,
|
|
|
|
button: Middle,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_XBUTTONDOWN => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Pressed, MouseButton::Other, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
|
|
|
|
|
|
|
|
capture_mouse(window, &mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Pressed,
|
|
|
|
button: Other(xbutton as u8),
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_XBUTTONUP => {
|
2019-06-22 01:33:15 +10:00
|
|
|
use crate::event::{
|
|
|
|
ElementState::Released, MouseButton::Other, WindowEvent::MouseInput,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
let xbutton = winuser::GET_XBUTTON_WPARAM(wparam);
|
|
|
|
|
|
|
|
release_mouse(&mut *subclass_input.window_state.lock());
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
2019-06-22 01:33:15 +10:00
|
|
|
event: MouseInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
state: Released,
|
|
|
|
button: Other(xbutton as u8),
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
2019-02-06 02:30:33 +11:00
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_TOUCH => {
|
2019-06-22 01:33:15 +10:00
|
|
|
let pcount = LOWORD(wparam as DWORD) as usize;
|
|
|
|
let mut inputs = Vec::with_capacity(pcount);
|
|
|
|
inputs.set_len(pcount);
|
2019-02-06 02:30:33 +11:00
|
|
|
let htouch = lparam as winuser::HTOUCHINPUT;
|
|
|
|
if winuser::GetTouchInputInfo(
|
|
|
|
htouch,
|
|
|
|
pcount as UINT,
|
|
|
|
inputs.as_mut_ptr(),
|
|
|
|
mem::size_of::<winuser::TOUCHINPUT>() as INT,
|
2019-06-22 01:33:15 +10:00
|
|
|
) > 0
|
|
|
|
{
|
2019-05-30 11:29:54 +10:00
|
|
|
let dpi_factor = hwnd_scale_factor(window);
|
2019-02-06 02:30:33 +11:00
|
|
|
for input in &inputs {
|
2019-07-18 02:25:35 +10:00
|
|
|
let mut location = POINT {
|
|
|
|
x: input.x / 100,
|
|
|
|
y: input.y / 100,
|
|
|
|
};
|
|
|
|
|
|
|
|
if winuser::ScreenToClient(window, &mut location as *mut _) == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let x = location.x as f64 + (input.x % 100) as f64 / 100f64;
|
|
|
|
let y = location.y as f64 + (input.y % 100) as f64 / 100f64;
|
2019-02-06 02:30:33 +11:00
|
|
|
let location = LogicalPosition::from_physical((x, y), dpi_factor);
|
2019-06-22 01:33:15 +10:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
2019-02-06 02:30:33 +11:00
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::Touch(Touch {
|
2019-06-22 01:33:15 +10:00
|
|
|
phase: if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 {
|
2019-02-06 02:30:33 +11:00
|
|
|
TouchPhase::Started
|
|
|
|
} else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 {
|
|
|
|
TouchPhase::Ended
|
|
|
|
} else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 {
|
|
|
|
TouchPhase::Moved
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
location,
|
2019-09-10 04:15:49 +10:00
|
|
|
force: None, // WM_TOUCH doesn't support pressure information
|
2019-02-06 02:30:33 +11:00
|
|
|
id: input.dwID as u64,
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser::CloseTouchInputHandle(htouch);
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-06-27 19:34:38 +10:00
|
|
|
winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => {
|
2019-07-26 17:12:06 +10:00
|
|
|
if let (
|
|
|
|
Some(GetPointerFrameInfoHistory),
|
|
|
|
Some(SkipPointerFrameMessages),
|
|
|
|
Some(GetPointerDeviceRects),
|
|
|
|
) = (
|
2019-07-06 02:37:25 +10:00
|
|
|
*GET_POINTER_FRAME_INFO_HISTORY,
|
|
|
|
*SKIP_POINTER_FRAME_MESSAGES,
|
2019-07-26 17:12:06 +10:00
|
|
|
*GET_POINTER_DEVICE_RECTS,
|
2019-07-06 02:37:25 +10:00
|
|
|
) {
|
|
|
|
let pointer_id = LOWORD(wparam as DWORD) as UINT;
|
|
|
|
let mut entries_count = 0 as UINT;
|
|
|
|
let mut pointers_count = 0 as UINT;
|
|
|
|
if GetPointerFrameInfoHistory(
|
|
|
|
pointer_id,
|
|
|
|
&mut entries_count as *mut _,
|
|
|
|
&mut pointers_count as *mut _,
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
) == 0
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2019-06-27 19:34:38 +10:00
|
|
|
|
2019-07-06 02:37:25 +10:00
|
|
|
let pointer_info_count = (entries_count * pointers_count) as usize;
|
|
|
|
let mut pointer_infos = Vec::with_capacity(pointer_info_count);
|
|
|
|
pointer_infos.set_len(pointer_info_count);
|
|
|
|
if GetPointerFrameInfoHistory(
|
|
|
|
pointer_id,
|
|
|
|
&mut entries_count as *mut _,
|
|
|
|
&mut pointers_count as *mut _,
|
|
|
|
pointer_infos.as_mut_ptr(),
|
|
|
|
) == 0
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2019-06-27 19:34:38 +10:00
|
|
|
|
2019-07-06 02:37:25 +10:00
|
|
|
let dpi_factor = hwnd_scale_factor(window);
|
|
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getpointerframeinfohistory
|
|
|
|
// The information retrieved appears in reverse chronological order, with the most recent entry in the first
|
|
|
|
// row of the returned array
|
|
|
|
for pointer_info in pointer_infos.iter().rev() {
|
2019-09-10 04:15:49 +10:00
|
|
|
let mut device_rect = mem::MaybeUninit::uninit();
|
|
|
|
let mut display_rect = mem::MaybeUninit::uninit();
|
2019-07-26 17:12:06 +10:00
|
|
|
|
|
|
|
if (GetPointerDeviceRects(
|
|
|
|
pointer_info.sourceDevice,
|
2019-09-10 04:15:49 +10:00
|
|
|
device_rect.as_mut_ptr(),
|
|
|
|
display_rect.as_mut_ptr(),
|
2019-07-26 17:12:06 +10:00
|
|
|
)) == 0
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-09-10 04:15:49 +10:00
|
|
|
let device_rect = device_rect.assume_init();
|
|
|
|
let display_rect = display_rect.assume_init();
|
|
|
|
|
2019-07-26 17:12:06 +10:00
|
|
|
// For the most precise himetric to pixel conversion we calculate the ratio between the resolution
|
|
|
|
// of the display device (pixel) and the touch device (himetric).
|
|
|
|
let himetric_to_pixel_ratio_x = (display_rect.right - display_rect.left) as f64
|
|
|
|
/ (device_rect.right - device_rect.left) as f64;
|
|
|
|
let himetric_to_pixel_ratio_y = (display_rect.bottom - display_rect.top) as f64
|
|
|
|
/ (device_rect.bottom - device_rect.top) as f64;
|
|
|
|
|
|
|
|
// ptHimetricLocation's origin is 0,0 even on multi-monitor setups.
|
|
|
|
// On multi-monitor setups we need to translate the himetric location to the rect of the
|
|
|
|
// display device it's attached to.
|
|
|
|
let x = display_rect.left as f64
|
|
|
|
+ pointer_info.ptHimetricLocation.x as f64 * himetric_to_pixel_ratio_x;
|
|
|
|
let y = display_rect.top as f64
|
|
|
|
+ pointer_info.ptHimetricLocation.y as f64 * himetric_to_pixel_ratio_y;
|
|
|
|
|
2019-07-18 02:25:35 +10:00
|
|
|
let mut location = POINT {
|
2019-07-26 17:12:06 +10:00
|
|
|
x: x.floor() as i32,
|
|
|
|
y: y.floor() as i32,
|
2019-07-18 02:25:35 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
if winuser::ScreenToClient(window, &mut location as *mut _) == 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-09-10 04:15:49 +10:00
|
|
|
let force = match pointer_info.pointerType {
|
|
|
|
winuser::PT_TOUCH => {
|
|
|
|
let mut touch_info = mem::MaybeUninit::uninit();
|
|
|
|
GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
|
|
|
|
match GetPointerTouchInfo(
|
|
|
|
pointer_info.pointerId,
|
|
|
|
touch_info.as_mut_ptr(),
|
|
|
|
) {
|
|
|
|
0 => None,
|
|
|
|
_ => normalize_pointer_pressure(
|
|
|
|
touch_info.assume_init().pressure,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
winuser::PT_PEN => {
|
|
|
|
let mut pen_info = mem::MaybeUninit::uninit();
|
|
|
|
GET_POINTER_PEN_INFO.and_then(|GetPointerPenInfo| {
|
|
|
|
match GetPointerPenInfo(
|
|
|
|
pointer_info.pointerId,
|
|
|
|
pen_info.as_mut_ptr(),
|
|
|
|
) {
|
|
|
|
0 => None,
|
|
|
|
_ => {
|
|
|
|
normalize_pointer_pressure(pen_info.assume_init().pressure)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
2019-07-26 17:12:06 +10:00
|
|
|
let x = location.x as f64 + x.fract();
|
|
|
|
let y = location.y as f64 + y.fract();
|
2019-07-06 02:37:25 +10:00
|
|
|
let location = LogicalPosition::from_physical((x, y), dpi_factor);
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::Touch(Touch {
|
|
|
|
phase: if pointer_info.pointerFlags & winuser::POINTER_FLAG_DOWN != 0 {
|
|
|
|
TouchPhase::Started
|
|
|
|
} else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UP != 0 {
|
|
|
|
TouchPhase::Ended
|
|
|
|
} else if pointer_info.pointerFlags & winuser::POINTER_FLAG_UPDATE != 0
|
|
|
|
{
|
|
|
|
TouchPhase::Moved
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
location,
|
2019-09-10 04:15:49 +10:00
|
|
|
force,
|
2019-07-06 02:37:25 +10:00
|
|
|
id: pointer_info.pointerId as u64,
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
2019-06-27 19:34:38 +10:00
|
|
|
|
2019-07-06 02:37:25 +10:00
|
|
|
SkipPointerFrameMessages(pointer_id);
|
|
|
|
}
|
2019-06-27 19:34:38 +10:00
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::WM_SETFOCUS => {
|
2019-12-28 08:26:23 +11:00
|
|
|
use crate::event::{ElementState::Released, WindowEvent::Focused};
|
|
|
|
for windows_keycode in event::get_pressed_keys() {
|
|
|
|
let scancode =
|
|
|
|
winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
|
|
|
|
let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode);
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::KeyboardInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
input: KeyboardInput {
|
|
|
|
scancode,
|
|
|
|
virtual_keycode,
|
|
|
|
state: Released,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
|
|
|
is_synthetic: true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: Focused(true),
|
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_KILLFOCUS => {
|
2019-12-28 08:26:23 +11:00
|
|
|
use crate::event::{ElementState::Released, WindowEvent::Focused};
|
|
|
|
for windows_keycode in event::get_pressed_keys() {
|
|
|
|
let scancode =
|
|
|
|
winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC);
|
|
|
|
let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode);
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: WindowEvent::KeyboardInput {
|
|
|
|
device_id: DEVICE_ID,
|
|
|
|
input: KeyboardInput {
|
|
|
|
scancode,
|
|
|
|
virtual_keycode,
|
|
|
|
state: Released,
|
|
|
|
modifiers: event::get_key_mods(),
|
|
|
|
},
|
|
|
|
is_synthetic: true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: Focused(false),
|
|
|
|
});
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_SETCURSOR => {
|
|
|
|
let set_cursor_to = {
|
|
|
|
let window_state = subclass_input.window_state.lock();
|
2019-06-22 01:33:15 +10:00
|
|
|
if window_state
|
|
|
|
.mouse
|
|
|
|
.cursor_flags()
|
|
|
|
.contains(CursorFlags::IN_WINDOW)
|
|
|
|
{
|
2019-02-06 02:30:33 +11:00
|
|
|
Some(window_state.mouse.cursor)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match set_cursor_to {
|
|
|
|
Some(cursor) => {
|
2019-06-22 01:33:15 +10:00
|
|
|
let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor());
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::SetCursor(cursor);
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
None => winuser::DefWindowProcW(window, msg, wparam, lparam),
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_DROPFILES => {
|
|
|
|
// See `FileDropHandler` for implementation.
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
winuser::WM_GETMINMAXINFO => {
|
|
|
|
let mmi = lparam as *mut winuser::MINMAXINFO;
|
|
|
|
|
|
|
|
let window_state = subclass_input.window_state.lock();
|
|
|
|
|
|
|
|
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 min_size = min_size.to_physical(window_state.dpi_factor);
|
|
|
|
let (width, height) = adjust_size(min_size, style, ex_style);
|
2019-06-22 01:33:15 +10:00
|
|
|
(*mmi).ptMinTrackSize = POINT {
|
|
|
|
x: width as i32,
|
|
|
|
y: height as i32,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
if let Some(max_size) = window_state.max_size {
|
|
|
|
let max_size = max_size.to_physical(window_state.dpi_factor);
|
|
|
|
let (width, height) = adjust_size(max_size, style, ex_style);
|
2019-06-22 01:33:15 +10:00
|
|
|
(*mmi).ptMaxTrackSize = POINT {
|
|
|
|
x: width as i32,
|
|
|
|
y: height as i32,
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
// Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change
|
|
|
|
// DPI, therefore all applications are closed while DPI is changing.
|
|
|
|
winuser::WM_DPICHANGED => {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::HiDpiFactorChanged;
|
2019-02-06 02:30:33 +11:00
|
|
|
|
|
|
|
// This message actually provides two DPI values - x and y. However MSDN says that
|
|
|
|
// "you only need to use either the X-axis or the Y-axis value when scaling your
|
|
|
|
// application since they are the same".
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx
|
|
|
|
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
|
|
|
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
|
|
|
|
|
|
|
let allow_resize = {
|
|
|
|
let mut window_state = subclass_input.window_state.lock();
|
|
|
|
let old_dpi_factor = window_state.dpi_factor;
|
|
|
|
window_state.dpi_factor = new_dpi_factor;
|
|
|
|
|
|
|
|
new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none()
|
|
|
|
};
|
|
|
|
|
|
|
|
// This prevents us from re-applying DPI adjustment to the restored size after exiting
|
|
|
|
// fullscreen (the restored size is already DPI adjusted).
|
|
|
|
if allow_resize {
|
|
|
|
// Resize window to the size suggested by Windows.
|
|
|
|
let rect = &*(lparam as *const RECT);
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
window,
|
|
|
|
ptr::null_mut(),
|
|
|
|
rect.left,
|
|
|
|
rect.top,
|
|
|
|
rect.right - rect.left,
|
|
|
|
rect.bottom - rect.top,
|
|
|
|
winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: HiDpiFactorChanged(new_dpi_factor),
|
|
|
|
});
|
|
|
|
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
|
2019-12-23 06:04:09 +11:00
|
|
|
winuser::WM_SETTINGCHANGE => {
|
|
|
|
use crate::event::WindowEvent::ThemeChanged;
|
|
|
|
|
|
|
|
let is_dark_mode = try_dark_mode(window);
|
|
|
|
let mut window_state = subclass_input.window_state.lock();
|
|
|
|
let changed = window_state.is_dark_mode != is_dark_mode;
|
|
|
|
|
|
|
|
if changed {
|
|
|
|
use crate::window::Theme::*;
|
|
|
|
let theme = if is_dark_mode { Dark } else { Light };
|
|
|
|
|
|
|
|
window_state.is_dark_mode = is_dark_mode;
|
|
|
|
mem::drop(window_state);
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: ThemeChanged(theme),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
_ => {
|
|
|
|
if msg == *DESTROY_MSG_ID {
|
|
|
|
winuser::DestroyWindow(window);
|
|
|
|
0
|
|
|
|
} else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID {
|
|
|
|
let mut window_state = subclass_input.window_state.lock();
|
2019-06-22 01:33:15 +10:00
|
|
|
window_state.set_window_flags_in_place(|f| {
|
|
|
|
f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)
|
|
|
|
});
|
2019-02-06 02:30:33 +11:00
|
|
|
0
|
|
|
|
} else if msg == *INITIAL_DPI_MSG_ID {
|
2019-06-18 04:27:00 +10:00
|
|
|
use crate::event::WindowEvent::HiDpiFactorChanged;
|
2019-02-06 02:30:33 +11:00
|
|
|
let scale_factor = dpi_to_scale_factor(wparam as u32);
|
|
|
|
subclass_input.send_event(Event::WindowEvent {
|
|
|
|
window_id: RootWindowId(WindowId(window)),
|
|
|
|
event: HiDpiFactorChanged(scale_factor),
|
|
|
|
});
|
|
|
|
// Automatically resize for actual DPI
|
|
|
|
let width = LOWORD(lparam as DWORD) as u32;
|
|
|
|
let height = HIWORD(lparam as DWORD) as u32;
|
2019-06-22 01:33:15 +10:00
|
|
|
let (adjusted_width, adjusted_height): (u32, u32) =
|
|
|
|
PhysicalSize::from_logical((width, height), scale_factor).into();
|
2019-02-06 02:30:33 +11:00
|
|
|
// We're not done yet! `SetWindowPos` needs the window size, not the client area size.
|
|
|
|
let mut rect = RECT {
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
bottom: adjusted_height as LONG,
|
|
|
|
right: adjusted_width as LONG,
|
|
|
|
};
|
|
|
|
let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
|
|
|
let b_menu = !winuser::GetMenu(window).is_null() as BOOL;
|
|
|
|
let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
|
|
let outer_x = (rect.right - rect.left).abs() as c_int;
|
|
|
|
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
window,
|
|
|
|
ptr::null_mut(),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
outer_x,
|
|
|
|
outer_y,
|
|
|
|
winuser::SWP_NOMOVE
|
2019-06-22 01:33:15 +10:00
|
|
|
| winuser::SWP_NOREPOSITION
|
|
|
|
| winuser::SWP_NOZORDER
|
|
|
|
| winuser::SWP_NOACTIVATE,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
|
|
|
}
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe extern "system" fn thread_event_target_callback<T>(
|
|
|
|
window: HWND,
|
|
|
|
msg: UINT,
|
|
|
|
wparam: WPARAM,
|
|
|
|
lparam: LPARAM,
|
|
|
|
_: UINT_PTR,
|
2019-06-22 01:33:15 +10:00
|
|
|
subclass_input_ptr: DWORD_PTR,
|
2019-02-06 02:30:33 +11:00
|
|
|
) -> LRESULT {
|
2019-06-22 01:33:15 +10:00
|
|
|
let subclass_input = &mut *(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput<T>);
|
2019-02-06 02:30:33 +11:00
|
|
|
match msg {
|
|
|
|
winuser::WM_DESTROY => {
|
|
|
|
Box::from_raw(subclass_input);
|
|
|
|
drop(subclass_input);
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
// Because WM_PAINT comes after all other messages, we use it during modal loops to detect
|
|
|
|
// when the event queue has been emptied. See `process_event` for more details.
|
|
|
|
winuser::WM_PAINT => {
|
|
|
|
winuser::ValidateRect(window, ptr::null());
|
|
|
|
let queue_call_again = || {
|
|
|
|
winuser::RedrawWindow(
|
|
|
|
window,
|
|
|
|
ptr::null(),
|
|
|
|
ptr::null_mut(),
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser::RDW_INTERNALPAINT,
|
2019-02-06 02:30:33 +11:00
|
|
|
);
|
|
|
|
};
|
2019-08-27 12:05:42 +10:00
|
|
|
let in_modal_loop = subclass_input.event_loop_runner.in_modal_loop();
|
2019-02-06 02:30:33 +11:00
|
|
|
if in_modal_loop {
|
2019-06-29 08:07:36 +10:00
|
|
|
let mut msg = mem::zeroed();
|
2019-02-06 02:30:33 +11:00
|
|
|
loop {
|
|
|
|
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Clear all paint/timer messages from the queue before sending the events cleared message.
|
|
|
|
match msg.message {
|
|
|
|
// Flush the event queue of WM_PAINT messages.
|
2019-06-22 01:33:15 +10:00
|
|
|
winuser::WM_PAINT | winuser::WM_TIMER => {
|
2019-02-06 02:30:33 +11:00
|
|
|
// Remove the message from the message queue.
|
|
|
|
winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1);
|
|
|
|
|
|
|
|
if msg.hwnd != window {
|
|
|
|
winuser::TranslateMessage(&mut msg);
|
|
|
|
winuser::DispatchMessageW(&mut msg);
|
|
|
|
}
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
// If the message isn't one of those three, it may be handled by the modal
|
|
|
|
// loop so we should return control flow to it.
|
|
|
|
_ => {
|
|
|
|
queue_call_again();
|
|
|
|
return 0;
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 12:05:42 +10:00
|
|
|
let runner = &subclass_input.event_loop_runner;
|
|
|
|
runner.events_cleared();
|
|
|
|
match runner.control_flow() {
|
|
|
|
// Waiting is handled by the modal loop.
|
|
|
|
ControlFlow::Exit | ControlFlow::Wait => runner.new_events(),
|
|
|
|
ControlFlow::WaitUntil(resume_time) => {
|
|
|
|
wait_until_time_or_msg(resume_time);
|
|
|
|
runner.new_events();
|
|
|
|
queue_call_again();
|
|
|
|
}
|
|
|
|
ControlFlow::Poll => {
|
|
|
|
runner.new_events();
|
|
|
|
queue_call_again();
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-12-31 06:11:11 +11:00
|
|
|
|
|
|
|
winuser::WM_INPUT_DEVICE_CHANGE => {
|
|
|
|
let event = match wparam as _ {
|
|
|
|
winuser::GIDC_ARRIVAL => DeviceEvent::Added,
|
|
|
|
winuser::GIDC_REMOVAL => DeviceEvent::Removed,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id: wrap_device_id(lparam as _),
|
|
|
|
event,
|
|
|
|
});
|
|
|
|
|
|
|
|
0
|
|
|
|
}
|
|
|
|
|
|
|
|
winuser::WM_INPUT => {
|
|
|
|
use crate::event::{
|
|
|
|
DeviceEvent::{Button, Key, ModifiersChanged, Motion, MouseMotion, MouseWheel},
|
|
|
|
ElementState::{Pressed, Released},
|
|
|
|
MouseScrollDelta::LineDelta,
|
|
|
|
VirtualKeyCode,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(data) = raw_input::get_raw_input_data(lparam as _) {
|
|
|
|
let device_id = wrap_device_id(data.header.hDevice as _);
|
|
|
|
|
|
|
|
if data.header.dwType == winuser::RIM_TYPEMOUSE {
|
|
|
|
let mouse = data.data.mouse();
|
|
|
|
|
|
|
|
if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) {
|
|
|
|
let x = mouse.lLastX as f64;
|
|
|
|
let y = mouse.lLastY as f64;
|
|
|
|
|
|
|
|
if x != 0.0 {
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: Motion { axis: 0, value: x },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if y != 0.0 {
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: Motion { axis: 1, value: y },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if x != 0.0 || y != 0.0 {
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: MouseMotion { delta: (x, y) },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) {
|
|
|
|
let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA;
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: MouseWheel {
|
|
|
|
delta: LineDelta(0.0, delta as f32),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags);
|
|
|
|
// Left, middle, and right, respectively.
|
|
|
|
for (index, state) in button_state.iter().enumerate() {
|
|
|
|
if let Some(state) = *state {
|
|
|
|
// This gives us consistency with X11, since there doesn't
|
|
|
|
// seem to be anything else reasonable to do for a mouse
|
|
|
|
// button ID.
|
|
|
|
let button = (index + 1) as _;
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: Button { button, state },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if data.header.dwType == winuser::RIM_TYPEKEYBOARD {
|
|
|
|
let keyboard = data.data.keyboard();
|
|
|
|
|
|
|
|
let pressed = keyboard.Message == winuser::WM_KEYDOWN
|
|
|
|
|| keyboard.Message == winuser::WM_SYSKEYDOWN;
|
|
|
|
let released = keyboard.Message == winuser::WM_KEYUP
|
|
|
|
|| keyboard.Message == winuser::WM_SYSKEYUP;
|
|
|
|
|
|
|
|
if pressed || released {
|
|
|
|
let state = if pressed { Pressed } else { Released };
|
|
|
|
|
|
|
|
let scancode = keyboard.MakeCode as _;
|
|
|
|
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _)
|
|
|
|
| util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _);
|
|
|
|
|
|
|
|
if let Some((vkey, scancode)) =
|
|
|
|
handle_extended_keys(keyboard.VKey as _, scancode, extended)
|
|
|
|
{
|
|
|
|
let virtual_keycode = vkey_to_winit_vkey(vkey);
|
|
|
|
|
|
|
|
// If we ever change the DeviceEvent API to only emit events when a
|
|
|
|
// window is focused, we'll need to emit synthetic `ModifiersChanged`
|
|
|
|
// events when Winit windows lose focus so that these don't drift out
|
|
|
|
// of sync with the actual modifier state.
|
|
|
|
let old_modifiers_state =
|
|
|
|
subclass_input.modifiers_state.filter_out_altgr().into();
|
|
|
|
match virtual_keycode {
|
|
|
|
Some(VirtualKeyCode::LShift) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::LSHIFT, pressed),
|
|
|
|
Some(VirtualKeyCode::RShift) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::RSHIFT, pressed),
|
|
|
|
Some(VirtualKeyCode::LControl) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::LCTRL, pressed),
|
|
|
|
Some(VirtualKeyCode::RControl) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::RCTRL, pressed),
|
|
|
|
Some(VirtualKeyCode::LAlt) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::LALT, pressed),
|
|
|
|
Some(VirtualKeyCode::RAlt) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::RALT, pressed),
|
|
|
|
Some(VirtualKeyCode::LWin) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::LLOGO, pressed),
|
|
|
|
Some(VirtualKeyCode::RWin) => subclass_input
|
|
|
|
.modifiers_state
|
|
|
|
.set(ModifiersStateSide::RLOGO, pressed),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
let new_modifiers_state =
|
|
|
|
subclass_input.modifiers_state.filter_out_altgr().into();
|
|
|
|
if new_modifiers_state != old_modifiers_state {
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: ModifiersChanged(new_modifiers_state),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
subclass_input.send_event(Event::DeviceEvent {
|
|
|
|
device_id,
|
|
|
|
event: Key(KeyboardInput {
|
|
|
|
scancode,
|
|
|
|
state,
|
|
|
|
virtual_keycode,
|
|
|
|
modifiers: new_modifiers_state,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
|
|
|
}
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
_ if msg == *USER_EVENT_MSG_ID => {
|
|
|
|
if let Ok(event) = subclass_input.user_event_receiver.recv() {
|
|
|
|
subclass_input.send_event(Event::UserEvent(event));
|
|
|
|
}
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
_ if msg == *EXEC_MSG_ID => {
|
|
|
|
let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _);
|
|
|
|
function();
|
|
|
|
0
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
_ => commctrl::DefSubclassProc(window, msg, wparam, lparam),
|
2019-02-06 02:30:33 +11:00
|
|
|
}
|
|
|
|
}
|