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)) {
69 QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
70 if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
79 quint32 historyCount = touchInfo[0].pointerInfo.historyCount;
82 touchInfo.resize(pointerCount * historyCount);
83 if (!GetPointerFrameTouchInfoHistory(pointerId,
93 for (
auto it = touchInfo.rbegin(),
end = touchInfo.rend();
it !=
end;
it += pointerCount) {
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;
114 QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount);
116 if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) {
123 for (
auto it = penInfoHistory.rbegin(),
end = penInfoHistory.rend();
it !=
end; ++
it) {
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) {
251 if (clientRect.contains(globalPos))
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 keyMapper->queryKeyboardModifiers());
434 m_lastTouchPoints.
clear();
440 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
441 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
443 m_lastTouchPoints.
remove(
id);
461 QList<QWindowSystemInterface::TouchPoint> touchPoints;
466 <<
" message=" <<
Qt::hex << msg.message
469 QEventPoint::States allStates;
475 <<
" TouchPoint id=" << touchInfo[
i].pointerInfo.pointerId
476 <<
" frame=" << touchInfo[
i].pointerInfo.frameId
477 <<
" flags=" <<
Qt::hex << touchInfo[
i].pointerInfo.pointerFlags;
480 const quint32 pointerId = touchInfo[
i].pointerInfo.pointerId;
481 int id = m_touchInputIDToTouchPointID.
value(pointerId, -1);
484 if ((touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0)
486 id = m_touchInputIDToTouchPointID.
size();
487 m_touchInputIDToTouchPointID.
insert(pointerId,
id);
490 touchPoint.
pressure = (touchInfo[
i].touchMask & TOUCH_MASK_PRESSURE) ?
491 touchInfo[
i].pressure / 1024.0 : 1.0;
492 if (m_lastTouchPoints.
contains(touchPoint.
id))
495 const QPointF screenPos =
QPointF(touchInfo[
i].pointerInfo.ptPixelLocation.x,
496 touchInfo[
i].pointerInfo.ptPixelLocation.y);
498 if (touchInfo[
i].touchMask & TOUCH_MASK_CONTACTAREA)
500 touchInfo[
i].rcContact.bottom - touchInfo[
i].rcContact.top));
503 screenPos.
y() / screenGeometry.
height());
504 const bool stationaryTouchPoint = (normalPosition == touchPoint.
normalPosition);
507 if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
509 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
510 }
else if (touchInfo[
i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
512 m_lastTouchPoints.
remove(touchPoint.
id);
515 m_lastTouchPoints.
insert(touchPoint.
id, touchPoint);
517 allStates |= touchPoint.
state;
519 touchPoints.append(touchPoint);
520 inputIds.insert(touchPoint.
id);
523 SkipPointerFrameMessages(touchInfo[
i].pointerInfo.pointerId);
528 for (
auto tp :
std::as_const(m_lastTouchPoints)) {
529 if (!inputIds.contains(tp.id)) {
531 allStates |= tp.state;
532 touchPoints.append(tp);
536 if (touchPoints.count() == 0)
541 m_touchInputIDToTouchPointID.
clear();
545 keyMapper->queryKeyboardModifiers());
549#if QT_CONFIG(tabletevent)
552 for (
const auto &
d : m_tabletDevices) {
561 MSG msg, PVOID vPenInfo)
563#if QT_CONFIG(tabletevent)
567 auto *penInfo =
static_cast<POINTER_PEN_INFO *
>(vPenInfo);
570 if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
573 const auto systemId = (
qint64)penInfo->pointerInfo.sourceDevice;
574 const QPoint globalPos =
QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y);
576 const QPointF hiResGlobalPos =
QPointF(dRect.left +
qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left)
577 / (pRect.right - pRect.left) * (dRect.right - dRect.left),
578 dRect.top +
qreal(penInfo->pointerInfo.ptHimetricLocation.y - pRect.top)
579 / (pRect.bottom - pRect.top) * (dRect.bottom - dRect.top));
580 const bool hasPressure = (penInfo->penMask & PEN_MASK_PRESSURE) != 0;
581 const bool hasRotation = (penInfo->penMask & PEN_MASK_ROTATION) != 0;
582 const qreal pressure = hasPressure ?
qreal(penInfo->pressure) / 1024.0 : 0.5;
583 const qreal rotation = hasRotation ?
qreal(penInfo->rotation) : 0.0;
584 const qreal tangentialPressure = 0.0;
585 const bool hasTiltX = (penInfo->penMask & PEN_MASK_TILT_X) != 0;
586 const bool hasTiltY = (penInfo->penMask & PEN_MASK_TILT_Y) != 0;
587 const int xTilt = hasTiltX ? penInfo->tiltX : 0;
588 const int yTilt = hasTiltY ? penInfo->tiltY : 0;
593 << __FUNCTION__ <<
" systemId=" << systemId
594 <<
" globalPos=" << globalPos <<
" localPos=" << localPos <<
" hiResGlobalPos=" << hiResGlobalPos
595 <<
" message=" <<
Qt::hex << msg.message
596 <<
" flags=" <<
Qt::hex << penInfo->pointerInfo.pointerFlags;
603 const bool pointerInContact = IS_POINTER_INCONTACT_WPARAM(msg.wParam);
604 if (pointerInContact)
607 if (penInfo->penFlags & (PEN_FLAG_ERASER | PEN_FLAG_INVERTED)) {
611 if (pointerInContact && penInfo->penFlags & PEN_FLAG_BARREL)
634 m_tabletDevices.append(
device);
637 const auto uniqueId =
device->uniqueId().numericId();
638 m_activeTabletDevice =
device;
640 switch (msg.message) {
643 m_windowUnderPointer =
window;
646 m_needsEnterOnPointerUpdate =
true;
650 if (m_windowUnderPointer && m_windowUnderPointer == m_currentWindow) {
652 m_windowUnderPointer =
nullptr;
653 m_currentWindow =
nullptr;
661 if (!
target && m_windowUnderPointer)
662 target = m_windowUnderPointer;
666 if (m_needsEnterOnPointerUpdate) {
667 m_needsEnterOnPointerUpdate =
false;
668 if (
window != m_currentWindow) {
675 wumPlatformWindow->applyCursor();
679 const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
682 localPos, hiResGlobalPos, mouseButtons,
683 pressure, xTilt, yTilt, tangentialPressure,
684 rotation,
z, keyModifiers);
703 const LONG_PTR SIGNATURE_MASK = 0xFFFFFF00;
704 const LONG_PTR MI_WP_SIGNATURE = 0xFF515700;
706 return ((::GetMessageExtraInfo() & SIGNATURE_MASK) == MI_WP_SIGNATURE);
709bool QWindowsPointerHandler::translateMouseWheelEvent(
QWindow *
window,
710 QWindow *currentWindowUnderPointer,
713 Qt::KeyboardModifiers keyModifiers)
715 QWindow *receiver = currentWindowUnderPointer;
721 int delta = GET_WHEEL_DELTA_WPARAM(msg.wParam);
747 QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
750 globalPos = eventPos;
755 GetClientRect(hwnd, &clientArea);
756 eventPos.
setX(clientArea.right - eventPos.
x());
760 auto targetHwnd = hwnd;
761 if (
auto *pw =
window->handle())
762 targetHwnd = HWND(pw->winId());
763 localPos = targetHwnd == hwnd
769 const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
773 return translateMouseWheelEvent(
window, currentWindowUnderPointer, msg, globalPos, keyModifiers);
779 bool discardEvent =
false;
780 if (msg.message == WM_MOUSEMOVE) {
795 switch (m_pointerType) {
800 if (!m_touchDevice.
isNull())
804#if QT_CONFIG(tabletevent)
805 qCDebug(lcQpaTablet) <<
"ignoring synth-mouse event for tablet event from" <<
device;
813 Qt::MouseButtons mouseButtons;
828 && (m_lastEventButton & mouseButtons) == 0) {
832 releaseType, keyModifiers,
source);
834 m_lastEventType = mouseEvent.
type;
835 m_lastEventButton = mouseEvent.button;
839 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
843 if (msg.message == WM_MOUSELEAVE) {
844 if (
window == m_currentWindow) {
845 QWindow *leaveTarget = m_windowUnderPointer ? m_windowUnderPointer : m_currentWindow;
846 qCDebug(lcQpaEvents) <<
"Leaving window " << leaveTarget;
848 m_windowUnderPointer =
nullptr;
849 m_currentWindow =
nullptr;
854 handleCaptureRelease(
window, currentWindowUnderPointer, hwnd, mouseEvent.
type, mouseButtons);
855 handleEnterLeave(
window, currentWindowUnderPointer, globalPos);
859 mouseEvent.button, mouseEvent.
type, keyModifiers,
source);
864 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.
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 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 &)