KWin
Loading...
Searching...
No Matches
xdgactivationv1.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "xdgactivationv1.h"
12#include "utils/common.h"
14#include "wayland/display.h"
16#include "wayland/surface.h"
18#include "wayland_server.h"
19#include "window.h"
20#include "workspace.h"
21#include <KApplicationTrader>
22#include <KDesktopFile>
23
24namespace KWin
25{
26
27static bool isPrivilegedInWindowManagement(const ClientConnection *client)
28{
29 Q_ASSERT(client);
30 auto requestedInterfaces = client->property("requestedInterfaces").toStringList();
31 return requestedInterfaces.contains(QLatin1String("org_kde_plasma_window_management")) || requestedInterfaces.contains(QLatin1String("kde_lockscreen_overlay_v1"));
32}
33
34static const QString windowDesktopFileName(Window *window)
35{
36 QString ret = window->desktopFileName();
37 if (!ret.isEmpty()) {
38 return ret;
39 }
40
41 // Fallback to StartupWMClass for legacy apps
42 const auto resourceName = window->resourceName();
43 const auto service = KApplicationTrader::query([&resourceName](const KService::Ptr &service) {
44 return service->property<QString>("StartupWMClass").compare(resourceName, Qt::CaseInsensitive) == 0;
45 });
46
47 if (!service.isEmpty()) {
48 ret = service.constFirst()->desktopEntryName();
49 }
50 return ret;
51}
52
54 : QObject(parent)
55{
57 connect(ws, &Workspace::windowActivated, this, [this](Window *window) {
58 if (!m_currentActivationToken || !window || window->property("token").toString() == m_currentActivationToken->token) {
59 return;
60 }
61
62 // We check that it's not the app that we are trying to activate
63 if (windowDesktopFileName(window) != m_currentActivationToken->applicationId) {
64 // But also that the new one has been requested after the token was requested
65 if (window->lastUsageSerial() < m_currentActivationToken->serial) {
66 return;
67 }
68 }
69
70 clear();
71 });
72 activation->setActivationTokenCreator([this](ClientConnection *client, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId) -> QString {
74 Q_ASSERT(client); // Should always be available as it's coming straight from the wayland implementation
75 const bool isPrivileged = isPrivilegedInWindowManagement(client);
76 if (!isPrivileged && ws->activeWindow() && ws->activeWindow()->surface() != surface) {
77 qCWarning(KWIN_CORE) << "Cannot grant a token to" << client;
78 return QStringLiteral("not-granted-666");
79 }
80
81 return requestToken(isPrivileged, surface, serial, seat, appId);
82 });
83
85}
86
87QString XdgActivationV1Integration::requestToken(bool isPrivileged, SurfaceInterface *surface, uint serial, SeatInterface *seat, const QString &appId)
88{
89 static int i = 0;
90 const auto newToken = QStringLiteral("kwin-%1").arg(++i);
91
92 if (m_currentActivationToken) {
93 clear();
94 }
95 bool showNotify = false;
96 QIcon icon = QIcon::fromTheme(QStringLiteral("system-run"));
97 if (const QString desktopFilePath = Window::findDesktopFile(appId); !desktopFilePath.isEmpty()) {
98 KDesktopFile df(desktopFilePath);
99 Window *window = Workspace::self()->activeWindow();
100 if (!window || appId != window->desktopFileName()) {
101 const auto desktop = df.desktopGroup();
102 showNotify = desktop.readEntry("X-KDE-StartupNotify", desktop.readEntry("StartupNotify", true));
103 }
104 icon = QIcon::fromTheme(df.readIcon(), icon);
105 }
106 std::unique_ptr<PlasmaWindowActivationInterface> activation;
107 if (showNotify) {
109 }
110 m_currentActivationToken = std::make_unique<ActivationToken>(ActivationToken{newToken, isPrivileged, surface, serial, seat, appId, showNotify, std::move(activation)});
111 if (showNotify) {
112 Q_EMIT effects->startupAdded(m_currentActivationToken->token, icon);
113 }
114 return newToken;
115}
116
117void XdgActivationV1Integration::activateSurface(SurfaceInterface *surface, const QString &token)
118{
119 Workspace *ws = Workspace::self();
120 auto window = waylandServer()->findWindow(surface);
121 if (!window) {
122 qCWarning(KWIN_CORE) << "could not find the toplevel to activate" << surface;
123 return;
124 }
125
126 if (!m_currentActivationToken || m_currentActivationToken->token != token) {
127 qCDebug(KWIN_CORE) << "Refusing to activate " << window << " (provided token: " << token << ", current token:" << (m_currentActivationToken ? m_currentActivationToken->token : QStringLiteral("null")) << ")";
128 window->demandAttention();
129 return;
130 }
131
132 auto ownerWindow = waylandServer()->findWindow(m_currentActivationToken->surface);
133 qCDebug(KWIN_CORE) << "activating" << window << surface << "on behalf of" << m_currentActivationToken->surface << "into" << ownerWindow;
134 if (!ws->activeWindow() || ws->activeWindow() == ownerWindow || ws->activeWindow()->lastUsageSerial() < m_currentActivationToken->serial || m_currentActivationToken->isPrivileged) {
135 ws->activateWindow(window);
136 } else {
137 qCWarning(KWIN_CORE) << "Activation requested while owner isn't active" << (ownerWindow ? ownerWindow->desktopFileName() : "null")
138 << m_currentActivationToken->applicationId;
139 window->demandAttention();
140 clear();
141 }
142}
143
144void XdgActivationV1Integration::clear()
145{
146 Q_ASSERT(m_currentActivationToken);
147 if (m_currentActivationToken->showNotify) {
148 Q_EMIT effects->startupRemoved(m_currentActivationToken->token);
149 }
150 m_currentActivationToken.reset();
151}
152
153}
154
155#include "moc_xdgactivationv1.cpp"
Convenient Class which represents a wl_client.
void startupRemoved(const QString &id)
void startupAdded(const QString &id, const QIcon &icon)
std::unique_ptr< PlasmaWindowActivationInterface > createActivation(const QString &app_id)
Represents a Seat on the Wayland Display.
Definition seat.h:134
Resource representing a wl_surface.
Definition surface.h:80
Window * findWindow(const SurfaceInterface *surface) const
PlasmaWindowActivationFeedbackInterface * plasmaActivationFeedback() const
SurfaceInterface * surface() const
Definition window.cpp:342
quint32 lastUsageSerial() const
Definition window.cpp:4162
Window * activeWindow() const
Definition workspace.h:767
void activateWindow(Window *window, bool force=false)
void windowActivated(KWin::Window *)
static Workspace * self()
Definition workspace.h:91
void activateSurface(SurfaceInterface *surface, const QString &token)
XdgActivationV1Integration(XdgActivationV1Interface *activation, QObject *parent)
void activateRequested(SurfaceInterface *surface, const QString &token)
Notifies about the surface being activated using token.
KWayland::Client::Seat * seat
WaylandServer * waylandServer()
EffectsHandler * effects