kwin/internal_client.cpp

337 lines
9.2 KiB
C++

/********************************************************************
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;
}
}