author | Stone Shih <sshih@mozilla.com> |
Sun, 20 Nov 2016 10:24:46 +0800 | |
changeset 374264 | f397c3444f2aa531fc00d64e4fed21cc933d0f48 |
parent 374263 | c8cb5fc30a367f7e023c2c381689e2b596f40100 |
child 374265 | 9bff72325005fb94a940968432bd39870789afcb |
push id | 1419 |
push user | jlund@mozilla.com |
push date | Mon, 10 Apr 2017 20:44:07 +0000 |
treeherder | mozilla-release@5e6801b73ef6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jimm, smaug |
bugs | 1031362 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -53,16 +53,25 @@ public: : pointerId(0) , tiltX(0) , tiltY(0) , convertToPointer(true) , retargetedByPointerCapture(false) { } + WidgetPointerHelper(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY) + : pointerId(aPointerId) + , tiltX(aTiltX) + , tiltY(aTiltY) + , convertToPointer(true) + , retargetedByPointerCapture(false) + { + } + void AssignPointerHelperData(const WidgetPointerHelper& aEvent) { pointerId = aEvent.pointerId; tiltX = aEvent.tiltX; tiltY = aEvent.tiltY; convertToPointer = aEvent.convertToPointer; retargetedByPointerCapture = aEvent.retargetedByPointerCapture; }
new file mode 100644 --- /dev/null +++ b/widget/windows/WinPointerEvents.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * WinPointerEvents - Helper functions to retrieve PointerEvent's attributes + */ + +#include "nscore.h" +#include "WinPointerEvents.h" +#include "mozilla/MouseEvents.h" + +using namespace mozilla; +using namespace mozilla::widget; + +const wchar_t WinPointerEvents::kPointerLibraryName[] = L"user32.dll"; +HMODULE WinPointerEvents::sLibraryHandle = nullptr; +WinPointerEvents::GetPointerTypePtr WinPointerEvents::getPointerType = nullptr; +WinPointerEvents::GetPointerInfoPtr WinPointerEvents::getPointerInfo = nullptr; +WinPointerEvents::GetPointerPenInfoPtr WinPointerEvents::getPointerPenInfo = nullptr; +bool WinPointerEvents::sPointerEventEnabled = true; + +WinPointerEvents::WinPointerEvents() +{ + InitLibrary(); + static bool addedPointerEventEnabled = false; + if (!addedPointerEventEnabled) { + Preferences::AddBoolVarCache(&sPointerEventEnabled, + "dom.w3c_pointer_events.enabled", true); + addedPointerEventEnabled = true; + } +} + +/* Load and shutdown */ +void +WinPointerEvents::InitLibrary() +{ + MOZ_ASSERT(XRE_IsParentProcess()); + if (!IsWin8OrLater()) { + // Only Win8 or later supports WM_POINTER* + return; + } + if (getPointerType) { + // Return if we already initialized the PointerEvent related interfaces + return; + } + sLibraryHandle = ::LoadLibraryW(kPointerLibraryName); + MOZ_ASSERT(sLibraryHandle, "cannot load pointer library"); + if (sLibraryHandle) { + getPointerType = + (GetPointerTypePtr)GetProcAddress(sLibraryHandle, "GetPointerType"); + getPointerInfo = + (GetPointerInfoPtr)GetProcAddress(sLibraryHandle, "GetPointerInfo"); + getPointerPenInfo = + (GetPointerPenInfoPtr)GetProcAddress(sLibraryHandle, "GetPointerPenInfo"); + } + + if (!getPointerType || !getPointerInfo || !getPointerPenInfo) { + MOZ_ASSERT(false, "get PointerEvent interfaces failed"); + getPointerType = nullptr; + getPointerInfo = nullptr; + getPointerPenInfo = nullptr; + return; + } +} + +bool +WinPointerEvents::ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam) +{ + if (!sLibraryHandle || !sPointerEventEnabled) { + // Firing mouse events by handling Windows WM_POINTER* when preference is on + // and the Windows platform supports PointerEvent related interfaces. + return false; + } + + uint32_t pointerId = GetPointerId(aWParam); + POINTER_INPUT_TYPE pointerType = PT_POINTER; + if (!GetPointerType(pointerId, &pointerType)) { + MOZ_ASSERT(false, "cannot find PointerType"); + return false; + } + return (pointerType == PT_PEN); +} + +bool +WinPointerEvents::GetPointerType(uint32_t aPointerId, + POINTER_INPUT_TYPE *aPointerType) +{ + if (!getPointerType) { + return false; + } + return getPointerType(aPointerId, aPointerType); +} + +bool +WinPointerEvents::GetPointerInfo(uint32_t aPointerId, + POINTER_INFO *aPointerInfo) +{ + if (!getPointerInfo) { + return false; + } + return getPointerInfo(aPointerId, aPointerInfo); +} + +bool +WinPointerEvents::GetPointerPenInfo(uint32_t aPointerId, + POINTER_PEN_INFO *aPenInfo) +{ + if (!getPointerPenInfo) { + return false; + } + return getPointerPenInfo(aPointerId, aPenInfo); +}
new file mode 100644 --- /dev/null +++ b/widget/windows/WinPointerEvents.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WinPointerEvents_h__ +#define WinPointerEvents_h__ + +#include "mozilla/MouseEvents.h" + +// Define PointerEvent related macros and structures when building code on +// Windows version before Win8. +#if WINVER < 0x0602 + +// These definitions are copied from WinUser.h. Some of them are not used but +// keep them here for future usage. +#define WM_NCPOINTERUPDATE 0x0241 +#define WM_NCPOINTERDOWN 0x0242 +#define WM_NCPOINTERUP 0x0243 +#define WM_POINTERUPDATE 0x0245 +#define WM_POINTERDOWN 0x0246 +#define WM_POINTERUP 0x0247 +#define WM_POINTERENTER 0x0249 +#define WM_POINTERLEAVE 0x024A +#define WM_POINTERACTIVATE 0x024B +#define WM_POINTERCAPTURECHANGED 0x024C +#define WM_TOUCHHITTESTING 0x024D +#define WM_POINTERWHEEL 0x024E +#define WM_POINTERHWHEEL 0x024F +#define DM_POINTERHITTEST 0x0250 + +typedef UINT32 PEN_FLAGS; +#define PEN_FLAG_NONE 0x00000000 // Default +#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed +#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted +#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed + +typedef UINT32 PEN_MASK; +#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid +#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid +#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid +#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid +#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid + +typedef struct tagPOINTER_PEN_INFO { + POINTER_INFO pointerInfo; + PEN_FLAGS penFlags; + PEN_MASK penMask; + UINT32 pressure; + UINT32 rotation; + INT32 tiltX; + INT32 tiltY; +} POINTER_PEN_INFO; + +/* + * Flags that appear in pointer input message parameters + */ +#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer +#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed +#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact +#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action +#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action +#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button +#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button +#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button +#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary +#define POINTER_MESSAGE_FLAG_CONFIDENCE 0x00004000 // Pointer is considered unlikely to be accidental +#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner + +/* + * Macros to retrieve information from pointer input message parameters + */ +#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) +#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) +#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) +#define IS_POINTER_INRANGE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) +#define IS_POINTER_INCONTACT_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) +#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) +#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) +#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) +#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) +#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) +#define IS_POINTER_PRIMARY_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) +#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) +#define IS_POINTER_CANCELED_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) + +/* + * WM_POINTERACTIVATE return codes + */ +#define PA_ACTIVATE MA_ACTIVATE +#define PA_NOACTIVATE MA_NOACTIVATE + +#endif // WINVER < 0x0602 + +/****************************************************************************** + * WinPointerInfo + * + * This is a helper class to handle WM_POINTER*. It only supports Win8 or later. + * + ******************************************************************************/ +class WinPointerInfo final : public mozilla::WidgetPointerHelper +{ +public: + WinPointerInfo() + : WidgetPointerHelper() + { + } + + WinPointerInfo(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY, + float aPressure, int16_t aButtons) + : WidgetPointerHelper(aPointerId, aTiltX, aTiltY) + , mPressure(aPressure) + , mButtons(aButtons) + { + } + + float mPressure; + int16_t mButtons; +}; + +class WinPointerEvents final +{ +public: + explicit WinPointerEvents(); + +public: + bool ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam); + + uint32_t GetPointerId(WPARAM aWParam) + { + return GET_POINTERID_WPARAM(aWParam); + } + bool GetPointerType(uint32_t aPointerId, POINTER_INPUT_TYPE *aPointerType); + bool GetPointerInfo(uint32_t aPointerId, POINTER_INFO *aPointerInfo); + bool GetPointerPenInfo(uint32_t aPointerId, POINTER_PEN_INFO *aPenInfo); + +private: + // Function prototypes + typedef BOOL (WINAPI* GetPointerTypePtr)(uint32_t aPointerId, + POINTER_INPUT_TYPE *aPointerType); + typedef BOOL (WINAPI* GetPointerInfoPtr)(uint32_t aPointerId, + POINTER_INFO *aPointerInfo); + typedef BOOL (WINAPI* GetPointerPenInfoPtr)(uint32_t aPointerId, + POINTER_PEN_INFO *aPenInfo); + + void InitLibrary(); + + static HMODULE sLibraryHandle; + static const wchar_t kPointerLibraryName[]; + static bool sPointerEventEnabled; + // Static function pointers + static GetPointerTypePtr getPointerType; + static GetPointerInfoPtr getPointerInfo; + static GetPointerPenInfoPtr getPointerPenInfo; +}; + +#endif // #ifndef WinPointerEvents_h__
--- a/widget/windows/moz.build +++ b/widget/windows/moz.build @@ -58,16 +58,17 @@ UNIFIED_SOURCES += [ 'nsWinGesture.cpp', 'TaskbarPreview.cpp', 'TaskbarPreviewButton.cpp', 'TaskbarTabPreview.cpp', 'TaskbarWindowPreview.cpp', 'WidgetTraceEvent.cpp', 'WindowHook.cpp', 'WinIMEHandler.cpp', + 'WinPointerEvents.cpp', 'WinTaskbar.cpp', 'WinTextEventDispatcherListener.cpp', 'WinUtils.cpp', ] # The following files cannot be built in unified mode because of name clashes. SOURCES += [ 'JumpListBuilder.cpp',
--- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3393,17 +3393,17 @@ nsWindow::MakeFullScreen(bool aFullScree // Send a eMouseEnterIntoWidget event since Windows has already sent // a WM_MOUSELEAVE that caused us to send a eMouseExitFromWidget event. if (aFullScreen && !sCurrentWindow) { sCurrentWindow = this; LPARAM pos = sCurrentWindow->lParamToClient(sMouseExitlParamScreen); sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget, sMouseExitwParam, pos, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); } return NS_OK; } /************************************************************** * * SECTION: Native data storage @@ -4127,17 +4127,17 @@ bool nsWindow::DispatchPluginEvent(UINT return ret; } // Deal with all sort of mouse event bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam, LPARAM lParam, bool aIsContextMenuKey, int16_t aButton, uint16_t aInputSource, - uint16_t aPointerId) + WinPointerInfo* aPointerInfo) { bool result = false; UserActivity(); if (!mWidgetListener) { return result; } @@ -4164,25 +4164,28 @@ nsWindow::DispatchMouseEvent(EventMessag // If mTouchWindow is true, then we must have APZ enabled and be // feeding it raw touch events. In that case we don't need to // send touch-generated mouse events to content. MOZ_ASSERT(mAPZC); return result; } } + uint32_t pointerId = aPointerInfo ? aPointerInfo->pointerId : + MOUSE_POINTERID(); + // Since it is unclear whether a user will use the digitizer, // Postpone initialization until first PEN message will be found. if (nsIDOMMouseEvent::MOZ_SOURCE_PEN == aInputSource // Messages should be only at topLevel window. && nsWindowType::eWindowType_toplevel == mWindowType // Currently this scheme is used only when pointer events is enabled. && gfxPrefs::PointerEventsEnabled()) { InkCollector::sInkCollector->SetTarget(mWnd); - InkCollector::sInkCollector->SetPointerId(aPointerId); + InkCollector::sInkCollector->SetPointerId(pointerId); } switch (aEventMessage) { case eMouseDown: CaptureMouse(true); break; // eMouseMove and eMouseExitFromWidget are here because we need to make @@ -4209,20 +4212,28 @@ nsWindow::DispatchMouseEvent(EventMessag } else { InitEvent(event, &eventPoint); } ModifierKeyState modifierKeyState; modifierKeyState.InitInputEvent(event); event.button = aButton; event.inputSource = aInputSource; - event.pointerId = aPointerId; - // If we get here the mouse events must be from non-touch sources, so - // convert it to pointer events as well - event.convertToPointer = true; + if (aPointerInfo) { + // Mouse events from Windows WM_POINTER*. Fill more information in + // WidgetMouseEvent. + event.AssignPointerHelperData(*aPointerInfo); + event.pressure = aPointerInfo->mPressure; + event.buttons = aPointerInfo->mButtons; + } else { + // If we get here the mouse events must be from non-touch sources, so + // convert it to pointer events as well + event.convertToPointer = true; + event.pointerId = pointerId; + } bool insideMovementThreshold = (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) < (short)::GetSystemMetrics(SM_CXDOUBLECLK)) && (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) < (short)::GetSystemMetrics(SM_CYDOUBLECLK)); BYTE eventButton; switch (aButton) { case WidgetMouseEvent::eLeftButton: eventButton = VK_LBUTTON; @@ -4361,25 +4372,25 @@ nsWindow::DispatchMouseEvent(EventMessag if (rect.Contains(event.mRefPoint)) { if (sCurrentWindow == nullptr || sCurrentWindow != this) { if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) { LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam)); sCurrentWindow->DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false, WidgetMouseEvent::eLeftButton, - aInputSource, aPointerId); + aInputSource, aPointerInfo); } sCurrentWindow = this; if (!mInDtor) { LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam)); sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget, wParam, pos, false, WidgetMouseEvent::eLeftButton, - aInputSource, aPointerId); + aInputSource, aPointerInfo); } } } } else if (aEventMessage == eMouseExitFromWidget) { sMouseExitwParam = wParam; sMouseExitlParamScreen = lParamToScreen(lParam); if (sCurrentWindow == this) { sCurrentWindow = nullptr; @@ -5280,17 +5291,17 @@ nsWindow::ProcessMessage(UINT msg, WPARA mp.y = GET_Y_LPARAM(lParamScreen); bool userMovedMouse = false; if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) { userMovedMouse = true; } result = DispatchMouseEvent(eMouseMove, wParam, lParam, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); if (userMovedMouse) { DispatchPendingEvents(); } } break; case WM_NCMOUSEMOVE: // If we receive a mouse move event on non-client chrome, make sure and @@ -5298,26 +5309,26 @@ nsWindow::ProcessMessage(UINT msg, WPARA if (mMousePresent && !sIsInMouseCapture) SendMessage(mWnd, WM_MOUSELEAVE, 0, 0); break; case WM_LBUTTONDOWN: { result = DispatchMouseEvent(eMouseDown, wParam, lParam, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); } break; case WM_LBUTTONUP: { result = DispatchMouseEvent(eMouseUp, wParam, lParam, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); } break; case WM_MOUSELEAVE: { if (!mMousePresent) break; @@ -5328,28 +5339,30 @@ nsWindow::ProcessMessage(UINT msg, WPARA WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) | (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) | (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0); // Synthesize an event position because we don't get one from // WM_MOUSELEAVE. LPARAM pos = lParamToClient(::GetMessagePos()); DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); } break; case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER: { LPARAM pos = lParamToClient(::GetMessagePos()); uint16_t pointerId = InkCollector::sInkCollector->GetPointerId(); if (pointerId != 0) { + WinPointerInfo pointerInfo; + pointerInfo.pointerId = pointerId; DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false, WidgetMouseEvent::eLeftButton, - nsIDOMMouseEvent::MOZ_SOURCE_PEN, pointerId); + nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo); InkCollector::sInkCollector->ClearTarget(); InkCollector::sInkCollector->ClearPointerId(); } } break; case WM_CONTEXTMENU: { @@ -5375,127 +5388,137 @@ nsWindow::ProcessMessage(UINT msg, WPARA { pos = lParamToClient(lParam); } result = DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey, contextMenukey ? WidgetMouseEvent::eLeftButton : WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); if (lParam != -1 && !result && mCustomNonClient && mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) { // Blank area hit, throw up the system menu. DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); result = true; } } break; + case WM_POINTERLEAVE: + case WM_POINTERDOWN: + case WM_POINTERUP: + case WM_POINTERUPDATE: + result = OnPointerEvents(msg, wParam, lParam); + if (result) { + DispatchPendingEvents(); + } + break; + case WM_LBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_MBUTTONDOWN: result = DispatchMouseEvent(eMouseDown, wParam, lParam, false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_MBUTTONUP: result = DispatchMouseEvent(eMouseUp, wParam, lParam, false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_MBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCMBUTTONDOWN: result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCMBUTTONUP: result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCMBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false, WidgetMouseEvent::eMiddleButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_RBUTTONDOWN: result = DispatchMouseEvent(eMouseDown, wParam, lParam, false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_RBUTTONUP: result = DispatchMouseEvent(eMouseUp, wParam, lParam, false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_RBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCRBUTTONDOWN: result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCRBUTTONUP: result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_NCRBUTTONDBLCLK: result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false, WidgetMouseEvent::eRightButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; // Windows doesn't provide to customize the behavior of 4th nor 5th button // of mouse. If 5-button mouse works with standard mouse deriver of // Windows, users cannot disable 4th button (browser back) nor 5th button // (browser forward). We should allow to do it with our prefs since we can // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP @@ -5566,21 +5589,21 @@ nsWindow::ProcessMessage(UINT msg, WPARA } break; } case WM_NCLBUTTONDBLCLK: DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false, WidgetMouseEvent::eLeftButton, - MOUSE_INPUT_SOURCE(), MOUSE_POINTERID()); + MOUSE_INPUT_SOURCE()); DispatchPendingEvents(); break; case WM_APPCOMMAND: { MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd); result = HandleAppCommandMsg(nativeMsg, aRetValue); break; @@ -8018,16 +8041,117 @@ nsWindow::OnWindowedPluginKeyEvent(const default: // We shouldn't consume WM_*CHAR messages here even if the preceding // keydown or keyup event on the plugin is consumed. It should be // managed in each plugin window rather than top level window. return NS_OK; } } +bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) +{ + if (!mPointerEvents.ShouldFireCompatibilityMouseEventsForPen(aWParam)) { + // We only handle WM_POINTER* when the input source is pen. This is because + // we need some information (e.g. tiltX, tiltY) which can't be retrieved by + // WM_*BUTTONDOWN. So we fire Gecko WidgetMouseEvent when handling + // WM_POINTER* and consume WM_POINTER* to stop Windows fire WM_*BUTTONDOWN. + return false; + } + + // When dispatching mouse events with pen, there may be some + // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with + // small movements. Those events will reset sLastMousePoint and reset + // sLastClickCount. To prevent that, we keep the last pen down position + // and compare it with the subsequent WM_POINTERUPDATE. If the movement is + // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing + // eMouseMove for WM_POINTERUPDATE. + static POINT sLastPointerDownPoint = {0}; + + // We don't support chorded buttons for pen. Keep the button at + // WM_POINTERDOWN. + static WidgetMouseEvent::buttonType sLastPenDownButton = + WidgetMouseEvent::eLeftButton; + static bool sPointerDown = false; + + EventMessage message; + WidgetMouseEvent::buttonType button = WidgetMouseEvent::eLeftButton; + switch (msg) { + case WM_POINTERDOWN: + { + LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam), + GET_Y_LPARAM(aLParam)); + sLastPointerDownPoint.x = eventPoint.x; + sLastPointerDownPoint.y = eventPoint.y; + message = eMouseDown; + button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam) ? + WidgetMouseEvent::eRightButton : WidgetMouseEvent::eLeftButton; + sLastPenDownButton = button; + sPointerDown = true; + } + break; + case WM_POINTERUP: + message = eMouseUp; + MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN"); + button = sPointerDown ? sLastPenDownButton : WidgetMouseEvent::eLeftButton; + sPointerDown = false; + break; + case WM_POINTERUPDATE: + message = eMouseMove; + if (sPointerDown) { + LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam), + GET_Y_LPARAM(aLParam)); + int32_t movementX = sLastPointerDownPoint.x > eventPoint.x ? + sLastPointerDownPoint.x - eventPoint.x : + eventPoint.x - sLastPointerDownPoint.x; + int32_t movementY = sLastPointerDownPoint.y > eventPoint.y ? + sLastPointerDownPoint.y - eventPoint.y : + eventPoint.y - sLastPointerDownPoint.y; + bool insideMovementThreshold = + movementX < (int32_t)::GetSystemMetrics(SM_CXDRAG) && + movementY < (int32_t)::GetSystemMetrics(SM_CYDRAG); + + if (insideMovementThreshold) { + // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement + // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG + return false; + } + button = sLastPenDownButton; + } + break; + case WM_POINTERLEAVE: + message = eMouseExitFromWidget; + break; + default: + return false; + } + uint32_t pointerId = mPointerEvents.GetPointerId(aWParam); + POINTER_PEN_INFO penInfo; + mPointerEvents.GetPointerPenInfo(pointerId, &penInfo); + + // Windows defines the pen pressure is normalized to a range between 0 and + // 1024. Convert it to float. + float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0; + int16_t buttons = + sPointerDown ? button == WidgetMouseEvent::eLeftButton ? + WidgetMouseEvent::eLeftButtonFlag : + WidgetMouseEvent::eRightButtonFlag : + WidgetMouseEvent::eNoButtonFlag; + WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure, + buttons); + + // The aLParam of WM_POINTER* is the screen location. Convert it to client + // location + LPARAM newLParam = lParamToClient(aLParam); + DispatchMouseEvent(message, aWParam, newLParam, false, button, + nsIDOMMouseEvent::MOZ_SOURCE_PEN, &pointerInfo); + // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP + // WM_MOUSEMOVE. + return true; +} + /************************************************************** ************************************************************** ** ** BLOCK: ChildWindow impl. ** ** Child window overrides. ** **************************************************************
--- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -26,16 +26,17 @@ #include "nsRegion.h" #include "mozilla/EventForwards.h" #include "mozilla/MouseEvents.h" #include "mozilla/TimeStamp.h" #include "nsMargin.h" #include "nsRegionFwd.h" #include "nsWinGesture.h" +#include "WinPointerEvents.h" #include "WinUtils.h" #include "WindowHook.h" #include "TaskbarWindowPreview.h" #ifdef ACCESSIBILITY #include "oleacc.h" #include "mozilla/a11y/Accessible.h" #endif @@ -224,17 +225,17 @@ public: mozilla::EventMessage aEventMessage, WPARAM wParam, LPARAM lParam, bool aIsContextMenuKey = false, int16_t aButton = mozilla::WidgetMouseEvent::eLeftButton, uint16_t aInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE, - uint16_t aPointerId = 0); + WinPointerInfo* aPointerInfo = nullptr); virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus); void DispatchPendingEvents(); bool DispatchPluginEvent(UINT aMessage, WPARAM aWParam, LPARAM aLParam, bool aDispatchPendingEvents); @@ -423,16 +424,18 @@ protected: bool OnTouch(WPARAM wParam, LPARAM lParam); bool OnHotKey(WPARAM wParam, LPARAM lParam); bool OnPaint(HDC aDC, uint32_t aNestingLevel); void OnWindowPosChanged(WINDOWPOS* wp); void OnWindowPosChanging(LPWINDOWPOS& info); void OnSysColorChanged(); void OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height); + bool OnPointerEvents(UINT msg, WPARAM wParam, + LPARAM lParam); /** * Function that registers when the user has been active (used for detecting * when the user is idle). */ void UserActivity(); int32_t GetHeight(int32_t aProposedHeight); @@ -649,16 +652,19 @@ protected: double mSizeConstraintsScale; // scale in effect when setting constraints // Used to remember the wParam (i.e. currently pressed modifier keys) // and lParam (i.e. last mouse position) in screen coordinates from // the previous mouse-exit. Static since it is not // associated with a particular widget (since we exited the widget). static WPARAM sMouseExitwParam; static LPARAM sMouseExitlParamScreen; + + // Pointer events processing and management + WinPointerEvents mPointerEvents; }; /** * A child window is a window with different style. */ class ChildWindow : public nsWindow { public: