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>
This commit is contained in:
Andre de la Rocha 2018-05-25 14:48:03 +02:00
parent dffbf4a7f6
commit 20d6dac63c
11 changed files with 853 additions and 64 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -66,7 +66,8 @@ public:
DontUseDirectWriteFonts = QWindowsFontDatabase::DontUseDirectWriteFonts,
DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts,
AlwaysUseNativeMenus = 0x100,
NoNativeMenus = 0x200
NoNativeMenus = 0x200,
DontUseWMPointer = 0x400,
};
explicit QWindowsIntegration(const QStringList &paramList);

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 \