From 4dfba8320d28b73ecf353f092d5a1bf23508ea34 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Tue, 22 Oct 2024 20:45:19 +0800 Subject: [PATCH] Add alwaysUpdateHoverTarget property for WSeat --- src/server/kernel/winputdevice.cpp | 38 +++++++++++ src/server/kernel/winputdevice.h | 9 +++ src/server/kernel/wseat.cpp | 77 ++++++++++++++++++++++ src/server/kernel/wseat.h | 7 ++ src/server/qtquick/woutputrenderwindow.cpp | 20 ++++++ src/server/qtquick/woutputrenderwindow.h | 1 + 6 files changed, 152 insertions(+) diff --git a/src/server/kernel/winputdevice.cpp b/src/server/kernel/winputdevice.cpp index 8166a7df..66e366b3 100644 --- a/src/server/kernel/winputdevice.cpp +++ b/src/server/kernel/winputdevice.cpp @@ -11,6 +11,8 @@ #include #include +#include + QW_USE_NAMESPACE WAYLIB_SERVER_BEGIN_NAMESPACE @@ -35,6 +37,7 @@ class Q_DECL_HIDDEN WInputDevicePrivate : public WWrapObjectPrivate W_DECLARE_PUBLIC(WInputDevice); QPointer qtDevice; + QPointer hoverTarget; WSeat *seat = nullptr; }; @@ -104,4 +107,39 @@ QInputDevice *WInputDevice::qtDevice() const return d->qtDevice; } +void WInputDevice::setExclusiveGrabber(QObject *grabber) +{ + W_D(WInputDevice); + auto pointerDevice = qobject_cast(d->qtDevice); + if (!pointerDevice) + return; + auto dd = QPointingDevicePrivate::get(pointerDevice); + if (dd->activePoints.isEmpty()) + return; + auto firstPoint = dd->activePoints.values().first(); + dd->setExclusiveGrabber(nullptr, firstPoint.eventPoint, grabber); +} + +QObject *WInputDevice::exclusiveGrabber() const +{ + W_DC(WInputDevice); + auto pointerDevice = qobject_cast(d->qtDevice); + if (!pointerDevice) + return nullptr; + auto dd = QPointingDevicePrivate::get(pointerDevice); + return dd->firstPointExclusiveGrabber(); +} + +QObject *WInputDevice::hoverTarget() const +{ + W_DC(WInputDevice); + return d->hoverTarget; +} + +void WInputDevice::setHoverTarget(QObject *object) +{ + W_D(WInputDevice); + d->hoverTarget = object; +} + WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/kernel/winputdevice.h b/src/server/kernel/winputdevice.h index 6e84ed95..e94c9b5f 100644 --- a/src/server/kernel/winputdevice.h +++ b/src/server/kernel/winputdevice.h @@ -13,6 +13,7 @@ QW_END_NAMESPACE QT_BEGIN_NAMESPACE class QInputDevice; +class QEventPoint; QT_END_NAMESPACE WAYLIB_SERVER_BEGIN_NAMESPACE @@ -53,7 +54,15 @@ class WAYLIB_SERVER_EXPORT WInputDevice : public WWrapObject private: friend class QWlrootsIntegration; + friend class WSeat; + friend class WSeatPrivate; void setQtDevice(QInputDevice *device); + + void setExclusiveGrabber(QObject *grabber); + QObject *exclusiveGrabber() const; + + QObject *hoverTarget() const; + void setHoverTarget(QObject *object); }; WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/kernel/wseat.cpp b/src/server/kernel/wseat.cpp index 7aa5c101..fe290e32 100644 --- a/src/server/kernel/wseat.cpp +++ b/src/server/kernel/wseat.cpp @@ -155,6 +155,7 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate handle()->pointer_notify_frame(); } inline bool doEnter(WSurface *surface, QObject *eventObject, const QPointF &position) { + qDebug() << surface << "====================================="; // doEnter be called from QEvent::HoverEnter is normal, // but doNotifyMotion will call doEnter too, // so should compare pointerFocusEventObject and eventObject early @@ -190,6 +191,7 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate return true; } inline void doClearPointerFocus() { + qDebug() << Q_FUNC_INFO << pointerFocusEventObject << "--------------"; pointerFocusEventObject.clear(); handle()->pointer_notify_clear_focus(); Q_ASSERT(!handle()->handle()->pointer_state.focused_surface); @@ -401,6 +403,8 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate WGlobal::CursorShape cursorShape = WGlobal::CursorShape::Invalid; QPointer dragSurface; + + bool alwaysUpdateHoverTarget = false; }; void WSeatPrivate::on_destroy() @@ -1037,6 +1041,39 @@ void WSeat::setKeyboard(WInputDevice *newKeyboard) Q_EMIT this->keyboardChanged(); } +bool WSeat::alwaysUpdateHoverTarget() const +{ + W_DC(WSeat); + return d->alwaysUpdateHoverTarget; +} + +void WSeat::setAlwaysUpdateHoverTarget(bool newIgnoreSurfacePointerEventExclusiveGrabber) +{ + W_D(WSeat); + if (d->alwaysUpdateHoverTarget == newIgnoreSurfacePointerEventExclusiveGrabber) + return; + d->alwaysUpdateHoverTarget = newIgnoreSurfacePointerEventExclusiveGrabber; + + if (d->alwaysUpdateHoverTarget) { + for (WInputDevice *device : std::as_const(d->deviceList)) { + // Qt will auto grab the pointer event for QQuickItem when mouse pressed + // until mouse released. But we want always update the HoverEnter/Leave's + // WSurfaceItem between drag move. + if (device->exclusiveGrabber() == device->hoverTarget()) + device->setExclusiveGrabber(nullptr); + } + } else { + for (WInputDevice *device : std::as_const(d->deviceList)) { + if (!device->exclusiveGrabber()) { + // Restore + device->setExclusiveGrabber(device->hoverTarget()); + } + } + } + + Q_EMIT alwaysUpdateHoverTargetChanged(); +} + void WSeat::notifyMotion(WCursor *cursor, WInputDevice *device, uint32_t timestamp) { W_D(WSeat); @@ -1434,6 +1471,17 @@ bool WSeat::filterEventBeforeDisposeStage(QWindow *targetWindow, QInputEvent *ev d->addEventState(event); + if (event->isPointerEvent()) { + auto pe = static_cast(event); + if (pe->isEndEvent()) { + auto device = WInputDevice::from(event->device()); + if (!device->exclusiveGrabber()) { + // Restore the grabber, See alwaysUpdateHoverTarget + device->setExclusiveGrabber(device->hoverTarget()); + } + } + } + if (Q_UNLIKELY(d->eventFilter)) { if (d->eventFilter->beforeDisposeEvent(this, targetWindow, event)) { if (event->type() == QEvent::MouseMove || event->type() == QEvent::HoverMove) { @@ -1444,6 +1492,7 @@ bool WSeat::filterEventBeforeDisposeStage(QWindow *targetWindow, QInputEvent *ev // because the QQuickDeliveryAgent can't get the real last mouse // position, the QQuickWindowPrivate::lastMousePosition is error. if (QQuickWindow *qw = qobject_cast(targetWindow)) { + Q_ASSERT(event->isSinglePointEvent()); const auto pos = static_cast(event)->position(); QQuickWindowPrivate::get(qw)->deliveryAgentPrivate()->lastMousePosition = pos; } @@ -1456,6 +1505,20 @@ bool WSeat::filterEventBeforeDisposeStage(QWindow *targetWindow, QInputEvent *ev return false; } +bool WSeat::filterEventBeforeDisposeStage(QObject *target, QInputEvent *event) +{ + if (event->type() == QEvent::HoverEnter) { + auto ie = WInputDevice::from(event->device()); + ie->setHoverTarget(target); + } else if (event->type() == QEvent::HoverLeave) { + auto ie = WInputDevice::from(event->device()); + if (ie->hoverTarget() == target) + ie->setHoverTarget(nullptr); + } + + return false; +} + bool WSeat::filterEventAfterDisposeStage(QWindow *targetWindow, QInputEvent *event) { W_D(WSeat); @@ -1465,6 +1528,20 @@ bool WSeat::filterEventAfterDisposeStage(QWindow *targetWindow, QInputEvent *eve if (event->isAccepted() || d->pendingEvents.at(eventStateIndex).isAccepted) { d->pendingEvents.removeAt(eventStateIndex); + + if (event->isPointerEvent()) { + auto pe = static_cast(event); + + // Qt will auto grab the pointer event for QQuickItem when mouse pressed + // until mouse released. But we want always update the HoverEnter/Leave's + // WSurfaceItem between drag move. + if (pe->isBeginEvent()) { + auto ie = WInputDevice::from(event->device()); + if (ie->exclusiveGrabber() == ie->hoverTarget()) + ie->setExclusiveGrabber(nullptr); + } + } + return false; } diff --git a/src/server/kernel/wseat.h b/src/server/kernel/wseat.h index bc321fad..0188bb63 100644 --- a/src/server/kernel/wseat.h +++ b/src/server/kernel/wseat.h @@ -57,6 +57,7 @@ class WAYLIB_SERVER_EXPORT WSeat : public WWrapObject, public WServerInterface W_DECLARE_PRIVATE(WSeat) Q_PROPERTY(WInputDevice* keyboard READ keyboard WRITE setKeyboard NOTIFY keyboardChanged FINAL) Q_PROPERTY(WSurface* keyboardFocus READ keyboardFocusSurface WRITE setKeyboardFocusSurface NOTIFY keyboardFocusSurfaceChanged FINAL) + Q_PROPERTY(bool alwaysUpdateHoverTarget READ alwaysUpdateHoverTarget WRITE setAlwaysUpdateHoverTarget NOTIFY alwaysUpdateHoverTargetChanged FINAL) public: WSeat(const QString &name = QStringLiteral("seat0")); @@ -99,12 +100,16 @@ class WAYLIB_SERVER_EXPORT WSeat : public WWrapObject, public WServerInterface WInputDevice *keyboard() const; void setKeyboard(WInputDevice *newKeyboard); + bool alwaysUpdateHoverTarget() const; + void setAlwaysUpdateHoverTarget(bool newIgnoreSurfacePointerEventExclusiveGrabber); + Q_SIGNALS: void keyboardChanged(); void keyboardFocusSurfaceChanged(); void requestCursorShape(WAYLIB_SERVER_NAMESPACE::WGlobal::CursorShape shape); void requestCursorSurface(WAYLIB_SERVER_NAMESPACE::WSurface *surface, const QPoint &hotspot); void requestDrag(WAYLIB_SERVER_NAMESPACE::WSurface *surface); + void alwaysUpdateHoverTargetChanged(); protected: using QObject::eventFilter; @@ -114,6 +119,7 @@ class WAYLIB_SERVER_EXPORT WSeat : public WWrapObject, public WServerInterface friend class QWlrootsRenderWindow; friend class WEventJunkman; friend class WCursorShapeManagerV1; + friend class WOutputRenderWindow; void create(WServer *server) override; void destroy(WServer *server) override; @@ -122,6 +128,7 @@ class WAYLIB_SERVER_EXPORT WSeat : public WWrapObject, public WServerInterface // for event filter bool filterEventBeforeDisposeStage(QWindow *targetWindow, QInputEvent *event); + bool filterEventBeforeDisposeStage(QObject *target, QInputEvent *event); bool filterEventAfterDisposeStage(QWindow *targetWindow, QInputEvent *event); bool filterUnacceptedEvent(QWindow *targetWindow, QInputEvent *event); diff --git a/src/server/qtquick/woutputrenderwindow.cpp b/src/server/qtquick/woutputrenderwindow.cpp index 7f5be6ba..94d8954d 100644 --- a/src/server/qtquick/woutputrenderwindow.cpp +++ b/src/server/qtquick/woutputrenderwindow.cpp @@ -13,6 +13,8 @@ #include "wbufferrenderer_p.h" #include "wquicktextureproxy.h" #include "weventjunkman.h" +#include "winputdevice.h" +#include "wseat.h" #include "platformplugin/qwlrootsintegration.h" #include "platformplugin/qwlrootscreen.h" @@ -1518,10 +1520,14 @@ WOutputRenderWindow::WOutputRenderWindow(QObject *parent) // see [QQuickApplicationWindow](qt6/qtdeclarative/src/quicktemplates/qquickapplicationwindow.cpp) contentItem()->setFlag(QQuickItem::ItemIsFocusScope); contentItem()->setFocus(true); + + qGuiApp->installEventFilter(this); } WOutputRenderWindow::~WOutputRenderWindow() { + qGuiApp->removeEventFilter(this); + renderControl()->disconnect(this); renderControl()->invalidate(); renderControl()->deleteLater(); @@ -1939,6 +1945,20 @@ bool WOutputRenderWindow::event(QEvent *event) return isAccepted; } +bool WOutputRenderWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (event->isInputEvent()) { + auto ie = static_cast(event); + auto device = WInputDevice::from(ie->device()); + Q_ASSERT(device); + Q_ASSERT(device->seat()); + if (device->seat()->filterEventBeforeDisposeStage(watched, ie)) + return true; + } + + return QQuickWindow::eventFilter(watched, event); +} + WAYLIB_SERVER_END_NAMESPACE #include "moc_woutputrenderwindow.cpp" diff --git a/src/server/qtquick/woutputrenderwindow.h b/src/server/qtquick/woutputrenderwindow.h index 04a07ec0..a3b10861 100644 --- a/src/server/qtquick/woutputrenderwindow.h +++ b/src/server/qtquick/woutputrenderwindow.h @@ -83,6 +83,7 @@ public Q_SLOTS: void componentComplete() override; bool event(QEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; friend class WOutputViewport; QList layers(const WOutputViewport *output) const;