Warning, file /plasma/kwin/src/internalwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Martin Flöser <mgraesslin@kde.org> 0006 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "internalwindow.h" 0011 #include "decorations/decorationbridge.h" 0012 #include "deleted.h" 0013 #include "scene/surfaceitem.h" 0014 #include "scene/windowitem.h" 0015 #include "workspace.h" 0016 0017 #include <KDecoration2/Decoration> 0018 0019 #include <QMouseEvent> 0020 #include <QOpenGLFramebufferObject> 0021 #include <QWindow> 0022 0023 Q_DECLARE_METATYPE(NET::WindowType) 0024 0025 static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); 0026 static const QByteArray s_shadowEnabledPropertyName = QByteArrayLiteral("kwin_shadow_enabled"); 0027 0028 namespace KWin 0029 { 0030 0031 InternalWindow::InternalWindow(QWindow *handle) 0032 : m_handle(handle) 0033 , m_internalWindowFlags(handle->flags()) 0034 { 0035 connect(m_handle, &QWindow::xChanged, this, &InternalWindow::updateInternalWindowGeometry); 0036 connect(m_handle, &QWindow::yChanged, this, &InternalWindow::updateInternalWindowGeometry); 0037 connect(m_handle, &QWindow::widthChanged, this, &InternalWindow::updateInternalWindowGeometry); 0038 connect(m_handle, &QWindow::heightChanged, this, &InternalWindow::updateInternalWindowGeometry); 0039 connect(m_handle, &QWindow::windowTitleChanged, this, &InternalWindow::setCaption); 0040 connect(m_handle, &QWindow::opacityChanged, this, &InternalWindow::setOpacity); 0041 connect(m_handle, &QWindow::destroyed, this, &InternalWindow::destroyWindow); 0042 0043 const QVariant windowType = m_handle->property("kwin_windowType"); 0044 if (!windowType.isNull()) { 0045 m_windowType = windowType.value<NET::WindowType>(); 0046 } 0047 0048 setCaption(m_handle->title()); 0049 setIcon(QIcon::fromTheme(QStringLiteral("kwin"))); 0050 setOnAllDesktops(true); 0051 setOpacity(m_handle->opacity()); 0052 setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); 0053 updateColorScheme(); 0054 0055 setMoveResizeGeometry(m_handle->geometry()); 0056 commitGeometry(m_handle->geometry()); 0057 0058 updateDecoration(true); 0059 0060 m_handle->installEventFilter(this); 0061 } 0062 0063 InternalWindow::~InternalWindow() 0064 { 0065 } 0066 0067 std::unique_ptr<WindowItem> InternalWindow::createItem(Scene *scene) 0068 { 0069 return std::make_unique<WindowItemInternal>(this, scene); 0070 } 0071 0072 bool InternalWindow::isClient() const 0073 { 0074 return true; 0075 } 0076 0077 bool InternalWindow::hitTest(const QPointF &point) const 0078 { 0079 if (!Window::hitTest(point)) { 0080 return false; 0081 } 0082 0083 const QRegion mask = m_handle->mask(); 0084 if (!mask.isEmpty() && !mask.contains(mapToLocal(point).toPoint())) { 0085 return false; 0086 } else if (m_handle->property("outputOnly").toBool()) { 0087 return false; 0088 } 0089 0090 return true; 0091 } 0092 0093 void InternalWindow::pointerEnterEvent(const QPointF &globalPos) 0094 { 0095 Window::pointerEnterEvent(globalPos); 0096 0097 QEnterEvent enterEvent(pos(), pos(), globalPos); 0098 QCoreApplication::sendEvent(m_handle, &enterEvent); 0099 } 0100 0101 void InternalWindow::pointerLeaveEvent() 0102 { 0103 if (!m_handle) { 0104 return; 0105 } 0106 Window::pointerLeaveEvent(); 0107 0108 QEvent event(QEvent::Leave); 0109 QCoreApplication::sendEvent(m_handle, &event); 0110 } 0111 0112 bool InternalWindow::eventFilter(QObject *watched, QEvent *event) 0113 { 0114 if (watched == m_handle && event->type() == QEvent::DynamicPropertyChange) { 0115 QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent *>(event); 0116 if (pe->propertyName() == s_skipClosePropertyName) { 0117 setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); 0118 } 0119 if (pe->propertyName() == s_shadowEnabledPropertyName) { 0120 // Some dialog e.g. Plasma::Dialog may update shadow in the middle of rendering. 0121 // The opengl context changed by updateShadow may break the QML Window rendering 0122 // and cause crash. 0123 QMetaObject::invokeMethod(this, &InternalWindow::updateShadow, Qt::QueuedConnection); 0124 } 0125 if (pe->propertyName() == "kwin_windowType") { 0126 m_windowType = m_handle->property("kwin_windowType").value<NET::WindowType>(); 0127 workspace()->updateClientArea(); 0128 } 0129 } 0130 return false; 0131 } 0132 0133 qreal InternalWindow::bufferScale() const 0134 { 0135 if (m_handle) { 0136 return m_handle->devicePixelRatio(); 0137 } 0138 return 1; 0139 } 0140 0141 QString InternalWindow::captionNormal() const 0142 { 0143 return m_captionNormal; 0144 } 0145 0146 QString InternalWindow::captionSuffix() const 0147 { 0148 return m_captionSuffix; 0149 } 0150 0151 QSizeF InternalWindow::minSize() const 0152 { 0153 return m_handle->minimumSize(); 0154 } 0155 0156 QSizeF InternalWindow::maxSize() const 0157 { 0158 return m_handle->maximumSize(); 0159 } 0160 0161 NET::WindowType InternalWindow::windowType(bool direct, int supported_types) const 0162 { 0163 return m_windowType; 0164 } 0165 0166 void InternalWindow::killWindow() 0167 { 0168 // We don't kill our internal windows. 0169 } 0170 0171 bool InternalWindow::isPopupWindow() const 0172 { 0173 if (Window::isPopupWindow()) { 0174 return true; 0175 } 0176 return m_internalWindowFlags.testFlag(Qt::Popup); 0177 } 0178 0179 QString InternalWindow::windowRole() const 0180 { 0181 return QString(); 0182 } 0183 0184 void InternalWindow::closeWindow() 0185 { 0186 if (m_handle) { 0187 m_handle->hide(); 0188 } 0189 } 0190 0191 bool InternalWindow::isCloseable() const 0192 { 0193 return true; 0194 } 0195 0196 bool InternalWindow::isMovable() const 0197 { 0198 return true; 0199 } 0200 0201 bool InternalWindow::isMovableAcrossScreens() const 0202 { 0203 return true; 0204 } 0205 0206 bool InternalWindow::isResizable() const 0207 { 0208 return true; 0209 } 0210 0211 bool InternalWindow::isPlaceable() const 0212 { 0213 return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); 0214 } 0215 0216 bool InternalWindow::noBorder() const 0217 { 0218 return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); 0219 } 0220 0221 bool InternalWindow::userCanSetNoBorder() const 0222 { 0223 return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); 0224 } 0225 0226 bool InternalWindow::wantsInput() const 0227 { 0228 return false; 0229 } 0230 0231 bool InternalWindow::isInternal() const 0232 { 0233 return true; 0234 } 0235 0236 bool InternalWindow::isLockScreen() const 0237 { 0238 if (m_handle) { 0239 return m_handle->property("org_kde_ksld_emergency").toBool(); 0240 } 0241 return false; 0242 } 0243 0244 bool InternalWindow::isOutline() const 0245 { 0246 if (m_handle) { 0247 return m_handle->property("__kwin_outline").toBool(); 0248 } 0249 return false; 0250 } 0251 0252 bool InternalWindow::isShown() const 0253 { 0254 return readyForPainting(); 0255 } 0256 0257 bool InternalWindow::isHiddenInternal() const 0258 { 0259 return false; 0260 } 0261 0262 void InternalWindow::hideClient() 0263 { 0264 } 0265 0266 void InternalWindow::showClient() 0267 { 0268 } 0269 0270 QRectF InternalWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size) 0271 { 0272 if (!m_handle) { 0273 return geometry; 0274 } 0275 const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center()); 0276 return QRectF(moveResizeGeometry().topLeft(), size.boundedTo(area.size())); 0277 } 0278 0279 void InternalWindow::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) 0280 { 0281 if (areGeometryUpdatesBlocked()) { 0282 setPendingMoveResizeMode(mode); 0283 return; 0284 } 0285 0286 const QSizeF requestedClientSize = frameSizeToClientSize(rect.size()); 0287 if (clientSize() == requestedClientSize) { 0288 commitGeometry(rect); 0289 } else { 0290 requestGeometry(rect); 0291 } 0292 } 0293 0294 Window *InternalWindow::findModal(bool allow_itself) 0295 { 0296 return nullptr; 0297 } 0298 0299 bool InternalWindow::takeFocus() 0300 { 0301 return false; 0302 } 0303 0304 void InternalWindow::setNoBorder(bool set) 0305 { 0306 if (!userCanSetNoBorder()) { 0307 return; 0308 } 0309 if (m_userNoBorder == set) { 0310 return; 0311 } 0312 m_userNoBorder = set; 0313 updateDecoration(true); 0314 } 0315 0316 void InternalWindow::createDecoration(const QRectF &oldGeometry) 0317 { 0318 setDecoration(std::shared_ptr<KDecoration2::Decoration>(Workspace::self()->decorationBridge()->createDecoration(this))); 0319 moveResize(QRectF(oldGeometry.topLeft(), clientSizeToFrameSize(clientSize()))); 0320 0321 Q_EMIT geometryShapeChanged(this, oldGeometry); 0322 } 0323 0324 void InternalWindow::destroyDecoration() 0325 { 0326 const QSizeF clientSize = frameSizeToClientSize(moveResizeGeometry().size()); 0327 setDecoration(nullptr); 0328 resize(clientSize); 0329 } 0330 0331 void InternalWindow::updateDecoration(bool check_workspace_pos, bool force) 0332 { 0333 if (!force && isDecorated() == !noBorder()) { 0334 return; 0335 } 0336 0337 GeometryUpdatesBlocker blocker(this); 0338 0339 const QRectF oldFrameGeometry = frameGeometry(); 0340 if (force) { 0341 destroyDecoration(); 0342 } 0343 0344 if (!noBorder()) { 0345 createDecoration(oldFrameGeometry); 0346 } else { 0347 destroyDecoration(); 0348 } 0349 0350 updateShadow(); 0351 0352 if (check_workspace_pos) { 0353 checkWorkspacePosition(oldFrameGeometry); 0354 } 0355 } 0356 0357 void InternalWindow::invalidateDecoration() 0358 { 0359 updateDecoration(true, true); 0360 } 0361 0362 void InternalWindow::destroyWindow() 0363 { 0364 markAsZombie(); 0365 if (isInteractiveMoveResize()) { 0366 leaveInteractiveMoveResize(); 0367 Q_EMIT clientFinishUserMovedResized(this); 0368 } 0369 0370 Deleted *deleted = Deleted::create(this); 0371 Q_EMIT windowClosed(this, deleted); 0372 0373 destroyDecoration(); 0374 0375 workspace()->removeInternalWindow(this); 0376 0377 deleted->unrefWindow(); 0378 m_handle = nullptr; 0379 0380 delete this; 0381 } 0382 0383 bool InternalWindow::hasPopupGrab() const 0384 { 0385 return !m_handle->flags().testFlag(Qt::WindowTransparentForInput) && m_handle->flags().testFlag(Qt::Popup) && !m_handle->flags().testFlag(Qt::ToolTip); 0386 } 0387 0388 void InternalWindow::popupDone() 0389 { 0390 m_handle->hide(); 0391 } 0392 0393 void InternalWindow::present(const std::shared_ptr<QOpenGLFramebufferObject> fbo) 0394 { 0395 Q_ASSERT(m_internalImage.isNull()); 0396 0397 const QSizeF bufferSize = fbo->size() / bufferScale(); 0398 QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); 0399 if (isInteractiveResize()) { 0400 geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity()); 0401 } 0402 0403 commitGeometry(geometry); 0404 markAsMapped(); 0405 0406 m_internalFBO = fbo; 0407 0408 setDepth(32); 0409 surfaceItem()->addDamage(surfaceItem()->rect().toAlignedRect()); 0410 } 0411 0412 void InternalWindow::present(const QImage &image, const QRegion &damage) 0413 { 0414 Q_ASSERT(m_internalFBO == nullptr); 0415 0416 const QSize bufferSize = image.size() / bufferScale(); 0417 QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); 0418 if (isInteractiveResize()) { 0419 geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity()); 0420 } 0421 0422 commitGeometry(geometry); 0423 markAsMapped(); 0424 0425 m_internalImage = image; 0426 0427 setDepth(32); 0428 surfaceItem()->addDamage(damage); 0429 } 0430 0431 QWindow *InternalWindow::handle() const 0432 { 0433 return m_handle; 0434 } 0435 0436 bool InternalWindow::acceptsFocus() const 0437 { 0438 return false; 0439 } 0440 0441 bool InternalWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const 0442 { 0443 const InternalWindow *otherInternal = qobject_cast<const InternalWindow *>(other); 0444 if (!otherInternal) { 0445 return false; 0446 } 0447 if (otherInternal == this) { 0448 return true; 0449 } 0450 return otherInternal->handle()->isAncestorOf(handle()) || handle()->isAncestorOf(otherInternal->handle()); 0451 } 0452 0453 void InternalWindow::doInteractiveResizeSync(const QRectF &rect) 0454 { 0455 moveResize(rect); 0456 } 0457 0458 void InternalWindow::updateCaption() 0459 { 0460 const QString oldSuffix = m_captionSuffix; 0461 const auto shortcut = shortcutCaptionSuffix(); 0462 m_captionSuffix = shortcut; 0463 if ((!isSpecialWindow() || isToolbar()) && findWindowWithSameCaption()) { 0464 int i = 2; 0465 do { 0466 m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>'); 0467 i++; 0468 } while (findWindowWithSameCaption()); 0469 } 0470 if (m_captionSuffix != oldSuffix) { 0471 Q_EMIT captionChanged(); 0472 } 0473 } 0474 0475 void InternalWindow::requestGeometry(const QRectF &rect) 0476 { 0477 if (m_handle) { 0478 m_handle->setGeometry(frameRectToClientRect(rect).toRect()); 0479 } 0480 } 0481 0482 void InternalWindow::commitGeometry(const QRectF &rect) 0483 { 0484 // The client geometry and the buffer geometry are the same. 0485 const QRectF oldClientGeometry = m_clientGeometry; 0486 const QRectF oldFrameGeometry = m_frameGeometry; 0487 const Output *oldOutput = m_output; 0488 0489 Q_EMIT frameGeometryAboutToChange(this); 0490 0491 m_clientGeometry = frameRectToClientRect(rect); 0492 m_frameGeometry = rect; 0493 m_bufferGeometry = m_clientGeometry; 0494 0495 if (oldClientGeometry == m_clientGeometry && oldFrameGeometry == m_frameGeometry) { 0496 return; 0497 } 0498 0499 m_output = workspace()->outputAt(rect.center()); 0500 syncGeometryToInternalWindow(); 0501 0502 if (oldClientGeometry != m_clientGeometry) { 0503 Q_EMIT bufferGeometryChanged(this, oldClientGeometry); 0504 Q_EMIT clientGeometryChanged(this, oldClientGeometry); 0505 } 0506 if (oldFrameGeometry != m_frameGeometry) { 0507 Q_EMIT frameGeometryChanged(this, oldFrameGeometry); 0508 } 0509 if (oldOutput != m_output) { 0510 Q_EMIT screenChanged(); 0511 } 0512 Q_EMIT geometryShapeChanged(this, oldFrameGeometry); 0513 } 0514 0515 void InternalWindow::setCaption(const QString &caption) 0516 { 0517 if (m_captionNormal == caption) { 0518 return; 0519 } 0520 0521 m_captionNormal = caption; 0522 0523 const QString oldCaptionSuffix = m_captionSuffix; 0524 updateCaption(); 0525 0526 if (m_captionSuffix == oldCaptionSuffix) { 0527 Q_EMIT captionChanged(); 0528 } 0529 } 0530 0531 void InternalWindow::markAsMapped() 0532 { 0533 if (!ready_for_painting) { 0534 setupCompositing(); 0535 setReadyForPainting(); 0536 workspace()->addInternalWindow(this); 0537 } 0538 } 0539 0540 void InternalWindow::syncGeometryToInternalWindow() 0541 { 0542 if (m_handle->geometry() == frameRectToClientRect(frameGeometry())) { 0543 return; 0544 } 0545 0546 QTimer::singleShot(0, this, [this] { 0547 requestGeometry(frameGeometry()); 0548 }); 0549 } 0550 0551 void InternalWindow::updateInternalWindowGeometry() 0552 { 0553 if (!isInteractiveMoveResize()) { 0554 const QRectF rect = clientRectToFrameRect(m_handle->geometry()); 0555 setMoveResizeGeometry(rect); 0556 commitGeometry(rect); 0557 } 0558 } 0559 0560 }