669 lines
25 KiB
Diff
669 lines
25 KiB
Diff
From 9f6c92806490d662117575a766f9fcb01e253344 Mon Sep 17 00:00:00 2001
|
|
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
Date: Tue, 22 Jul 2025 23:24:51 +0200
|
|
Subject: [PATCH 1/4] xdgactivation: move the activation token to workspace
|
|
|
|
---
|
|
src/activation.cpp | 14 ++++++++++++
|
|
src/workspace.h | 6 +++++
|
|
src/xdgactivationv1.cpp | 50 +++++++++--------------------------------
|
|
src/xdgactivationv1.h | 14 ++----------
|
|
4 files changed, 32 insertions(+), 52 deletions(-)
|
|
|
|
diff --git a/src/activation.cpp b/src/activation.cpp
|
|
index 60b921247ce..9e743b8b098 100644
|
|
--- a/src/activation.cpp
|
|
+++ b/src/activation.cpp
|
|
@@ -630,4 +630,18 @@ void Workspace::windowAttentionChanged(Window *window, bool set)
|
|
}
|
|
}
|
|
|
|
+void Workspace::setActivationToken(const QString &token, uint32_t serial)
|
|
+{
|
|
+ m_activationToken = token;
|
|
+ m_activationTokenSerial = serial;
|
|
+}
|
|
+
|
|
+bool Workspace::mayActivate(const QString &token) const
|
|
+{
|
|
+ if (!m_activeWindow) {
|
|
+ return true;
|
|
+ }
|
|
+ return !m_activationToken.isEmpty() && token == m_activationToken && m_activeWindow->lastUsageSerial() <= m_activationTokenSerial;
|
|
+}
|
|
+
|
|
} // namespace
|
|
diff --git a/src/workspace.h b/src/workspace.h
|
|
index 2082bbe148d..03c54e06750 100644
|
|
--- a/src/workspace.h
|
|
+++ b/src/workspace.h
|
|
@@ -436,6 +436,9 @@ public:
|
|
OutputConfigurationError applyOutputConfiguration(OutputConfiguration &config, const std::optional<QList<Output *>> &outputOrder = std::nullopt);
|
|
void updateXwaylandScale();
|
|
|
|
+ void setActivationToken(const QString &token, uint32_t serial);
|
|
+ bool mayActivate(const QString &token) const;
|
|
+
|
|
public Q_SLOTS:
|
|
void performWindowOperation(KWin::Window *window, Options::WindowOperation op);
|
|
// Keybindings
|
|
@@ -729,6 +732,9 @@ private:
|
|
std::unique_ptr<DpmsInputEventFilter> m_dpmsFilter;
|
|
KConfigWatcher::Ptr m_kdeglobalsWatcher;
|
|
|
|
+ QString m_activationToken;
|
|
+ uint32_t m_activationTokenSerial = 0;
|
|
+
|
|
private:
|
|
friend bool performTransiencyCheck();
|
|
friend Workspace *workspace();
|
|
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
index 39dade95332..a6be350399a 100644
|
|
--- a/src/xdgactivationv1.cpp
|
|
+++ b/src/xdgactivationv1.cpp
|
|
@@ -33,22 +33,6 @@ static bool isPrivilegedInWindowManagement(const ClientConnection *client)
|
|
XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface *activation, QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
- Workspace *ws = Workspace::self();
|
|
- connect(ws, &Workspace::windowActivated, this, [this](Window *window) {
|
|
- if (!m_currentActivationToken || !window || window->property("token").toString() == m_currentActivationToken->token) {
|
|
- return;
|
|
- }
|
|
-
|
|
- // We check that it's not the app that we are trying to activate
|
|
- if (window->desktopFileName() != m_currentActivationToken->applicationId) {
|
|
- // But also that the new one has been requested after the token was requested
|
|
- if (window->lastUsageSerial() < m_currentActivationToken->serial) {
|
|
- return;
|
|
- }
|
|
- }
|
|
-
|
|
- clear();
|
|
- });
|
|
activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
|
|
Workspace *ws = Workspace::self();
|
|
Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
|
|
@@ -69,9 +53,6 @@ QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInter
|
|
static int i = 0;
|
|
const auto newToken = QStringLiteral("kwin-%1").arg(++i);
|
|
|
|
- if (m_currentActivationToken) {
|
|
- clear();
|
|
- }
|
|
bool showNotify = false;
|
|
QIcon icon = QIcon::fromTheme(QStringLiteral("system-run"));
|
|
if (const QString desktopFilePath = Window::findDesktopFile(appId); !desktopFilePath.isEmpty()) {
|
|
@@ -83,13 +64,13 @@ QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInter
|
|
}
|
|
icon = QIcon::fromTheme(df.readIcon(), icon);
|
|
}
|
|
- std::unique_ptr<PlasmaWindowActivationInterface> activation;
|
|
if (showNotify) {
|
|
- activation = waylandServer()->plasmaActivationFeedback()->createActivation(appId);
|
|
+ m_lastToken = newToken;
|
|
+ m_activation = waylandServer()->plasmaActivationFeedback()->createActivation(appId);
|
|
}
|
|
- m_currentActivationToken = std::make_unique<ActivationToken>(ActivationToken{newToken, isPrivileged, surface, serial, seat, appId, showNotify, std::move(activation)});
|
|
+ workspace()->setActivationToken(newToken, serial);
|
|
if (showNotify) {
|
|
- Q_EMIT effects->startupAdded(m_currentActivationToken->token, icon);
|
|
+ Q_EMIT effects->startupAdded(newToken, icon);
|
|
}
|
|
return newToken;
|
|
}
|
|
@@ -103,31 +84,20 @@ void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, cons
|
|
return;
|
|
}
|
|
|
|
- if (!m_currentActivationToken || m_currentActivationToken->token != token) {
|
|
- qCDebug(KWIN_CORE) << "Refusing to activate " << window << " (provided token: " << token << ", current token:" << (m_currentActivationToken ? m_currentActivationToken->token : QStringLiteral("null")) << ")";
|
|
+ if (!ws->mayActivate(token)) {
|
|
window->demandAttention();
|
|
return;
|
|
}
|
|
-
|
|
- auto ownerWindow = waylandServer()->findWindow(m_currentActivationToken->surface);
|
|
- qCDebug(KWIN_CORE) << "activating" << window << surface << "on behalf of" << m_currentActivationToken->surface << "into" << ownerWindow;
|
|
- if (!ws->activeWindow() || ws->activeWindow() == ownerWindow || ws->activeWindow()->lastUsageSerial() < m_currentActivationToken->serial || m_currentActivationToken->isPrivileged) {
|
|
- ws->activateWindow(window);
|
|
- } else {
|
|
- qCWarning(KWIN_CORE) << "Activation requested while owner isn't active" << (ownerWindow ? ownerWindow->desktopFileName() : "null")
|
|
- << m_currentActivationToken->applicationId;
|
|
- window->demandAttention();
|
|
- clear();
|
|
- }
|
|
+ ws->activateWindow(window);
|
|
+ clear();
|
|
}
|
|
|
|
void XdgActivationV1Integration::clear()
|
|
{
|
|
- Q_ASSERT(m_currentActivationToken);
|
|
- if (m_currentActivationToken->showNotify) {
|
|
- Q_EMIT effects->startupRemoved(m_currentActivationToken->token);
|
|
+ if (m_activation) {
|
|
+ Q_EMIT effects->startupRemoved(m_lastToken);
|
|
+ m_activation.reset();
|
|
}
|
|
- m_currentActivationToken.reset();
|
|
}
|
|
|
|
}
|
|
diff --git a/src/xdgactivationv1.h b/src/xdgactivationv1.h
|
|
index 98835def8aa..77d21856095 100644
|
|
--- a/src/xdgactivationv1.h
|
|
+++ b/src/xdgactivationv1.h
|
|
@@ -38,18 +38,8 @@ private:
|
|
QString requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId);
|
|
void clear();
|
|
|
|
- struct ActivationToken
|
|
- {
|
|
- QString token;
|
|
- bool isPrivileged;
|
|
- QPointer<const SurfaceInterface> surface;
|
|
- uint serial;
|
|
- SeatInterface *seat;
|
|
- QString applicationId;
|
|
- bool showNotify;
|
|
- std::unique_ptr<PlasmaWindowActivationInterface> activation;
|
|
- };
|
|
- std::unique_ptr<ActivationToken> m_currentActivationToken;
|
|
+ QString m_lastToken;
|
|
+ std::unique_ptr<PlasmaWindowActivationInterface> m_activation;
|
|
};
|
|
|
|
}
|
|
--
|
|
GitLab
|
|
|
|
|
|
From 6c673a479412902a14c06046199f976e2192dc65 Mon Sep 17 00:00:00 2001
|
|
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
Date: Tue, 22 Jul 2025 23:43:14 +0200
|
|
Subject: [PATCH 2/4] xdgactivation: also allow using activation tokens before
|
|
the window is mapped
|
|
|
|
We just store the token in the window, and then Workspace uses it to decide whether
|
|
or not to activate the window when it's actually shown.
|
|
---
|
|
src/window.cpp | 10 ++++++++++
|
|
src/window.h | 5 +++++
|
|
src/workspace.cpp | 4 ++--
|
|
src/xdgactivationv1.cpp | 6 +++++-
|
|
4 files changed, 22 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/src/window.cpp b/src/window.cpp
|
|
index 4a97a54aa74..879147ec673 100644
|
|
--- a/src/window.cpp
|
|
+++ b/src/window.cpp
|
|
@@ -4675,6 +4675,16 @@ void Window::setDescription(const QString &description)
|
|
}
|
|
}
|
|
|
|
+void Window::setActivationToken(const QString &token)
|
|
+{
|
|
+ m_activationToken = token;
|
|
+}
|
|
+
|
|
+QString Window::activationToken() const
|
|
+{
|
|
+ return m_activationToken;
|
|
+}
|
|
+
|
|
} // namespace KWin
|
|
|
|
#include "moc_window.cpp"
|
|
diff --git a/src/window.h b/src/window.h
|
|
index 1eb371018ca..91addb46819 100644
|
|
--- a/src/window.h
|
|
+++ b/src/window.h
|
|
@@ -1357,6 +1357,9 @@ public:
|
|
QString tag() const;
|
|
QString description() const;
|
|
|
|
+ void setActivationToken(const QString &token);
|
|
+ QString activationToken() const;
|
|
+
|
|
public Q_SLOTS:
|
|
virtual void closeWindow() = 0;
|
|
|
|
@@ -1880,6 +1883,8 @@ protected:
|
|
|
|
QString m_tag;
|
|
QString m_description;
|
|
+
|
|
+ QString m_activationToken;
|
|
};
|
|
|
|
inline QRectF Window::bufferGeometry() const
|
|
diff --git a/src/workspace.cpp b/src/workspace.cpp
|
|
index 3a5cb12677b..866abb8080d 100644
|
|
--- a/src/workspace.cpp
|
|
+++ b/src/workspace.cpp
|
|
@@ -781,8 +781,8 @@ void Workspace::addWaylandWindow(Window *window)
|
|
rearrange();
|
|
}
|
|
if (window->wantsInput() && !window->isMinimized()) {
|
|
- // Never activate a window on its own in "Extreme" mode.
|
|
- if (options->focusStealingPreventionLevel() < 4) {
|
|
+ // In "Extreme" mode, require an activation token to activate new windows
|
|
+ if (options->focusStealingPreventionLevel() < 4 || (!m_activationToken.isEmpty() && window->activationToken() == m_activationToken)) {
|
|
if (!window->isDesktop()
|
|
// If there's no active window, make this desktop the active one.
|
|
|| (activeWindow() == nullptr && should_get_focus.count() == 0)) {
|
|
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
index a6be350399a..11c95c84b32 100644
|
|
--- a/src/xdgactivationv1.cpp
|
|
+++ b/src/xdgactivationv1.cpp
|
|
@@ -88,7 +88,11 @@ void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, cons
|
|
window->demandAttention();
|
|
return;
|
|
}
|
|
- ws->activateWindow(window);
|
|
+ if (window->readyForPainting()) {
|
|
+ ws->activateWindow(window);
|
|
+ } else {
|
|
+ window->setActivationToken(token);
|
|
+ }
|
|
clear();
|
|
}
|
|
|
|
--
|
|
GitLab
|
|
|
|
|
|
From 2436625a66257f586a0934c3d678c910bbdb3705 Mon Sep 17 00:00:00 2001
|
|
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
Date: Wed, 23 Jul 2025 13:18:39 +0200
|
|
Subject: [PATCH 3/4] xdgactivation: move the "not granted" token to
|
|
requestToken
|
|
|
|
Having some but not all checks in that method is a bit confusing
|
|
---
|
|
src/xdgactivationv1.cpp | 14 ++++++--------
|
|
1 file changed, 6 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/src/xdgactivationv1.cpp b/src/xdgactivationv1.cpp
|
|
index 11c95c84b32..e818466f8ad 100644
|
|
--- a/src/xdgactivationv1.cpp
|
|
+++ b/src/xdgactivationv1.cpp
|
|
@@ -34,15 +34,8 @@ XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface
|
|
: QObject(parent)
|
|
{
|
|
activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
|
|
- Workspace *ws = Workspace::self();
|
|
Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
|
|
- const bool isPrivileged = isPrivilegedInWindowManagement(client);
|
|
- if (!isPrivileged && ws->activeWindow() && ws->activeWindow()->surface() != surface) {
|
|
- qCDebug(KWIN_CORE) << "Cannot grant a token to" << client;
|
|
- return QStringLiteral("not-granted-666");
|
|
- }
|
|
-
|
|
- return requestToken(isPrivileged, surface, serial, seat, appId);
|
|
+ return requestToken(isPrivilegedInWindowManagement(client), surface, serial, seat, appId);
|
|
});
|
|
|
|
connect(activation, &XdgActivationV1Interface::activateRequested, this, &XdgActivationV1Integration::activateSurface);
|
|
@@ -50,6 +43,11 @@ XdgActivationV1Integration::XdgActivationV1Integration(XdgActivationV1Interface
|
|
|
|
QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId)
|
|
{
|
|
+ auto window = waylandServer()->findWindow(surface);
|
|
+ if (!isPrivileged && workspace()->activeWindow() && workspace()->activeWindow()->surface() != surface) {
|
|
+ qCWarning(KWIN_CORE) << "Cannot grant a token to" << window;
|
|
+ return QStringLiteral("not-granted-666");
|
|
+ }
|
|
static int i = 0;
|
|
const auto newToken = QStringLiteral("kwin-%1").arg(++i);
|
|
|
|
--
|
|
GitLab
|
|
|
|
|
|
From 8508b8060813c07fe035801381bad1f9a375acf0 Mon Sep 17 00:00:00 2001
|
|
From: Xaver Hugl <xaver.hugl@kde.org>
|
|
Date: Thu, 24 Jul 2025 20:43:46 +0200
|
|
Subject: [PATCH 4/4] autotests/integration: add a test case for xdg activation
|
|
|
|
---
|
|
autotests/integration/CMakeLists.txt | 3 +-
|
|
autotests/integration/activation_test.cpp | 126 +++++++++++++++++++++-
|
|
autotests/integration/kwin_wayland_test.h | 31 ++++++
|
|
autotests/integration/test_helpers.cpp | 50 +++++++++
|
|
4 files changed, 208 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt
|
|
index 13b0cde04f5..408af6ac8b8 100644
|
|
--- a/autotests/integration/CMakeLists.txt
|
|
+++ b/autotests/integration/CMakeLists.txt
|
|
@@ -16,13 +16,14 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
|
|
FILES
|
|
${CMAKE_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
|
|
${WaylandProtocols_DATADIR}/stable/presentation-time/presentation-time.xml
|
|
+ ${WaylandProtocols_DATADIR}/stable/tablet/tablet-v2.xml
|
|
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
|
|
${WaylandProtocols_DATADIR}/staging/color-management/color-management-v1.xml
|
|
- ${WaylandProtocols_DATADIR}/stable/tablet/tablet-v2.xml
|
|
${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml
|
|
${WaylandProtocols_DATADIR}/staging/fifo/fifo-v1.xml
|
|
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
|
|
${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
|
|
+ ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
|
|
${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
|
${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
|
|
${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml
|
|
diff --git a/autotests/integration/activation_test.cpp b/autotests/integration/activation_test.cpp
|
|
index 75c9e7e8c7b..30f671d6fe2 100644
|
|
--- a/autotests/integration/activation_test.cpp
|
|
+++ b/autotests/integration/activation_test.cpp
|
|
@@ -15,6 +15,7 @@
|
|
#include "workspace.h"
|
|
#include "x11window.h"
|
|
|
|
+#include <KWayland/Client/seat.h>
|
|
#include <KWayland/Client/surface.h>
|
|
#include <netwm.h>
|
|
#include <xcb/xcb_icccm.h>
|
|
@@ -40,6 +41,7 @@ private Q_SLOTS:
|
|
void testSwitchToWindowMaximized();
|
|
void testSwitchToWindowFullScreen();
|
|
void testActiveFullscreen();
|
|
+ void testXdgActivation();
|
|
|
|
private:
|
|
void stackScreensHorizontally();
|
|
@@ -64,7 +66,7 @@ void ActivationTest::initTestCase()
|
|
|
|
void ActivationTest::init()
|
|
{
|
|
- QVERIFY(Test::setupWaylandConnection());
|
|
+ QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgActivation | Test::AdditionalWaylandInterface::Seat));
|
|
|
|
workspace()->setActiveOutput(QPoint(640, 512));
|
|
input()->pointer()->warp(QPoint(640, 512));
|
|
@@ -592,6 +594,128 @@ void ActivationTest::testActiveFullscreen()
|
|
QCOMPARE(workspace()->activeWindow(), waylandWindow);
|
|
QCOMPARE(x11Window->layer(), Layer::NormalLayer);
|
|
}
|
|
+
|
|
+void ActivationTest::testXdgActivation()
|
|
+{
|
|
+ Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
|
|
+
|
|
+ uint32_t time = 0;
|
|
+
|
|
+ std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
|
+ std::vector<std::unique_ptr<Test::XdgToplevel>> shellSurfaces;
|
|
+ std::vector<Window *> windows;
|
|
+ const auto setupWindows = [&]() {
|
|
+ windows.clear();
|
|
+ shellSurfaces.clear();
|
|
+ surfaces.clear();
|
|
+
|
|
+ // re-create the same setup every time for reduced confusion
|
|
+ for (int i = 0; i < 3; i++) {
|
|
+ surfaces.push_back(Test::createSurface());
|
|
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get()));
|
|
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
+ windows.back()->move(QPoint(150 * i, 0));
|
|
+
|
|
+ Test::pointerMotion(windows.back()->frameGeometry().center(), time++);
|
|
+ Test::pointerButtonPressed(1, time++);
|
|
+ Test::pointerButtonReleased(1, time++);
|
|
+ }
|
|
+ };
|
|
+ setupWindows();
|
|
+
|
|
+ QSignalSpy activationSpy(workspace(), &Workspace::windowActivated);
|
|
+
|
|
+ // activating a window without a valid token should fail
|
|
+ Test::xdgActivation()->activate(QString(), *surfaces[1]);
|
|
+ QVERIFY(!activationSpy.wait(10));
|
|
+
|
|
+ // activating it without a surface should fail as well, even if a serial is present
|
|
+ auto token = Test::xdgActivation()->createToken();
|
|
+ token->set_serial(windows.back()->lastUsageSerial(), *Test::waylandSeat());
|
|
+ Test::xdgActivation()->activate(token->commitAndWait(), *surfaces[1]);
|
|
+ QVERIFY(!activationSpy.wait(10));
|
|
+
|
|
+ // adding the surface should make it work
|
|
+ token = Test::xdgActivation()->createToken();
|
|
+ token->set_surface(*surfaces.back());
|
|
+ token->set_serial(windows.back()->lastUsageSerial(), *Test::waylandSeat());
|
|
+ Test::xdgActivation()->activate(token->commitAndWait(), *surfaces[1]);
|
|
+ QVERIFY(activationSpy.wait(10));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
+
|
|
+ // activation should still work if the window is closed after creating the token
|
|
+ setupWindows();
|
|
+ token = Test::xdgActivation()->createToken();
|
|
+ token->set_surface(*surfaces[2]);
|
|
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
+ QString result = token->commitAndWait();
|
|
+
|
|
+ surfaces[2]->attachBuffer((wl_buffer *)nullptr);
|
|
+ surfaces[2]->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
+ QVERIFY(activationSpy.wait(10));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
+
|
|
+ Test::xdgActivation()->activate(result, *surfaces[0]);
|
|
+ QVERIFY(activationSpy.wait(10));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[0]);
|
|
+
|
|
+ // ...unless the user interacted with another window in between
|
|
+ setupWindows();
|
|
+ token = Test::xdgActivation()->createToken();
|
|
+ token->set_surface(*surfaces[2]);
|
|
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
+ result = token->commitAndWait();
|
|
+
|
|
+ surfaces[2]->attachBuffer((wl_buffer *)nullptr);
|
|
+ surfaces[2]->commit(KWayland::Client::Surface::CommitFlag::None);
|
|
+ QVERIFY(activationSpy.wait(10));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
+
|
|
+ Test::pointerMotion(windows[1]->frameGeometry().center(), time++);
|
|
+ Test::pointerButtonPressed(1, time++);
|
|
+ Test::pointerButtonReleased(1, time++);
|
|
+
|
|
+ Test::xdgActivation()->activate(result, *surfaces[0]);
|
|
+ QVERIFY(!activationSpy.wait(10));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
+
|
|
+ // ensure that windows are only activated on show with a valid activation token
|
|
+ options->setFocusStealingPreventionLevel(4);
|
|
+
|
|
+ // creating a new window and immediately activating it should work
|
|
+ setupWindows();
|
|
+ token = Test::xdgActivation()->createToken();
|
|
+ token->set_surface(*surfaces[2]);
|
|
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
+ result = token->commitAndWait();
|
|
+ surfaces.push_back(Test::createSurface());
|
|
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get(), [&](Test::XdgToplevel *toplevel) {
|
|
+ Test::xdgActivation()->activate(result, *surfaces.back());
|
|
+ }));
|
|
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows.back());
|
|
+ windows.back()->move(QPoint(150 * 3, 0));
|
|
+
|
|
+ // activation should fail if the user clicks on another window in between
|
|
+ // creating the activation token and using it
|
|
+ setupWindows();
|
|
+ token = Test::xdgActivation()->createToken();
|
|
+ token->set_surface(*surfaces[2]);
|
|
+ token->set_serial(windows[2]->lastUsageSerial(), *Test::waylandSeat());
|
|
+ result = token->commitAndWait();
|
|
+
|
|
+ Test::pointerMotion(windows[1]->frameGeometry().center(), time++);
|
|
+ Test::pointerButtonPressed(1, time++);
|
|
+ Test::pointerButtonReleased(1, time++);
|
|
+
|
|
+ surfaces.push_back(Test::createSurface());
|
|
+ shellSurfaces.push_back(Test::createXdgToplevelSurface(surfaces.back().get(), [&](Test::XdgToplevel *toplevel) {
|
|
+ Test::xdgActivation()->activate(result, *surfaces.back());
|
|
+ }));
|
|
+ windows.push_back(Test::renderAndWaitForShown(surfaces.back().get(), QSize(100, 50), Qt::blue));
|
|
+ QCOMPARE(workspace()->activeWindow(), windows[1]);
|
|
+ windows.back()->move(QPoint(150 * 3, 0));
|
|
+}
|
|
}
|
|
|
|
WAYLANDTEST_MAIN(KWin::ActivationTest)
|
|
diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h
|
|
index b0f63dadbc5..a6616189617 100644
|
|
--- a/autotests/integration/kwin_wayland_test.h
|
|
+++ b/autotests/integration/kwin_wayland_test.h
|
|
@@ -34,6 +34,7 @@
|
|
#include "qwayland-security-context-v1.h"
|
|
#include "qwayland-text-input-unstable-v3.h"
|
|
#include "qwayland-wlr-layer-shell-unstable-v1.h"
|
|
+#include "qwayland-xdg-activation-v1.h"
|
|
#include "qwayland-xdg-decoration-unstable-v1.h"
|
|
#include "qwayland-xdg-dialog-v1.h"
|
|
#include "qwayland-xdg-shell.h"
|
|
@@ -611,6 +612,7 @@ enum class AdditionalWaylandInterface {
|
|
ColorManagement = 1 << 22,
|
|
FifoV1 = 1 << 23,
|
|
PresentationTime = 1 << 24,
|
|
+ XdgActivation = 1 << 25,
|
|
};
|
|
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
|
|
|
|
@@ -717,6 +719,33 @@ private:
|
|
void wp_presentation_feedback_discarded() override;
|
|
};
|
|
|
|
+class XdgActivationToken : public QObject, public QtWayland::xdg_activation_token_v1
|
|
+{
|
|
+ Q_OBJECT
|
|
+public:
|
|
+ explicit XdgActivationToken(::xdg_activation_token_v1 *object);
|
|
+ ~XdgActivationToken() override;
|
|
+
|
|
+ QString commitAndWait();
|
|
+
|
|
+Q_SIGNALS:
|
|
+ void tokenReceived();
|
|
+
|
|
+private:
|
|
+ void xdg_activation_token_v1_done(const QString &token) override;
|
|
+
|
|
+ QString m_token;
|
|
+};
|
|
+
|
|
+class XdgActivation : public QtWayland::xdg_activation_v1
|
|
+{
|
|
+public:
|
|
+ explicit XdgActivation(::wl_registry *registry, uint32_t id, int version);
|
|
+ ~XdgActivation() override;
|
|
+
|
|
+ std::unique_ptr<XdgActivationToken> createToken();
|
|
+};
|
|
+
|
|
struct Connection
|
|
{
|
|
static std::unique_ptr<Connection> setup(AdditionalWaylandInterfaces interfaces = AdditionalWaylandInterfaces());
|
|
@@ -757,6 +786,7 @@ struct Connection
|
|
std::unique_ptr<ColorManagerV1> colorManager;
|
|
std::unique_ptr<FifoManagerV1> fifoManager;
|
|
std::unique_ptr<PresentationTime> presentationTime;
|
|
+ std::unique_ptr<XdgActivation> xdgActivation;
|
|
};
|
|
|
|
void keyboardKeyPressed(quint32 key, quint32 time);
|
|
@@ -821,6 +851,7 @@ SecurityContextManagerV1 *waylandSecurityContextManagerV1();
|
|
ColorManagerV1 *colorManager();
|
|
FifoManagerV1 *fifoManager();
|
|
PresentationTime *presentationTime();
|
|
+XdgActivation *xdgActivation();
|
|
|
|
bool waitForWaylandSurface(Window *window);
|
|
|
|
diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp
|
|
index 22380c947d3..e524f10826d 100644
|
|
--- a/autotests/integration/test_helpers.cpp
|
|
+++ b/autotests/integration/test_helpers.cpp
|
|
@@ -535,6 +535,11 @@ std::unique_ptr<Connection> Connection::setup(AdditionalWaylandInterfaces flags)
|
|
c->presentationTime = std::make_unique<PresentationTime>(*c->registry, name, version);
|
|
}
|
|
}
|
|
+ if (flags & AdditionalWaylandInterface::XdgActivation) {
|
|
+ if (interface == xdg_activation_v1_interface.name) {
|
|
+ c->xdgActivation = std::make_unique<XdgActivation>(*c->registry, name, version);
|
|
+ }
|
|
+ }
|
|
});
|
|
|
|
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
|
|
@@ -665,6 +670,7 @@ Connection::~Connection()
|
|
colorManager.reset();
|
|
fifoManager.reset();
|
|
presentationTime.reset();
|
|
+ xdgActivation.reset();
|
|
|
|
delete queue; // Must be destroyed last
|
|
queue = nullptr;
|
|
@@ -796,6 +802,11 @@ PresentationTime *presentationTime()
|
|
return s_waylandConnection->presentationTime.get();
|
|
}
|
|
|
|
+XdgActivation *xdgActivation()
|
|
+{
|
|
+ return s_waylandConnection->xdgActivation.get();
|
|
+}
|
|
+
|
|
bool waitForWaylandSurface(Window *window)
|
|
{
|
|
if (window->surface()) {
|
|
@@ -1817,6 +1828,45 @@ void WpPresentationFeedback::wp_presentation_feedback_discarded()
|
|
Q_EMIT discarded();
|
|
}
|
|
|
|
+XdgActivationToken::XdgActivationToken(::xdg_activation_token_v1 *object)
|
|
+ : QtWayland::xdg_activation_token_v1(object)
|
|
+{
|
|
+}
|
|
+
|
|
+XdgActivationToken::~XdgActivationToken()
|
|
+{
|
|
+ destroy();
|
|
+}
|
|
+
|
|
+QString XdgActivationToken::commitAndWait()
|
|
+{
|
|
+ QSignalSpy received(this, &XdgActivationToken::tokenReceived);
|
|
+ commit();
|
|
+ received.wait();
|
|
+ return m_token;
|
|
+}
|
|
+
|
|
+void XdgActivationToken::xdg_activation_token_v1_done(const QString &token)
|
|
+{
|
|
+ m_token = token;
|
|
+ Q_EMIT tokenReceived();
|
|
+}
|
|
+
|
|
+XdgActivation::XdgActivation(::wl_registry *registry, uint32_t id, int version)
|
|
+ : QtWayland::xdg_activation_v1(registry, id, version)
|
|
+{
|
|
+}
|
|
+
|
|
+XdgActivation::~XdgActivation()
|
|
+{
|
|
+ destroy();
|
|
+}
|
|
+
|
|
+std::unique_ptr<XdgActivationToken> XdgActivation::createToken()
|
|
+{
|
|
+ return std::make_unique<XdgActivationToken>(get_activation_token());
|
|
+}
|
|
+
|
|
void keyboardKeyPressed(quint32 key, quint32 time)
|
|
{
|
|
auto virtualKeyboard = static_cast<WaylandTestApplication *>(kwinApp())->virtualKeyboard();
|
|
--
|
|
GitLab
|
|
|