From 026b331ba54e1a49909242d72c5d78ed0d24b255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kalm=C3=A1r=20R=C3=B3bert?= Date: Fri, 5 Jul 2019 18:37:25 +0200 Subject: [PATCH] Handle WM_POINTER* events in favor of WM_TOUCH Fixes #975 --- src/platform_impl/windows/dpi.rs | 30 +----- src/platform_impl/windows/event_loop.rs | 125 ++++++++++++++---------- src/platform_impl/windows/mod.rs | 3 +- src/platform_impl/windows/util.rs | 38 ++++++- 4 files changed, 114 insertions(+), 82 deletions(-) diff --git a/src/platform_impl/windows/dpi.rs b/src/platform_impl/windows/dpi.rs index 12186182..548c2608 100644 --- a/src/platform_impl/windows/dpi.rs +++ b/src/platform_impl/windows/dpi.rs @@ -9,13 +9,12 @@ use winapi::{ winerror::S_OK, }, um::{ - libloaderapi::{GetProcAddress, LoadLibraryA}, shellscalingapi::{ MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS, PROCESS_PER_MONITOR_DPI_AWARE, }, wingdi::{GetDeviceCaps, LOGPIXELSX}, - winnt::{HRESULT, LPCSTR}, + winnt::HRESULT, winuser::{self, MONITOR_DEFAULTTONEAREST}, }, }; @@ -35,33 +34,6 @@ type GetDpiForMonitor = unsafe extern "system" fn( ) -> HRESULT; type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL; -// Helper function to dynamically load function pointer. -// `library` and `function` must be zero-terminated. -fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> { - assert_eq!(library.chars().last(), Some('\0')); - assert_eq!(function.chars().last(), Some('\0')); - - // Library names we will use are ASCII so we can use the A version to avoid string conversion. - let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) }; - if module.is_null() { - return None; - } - - let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) }; - if function_ptr.is_null() { - return None; - } - - Some(function_ptr as _) -} - -macro_rules! get_function { - ($lib:expr, $func:ident) => { - get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0')) - .map(|f| unsafe { mem::transmute::<*const _, $func>(f) }) - }; -} - lazy_static! { static ref GET_DPI_FOR_WINDOW: Option = get_function!("user32.dll", GetDpiForWindow); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index d5d1e024..0204747e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1,3 +1,4 @@ +#![allow(non_snake_case)] //! 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. @@ -62,6 +63,22 @@ use crate::{ window::WindowId as RootWindowId, }; +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; + +lazy_static! { + static ref GET_POINTER_FRAME_INFO_HISTORY: Option = + get_function!("user32.dll", GetPointerFrameInfoHistory); + static ref SKIP_POINTER_FRAME_MESSAGES: Option = + get_function!("user32.dll", SkipPointerFrameMessages); +} + pub(crate) struct SubclassInput { pub window_state: Arc>, pub event_loop_runner: EventLoopRunnerShared, @@ -1458,60 +1475,66 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => { - let pointer_id = LOWORD(wparam as DWORD) as UINT; - let mut entries_count = 0 as UINT; - let mut pointers_count = 0 as UINT; - if winuser::GetPointerFrameInfoHistory( - pointer_id, - &mut entries_count as *mut _, - &mut pointers_count as *mut _, - std::ptr::null_mut(), - ) == 0 - { - return 0; - } + if let (Some(GetPointerFrameInfoHistory), Some(SkipPointerFrameMessages)) = ( + *GET_POINTER_FRAME_INFO_HISTORY, + *SKIP_POINTER_FRAME_MESSAGES, + ) { + 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; + } - 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 winuser::GetPointerFrameInfoHistory( - pointer_id, - &mut entries_count as *mut _, - &mut pointers_count as *mut _, - pointer_infos.as_mut_ptr(), - ) == 0 - { - return 0; - } + 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; + } - 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() { - let x = pointer_info.ptPixelLocation.x as f64; - let y = pointer_info.ptPixelLocation.y as f64; - 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, - id: pointer_info.pointerId as u64, - device_id: DEVICE_ID, - }), - }); - } + 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() { + let x = pointer_info.ptPixelLocation.x as f64; + let y = pointer_info.ptPixelLocation.y as f64; + 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, + id: pointer_info.pointerId as u64, + device_id: DEVICE_ID, + }), + }); + } - winuser::SkipPointerFrameMessages(pointer_id); + SkipPointerFrameMessages(pointer_id); + } 0 } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 58d86faf..6216fa29 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -67,6 +67,8 @@ impl WindowId { } } +#[macro_use] +mod util; mod dpi; mod drop_handler; mod event; @@ -74,6 +76,5 @@ mod event_loop; mod icon; mod monitor; mod raw_input; -mod util; mod window; mod window_state; diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 28513d30..4b82cefa 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -1,6 +1,7 @@ use std::{ io, mem, ops::BitAnd, + os::raw::c_void, ptr, slice, sync::atomic::{AtomicBool, Ordering}, }; @@ -12,9 +13,44 @@ use winapi::{ minwindef::{BOOL, DWORD}, windef::{HWND, POINT, RECT}, }, - um::{winbase::lstrlenW, winuser}, + um::{ + libloaderapi::{GetProcAddress, LoadLibraryA}, + winbase::lstrlenW, + winnt::LPCSTR, + winuser, + }, }; +// Helper function to dynamically load function pointer. +// `library` and `function` must be zero-terminated. +pub(super) fn get_function_impl(library: &str, function: &str) -> Option<*const c_void> { + assert_eq!(library.chars().last(), Some('\0')); + assert_eq!(function.chars().last(), Some('\0')); + + // Library names we will use are ASCII so we can use the A version to avoid string conversion. + let module = unsafe { LoadLibraryA(library.as_ptr() as LPCSTR) }; + if module.is_null() { + return None; + } + + let function_ptr = unsafe { GetProcAddress(module, function.as_ptr() as LPCSTR) }; + if function_ptr.is_null() { + return None; + } + + Some(function_ptr as _) +} + +macro_rules! get_function { + ($lib:expr, $func:ident) => { + crate::platform_impl::platform::util::get_function_impl( + concat!($lib, '\0'), + concat!(stringify!($func), '\0'), + ) + .map(|f| unsafe { std::mem::transmute::<*const _, $func>(f) }) + }; +} + pub fn has_flag(bitset: T, flag: T) -> bool where T: Copy + PartialEq + BitAnd,