Changeset View
Changeset View
Standalone View
Standalone View
waylandclient.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (C) 2015 Martin Flöser <mgraesslin@kde.org> | ||||
3 | * Copyright (C) 2018 David Edmundson <davidedmundson@kde.org> | ||||
4 | * Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org> | ||||
5 | * | ||||
6 | * This program is free software; you can redistribute it and/or modify | ||||
7 | * it under the terms of the GNU General Public License as published by | ||||
8 | * the Free Software Foundation; either version 2 of the License, or | ||||
9 | * (at your option) any later version. | ||||
10 | * | ||||
11 | * This program is distributed in the hope that it will be useful, | ||||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
14 | * GNU General Public License for more details. | ||||
15 | * | ||||
16 | * You should have received a copy of the GNU General Public License | ||||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
18 | */ | ||||
19 | | ||||
20 | #include "waylandclient.h" | ||||
21 | #include "screens.h" | ||||
22 | #include "wayland_server.h" | ||||
23 | #include "workspace.h" | ||||
24 | | ||||
25 | #include <KWaylandServer/display.h> | ||||
26 | #include <KWaylandServer/clientconnection.h> | ||||
27 | #include <KWaylandServer/surface_interface.h> | ||||
28 | | ||||
29 | #include <QFileInfo> | ||||
30 | | ||||
31 | #include <csignal> | ||||
32 | | ||||
33 | #include <sys/types.h> | ||||
34 | #include <unistd.h> | ||||
35 | | ||||
36 | using namespace KWaylandServer; | ||||
37 | | ||||
38 | namespace KWin | ||||
39 | { | ||||
40 | | ||||
41 | WaylandClient::WaylandClient(SurfaceInterface *surface) | ||||
42 | { | ||||
43 | // Note that we cannot setup compositing here because we may need to call visibleRect(), | ||||
44 | // which in its turn will call bufferGeometry(), which is a pure virtual method. | ||||
45 | | ||||
46 | setSurface(surface); | ||||
47 | | ||||
48 | m_windowId = waylandServer()->createWindowId(surface); | ||||
49 | | ||||
50 | connect(this, &WaylandClient::frameGeometryChanged, | ||||
51 | this, &WaylandClient::updateClientOutputs); | ||||
52 | connect(this, &WaylandClient::frameGeometryChanged, | ||||
53 | this, &WaylandClient::updateClientArea); | ||||
54 | connect(this, &WaylandClient::desktopFileNameChanged, | ||||
55 | this, &WaylandClient::updateIcon); | ||||
56 | connect(screens(), &Screens::changed, this, | ||||
57 | &WaylandClient::updateClientOutputs); | ||||
58 | | ||||
59 | updateResourceName(); | ||||
60 | updateIcon(); | ||||
61 | } | ||||
62 | | ||||
63 | QString WaylandClient::captionNormal() const | ||||
64 | { | ||||
65 | return m_captionNormal; | ||||
66 | } | ||||
67 | | ||||
68 | QString WaylandClient::captionSuffix() const | ||||
69 | { | ||||
70 | return m_captionSuffix; | ||||
71 | } | ||||
72 | | ||||
73 | QStringList WaylandClient::activities() const | ||||
74 | { | ||||
75 | return QStringList(); | ||||
76 | } | ||||
77 | | ||||
78 | void WaylandClient::setOnAllActivities(bool set) | ||||
79 | { | ||||
80 | Q_UNUSED(set) | ||||
81 | } | ||||
82 | | ||||
83 | void WaylandClient::blockActivityUpdates(bool b) | ||||
84 | { | ||||
85 | Q_UNUSED(b) | ||||
86 | } | ||||
87 | | ||||
88 | QPoint WaylandClient::clientContentPos() const | ||||
89 | { | ||||
90 | return -clientPos(); | ||||
91 | } | ||||
92 | | ||||
93 | QRect WaylandClient::transparentRect() const | ||||
94 | { | ||||
95 | return QRect(); | ||||
96 | } | ||||
97 | | ||||
98 | quint32 WaylandClient::windowId() const | ||||
99 | { | ||||
100 | return m_windowId; | ||||
101 | } | ||||
102 | | ||||
103 | pid_t WaylandClient::pid() const | ||||
104 | { | ||||
105 | return surface()->client()->processId(); | ||||
106 | } | ||||
107 | | ||||
108 | bool WaylandClient::isLockScreen() const | ||||
109 | { | ||||
110 | return surface()->client() == waylandServer()->screenLockerClientConnection(); | ||||
111 | } | ||||
112 | | ||||
113 | bool WaylandClient::isInputMethod() const | ||||
114 | { | ||||
115 | return surface()->client() == waylandServer()->inputMethodConnection(); | ||||
116 | } | ||||
117 | | ||||
118 | bool WaylandClient::isLocalhost() const | ||||
119 | { | ||||
120 | return true; | ||||
121 | } | ||||
122 | | ||||
123 | double WaylandClient::opacity() const | ||||
124 | { | ||||
125 | return m_opacity; | ||||
126 | } | ||||
127 | | ||||
128 | void WaylandClient::setOpacity(double opacity) | ||||
129 | { | ||||
130 | const qreal newOpacity = qBound(0.0, opacity, 1.0); | ||||
131 | if (newOpacity == m_opacity) { | ||||
132 | return; | ||||
133 | } | ||||
134 | const qreal oldOpacity = m_opacity; | ||||
135 | m_opacity = newOpacity; | ||||
136 | addRepaintFull(); | ||||
137 | emit opacityChanged(this, oldOpacity); | ||||
138 | } | ||||
139 | | ||||
140 | AbstractClient *WaylandClient::findModal(bool allow_itself) | ||||
141 | { | ||||
142 | Q_UNUSED(allow_itself) | ||||
143 | return nullptr; | ||||
144 | } | ||||
145 | | ||||
146 | void WaylandClient::resizeWithChecks(const QSize &size, ForceGeometry_t force) | ||||
147 | { | ||||
148 | const QRect area = workspace()->clientArea(WorkArea, this); | ||||
149 | | ||||
150 | int width = size.width(); | ||||
151 | int height = size.height(); | ||||
152 | | ||||
153 | // don't allow growing larger than workarea | ||||
154 | if (width > area.width()) { | ||||
155 | width = area.width(); | ||||
156 | } | ||||
157 | if (height > area.height()) { | ||||
158 | height = area.height(); | ||||
159 | } | ||||
160 | setFrameGeometry(QRect(x(), y(), width, height), force); | ||||
161 | } | ||||
162 | | ||||
163 | void WaylandClient::killWindow() | ||||
164 | { | ||||
165 | if (!surface()) { | ||||
166 | return; | ||||
167 | } | ||||
168 | auto c = surface()->client(); | ||||
169 | if (c->processId() == getpid() || c->processId() == 0) { | ||||
170 | c->destroy(); | ||||
171 | return; | ||||
172 | } | ||||
173 | ::kill(c->processId(), SIGTERM); | ||||
174 | // give it time to terminate and only if terminate fails, try destroy Wayland connection | ||||
175 | QTimer::singleShot(5000, c, &ClientConnection::destroy); | ||||
176 | } | ||||
177 | | ||||
178 | QByteArray WaylandClient::windowRole() const | ||||
179 | { | ||||
180 | return QByteArray(); | ||||
181 | } | ||||
182 | | ||||
183 | bool WaylandClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const | ||||
184 | { | ||||
185 | if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) { | ||||
186 | if (other->desktopFileName() == desktopFileName()) { | ||||
187 | return true; | ||||
188 | } | ||||
189 | } | ||||
190 | if (auto s = other->surface()) { | ||||
191 | return s->client() == surface()->client(); | ||||
192 | } | ||||
193 | return false; | ||||
194 | } | ||||
195 | | ||||
196 | bool WaylandClient::belongsToDesktop() const | ||||
197 | { | ||||
198 | const auto clients = waylandServer()->clients(); | ||||
199 | | ||||
200 | return std::any_of(clients.constBegin(), clients.constEnd(), | ||||
201 | [this](const AbstractClient *client) { | ||||
202 | if (belongsToSameApplication(client, SameApplicationChecks())) { | ||||
203 | return client->isDesktop(); | ||||
204 | } | ||||
205 | return false; | ||||
206 | } | ||||
207 | ); | ||||
208 | } | ||||
209 | | ||||
210 | void WaylandClient::updateClientArea() | ||||
211 | { | ||||
212 | if (hasStrut()) { | ||||
213 | workspace()->updateClientArea(); | ||||
214 | } | ||||
215 | } | ||||
216 | | ||||
217 | void WaylandClient::updateClientOutputs() | ||||
218 | { | ||||
219 | const auto outputs = waylandServer()->display()->outputs(); | ||||
220 | QVector<OutputInterface *> clientOutputs; | ||||
221 | clientOutputs.reserve(outputs.count()); | ||||
222 | for (OutputInterface *output : outputs) { | ||||
223 | const QRect outputGeometry(output->globalPosition(), output->pixelSize() / output->scale()); | ||||
224 | if (frameGeometry().intersects(outputGeometry)) { | ||||
225 | clientOutputs << output; | ||||
226 | } | ||||
227 | } | ||||
228 | surface()->setOutputs(clientOutputs); | ||||
229 | } | ||||
230 | | ||||
231 | void WaylandClient::updateIcon() | ||||
232 | { | ||||
233 | const QString waylandIconName = QStringLiteral("wayland"); | ||||
234 | const QString dfIconName = iconFromDesktopFile(); | ||||
235 | const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName; | ||||
236 | if (iconName == icon().name()) { | ||||
237 | return; | ||||
238 | } | ||||
239 | setIcon(QIcon::fromTheme(iconName)); | ||||
240 | } | ||||
241 | | ||||
242 | void WaylandClient::updateResourceName() | ||||
243 | { | ||||
244 | const QFileInfo fileInfo(surface()->client()->executablePath()); | ||||
245 | if (fileInfo.exists()) { | ||||
246 | setResourceClass(fileInfo.fileName().toUtf8()); | ||||
247 | } | ||||
248 | } | ||||
249 | | ||||
250 | void WaylandClient::updateCaption() | ||||
251 | { | ||||
252 | const QString oldSuffix = m_captionSuffix; | ||||
253 | const auto shortcut = shortcutCaptionSuffix(); | ||||
254 | m_captionSuffix = shortcut; | ||||
255 | if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) { | ||||
256 | int i = 2; | ||||
257 | do { | ||||
258 | m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>'); | ||||
259 | i++; | ||||
260 | } while (findClientWithSameCaption()); | ||||
261 | } | ||||
262 | if (m_captionSuffix != oldSuffix) { | ||||
263 | emit captionChanged(); | ||||
264 | } | ||||
265 | } | ||||
266 | | ||||
267 | void WaylandClient::setCaption(const QString &caption) | ||||
268 | { | ||||
269 | const QString oldSuffix = m_captionSuffix; | ||||
270 | m_captionNormal = caption.simplified(); | ||||
271 | updateCaption(); | ||||
272 | if (m_captionSuffix == oldSuffix) { | ||||
273 | // Don't emit caption change twice it already got emitted by the changing suffix. | ||||
274 | emit captionChanged(); | ||||
275 | } | ||||
276 | } | ||||
277 | | ||||
278 | } // namespace KWin |