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
|
|
|
|
*/
|
2019-01-27 19:48:00 +00:00
|
|
|
#include "internal_client.h"
|
2019-08-26 07:44:04 +00:00
|
|
|
#include "decorations/decorationbridge.h"
|
|
|
|
#include "deleted.h"
|
2021-02-04 09:07:20 +00:00
|
|
|
#include "surfaceitem.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
|
|
|
|
{
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
InternalClient::InternalClient(QWindow *window)
|
|
|
|
: m_internalWindow(window)
|
|
|
|
, m_internalWindowFlags(window->flags())
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry);
|
|
|
|
connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry);
|
|
|
|
connect(m_internalWindow, &QWindow::widthChanged, this, &InternalClient::updateInternalWindowGeometry);
|
|
|
|
connect(m_internalWindow, &QWindow::heightChanged, this, &InternalClient::updateInternalWindowGeometry);
|
|
|
|
connect(m_internalWindow, &QWindow::windowTitleChanged, this, &InternalClient::setCaption);
|
|
|
|
connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity);
|
|
|
|
connect(m_internalWindow, &QWindow::destroyed, this, &InternalClient::destroyClient);
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
const QVariant windowType = m_internalWindow->property("kwin_windowType");
|
|
|
|
if (!windowType.isNull()) {
|
|
|
|
m_windowType = windowType.value<NET::WindowType>();
|
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
setCaption(m_internalWindow->title());
|
|
|
|
setIcon(QIcon::fromTheme(QStringLiteral("kwin")));
|
|
|
|
setOnAllDesktops(true);
|
|
|
|
setOpacity(m_internalWindow->opacity());
|
|
|
|
setSkipCloseAnimation(m_internalWindow->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);
|
|
|
|
commitGeometry(m_internalWindow->geometry());
|
|
|
|
updateDecoration(true);
|
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
|
|
|
moveResize(clientRectToFrameRect(m_internalWindow->geometry()));
|
2019-08-26 07:44:04 +00:00
|
|
|
blockGeometryUpdates(false);
|
|
|
|
|
|
|
|
m_internalWindow->installEventFilter(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
InternalClient::~InternalClient()
|
|
|
|
{
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 16:06:17 +00:00
|
|
|
bool InternalClient::isClient() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-05-09 15:07:36 +00:00
|
|
|
bool InternalClient::hitTest(const QPoint &point) const
|
|
|
|
{
|
|
|
|
if (!AbstractClient::hitTest(point)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QRegion mask = m_internalWindow->mask();
|
|
|
|
if (!mask.isEmpty() && !mask.contains(mapToLocal(point))) {
|
|
|
|
return false;
|
|
|
|
} else if (m_internalWindow->property("outputOnly").toBool()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-29 20:19:38 +00:00
|
|
|
void InternalClient::pointerEnterEvent(const QPoint &globalPos)
|
|
|
|
{
|
|
|
|
AbstractClient::pointerEnterEvent(globalPos);
|
|
|
|
|
|
|
|
QEnterEvent enterEvent(pos(), pos(), globalPos);
|
|
|
|
QCoreApplication::sendEvent(m_internalWindow, &enterEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::pointerLeaveEvent()
|
|
|
|
{
|
|
|
|
AbstractClient::pointerLeaveEvent();
|
|
|
|
|
|
|
|
QEvent event(QEvent::Leave);
|
|
|
|
QCoreApplication::sendEvent(m_internalWindow, &event);
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
bool InternalClient::eventFilter(QObject *watched, QEvent *event)
|
|
|
|
{
|
|
|
|
if (watched == m_internalWindow && event->type() == QEvent::DynamicPropertyChange) {
|
|
|
|
QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent*>(event);
|
|
|
|
if (pe->propertyName() == s_skipClosePropertyName) {
|
|
|
|
setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool());
|
|
|
|
}
|
2020-01-01 01:11:17 +00:00
|
|
|
if (pe->propertyName() == s_shadowEnabledPropertyName) {
|
|
|
|
updateShadow();
|
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
if (pe->propertyName() == "kwin_windowType") {
|
|
|
|
m_windowType = m_internalWindow->property("kwin_windowType").value<NET::WindowType>();
|
|
|
|
workspace()->updateClientArea();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
qreal InternalClient::bufferScale() const
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
|
|
|
return m_internalWindow->devicePixelRatio();
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString InternalClient::captionNormal() const
|
|
|
|
{
|
|
|
|
return m_captionNormal;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString InternalClient::captionSuffix() const
|
|
|
|
{
|
|
|
|
return m_captionSuffix;
|
|
|
|
}
|
|
|
|
|
2020-02-12 10:44:10 +00:00
|
|
|
QSize InternalClient::minSize() const
|
|
|
|
{
|
|
|
|
return m_internalWindow->minimumSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize InternalClient::maxSize() const
|
|
|
|
{
|
|
|
|
return m_internalWindow->maximumSize();
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
NET::WindowType InternalClient::windowType(bool direct, int supported_types) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(direct)
|
|
|
|
Q_UNUSED(supported_types)
|
|
|
|
return m_windowType;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::killWindow()
|
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
// We don't kill our internal windows.
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isPopupWindow() const
|
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (AbstractClient::isPopupWindow()) {
|
2019-01-27 19:48:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return m_internalWindowFlags.testFlag(Qt::Popup);
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
QByteArray InternalClient::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
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::closeWindow()
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
|
|
|
m_internalWindow->hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isCloseable() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isMovable() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isMovableAcrossScreens() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isResizable() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-09-23 13:24:10 +00:00
|
|
|
bool InternalClient::isPlaceable() const
|
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
bool InternalClient::noBorder() const
|
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::userCanSetNoBorder() const
|
|
|
|
{
|
|
|
|
return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::wantsInput() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isInternal() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isLockScreen() const
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
|
|
|
return m_internalWindow->property("org_kde_ksld_emergency").toBool();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-19 14:01:29 +00:00
|
|
|
bool InternalClient::isOutline() const
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
|
|
|
return m_internalWindow->property("__kwin_outline").toBool();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-23 12:13:56 +00:00
|
|
|
bool InternalClient::isShown() const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
return readyForPainting();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isHiddenInternal() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-11-03 09:40:31 +00:00
|
|
|
void InternalClient::hideClient()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::showClient()
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-04-25 08:52:30 +00:00
|
|
|
void InternalClient::resizeWithChecks(const QSize &size)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
2019-01-27 19:48:00 +00:00
|
|
|
if (!m_internalWindow) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
void InternalClient::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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
AbstractClient *InternalClient::findModal(bool allow_itself)
|
|
|
|
{
|
|
|
|
Q_UNUSED(allow_itself)
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-07-22 11:00:11 +00:00
|
|
|
bool InternalClient::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
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::setNoBorder(bool set)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2021-12-09 11:55:13 +00:00
|
|
|
void InternalClient::createDecoration(const QRect &oldGeometry)
|
|
|
|
{
|
|
|
|
setDecoration(QSharedPointer<KDecoration2::Decoration>(Decoration::DecorationBridge::self()->createDecoration(this)));
|
|
|
|
moveResize(oldGeometry);
|
|
|
|
|
|
|
|
Q_EMIT geometryShapeChanged(this, oldGeometry);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::destroyDecoration()
|
|
|
|
{
|
|
|
|
const QSize clientSize = frameSizeToClientSize(moveResizeGeometry().size());
|
|
|
|
setDecoration(nullptr);
|
|
|
|
resize(clientSize);
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::updateDecoration(bool check_workspace_pos, bool force)
|
|
|
|
{
|
|
|
|
if (!force && isDecorated() == !noBorder()) {
|
|
|
|
return;
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 10:01:10 +00:00
|
|
|
const QRect oldFrameGeometry = frameGeometry();
|
2019-08-26 07:44:04 +00:00
|
|
|
const QRect oldClientGeometry = oldFrameGeometry - frameMargins();
|
|
|
|
|
|
|
|
GeometryUpdatesBlocker blocker(this);
|
|
|
|
|
|
|
|
if (force) {
|
|
|
|
destroyDecoration();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
if (!noBorder()) {
|
|
|
|
createDecoration(oldClientGeometry);
|
|
|
|
} 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) {
|
2021-08-21 17:46:51 +00:00
|
|
|
checkWorkspacePosition(oldFrameGeometry, oldClientGeometry);
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-08 13:21:48 +00:00
|
|
|
void InternalClient::invalidateDecoration()
|
|
|
|
{
|
|
|
|
updateDecoration(true, true);
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::destroyClient()
|
|
|
|
{
|
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();
|
|
|
|
|
|
|
|
workspace()->removeInternalClient(this);
|
|
|
|
|
|
|
|
deleted->unrefWindow();
|
|
|
|
m_internalWindow = nullptr;
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2021-01-30 20:29:55 +00:00
|
|
|
bool InternalClient::hasPopupGrab() const
|
|
|
|
{
|
|
|
|
return !m_internalWindow->flags().testFlag(Qt::WindowTransparentForInput) &&
|
|
|
|
m_internalWindow->flags().testFlag(Qt::Popup) &&
|
|
|
|
!m_internalWindow->flags().testFlag(Qt::ToolTip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::popupDone()
|
|
|
|
{
|
|
|
|
m_internalWindow->hide();
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::present(const QSharedPointer<QOpenGLFramebufferObject> fbo)
|
|
|
|
{
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::present(const QImage &image, const QRegion &damage)
|
|
|
|
{
|
|
|
|
Q_ASSERT(m_internalFBO.isNull());
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
QWindow *InternalClient::internalWindow() const
|
|
|
|
{
|
|
|
|
return m_internalWindow;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::acceptsFocus() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(checks)
|
2021-01-30 20:29:55 +00:00
|
|
|
const InternalClient *otherInternal = qobject_cast<const InternalClient *>(other);
|
|
|
|
if (!otherInternal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (otherInternal == this) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return otherInternal->internalWindow()->isAncestorOf(internalWindow()) ||
|
|
|
|
internalWindow()->isAncestorOf(otherInternal->internalWindow());
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2021-04-30 18:06:58 +00:00
|
|
|
void InternalClient::doInteractiveResizeSync()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
requestGeometry(moveResizeGeometry());
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::updateCaption()
|
|
|
|
{
|
|
|
|
const QString oldSuffix = m_captionSuffix;
|
|
|
|
const auto shortcut = shortcutCaptionSuffix();
|
|
|
|
m_captionSuffix = shortcut;
|
|
|
|
if ((!isSpecialWindow() || isToolbar()) && findClientWithSameCaption()) {
|
|
|
|
int i = 2;
|
|
|
|
do {
|
|
|
|
m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>');
|
|
|
|
i++;
|
|
|
|
} while (findClientWithSameCaption());
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::requestGeometry(const QRect &rect)
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
2020-01-07 22:12:58 +00:00
|
|
|
m_internalWindow->setGeometry(frameRectToClientRect(rect));
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::commitGeometry(const QRect &rect)
|
|
|
|
{
|
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;
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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-06-08 07:02:14 +00:00
|
|
|
Q_EMIT geometryShapeChanged(this, oldFrameGeometry);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::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
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::markAsMapped()
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (!ready_for_painting) {
|
|
|
|
setReadyForPainting();
|
|
|
|
workspace()->addInternalClient(this);
|
|
|
|
}
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::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
|
|
|
{
|
2020-01-07 22:12:58 +00:00
|
|
|
if (m_internalWindow->geometry() == frameRectToClientRect(frameGeometry())) {
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-27 10:01:10 +00:00
|
|
|
QTimer::singleShot(0, this, [this] { requestGeometry(frameGeometry()); });
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::updateInternalWindowGeometry()
|
|
|
|
{
|
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()) {
|
|
|
|
const QRect rect = clientRectToFrameRect(m_internalWindow->geometry());
|
|
|
|
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
|
|
|
}
|