summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre de la Rocha <andre.rocha@qt.io>2018-05-25 14:48:03 +0200
committerAndre de la Rocha <andre.rocha@qt.io>2018-07-12 09:52:07 +0000
commit20d6dac63c25d227ed5315801e3e853ee78ec248 (patch)
tree1d192a1f407db1146191db653e3b1737d0d27d55
parentdffbf4a7f6b5d5544c13ac059822512a770d4df8 (diff)
Add Windows Pointer Input Messages support
Replaces the handling of tablet/touchscreen/touchpad/mouse input with a unified implementation based on the Windows Pointer Input Messages added to Windows 8. The legacy implementation is still used for Windows 7. Task-number: QTBUG-60437 Change-Id: I0a0f48ee9d5365f84ba528aa04c6ab1fe4253c50 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
-rw-r--r--src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp24
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h25
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp125
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h23
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp49
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.h2
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp573
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.h82
-rw-r--r--src/plugins/platforms/windows/windows.pri2
11 files changed, 854 insertions, 65 deletions
diff --git a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp
index aa7bb102c6..3583ead788 100644
--- a/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp
+++ b/src/platformsupport/eventdispatchers/qwindowsguieventdispatcher.cpp
@@ -184,7 +184,29 @@ messageDebugEntries[] = {
{WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD", true},
{WM_THEMECHANGED, "WM_THEMECHANGED", true},
{0x90, "WM_UAHDESTROYWINDOW", true},
- {0x272, "WM_UNREGISTER_WINDOW_SERVICES", true}
+ {0x272, "WM_UNREGISTER_WINDOW_SERVICES", true},
+#ifdef WM_POINTERUPDATE
+ {WM_POINTERDEVICECHANGE, "WM_POINTERDEVICECHANGE", true},
+ {WM_POINTERDEVICEINRANGE, "WM_POINTERDEVICEINRANGE", true},
+ {WM_POINTERDEVICEOUTOFRANGE, "WM_POINTERDEVICEOUTOFRANGE", true},
+ {WM_NCPOINTERUPDATE, "WM_NCPOINTERUPDATE", true},
+ {WM_NCPOINTERDOWN, "WM_NCPOINTERDOWN", true},
+ {WM_NCPOINTERUP, "WM_NCPOINTERUP", true},
+ {WM_POINTERUPDATE, "WM_POINTERUPDATE", true},
+ {WM_POINTERDOWN, "WM_POINTERDOWN", true},
+ {WM_POINTERUP, "WM_POINTERUP", true},
+ {WM_POINTERENTER, "WM_POINTERENTER", true},
+ {WM_POINTERLEAVE, "WM_POINTERLEAVE", true},
+ {WM_POINTERACTIVATE, "WM_POINTERACTIVATE", true},
+ {WM_POINTERCAPTURECHANGED, "WM_POINTERCAPTURECHANGED", true},
+ {WM_TOUCHHITTESTING, "WM_TOUCHHITTESTING", true},
+ {WM_POINTERWHEEL, "WM_POINTERWHEEL", true},
+ {WM_POINTERHWHEEL, "WM_POINTERHWHEEL", true},
+ {DM_POINTERHITTEST, "DM_POINTERHITTEST", true},
+ {WM_POINTERROUTEDTO, "WM_POINTERROUTEDTO", true},
+ {WM_POINTERROUTEDAWAY, "WM_POINTERROUTEDAWAY", true},
+ {WM_POINTERROUTEDRELEASED, "WM_POINTERROUTEDRELEASED", true}
+#endif // WM_POINTERUPDATE
};
static inline const MessageDebugEntry *messageDebugEntry(UINT msg)
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index 6c44541314..ef22a4451a 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -60,6 +60,21 @@
# define WM_DPICHANGED 0x02E0
#endif
+// WM_POINTER support from Windows 8 onwards (WINVER >= 0x0602)
+#ifndef WM_POINTERUPDATE
+# 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_POINTERWHEEL 0x024E
+# define WM_POINTERHWHEEL 0x024F
+#endif // WM_POINTERUPDATE
+
QT_BEGIN_NAMESPACE
namespace QtWindows
@@ -78,6 +93,7 @@ enum
ApplicationEventFlag = 0x1000000,
ThemingEventFlag = 0x2000000,
GenericEventFlag = 0x4000000, // Misc
+ PointerEventFlag = 0x8000000,
};
enum WindowsEventType // Simplify event types
@@ -103,13 +119,16 @@ enum WindowsEventType // Simplify event types
DpiChangedEvent = WindowEventFlag + 21,
EnterSizeMoveEvent = WindowEventFlag + 22,
ExitSizeMoveEvent = WindowEventFlag + 23,
+ PointerActivateWindowEvent = WindowEventFlag + 24,
MouseEvent = MouseEventFlag + 1,
MouseWheelEvent = MouseEventFlag + 2,
CursorEvent = MouseEventFlag + 3,
TouchEvent = TouchEventFlag + 1,
+ PointerEvent = PointerEventFlag + 1,
NonClientMouseEvent = NonClientEventFlag + MouseEventFlag + 1,
NonClientHitTest = NonClientEventFlag + 2,
NonClientCreate = NonClientEventFlag + 3,
+ NonClientPointerEvent = NonClientEventFlag + PointerEventFlag + 4,
KeyEvent = KeyEventFlag + 1,
KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1,
KeyboardLayoutChangeEvent = KeyEventFlag + 2,
@@ -167,6 +186,8 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
QtWindows::ActivateApplicationEvent : QtWindows::DeactivateApplicationEvent;
case WM_MOUSEACTIVATE:
return QtWindows::MouseActivateWindowEvent;
+ case WM_POINTERACTIVATE:
+ return QtWindows::PointerActivateWindowEvent;
case WM_ACTIVATE:
return LOWORD(wParamIn) == WA_INACTIVE ?
QtWindows::DeactivateWindowEvent : QtWindows::ActivateWindowEvent;
@@ -297,6 +318,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST)
|| (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONDBLCLK))
return QtWindows::MouseEvent;
+ if (message >= WM_NCPOINTERUPDATE && message <= WM_NCPOINTERUP)
+ return QtWindows::NonClientPointerEvent;
+ if (message >= WM_POINTERUPDATE && message <= WM_POINTERHWHEEL)
+ return QtWindows::PointerEvent;
return QtWindows::UnknownEvent;
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 980a9fe9ab..8d6a81036b 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -43,6 +43,7 @@
#include "qwindowswindow.h"
#include "qwindowskeymapper.h"
#include "qwindowsmousehandler.h"
+#include "qwindowspointerhandler.h"
#include "qtwindowsglobal.h"
#include "qwindowsmenu.h"
#include "qwindowsmime.h"
@@ -193,6 +194,15 @@ void QWindowsUser32DLL::init()
getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8) {
+ enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer");
+ getPointerType = (GetPointerType)library.resolve("GetPointerType");
+ getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo");
+ getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo");
+ getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo");
+ getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo");
+ }
+
if (QOperatingSystemVersion::current()
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
@@ -201,6 +211,11 @@ void QWindowsUser32DLL::init()
}
}
+bool QWindowsUser32DLL::supportsPointerApi()
+{
+ return enableMouseInPointer && getPointerType && getPointerInfo && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerPenInfo;
+}
+
void QWindowsShcoreDLL::init()
{
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1)
@@ -238,6 +253,7 @@ struct QWindowsContextPrivate {
int m_defaultDPI = 96;
QWindowsKeyMapper m_keyMapper;
QWindowsMouseHandler m_mouseHandler;
+ QWindowsPointerHandler m_pointerHandler;
QWindowsMimeConverter m_mimeConverter;
QWindowsScreenManager m_screenManager;
QSharedPointer<QWindowCreationContext> m_creationContext;
@@ -255,7 +271,7 @@ QWindowsContextPrivate::QWindowsContextPrivate()
QWindowsContext::user32dll.init();
QWindowsContext::shcoredll.init();
- if (m_mouseHandler.touchDevice())
+ if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
m_systemInfo |= QWindowsContext::SI_SupportsTouch;
m_displayContext = GetDC(0);
m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY);
@@ -280,10 +296,6 @@ QWindowsContext::QWindowsContext() :
const QByteArray bv = qgetenv("QT_QPA_VERBOSE");
if (!bv.isEmpty())
QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv));
-#if QT_CONFIG(tabletevent)
- d->m_tabletSupport.reset(QWindowsTabletSupport::create());
- qCDebug(lcQpaTablet) << "Tablet support: " << (d->m_tabletSupport.isNull() ? QStringLiteral("None") : d->m_tabletSupport->description());
-#endif
}
QWindowsContext::~QWindowsContext()
@@ -309,7 +321,8 @@ bool QWindowsContext::initTouch(unsigned integrationOptions)
if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
return true;
- QTouchDevice *touchDevice = d->m_mouseHandler.ensureTouchDevice();
+ QTouchDevice *touchDevice = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
+ d->m_pointerHandler.ensureTouchDevice() : d->m_mouseHandler.ensureTouchDevice();
if (!touchDevice)
return false;
@@ -329,6 +342,33 @@ bool QWindowsContext::initTouch(unsigned integrationOptions)
return true;
}
+bool QWindowsContext::initTablet(unsigned integrationOptions)
+{
+ Q_UNUSED(integrationOptions);
+#if QT_CONFIG(tabletevent)
+ d->m_tabletSupport.reset(QWindowsTabletSupport::create());
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool QWindowsContext::initPointer(unsigned integrationOptions)
+{
+ if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
+ return false;
+
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
+ return false;
+
+ if (!QWindowsContext::user32dll.supportsPointerApi())
+ return false;
+
+ QWindowsContext::user32dll.enableMouseInPointer(TRUE);
+ d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
+ return true;
+}
+
void QWindowsContext::setTabletAbsoluteRange(int a)
{
#if QT_CONFIG(tabletevent)
@@ -631,12 +671,16 @@ QWindow *QWindowsContext::findWindow(HWND hwnd) const
QWindow *QWindowsContext::windowUnderMouse() const
{
- return d->m_mouseHandler.windowUnderMouse();
+ return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
+ d->m_pointerHandler.windowUnderMouse() : d->m_mouseHandler.windowUnderMouse();
}
void QWindowsContext::clearWindowUnderMouse()
{
- d->m_mouseHandler.clearWindowUnderMouse();
+ if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
+ d->m_pointerHandler.clearWindowUnderMouse();
+ else
+ d->m_mouseHandler.clearWindowUnderMouse();
}
/*!
@@ -957,7 +1001,9 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
switch (et) {
case QtWindows::GestureEvent:
- return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
+ if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
+ break;
case QtWindows::InputMethodOpenCandidateWindowEvent:
case QtWindows::InputMethodCloseCandidateWindowEvent:
// TODO: Release/regrab mouse if a popup has mouse grab.
@@ -1083,19 +1129,25 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::ExposeEvent:
return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
case QtWindows::NonClientMouseEvent:
- if (platformWindow->frameStrutEventsEnabled())
+ if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
break;
+ case QtWindows::NonClientPointerEvent:
+ if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer) && platformWindow->frameStrutEventsEnabled())
+ return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
+ break;
case QtWindows::EnterSizeMoveEvent:
platformWindow->setFlag(QWindowsWindow::ResizeMoveActive);
return true;
case QtWindows::ExitSizeMoveEvent:
platformWindow->clearFlag(QWindowsWindow::ResizeMoveActive);
platformWindow->checkForScreenChanged();
- QWindowsMouseHandler::handleExitSizeMove(platformWindow->window());
+ handleExitSizeMove(platformWindow->window());
return true;
case QtWindows::ScrollEvent:
- return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
+ if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
+ break;
case QtWindows::MouseWheelEvent:
case QtWindows::MouseEvent:
case QtWindows::LeaveEvent:
@@ -1105,10 +1157,20 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
window = window->parent();
if (!window)
return false;
- return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
+ if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
+ else
+ return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
}
+ break;
case QtWindows::TouchEvent:
- return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
+ if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
+ break;
+ case QtWindows::PointerEvent:
+ if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
+ return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
+ break;
case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
case QtWindows::FocusOutEvent:
handleFocusEvent(et, platformWindow);
@@ -1151,6 +1213,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
}
break;
case QtWindows::MouseActivateWindowEvent:
+ case QtWindows::PointerActivateWindowEvent:
if (platformWindow->window()->flags() & Qt::WindowDoesNotAcceptFocus) {
*result = LRESULT(MA_NOACTIVATE);
return true;
@@ -1295,6 +1358,37 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg)
}
#endif
+void QWindowsContext::handleExitSizeMove(QWindow *window)
+{
+ // Windows can be moved/resized by:
+ // 1) User moving a window by dragging the title bar: Causes a sequence
+ // of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP,
+ // leaving the left mouse button 'pressed'
+ // 2) User choosing Resize/Move from System menu and using mouse/cursor keys:
+ // No mouse events are received
+ // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move():
+ // Mouse is left in pressed state after press on size grip (inside window),
+ // no further mouse events are received
+ // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons.
+ const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons();
+ const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
+ if (currentButtons == appButtons)
+ return;
+ const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const QPoint globalPos = QWindowsCursor::mousePosition();
+ const QPlatformWindow *platWin = window->handle();
+ const QPoint localPos = platWin->mapFromGlobal(globalPos);
+ const QEvent::Type type = platWin->geometry().contains(globalPos)
+ ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
+ for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
+ if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
+ currentButtons, button, type,
+ keyboardModifiers);
+ }
+ }
+}
+
bool QWindowsContext::asyncExpose() const
{
return d->m_asyncExpose;
@@ -1307,7 +1401,8 @@ void QWindowsContext::setAsyncExpose(bool value)
QTouchDevice *QWindowsContext::touchDevice() const
{
- return d->m_mouseHandler.touchDevice();
+ return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
+ d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice();
}
static DWORD readDwordRegistrySetting(const wchar_t *regKey, const wchar_t *subKey, DWORD defaultValue)
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index cb4d049081..2c2313c30a 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -85,7 +85,14 @@ class QTouchDevice;
struct QWindowsUser32DLL
{
inline void init();
-
+ inline bool supportsPointerApi();
+
+ typedef BOOL (WINAPI *EnableMouseInPointer)(BOOL);
+ typedef BOOL (WINAPI *GetPointerType)(UINT32, PVOID);
+ typedef BOOL (WINAPI *GetPointerInfo)(UINT32, PVOID);
+ typedef BOOL (WINAPI *GetPointerTouchInfo)(UINT32, PVOID);
+ typedef BOOL (WINAPI *GetPointerFrameTouchInfo)(UINT32, UINT32 *, PVOID);
+ typedef BOOL (WINAPI *GetPointerPenInfo)(UINT32, PVOID);
typedef BOOL (WINAPI *SetProcessDPIAware)();
typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND);
typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
@@ -95,6 +102,14 @@ struct QWindowsUser32DLL
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
+ // Windows pointer functions (Windows 8 or later).
+ EnableMouseInPointer enableMouseInPointer = nullptr;
+ GetPointerType getPointerType = nullptr;
+ GetPointerInfo getPointerInfo = nullptr;
+ GetPointerTouchInfo getPointerTouchInfo = nullptr;
+ GetPointerFrameTouchInfo getPointerFrameTouchInfo = nullptr;
+ GetPointerPenInfo getPointerPenInfo = nullptr;
+
// Windows Vista onwards
SetProcessDPIAware setProcessDPIAware = nullptr;
@@ -134,7 +149,8 @@ public:
enum SystemInfoFlags
{
SI_RTL_Extensions = 0x1,
- SI_SupportsTouch = 0x2
+ SI_SupportsTouch = 0x2,
+ SI_SupportsPointer = 0x4,
};
// Verbose flag set by environment variable QT_QPA_VERBOSE
@@ -145,6 +161,8 @@ public:
bool initTouch();
bool initTouch(unsigned integrationOptions); // For calls from QWindowsIntegration::QWindowsIntegration() only.
+ bool initTablet(unsigned integrationOptions);
+ bool initPointer(unsigned integrationOptions);
int defaultDPI() const;
@@ -220,6 +238,7 @@ private:
#ifndef QT_NO_CONTEXTMENU
bool handleContextMenuEvent(QWindow *window, const MSG &msg);
#endif
+ void handleExitSizeMove(QWindow *window);
void unregisterWindowClasses();
QScopedPointer<QWindowsContextPrivate> d;
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 9131b323df..7194fa1d1f 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -212,6 +212,8 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::AlwaysUseNativeMenus;
} else if (param == QLatin1String("menus=none")) {
options |= QWindowsIntegration::NoNativeMenus;
+ } else if (param == QLatin1String("nowmpointer")) {
+ options |= QWindowsIntegration::DontUseWMPointer;
} else {
qWarning() << "Unknown option" << param;
}
@@ -230,8 +232,13 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList &paramL
QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorDpiAware;
m_options = parseOptions(paramList, &tabletAbsoluteRange, &dpiAwareness);
QWindowsFontDatabase::setFontOptions(m_options);
- if (tabletAbsoluteRange >= 0)
- m_context.setTabletAbsoluteRange(tabletAbsoluteRange);
+
+ if (!m_context.initPointer(m_options)) {
+ m_context.initTablet(m_options);
+ if (tabletAbsoluteRange >= 0)
+ m_context.setTabletAbsoluteRange(tabletAbsoluteRange);
+ }
+
if (!dpiAwarenessSet) { // Set only once in case of repeated instantiations of QGuiApplication.
if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
m_context.setProcessDpiAwareness(dpiAwareness);
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index 7ae941f7be..ef074e309e 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -66,7 +66,8 @@ public:
DontUseDirectWriteFonts = QWindowsFontDatabase::DontUseDirectWriteFonts,
DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts,
AlwaysUseNativeMenus = 0x100,
- NoNativeMenus = 0x200
+ NoNativeMenus = 0x200,
+ DontUseWMPointer = 0x400,
};
explicit QWindowsIntegration(const QStringList &paramList);
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index add01f65cc..92aaf70e40 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -119,20 +119,16 @@ static inline void compressMouseMove(MSG *msg)
static inline QTouchDevice *createTouchDevice()
{
- enum { QT_SM_TABLETPC = 86, QT_SM_DIGITIZER = 94, QT_SM_MAXIMUMTOUCHES = 95,
- QT_NID_INTEGRATED_TOUCH = 0x1, QT_NID_EXTERNAL_TOUCH = 0x02,
- QT_NID_MULTI_INPUT = 0x40, QT_NID_READY = 0x80 };
-
- const int digitizers = GetSystemMetrics(QT_SM_DIGITIZER);
- if (!(digitizers & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH)))
+ const int digitizers = GetSystemMetrics(SM_DIGITIZER);
+ if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH)))
return 0;
- const int tabletPc = GetSystemMetrics(QT_SM_TABLETPC);
- const int maxTouchPoints = GetSystemMetrics(QT_SM_MAXIMUMTOUCHES);
- qCDebug(lcQpaEvents) << "Digitizers:" << hex << showbase << (digitizers & ~QT_NID_READY)
- << "Ready:" << (digitizers & QT_NID_READY) << dec << noshowbase
+ const int tabletPc = GetSystemMetrics(SM_TABLETPC);
+ const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES);
+ qCDebug(lcQpaEvents) << "Digitizers:" << hex << showbase << (digitizers & ~NID_READY)
+ << "Ready:" << (digitizers & NID_READY) << dec << noshowbase
<< "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints;
QTouchDevice *result = new QTouchDevice;
- result->setType(digitizers & QT_NID_INTEGRATED_TOUCH
+ result->setType(digitizers & NID_INTEGRATED_TOUCH
? QTouchDevice::TouchScreen : QTouchDevice::TouchPad);
QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition;
if (result->type() == QTouchDevice::TouchPad)
@@ -178,37 +174,6 @@ Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()
return result;
}
-void QWindowsMouseHandler::handleExitSizeMove(QWindow *window)
-{
- // Windows can be moved/resized by:
- // 1) User moving a window by dragging the title bar: Causes a sequence
- // of WM_NCLBUTTONDOWN, WM_NCMOUSEMOVE but no WM_NCLBUTTONUP,
- // leaving the left mouse button 'pressed'
- // 2) User choosing Resize/Move from System menu and using mouse/cursor keys:
- // No mouse events are received
- // 3) Programmatically via QSizeGrip calling QPlatformWindow::startSystemResize/Move():
- // Mouse is left in pressed state after press on size grip (inside window),
- // no further mouse events are received
- // For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons.
- const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons();
- const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
- if (currentButtons == appButtons)
- return;
- const Qt::KeyboardModifiers keyboardModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
- const QPoint globalPos = QWindowsCursor::mousePosition();
- const QPlatformWindow *platWin = window->handle();
- const QPoint localPos = platWin->mapFromGlobal(globalPos);
- const QEvent::Type type = platWin->geometry().contains(globalPos)
- ? QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
- for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) {
- if (appButtons.testFlag(button) && !currentButtons.testFlag(button)) {
- QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
- currentButtons, button, type,
- keyboardModifiers);
- }
- }
-}
-
static QPoint lastMouseMovePos;
namespace {
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h
index 04d3a8bff6..480662c9bf 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.h
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.h
@@ -80,8 +80,6 @@ public:
QWindow *windowUnderMouse() const { return m_windowUnderMouse.data(); }
void clearWindowUnderMouse() { m_windowUnderMouse = 0; }
- static void handleExitSizeMove(QWindow *window);
-
private:
inline bool translateMouseWheelEvent(QWindow *window, HWND hwnd,
MSG msg, LRESULT *result);
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
new file mode 100644
index 0000000000..f25e6d13d8
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -0,0 +1,573 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(WINVER) && WINVER < 0x0603
+# undef WINVER
+#endif
+#if !defined(WINVER)
+# define WINVER 0x0603 // Enable pointer functions for MinGW
+#endif
+
+#include "qwindowspointerhandler.h"
+#include "qwindowskeymapper.h"
+#include "qwindowscontext.h"
+#include "qwindowswindow.h"
+#include "qwindowsintegration.h"
+#include "qwindowsscreen.h"
+
+#include <qpa/qwindowsysteminterface.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qtouchdevice.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qoperatingsystemversion.h>
+
+#include <windowsx.h>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ QT_PT_POINTER = 1,
+ QT_PT_TOUCH = 2,
+ QT_PT_PEN = 3,
+ QT_PT_MOUSE = 4,
+ QT_PT_TOUCHPAD = 5, // MinGW is missing PT_TOUCHPAD
+};
+
+bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
+{
+ *result = 0;
+ const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
+
+ POINTER_INPUT_TYPE pointerType;
+ if (!QWindowsContext::user32dll.getPointerType(pointerId, &pointerType)) {
+ qWarning() << "GetPointerType() failed:" << qt_error_string();
+ return false;
+ }
+
+ switch (pointerType) {
+ case QT_PT_POINTER:
+ case QT_PT_MOUSE:
+ case QT_PT_TOUCHPAD: {
+ POINTER_INFO pointerInfo;
+ if (!QWindowsContext::user32dll.getPointerInfo(pointerId, &pointerInfo)) {
+ qWarning() << "GetPointerInfo() failed:" << qt_error_string();
+ return false;
+ }
+ return translateMouseTouchPadEvent(window, hwnd, et, msg, &pointerInfo);
+ }
+ case QT_PT_TOUCH: {
+ quint32 pointerCount = 0;
+ if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
+ qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
+ return false;
+ }
+ QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
+ if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
+ qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
+ return false;
+ }
+ return translateTouchEvent(window, hwnd, et, msg, touchInfo.data(), pointerCount);
+ }
+ case QT_PT_PEN: {
+ POINTER_PEN_INFO penInfo;
+ if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) {
+ qWarning() << "GetPointerPenInfo() failed:" << qt_error_string();
+ return false;
+ }
+ return translatePenEvent(window, hwnd, et, msg, &penInfo);
+ }
+ }
+ return false;
+}
+
+static void getMouseEventInfo(UINT message, POINTER_BUTTON_CHANGE_TYPE changeType, QPoint globalPos, QEvent::Type *eventType, Qt::MouseButton *mouseButton)
+{
+ static const QHash<POINTER_BUTTON_CHANGE_TYPE, Qt::MouseButton> buttonMapping {
+ {POINTER_CHANGE_FIRSTBUTTON_DOWN, Qt::LeftButton},
+ {POINTER_CHANGE_FIRSTBUTTON_UP, Qt::LeftButton},
+ {POINTER_CHANGE_SECONDBUTTON_DOWN, Qt::RightButton},
+ {POINTER_CHANGE_SECONDBUTTON_UP, Qt::RightButton},
+ {POINTER_CHANGE_THIRDBUTTON_DOWN, Qt::MiddleButton},
+ {POINTER_CHANGE_THIRDBUTTON_UP, Qt::MiddleButton},
+ {POINTER_CHANGE_FOURTHBUTTON_DOWN, Qt::XButton1},
+ {POINTER_CHANGE_FOURTHBUTTON_UP, Qt::XButton1},
+ {POINTER_CHANGE_FIFTHBUTTON_DOWN, Qt::XButton2},
+ {POINTER_CHANGE_FIFTHBUTTON_UP, Qt::XButton2},
+ };
+
+ static const QHash<UINT, QEvent::Type> eventMapping {
+ {WM_POINTERUPDATE, QEvent::MouseMove},
+ {WM_POINTERDOWN, QEvent::MouseButtonPress},
+ {WM_POINTERUP, QEvent::MouseButtonRelease},
+ {WM_NCPOINTERUPDATE, QEvent::NonClientAreaMouseMove},
+ {WM_NCPOINTERDOWN, QEvent::NonClientAreaMouseButtonPress},
+ {WM_NCPOINTERUP, QEvent::NonClientAreaMouseButtonRelease},
+ {WM_POINTERWHEEL, QEvent::Wheel},
+ {WM_POINTERHWHEEL, QEvent::Wheel},
+ };
+
+ if (!eventType || !mouseButton)
+ return;
+
+ if (message == WM_POINTERDOWN || message == WM_POINTERUP || message == WM_NCPOINTERDOWN || message == WM_NCPOINTERUP)
+ *mouseButton = buttonMapping.value(changeType, Qt::NoButton);
+ else
+ *mouseButton = Qt::NoButton;
+
+ *eventType = eventMapping.value(message, QEvent::None);
+
+ // Pointer messages lack a double click indicator. Check if this is the case here.
+ if (message == WM_POINTERDOWN) {
+ static LONG lastTime = 0;
+ static Qt::MouseButton lastButton = Qt::NoButton;
+ static QPoint lastPos;
+ LONG messageTime = GetMessageTime();
+ if (*mouseButton == lastButton
+ && messageTime - lastTime < (LONG)GetDoubleClickTime()
+ && qAbs(globalPos.x() - lastPos.x()) < GetSystemMetrics(SM_CXDOUBLECLK)
+ && qAbs(globalPos.y() - lastPos.y()) < GetSystemMetrics(SM_CYDOUBLECLK)) {
+ *eventType = QEvent::MouseButtonDblClick;
+ }
+ lastTime = messageTime;
+ lastButton = *mouseButton;
+ lastPos = globalPos;
+ }
+}
+
+static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos)
+{
+ QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
+
+ while (currentWindowUnderPointer && currentWindowUnderPointer->flags() & Qt::WindowTransparentForInput)
+ currentWindowUnderPointer = currentWindowUnderPointer->parent();
+
+ // QTBUG-44332: When Qt is running at low integrity level and
+ // a Qt Window is parented on a Window of a higher integrity process
+ // using QWindow::fromWinId() (for example, Qt running in a browser plugin)
+ // ChildWindowFromPointEx() may not find the Qt window (failing with ERROR_ACCESS_DENIED)
+ if (!currentWindowUnderPointer) {
+ const QRect clientRect(QPoint(0, 0), window->size());
+ if (clientRect.contains(globalPos))
+ currentWindowUnderPointer = window;
+ }
+ return currentWindowUnderPointer;
+}
+
+static bool trackLeave(HWND hwnd)
+{
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ return TrackMouseEvent(&tme);
+}
+
+static bool isValidWheelReceiver(QWindow *candidate)
+{
+ if (candidate) {
+ const QWindow *toplevel = QWindowsWindow::topLevelOf(candidate);
+ if (toplevel->handle() && toplevel->handle()->isForeignWindow())
+ return true;
+ if (const QWindowsWindow *ww = QWindowsWindow::windowsWindowOf(toplevel))
+ return !ww->testFlag(QWindowsWindow::BlockedByModal);
+ }
+ return false;
+}
+
+static QTouchDevice *createTouchDevice()
+{
+ const int digitizers = GetSystemMetrics(SM_DIGITIZER);
+ if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH)))
+ return nullptr;
+ const int tabletPc = GetSystemMetrics(SM_TABLETPC);
+ const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES);
+ qCDebug(lcQpaEvents) << "Digitizers:" << hex << showbase << (digitizers & ~NID_READY)
+ << "Ready:" << (digitizers & NID_READY) << dec << noshowbase
+ << "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints;
+ QTouchDevice *result = new QTouchDevice;
+ result->setType(digitizers & NID_INTEGRATED_TOUCH
+ ? QTouchDevice::TouchScreen : QTouchDevice::TouchPad);
+ QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition;
+ if (result->type() == QTouchDevice::TouchPad)
+ capabilities |= QTouchDevice::MouseEmulation;
+ result->setCapabilities(capabilities);
+ result->setMaximumTouchPoints(maxTouchPoints);
+ return result;
+}
+
+QTouchDevice *QWindowsPointerHandler::ensureTouchDevice()
+{
+ if (!m_touchDevice)
+ m_touchDevice.reset(createTouchDevice());
+ return m_touchDevice.data();
+}
+
+Qt::MouseButtons QWindowsPointerHandler::queryMouseButtons()
+{
+ Qt::MouseButtons result = 0;
+ const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON);
+ if (GetAsyncKeyState(VK_LBUTTON) < 0)
+ result |= mouseSwapped ? Qt::RightButton: Qt::LeftButton;
+ if (GetAsyncKeyState(VK_RBUTTON) < 0)
+ result |= mouseSwapped ? Qt::LeftButton : Qt::RightButton;
+ if (GetAsyncKeyState(VK_MBUTTON) < 0)
+ result |= Qt::MidButton;
+ if (GetAsyncKeyState(VK_XBUTTON1) < 0)
+ result |= Qt::XButton1;
+ if (GetAsyncKeyState(VK_XBUTTON2) < 0)
+ result |= Qt::XButton2;
+ return result;
+}
+
+bool QWindowsPointerHandler::translateMouseTouchPadEvent(QWindow *window, HWND hwnd,
+ QtWindows::WindowsEventType et,
+ MSG msg, PVOID vPointerInfo)
+{
+ POINTER_INFO *pointerInfo = static_cast<POINTER_INFO *>(vPointerInfo);
+ const QPoint globalPos = QPoint(pointerInfo->ptPixelLocation.x, pointerInfo->ptPixelLocation.y);
+ const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos);
+ const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const Qt::MouseButtons mouseButtons = queryMouseButtons();
+
+ QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+
+ switch (msg.message) {
+ case WM_NCPOINTERDOWN:
+ case WM_NCPOINTERUP:
+ case WM_NCPOINTERUPDATE:
+ case WM_POINTERDOWN:
+ case WM_POINTERUP:
+ case WM_POINTERUPDATE: {
+
+ QEvent::Type eventType;
+ Qt::MouseButton button;
+ getMouseEventInfo(msg.message, pointerInfo->ButtonChangeType, globalPos, &eventType, &button);
+
+ if (et & QtWindows::NonClientEventFlag) {
+ QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons, button, eventType,
+ keyModifiers, Qt::MouseEventNotSynthesized);
+ return false; // To allow window dragging, etc.
+ } else {
+ if (currentWindowUnderPointer != m_windowUnderPointer) {
+ if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
+ QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
+ m_currentWindow = nullptr;
+ }
+
+ if (currentWindowUnderPointer) {
+ if (currentWindowUnderPointer != m_currentWindow) {
+ QWindowSystemInterface::handleEnterEvent(currentWindowUnderPointer, localPos, globalPos);
+ m_currentWindow = currentWindowUnderPointer;
+ if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(currentWindowUnderPointer))
+ wumPlatformWindow->applyCursor();
+ trackLeave(hwnd);
+ }
+ } else {
+ platformWindow->applyCursor();
+ }
+ m_windowUnderPointer = currentWindowUnderPointer;
+ }
+
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, button, eventType,
+ keyModifiers, Qt::MouseEventNotSynthesized);
+
+ // The initial down click over the QSizeGrip area, which posts a resize WM_SYSCOMMAND
+ // has go to through DefWindowProc() for resizing to work, so we return false here,
+ // unless the mouse is captured, as it would mess with menu processing.
+ return msg.message != WM_POINTERDOWN || GetCapture();
+ }
+ }
+ case WM_POINTERHWHEEL:
+ case WM_POINTERWHEEL: {
+
+ int delta = GET_WHEEL_DELTA_WPARAM(msg.wParam);
+
+ // Qt horizontal wheel rotation orientation is opposite to the one in WM_POINTERHWHEEL
+ if (msg.message == WM_POINTERHWHEEL)
+ delta = -delta;
+
+ const QPoint angleDelta = (msg.message == WM_POINTERHWHEEL || (keyModifiers & Qt::AltModifier)) ?
+ QPoint(delta, 0) : QPoint(0, delta);
+
+ if (isValidWheelReceiver(window))
+ QWindowSystemInterface::handleWheelEvent(window, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
+ return true;
+ }
+ case WM_POINTERLEAVE:
+ return true;
+ }
+ return false;
+}
+
+bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
+ QtWindows::WindowsEventType et,
+ MSG msg, PVOID vTouchInfo, quint32 count)
+{
+ Q_UNUSED(hwnd);
+ Q_UNUSED(et);
+
+ if (et & QtWindows::NonClientEventFlag)
+ return false; // Let DefWindowProc() handle Non Client messages.
+
+ if (count < 1)
+ return false;
+
+ const QScreen *screen = window->screen();
+ if (!screen)
+ screen = QGuiApplication::primaryScreen();
+ if (!screen)
+ return false;
+
+ POINTER_TOUCH_INFO *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
+
+ const QRect screenGeometry = screen->geometry();
+
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+
+ for (quint32 i = 0; i < count; ++i) {
+
+ QWindowSystemInterface::TouchPoint touchPoint;
+ touchPoint.id = touchInfo[i].pointerInfo.pointerId;
+ touchPoint.pressure = (touchInfo[i].touchMask & TOUCH_MASK_PRESSURE) ?
+ touchInfo[i].pressure / 1024.0 : 1.0;
+ if (m_lastTouchPositions.contains(touchPoint.id))
+ touchPoint.normalPosition = m_lastTouchPositions.value(touchPoint.id);
+
+ const QPointF screenPos = QPointF(touchInfo[i].pointerInfo.ptPixelLocation.x,
+ touchInfo[i].pointerInfo.ptPixelLocation.y);
+
+ if (touchInfo[i].touchMask & TOUCH_MASK_CONTACTAREA)
+ touchPoint.area.setSize(QSizeF(touchInfo[i].rcContact.right - touchInfo[i].rcContact.left,
+ touchInfo[i].rcContact.bottom - touchInfo[i].rcContact.top));
+ touchPoint.area.moveCenter(screenPos);
+ QPointF normalPosition = QPointF(screenPos.x() / screenGeometry.width(),
+ screenPos.y() / screenGeometry.height());
+ const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
+ touchPoint.normalPosition = normalPosition;
+
+ if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
+ touchPoint.state = Qt::TouchPointPressed;
+ m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ } else if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
+ touchPoint.state = Qt::TouchPointReleased;
+ m_lastTouchPositions.remove(touchPoint.id);
+ } else {
+ touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved;
+ m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ }
+ touchPoints.append(touchPoint);
+ }
+
+ QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints,
+ QWindowsKeyMapper::queryKeyboardModifiers());
+
+ if (!(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch)) {
+
+ const QPoint globalPos = QPoint(touchInfo->pointerInfo.ptPixelLocation.x, touchInfo->pointerInfo.ptPixelLocation.y);
+ const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos);
+ const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const Qt::MouseButtons mouseButtons = queryMouseButtons();
+
+ QEvent::Type eventType;
+ Qt::MouseButton button;
+ getMouseEventInfo(msg.message, touchInfo->pointerInfo.ButtonChangeType, globalPos, &eventType, &button);
+
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, button, eventType,
+ keyModifiers, Qt::MouseEventSynthesizedByQt);
+ }
+
+ return true;
+}
+
+bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et,
+ MSG msg, PVOID vPenInfo)
+{
+ if (et & QtWindows::NonClientEventFlag)
+ return false; // Let DefWindowProc() handle Non Client messages.
+
+ POINTER_PEN_INFO *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
+ const quint32 pointerId = penInfo->pointerInfo.pointerId;
+ const QPoint globalPos = QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y);
+ const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos);
+ const qreal pressure = (penInfo->penMask & PEN_MASK_PRESSURE) ? qreal(penInfo->pressure) / 1024.0 : 0.5;
+ const qreal rotation = (penInfo->penMask & PEN_MASK_ROTATION) ? qreal(penInfo->rotation) : 0.0;
+ const qreal tangentialPressure = 0.0;
+ const int xTilt = (penInfo->penMask & PEN_MASK_TILT_X) ? penInfo->tiltX : 0;
+ const int yTilt = (penInfo->penMask & PEN_MASK_TILT_Y) ? penInfo->tiltY : 0;
+ const int z = 0;
+
+ const QTabletEvent::TabletDevice device = QTabletEvent::Stylus;
+ QTabletEvent::PointerType type;
+ Qt::MouseButtons mouseButtons;
+
+ const bool pointerInContact = IS_POINTER_INCONTACT_WPARAM(msg.wParam);
+ if (pointerInContact)
+ mouseButtons = Qt::LeftButton;
+
+ if (penInfo->penFlags & (PEN_FLAG_ERASER | PEN_FLAG_INVERTED)) {
+ type = QTabletEvent::Eraser;
+ } else {
+ type = QTabletEvent::Pen;
+ if (pointerInContact && penInfo->penFlags & PEN_FLAG_BARREL)
+ mouseButtons = Qt::RightButton; // Either left or right, not both
+ }
+
+ switch (msg.message) {
+ case WM_POINTERENTER: {
+ QWindowSystemInterface::handleTabletEnterProximityEvent(device, type, pointerId);
+ m_windowUnderPointer = window;
+ // The local coordinates may fall outside the window.
+ // Wait until the next update to send the enter event.
+ m_needsEnterOnPointerUpdate = true;
+ break;
+ }
+ case WM_POINTERLEAVE:
+ if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
+ QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
+ m_windowUnderPointer = nullptr;
+ m_currentWindow = nullptr;
+ }
+ QWindowSystemInterface::handleTabletLeaveProximityEvent(device, type, pointerId);
+ break;
+ case WM_POINTERDOWN:
+ case WM_POINTERUP:
+ case WM_POINTERUPDATE: {
+ QWindow *target = QGuiApplicationPrivate::tabletDevicePoint(pointerId).target; // Pass to window that grabbed it.
+ if (!target && m_windowUnderPointer)
+ target = m_windowUnderPointer;
+ if (!target)
+ target = window;
+
+ if (m_needsEnterOnPointerUpdate) {
+ m_needsEnterOnPointerUpdate = false;
+ if (window != m_currentWindow) {
+ QWindowSystemInterface::handleEnterEvent(window, localPos, globalPos);
+ m_currentWindow = window;
+ if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(target))
+ wumPlatformWindow->applyCursor();
+ }
+ }
+ const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+
+ QWindowSystemInterface::handleTabletEvent(target, localPos, globalPos, device, type, mouseButtons,
+ pressure, xTilt, yTilt, tangentialPressure, rotation, z,
+ pointerId, keyModifiers);
+
+ QEvent::Type eventType;
+ Qt::MouseButton button;
+ getMouseEventInfo(msg.message, penInfo->pointerInfo.ButtonChangeType, globalPos, &eventType, &button);
+
+ QWindowSystemInterface::handleMouseEvent(target, localPos, globalPos, mouseButtons, button, eventType,
+ keyModifiers, Qt::MouseEventSynthesizedByQt);
+ break;
+ }
+ }
+ return true;
+}
+
+// SetCursorPos()/TrackMouseEvent() will generate old-style WM_MOUSE messages. Handle them here.
+bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
+{
+ Q_UNUSED(et);
+
+ *result = 0;
+ if (msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
+ return false;
+
+ const QPoint localPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
+ const QPoint globalPos = QWindowsGeometryHint::mapToGlobal(hwnd, localPos);
+
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+
+ if (msg.message == WM_MOUSELEAVE) {
+ if (window == m_currentWindow) {
+ QWindowSystemInterface::handleLeaveEvent(window);
+ m_windowUnderPointer = nullptr;
+ m_currentWindow = nullptr;
+ platformWindow->applyCursor();
+ }
+ return false;
+ }
+
+ // Windows sends a mouse move with no buttons pressed to signal "Enter"
+ // when a window is shown over the cursor. Discard the event and only use
+ // it for generating QEvent::Enter to be consistent with other platforms -
+ // X11 and macOS.
+ static QPoint lastMouseMovePos;
+ const bool discardEvent = msg.wParam == 0 && (m_windowUnderPointer.isNull() || globalPos == lastMouseMovePos);
+ lastMouseMovePos = globalPos;
+
+ QWindow *currentWindowUnderPointer = getWindowUnderPointer(window, globalPos);
+
+ if (currentWindowUnderPointer != m_windowUnderPointer) {
+ if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
+ QWindowSystemInterface::handleLeaveEvent(m_windowUnderPointer);
+ m_currentWindow = nullptr;
+ }
+
+ if (currentWindowUnderPointer) {
+ if (currentWindowUnderPointer != m_currentWindow) {
+ QWindowSystemInterface::handleEnterEvent(currentWindowUnderPointer, localPos, globalPos);
+ m_currentWindow = currentWindowUnderPointer;
+ if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(currentWindowUnderPointer))
+ wumPlatformWindow->applyCursor();
+ trackLeave(hwnd);
+ }
+ } else {
+ platformWindow->applyCursor();
+ }
+ m_windowUnderPointer = currentWindowUnderPointer;
+ }
+
+ const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const Qt::MouseButtons mouseButtons = queryMouseButtons();
+
+ if (!discardEvent)
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, Qt::NoButton, QEvent::MouseMove,
+ keyModifiers, Qt::MouseEventNotSynthesized);
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h
new file mode 100644
index 0000000000..11bc9419d7
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSPOINTERHANDLER_H
+#define QWINDOWSPOINTERHANDLER_H
+
+#include "qtwindowsglobal.h"
+#include <QtCore/qt_windows.h>
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qhash.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+class QTouchDevice;
+
+class QWindowsPointerHandler
+{
+ Q_DISABLE_COPY(QWindowsPointerHandler)
+public:
+ QWindowsPointerHandler() = default;
+ bool translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result);
+ bool translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result);
+ QTouchDevice *touchDevice() const { return m_touchDevice.data(); }
+ QTouchDevice *ensureTouchDevice();
+ Qt::MouseButtons queryMouseButtons();
+ QWindow *windowUnderMouse() const { return m_windowUnderPointer.data(); }
+ void clearWindowUnderMouse() { m_windowUnderPointer = nullptr; }
+
+private:
+ bool translateMouseTouchPadEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vPointerInfo);
+ bool translateTouchEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vTouchInfo, unsigned int count);
+ bool translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vPenInfo);
+
+ QScopedPointer<QTouchDevice> m_touchDevice;
+ QHash<int, QPointF> m_lastTouchPositions;
+ QPointer<QWindow> m_windowUnderPointer;
+ QPointer<QWindow> m_currentWindow;
+ bool m_needsEnterOnPointerUpdate = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSPOINTERHANDLER_H
diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri
index e2129b7c9d..c1d4e907d9 100644
--- a/src/plugins/platforms/windows/windows.pri
+++ b/src/plugins/platforms/windows/windows.pri
@@ -18,6 +18,7 @@ SOURCES += \
$$PWD/qwindowsscreen.cpp \
$$PWD/qwindowskeymapper.cpp \
$$PWD/qwindowsmousehandler.cpp \
+ $$PWD/qwindowspointerhandler.cpp \
$$PWD/qwindowsole.cpp \
$$PWD/qwindowsdropdataobject.cpp \
$$PWD/qwindowsmime.cpp \
@@ -40,6 +41,7 @@ HEADERS += \
$$PWD/qwindowsscreen.h \
$$PWD/qwindowskeymapper.h \
$$PWD/qwindowsmousehandler.h \
+ $$PWD/qwindowspointerhandler.h \
$$PWD/qtwindowsglobal.h \
$$PWD/qwindowsole.h \
$$PWD/qwindowsdropdataobject.h \