Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
5.15.14, 6.5.0
-
None
Description
This bug is like in the attached video,can be reproduced like this
Reproduced
1. Setting a MouseArea in window, and set the cursorShape
Window { width: 640 height: 480 visible: true title: qsTr("Hello World") Rectangle{ width: 100 height: 100 color: "red" MouseArea{ anchors.fill:parent cursorShape: Qt.BusyCursor } } }
2. Put the mouse in the window before the window starts, when the window opens, the mouse will change to the shape set at (0, 0) coordinates
Analyze
Root case:
The static method QCursor:pos() will read the `mLastPos` in QWaylandCursor. It is only updated when the MouseEvent occurs. When entering for the first time, only enter event, so its value is the default {0,0}. At {0,0}, the mouse is set to a new shape, so it appears made a mistake
Related code:
in qtdeclarative/src/quick/items/qquickwindow.cpp, it use QCursor:pos() to updateCursor
case QEvent::Enter: { if (!d->contentItem) return false; QEnterEvent *enter = static_cast<QEnterEvent*>(e); bool accepted = enter->isAccepted(); bool delivered = d->deliverHoverEvent(d->contentItem, enter->windowPos(), d->lastMousePosition, QGuiApplication::keyboardModifiers(), 0L, accepted); d->lastMousePosition = enter->windowPos(); enter->setAccepted(accepted); #if QT_CONFIG(cursor) d->updateCursor(mapFromGlobal(QCursor::pos())); #endif return delivered;
final read the property, and it's just be changed in QWaylandCursor::pointerEvent
QPoint QWaylandCursor::pos() const { return mLastPos; } void QWaylandCursor::pointerEvent(const QMouseEvent &event) { mLastPos = event.globalPos(); }
And the pointerEvent is only called in MouseEvent `cursor->pointerEvent(ev)`
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e){ ```````` #ifndef QT_NO_CURSOR if (!e->synthetic()) { if (const QScreen *screen = window->screen()) if (QPlatformCursor *cursor = screen->handle()->cursor()) { const QPointF nativeLocalPoint = QHighDpi::toNativePixels(localPoint, screen); const QPointF nativeGlobalPoint = QHighDpi::toNativePixels(globalPoint, screen); QMouseEvent ev(type, nativeLocalPoint, nativeLocalPoint, nativeGlobalPoint, button, e->buttons, e->modifiers, e->source); ev.setTimestamp(e->timestamp); cursor->pointerEvent(ev); } } #endif ``````
Due to the fact that the enter event does not trigger a MouseEvent, it will not be called here. The issue is caused by the default value of mLastPos being {0,0}.
I attempted to solve the issue by handling the MouseEvent in advance in the enter event of QtWayland, but I am not sure if this is a reasonable approach.
void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e) { if (mWindowDecoration) { handleMouseEventWithDecoration(inputDevice, e); } else { switch (e.type) { case QEvent::Enter: // ADD QWindowSystemInterface::handleMouseEvent(window(), e.timestamp, e.local, e.global, e.buttons, e.button, e.type, e.modifiers); QWindowSystemInterface::handleEnterEvent(window(), e.local, e.global); break;
By the way, this issue was not reproduced on x11 because they retrieve the position of the coordinates in real time.
// qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp QPoint QXcbCursor::pos() const { QPoint p; queryPointer(connection(), nullptr, &p); return p; } void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask) { if (pos) *pos = QPoint(); xcb_window_t root = c->primaryVirtualDesktop()->root(); auto reply = Q_XCB_REPLY(xcb_query_pointer, c->xcb_connection(), root); if (reply) { if (virtualDesktop) { const auto virtualDesktops = c->virtualDesktops(); for (QXcbVirtualDesktop *vd : virtualDesktops) { if (vd->root() == reply->root) { *virtualDesktop = vd; break; } } } if (pos) *pos = QPoint(reply->root_x, reply->root_y); if (keybMask) *keybMask = reply->mask; return; } }