4#include <QtCore/qt_windows.h>
7#if QT_CONFIG(tabletevent)
16#include <QtGui/qguiapplication.h>
17#include <QtGui/qscreen.h>
18#include <QtGui/qpointingdevice.h>
19#include <QtGui/qwindow.h>
20#include <QtGui/private/qguiapplication_p.h>
21#include <QtCore/qvarlengtharray.h>
22#include <QtCore/qloggingcategory.h>
23#include <QtCore/qqueue.h>
39qint64 QWindowsPointerHandler::m_nextInputDeviceId = 1;
43 static QPointer<const QPointingDevice>
result;
56 const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
58 if (!GetPointerType(pointerId, &m_pointerType)) {
63 switch (m_pointerType) {
72 if (!GetPointerFrameTouchInfo(pointerId, &pointerCount,
nullptr)) {
76 QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
77 if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
86 quint32 historyCount = touchInfo[0].pointerInfo.historyCount;
89 touchInfo.resize(pointerCount * historyCount);
90 if (!GetPointerFrameTouchInfoHistory(pointerId,
100 for (
auto it = touchInfo.rbegin(),
end = touchInfo.rend();
it !=
end;
it += pointerCount) {
102 &(*(
it + (pointerCount - 1))), pointerCount);
107 return translateTouchEvent(
window, hwnd, et, msg, touchInfo.data(), pointerCount);
110 POINTER_PEN_INFO penInfo;
111 if (!GetPointerPenInfo(pointerId, &penInfo)) {
116 quint32 historyCount = penInfo.pointerInfo.historyCount;
121 QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount);
123 if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) {
130 for (
auto it = penInfoHistory.rbegin(),
end = penInfoHistory.rend();
it !=
end; ++
it) {
136 return translatePenEvent(
window, hwnd, et, msg, &penInfo);
156 switch (msg.message) {
163 case WM_LBUTTONDBLCLK:
169 case WM_MBUTTONDBLCLK:
175 case WM_RBUTTONDBLCLK:
181 case WM_XBUTTONDBLCLK:
185 case WM_NCLBUTTONDOWN:
189 case WM_NCLBUTTONDBLCLK:
191 case WM_NCMBUTTONDOWN:
195 case WM_NCMBUTTONDBLCLK:
197 case WM_NCRBUTTONDOWN:
201 case WM_NCRBUTTONDBLCLK:
212 if (keyState & MK_LBUTTON)
214 if (keyState & MK_RBUTTON)
216 if (keyState & MK_MBUTTON)
218 if (keyState & MK_XBUTTON1)
220 if (keyState & MK_XBUTTON2)
228 const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON);
229 if (GetAsyncKeyState(VK_LBUTTON) < 0)
231 if (GetAsyncKeyState(VK_RBUTTON) < 0)
233 if (GetAsyncKeyState(VK_MBUTTON) < 0)
235 if (GetAsyncKeyState(VK_XBUTTON1) < 0)
237 if (GetAsyncKeyState(VK_XBUTTON2) < 0)
250 currentWindowUnderPointer = currentWindowUnderPointer->parent();
256 if (!currentWindowUnderPointer) {
258 if (clientRect.contains(globalPos))
259 currentWindowUnderPointer =
window;
261 return currentWindowUnderPointer;
267 tme.cbSize =
sizeof(TRACKMOUSEEVENT);
268 tme.dwFlags = TME_LEAVE;
269 tme.hwndTrack = hwnd;
270 tme.dwHoverTime = HOVER_DEFAULT;
271 return TrackMouseEvent(&tme);
288 const int digitizers = GetSystemMetrics(SM_DIGITIZER);
289 if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH)))
291 const int tabletPc = GetSystemMetrics(SM_TABLETPC);
292 const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES);
301 }
else if (mouseEmulation) {
305 const int flags = digitizers & ~NID_READY;
308 <<
"Tablet PC:" << tabletPc <<
"Max touch points:" << maxTouchPoints <<
"Capabilities:" << capabilities;
315 capabilities, maxTouchPoints, buttonCount,
326void QWindowsPointerHandler::handleCaptureRelease(
QWindow *
window,
327 QWindow *currentWindowUnderPointer,
330 Qt::MouseButtons mouseButtons)
339 qCDebug(lcQpaEvents) <<
"Automatic mouse capture " <<
window;
343 window->requestActivate();
345 }
else if (platformWindow->hasMouseCapture()
350 platformWindow->setMouseGrabEnabled(
false);
351 qCDebug(lcQpaEvents) <<
"Releasing automatic mouse capture " <<
window;
357 if (
window != m_currentWindow &&
358 (!platformWindow->hasMouseCapture() || currentWindowUnderPointer ==
window)) {
364void QWindowsPointerHandler::handleEnterLeave(
QWindow *
window,
365 QWindow *currentWindowUnderPointer,
379 if ((m_windowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
380 && (!hasCapture ||
window == m_windowUnderPointer))
381 || (hasCapture && m_previousCaptureWindow !=
window && m_windowUnderPointer
382 && m_windowUnderPointer !=
window)) {
384 qCDebug(lcQpaEvents) <<
"Leaving window " << m_windowUnderPointer;
387 if (hasCapture && currentWindowUnderPointer !=
window) {
390 m_currentWindow =
nullptr;
393 platformWindow->applyCursor();
401 if ((currentWindowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
402 && (!hasCapture || currentWindowUnderPointer ==
window))
403 || (m_previousCaptureWindow && !hasCapture && currentWindowUnderPointer
404 && currentWindowUnderPointer != m_previousCaptureWindow)) {
408 wumLocalPos = wumPlatformWindow->mapFromGlobal(globalPos);
409 wumPlatformWindow->applyCursor();
411 qCDebug(lcQpaEvents) <<
"Entering window " << currentWindowUnderPointer;
417 m_windowUnderPointer = currentWindowUnderPointer;
420 m_previousCaptureWindow = hasCapture ?
window :
nullptr;
423bool QWindowsPointerHandler::translateTouchEvent(
QWindow *
window, HWND hwnd,
429 auto *touchInfo =
static_cast<POINTER_TOUCH_INFO *
>(vTouchInfo);
440 keyMapper->queryKeyboardModifiers());
441 m_lastTouchPoints.
clear();
447 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
448 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
450 m_lastTouchPoints.
remove(
id);
468 QList<QWindowSystemInterface::TouchPoint> touchPoints;
473 <<
" message=" <<
Qt::hex << msg.message
476 QEventPoint::States allStates;
482 <<
" TouchPoint id=" << touchInfo[
i].pointerInfo.pointerId
483 <<
" frame=" << touchInfo[
i].pointerInfo.frameId
484 <<
" flags=" <<
Qt::hex << touchInfo[
i].pointerInfo.pointerFlags;
487 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
488 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
491 if ((touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0)
493 id = m_touchInputIDToTouchPointID.
size();
494 m_touchInputIDToTouchPointID.
insert(pointerId,
id);
497 touchPoint.
pressure = (touchInfo[
i].touchMask & TOUCH_MASK_PRESSURE) ?
498 touchInfo[
i].pressure / 1024.0 : 1.0;
499 if (m_lastTouchPoints.
contains(touchPoint.
id))
502 const QPointF screenPos =
QPointF(touchInfo[
i].pointerInfo.ptPixelLocation.x,
503 touchInfo[
i].pointerInfo.ptPixelLocation.y);
505 if (touchInfo[
i].touchMask & TOUCH_MASK_CONTACTAREA)
507 touchInfo[
i].rcContact.bottom - touchInfo[
i].rcContact.top));
510 screenPos.
y() / screenGeometry.
height());
511 const bool stationaryTouchPoint = (normalPosition == touchPoint.
normalPosition);
514 if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
516 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
517 }
else if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
519 m_lastTouchPoints.
remove(touchPoint.
id);
522 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
524 allStates |= touchPoint.
state;
526 touchPoints.append(touchPoint);
527 inputIds.insert(touchPoint.
id);
530 SkipPointerFrameMessages(touchInfo[
i].pointerInfo.pointerId);
535 for (
auto tp :
std::as_const(m_lastTouchPoints)) {
536 if (!inputIds.contains(tp.id)) {
538 allStates |= tp.state;
539 touchPoints.append(tp);
543 if (touchPoints.count() == 0)
548 m_touchInputIDToTouchPointID.
clear();
552 keyMapper->queryKeyboardModifiers());
556#if QT_CONFIG(tabletevent)
559 for (
const auto &
d : m_tabletDevices) {
568 MSG msg, PVOID vPenInfo)
570#if QT_CONFIG(tabletevent)
574 auto *penInfo =
static_cast<POINTER_PEN_INFO *
>(vPenInfo);
577 if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
580 const auto systemId = (
qint64)penInfo->pointerInfo.sourceDevice;
581 const QPoint globalPos =
QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y);
583 const QPointF hiResGlobalPos =
QPointF(dRect.left +
qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left)
584 / (pRect.right - pRect.left) * (dRect.right - dRect.left),
585 dRect.top +
qreal(penInfo->pointerInfo.ptHimetricLocation.y - pRect.top)
586 / (pRect.bottom - pRect.top) * (dRect.bottom - dRect.top));
587 const bool hasPressure = (penInfo->penMask & PEN_MASK_PRESSURE) != 0;
588 const bool hasRotation = (penInfo->penMask & PEN_MASK_ROTATION) != 0;
589 const qreal pressure = hasPressure ?
qreal(penInfo->pressure) / 1024.0 : 0.5;
590 const qreal rotation = hasRotation ?
qreal(penInfo->rotation) : 0.0;
591 const qreal tangentialPressure = 0.0;
592 const bool hasTiltX = (penInfo->penMask & PEN_MASK_TILT_X) != 0;
593 const bool hasTiltY = (penInfo->penMask & PEN_MASK_TILT_Y) != 0;
594 const int xTilt = hasTiltX ? penInfo->tiltX : 0;
595 const int yTilt = hasTiltY ? penInfo->tiltY : 0;
600 << __FUNCTION__ <<
" systemId=" << systemId
601 <<
" globalPos=" << globalPos <<
" localPos=" << localPos <<
" hiResGlobalPos=" << hiResGlobalPos
602 <<
" message=" <<
Qt::hex << msg.message
603 <<
" flags=" <<
Qt::hex << penInfo->pointerInfo.pointerFlags;
610 const bool pointerInContact = IS_POINTER_INCONTACT_WPARAM(msg.wParam);
611 if (pointerInContact)
614 if (penInfo->penFlags & (PEN_FLAG_ERASER | PEN_FLAG_INVERTED)) {
618 if (pointerInContact && penInfo->penFlags & PEN_FLAG_BARREL)
641 m_tabletDevices.append(
device);
644 const auto uniqueId =
device->uniqueId().numericId();
645 m_activeTabletDevice =
device;
647 switch (msg.message) {
650 m_windowUnderPointer =
window;
653 m_needsEnterOnPointerUpdate =
true;
657 if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
659 m_windowUnderPointer =
nullptr;
660 m_currentWindow =
nullptr;
668 if (!
target && m_windowUnderPointer)
669 target = m_windowUnderPointer;
673 if (m_needsEnterOnPointerUpdate) {
674 m_needsEnterOnPointerUpdate =
false;
675 if (
window != m_currentWindow) {
682 wumPlatformWindow->applyCursor();
686 const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
689 localPos, hiResGlobalPos, mouseButtons,
690 pressure, xTilt, yTilt, tangentialPressure,
691 rotation,
z, keyModifiers);
710 const LONG_PTR SIGNATURE_MASK = 0xFFFFFF00;
711 const LONG_PTR MI_WP_SIGNATURE = 0xFF515700;
713 return ((::GetMessageExtraInfo() & SIGNATURE_MASK) == MI_WP_SIGNATURE);
716bool QWindowsPointerHandler::translateMouseWheelEvent(
QWindow *
window,
717 QWindow *currentWindowUnderPointer,
720 Qt::KeyboardModifiers keyModifiers)
722 QWindow *receiver = currentWindowUnderPointer;
728 int delta = GET_WHEEL_DELTA_WPARAM(msg.wParam);
754 QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
757 globalPos = eventPos;
762 GetClientRect(hwnd, &clientArea);
763 eventPos.
setX(clientArea.right - eventPos.
x());
767 auto targetHwnd = hwnd;
768 if (
auto *pw =
window->handle())
769 targetHwnd = HWND(pw->winId());
770 localPos = targetHwnd == hwnd
776 const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
780 return translateMouseWheelEvent(
window, currentWindowUnderPointer, msg, globalPos, keyModifiers);
786 bool discardEvent =
false;
787 if (msg.message == WM_MOUSEMOVE) {
788 Q_CONSTINIT
static QPoint lastMouseMovePos;
789 if (msg.wParam == 0 && (m_windowUnderPointer.
isNull() || globalPos == lastMouseMovePos))
791 lastMouseMovePos = globalPos;
802 switch (m_pointerType) {
807 if (!m_touchDevice.
isNull())
811#if QT_CONFIG(tabletevent)
812 qCDebug(lcQpaTablet) <<
"ignoring synth-mouse event for tablet event from" <<
device;
820 Qt::MouseButtons mouseButtons;
835 && (m_lastEventButton & mouseButtons) == 0) {
839 releaseType, keyModifiers,
source);
841 m_lastEventType = mouseEvent.
type;
842 m_lastEventButton = mouseEvent.button;
846 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
850 if (msg.message == WM_MOUSELEAVE) {
851 if (
window == m_currentWindow) {
852 QWindow *leaveTarget = m_windowUnderPointer ? m_windowUnderPointer : m_currentWindow;
853 qCDebug(lcQpaEvents) <<
"Leaving window " << leaveTarget;
855 m_windowUnderPointer =
nullptr;
856 m_currentWindow =
nullptr;
861 handleCaptureRelease(
window, currentWindowUnderPointer, hwnd, mouseEvent.
type, mouseButtons);
862 handleEnterLeave(
window, currentWindowUnderPointer, globalPos);
866 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
871 return (msg.message != WM_XBUTTONUP && msg.message != WM_XBUTTONDOWN && msg.message != WM_XBUTTONDBLCLK)
IOBluetoothDevice * device
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
Type
This enum type defines the valid event types in Qt.
@ NonClientAreaMouseButtonDblClick
@ NonClientAreaMouseButtonRelease
@ NonClientAreaMouseButtonPress
static TabletPointData & tabletDevicePoint(qint64 deviceId)
QScreen * primaryScreen
the primary (or default) screen of the application.
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
bool remove(const Key &key)
Removes the item that has the key from the hash.
qsizetype size() const noexcept
Returns the number of items in the hash.
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
T value(const Key &key) const noexcept
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
\inmodule QtCore\reentrant
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
\inmodule QtCore\reentrant
constexpr int x() const noexcept
Returns the x coordinate of this point.
constexpr void setX(int x) noexcept
Sets the x coordinate of this point to the given x coordinate.
bool isNull() const noexcept
static QPointingDeviceUniqueId fromNumericId(qint64 id)
Constructs a unique pointer ID from numeric ID id.
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
static const QPointingDevice * primaryPointingDevice(const QString &seatName=QString())
Returns the primary pointing device (the core pointer, traditionally assumed to be a mouse) on the gi...
PointerType
This enum represents what is interacting with the pointing device.
constexpr void moveCenter(const QPointF &p) noexcept
Moves the rectangle, leaving the center point at the given position.
constexpr void setSize(const QSizeF &s) noexcept
Sets the size of the rectangle to the given finite size.
\inmodule QtCore\reentrant
constexpr int height() const noexcept
Returns the height of the rectangle.
constexpr int width() const noexcept
Returns the width of the rectangle.
The QScreen class is used to query screen properties. \inmodule QtGui.
QRect geometry
the screen's geometry in pixels
bool isNull() const noexcept
Returns true if this object refers to \nullptr.
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static bool handleTouchEvent(QWindow *window, const QPointingDevice *device, const QList< struct TouchPoint > &points, Qt::KeyboardModifiers mods=Qt::NoModifier)
static bool flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Make Qt Gui process all events on the event queue immediately.
static bool handleTabletEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, const QPointF &local, const QPointF &global, Qt::MouseButtons buttons, qreal pressure, int xTilt, int yTilt, qreal tangentialPressure, qreal rotation, int z, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
static void handleLeaveEvent(QWindow *window)
static bool handleTouchCancelEvent(QWindow *window, const QPointingDevice *device, Qt::KeyboardModifiers mods=Qt::NoModifier)
static bool handleMouseEvent(QWindow *window, const QPointF &local, const QPointF &global, Qt::MouseButtons state, Qt::MouseButton button, QEvent::Type type, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
static void registerInputDevice(const QInputDevice *device)
static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local=QPointF(), const QPointF &global=QPointF())
This method can be used to ensure leave and enter events are both in queue when moving from one QWind...
static bool handleTabletEnterLeaveProximityEvent(QWindow *window, ulong timestamp, const QPointingDevice *device, bool inProximity, const QPointF &local=QPointF(), const QPointF &global=QPointF(), Qt::MouseButtons buttons={}, int xTilt=0, int yTilt=0, qreal tangentialPressure=0, qreal rotation=0, int z=0, Qt::KeyboardModifiers modifiers=Qt::NoModifier)
static void handleEnterEvent(QWindow *window, const QPointF &local=QPointF(), const QPointF &global=QPointF())
static bool handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::ScrollPhase phase=Qt::NoScrollPhase, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
static bool isRtlLayout(HWND hwnd)
static QWindowsContext * instance()
@ DontPassOsMouseEventsSynthesizedFromTouch
static QWindowsIntegration * instance()
static QPointingDevicePtr createTouchDevice(bool mouseEmulation)
~QWindowsPointerHandler()
static Qt::MouseButtons queryMouseButtons()
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)
QSharedPointer< QPointingDevice > QPointingDevicePtr
static QWindow * windowAt(const QPoint &point, unsigned flags)
static QWindowsWindow * windowsWindowOf(const QWindow *w)
bool setMouseGrabEnabled(bool grab) override
static QWindow * topLevelOf(QWindow *w)
bool hasMouseCapture() const
QSet< QString >::iterator it
Combined button and popup list for selecting options.
WindowsEventType
Enumerations for WM_XX events.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & showbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on stream and r...
@ MouseEventSynthesizedBySystem
@ MouseEventNotSynthesized
QTextStream & noshowbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() & ~QTextStream::ShowBase) on stream and ...
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
@ AA_CompressTabletEvents
@ AA_CompressHighFrequencyEvents
@ WindowTransparentForInput
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qCDebug(category,...)
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLenum GLenum GLsizei count
GLsizei GLsizei GLchar * source
#define QStringLiteral(str)
#define WM_POINTERCAPTURECHANGED
static bool isValidWheelReceiver(QWindow *candidate)
static bool trackLeave(HWND hwnd)
static QWindow * getWindowUnderPointer(QWindow *window, QPoint globalPos)
static Qt::MouseButton extraButton(WPARAM wParam)
const QPointingDevice * primaryMouse()
static MouseEvent eventFromMsg(const MSG &msg)
static bool isMouseEventSynthesizedFromPenOrTouch()
static Qt::MouseButtons mouseButtonsFromKeyState(WPARAM keyState)
static QPointingDevice::PointerType pointerType(unsigned currentCursor)
static QPoint mapToGlobal(HWND hwnd, const QPoint &)
static QPoint mapFromGlobal(const HWND hwnd, const QPoint &)