kwin/src/internalwindow.cpp
Vlad Zahorodnii 176ae6e692 Drop window type rule
Practically all code assumes that the window type is static and fixing
it would bring extra complexity, which may not be worth given that there
are window rules to control position, focus, layer, etc.

BUG: 466016
2024-01-17 23:05:53 +00:00

516 lines
12 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Martin Flöser <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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 <KDecoration2/Decoration>
#include <QMouseEvent>
#include <QWindow>
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);
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<WindowItem> InternalWindow::createItem(Scene *scene)
{
return std::make_unique<WindowItemInternal>(this, scene);
}
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<QDynamicPropertyChangeEvent *>(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();
}
NET::WindowType InternalWindow::windowType() const
{
return NET::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 (m_handle) {
m_handle->hide();
}
}
bool InternalWindow::isCloseable() const
{
return true;
}
bool InternalWindow::isMovable() const
{
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)
{
if (areGeometryUpdatesBlocked()) {
setPendingMoveResizeMode(mode);
return;
}
const QSizeF requestedClientSize = frameSizeToClientSize(rect.size());
if (clientSize() == requestedClientSize) {
commitGeometry(rect);
} else {
requestGeometry(rect);
}
}
Window *InternalWindow::findModal(bool allow_itself)
{
return nullptr;
}
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<KDecoration2::Decoration>(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;
}
GeometryUpdatesBlocker blocker(this);
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();
if (isInteractiveMoveResize()) {
leaveInteractiveMoveResize();
Q_EMIT interactiveMoveResizeFinished();
}
Q_EMIT closed();
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<const InternalWindow *>(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 oldSuffix = m_captionSuffix;
const auto shortcut = shortcutCaptionSuffix();
m_captionSuffix = shortcut;
if ((!isSpecialWindow() || isToolbar()) && findWindowWithSameCaption()) {
int i = 2;
do {
m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>');
i++;
} while (findWindowWithSameCaption());
}
if (m_captionSuffix != oldSuffix) {
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;
const QString oldCaptionSuffix = m_captionSuffix;
updateCaption();
Q_EMIT captionNormalChanged();
if (m_captionSuffix == oldCaptionSuffix) {
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"