4#include <QtCore/qt_windows.h>
8#if QT_CONFIG(tabletevent)
17#include <QtGui/qguiapplication.h>
18#include <QtGui/qscreen.h>
19#include <QtGui/qpointingdevice.h>
20#include <QtGui/qwindow.h>
21#include <QtGui/private/qguiapplication_p.h>
22#include <QtCore/qvarlengtharray.h>
23#include <QtCore/qloggingcategory.h>
24#include <QtCore/qqueue.h>
40qint64 QWindowsPointerHandler::m_nextInputDeviceId = 1;
49 const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
51 if (!GetPointerType(pointerId, &m_pointerType)) {
56 switch (m_pointerType) {
65 if (!GetPointerFrameTouchInfo(pointerId, &pointerCount,
nullptr)) {
70 if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.
data())) {
79 quint32 historyCount = touchInfo[0].pointerInfo.historyCount;
82 touchInfo.
resize(pointerCount * historyCount);
83 if (!GetPointerFrameTouchInfoHistory(pointerId,
95 &(*(
it + (pointerCount - 1))), pointerCount);
100 return translateTouchEvent(
window, hwnd, et, msg, touchInfo.
data(), pointerCount);
103 POINTER_PEN_INFO penInfo;
104 if (!GetPointerPenInfo(pointerId, &penInfo)) {
109 quint32 historyCount = penInfo.pointerInfo.historyCount;
116 if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.
data())) {
129 return translatePenEvent(
window, hwnd, et, msg, &penInfo);
149 switch (msg.message) {
156 case WM_LBUTTONDBLCLK:
162 case WM_MBUTTONDBLCLK:
168 case WM_RBUTTONDBLCLK:
174 case WM_XBUTTONDBLCLK:
178 case WM_NCLBUTTONDOWN:
182 case WM_NCLBUTTONDBLCLK:
184 case WM_NCMBUTTONDOWN:
188 case WM_NCMBUTTONDBLCLK:
190 case WM_NCRBUTTONDOWN:
194 case WM_NCRBUTTONDBLCLK:
205 if (keyState & MK_LBUTTON)
207 if (keyState & MK_RBUTTON)
209 if (keyState & MK_MBUTTON)
211 if (keyState & MK_XBUTTON1)
213 if (keyState & MK_XBUTTON2)
221 const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON);
222 if (GetAsyncKeyState(VK_LBUTTON) < 0)
224 if (GetAsyncKeyState(VK_RBUTTON) < 0)
226 if (GetAsyncKeyState(VK_MBUTTON) < 0)
228 if (GetAsyncKeyState(VK_XBUTTON1) < 0)
230 if (GetAsyncKeyState(VK_XBUTTON2) < 0)
243 currentWindowUnderPointer = currentWindowUnderPointer->parent();
249 if (!currentWindowUnderPointer) {
252 currentWindowUnderPointer =
window;
254 return currentWindowUnderPointer;
260 tme.cbSize =
sizeof(TRACKMOUSEEVENT);
261 tme.dwFlags = TME_LEAVE;
262 tme.hwndTrack = hwnd;
263 tme.dwHoverTime = HOVER_DEFAULT;
264 return TrackMouseEvent(&tme);
281 const int digitizers = GetSystemMetrics(SM_DIGITIZER);
282 if (!(digitizers & (NID_INTEGRATED_TOUCH | NID_EXTERNAL_TOUCH)))
284 const int tabletPc = GetSystemMetrics(SM_TABLETPC);
285 const int maxTouchPoints = GetSystemMetrics(SM_MAXIMUMTOUCHES);
294 }
else if (mouseEmulation) {
298 const int flags = digitizers & ~NID_READY;
301 <<
"Tablet PC:" << tabletPc <<
"Max touch points:" << maxTouchPoints <<
"Capabilities:" << capabilities;
308 capabilities, maxTouchPoints, buttonCount,
319void QWindowsPointerHandler::handleCaptureRelease(
QWindow *
window,
320 QWindow *currentWindowUnderPointer,
323 Qt::MouseButtons mouseButtons)
332 qCDebug(lcQpaEvents) <<
"Automatic mouse capture " <<
window;
336 window->requestActivate();
338 }
else if (platformWindow->hasMouseCapture()
343 platformWindow->setMouseGrabEnabled(
false);
344 qCDebug(lcQpaEvents) <<
"Releasing automatic mouse capture " <<
window;
350 if (
window != m_currentWindow &&
351 (!platformWindow->hasMouseCapture() || currentWindowUnderPointer ==
window)) {
357void QWindowsPointerHandler::handleEnterLeave(
QWindow *
window,
358 QWindow *currentWindowUnderPointer,
372 if ((m_windowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
373 && (!hasCapture ||
window == m_windowUnderPointer))
374 || (hasCapture && m_previousCaptureWindow !=
window && m_windowUnderPointer
375 && m_windowUnderPointer !=
window)) {
377 qCDebug(lcQpaEvents) <<
"Leaving window " << m_windowUnderPointer;
380 if (hasCapture && currentWindowUnderPointer !=
window) {
383 m_currentWindow =
nullptr;
386 platformWindow->applyCursor();
394 if ((currentWindowUnderPointer && m_windowUnderPointer != currentWindowUnderPointer
395 && (!hasCapture || currentWindowUnderPointer ==
window))
396 || (m_previousCaptureWindow && !hasCapture && currentWindowUnderPointer
397 && currentWindowUnderPointer != m_previousCaptureWindow)) {
401 wumLocalPos = wumPlatformWindow->mapFromGlobal(globalPos);
402 wumPlatformWindow->applyCursor();
404 qCDebug(lcQpaEvents) <<
"Entering window " << currentWindowUnderPointer;
410 m_windowUnderPointer = currentWindowUnderPointer;
413 m_previousCaptureWindow = hasCapture ?
window :
nullptr;
416bool QWindowsPointerHandler::translateTouchEvent(
QWindow *
window, HWND hwnd,
422 auto *touchInfo =
static_cast<POINTER_TOUCH_INFO *
>(vTouchInfo);
433 m_lastTouchPoints.
clear();
439 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
440 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
442 m_lastTouchPoints.
remove(
id);
465 <<
" message=" <<
Qt::hex << msg.message
468 QEventPoint::States allStates;
474 <<
" TouchPoint id=" << touchInfo[
i].pointerInfo.pointerId
475 <<
" frame=" << touchInfo[
i].pointerInfo.frameId
476 <<
" flags=" <<
Qt::hex << touchInfo[
i].pointerInfo.pointerFlags;
479 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
480 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
483 if ((touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0)
485 id = m_touchInputIDToTouchPointID.
size();
486 m_touchInputIDToTouchPointID.
insert(pointerId,
id);
489 touchPoint.
pressure = (touchInfo[
i].touchMask & TOUCH_MASK_PRESSURE) ?
490 touchInfo[
i].pressure / 1024.0 : 1.0;
491 if (m_lastTouchPoints.
contains(touchPoint.
id))
494 const QPointF screenPos =
QPointF(touchInfo[
i].pointerInfo.ptPixelLocation.x,
495 touchInfo[
i].pointerInfo.ptPixelLocation.y);
497 if (touchInfo[
i].touchMask & TOUCH_MASK_CONTACTAREA)
499 touchInfo[
i].rcContact.bottom - touchInfo[
i].rcContact.top));
502 screenPos.
y() / screenGeometry.
height());
503 const bool stationaryTouchPoint = (normalPosition == touchPoint.
normalPosition);
506 if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
508 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
509 }
else if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
511 m_lastTouchPoints.
remove(touchPoint.
id);
514 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
516 allStates |= touchPoint.
state;
518 touchPoints.
append(touchPoint);
522 SkipPointerFrameMessages(touchInfo[
i].pointerInfo.pointerId);
527 for (
auto tp :
std::as_const(m_lastTouchPoints)) {
530 allStates |= tp.state;
535 if (touchPoints.
count() == 0)
540 m_touchInputIDToTouchPointID.
clear();
547#if QT_CONFIG(tabletevent)
550 for (
const auto &
d : m_tabletDevices) {
559 MSG msg, PVOID vPenInfo)
561#if QT_CONFIG(tabletevent)
565 auto *penInfo =
static_cast<POINTER_PEN_INFO *
>(vPenInfo);
568 if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
571 const auto systemId = (
qint64)penInfo->pointerInfo.sourceDevice;
572 const QPoint globalPos =
QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y);
574 const QPointF hiResGlobalPos =
QPointF(dRect.left +
qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left)
575 / (pRect.right - pRect.left) * (dRect.right - dRect.left),
576 dRect.top +
qreal(penInfo->pointerInfo.ptHimetricLocation.y - pRect.top)
577 / (pRect.bottom - pRect.top) * (dRect.bottom - dRect.top));
578 const bool hasPressure = (penInfo->penMask & PEN_MASK_PRESSURE) != 0;
579 const bool hasRotation = (penInfo->penMask & PEN_MASK_ROTATION) != 0;
580 const qreal pressure = hasPressure ?
qreal(penInfo->pressure) / 1024.0 : 0.5;
581 const qreal rotation = hasRotation ?
qreal(penInfo->rotation) : 0.0;
582 const qreal tangentialPressure = 0.0;
583 const bool hasTiltX = (penInfo->penMask & PEN_MASK_TILT_X) != 0;
584 const bool hasTiltY = (penInfo->penMask & PEN_MASK_TILT_Y) != 0;
585 const int xTilt = hasTiltX ? penInfo->tiltX : 0;
586 const int yTilt = hasTiltY ? penInfo->tiltY : 0;
591 << __FUNCTION__ <<
" systemId=" << systemId
592 <<
" globalPos=" << globalPos <<
" localPos=" << localPos <<
" hiResGlobalPos=" << hiResGlobalPos
593 <<
" message=" <<
Qt::hex << msg.message
594 <<
" flags=" <<
Qt::hex << penInfo->pointerInfo.pointerFlags;
601 const bool pointerInContact = IS_POINTER_INCONTACT_WPARAM(msg.wParam);
602 if (pointerInContact)
605 if (penInfo->penFlags & (PEN_FLAG_ERASER | PEN_FLAG_INVERTED)) {
609 if (pointerInContact && penInfo->penFlags & PEN_FLAG_BARREL)
632 m_tabletDevices.append(
device);
635 const auto uniqueId =
device->uniqueId().numericId();
636 m_activeTabletDevice =
device;
638 switch (msg.message) {
641 m_windowUnderPointer =
window;
644 m_needsEnterOnPointerUpdate =
true;
648 if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
650 m_windowUnderPointer =
nullptr;
651 m_currentWindow =
nullptr;
659 if (!
target && m_windowUnderPointer)
660 target = m_windowUnderPointer;
664 if (m_needsEnterOnPointerUpdate) {
665 m_needsEnterOnPointerUpdate =
false;
666 if (
window != m_currentWindow) {
673 wumPlatformWindow->applyCursor();
679 localPos, hiResGlobalPos, mouseButtons,
680 pressure, xTilt, yTilt, tangentialPressure,
681 rotation,
z, keyModifiers);
700 const LONG_PTR SIGNATURE_MASK = 0xFFFFFF00;
701 const LONG_PTR MI_WP_SIGNATURE = 0xFF515700;
703 return ((::GetMessageExtraInfo() & SIGNATURE_MASK) == MI_WP_SIGNATURE);
706bool QWindowsPointerHandler::translateMouseWheelEvent(
QWindow *
window,
707 QWindow *currentWindowUnderPointer,
710 Qt::KeyboardModifiers keyModifiers)
712 QWindow *receiver = currentWindowUnderPointer;
718 int delta = GET_WHEEL_DELTA_WPARAM(msg.wParam);
742 QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
745 GetClientRect(hwnd, &clientArea);
746 eventPos.
setX(clientArea.right - eventPos.
x());
753 globalPos = eventPos;
757 auto targetHwnd = hwnd;
758 if (
auto *pw =
window->handle())
759 targetHwnd = HWND(pw->winId());
760 localPos = targetHwnd == hwnd
769 return translateMouseWheelEvent(
window, currentWindowUnderPointer, msg, globalPos, keyModifiers);
775 bool discardEvent =
false;
776 if (msg.message == WM_MOUSEMOVE) {
791 switch (m_pointerType) {
796 if (!m_touchDevice.
isNull())
800#if QT_CONFIG(tabletevent)
801 qCDebug(lcQpaTablet) <<
"ignoring synth-mouse event for tablet event from" <<
device;
809 Qt::MouseButtons mouseButtons;
824 && (m_lastEventButton & mouseButtons) == 0) {
828 releaseType, keyModifiers,
source);
830 m_lastEventType = mouseEvent.
type;
831 m_lastEventButton = mouseEvent.button;
835 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
839 if (msg.message == WM_MOUSELEAVE) {
840 if (
window == m_currentWindow) {
841 QWindow *leaveTarget = m_windowUnderPointer ? m_windowUnderPointer : m_currentWindow;
842 qCDebug(lcQpaEvents) <<
"Leaving window " << leaveTarget;
844 m_windowUnderPointer =
nullptr;
845 m_currentWindow =
nullptr;
850 handleCaptureRelease(
window, currentWindowUnderPointer, hwnd, mouseEvent.
type, mouseButtons);
851 handleEnterLeave(
window, currentWindowUnderPointer, globalPos);
855 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
860 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.
qsizetype count() const noexcept
void append(parameter_type t)
\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
Returns true if the referenced object has been destroyed or if there is no referenced object; otherwi...
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.
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.
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
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 contains(const T &value) const
iterator insert(const T &value)
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
void resize(qsizetype sz)
reverse_iterator rbegin() noexcept
reverse_iterator rend() noexcept
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)
Qt::WindowFlags flags
the window flags of the window
static bool isRtlLayout(HWND hwnd)
@ DontPassOsMouseEventsSynthesizedFromTouch
static QWindowsIntegration * instance()
static Qt::KeyboardModifiers queryKeyboardModifiers()
static const QPointingDevice * primaryMouse()
static QPointingDevicePtr createTouchDevice(bool mouseEmulation)
~QWindowsPointerHandler()
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 Q_CONSTINIT QPoint lastMouseMovePos
static MouseEvent eventFromMsg(const MSG &msg)
static bool isValidWheelReceiver(QWindow *candidate)
static Qt::MouseButtons queryMouseButtons()
static bool trackLeave(HWND hwnd)
static QWindow * getWindowUnderPointer(QWindow *window, QPoint globalPos)
static Qt::MouseButton extraButton(WPARAM wParam)
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 &)