/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2019 Martin Flöser SPDX-FileCopyrightText: 2019 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #include "internalwindow.h" #include "decorations/decorationbridge.h" #include "scene/surfaceitem.h" #include "scene/windowitem.h" #include "workspace.h" #include #include #include #include Q_DECLARE_METATYPE(NET::WindowType) static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); static const QByteArray s_shadowEnabledPropertyName = QByteArrayLiteral("kwin_shadow_enabled"); namespace KWin { InternalWindow::InternalWindow(QWindow *handle) : m_handle(handle) , m_internalWindowFlags(handle->flags()) { connect(m_handle, &QWindow::xChanged, this, &InternalWindow::updateInternalWindowGeometry); connect(m_handle, &QWindow::yChanged, this, &InternalWindow::updateInternalWindowGeometry); connect(m_handle, &QWindow::widthChanged, this, &InternalWindow::updateInternalWindowGeometry); connect(m_handle, &QWindow::heightChanged, this, &InternalWindow::updateInternalWindowGeometry); connect(m_handle, &QWindow::windowTitleChanged, this, &InternalWindow::setCaption); connect(m_handle, &QWindow::opacityChanged, this, &InternalWindow::setOpacity); connect(m_handle, &QWindow::destroyed, this, &InternalWindow::destroyWindow); setOutput(workspace()->activeOutput()); setMoveResizeOutput(workspace()->activeOutput()); setCaption(m_handle->title()); setIcon(QIcon::fromTheme(QStringLiteral("kwin"))); setOnAllDesktops(true); setOpacity(m_handle->opacity()); setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); updateColorScheme(); updateShadow(); setMoveResizeGeometry(m_handle->geometry()); commitGeometry(m_handle->geometry()); updateDecoration(true); m_handle->installEventFilter(this); } InternalWindow::~InternalWindow() { } std::unique_ptr InternalWindow::createItem(Item *parentItem) { return std::make_unique(this, parentItem); } bool InternalWindow::isClient() const { return true; } bool InternalWindow::hitTest(const QPointF &point) const { if (!Window::hitTest(point)) { return false; } const QRegion mask = m_handle->mask(); if (!mask.isEmpty() && !mask.contains(mapToLocal(point).toPoint())) { return false; } else if (m_handle->property("outputOnly").toBool()) { return false; } return true; } void InternalWindow::pointerEnterEvent(const QPointF &globalPos) { Window::pointerEnterEvent(globalPos); QEnterEvent enterEvent(pos(), pos(), globalPos); QCoreApplication::sendEvent(m_handle, &enterEvent); } void InternalWindow::pointerLeaveEvent() { if (!m_handle) { return; } Window::pointerLeaveEvent(); QEvent event(QEvent::Leave); QCoreApplication::sendEvent(m_handle, &event); } bool InternalWindow::eventFilter(QObject *watched, QEvent *event) { if (watched == m_handle && event->type() == QEvent::DynamicPropertyChange) { QDynamicPropertyChangeEvent *pe = static_cast(event); if (pe->propertyName() == s_skipClosePropertyName) { setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); } if (pe->propertyName() == s_shadowEnabledPropertyName) { updateShadow(); } } return false; } qreal InternalWindow::bufferScale() const { if (m_handle) { return m_handle->devicePixelRatio(); } return 1; } QString InternalWindow::captionNormal() const { return m_captionNormal; } QString InternalWindow::captionSuffix() const { return m_captionSuffix; } QSizeF InternalWindow::minSize() const { return m_handle->minimumSize(); } QSizeF InternalWindow::maxSize() const { return m_handle->maximumSize(); } WindowType InternalWindow::windowType() const { return WindowType::Normal; } void InternalWindow::killWindow() { // We don't kill our internal windows. } bool InternalWindow::isPopupWindow() const { if (Window::isPopupWindow()) { return true; } return m_internalWindowFlags.testFlag(Qt::Popup); } QString InternalWindow::windowRole() const { return QString(); } void InternalWindow::closeWindow() { if (!isDeleted()) { QWindowSystemInterface::handleCloseEvent(m_handle); } } bool InternalWindow::isCloseable() const { return true; } bool InternalWindow::isMovable() const { if (!options->interactiveWindowMoveEnabled()) { return false; } return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalWindow::isMovableAcrossScreens() const { return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalWindow::isResizable() const { return true; } bool InternalWindow::isPlaceable() const { return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalWindow::noBorder() const { return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalWindow::userCanSetNoBorder() const { return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); } bool InternalWindow::wantsInput() const { return false; } bool InternalWindow::isInternal() const { return true; } bool InternalWindow::isLockScreen() const { if (m_handle) { return m_handle->property("org_kde_ksld_emergency").toBool(); } return false; } bool InternalWindow::isOutline() const { if (m_handle) { return m_handle->property("__kwin_outline").toBool(); } return false; } QRectF InternalWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size) { if (!m_handle) { return geometry; } const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center()); return QRectF(moveResizeGeometry().topLeft(), size.boundedTo(area.size())); } void InternalWindow::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) { const QSizeF requestedClientSize = frameSizeToClientSize(rect.size()); if (clientSize() == requestedClientSize) { commitGeometry(rect); } else { requestGeometry(rect); } } bool InternalWindow::takeFocus() { return false; } void InternalWindow::setNoBorder(bool set) { if (!userCanSetNoBorder()) { return; } if (m_userNoBorder == set) { return; } m_userNoBorder = set; updateDecoration(true); } void InternalWindow::createDecoration(const QRectF &oldGeometry) { setDecoration(std::shared_ptr(Workspace::self()->decorationBridge()->createDecoration(this))); moveResize(QRectF(oldGeometry.topLeft(), clientSizeToFrameSize(clientSize()))); } void InternalWindow::destroyDecoration() { const QSizeF clientSize = frameSizeToClientSize(moveResizeGeometry().size()); setDecoration(nullptr); resize(clientSize); } void InternalWindow::updateDecoration(bool check_workspace_pos, bool force) { if (!force && isDecorated() == !noBorder()) { return; } const QRectF oldFrameGeometry = frameGeometry(); if (force) { destroyDecoration(); } if (!noBorder()) { createDecoration(oldFrameGeometry); } else { destroyDecoration(); } updateShadow(); if (check_workspace_pos) { checkWorkspacePosition(oldFrameGeometry); } } void InternalWindow::invalidateDecoration() { updateDecoration(true, true); } void InternalWindow::destroyWindow() { m_handle->removeEventFilter(this); m_handle->disconnect(this); markAsDeleted(); stopDelayedInteractiveMoveResize(); if (isInteractiveMoveResize()) { leaveInteractiveMoveResize(); Q_EMIT interactiveMoveResizeFinished(); } Q_EMIT closed(); commitTile(nullptr); workspace()->removeInternalWindow(this); m_handle = nullptr; unref(); } bool InternalWindow::hasPopupGrab() const { return !m_handle->flags().testFlag(Qt::WindowTransparentForInput) && m_handle->flags().testFlag(Qt::Popup) && !m_handle->flags().testFlag(Qt::ToolTip); } void InternalWindow::popupDone() { m_handle->close(); } GraphicsBuffer *InternalWindow::graphicsBuffer() const { return m_graphicsBufferRef.buffer(); } GraphicsBufferOrigin InternalWindow::graphicsBufferOrigin() const { return m_graphicsBufferOrigin; } void InternalWindow::present(const InternalWindowFrame &frame) { const QSize bufferSize = frame.buffer->size() / bufferScale(); QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); if (isInteractiveResize()) { geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity()); } commitGeometry(geometry); markAsMapped(); m_graphicsBufferRef = frame.buffer; m_graphicsBufferOrigin = frame.bufferOrigin; surfaceItem()->addDamage(frame.bufferDamage); } QWindow *InternalWindow::handle() const { return m_handle; } bool InternalWindow::acceptsFocus() const { return false; } bool InternalWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const { const InternalWindow *otherInternal = qobject_cast(other); if (!otherInternal) { return false; } if (otherInternal == this) { return true; } return otherInternal->handle()->isAncestorOf(handle()) || handle()->isAncestorOf(otherInternal->handle()); } void InternalWindow::doInteractiveResizeSync(const QRectF &rect) { moveResize(rect); } void InternalWindow::updateCaption() { const QString suffix = shortcutCaptionSuffix(); if (m_captionSuffix != suffix) { m_captionSuffix = suffix; Q_EMIT captionChanged(); } } void InternalWindow::requestGeometry(const QRectF &rect) { if (m_handle) { m_handle->setGeometry(frameRectToClientRect(rect).toRect()); } } void InternalWindow::commitGeometry(const QRectF &rect) { // The client geometry and the buffer geometry are the same. const QRectF oldClientGeometry = m_clientGeometry; const QRectF oldFrameGeometry = m_frameGeometry; const Output *oldOutput = m_output; Q_EMIT frameGeometryAboutToChange(); m_clientGeometry = frameRectToClientRect(rect); m_frameGeometry = rect; m_bufferGeometry = m_clientGeometry; if (oldClientGeometry == m_clientGeometry && oldFrameGeometry == m_frameGeometry) { return; } m_output = workspace()->outputAt(rect.center()); syncGeometryToInternalWindow(); if (oldClientGeometry != m_clientGeometry) { Q_EMIT bufferGeometryChanged(oldClientGeometry); Q_EMIT clientGeometryChanged(oldClientGeometry); } if (oldFrameGeometry != m_frameGeometry) { Q_EMIT frameGeometryChanged(oldFrameGeometry); } if (oldOutput != m_output) { Q_EMIT outputChanged(); } } void InternalWindow::setCaption(const QString &caption) { if (m_captionNormal == caption) { return; } m_captionNormal = caption; Q_EMIT captionNormalChanged(); Q_EMIT captionChanged(); } void InternalWindow::markAsMapped() { if (!ready_for_painting) { setupCompositing(); setReadyForPainting(); workspace()->addInternalWindow(this); } } void InternalWindow::syncGeometryToInternalWindow() { if (m_handle->geometry() == frameRectToClientRect(frameGeometry())) { return; } QTimer::singleShot(0, this, [this] { requestGeometry(frameGeometry()); }); } void InternalWindow::updateInternalWindowGeometry() { if (!isInteractiveMoveResize()) { const QRectF rect = clientRectToFrameRect(m_handle->geometry()); setMoveResizeGeometry(rect); commitGeometry(rect); } } } #include "moc_internalwindow.cpp"