2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2019 Martin Flöser <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2022-04-22 17:45:19 +00:00
|
|
|
#include "internalwindow.h"
|
2019-08-26 07:44:04 +00:00
|
|
|
#include "decorations/decorationbridge.h"
|
|
|
|
#include "deleted.h"
|
2021-11-26 10:03:14 +00:00
|
|
|
#include "platform.h"
|
2021-02-04 09:07:20 +00:00
|
|
|
#include "surfaceitem.h"
|
2022-05-09 15:47:12 +00:00
|
|
|
#include "windowitem.h"
|
2019-01-27 19:48:00 +00:00
|
|
|
#include "workspace.h"
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
#include <KDecoration2/Decoration>
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2021-01-30 20:29:55 +00:00
|
|
|
#include <QMouseEvent>
|
2019-01-27 19:48:00 +00:00
|
|
|
#include <QOpenGLFramebufferObject>
|
2019-08-26 07:44:04 +00:00
|
|
|
#include <QWindow>
|
2019-01-27 19:48:00 +00:00
|
|
|
|
|
|
|
Q_DECLARE_METATYPE(NET::WindowType)
|
|
|
|
|
|
|
|
static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION");
|
2020-01-01 01:11:17 +00:00
|
|
|
static const QByteArray s_shadowEnabledPropertyName = QByteArrayLiteral("kwin_shadow_enabled");
|
2019-01-27 19:48:00 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2022-04-28 12:00:38 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
const QVariant windowType = m_handle->property("kwin_windowType");
|
2019-08-26 07:44:04 +00:00
|
|
|
if (!windowType.isNull()) {
|
|
|
|
m_windowType = windowType.value<NET::WindowType>();
|
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2022-04-28 12:00:38 +00:00
|
|
|
setCaption(m_handle->title());
|
2019-08-26 07:44:04 +00:00
|
|
|
setIcon(QIcon::fromTheme(QStringLiteral("kwin")));
|
|
|
|
setOnAllDesktops(true);
|
2022-04-28 12:00:38 +00:00
|
|
|
setOpacity(m_handle->opacity());
|
|
|
|
setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool());
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2020-01-01 01:11:17 +00:00
|
|
|
// Create scene window, effect window, and update server-side shadow.
|
2019-08-26 07:44:04 +00:00
|
|
|
setupCompositing();
|
|
|
|
updateColorScheme();
|
2019-07-01 19:20:18 +00:00
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
blockGeometryUpdates(true);
|
2022-04-28 12:00:38 +00:00
|
|
|
commitGeometry(m_handle->geometry());
|
2019-08-26 07:44:04 +00:00
|
|
|
updateDecoration(true);
|
2022-04-28 12:00:38 +00:00
|
|
|
moveResize(clientRectToFrameRect(m_handle->geometry()));
|
2019-08-26 07:44:04 +00:00
|
|
|
blockGeometryUpdates(false);
|
|
|
|
|
2022-04-28 12:00:38 +00:00
|
|
|
m_handle->installEventFilter(this);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
InternalWindow::~InternalWindow()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-05-09 15:47:12 +00:00
|
|
|
WindowItem *InternalWindow::createItem()
|
|
|
|
{
|
|
|
|
return new WindowItemInternal(this);
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isClient() const
|
2021-05-25 16:06:17 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::hitTest(const QPoint &point) const
|
2021-05-09 15:07:36 +00:00
|
|
|
{
|
2022-04-22 17:39:12 +00:00
|
|
|
if (!Window::hitTest(point)) {
|
2021-05-09 15:07:36 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-28 12:00:38 +00:00
|
|
|
const QRegion mask = m_handle->mask();
|
2021-05-09 15:07:36 +00:00
|
|
|
if (!mask.isEmpty() && !mask.contains(mapToLocal(point))) {
|
|
|
|
return false;
|
2022-04-28 12:00:38 +00:00
|
|
|
} else if (m_handle->property("outputOnly").toBool()) {
|
2021-05-09 15:07:36 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::pointerEnterEvent(const QPoint &globalPos)
|
2021-11-29 20:19:38 +00:00
|
|
|
{
|
2022-04-22 17:39:12 +00:00
|
|
|
Window::pointerEnterEvent(globalPos);
|
2021-11-29 20:19:38 +00:00
|
|
|
|
|
|
|
QEnterEvent enterEvent(pos(), pos(), globalPos);
|
2022-04-28 12:00:38 +00:00
|
|
|
QCoreApplication::sendEvent(m_handle, &enterEvent);
|
2021-11-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::pointerLeaveEvent()
|
2021-11-29 20:19:38 +00:00
|
|
|
{
|
2022-04-22 17:39:12 +00:00
|
|
|
Window::pointerLeaveEvent();
|
2021-11-29 20:19:38 +00:00
|
|
|
|
|
|
|
QEvent event(QEvent::Leave);
|
2022-04-28 12:00:38 +00:00
|
|
|
QCoreApplication::sendEvent(m_handle, &event);
|
2021-11-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::eventFilter(QObject *watched, QEvent *event)
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (watched == m_handle && event->type() == QEvent::DynamicPropertyChange) {
|
2022-03-23 10:13:38 +00:00
|
|
|
QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent *>(event);
|
2019-01-27 19:48:00 +00:00
|
|
|
if (pe->propertyName() == s_skipClosePropertyName) {
|
2022-04-28 12:00:38 +00:00
|
|
|
setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool());
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2020-01-01 01:11:17 +00:00
|
|
|
if (pe->propertyName() == s_shadowEnabledPropertyName) {
|
2022-06-20 02:16:51 +00:00
|
|
|
// Some dialog e.g. Plasma::Dialog may update shadow in the middle of rendering.
|
|
|
|
// The opengl context changed by updateShadow may break the QML Window rendering
|
|
|
|
// and cause crash.
|
|
|
|
QMetaObject::invokeMethod(
|
|
|
|
this, [this]() {
|
|
|
|
updateShadow();
|
|
|
|
},
|
|
|
|
Qt::QueuedConnection);
|
2020-01-01 01:11:17 +00:00
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
if (pe->propertyName() == "kwin_windowType") {
|
2022-04-28 12:00:38 +00:00
|
|
|
m_windowType = m_handle->property("kwin_windowType").value<NET::WindowType>();
|
2019-01-27 19:48:00 +00:00
|
|
|
workspace()->updateClientArea();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
qreal InternalWindow::bufferScale() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle) {
|
|
|
|
return m_handle->devicePixelRatio();
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
QString InternalWindow::captionNormal() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
return m_captionNormal;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
QString InternalWindow::captionSuffix() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
return m_captionSuffix;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
QSize InternalWindow::minSize() const
|
2020-02-12 10:44:10 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
return m_handle->minimumSize();
|
2020-02-12 10:44:10 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
QSize InternalWindow::maxSize() const
|
2020-02-12 10:44:10 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
return m_handle->maximumSize();
|
2020-02-12 10:44:10 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
NET::WindowType InternalWindow::windowType(bool direct, int supported_types) const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(direct)
|
|
|
|
Q_UNUSED(supported_types)
|
|
|
|
return m_windowType;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::killWindow()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
// We don't kill our internal windows.
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isPopupWindow() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2022-04-22 17:39:12 +00:00
|
|
|
if (Window::isPopupWindow()) {
|
2019-01-27 19:48:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return m_internalWindowFlags.testFlag(Qt::Popup);
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
QByteArray InternalWindow::windowRole() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
return QByteArray();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::closeWindow()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle) {
|
|
|
|
m_handle->hide();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isCloseable() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isMovable() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isMovableAcrossScreens() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isResizable() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isPlaceable() const
|
2020-09-23 13:24:10 +00:00
|
|
|
{
|
2020-09-23 15:18:40 +00:00
|
|
|
return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup);
|
2020-09-23 13:24:10 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::noBorder() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::userCanSetNoBorder() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::wantsInput() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isInternal() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isLockScreen() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle) {
|
|
|
|
return m_handle->property("org_kde_ksld_emergency").toBool();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isOutline() const
|
2019-03-19 14:01:29 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle) {
|
|
|
|
return m_handle->property("__kwin_outline").toBool();
|
2019-03-19 14:01:29 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isShown() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
return readyForPainting();
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::isHiddenInternal() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::hideClient()
|
2021-11-03 09:40:31 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::showClient()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::resizeWithChecks(const QSize &size)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (!m_handle) {
|
2019-01-27 19:48:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-03-25 15:15:23 +00:00
|
|
|
const QRect area = workspace()->clientArea(WorkArea, this);
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
resize(size.boundedTo(area.size()));
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (areGeometryUpdatesBlocked()) {
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
setPendingMoveResizeMode(mode);
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
const QSize requestedClientSize = frameSizeToClientSize(rect.size());
|
|
|
|
if (clientSize() == requestedClientSize) {
|
2019-08-26 07:44:04 +00:00
|
|
|
commitGeometry(rect);
|
|
|
|
} else {
|
|
|
|
requestGeometry(rect);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
Window *InternalWindow::findModal(bool allow_itself)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(allow_itself)
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::takeFocus()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2020-07-22 11:00:11 +00:00
|
|
|
return false;
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::setNoBorder(bool set)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
if (!userCanSetNoBorder()) {
|
2019-01-27 19:48:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
if (m_userNoBorder == set) {
|
|
|
|
return;
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
m_userNoBorder = set;
|
|
|
|
updateDecoration(true);
|
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::createDecoration(const QRect &oldGeometry)
|
2021-12-09 11:55:13 +00:00
|
|
|
{
|
2022-05-17 10:36:34 +00:00
|
|
|
setDecoration(std::shared_ptr<KDecoration2::Decoration>(Decoration::DecorationBridge::self()->createDecoration(this)));
|
2021-12-09 11:55:13 +00:00
|
|
|
moveResize(oldGeometry);
|
|
|
|
|
|
|
|
Q_EMIT geometryShapeChanged(this, oldGeometry);
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::destroyDecoration()
|
2021-12-09 11:55:13 +00:00
|
|
|
{
|
|
|
|
const QSize clientSize = frameSizeToClientSize(moveResizeGeometry().size());
|
|
|
|
setDecoration(nullptr);
|
|
|
|
resize(clientSize);
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::updateDecoration(bool check_workspace_pos, bool force)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
if (!force && isDecorated() == !noBorder()) {
|
|
|
|
return;
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
GeometryUpdatesBlocker blocker(this);
|
|
|
|
|
Make checkWorkspacePosition() work without client geometry
A good portion of geometry handling code was written during the X11
times. The main difference between X11 and Wayland is that kwin doesn't
know where a window will exactly be after resize() or moveResize().
In order to handle Wayland specifics, every window has a bounding
geometry that is being manipulated by move(), resize(), and moveResize().
The frameGeometry(), the clientGeometry(), and the bufferGeometry() are
not manipulated by move(), resize(), and moveResize() directly. Almost
everything that manipulates geometry should use moveResizeGeometry().
This creates a problem though, since the clientGeometry() will be
updated only after the client provides a new buffer, kwin has absolutely
no idea what the client geometry for a given move resize geometry will
be.
Another side of the coin is that decoration updates are performed
asynchronously on wayland, meaning that you cannot use border properties
for anything related to geometry handling and you should avoid using
borderLeft(), borderTop(), borderRight(), and borderBottom() in general.
clientGeometry(), bufferGeometry(), and border*() are good only if you
want to forward an event or render something. They can't be used for
manipulating the geometry.
Unfortunately, AbstractClient::checkWorkspacePosition() needs both,
which is a bit of a problem. To add more oil to the fire, contents
of a decorated window can be snapped to a screen edge. This goes against
the nature of geometry updates on wayland, where we try to indicate
the bounds of the frame geometry and avoid using client and buffer
geometries.
In order to make geometry handling more correct on wayland, this change
removes the ability to snap the contents of a decorated window to a
screen edge. This allows to avoid using the client geometry in
checkWorkspacePosition(), which is a very important function that ensures
the window is inside the workspace.
There is nothing wrong with snapping the frame rather than its contents
and that's what kwin used to do. It was changed with the removal of
"Display borders on maximized windows" option, the relevant commit
didn't provide any reasoning behind the change.
2022-01-14 16:07:44 +00:00
|
|
|
const QRect oldFrameGeometry = frameGeometry();
|
2019-08-26 07:44:04 +00:00
|
|
|
if (force) {
|
|
|
|
destroyDecoration();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
if (!noBorder()) {
|
Make checkWorkspacePosition() work without client geometry
A good portion of geometry handling code was written during the X11
times. The main difference between X11 and Wayland is that kwin doesn't
know where a window will exactly be after resize() or moveResize().
In order to handle Wayland specifics, every window has a bounding
geometry that is being manipulated by move(), resize(), and moveResize().
The frameGeometry(), the clientGeometry(), and the bufferGeometry() are
not manipulated by move(), resize(), and moveResize() directly. Almost
everything that manipulates geometry should use moveResizeGeometry().
This creates a problem though, since the clientGeometry() will be
updated only after the client provides a new buffer, kwin has absolutely
no idea what the client geometry for a given move resize geometry will
be.
Another side of the coin is that decoration updates are performed
asynchronously on wayland, meaning that you cannot use border properties
for anything related to geometry handling and you should avoid using
borderLeft(), borderTop(), borderRight(), and borderBottom() in general.
clientGeometry(), bufferGeometry(), and border*() are good only if you
want to forward an event or render something. They can't be used for
manipulating the geometry.
Unfortunately, AbstractClient::checkWorkspacePosition() needs both,
which is a bit of a problem. To add more oil to the fire, contents
of a decorated window can be snapped to a screen edge. This goes against
the nature of geometry updates on wayland, where we try to indicate
the bounds of the frame geometry and avoid using client and buffer
geometries.
In order to make geometry handling more correct on wayland, this change
removes the ability to snap the contents of a decorated window to a
screen edge. This allows to avoid using the client geometry in
checkWorkspacePosition(), which is a very important function that ensures
the window is inside the workspace.
There is nothing wrong with snapping the frame rather than its contents
and that's what kwin used to do. It was changed with the removal of
"Display borders on maximized windows" option, the relevant commit
didn't provide any reasoning behind the change.
2022-01-14 16:07:44 +00:00
|
|
|
createDecoration(oldFrameGeometry);
|
2019-08-26 07:44:04 +00:00
|
|
|
} else {
|
|
|
|
destroyDecoration();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2019-09-29 11:26:04 +00:00
|
|
|
updateShadow();
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
if (check_workspace_pos) {
|
Make checkWorkspacePosition() work without client geometry
A good portion of geometry handling code was written during the X11
times. The main difference between X11 and Wayland is that kwin doesn't
know where a window will exactly be after resize() or moveResize().
In order to handle Wayland specifics, every window has a bounding
geometry that is being manipulated by move(), resize(), and moveResize().
The frameGeometry(), the clientGeometry(), and the bufferGeometry() are
not manipulated by move(), resize(), and moveResize() directly. Almost
everything that manipulates geometry should use moveResizeGeometry().
This creates a problem though, since the clientGeometry() will be
updated only after the client provides a new buffer, kwin has absolutely
no idea what the client geometry for a given move resize geometry will
be.
Another side of the coin is that decoration updates are performed
asynchronously on wayland, meaning that you cannot use border properties
for anything related to geometry handling and you should avoid using
borderLeft(), borderTop(), borderRight(), and borderBottom() in general.
clientGeometry(), bufferGeometry(), and border*() are good only if you
want to forward an event or render something. They can't be used for
manipulating the geometry.
Unfortunately, AbstractClient::checkWorkspacePosition() needs both,
which is a bit of a problem. To add more oil to the fire, contents
of a decorated window can be snapped to a screen edge. This goes against
the nature of geometry updates on wayland, where we try to indicate
the bounds of the frame geometry and avoid using client and buffer
geometries.
In order to make geometry handling more correct on wayland, this change
removes the ability to snap the contents of a decorated window to a
screen edge. This allows to avoid using the client geometry in
checkWorkspacePosition(), which is a very important function that ensures
the window is inside the workspace.
There is nothing wrong with snapping the frame rather than its contents
and that's what kwin used to do. It was changed with the removal of
"Display borders on maximized windows" option, the relevant commit
didn't provide any reasoning behind the change.
2022-01-14 16:07:44 +00:00
|
|
|
checkWorkspacePosition(oldFrameGeometry);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::invalidateDecoration()
|
2021-12-08 13:21:48 +00:00
|
|
|
{
|
|
|
|
updateDecoration(true, true);
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
void InternalWindow::destroyWindow()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2020-07-16 07:17:19 +00:00
|
|
|
markAsZombie();
|
2021-04-30 18:06:58 +00:00
|
|
|
if (isInteractiveMoveResize()) {
|
|
|
|
leaveInteractiveMoveResize();
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT clientFinishUserMovedResized(this);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
Deleted *deleted = Deleted::create(this);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT windowClosed(this, deleted);
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
destroyDecoration();
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
workspace()->removeInternalWindow(this);
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
deleted->unrefWindow();
|
2022-04-28 12:00:38 +00:00
|
|
|
m_handle = nullptr;
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::hasPopupGrab() const
|
2021-01-30 20:29:55 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
return !m_handle->flags().testFlag(Qt::WindowTransparentForInput) && m_handle->flags().testFlag(Qt::Popup) && !m_handle->flags().testFlag(Qt::ToolTip);
|
2021-01-30 20:29:55 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::popupDone()
|
2021-01-30 20:29:55 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
m_handle->hide();
|
2021-01-30 20:29:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 10:36:34 +00:00
|
|
|
void InternalWindow::present(const std::shared_ptr<QOpenGLFramebufferObject> fbo)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(m_internalImage.isNull());
|
|
|
|
|
|
|
|
const QSize bufferSize = fbo->size() / bufferScale();
|
|
|
|
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
commitGeometry(QRect(pos(), clientSizeToFrameSize(bufferSize)));
|
2019-08-26 07:44:04 +00:00
|
|
|
markAsMapped();
|
|
|
|
|
2021-05-07 14:49:09 +00:00
|
|
|
m_internalFBO = fbo;
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
setDepth(32);
|
2021-02-04 09:07:20 +00:00
|
|
|
surfaceItem()->addDamage(surfaceItem()->rect());
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::present(const QImage &image, const QRegion &damage)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2022-05-17 10:36:34 +00:00
|
|
|
Q_ASSERT(m_internalFBO == nullptr);
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
const QSize bufferSize = image.size() / bufferScale();
|
|
|
|
|
Refactor geometry constraints code
Summary:
Currently, there are a couple of issues with sizeForClientSize(). First
of all, we have a method called clientSizeToFrameSize() which does similar
thing except applying geometry constraints and checking window rules. The
other issue is that sizeForClientSize() is doing a bit too much, it checks
window rules, it applies a bunch of geometry constrains. Sometimes it
does not perform conversion between client sizes and frame sizes!
This change attempts to address those issues by replacing sizeForClientSize
with two similar methods and changing semantics of some methods of the
X11Client class.
The most significant difference between sizeForClientSize() and the new
methods is that neither constrainClientSize() nor constrainFrameSize()
check window rules. This is up to users of those methods. In many places,
we don't have to check window rules because we check isResizable(),
which returns false if the frame size is enforced by a window rule.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, romangg, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D26828
2020-02-12 10:38:40 +00:00
|
|
|
commitGeometry(QRect(pos(), clientSizeToFrameSize(bufferSize)));
|
2019-08-26 07:44:04 +00:00
|
|
|
markAsMapped();
|
|
|
|
|
|
|
|
m_internalImage = image;
|
|
|
|
|
|
|
|
setDepth(32);
|
2021-02-04 09:07:20 +00:00
|
|
|
surfaceItem()->addDamage(damage);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-28 12:00:38 +00:00
|
|
|
QWindow *InternalWindow::handle() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
return m_handle;
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::acceptsFocus() const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
bool InternalWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(checks)
|
2022-04-22 17:45:19 +00:00
|
|
|
const InternalWindow *otherInternal = qobject_cast<const InternalWindow *>(other);
|
2021-01-30 20:29:55 +00:00
|
|
|
if (!otherInternal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (otherInternal == this) {
|
|
|
|
return true;
|
|
|
|
}
|
2022-04-28 12:00:38 +00:00
|
|
|
return otherInternal->handle()->isAncestorOf(handle()) || handle()->isAncestorOf(otherInternal->handle());
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::doInteractiveResizeSync()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
requestGeometry(moveResizeGeometry());
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::updateCaption()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
const QString oldSuffix = m_captionSuffix;
|
|
|
|
const auto shortcut = shortcutCaptionSuffix();
|
|
|
|
m_captionSuffix = shortcut;
|
2022-04-23 08:33:23 +00:00
|
|
|
if ((!isSpecialWindow() || isToolbar()) && findWindowWithSameCaption()) {
|
2019-08-26 07:44:04 +00:00
|
|
|
int i = 2;
|
|
|
|
do {
|
|
|
|
m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>');
|
|
|
|
i++;
|
2022-04-23 08:33:23 +00:00
|
|
|
} while (findWindowWithSameCaption());
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
if (m_captionSuffix != oldSuffix) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT captionChanged();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::requestGeometry(const QRect &rect)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle) {
|
|
|
|
m_handle->setGeometry(frameRectToClientRect(rect));
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::commitGeometry(const QRect &rect)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2020-07-20 19:33:19 +00:00
|
|
|
// The client geometry and the buffer geometry are the same.
|
|
|
|
const QRect oldClientGeometry = m_clientGeometry;
|
|
|
|
const QRect oldFrameGeometry = m_frameGeometry;
|
2022-04-14 12:33:28 +00:00
|
|
|
const Output *oldOutput = m_output;
|
2020-07-20 19:33:19 +00:00
|
|
|
|
2020-06-01 10:43:49 +00:00
|
|
|
m_clientGeometry = frameRectToClientRect(rect);
|
2019-12-04 13:18:34 +00:00
|
|
|
m_frameGeometry = rect;
|
2021-05-15 10:56:35 +00:00
|
|
|
m_bufferGeometry = m_clientGeometry;
|
2019-08-26 07:44:04 +00:00
|
|
|
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (oldClientGeometry == m_clientGeometry && oldFrameGeometry == m_frameGeometry) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-26 10:03:14 +00:00
|
|
|
m_output = kwinApp()->platform()->outputAt(rect.center());
|
2019-08-26 07:44:04 +00:00
|
|
|
syncGeometryToInternalWindow();
|
|
|
|
|
2020-07-20 19:33:19 +00:00
|
|
|
if (oldClientGeometry != m_clientGeometry) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT bufferGeometryChanged(this, oldClientGeometry);
|
|
|
|
Q_EMIT clientGeometryChanged(this, oldClientGeometry);
|
2020-07-14 12:00:29 +00:00
|
|
|
}
|
2020-07-20 19:33:19 +00:00
|
|
|
if (oldFrameGeometry != m_frameGeometry) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT frameGeometryChanged(this, oldFrameGeometry);
|
2020-06-01 10:43:49 +00:00
|
|
|
}
|
2021-11-26 10:03:14 +00:00
|
|
|
if (oldOutput != m_output) {
|
|
|
|
Q_EMIT screenChanged();
|
|
|
|
}
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT geometryShapeChanged(this, oldFrameGeometry);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::setCaption(const QString &caption)
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (m_captionNormal == caption) {
|
2019-01-27 19:48:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
m_captionNormal = caption;
|
|
|
|
|
|
|
|
const QString oldCaptionSuffix = m_captionSuffix;
|
|
|
|
updateCaption();
|
|
|
|
|
|
|
|
if (m_captionSuffix == oldCaptionSuffix) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT captionChanged();
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::markAsMapped()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (!ready_for_painting) {
|
|
|
|
setReadyForPainting();
|
2022-04-23 08:33:23 +00:00
|
|
|
workspace()->addInternalWindow(this);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::syncGeometryToInternalWindow()
|
[wayland] Apply window rules only to xdg-shell clients
Summary:
There are rules that have to be applied only once, e.g. every Remember
and Apply Initially rule, as well rules that need to configure the client,
e.g. size, etc. In the best scenario the compositor would evaluate such
rules when the client is about to be mapped.
This change limits window rules only to xdg-shell clients because right
now only this protocol lets compositors to intervene in the client
initialization process. Also, it makes things a bit easier for us on the
compositor side.
xdg-shell protocol satisfies most of ours requirements to implement window
rules, but not all of them. If the client is about to be mapped for the
second time and its size is forced by a rule, then compositor may need
to configure it. Currently, xdg-shell protocol doesn't have any mechanism
that a client could use to notify the compositor about its intent to map.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: fmonteiro, davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19411
2019-07-09 11:58:57 +00:00
|
|
|
{
|
2022-04-28 12:00:38 +00:00
|
|
|
if (m_handle->geometry() == frameRectToClientRect(frameGeometry())) {
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-23 10:13:38 +00:00
|
|
|
QTimer::singleShot(0, this, [this] {
|
|
|
|
requestGeometry(frameGeometry());
|
|
|
|
});
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:45:19 +00:00
|
|
|
void InternalWindow::updateInternalWindowGeometry()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
if (!isInteractiveMoveResize()) {
|
2022-04-28 12:00:38 +00:00
|
|
|
const QRect rect = clientRectToFrameRect(m_handle->geometry());
|
Rework async geometry updates
Window management features were written with synchronous geometry
updates in mind. Currently, this poses a big problem on Wayland because
geometry updates are done in asynchronous fashion there.
At the moment, geometry is updated in a so called pseudo-asynchronous
fashion, meaning that the frame geometry will be reset to the old value
once geometry updates are unblocked. The main drawback of this approach
is that it is too error prone, the data flow is hard to comprehend, etc.
It is worth noting that there is already a machinery to perform async
geometry which is used during interactive move/resize operations.
This change extends the move/resize geometry usage beyond interactive
move/resize to make asynchronous geometry updates less error prone and
easier to comprehend.
With the proposed solution, all geometry updates must be done on the
move/resize geometry first. After that, the new geometry is passed on to
the Client-specific implementation of moveResizeInternal().
To be more specific, the frameGeometry() returns the current frame
geometry, it is primarily useful only to the scene. If you want to move
or resize a window, you need to use moveResizeGeometry() because it
corresponds to the last requested frame geometry.
It is worth noting that the moveResizeGeometry() returns the desired
bounding geometry. The client may commit the xdg_toplevel surface with a
slightly smaller window geometry, for example to enforce a specific
aspect ratio. The client is not allowed to resize beyond the size as
indicated in moveResizeGeometry().
The data flow is very simple: moveResize() updates the move/resize
geometry and calls the client-specific implementation of the
moveResizeInternal() method. Based on whether a configure event is
needed, moveResizeInternal() will update the frameGeometry() either
immediately or after the client commits a new buffer.
Unfortunately, both the compositor and xdg-shell clients try to update
the window geometry. It means that it's possible to have conflicts
between the two. With this change, the compositor's move resize geometry
will be synced only if there are no pending configure events, meaning
that the user doesn't try to resize the window.
2021-04-30 18:26:09 +00:00
|
|
|
setMoveResizeGeometry(rect);
|
|
|
|
commitGeometry(rect);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
[wayland] Apply window rules only to xdg-shell clients
Summary:
There are rules that have to be applied only once, e.g. every Remember
and Apply Initially rule, as well rules that need to configure the client,
e.g. size, etc. In the best scenario the compositor would evaluate such
rules when the client is about to be mapped.
This change limits window rules only to xdg-shell clients because right
now only this protocol lets compositors to intervene in the client
initialization process. Also, it makes things a bit easier for us on the
compositor side.
xdg-shell protocol satisfies most of ours requirements to implement window
rules, but not all of them. If the client is about to be mapped for the
second time and its size is forced by a rule, then compositor may need
to configure it. Currently, xdg-shell protocol doesn't have any mechanism
that a client could use to notify the compositor about its intent to map.
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: fmonteiro, davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19411
2019-07-09 11:58:57 +00:00
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|