kwin/waylandclient.cpp
Vlad Zahorodnii 40dca1c93e Implement window hiding in the WaylandClient class
This change upstreams window hiding functionality from the XdgSurfaceClient
class to the WaylandClient class in order to reduce the amount of duplicated
code in new wayland client sub-classes.
2020-08-18 14:21:41 +00:00

335 lines
7.8 KiB
C++

/*
SPDX-FileCopyrightText: 2015 Martin Flöser <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "waylandclient.h"
#include "screens.h"
#include "wayland_server.h"
#include "workspace.h"
#include <KWaylandServer/display.h>
#include <KWaylandServer/clientconnection.h>
#include <KWaylandServer/surface_interface.h>
#include <KWaylandServer/buffer_interface.h>
#include <QFileInfo>
#include <csignal>
#include <sys/types.h>
#include <unistd.h>
using namespace KWaylandServer;
namespace KWin
{
WaylandClient::WaylandClient(SurfaceInterface *surface)
{
// Note that we cannot setup compositing here because we may need to call visibleRect(),
// which in its turn will call bufferGeometry(), which is a pure virtual method.
setSurface(surface);
m_windowId = waylandServer()->createWindowId(surface);
connect(this, &WaylandClient::frameGeometryChanged,
this, &WaylandClient::updateClientOutputs);
connect(this, &WaylandClient::frameGeometryChanged,
this, &WaylandClient::updateClientArea);
connect(this, &WaylandClient::desktopFileNameChanged,
this, &WaylandClient::updateIcon);
connect(screens(), &Screens::changed, this,
&WaylandClient::updateClientOutputs);
updateResourceName();
updateIcon();
}
QString WaylandClient::captionNormal() const
{
return m_captionNormal;
}
QString WaylandClient::captionSuffix() const
{
return m_captionSuffix;
}
QStringList WaylandClient::activities() const
{
return QStringList();
}
void WaylandClient::setOnAllActivities(bool set)
{
Q_UNUSED(set)
}
void WaylandClient::blockActivityUpdates(bool b)
{
Q_UNUSED(b)
}
QPoint WaylandClient::clientContentPos() const
{
return -clientPos();
}
QRect WaylandClient::transparentRect() const
{
return QRect();
}
quint32 WaylandClient::windowId() const
{
return m_windowId;
}
pid_t WaylandClient::pid() const
{
return surface()->client()->processId();
}
bool WaylandClient::isLockScreen() const
{
return surface()->client() == waylandServer()->screenLockerClientConnection();
}
bool WaylandClient::isInputMethod() const
{
return surface()->client() == waylandServer()->inputMethodConnection();
}
bool WaylandClient::isLocalhost() const
{
return true;
}
double WaylandClient::opacity() const
{
return m_opacity;
}
void WaylandClient::setOpacity(double opacity)
{
const qreal newOpacity = qBound(0.0, opacity, 1.0);
if (newOpacity == m_opacity) {
return;
}
const qreal oldOpacity = m_opacity;
m_opacity = newOpacity;
addRepaintFull();
emit opacityChanged(this, oldOpacity);
}
AbstractClient *WaylandClient::findModal(bool allow_itself)
{
Q_UNUSED(allow_itself)
return nullptr;
}
void WaylandClient::resizeWithChecks(const QSize &size, ForceGeometry_t force)
{
const QRect area = workspace()->clientArea(WorkArea, this);
int width = size.width();
int height = size.height();
// don't allow growing larger than workarea
if (width > area.width()) {
width = area.width();
}
if (height > area.height()) {
height = area.height();
}
setFrameGeometry(QRect(x(), y(), width, height), force);
}
void WaylandClient::killWindow()
{
if (!surface()) {
return;
}
auto c = surface()->client();
if (c->processId() == getpid() || c->processId() == 0) {
c->destroy();
return;
}
::kill(c->processId(), SIGTERM);
// give it time to terminate and only if terminate fails, try destroy Wayland connection
QTimer::singleShot(5000, c, &ClientConnection::destroy);
}
QByteArray WaylandClient::windowRole() const
{
return QByteArray();
}
bool WaylandClient::belongsToSameApplication(const AbstractClient *other, SameApplicationChecks checks) const
{
if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) {
if (other->desktopFileName() == desktopFileName()) {
return true;
}
}
if (auto s = other->surface()) {
return s->client() == surface()->client();
}
return false;
}
bool WaylandClient::belongsToDesktop() const
{
const auto clients = waylandServer()->clients();
return std::any_of(clients.constBegin(), clients.constEnd(),
[this](const AbstractClient *client) {
if (belongsToSameApplication(client, SameApplicationChecks())) {
return client->isDesktop();
}
return false;
}
);
}
void WaylandClient::updateClientArea()
{
if (hasStrut()) {
workspace()->updateClientArea();
}
}
void WaylandClient::updateClientOutputs()
{
QVector<OutputInterface *> clientOutputs;
const auto outputs = waylandServer()->display()->outputs();
for (const auto output : outputs) {
if (output->isEnabled()) {
const QRect outputGeometry(output->globalPosition(), output->pixelSize() / output->scale());
if (frameGeometry().intersects(outputGeometry)) {
clientOutputs << output;
}
}
}
surface()->setOutputs(clientOutputs);
}
void WaylandClient::updateIcon()
{
const QString waylandIconName = QStringLiteral("wayland");
const QString dfIconName = iconFromDesktopFile();
const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName;
if (iconName == icon().name()) {
return;
}
setIcon(QIcon::fromTheme(iconName));
}
void WaylandClient::updateResourceName()
{
const QFileInfo fileInfo(surface()->client()->executablePath());
if (fileInfo.exists()) {
setResourceClass(fileInfo.fileName().toUtf8());
}
}
void WaylandClient::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());
}
if (m_captionSuffix != oldSuffix) {
emit captionChanged();
}
}
void WaylandClient::setCaption(const QString &caption)
{
const QString oldSuffix = m_captionSuffix;
m_captionNormal = caption.simplified();
updateCaption();
if (m_captionSuffix == oldSuffix) {
// Don't emit caption change twice it already got emitted by the changing suffix.
emit captionChanged();
}
}
void WaylandClient::doSetActive()
{
if (isActive()) { // TODO: Xwayland clients must be unfocused somewhere else.
StackingUpdatesBlocker blocker(workspace());
workspace()->focusToNull();
}
}
void WaylandClient::updateDepth()
{
if (surface()->buffer()->hasAlphaChannel() && !isDesktop()) {
setDepth(32);
} else {
setDepth(24);
}
}
bool WaylandClient::isShown(bool shaded_is_shown) const
{
Q_UNUSED(shaded_is_shown)
return !isZombie() && !isHidden() && !isMinimized();
}
bool WaylandClient::isHiddenInternal() const
{
return isHidden();
}
void WaylandClient::hideClient(bool hide)
{
if (hide) {
internalHide();
} else {
internalShow();
}
}
bool WaylandClient::isHidden() const
{
return m_isHidden;
}
void WaylandClient::internalShow()
{
if (!isHidden()) {
return;
}
m_isHidden = false;
addRepaintFull();
emit windowShown(this);
}
void WaylandClient::internalHide()
{
if (isHidden()) {
return;
}
if (isMoveResize()) {
leaveMoveResize();
}
m_isHidden = true;
addWorkspaceRepaint(visibleRect());
workspace()->clientHidden(this);
emit windowHidden(this);
}
} // namespace KWin