2019-01-27 19:48:00 +00:00
|
|
|
/********************************************************************
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2019 Martin Flöser <mgraesslin@kde.org>
|
2020-01-14 16:17:18 +00:00
|
|
|
Copyright (C) 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2019-01-27 19:48:00 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************/
|
|
|
|
#include "internal_client.h"
|
2019-08-26 07:44:04 +00:00
|
|
|
#include "decorations/decorationbridge.h"
|
|
|
|
#include "deleted.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
|
|
|
|
|
|
|
#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_windowId(window->winId())
|
|
|
|
, 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
|
|
|
connect(this, &InternalClient::opacityChanged, this, &InternalClient::addRepaintFull);
|
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);
|
2020-01-07 22:12:58 +00:00
|
|
|
setFrameGeometry(clientRectToFrameRect(m_internalWindow->geometry()));
|
2019-09-27 10:01:10 +00:00
|
|
|
setGeometryRestore(frameGeometry());
|
2019-08-26 07:44:04 +00:00
|
|
|
blockGeometryUpdates(false);
|
|
|
|
|
|
|
|
m_internalWindow->installEventFilter(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
InternalClient::~InternalClient()
|
|
|
|
{
|
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-10-03 19:43:28 +00:00
|
|
|
QRect InternalClient::bufferGeometry() const
|
|
|
|
{
|
2020-07-20 19:33:19 +00:00
|
|
|
return m_clientGeometry;
|
2019-10-03 19:43:28 +00:00
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
QStringList InternalClient::activities() const
|
|
|
|
{
|
|
|
|
return QStringList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::blockActivityUpdates(bool b)
|
|
|
|
{
|
|
|
|
Q_UNUSED(b)
|
|
|
|
|
|
|
|
// Internal clients do not support activities.
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint InternalClient::clientContentPos() const
|
|
|
|
{
|
|
|
|
return -1 * clientPos();
|
|
|
|
}
|
|
|
|
|
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-08-26 07:44:04 +00:00
|
|
|
void InternalClient::debug(QDebug &stream) const
|
|
|
|
{
|
|
|
|
stream.nospace() << "\'InternalClient:" << m_internalWindow << "\'";
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect InternalClient::transparentRect() const
|
|
|
|
{
|
|
|
|
return QRect();
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
double InternalClient::opacity() const
|
|
|
|
{
|
|
|
|
return m_opacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::setOpacity(double opacity)
|
|
|
|
{
|
|
|
|
if (m_opacity == opacity) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const double oldOpacity = m_opacity;
|
|
|
|
m_opacity = opacity;
|
|
|
|
|
|
|
|
emit opacityChanged(this, oldOpacity);
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isInputMethod() const
|
|
|
|
{
|
|
|
|
if (m_internalWindow) {
|
|
|
|
return m_internalWindow->property("__kwin_input_method").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;
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
quint32 InternalClient::windowId() const
|
|
|
|
{
|
|
|
|
return m_windowId;
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
bool InternalClient::isShown(bool shaded_is_shown) const
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
Q_UNUSED(shaded_is_shown)
|
|
|
|
|
|
|
|
return readyForPainting();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InternalClient::isHiddenInternal() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::hideClient(bool hide)
|
|
|
|
{
|
|
|
|
Q_UNUSED(hide)
|
|
|
|
}
|
|
|
|
|
2020-03-25 15:15:23 +00:00
|
|
|
void InternalClient::resizeWithChecks(const QSize &size, ForceGeometry_t force)
|
2019-08-26 07:44:04 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(force)
|
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);
|
|
|
|
setFrameGeometry(QRect{pos(), size.boundedTo(area.size())}, force);
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 15:15:23 +00:00
|
|
|
void InternalClient::setFrameGeometry(const QRect &rect, ForceGeometry_t force)
|
2019-01-27 19:48:00 +00:00
|
|
|
{
|
2019-08-26 07:44:04 +00:00
|
|
|
if (areGeometryUpdatesBlocked()) {
|
2019-12-04 13:18:34 +00:00
|
|
|
m_frameGeometry = rect;
|
2019-08-26 07:44:04 +00:00
|
|
|
if (pendingGeometryUpdate() == PendingGeometryForced) {
|
|
|
|
// Maximum, nothing needed.
|
|
|
|
} else if (force == ForceGeometrySet) {
|
|
|
|
setPendingGeometryUpdate(PendingGeometryForced);
|
|
|
|
} else {
|
|
|
|
setPendingGeometryUpdate(PendingGeometryNormal);
|
|
|
|
}
|
|
|
|
return;
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
if (pendingGeometryUpdate() != PendingGeometryNone) {
|
|
|
|
// Reset geometry to the one before blocking, so that we can compare properly.
|
2019-12-04 13:18:34 +00:00
|
|
|
m_frameGeometry = frameGeometryBeforeUpdateBlocking();
|
2019-08-26 07:44:04 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 13:18:34 +00:00
|
|
|
if (m_frameGeometry == rect) {
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-07 22:12:58 +00:00
|
|
|
const QRect newClientGeometry = frameRectToClientRect(rect);
|
2019-08-26 07:44:04 +00:00
|
|
|
|
2020-06-01 10:43:49 +00:00
|
|
|
if (clientSize() == newClientGeometry.size()) {
|
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
|
|
|
bool InternalClient::supportsWindowRules() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
AbstractClient *InternalClient::findModal(bool allow_itself)
|
|
|
|
{
|
|
|
|
Q_UNUSED(allow_itself)
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::setOnAllActivities(bool set)
|
|
|
|
{
|
|
|
|
Q_UNUSED(set)
|
|
|
|
|
|
|
|
// Internal clients do not support activities.
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
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) {
|
|
|
|
checkWorkspacePosition(oldFrameGeometry, -2, oldClientGeometry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::updateColorScheme()
|
|
|
|
{
|
|
|
|
AbstractClient::updateColorScheme(QString());
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::showOnScreenEdge()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void InternalClient::destroyClient()
|
|
|
|
{
|
2020-07-16 07:17:19 +00:00
|
|
|
markAsZombie();
|
2019-08-26 07:44:04 +00:00
|
|
|
if (isMoveResize()) {
|
|
|
|
leaveMoveResize();
|
2019-01-27 19:48:00 +00:00
|
|
|
}
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
Deleted *deleted = Deleted::create(this);
|
|
|
|
emit windowClosed(this, deleted);
|
|
|
|
|
|
|
|
destroyDecoration();
|
|
|
|
|
|
|
|
workspace()->removeInternalClient(this);
|
|
|
|
|
|
|
|
deleted->unrefWindow();
|
|
|
|
m_internalWindow = nullptr;
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
if (m_internalFBO != fbo) {
|
|
|
|
discardWindowPixmap();
|
|
|
|
m_internalFBO = fbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
setDepth(32);
|
|
|
|
addDamageFull();
|
|
|
|
addRepaintFull();
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
if (m_internalImage.size() != image.size()) {
|
|
|
|
discardWindowPixmap();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_internalImage = image;
|
|
|
|
|
|
|
|
setDepth(32);
|
|
|
|
addDamage(damage);
|
|
|
|
addRepaint(damage.translated(borderLeft(), borderTop()));
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
return qobject_cast<const InternalClient *>(other) != nullptr;
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
void InternalClient::doMove(int x, int y)
|
|
|
|
{
|
|
|
|
Q_UNUSED(x)
|
|
|
|
Q_UNUSED(y)
|
2019-08-26 07:44:04 +00:00
|
|
|
|
2019-01-27 19:48:00 +00:00
|
|
|
syncGeometryToInternalWindow();
|
|
|
|
}
|
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
void InternalClient::doResizeSync()
|
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) {
|
|
|
|
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)
|
|
|
|
{
|
2019-12-04 13:18:34 +00:00
|
|
|
if (m_frameGeometry == rect && pendingGeometryUpdate() == PendingGeometryNone) {
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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;
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
addWorkspaceRepaint(visibleRect());
|
2020-07-20 19:33:19 +00:00
|
|
|
updateGeometryBeforeUpdateBlocking();
|
2019-08-26 07:44:04 +00:00
|
|
|
syncGeometryToInternalWindow();
|
|
|
|
|
2020-07-20 19:33:19 +00:00
|
|
|
if (oldClientGeometry != m_clientGeometry) {
|
|
|
|
emit bufferGeometryChanged(this, oldClientGeometry);
|
|
|
|
emit clientGeometryChanged(this, oldClientGeometry);
|
2020-07-14 12:00:29 +00:00
|
|
|
}
|
2020-07-20 19:33:19 +00:00
|
|
|
if (oldFrameGeometry != m_frameGeometry) {
|
|
|
|
emit frameGeometryChanged(this, oldFrameGeometry);
|
2020-06-01 10:43:49 +00:00
|
|
|
}
|
2020-07-20 19:33:19 +00:00
|
|
|
emit geometryShapeChanged(this, oldFrameGeometry);
|
2019-08-26 07:44:04 +00:00
|
|
|
|
|
|
|
if (isResize()) {
|
|
|
|
performMoveResize();
|
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) {
|
|
|
|
emit captionChanged();
|
|
|
|
}
|
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()
|
|
|
|
{
|
|
|
|
if (isMoveResize()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-07 22:12:58 +00:00
|
|
|
commitGeometry(clientRectToFrameRect(m_internalWindow->geometry()));
|
[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
|
|
|
}
|