Changeset View
Changeset View
Standalone View
Standalone View
internal_client.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2019 Martin Flöser <mgraesslin@kde.org> | 5 | Copyright (C) 2019 Martin Flöser <mgraesslin@kde.org> | ||
6 | Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com> | ||||
6 | 7 | | |||
7 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | 10 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | 11 | (at your option) any later version. | ||
11 | 12 | | |||
12 | This program is distributed in the hope that it will be useful, | 13 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | 16 | GNU General Public License for more details. | ||
16 | 17 | | |||
17 | You should have received a copy of the GNU General Public License | 18 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | *********************************************************************/ | 20 | *********************************************************************/ | ||
20 | #include "internal_client.h" | 21 | #include "internal_client.h" | ||
22 | #include "decorations/decorationbridge.h" | ||||
23 | #include "deleted.h" | ||||
21 | #include "workspace.h" | 24 | #include "workspace.h" | ||
22 | 25 | | |||
23 | #include <KWayland/Client/surface.h> | 26 | #include <KDecoration2/Decoration> | ||
24 | #include <KWayland/Server/surface_interface.h> | | |||
25 | 27 | | |||
26 | #include <QOpenGLFramebufferObject> | 28 | #include <QOpenGLFramebufferObject> | ||
29 | #include <QWindow> | ||||
27 | 30 | | |||
28 | Q_DECLARE_METATYPE(NET::WindowType) | 31 | Q_DECLARE_METATYPE(NET::WindowType) | ||
29 | 32 | | |||
30 | static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); | 33 | static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); | ||
31 | 34 | | |||
32 | namespace KWin | 35 | namespace KWin | ||
33 | { | 36 | { | ||
34 | 37 | | |||
35 | InternalClient::InternalClient(KWayland::Server::ShellSurfaceInterface *surface) | 38 | InternalClient::InternalClient(QWindow *window) | ||
36 | : ShellClient(surface) | 39 | : m_internalWindow(window) | ||
40 | , m_clientSize(window->size()) | ||||
41 | , m_windowId(window->winId()) | ||||
42 | , m_internalWindowFlags(window->flags()) | ||||
37 | { | 43 | { | ||
38 | findInternalWindow(); | 44 | // Don't render the client until it provides a buffer. | ||
39 | updateInternalWindowGeometry(); | 45 | ready_for_painting = false; | ||
40 | updateDecoration(true); | | |||
41 | } | | |||
42 | | ||||
43 | InternalClient::InternalClient(KWayland::Server::XdgShellSurfaceInterface *surface) | | |||
44 | : ShellClient(surface) | | |||
45 | { | | |||
46 | } | | |||
47 | 46 | | |||
48 | InternalClient::InternalClient(KWayland::Server::XdgShellPopupInterface *surface) | | |||
49 | : ShellClient(surface) | | |||
50 | { | | |||
51 | } | | |||
52 | | ||||
53 | InternalClient::~InternalClient() = default; | | |||
54 | | ||||
55 | void InternalClient::findInternalWindow() | | |||
56 | { | | |||
57 | const QWindowList windows = kwinApp()->topLevelWindows(); | | |||
58 | for (QWindow *w: windows) { | | |||
59 | auto s = KWayland::Client::Surface::fromWindow(w); | | |||
60 | if (!s) { | | |||
61 | continue; | | |||
62 | } | | |||
63 | if (s->id() != surface()->id()) { | | |||
64 | continue; | | |||
65 | } | | |||
66 | m_internalWindow = w; | | |||
67 | m_windowId = m_internalWindow->winId(); | | |||
68 | m_internalWindowFlags = m_internalWindow->flags(); | | |||
69 | connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry); | 47 | connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry); | ||
70 | connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry); | 48 | connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry); | ||
71 | connect(m_internalWindow, &QWindow::destroyed, this, [this] { m_internalWindow = nullptr; }); | 49 | connect(m_internalWindow, &QWindow::widthChanged, this, &InternalClient::updateInternalWindowGeometry); | ||
50 | connect(m_internalWindow, &QWindow::heightChanged, this, &InternalClient::updateInternalWindowGeometry); | ||||
51 | connect(m_internalWindow, &QWindow::windowTitleChanged, this, &InternalClient::setCaption); | ||||
72 | connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); | 52 | connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); | ||
53 | connect(m_internalWindow, &QWindow::destroyed, this, &InternalClient::destroyClient); | ||||
54 | | ||||
55 | connect(this, &InternalClient::opacityChanged, this, &InternalClient::addRepaintFull); | ||||
73 | 56 | | |||
74 | const QVariant windowType = m_internalWindow->property("kwin_windowType"); | 57 | const QVariant windowType = m_internalWindow->property("kwin_windowType"); | ||
75 | if (!windowType.isNull()) { | 58 | if (!windowType.isNull()) { | ||
76 | m_windowType = windowType.value<NET::WindowType>(); | 59 | m_windowType = windowType.value<NET::WindowType>(); | ||
77 | } | 60 | } | ||
78 | setOpacity(m_internalWindow->opacity()); | | |||
79 | 61 | | |||
80 | // skip close animation support | 62 | setCaption(m_internalWindow->title()); | ||
63 | setIcon(QIcon::fromTheme(QStringLiteral("kwin"))); | ||||
64 | setOnAllDesktops(true); | ||||
65 | setOpacity(m_internalWindow->opacity()); | ||||
81 | setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); | 66 | setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); | ||
67 | | ||||
68 | setupCompositing(); | ||||
69 | updateColorScheme(); | ||||
70 | | ||||
71 | blockGeometryUpdates(true); | ||||
72 | commitGeometry(m_internalWindow->geometry()); | ||||
73 | updateDecoration(true); | ||||
74 | setGeometry(mapFromClient(m_internalWindow->geometry())); | ||||
75 | setGeometryRestore(geometry()); | ||||
76 | blockGeometryUpdates(false); | ||||
77 | | ||||
82 | m_internalWindow->installEventFilter(this); | 78 | m_internalWindow->installEventFilter(this); | ||
83 | return; | | |||
84 | } | 79 | } | ||
85 | 80 | | |||
86 | qCWarning(KWIN_CORE, "Couldn't find an internal window for surface with id %x", surface()->id()); | 81 | InternalClient::~InternalClient() | ||
82 | { | ||||
87 | } | 83 | } | ||
88 | 84 | | |||
89 | bool InternalClient::eventFilter(QObject *watched, QEvent *event) | 85 | bool InternalClient::eventFilter(QObject *watched, QEvent *event) | ||
90 | { | 86 | { | ||
91 | if (watched == m_internalWindow && event->type() == QEvent::DynamicPropertyChange) { | 87 | if (watched == m_internalWindow && event->type() == QEvent::DynamicPropertyChange) { | ||
92 | QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent*>(event); | 88 | QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent*>(event); | ||
93 | if (pe->propertyName() == s_skipClosePropertyName) { | 89 | if (pe->propertyName() == s_skipClosePropertyName) { | ||
94 | setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); | 90 | setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); | ||
95 | } | 91 | } | ||
96 | if (pe->propertyName() == "kwin_windowType") { | 92 | if (pe->propertyName() == "kwin_windowType") { | ||
97 | m_windowType = m_internalWindow->property("kwin_windowType").value<NET::WindowType>(); | 93 | m_windowType = m_internalWindow->property("kwin_windowType").value<NET::WindowType>(); | ||
98 | workspace()->updateClientArea(); | 94 | workspace()->updateClientArea(); | ||
99 | } | 95 | } | ||
100 | } | 96 | } | ||
101 | return false; | 97 | return false; | ||
102 | } | 98 | } | ||
103 | 99 | | |||
100 | QStringList InternalClient::activities() const | ||||
101 | { | ||||
102 | return QStringList(); | ||||
103 | } | ||||
104 | | ||||
105 | void InternalClient::blockActivityUpdates(bool b) | ||||
106 | { | ||||
107 | Q_UNUSED(b) | ||||
108 | | ||||
109 | // Internal clients do not support activities. | ||||
110 | } | ||||
111 | | ||||
112 | qreal InternalClient::bufferScale() const | ||||
113 | { | ||||
114 | if (m_internalWindow) { | ||||
115 | return m_internalWindow->devicePixelRatio(); | ||||
116 | } | ||||
117 | return 1; | ||||
118 | } | ||||
119 | | ||||
120 | QString InternalClient::captionNormal() const | ||||
121 | { | ||||
122 | return m_captionNormal; | ||||
123 | } | ||||
124 | | ||||
125 | QString InternalClient::captionSuffix() const | ||||
126 | { | ||||
127 | return m_captionSuffix; | ||||
128 | } | ||||
129 | | ||||
130 | QPoint InternalClient::clientContentPos() const | ||||
131 | { | ||||
132 | return -1 * clientPos(); | ||||
133 | } | ||||
134 | | ||||
135 | QSize InternalClient::clientSize() const | ||||
136 | { | ||||
137 | return m_clientSize; | ||||
138 | } | ||||
139 | | ||||
140 | void InternalClient::debug(QDebug &stream) const | ||||
141 | { | ||||
142 | stream.nospace() << "\'InternalClient:" << m_internalWindow << "\'"; | ||||
143 | } | ||||
144 | | ||||
145 | QRect InternalClient::transparentRect() const | ||||
146 | { | ||||
147 | return QRect(); | ||||
148 | } | ||||
149 | | ||||
104 | NET::WindowType InternalClient::windowType(bool direct, int supported_types) const | 150 | NET::WindowType InternalClient::windowType(bool direct, int supported_types) const | ||
105 | { | 151 | { | ||
106 | Q_UNUSED(direct) | 152 | Q_UNUSED(direct) | ||
107 | Q_UNUSED(supported_types) | 153 | Q_UNUSED(supported_types) | ||
108 | return m_windowType; | 154 | return m_windowType; | ||
109 | } | 155 | } | ||
110 | 156 | | |||
157 | double InternalClient::opacity() const | ||||
158 | { | ||||
159 | return m_opacity; | ||||
160 | } | ||||
161 | | ||||
162 | void InternalClient::setOpacity(double opacity) | ||||
163 | { | ||||
164 | if (m_opacity == opacity) { | ||||
165 | return; | ||||
166 | } | ||||
167 | | ||||
168 | const double oldOpacity = m_opacity; | ||||
169 | m_opacity = opacity; | ||||
170 | | ||||
171 | emit opacityChanged(this, oldOpacity); | ||||
172 | } | ||||
173 | | ||||
111 | void InternalClient::killWindow() | 174 | void InternalClient::killWindow() | ||
112 | { | 175 | { | ||
113 | // we don't kill our internal windows | 176 | // We don't kill our internal windows. | ||
114 | } | 177 | } | ||
115 | 178 | | |||
116 | bool InternalClient::isPopupWindow() const | 179 | bool InternalClient::isPopupWindow() const | ||
117 | { | 180 | { | ||
118 | if (Toplevel::isPopupWindow()) { | 181 | if (AbstractClient::isPopupWindow()) { | ||
119 | return true; | 182 | return true; | ||
120 | } | 183 | } | ||
121 | return m_internalWindowFlags.testFlag(Qt::Popup); | 184 | return m_internalWindowFlags.testFlag(Qt::Popup); | ||
122 | } | 185 | } | ||
123 | 186 | | |||
124 | void InternalClient::setInternalFramebufferObject(const QSharedPointer<QOpenGLFramebufferObject> &fbo) | 187 | QByteArray InternalClient::windowRole() const | ||
125 | { | 188 | { | ||
126 | if (fbo.isNull()) { | 189 | return QByteArray(); | ||
127 | unmap(); | | |||
128 | return; | | |||
129 | } | | |||
130 | | ||||
131 | setClientSize(fbo->size() / surface()->scale()); | | |||
132 | markAsMapped(); | | |||
133 | doSetGeometry(QRect(geom.topLeft(), clientSize())); | | |||
134 | Toplevel::setInternalFramebufferObject(fbo); | | |||
135 | Toplevel::addDamage(QRegion(0, 0, width(), height())); | | |||
136 | } | 190 | } | ||
137 | 191 | | |||
138 | void InternalClient::closeWindow() | 192 | void InternalClient::closeWindow() | ||
139 | { | 193 | { | ||
140 | if (m_internalWindow) { | 194 | if (m_internalWindow) { | ||
141 | m_internalWindow->hide(); | 195 | m_internalWindow->hide(); | ||
142 | } | 196 | } | ||
143 | } | 197 | } | ||
144 | 198 | | |||
145 | bool InternalClient::isCloseable() const | 199 | bool InternalClient::isCloseable() const | ||
146 | { | 200 | { | ||
147 | return true; | 201 | return true; | ||
148 | } | 202 | } | ||
149 | 203 | | |||
204 | bool InternalClient::isFullScreenable() const | ||||
205 | { | ||||
206 | return false; | ||||
207 | } | ||||
208 | | ||||
209 | bool InternalClient::isFullScreen() const | ||||
210 | { | ||||
211 | return false; | ||||
212 | } | ||||
213 | | ||||
150 | bool InternalClient::isMaximizable() const | 214 | bool InternalClient::isMaximizable() const | ||
151 | { | 215 | { | ||
152 | return false; | 216 | return false; | ||
153 | } | 217 | } | ||
154 | 218 | | |||
155 | bool InternalClient::isMinimizable() const | 219 | bool InternalClient::isMinimizable() const | ||
156 | { | 220 | { | ||
157 | return false; | 221 | return false; | ||
Show All 11 Lines | |||||
169 | 233 | | |||
170 | bool InternalClient::isResizable() const | 234 | bool InternalClient::isResizable() const | ||
171 | { | 235 | { | ||
172 | return true; | 236 | return true; | ||
173 | } | 237 | } | ||
174 | 238 | | |||
175 | bool InternalClient::noBorder() const | 239 | bool InternalClient::noBorder() const | ||
176 | { | 240 | { | ||
177 | return m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); | 241 | return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); | ||
178 | } | 242 | } | ||
179 | 243 | | |||
180 | bool InternalClient::userCanSetNoBorder() const | 244 | bool InternalClient::userCanSetNoBorder() const | ||
181 | { | 245 | { | ||
182 | return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); | 246 | return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); | ||
183 | } | 247 | } | ||
184 | 248 | | |||
185 | bool InternalClient::wantsInput() const | 249 | bool InternalClient::wantsInput() const | ||
186 | { | 250 | { | ||
187 | return false; | 251 | return false; | ||
188 | } | 252 | } | ||
189 | 253 | | |||
190 | bool InternalClient::acceptsFocus() const | | |||
191 | { | | |||
192 | return false; | | |||
193 | } | | |||
194 | | ||||
195 | bool InternalClient::isInternal() const | 254 | bool InternalClient::isInternal() const | ||
196 | { | 255 | { | ||
197 | return true; | 256 | return true; | ||
198 | } | 257 | } | ||
199 | 258 | | |||
200 | bool InternalClient::isLockScreen() const | 259 | bool InternalClient::isLockScreen() const | ||
201 | { | 260 | { | ||
202 | if (m_internalWindow) { | 261 | if (m_internalWindow) { | ||
Show All 18 Lines | 276 | { | |||
221 | return false; | 280 | return false; | ||
222 | } | 281 | } | ||
223 | 282 | | |||
224 | quint32 InternalClient::windowId() const | 283 | quint32 InternalClient::windowId() const | ||
225 | { | 284 | { | ||
226 | return m_windowId; | 285 | return m_windowId; | ||
227 | } | 286 | } | ||
228 | 287 | | |||
229 | void InternalClient::updateInternalWindowGeometry() | 288 | MaximizeMode InternalClient::maximizeMode() const | ||
289 | { | ||||
290 | return MaximizeRestore; | ||||
291 | } | ||||
292 | | ||||
293 | QRect InternalClient::geometryRestore() const | ||||
294 | { | ||||
295 | return m_maximizeRestoreGeometry; | ||||
296 | } | ||||
297 | | ||||
298 | bool InternalClient::isShown(bool shaded_is_shown) const | ||||
299 | { | ||||
300 | Q_UNUSED(shaded_is_shown) | ||||
301 | | ||||
302 | return readyForPainting(); | ||||
303 | } | ||||
304 | | ||||
305 | bool InternalClient::isHiddenInternal() const | ||||
306 | { | ||||
307 | return false; | ||||
308 | } | ||||
309 | | ||||
310 | void InternalClient::hideClient(bool hide) | ||||
230 | { | 311 | { | ||
312 | Q_UNUSED(hide) | ||||
313 | } | ||||
314 | | ||||
315 | void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) | ||||
316 | { | ||||
317 | Q_UNUSED(force) | ||||
231 | if (!m_internalWindow) { | 318 | if (!m_internalWindow) { | ||
232 | return; | 319 | return; | ||
233 | } | 320 | } | ||
234 | doSetGeometry(QRect(m_internalWindow->geometry().topLeft() - QPoint(borderLeft(), borderTop()), | 321 | QRect area = workspace()->clientArea(WorkArea, this); | ||
235 | m_internalWindow->geometry().size() + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); | 322 | // don't allow growing larger than workarea | ||
323 | if (w > area.width()) { | ||||
324 | w = area.width(); | ||||
325 | } | ||||
326 | if (h > area.height()) { | ||||
327 | h = area.height(); | ||||
328 | } | ||||
329 | setGeometry(QRect(x(), y(), w, h)); | ||||
330 | } | ||||
331 | | ||||
332 | void InternalClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force) | ||||
333 | { | ||||
334 | const QRect rect(x, y, w, h); | ||||
335 | | ||||
336 | if (areGeometryUpdatesBlocked()) { | ||||
337 | geom = rect; | ||||
338 | if (pendingGeometryUpdate() == PendingGeometryForced) { | ||||
339 | // Maximum, nothing needed. | ||||
340 | } else if (force == ForceGeometrySet) { | ||||
341 | setPendingGeometryUpdate(PendingGeometryForced); | ||||
342 | } else { | ||||
343 | setPendingGeometryUpdate(PendingGeometryNormal); | ||||
344 | } | ||||
345 | return; | ||||
346 | } | ||||
347 | | ||||
348 | if (pendingGeometryUpdate() != PendingGeometryNone) { | ||||
349 | // Reset geometry to the one before blocking, so that we can compare properly. | ||||
350 | geom = geometryBeforeUpdateBlocking(); | ||||
351 | } | ||||
352 | | ||||
353 | if (geom == rect) { | ||||
354 | return; | ||||
355 | } | ||||
356 | | ||||
357 | const QRect newClientGeometry = mapToClient(rect); | ||||
358 | | ||||
359 | if (m_clientSize == newClientGeometry.size()) { | ||||
360 | commitGeometry(rect); | ||||
361 | } else { | ||||
362 | requestGeometry(rect); | ||||
363 | } | ||||
236 | } | 364 | } | ||
237 | 365 | | |||
238 | bool InternalClient::requestGeometry(const QRect &rect) | 366 | void InternalClient::setGeometryRestore(const QRect &rect) | ||
367 | { | ||||
368 | m_maximizeRestoreGeometry = rect; | ||||
369 | } | ||||
370 | | ||||
371 | bool InternalClient::supportsWindowRules() const | ||||
239 | { | 372 | { | ||
240 | if (!ShellClient::requestGeometry(rect)) { | | |||
241 | return false; | 373 | return false; | ||
242 | } | 374 | } | ||
243 | if (m_internalWindow) { | 375 | | ||
244 | m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); | 376 | AbstractClient *InternalClient::findModal(bool allow_itself) | ||
377 | { | ||||
378 | Q_UNUSED(allow_itself) | ||||
379 | return nullptr; | ||||
245 | } | 380 | } | ||
246 | return true; | 381 | | ||
382 | void InternalClient::setOnAllActivities(bool set) | ||||
383 | { | ||||
384 | Q_UNUSED(set) | ||||
385 | | ||||
386 | // Internal clients do not support activities. | ||||
247 | } | 387 | } | ||
248 | 388 | | |||
249 | void InternalClient::doSetGeometry(const QRect &rect) | 389 | void InternalClient::takeFocus() | ||
250 | { | 390 | { | ||
251 | if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { | 391 | } | ||
392 | | ||||
393 | bool InternalClient::userCanSetFullScreen() const | ||||
394 | { | ||||
395 | return false; | ||||
396 | } | ||||
397 | | ||||
398 | void InternalClient::setFullScreen(bool set, bool user) | ||||
399 | { | ||||
400 | Q_UNUSED(set) | ||||
401 | Q_UNUSED(user) | ||||
402 | } | ||||
403 | | ||||
404 | void InternalClient::setNoBorder(bool set) | ||||
405 | { | ||||
406 | if (!userCanSetNoBorder()) { | ||||
252 | return; | 407 | return; | ||
253 | } | 408 | } | ||
254 | if (!isUnmapped()) { | 409 | if (m_userNoBorder == set) { | ||
255 | addWorkspaceRepaint(visibleRect()); | 410 | return; | ||
411 | } | ||||
412 | m_userNoBorder = set; | ||||
413 | updateDecoration(true); | ||||
256 | } | 414 | } | ||
257 | geom = rect; | | |||
258 | 415 | | |||
259 | if (isUnmapped() && geometryRestore().isEmpty() && !geom.isEmpty()) { | 416 | void InternalClient::updateDecoration(bool check_workspace_pos, bool force) | ||
260 | // use first valid geometry as restore geometry | 417 | { | ||
261 | setGeometryRestore(geom); | 418 | if (!force && isDecorated() == !noBorder()) { | ||
419 | return; | ||||
262 | } | 420 | } | ||
263 | 421 | | |||
264 | if (!isUnmapped()) { | 422 | const QRect oldFrameGeometry = geometry(); | ||
265 | addWorkspaceRepaint(visibleRect()); | 423 | const QRect oldClientGeometry = oldFrameGeometry - frameMargins(); | ||
424 | | ||||
425 | GeometryUpdatesBlocker blocker(this); | ||||
426 | | ||||
427 | if (force) { | ||||
428 | destroyDecoration(); | ||||
266 | } | 429 | } | ||
267 | syncGeometryToInternalWindow(); | 430 | | ||
268 | if (hasStrut()) { | 431 | if (!noBorder()) { | ||
269 | workspace()->updateClientArea(); | 432 | createDecoration(oldClientGeometry); | ||
433 | } else { | ||||
434 | destroyDecoration(); | ||||
270 | } | 435 | } | ||
271 | const auto old = geometryBeforeUpdateBlocking(); | | |||
272 | updateGeometryBeforeUpdateBlocking(); | | |||
273 | emit geometryShapeChanged(this, old); | | |||
274 | 436 | | |||
275 | if (isResize()) { | 437 | getShadow(); | ||
276 | performMoveResize(); | 438 | | ||
439 | if (check_workspace_pos) { | ||||
440 | checkWorkspacePosition(oldFrameGeometry, -2, oldClientGeometry); | ||||
441 | } | ||||
442 | } | ||||
443 | | ||||
444 | void InternalClient::updateColorScheme() | ||||
445 | { | ||||
446 | AbstractClient::updateColorScheme(QString()); | ||||
447 | } | ||||
448 | | ||||
449 | void InternalClient::showOnScreenEdge() | ||||
450 | { | ||||
451 | } | ||||
452 | | ||||
453 | void InternalClient::destroyClient() | ||||
454 | { | ||||
455 | if (isMoveResize()) { | ||||
456 | leaveMoveResize(); | ||||
457 | } | ||||
458 | | ||||
459 | Deleted *deleted = Deleted::create(this); | ||||
460 | emit windowClosed(this, deleted); | ||||
461 | | ||||
462 | destroyDecoration(); | ||||
463 | | ||||
464 | workspace()->removeInternalClient(this); | ||||
465 | | ||||
466 | deleted->unrefWindow(); | ||||
467 | m_internalWindow = nullptr; | ||||
468 | | ||||
469 | delete this; | ||||
470 | } | ||||
471 | | ||||
472 | void InternalClient::present(const QSharedPointer<QOpenGLFramebufferObject> fbo) | ||||
473 | { | ||||
474 | Q_ASSERT(m_internalImage.isNull()); | ||||
475 | | ||||
476 | const QSize bufferSize = fbo->size() / bufferScale(); | ||||
477 | | ||||
478 | commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); | ||||
479 | markAsMapped(); | ||||
480 | | ||||
481 | if (m_internalFBO != fbo) { | ||||
482 | discardWindowPixmap(); | ||||
483 | m_internalFBO = fbo; | ||||
484 | } | ||||
485 | | ||||
486 | setDepth(32); | ||||
487 | addDamageFull(); | ||||
488 | addRepaintFull(); | ||||
489 | } | ||||
490 | | ||||
491 | void InternalClient::present(const QImage &image, const QRegion &damage) | ||||
492 | { | ||||
493 | Q_ASSERT(m_internalFBO.isNull()); | ||||
494 | | ||||
495 | const QSize bufferSize = image.size() / bufferScale(); | ||||
496 | | ||||
497 | commitGeometry(QRect(pos(), sizeForClientSize(bufferSize))); | ||||
498 | markAsMapped(); | ||||
499 | | ||||
500 | if (m_internalImage.size() != image.size()) { | ||||
501 | discardWindowPixmap(); | ||||
502 | } | ||||
503 | | ||||
504 | m_internalImage = image; | ||||
505 | | ||||
506 | setDepth(32); | ||||
507 | addDamage(damage); | ||||
508 | addRepaint(damage.translated(borderLeft(), borderTop())); | ||||
509 | } | ||||
510 | | ||||
511 | QWindow *InternalClient::internalWindow() const | ||||
512 | { | ||||
513 | return m_internalWindow; | ||||
514 | } | ||||
515 | | ||||
516 | bool InternalClient::acceptsFocus() const | ||||
517 | { | ||||
518 | return false; | ||||
519 | } | ||||
520 | | ||||
521 | bool InternalClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const | ||||
522 | { | ||||
523 | Q_UNUSED(checks) | ||||
524 | | ||||
525 | return qobject_cast<const InternalClient *>(other) != nullptr; | ||||
526 | } | ||||
527 | | ||||
528 | void InternalClient::changeMaximize(bool horizontal, bool vertical, bool adjust) | ||||
529 | { | ||||
530 | Q_UNUSED(horizontal) | ||||
531 | Q_UNUSED(vertical) | ||||
532 | Q_UNUSED(adjust) | ||||
533 | | ||||
534 | // Internal clients are not maximizable. | ||||
535 | } | ||||
536 | | ||||
537 | void InternalClient::destroyDecoration() | ||||
538 | { | ||||
539 | if (!isDecorated()) { | ||||
540 | return; | ||||
277 | } | 541 | } | ||
542 | | ||||
543 | const QRect clientGeometry = mapToClient(geometry()); | ||||
544 | AbstractClient::destroyDecoration(); | ||||
545 | setGeometry(clientGeometry); | ||||
278 | } | 546 | } | ||
279 | 547 | | |||
280 | void InternalClient::doMove(int x, int y) | 548 | void InternalClient::doMove(int x, int y) | ||
281 | { | 549 | { | ||
282 | Q_UNUSED(x) | 550 | Q_UNUSED(x) | ||
283 | Q_UNUSED(y) | 551 | Q_UNUSED(y) | ||
552 | | ||||
284 | syncGeometryToInternalWindow(); | 553 | syncGeometryToInternalWindow(); | ||
285 | } | 554 | } | ||
286 | 555 | | |||
287 | void InternalClient::syncGeometryToInternalWindow() | 556 | void InternalClient::doResizeSync() | ||
288 | { | 557 | { | ||
289 | if (!m_internalWindow) { | 558 | requestGeometry(moveResizeGeometry()); | ||
559 | } | ||||
560 | | ||||
561 | void InternalClient::updateCaption() | ||||
562 | { | ||||
563 | const QString oldSuffix = m_captionSuffix; | ||||
564 | const auto shortcut = shortcutCaptionSuffix(); | ||||
565 | m_captionSuffix = shortcut; | ||||
566 | if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) { | ||||
567 | int i = 2; | ||||
568 | do { | ||||
569 | m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>'); | ||||
570 | i++; | ||||
571 | } while (findClientWithSameCaption()); | ||||
572 | } | ||||
573 | if (m_captionSuffix != oldSuffix) { | ||||
574 | emit captionChanged(); | ||||
575 | } | ||||
576 | } | ||||
577 | | ||||
578 | QRect InternalClient::mapFromClient(const QRect &rect) const | ||||
579 | { | ||||
580 | return rect + frameMargins(); | ||||
581 | } | ||||
582 | | ||||
583 | QRect InternalClient::mapToClient(const QRect &rect) const | ||||
584 | { | ||||
585 | return rect - frameMargins(); | ||||
586 | } | ||||
587 | | ||||
588 | void InternalClient::createDecoration(const QRect &rect) | ||||
589 | { | ||||
590 | KDecoration2::Decoration *decoration = Decoration::DecorationBridge::self()->createDecoration(this); | ||||
591 | if (decoration) { | ||||
592 | QMetaObject::invokeMethod(decoration, "update", Qt::QueuedConnection); | ||||
593 | connect(decoration, &KDecoration2::Decoration::shadowChanged, this, &Toplevel::getShadow); | ||||
594 | connect(decoration, &KDecoration2::Decoration::bordersChanged, this, | ||||
595 | [this]() { | ||||
596 | GeometryUpdatesBlocker blocker(this); | ||||
597 | const QRect oldGeometry = geometry(); | ||||
598 | if (!isShade()) { | ||||
599 | checkWorkspacePosition(oldGeometry); | ||||
600 | } | ||||
601 | emit geometryShapeChanged(this, oldGeometry); | ||||
602 | } | ||||
603 | ); | ||||
604 | } | ||||
605 | | ||||
606 | const QRect oldFrameGeometry = geometry(); | ||||
607 | | ||||
608 | setDecoration(decoration); | ||||
609 | setGeometry(mapFromClient(rect)); | ||||
610 | | ||||
611 | emit geometryShapeChanged(this, oldFrameGeometry); | ||||
612 | } | ||||
613 | | ||||
614 | void InternalClient::requestGeometry(const QRect &rect) | ||||
615 | { | ||||
616 | if (m_internalWindow) { | ||||
617 | m_internalWindow->setGeometry(mapToClient(rect)); | ||||
618 | } | ||||
619 | } | ||||
620 | | ||||
621 | void InternalClient::commitGeometry(const QRect &rect) | ||||
622 | { | ||||
623 | if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { | ||||
290 | return; | 624 | return; | ||
291 | } | 625 | } | ||
292 | const QRect windowRect = QRect(geom.topLeft() + QPoint(borderLeft(), borderTop()), | 626 | | ||
293 | geom.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); | 627 | geom = rect; | ||
294 | if (m_internalWindow->geometry() != windowRect) { | 628 | | ||
295 | // delay to end of cycle to prevent freeze, see BUG 384441 | 629 | m_clientSize = mapToClient(geometry()).size(); | ||
296 | QTimer::singleShot(0, m_internalWindow, std::bind(static_cast<void (QWindow::*)(const QRect&)>(&QWindow::setGeometry), m_internalWindow, windowRect)); | 630 | | ||
631 | addWorkspaceRepaint(visibleRect()); | ||||
632 | syncGeometryToInternalWindow(); | ||||
633 | | ||||
634 | const QRect oldGeometry = geometryBeforeUpdateBlocking(); | ||||
635 | updateGeometryBeforeUpdateBlocking(); | ||||
636 | emit geometryShapeChanged(this, oldGeometry); | ||||
637 | | ||||
638 | if (isResize()) { | ||||
639 | performMoveResize(); | ||||
297 | } | 640 | } | ||
298 | } | 641 | } | ||
299 | 642 | | |||
300 | void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) | 643 | void InternalClient::setCaption(const QString &caption) | ||
301 | { | 644 | { | ||
302 | Q_UNUSED(force) | 645 | if (m_captionNormal == caption) { | ||
303 | if (!m_internalWindow) { | | |||
304 | return; | 646 | return; | ||
305 | } | 647 | } | ||
306 | QRect area = workspace()->clientArea(WorkArea, this); | 648 | | ||
307 | // don't allow growing larger than workarea | 649 | m_captionNormal = caption; | ||
308 | if (w > area.width()) { | 650 | | ||
309 | w = area.width(); | 651 | const QString oldCaptionSuffix = m_captionSuffix; | ||
652 | updateCaption(); | ||||
653 | | ||||
654 | if (m_captionSuffix == oldCaptionSuffix) { | ||||
655 | emit captionChanged(); | ||||
310 | } | 656 | } | ||
311 | if (h > area.height()) { | | |||
312 | h = area.height(); | | |||
313 | } | 657 | } | ||
314 | m_internalWindow->setGeometry(QRect(pos() + QPoint(borderLeft(), borderTop()), QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); | 658 | | ||
659 | void InternalClient::markAsMapped() | ||||
660 | { | ||||
661 | if (!ready_for_painting) { | ||||
662 | setReadyForPainting(); | ||||
663 | workspace()->addInternalClient(this); | ||||
664 | } | ||||
315 | } | 665 | } | ||
316 | 666 | | |||
317 | void InternalClient::doResizeSync() | 667 | void InternalClient::syncGeometryToInternalWindow() | ||
318 | { | 668 | { | ||
319 | if (!m_internalWindow) { | 669 | if (m_internalWindow->geometry() == mapToClient(geometry())) { | ||
320 | return; | 670 | return; | ||
321 | } | 671 | } | ||
322 | const auto rect = moveResizeGeometry(); | 672 | | ||
323 | m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); | 673 | QTimer::singleShot(0, this, [this] { requestGeometry(geometry()); }); | ||
324 | } | 674 | } | ||
325 | 675 | | |||
326 | QWindow *InternalClient::internalWindow() const | 676 | void InternalClient::updateInternalWindowGeometry() | ||
327 | { | 677 | { | ||
328 | return m_internalWindow; | 678 | if (isMoveResize()) { | ||
679 | return; | ||||
329 | } | 680 | } | ||
330 | 681 | | |||
331 | bool InternalClient::supportsWindowRules() const | 682 | commitGeometry(mapFromClient(m_internalWindow->geometry())); | ||
332 | { | | |||
333 | return false; | | |||
334 | } | 683 | } | ||
335 | 684 | | |||
336 | } | 685 | } |