From 55d2590abf823f0529ab844ee26f60d0371a95d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9ven=20Car?= Date: Wed, 18 Aug 2021 18:28:20 +0200 Subject: [PATCH] Wayland client: use wl_keyboard to determine active state Commit d4d47f2a043f3e7548ba10c69fb54637511ba563 made QWaylandDisplay use the xdgshell's active state for QWindow::isActive(), instead of using wl_keyboard activate/deactivate events. That seems to have been a misunderstanding, since xdgshell activation is only supposed to be used to determine visual appearance, and there is an explicit warning not to assume it means focus. This commit reverts this logic back to listening to wl_keyboard. It adds a fallback when there is no wl_keyboard available to handle activated/deactivated events through xdg-shell, in order to fix QTBUG-53702. windowStates is handled so that we're not using the Xdg hint for anything with QWindowSystemInterface::handleWindowStateChanged or anything where we need to track only having one active. We are still exposing it for decorations, which is the only reason to use the Xdghint over keyboard focus - so you can keep the toplevel active whilst you show a popup. Change-Id: I4343d2ed9fb5b066cde95628ed0b4ccc84a424db Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../xdg-shell/qwaylandxdgshell.cpp | 16 +++++----------- .../xdg-shell/qwaylandxdgshellintegration.cpp | 14 -------------- .../xdg-shell/qwaylandxdgshellintegration_p.h | 1 - .../platforms/wayland/qwaylanddisplay.cpp | 19 +++++++++++-------- .../platforms/wayland/qwaylanddisplay_p.h | 1 + .../platforms/wayland/qwaylandwindow.cpp | 8 ++++++-- .../qwaylandshellintegration_p.h | 6 ------ tests/auto/wayland/xdgshell/tst_xdgshell.cpp | 10 +++++++--- 8 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 5d9a21f81b2..afb2e3b27a3 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -67,11 +67,6 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) QWaylandXdgSurface::Toplevel::~Toplevel() { - if (m_applied.states & Qt::WindowActive) { - QWaylandWindow *window = m_xdgSurface->window(); - window->display()->handleWindowDeactivated(window); - } - // The protocol spec requires that the decoration object is deleted before xdg_toplevel. delete m_decoration; m_decoration = nullptr; @@ -85,17 +80,16 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) m_normalSize = m_xdgSurface->m_window->windowFrameGeometry().size(); - if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)) + if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive) + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window); - if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)) + if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive) + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); - // TODO: none of the other plugins send WindowActive either, but is it on purpose? - Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; - m_xdgSurface->m_window->handleToplevelWindowTilingStatesChanged(m_toplevelStates); - m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); + m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); if (m_pending.size.isEmpty()) { // An empty size in the configure means it's up to the client to choose the size diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp index b259a301016..fcdd435c6b7 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp @@ -69,20 +69,6 @@ QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWi return m_xdgShell->getXdgSurface(window); } -void QWaylandXdgShellIntegration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) -{ - if (newFocus) { - auto *xdgSurface = qobject_cast(newFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowActivated(newFocus); - } - if (oldFocus && qobject_cast(oldFocus->shellSurface())) { - auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); - if (xdgSurface && !xdgSurface->handlesActiveState()) - m_display->handleWindowDeactivated(oldFocus); - } -} - void *QWaylandXdgShellIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) { if (auto waylandWindow = static_cast(window->handle())) { diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h index ceca0335db0..fced9eb07c2 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h @@ -65,7 +65,6 @@ public: QWaylandXdgShellIntegration() {} bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; - void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; private: diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index aeb5f4b8702..d624fab33f5 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -578,14 +578,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic if (mLastKeyboardFocus == keyboardFocus) return; - if (mWaylandIntegration->mShellIntegration) { - mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus); - } else { - if (keyboardFocus) - handleWindowActivated(keyboardFocus); - if (mLastKeyboardFocus) - handleWindowDeactivated(mLastKeyboardFocus); - } + if (keyboardFocus) + handleWindowActivated(keyboardFocus); + if (mLastKeyboardFocus) + handleWindowDeactivated(mLastKeyboardFocus); mLastKeyboardFocus = keyboardFocus; } @@ -630,6 +626,13 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const return mInputDevices.isEmpty() ? 0 : mInputDevices.first(); } +bool QWaylandDisplay::isKeyboardAvailable() const +{ + return std::any_of( + mInputDevices.constBegin(), mInputDevices.constEnd(), + [this](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); +} + #if QT_CONFIG(cursor) QWaylandCursor *QWaylandDisplay::waylandCursor() diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index a027eb8955a..5fc7aec32f4 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -220,6 +220,7 @@ public: void destroyFrameQueue(const FrameQueue &q); void dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout = -1); + bool isKeyboardAvailable() const; public slots: void blockingReadEvents(); void flushRequests(); diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 5561f58f74c..7f4088aa0a8 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -96,7 +96,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) QWaylandWindow::~QWaylandWindow() { mDisplay->destroyFrameQueue(mFrameQueue); - mDisplay->handleWindowDestroyed(this); delete mWindowDecoration; @@ -265,6 +264,8 @@ void QWaylandWindow::reset() mMask = QRegion(); mQueuedBuffer = nullptr; + + mDisplay->handleWindowDestroyed(this); } QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) @@ -1273,7 +1274,10 @@ Qt::WindowStates QWaylandWindow::windowStates() const void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) { createDecoration(); - QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); + Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive; + Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive; + QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive, + lastStatesWithoutActive); mLastReportedWindowStates = states; } diff --git a/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h index ccad0048192..b308ffe25ee 100644 --- a/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h +++ b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h @@ -73,12 +73,6 @@ public: return true; } virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; - virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { - if (newFocus) - m_display->handleWindowActivated(newFocus); - if (oldFocus) - m_display->handleWindowDeactivated(oldFocus); - } virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { Q_UNUSED(resource); Q_UNUSED(window); diff --git a/tests/auto/wayland/xdgshell/tst_xdgshell.cpp b/tests/auto/wayland/xdgshell/tst_xdgshell.cpp index 962093c75c1..b8b41d1e150 100644 --- a/tests/auto/wayland/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/wayland/xdgshell/tst_xdgshell.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace MockCompositor; @@ -155,9 +156,12 @@ void tst_xdgshell::configureStates() // Toplevel windows don't know their position on xdg-shell // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled -// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); -// QVERIFY(window.isActive()); - QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly + // window.windowstate() is driven by keyboard focus, however for decorations we want to follow + // XDGShell this is internal to QtWayland so it is queried directly + auto waylandWindow = static_cast(window.handle()); + Q_ASSERT(waylandWindow); + QTRY_VERIFY(waylandWindow->windowStates().testFlag( + Qt::WindowActive)); // Just make sure it eventually get's set correctly const QSize screenSize(640, 480); const uint maximizedSerial = exec([=] {