native/desktop-win32/src/win32/pointer.rs (250 lines of code) (raw):

use windows::Win32::{ Foundation::{POINT, RECT, WPARAM}, Graphics::Gdi::{InflateRect, MapWindowPoints, PtInRect, SetRect}, UI::{ HiDpi::GetDpiForWindow, Input::{ KeyboardAndMouse::GetDoubleClickTime, Pointer::{ GetPointerInfo, GetPointerPenInfo, GetPointerTouchInfo, GetPointerType, POINTER_CHANGE_FIFTHBUTTON_DOWN, POINTER_CHANGE_FIFTHBUTTON_UP, POINTER_CHANGE_FIRSTBUTTON_DOWN, POINTER_CHANGE_FIRSTBUTTON_UP, POINTER_CHANGE_FOURTHBUTTON_DOWN, POINTER_CHANGE_FOURTHBUTTON_UP, POINTER_CHANGE_SECONDBUTTON_DOWN, POINTER_CHANGE_SECONDBUTTON_UP, POINTER_CHANGE_THIRDBUTTON_DOWN, POINTER_CHANGE_THIRDBUTTON_UP, POINTER_FLAG_FIFTHBUTTON, POINTER_FLAG_FIRSTBUTTON, POINTER_FLAG_FOURTHBUTTON, POINTER_FLAG_SECONDBUTTON, POINTER_FLAG_THIRDBUTTON, POINTER_INFO, POINTER_PEN_INFO, POINTER_TOUCH_INFO, }, }, WindowsAndMessaging::{ GetMessageTime, GetSystemMetrics, POINTER_INPUT_TYPE, PT_PEN, PT_TOUCH, SM_CXDOUBLECLK, SM_CYDOUBLECLK, USER_DEFAULT_SCREEN_DPI, }, }, }; use super::{ events::Timestamp, geometry::{LogicalPoint, PhysicalPoint}, utils::LOWORD, }; pub(crate) enum PointerInfo { Touch(POINTER_TOUCH_INFO), Pen(POINTER_PEN_INFO), Common(POINTER_INFO), } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PointerState { pressed_buttons: PointerButtons, modifiers: PointerModifiers, } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PointerButton { None = 0, Left = 1 << 0, Right = 1 << 1, Middle = 1 << 2, XButton1 = 1 << 3, XButton2 = 1 << 4, } #[repr(transparent)] #[derive(Debug, Clone, Copy)] pub struct PointerButtons(u32); #[repr(transparent)] #[derive(Debug, Clone, Copy)] pub struct PointerModifiers(u32); #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PointerButtonChange { button: PointerButton, kind: PointerButtonChangeKind, } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PointerButtonChangeKind { Other, Pressed, Released, } impl PointerButtonChange { #[inline] pub(crate) const fn button(self) -> PointerButton { self.button } #[inline] pub(crate) const fn kind(self) -> PointerButtonChangeKind { self.kind } } impl PointerInfo { pub(crate) fn try_from_message(wparam: WPARAM) -> windows::core::Result<Self> { let pointer_id = u32::from(LOWORD!(wparam.0)); let pointer_type = unsafe { let mut pointer_type = POINTER_INPUT_TYPE::default(); GetPointerType(pointer_id, &raw mut pointer_type) .inspect_err(|err| log::error!("failed to get pointer type for {pointer_id}: {err}")) .map(|()| pointer_type)? }; match pointer_type { PT_TOUCH => unsafe { let mut touch_info = POINTER_TOUCH_INFO::default(); GetPointerTouchInfo(pointer_id, &raw mut touch_info) .inspect_err(|err| log::error!("failed to get pointer touch info for {pointer_id}: {err}")) .map(|()| Self::Touch(touch_info)) }, PT_PEN => unsafe { let mut pen_info = POINTER_PEN_INFO::default(); GetPointerPenInfo(pointer_id, &raw mut pen_info) .inspect_err(|err| log::error!("failed to get pointer pen info for {pointer_id}: {err}")) .map(|()| Self::Pen(pen_info)) }, _ => unsafe { let mut pointer_info = POINTER_INFO::default(); GetPointerInfo(pointer_id, &raw mut pointer_info) .inspect_err(|err| log::error!("failed to get pointer info for {pointer_id}: {err}")) .map(|()| Self::Common(pointer_info)) }, } } const fn get_native_pointer_info(&self) -> &POINTER_INFO { match self { Self::Touch(touch_info) => &touch_info.pointerInfo, Self::Pen(pen_info) => &pen_info.pointerInfo, Self::Common(pointer_info) => pointer_info, } } pub(crate) fn get_pointer_state(&self) -> PointerState { let native_pointer_info = self.get_native_pointer_info(); let pointer_flags = native_pointer_info.pointerFlags; let pressed_buttons = { let mut buttons = 0_u32; if (pointer_flags & POINTER_FLAG_FIRSTBUTTON) == POINTER_FLAG_FIRSTBUTTON { buttons |= PointerButton::Left as u32; } if (pointer_flags & POINTER_FLAG_SECONDBUTTON) == POINTER_FLAG_SECONDBUTTON { buttons |= PointerButton::Right as u32; } if (pointer_flags & POINTER_FLAG_THIRDBUTTON) == POINTER_FLAG_THIRDBUTTON { buttons |= PointerButton::Middle as u32; } if (pointer_flags & POINTER_FLAG_FOURTHBUTTON) == POINTER_FLAG_FOURTHBUTTON { buttons |= PointerButton::XButton1 as u32; } if (pointer_flags & POINTER_FLAG_FIFTHBUTTON) == POINTER_FLAG_FIFTHBUTTON { buttons |= PointerButton::XButton2 as u32; } PointerButtons(buttons) }; PointerState { pressed_buttons, modifiers: unsafe { core::mem::transmute::<u32, PointerModifiers>(native_pointer_info.dwKeyStates) }, } } pub(crate) fn get_timestamp(&self) -> Timestamp { let native_pointer_info = self.get_native_pointer_info(); Timestamp(u64::from(native_pointer_info.dwTime) * 1000) } #[allow(clippy::cast_precision_loss)] pub(crate) fn get_location_in_window(&self) -> LogicalPoint { let native_pointer_info = self.get_native_pointer_info(); let window_dpi = unsafe { GetDpiForWindow(native_pointer_info.hwndTarget) }; let mut points = [native_pointer_info.ptPixelLocation]; unsafe { MapWindowPoints(None, Some(native_pointer_info.hwndTarget), &mut points) }; let x = ((points[0].x * USER_DEFAULT_SCREEN_DPI as i32) as f32) / (window_dpi as f32); let y = ((points[0].y * USER_DEFAULT_SCREEN_DPI as i32) as f32) / (window_dpi as f32); LogicalPoint::new(x, y) } pub(crate) const fn get_physical_location(&self) -> PhysicalPoint { let native_pointer_info = self.get_native_pointer_info(); PhysicalPoint::new(native_pointer_info.ptPixelLocation.x, native_pointer_info.ptPixelLocation.y) } pub(crate) const fn get_pointer_button_change(&self) -> PointerButtonChange { let native_pointer_info = self.get_native_pointer_info(); match native_pointer_info.ButtonChangeType { POINTER_CHANGE_FIRSTBUTTON_DOWN => PointerButtonChange { button: PointerButton::Left, kind: PointerButtonChangeKind::Pressed, }, POINTER_CHANGE_FIRSTBUTTON_UP => PointerButtonChange { button: PointerButton::Left, kind: PointerButtonChangeKind::Released, }, POINTER_CHANGE_SECONDBUTTON_DOWN => PointerButtonChange { button: PointerButton::Right, kind: PointerButtonChangeKind::Pressed, }, POINTER_CHANGE_SECONDBUTTON_UP => PointerButtonChange { button: PointerButton::Right, kind: PointerButtonChangeKind::Released, }, POINTER_CHANGE_THIRDBUTTON_DOWN => PointerButtonChange { button: PointerButton::Middle, kind: PointerButtonChangeKind::Pressed, }, POINTER_CHANGE_THIRDBUTTON_UP => PointerButtonChange { button: PointerButton::Middle, kind: PointerButtonChangeKind::Released, }, POINTER_CHANGE_FOURTHBUTTON_DOWN => PointerButtonChange { button: PointerButton::XButton1, kind: PointerButtonChangeKind::Pressed, }, POINTER_CHANGE_FOURTHBUTTON_UP => PointerButtonChange { button: PointerButton::XButton1, kind: PointerButtonChangeKind::Released, }, POINTER_CHANGE_FIFTHBUTTON_DOWN => PointerButtonChange { button: PointerButton::XButton2, kind: PointerButtonChangeKind::Pressed, }, POINTER_CHANGE_FIFTHBUTTON_UP => PointerButtonChange { button: PointerButton::XButton2, kind: PointerButtonChangeKind::Released, }, _ => PointerButtonChange { button: PointerButton::None, kind: PointerButtonChangeKind::Other, }, } } } pub(crate) struct PointerClickCounter { button: PointerButton, clicks: u32, last_click_time: u32, last_click_rect: RECT, } impl PointerClickCounter { pub fn new() -> Self { Self { button: PointerButton::None, clicks: 0, last_click_time: 0, last_click_rect: RECT::default(), } } // See https://devblogs.microsoft.com/oldnewthing/20041018-00/?p=37543 #[allow(clippy::cast_sign_loss)] pub fn register_click(&mut self, button: PointerButton, physical_point: PhysicalPoint) -> u32 { let (x, y) = (physical_point.x.0, physical_point.y.0); let pt = POINT { x, y }; let tm_click = unsafe { GetMessageTime() } as u32; if button != self.button || !unsafe { PtInRect(&raw const self.last_click_rect, pt) }.as_bool() || tm_click - self.last_click_time > unsafe { GetDoubleClickTime() } { self.clicks = 0; } self.clicks += 1; self.last_click_time = tm_click; unsafe { let _ = SetRect(&raw mut self.last_click_rect, x, y, x, y); let _ = InflateRect( &raw mut self.last_click_rect, GetSystemMetrics(SM_CXDOUBLECLK) / 2, GetSystemMetrics(SM_CYDOUBLECLK) / 2, ); } self.clicks } pub const fn reset(&mut self) { self.clicks = 0; } }