You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
9.2 KiB
336 lines
9.2 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2019 Martin Flöser <mgraesslin@kde.org> |
|
|
|
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" |
|
#include "workspace.h" |
|
|
|
#include <KWayland/Client/surface.h> |
|
#include <KWayland/Server/surface_interface.h> |
|
|
|
#include <QOpenGLFramebufferObject> |
|
|
|
Q_DECLARE_METATYPE(NET::WindowType) |
|
|
|
static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); |
|
|
|
namespace KWin |
|
{ |
|
|
|
InternalClient::InternalClient(KWayland::Server::ShellSurfaceInterface *surface) |
|
: ShellClient(surface) |
|
{ |
|
findInternalWindow(); |
|
updateInternalWindowGeometry(); |
|
updateDecoration(true); |
|
} |
|
|
|
InternalClient::InternalClient(KWayland::Server::XdgShellSurfaceInterface *surface) |
|
: ShellClient(surface) |
|
{ |
|
} |
|
|
|
InternalClient::InternalClient(KWayland::Server::XdgShellPopupInterface *surface) |
|
: ShellClient(surface) |
|
{ |
|
} |
|
|
|
InternalClient::~InternalClient() = default; |
|
|
|
void InternalClient::findInternalWindow() |
|
{ |
|
const QWindowList windows = kwinApp()->topLevelWindows(); |
|
for (QWindow *w: windows) { |
|
auto s = KWayland::Client::Surface::fromWindow(w); |
|
if (!s) { |
|
continue; |
|
} |
|
if (s->id() != surface()->id()) { |
|
continue; |
|
} |
|
m_internalWindow = w; |
|
m_windowId = m_internalWindow->winId(); |
|
m_internalWindowFlags = m_internalWindow->flags(); |
|
connect(m_internalWindow, &QWindow::xChanged, this, &InternalClient::updateInternalWindowGeometry); |
|
connect(m_internalWindow, &QWindow::yChanged, this, &InternalClient::updateInternalWindowGeometry); |
|
connect(m_internalWindow, &QWindow::destroyed, this, [this] { m_internalWindow = nullptr; }); |
|
connect(m_internalWindow, &QWindow::opacityChanged, this, &InternalClient::setOpacity); |
|
|
|
const QVariant windowType = m_internalWindow->property("kwin_windowType"); |
|
if (!windowType.isNull()) { |
|
m_windowType = windowType.value<NET::WindowType>(); |
|
} |
|
setOpacity(m_internalWindow->opacity()); |
|
|
|
// skip close animation support |
|
setSkipCloseAnimation(m_internalWindow->property(s_skipClosePropertyName).toBool()); |
|
m_internalWindow->installEventFilter(this); |
|
return; |
|
} |
|
|
|
qCWarning(KWIN_CORE, "Couldn't find an internal window for surface with id %x", surface()->id()); |
|
} |
|
|
|
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()); |
|
} |
|
if (pe->propertyName() == "kwin_windowType") { |
|
m_windowType = m_internalWindow->property("kwin_windowType").value<NET::WindowType>(); |
|
workspace()->updateClientArea(); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
NET::WindowType InternalClient::windowType(bool direct, int supported_types) const |
|
{ |
|
Q_UNUSED(direct) |
|
Q_UNUSED(supported_types) |
|
return m_windowType; |
|
} |
|
|
|
void InternalClient::killWindow() |
|
{ |
|
// we don't kill our internal windows |
|
} |
|
|
|
bool InternalClient::isPopupWindow() const |
|
{ |
|
if (Toplevel::isPopupWindow()) { |
|
return true; |
|
} |
|
return m_internalWindowFlags.testFlag(Qt::Popup); |
|
} |
|
|
|
void InternalClient::setInternalFramebufferObject(const QSharedPointer<QOpenGLFramebufferObject> &fbo) |
|
{ |
|
if (fbo.isNull()) { |
|
unmap(); |
|
return; |
|
} |
|
|
|
setClientSize(fbo->size() / surface()->scale()); |
|
markAsMapped(); |
|
doSetGeometry(QRect(geom.topLeft(), clientSize())); |
|
Toplevel::setInternalFramebufferObject(fbo); |
|
Toplevel::addDamage(QRegion(0, 0, width(), height())); |
|
} |
|
|
|
void InternalClient::closeWindow() |
|
{ |
|
if (m_internalWindow) { |
|
m_internalWindow->hide(); |
|
} |
|
} |
|
|
|
bool InternalClient::isCloseable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool InternalClient::isMaximizable() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool InternalClient::isMinimizable() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool InternalClient::isMovable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool InternalClient::isMovableAcrossScreens() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool InternalClient::isResizable() const |
|
{ |
|
return true; |
|
} |
|
|
|
bool InternalClient::noBorder() const |
|
{ |
|
return m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); |
|
} |
|
|
|
bool InternalClient::userCanSetNoBorder() const |
|
{ |
|
return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); |
|
} |
|
|
|
bool InternalClient::wantsInput() const |
|
{ |
|
return false; |
|
} |
|
|
|
bool InternalClient::acceptsFocus() 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; |
|
} |
|
|
|
bool InternalClient::isOutline() const |
|
{ |
|
if (m_internalWindow) { |
|
return m_internalWindow->property("__kwin_outline").toBool(); |
|
} |
|
return false; |
|
} |
|
|
|
quint32 InternalClient::windowId() const |
|
{ |
|
return m_windowId; |
|
} |
|
|
|
void InternalClient::updateInternalWindowGeometry() |
|
{ |
|
if (!m_internalWindow) { |
|
return; |
|
} |
|
doSetGeometry(QRect(m_internalWindow->geometry().topLeft() - QPoint(borderLeft(), borderTop()), |
|
m_internalWindow->geometry().size() + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); |
|
} |
|
|
|
bool InternalClient::requestGeometry(const QRect &rect) |
|
{ |
|
if (!ShellClient::requestGeometry(rect)) { |
|
return false; |
|
} |
|
if (m_internalWindow) { |
|
m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); |
|
} |
|
return true; |
|
} |
|
|
|
void InternalClient::doSetGeometry(const QRect &rect) |
|
{ |
|
if (geom == rect && pendingGeometryUpdate() == PendingGeometryNone) { |
|
return; |
|
} |
|
if (!isUnmapped()) { |
|
addWorkspaceRepaint(visibleRect()); |
|
} |
|
geom = rect; |
|
|
|
if (isUnmapped() && geometryRestore().isEmpty() && !geom.isEmpty()) { |
|
// use first valid geometry as restore geometry |
|
setGeometryRestore(geom); |
|
} |
|
|
|
if (!isUnmapped()) { |
|
addWorkspaceRepaint(visibleRect()); |
|
} |
|
syncGeometryToInternalWindow(); |
|
if (hasStrut()) { |
|
workspace()->updateClientArea(); |
|
} |
|
const auto old = geometryBeforeUpdateBlocking(); |
|
updateGeometryBeforeUpdateBlocking(); |
|
emit geometryShapeChanged(this, old); |
|
|
|
if (isResize()) { |
|
performMoveResize(); |
|
} |
|
} |
|
|
|
void InternalClient::doMove(int x, int y) |
|
{ |
|
Q_UNUSED(x) |
|
Q_UNUSED(y) |
|
syncGeometryToInternalWindow(); |
|
} |
|
|
|
void InternalClient::syncGeometryToInternalWindow() |
|
{ |
|
if (!m_internalWindow) { |
|
return; |
|
} |
|
const QRect windowRect = QRect(geom.topLeft() + QPoint(borderLeft(), borderTop()), |
|
geom.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom())); |
|
if (m_internalWindow->geometry() != windowRect) { |
|
// delay to end of cycle to prevent freeze, see BUG 384441 |
|
QTimer::singleShot(0, m_internalWindow, std::bind(static_cast<void (QWindow::*)(const QRect&)>(&QWindow::setGeometry), m_internalWindow, windowRect)); |
|
} |
|
} |
|
|
|
void InternalClient::resizeWithChecks(int w, int h, ForceGeometry_t force) |
|
{ |
|
Q_UNUSED(force) |
|
if (!m_internalWindow) { |
|
return; |
|
} |
|
QRect area = workspace()->clientArea(WorkArea, this); |
|
// don't allow growing larger than workarea |
|
if (w > area.width()) { |
|
w = area.width(); |
|
} |
|
if (h > area.height()) { |
|
h = area.height(); |
|
} |
|
m_internalWindow->setGeometry(QRect(pos() + QPoint(borderLeft(), borderTop()), QSize(w, h) - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); |
|
} |
|
|
|
void InternalClient::doResizeSync() |
|
{ |
|
if (!m_internalWindow) { |
|
return; |
|
} |
|
const auto rect = moveResizeGeometry(); |
|
m_internalWindow->setGeometry(QRect(rect.topLeft() + QPoint(borderLeft(), borderTop()), rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); |
|
} |
|
|
|
QWindow *InternalClient::internalWindow() const |
|
{ |
|
return m_internalWindow; |
|
} |
|
|
|
bool InternalClient::supportsWindowRules() const |
|
{ |
|
return false; |
|
} |
|
|
|
}
|
|
|