File indexing completed on 2024-04-28 05:30:21

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 "scene/surfaceitem.h"
0013 #include "scene/windowitem.h"
0014 #include "workspace.h"
0015 
0016 #include <KDecoration2/Decoration>
0017 
0018 #include <QMouseEvent>
0019 #include <QWindow>
0020 
0021 Q_DECLARE_METATYPE(NET::WindowType)
0022 
0023 static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION");
0024 static const QByteArray s_shadowEnabledPropertyName = QByteArrayLiteral("kwin_shadow_enabled");
0025 
0026 namespace KWin
0027 {
0028 
0029 InternalWindow::InternalWindow(QWindow *handle)
0030     : m_handle(handle)
0031     , m_internalWindowFlags(handle->flags())
0032 {
0033     connect(m_handle, &QWindow::xChanged, this, &InternalWindow::updateInternalWindowGeometry);
0034     connect(m_handle, &QWindow::yChanged, this, &InternalWindow::updateInternalWindowGeometry);
0035     connect(m_handle, &QWindow::widthChanged, this, &InternalWindow::updateInternalWindowGeometry);
0036     connect(m_handle, &QWindow::heightChanged, this, &InternalWindow::updateInternalWindowGeometry);
0037     connect(m_handle, &QWindow::windowTitleChanged, this, &InternalWindow::setCaption);
0038     connect(m_handle, &QWindow::opacityChanged, this, &InternalWindow::setOpacity);
0039     connect(m_handle, &QWindow::destroyed, this, &InternalWindow::destroyWindow);
0040 
0041     setCaption(m_handle->title());
0042     setIcon(QIcon::fromTheme(QStringLiteral("kwin")));
0043     setOnAllDesktops(true);
0044     setOpacity(m_handle->opacity());
0045     setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool());
0046     updateColorScheme();
0047     updateShadow();
0048 
0049     setMoveResizeGeometry(m_handle->geometry());
0050     commitGeometry(m_handle->geometry());
0051 
0052     updateDecoration(true);
0053 
0054     m_handle->installEventFilter(this);
0055 }
0056 
0057 InternalWindow::~InternalWindow()
0058 {
0059 }
0060 
0061 std::unique_ptr<WindowItem> InternalWindow::createItem(Scene *scene)
0062 {
0063     return std::make_unique<WindowItemInternal>(this, scene);
0064 }
0065 
0066 bool InternalWindow::isClient() const
0067 {
0068     return true;
0069 }
0070 
0071 bool InternalWindow::hitTest(const QPointF &point) const
0072 {
0073     if (!Window::hitTest(point)) {
0074         return false;
0075     }
0076 
0077     const QRegion mask = m_handle->mask();
0078     if (!mask.isEmpty() && !mask.contains(mapToLocal(point).toPoint())) {
0079         return false;
0080     } else if (m_handle->property("outputOnly").toBool()) {
0081         return false;
0082     }
0083 
0084     return true;
0085 }
0086 
0087 void InternalWindow::pointerEnterEvent(const QPointF &globalPos)
0088 {
0089     Window::pointerEnterEvent(globalPos);
0090 
0091     QEnterEvent enterEvent(pos(), pos(), globalPos);
0092     QCoreApplication::sendEvent(m_handle, &enterEvent);
0093 }
0094 
0095 void InternalWindow::pointerLeaveEvent()
0096 {
0097     if (!m_handle) {
0098         return;
0099     }
0100     Window::pointerLeaveEvent();
0101 
0102     QEvent event(QEvent::Leave);
0103     QCoreApplication::sendEvent(m_handle, &event);
0104 }
0105 
0106 bool InternalWindow::eventFilter(QObject *watched, QEvent *event)
0107 {
0108     if (watched == m_handle && event->type() == QEvent::DynamicPropertyChange) {
0109         QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent *>(event);
0110         if (pe->propertyName() == s_skipClosePropertyName) {
0111             setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool());
0112         }
0113         if (pe->propertyName() == s_shadowEnabledPropertyName) {
0114             updateShadow();
0115         }
0116     }
0117     return false;
0118 }
0119 
0120 qreal InternalWindow::bufferScale() const
0121 {
0122     if (m_handle) {
0123         return m_handle->devicePixelRatio();
0124     }
0125     return 1;
0126 }
0127 
0128 QString InternalWindow::captionNormal() const
0129 {
0130     return m_captionNormal;
0131 }
0132 
0133 QString InternalWindow::captionSuffix() const
0134 {
0135     return m_captionSuffix;
0136 }
0137 
0138 QSizeF InternalWindow::minSize() const
0139 {
0140     return m_handle->minimumSize();
0141 }
0142 
0143 QSizeF InternalWindow::maxSize() const
0144 {
0145     return m_handle->maximumSize();
0146 }
0147 
0148 NET::WindowType InternalWindow::windowType() const
0149 {
0150     return NET::Normal;
0151 }
0152 
0153 void InternalWindow::killWindow()
0154 {
0155     // We don't kill our internal windows.
0156 }
0157 
0158 bool InternalWindow::isPopupWindow() const
0159 {
0160     if (Window::isPopupWindow()) {
0161         return true;
0162     }
0163     return m_internalWindowFlags.testFlag(Qt::Popup);
0164 }
0165 
0166 QString InternalWindow::windowRole() const
0167 {
0168     return QString();
0169 }
0170 
0171 void InternalWindow::closeWindow()
0172 {
0173     if (m_handle) {
0174         m_handle->hide();
0175     }
0176 }
0177 
0178 bool InternalWindow::isCloseable() const
0179 {
0180     return true;
0181 }
0182 
0183 bool InternalWindow::isMovable() const
0184 {
0185     return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup);
0186 }
0187 
0188 bool InternalWindow::isMovableAcrossScreens() const
0189 {
0190     return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup);
0191 }
0192 
0193 bool InternalWindow::isResizable() const
0194 {
0195     return true;
0196 }
0197 
0198 bool InternalWindow::isPlaceable() const
0199 {
0200     return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup);
0201 }
0202 
0203 bool InternalWindow::noBorder() const
0204 {
0205     return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
0206 }
0207 
0208 bool InternalWindow::userCanSetNoBorder() const
0209 {
0210     return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
0211 }
0212 
0213 bool InternalWindow::wantsInput() const
0214 {
0215     return false;
0216 }
0217 
0218 bool InternalWindow::isInternal() const
0219 {
0220     return true;
0221 }
0222 
0223 bool InternalWindow::isLockScreen() const
0224 {
0225     if (m_handle) {
0226         return m_handle->property("org_kde_ksld_emergency").toBool();
0227     }
0228     return false;
0229 }
0230 
0231 bool InternalWindow::isOutline() const
0232 {
0233     if (m_handle) {
0234         return m_handle->property("__kwin_outline").toBool();
0235     }
0236     return false;
0237 }
0238 
0239 QRectF InternalWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size)
0240 {
0241     if (!m_handle) {
0242         return geometry;
0243     }
0244     const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center());
0245     return QRectF(moveResizeGeometry().topLeft(), size.boundedTo(area.size()));
0246 }
0247 
0248 void InternalWindow::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
0249 {
0250     if (areGeometryUpdatesBlocked()) {
0251         setPendingMoveResizeMode(mode);
0252         return;
0253     }
0254 
0255     const QSizeF requestedClientSize = frameSizeToClientSize(rect.size());
0256     if (clientSize() == requestedClientSize) {
0257         commitGeometry(rect);
0258     } else {
0259         requestGeometry(rect);
0260     }
0261 }
0262 
0263 Window *InternalWindow::findModal(bool allow_itself)
0264 {
0265     return nullptr;
0266 }
0267 
0268 bool InternalWindow::takeFocus()
0269 {
0270     return false;
0271 }
0272 
0273 void InternalWindow::setNoBorder(bool set)
0274 {
0275     if (!userCanSetNoBorder()) {
0276         return;
0277     }
0278     if (m_userNoBorder == set) {
0279         return;
0280     }
0281     m_userNoBorder = set;
0282     updateDecoration(true);
0283 }
0284 
0285 void InternalWindow::createDecoration(const QRectF &oldGeometry)
0286 {
0287     setDecoration(std::shared_ptr<KDecoration2::Decoration>(Workspace::self()->decorationBridge()->createDecoration(this)));
0288     moveResize(QRectF(oldGeometry.topLeft(), clientSizeToFrameSize(clientSize())));
0289 }
0290 
0291 void InternalWindow::destroyDecoration()
0292 {
0293     const QSizeF clientSize = frameSizeToClientSize(moveResizeGeometry().size());
0294     setDecoration(nullptr);
0295     resize(clientSize);
0296 }
0297 
0298 void InternalWindow::updateDecoration(bool check_workspace_pos, bool force)
0299 {
0300     if (!force && isDecorated() == !noBorder()) {
0301         return;
0302     }
0303 
0304     GeometryUpdatesBlocker blocker(this);
0305 
0306     const QRectF oldFrameGeometry = frameGeometry();
0307     if (force) {
0308         destroyDecoration();
0309     }
0310 
0311     if (!noBorder()) {
0312         createDecoration(oldFrameGeometry);
0313     } else {
0314         destroyDecoration();
0315     }
0316 
0317     updateShadow();
0318 
0319     if (check_workspace_pos) {
0320         checkWorkspacePosition(oldFrameGeometry);
0321     }
0322 }
0323 
0324 void InternalWindow::invalidateDecoration()
0325 {
0326     updateDecoration(true, true);
0327 }
0328 
0329 void InternalWindow::destroyWindow()
0330 {
0331     m_handle->removeEventFilter(this);
0332     m_handle->disconnect(this);
0333 
0334     markAsDeleted();
0335     if (isInteractiveMoveResize()) {
0336         leaveInteractiveMoveResize();
0337         Q_EMIT interactiveMoveResizeFinished();
0338     }
0339 
0340     Q_EMIT closed();
0341 
0342     workspace()->removeInternalWindow(this);
0343     m_handle = nullptr;
0344 
0345     unref();
0346 }
0347 
0348 bool InternalWindow::hasPopupGrab() const
0349 {
0350     return !m_handle->flags().testFlag(Qt::WindowTransparentForInput) && m_handle->flags().testFlag(Qt::Popup) && !m_handle->flags().testFlag(Qt::ToolTip);
0351 }
0352 
0353 void InternalWindow::popupDone()
0354 {
0355     m_handle->close();
0356 }
0357 
0358 GraphicsBuffer *InternalWindow::graphicsBuffer() const
0359 {
0360     return m_graphicsBufferRef.buffer();
0361 }
0362 
0363 GraphicsBufferOrigin InternalWindow::graphicsBufferOrigin() const
0364 {
0365     return m_graphicsBufferOrigin;
0366 }
0367 
0368 void InternalWindow::present(const InternalWindowFrame &frame)
0369 {
0370     const QSize bufferSize = frame.buffer->size() / bufferScale();
0371     QRectF geometry(pos(), clientSizeToFrameSize(bufferSize));
0372     if (isInteractiveResize()) {
0373         geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity());
0374     }
0375 
0376     commitGeometry(geometry);
0377     markAsMapped();
0378 
0379     m_graphicsBufferRef = frame.buffer;
0380     m_graphicsBufferOrigin = frame.bufferOrigin;
0381 
0382     surfaceItem()->addDamage(frame.bufferDamage);
0383 }
0384 
0385 QWindow *InternalWindow::handle() const
0386 {
0387     return m_handle;
0388 }
0389 
0390 bool InternalWindow::acceptsFocus() const
0391 {
0392     return false;
0393 }
0394 
0395 bool InternalWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const
0396 {
0397     const InternalWindow *otherInternal = qobject_cast<const InternalWindow *>(other);
0398     if (!otherInternal) {
0399         return false;
0400     }
0401     if (otherInternal == this) {
0402         return true;
0403     }
0404     return otherInternal->handle()->isAncestorOf(handle()) || handle()->isAncestorOf(otherInternal->handle());
0405 }
0406 
0407 void InternalWindow::doInteractiveResizeSync(const QRectF &rect)
0408 {
0409     moveResize(rect);
0410 }
0411 
0412 void InternalWindow::updateCaption()
0413 {
0414     const QString suffix = shortcutCaptionSuffix();
0415     if (m_captionSuffix != suffix) {
0416         m_captionSuffix = suffix;
0417         Q_EMIT captionChanged();
0418     }
0419 }
0420 
0421 void InternalWindow::requestGeometry(const QRectF &rect)
0422 {
0423     if (m_handle) {
0424         m_handle->setGeometry(frameRectToClientRect(rect).toRect());
0425     }
0426 }
0427 
0428 void InternalWindow::commitGeometry(const QRectF &rect)
0429 {
0430     // The client geometry and the buffer geometry are the same.
0431     const QRectF oldClientGeometry = m_clientGeometry;
0432     const QRectF oldFrameGeometry = m_frameGeometry;
0433     const Output *oldOutput = m_output;
0434 
0435     Q_EMIT frameGeometryAboutToChange();
0436 
0437     m_clientGeometry = frameRectToClientRect(rect);
0438     m_frameGeometry = rect;
0439     m_bufferGeometry = m_clientGeometry;
0440 
0441     if (oldClientGeometry == m_clientGeometry && oldFrameGeometry == m_frameGeometry) {
0442         return;
0443     }
0444 
0445     m_output = workspace()->outputAt(rect.center());
0446     syncGeometryToInternalWindow();
0447 
0448     if (oldClientGeometry != m_clientGeometry) {
0449         Q_EMIT bufferGeometryChanged(oldClientGeometry);
0450         Q_EMIT clientGeometryChanged(oldClientGeometry);
0451     }
0452     if (oldFrameGeometry != m_frameGeometry) {
0453         Q_EMIT frameGeometryChanged(oldFrameGeometry);
0454     }
0455     if (oldOutput != m_output) {
0456         Q_EMIT outputChanged();
0457     }
0458 }
0459 
0460 void InternalWindow::setCaption(const QString &caption)
0461 {
0462     if (m_captionNormal == caption) {
0463         return;
0464     }
0465 
0466     m_captionNormal = caption;
0467     Q_EMIT captionNormalChanged();
0468     Q_EMIT captionChanged();
0469 }
0470 
0471 void InternalWindow::markAsMapped()
0472 {
0473     if (!ready_for_painting) {
0474         setupCompositing();
0475         setReadyForPainting();
0476         workspace()->addInternalWindow(this);
0477     }
0478 }
0479 
0480 void InternalWindow::syncGeometryToInternalWindow()
0481 {
0482     if (m_handle->geometry() == frameRectToClientRect(frameGeometry())) {
0483         return;
0484     }
0485 
0486     QTimer::singleShot(0, this, [this] {
0487         requestGeometry(frameGeometry());
0488     });
0489 }
0490 
0491 void InternalWindow::updateInternalWindowGeometry()
0492 {
0493     if (!isInteractiveMoveResize()) {
0494         const QRectF rect = clientRectToFrameRect(m_handle->geometry());
0495         setMoveResizeGeometry(rect);
0496         commitGeometry(rect);
0497     }
0498 }
0499 
0500 }
0501 
0502 #include "moc_internalwindow.cpp"