File indexing completed on 2024-02-18 16:20:04

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 }