winit-core:winit: add tablet input support
The API is integrated into the `WindowEvent::Pointer*` API and is present in form of `TabletTool` variant on corresponding data entries. For now implemented for Web, Windows, and with limitations for Wayland. Fixes #99. Co-authored-by: daxpedda <daxpedda@gmail.com>
This commit is contained in:
parent
ffcdf80192
commit
f046e778aa
23 changed files with 1206 additions and 204 deletions
|
|
@ -44,28 +44,29 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
|||
GetMenu, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
||||
RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos, SystemParametersInfoW,
|
||||
TranslateMessage, CREATESTRUCTW, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT, MINMAXINFO,
|
||||
MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PM_REMOVE, PT_TOUCH, QS_ALLINPUT,
|
||||
RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||
SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE,
|
||||
SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT, WMSZ_BOTTOMRIGHT,
|
||||
WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT, WM_CAPTURECHANGED, WM_CLOSE,
|
||||
WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO,
|
||||
WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION,
|
||||
WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
|
||||
WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE,
|
||||
WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN,
|
||||
WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS,
|
||||
WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH,
|
||||
WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW,
|
||||
WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
|
||||
WS_VISIBLE,
|
||||
MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, NCCALCSIZE_PARAMS, PEN_FLAG_BARREL, PEN_FLAG_ERASER,
|
||||
PEN_MASK_PRESSURE, PEN_MASK_ROTATION, PEN_MASK_TILT_X, PEN_MASK_TILT_Y, PM_REMOVE, PT_PEN,
|
||||
PT_TOUCH, QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE,
|
||||
SIZE_MAXIMIZED, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE,
|
||||
SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WMSZ_BOTTOM, WMSZ_BOTTOMLEFT,
|
||||
WMSZ_BOTTOMRIGHT, WMSZ_LEFT, WMSZ_RIGHT, WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT,
|
||||
WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE,
|
||||
WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION,
|
||||
WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS,
|
||||
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL,
|
||||
WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN,
|
||||
WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SIZING, WM_SYSCOMMAND,
|
||||
WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING,
|
||||
WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
|
||||
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||
};
|
||||
use winit_core::application::ApplicationHandler;
|
||||
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
||||
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use winit_core::event::{
|
||||
DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase,
|
||||
WindowEvent,
|
||||
DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TabletToolButton,
|
||||
TabletToolData, TabletToolKind, TabletToolTilt, TouchPhase, WindowEvent,
|
||||
};
|
||||
use winit_core::event_loop::pump_events::PumpStatus;
|
||||
use winit_core::event_loop::{
|
||||
|
|
@ -106,6 +107,7 @@ pub(crate) struct WindowData {
|
|||
pub key_event_builder: KeyEventBuilder,
|
||||
pub _file_drop_handler: Option<FileDropHandler>,
|
||||
pub userdata_removed: Cell<bool>,
|
||||
pub last_tablet_down_button_state: Cell<u32>,
|
||||
pub recurse_depth: Cell<u32>,
|
||||
}
|
||||
|
||||
|
|
@ -2085,22 +2087,6 @@ unsafe fn public_window_callback_inner(
|
|||
continue;
|
||||
}
|
||||
|
||||
let force = if let PT_TOUCH = pointer_info.pointerType {
|
||||
let mut touch_info = mem::MaybeUninit::uninit();
|
||||
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
|
||||
match unsafe {
|
||||
GetPointerTouchInfo(pointer_info.pointerId, touch_info.as_mut_ptr())
|
||||
} {
|
||||
0 => None,
|
||||
_ => normalize_pointer_pressure(unsafe {
|
||||
touch_info.assume_init().pressure
|
||||
}),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let x = location.x as f64 + x.fract();
|
||||
let y = location.y as f64 + y.fract();
|
||||
let position = PhysicalPosition::new(x, y);
|
||||
|
|
@ -2108,60 +2094,83 @@ unsafe fn public_window_callback_inner(
|
|||
let finger_id = FingerId::from_raw(pointer_info.pointerId as usize);
|
||||
let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY);
|
||||
|
||||
if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN) {
|
||||
userdata.send_window_event(window, WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerKind::Touch(finger_id)
|
||||
} else {
|
||||
PointerKind::Unknown
|
||||
let is_down = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN);
|
||||
if is_down || util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
|
||||
let (kind, button) = match pointer_info.pointerType {
|
||||
PT_TOUCH => (PointerKind::Touch(finger_id), ButtonSource::Touch {
|
||||
finger_id,
|
||||
force: force_for_touch(pointer_info.pointerId),
|
||||
}),
|
||||
PT_PEN => {
|
||||
let kind = PointerKind::TabletTool(TabletToolKind::Pen);
|
||||
let (mut pen_flags, data) =
|
||||
tablet_tool_info_for_pen(pointer_info.pointerId);
|
||||
let old_pen_flags =
|
||||
userdata.last_tablet_down_button_state.replace(pen_flags);
|
||||
// For release, use a diff.
|
||||
if !is_down {
|
||||
pen_flags ^= old_pen_flags;
|
||||
};
|
||||
|
||||
let button = ButtonSource::TabletTool {
|
||||
kind: TabletToolKind::Pen,
|
||||
button: pen_flags_to_button(pen_flags),
|
||||
data,
|
||||
};
|
||||
|
||||
(kind, button)
|
||||
},
|
||||
});
|
||||
userdata.send_window_event(window, WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Pressed,
|
||||
position,
|
||||
button: if let PT_TOUCH = pointer_info.pointerType {
|
||||
ButtonSource::Touch { finger_id, force }
|
||||
} else {
|
||||
ButtonSource::Unknown(0)
|
||||
},
|
||||
});
|
||||
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) {
|
||||
userdata.send_window_event(window, WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Released,
|
||||
position,
|
||||
button: if let PT_TOUCH = pointer_info.pointerType {
|
||||
ButtonSource::Touch { finger_id, force }
|
||||
} else {
|
||||
ButtonSource::Unknown(0)
|
||||
},
|
||||
});
|
||||
userdata.send_window_event(window, WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerKind::Touch(finger_id)
|
||||
} else {
|
||||
PointerKind::Unknown
|
||||
},
|
||||
});
|
||||
_ => (PointerKind::Unknown, ButtonSource::Unknown(0)),
|
||||
};
|
||||
|
||||
if is_down {
|
||||
userdata.send_window_event(window, WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
kind,
|
||||
});
|
||||
|
||||
userdata.send_window_event(window, WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Pressed,
|
||||
position,
|
||||
button,
|
||||
});
|
||||
} else {
|
||||
userdata.send_window_event(window, WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary,
|
||||
state: Released,
|
||||
position,
|
||||
button,
|
||||
});
|
||||
userdata.send_window_event(window, WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary,
|
||||
position: Some(position),
|
||||
kind,
|
||||
});
|
||||
}
|
||||
} else if util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UPDATE) {
|
||||
let source = match pointer_info.pointerType {
|
||||
PT_TOUCH => PointerSource::Touch {
|
||||
finger_id,
|
||||
force: force_for_touch(pointer_info.pointerId),
|
||||
},
|
||||
PT_PEN => PointerSource::TabletTool {
|
||||
kind: TabletToolKind::Pen,
|
||||
data: tablet_tool_info_for_pen(pointer_info.pointerId).1,
|
||||
},
|
||||
_ => PointerSource::Unknown,
|
||||
};
|
||||
|
||||
userdata.send_window_event(window, WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary,
|
||||
position,
|
||||
source: if let PT_TOUCH = pointer_info.pointerType {
|
||||
PointerSource::Touch { finger_id, force }
|
||||
} else {
|
||||
PointerSource::Unknown
|
||||
},
|
||||
source,
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
|
|
@ -2691,3 +2700,57 @@ fn apply_win10_dpi_adjustment(
|
|||
|
||||
conservative_rect
|
||||
}
|
||||
|
||||
fn force_for_touch(pointer_id: u32) -> Option<Force> {
|
||||
let mut touch_info = mem::MaybeUninit::uninit();
|
||||
util::GET_POINTER_TOUCH_INFO.and_then(|GetPointerTouchInfo| {
|
||||
match unsafe { GetPointerTouchInfo(pointer_id, touch_info.as_mut_ptr()) } {
|
||||
0 => None,
|
||||
_ => normalize_pointer_pressure(unsafe { touch_info.assume_init().pressure }),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Information is stored on the same thing, so we don't save anything by
|
||||
// splitting the functions.
|
||||
fn tablet_tool_info_for_pen(pointer_id: u32) -> (u32, TabletToolData) {
|
||||
let mut tool_data = TabletToolData::default();
|
||||
let mut tool_button = 0;
|
||||
|
||||
let mut tablet_info = mem::MaybeUninit::uninit();
|
||||
util::GET_POINTER_PEN_INFO.map(|GetPointerPenInfo| {
|
||||
if unsafe { GetPointerPenInfo(pointer_id, tablet_info.as_mut_ptr()) } == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let pen_info = unsafe { tablet_info.assume_init() };
|
||||
if pen_info.penMask & PEN_MASK_PRESSURE != 0 {
|
||||
tool_data.force = normalize_pointer_pressure(pen_info.pressure);
|
||||
}
|
||||
if pen_info.penMask & (PEN_MASK_TILT_X | PEN_MASK_TILT_Y) != 0 {
|
||||
tool_data.tilt =
|
||||
Some(TabletToolTilt { x: pen_info.tiltX as i8, y: pen_info.tiltY as i8 });
|
||||
}
|
||||
if pen_info.penMask & PEN_MASK_ROTATION != 0 {
|
||||
tool_data.twist = Some(pen_info.rotation as u16);
|
||||
}
|
||||
|
||||
tool_button = pen_info.penFlags;
|
||||
});
|
||||
|
||||
(tool_button, tool_data)
|
||||
}
|
||||
|
||||
// NOTE: According to firefox, the buttons while can be combined, in
|
||||
// reality they are not.
|
||||
fn pen_flags_to_button(flags: u32) -> TabletToolButton {
|
||||
if flags & PEN_FLAG_BARREL != 0 {
|
||||
TabletToolButton::Barrel
|
||||
} else if flags & PEN_FLAG_ERASER != 0 {
|
||||
// NOTE: this is likely not a correct thing to do, but route
|
||||
// it that way for now.
|
||||
TabletToolButton::Other(PEN_FLAG_ERASER as u16)
|
||||
} else {
|
||||
TabletToolButton::Contact
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use windows_sys::Win32::UI::HiDpi::{
|
|||
DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
|
||||
};
|
||||
use windows_sys::Win32::UI::Input::KeyboardAndMouse::GetActiveWindow;
|
||||
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_TOUCH_INFO};
|
||||
use windows_sys::Win32::UI::Input::Pointer::{POINTER_INFO, POINTER_PEN_INFO, POINTER_TOUCH_INFO};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
ClipCursor, GetClientRect, GetClipCursor, GetCursorPos, GetSystemMetrics, GetWindowPlacement,
|
||||
GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP,
|
||||
|
|
@ -254,6 +254,8 @@ pub type GetPointerDeviceRects = unsafe extern "system" fn(
|
|||
|
||||
pub type GetPointerTouchInfo =
|
||||
unsafe extern "system" fn(pointer_id: u32, touch_info: *mut POINTER_TOUCH_INFO) -> BOOL;
|
||||
pub type GetPointerPenInfo =
|
||||
unsafe extern "system" fn(pointer_id: u32, pen_info: *mut POINTER_PEN_INFO) -> BOOL;
|
||||
|
||||
pub(crate) static WIN10_BUILD_VERSION: LazyLock<Option<u32>> = LazyLock::new(|| {
|
||||
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS;
|
||||
|
|
@ -306,6 +308,8 @@ pub(crate) static GET_POINTER_DEVICE_RECTS: LazyLock<Option<GetPointerDeviceRect
|
|||
LazyLock::new(|| get_function!("user32.dll", GetPointerDeviceRects));
|
||||
pub(crate) static GET_POINTER_TOUCH_INFO: LazyLock<Option<GetPointerTouchInfo>> =
|
||||
LazyLock::new(|| get_function!("user32.dll", GetPointerTouchInfo));
|
||||
pub(crate) static GET_POINTER_PEN_INFO: LazyLock<Option<GetPointerPenInfo>> =
|
||||
LazyLock::new(|| get_function!("user32.dll", GetPointerPenInfo));
|
||||
|
||||
pub(crate) fn wrap_device_id(id: u32) -> DeviceId {
|
||||
DeviceId::from_raw(id as i64)
|
||||
|
|
|
|||
|
|
@ -1263,6 +1263,7 @@ impl InitData<'_> {
|
|||
_file_drop_handler: file_drop_handler,
|
||||
userdata_removed: Cell::new(false),
|
||||
recurse_depth: Cell::new(0),
|
||||
last_tablet_down_button_state: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue