Bug 1648104 - Consume WM_POINTER with PT_TOUCH r=win-reviewers,rkraesig

Using WM_POINTER prevents touch and mouse pointer interfering each other, which is good for users who use both touchscreen and touchpad e.g. on Surface Pro.

Differential Revision: https://phabricator.services.mozilla.com/D236227
This commit is contained in:
Kagami Sascha Rosylight
2025-02-03 13:13:14 +00:00
parent 01ddb24480
commit efa435a465
5 changed files with 122 additions and 8 deletions

View File

@@ -5043,6 +5043,13 @@
value: true
mirror: always
# Control consuming WM_POINTER for touch pointers instead of WM_TOUCH
- name: dom.w3c_pointer_events.dispatch_by_pointer_messages.touch
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Control whether to fire WidgetTouchEvent from pen pointers for APZ to handle scrolling.
- name: dom.w3c_pointer_events.scroll_by_pen.enabled
type: bool
value: true

View File

@@ -105,6 +105,21 @@ bool WinPointerEvents::GetPointerPenInfo(uint32_t aPointerId,
return getPointerPenInfo(aPointerId, aPenInfo);
}
void WinPointerEvents::GetPointerFrameTouchInfo(
uint32_t pointerId, nsTArray<POINTER_TOUCH_INFO>& aTouchInfoArray) {
uint32_t pointerCount = 0;
if (!::GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
return;
}
if (!pointerCount) {
return;
}
aTouchInfoArray.SetLength(pointerCount);
::GetPointerFrameTouchInfo(pointerId, &pointerCount,
aTouchInfoArray.Elements());
}
bool WinPointerEvents::ShouldRollupOnPointerEvent(UINT aMsg, WPARAM aWParam) {
MOZ_ASSERT(aMsg == WM_POINTERDOWN);
// Only roll up popups when we handling WM_POINTER* to fire Gecko

View File

@@ -44,6 +44,8 @@ class WinPointerEvents final {
POINTER_INPUT_TYPE GetPointerType(uint32_t aPointerId);
bool GetPointerInfo(uint32_t aPointerId, POINTER_INFO* aPointerInfo);
bool GetPointerPenInfo(uint32_t aPointerId, POINTER_PEN_INFO* aPenInfo);
void GetPointerFrameTouchInfo(uint32_t pointerId,
nsTArray<POINTER_TOUCH_INFO>& aTouchInfoArray);
bool ShouldRollupOnPointerEvent(UINT aMsg, WPARAM aWParam);
bool ShouldFirePointerEventByWinPointerMessages();
WinPointerInfo* GetCachedPointerInfo(UINT aMsg, WPARAM aWParam);

View File

@@ -8035,22 +8035,43 @@ bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
// which fallbacks to WM_LBUTTON* and WM_GESTURE, to keep consistency.
return false;
}
if (!mPointerEvents.ShouldHandleWinPointerMessages(msg, aWParam)) {
uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
POINTER_INPUT_TYPE pointerType = PT_POINTER;
if (!GetPointerType(pointerId, &pointerType)) {
MOZ_ASSERT(false, "cannot find PointerType");
return false;
}
if (pointerType == PT_TOUCH) {
if (!StaticPrefs::
dom_w3c_pointer_events_dispatch_by_pointer_messages_touch()) {
return false;
}
return OnTouchPointerEvents(pointerId, msg, aWParam, aLParam);
}
if (pointerType == PT_PEN) {
return OnPenPointerEvents(pointerId, msg, aWParam, aLParam);
}
return false;
}
bool nsWindow::OnPenPointerEvents(uint32_t aPointerId, UINT aMsg,
WPARAM aWParam, LPARAM aLParam) {
if (!mPointerEvents.ShouldFirePointerEventByWinPointerMessages()) {
// We have to handle WM_POINTER* to fetch and cache pen related information
// and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
// handler. This is because Windows doesn't support ::DoDragDrop in the
// touch or pen message handlers.
mPointerEvents.ConvertAndCachePointerInfo(msg, aWParam);
mPointerEvents.ConvertAndCachePointerInfo(aMsg, aWParam);
// Don't consume the Windows WM_POINTER* messages
return false;
}
uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
POINTER_PEN_INFO penInfo{};
if (!mPointerEvents.GetPointerPenInfo(pointerId, &penInfo)) {
if (!mPointerEvents.GetPointerPenInfo(aPointerId, &penInfo)) {
return false;
}
@@ -8070,7 +8091,7 @@ bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
EventMessage message;
mozilla::MouseButton button = MouseButton::ePrimary;
switch (msg) {
switch (aMsg) {
case WM_POINTERDOWN: {
LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
GET_Y_LPARAM(aLParam));
@@ -8122,8 +8143,8 @@ bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
int16_t buttons = sPointerDown
? nsContentUtils::GetButtonsFlagForButton(button)
: MouseButtonsFlag::eNoButtons;
WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
: static_cast<int16_t>(MouseButtonsFlag::eNoButtons);
WinPointerInfo pointerInfo(aPointerId, penInfo.tiltX, penInfo.tiltY, pressure,
buttons);
// Per
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_pen_info,
@@ -8134,7 +8155,7 @@ bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
// Fire touch events but not when the barrel button is pressed.
if (button != MouseButton::eSecondary &&
StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
DispatchTouchEventFromWMPointer(msg, aLParam, pointerInfo, button)) {
DispatchTouchEventFromWMPointer(aMsg, aLParam, pointerInfo, button)) {
return true;
}
@@ -8155,6 +8176,71 @@ bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
return true;
}
bool nsWindow::OnTouchPointerEvents(uint32_t aPointerId, UINT aMsg,
WPARAM aWParam, LPARAM aLParam) {
MultiTouchInput::MultiTouchType touchType;
switch (aMsg) {
case WM_POINTERDOWN:
touchType = MultiTouchInput::MULTITOUCH_START;
break;
case WM_POINTERUPDATE:
touchType = MultiTouchInput::MULTITOUCH_MOVE;
break;
case WM_POINTERUP:
touchType = MultiTouchInput::MULTITOUCH_END;
break;
default:
return false;
}
nsTArray<POINTER_TOUCH_INFO> touchInfoArray{};
mPointerEvents.GetPointerFrameTouchInfo(aPointerId, touchInfoArray);
if (touchInfoArray.IsEmpty()) {
return false;
}
MultiTouchInput inputToDispatch;
inputToDispatch.mInputType = MULTITOUCH_INPUT;
inputToDispatch.mType = touchType;
inputToDispatch.mTimeStamp = GetMessageTimeStamp(::GetMessageTime());
for (const POINTER_TOUCH_INFO& touchInfo : touchInfoArray) {
ScreenSize size(static_cast<float>(touchInfo.rcContact.right -
touchInfo.rcContact.left),
static_cast<float>(touchInfo.rcContact.bottom -
touchInfo.rcContact.top));
nsPointWin touchPoint;
touchPoint.x = touchInfo.pointerInfo.ptPixelLocation.x;
touchPoint.y = touchInfo.pointerInfo.ptPixelLocation.y;
touchPoint.ScreenToClient(mWnd);
// Windows provides orientation info, but the behavior differs from
// TouchEvent because TouchEvent's angle rotates the elliptic contact region
// while the Windows provided orientation is independent from touch point
// rect.
//
// e.g. For a vertically long touch pointer, Windows would give vertically
// long rect and also give a 90 degree orientation, and passing both would
// incorrectly represent a horizontal ellipse.
//
// See also: https://w3c.github.io/touch-events/#dom-touch-rotationangle
// "The angle (in degrees) that the ellipse described by radiusX and radiusY
// is rotated clockwise about its center; 0 if no value is known."
float angle = 0.0f;
bool hasPressure = !!(touchInfo.touchMask & TOUCH_MASK_PRESSURE);
float pressure = hasPressure ? (float)touchInfo.pressure / 1024 : 0;
inputToDispatch.mTouches.AppendElement(
SingleTouchData(static_cast<int32_t>(touchInfo.pointerInfo.pointerId),
ScreenIntPoint::FromUnknownPoint(touchPoint), size / 2,
angle, pressure));
}
DispatchTouchInput(inputToDispatch);
return true;
}
void nsWindow::GetCompositorWidgetInitData(
mozilla::widget::CompositorWidgetInitData* aInitData) {
*aInitData = WinCompositorWidgetInitData(

View File

@@ -609,6 +609,10 @@ class nsWindow final : public nsBaseWidget {
void OnSysColorChanged();
void OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height);
bool OnPointerEvents(UINT msg, WPARAM wParam, LPARAM lParam);
bool OnPenPointerEvents(uint32_t aPointerId, UINT aMsg, WPARAM aWParam,
LPARAM aLParam);
bool OnTouchPointerEvents(uint32_t aPointerId, UINT aMsg, WPARAM aWParam,
LPARAM aLParam);
/**
* Function that registers when the user has been active (used for detecting