/* SPDX-FileCopyrightText: 2015 Martin Flöser SPDX-FileCopyrightText: 2018 David Edmundson SPDX-FileCopyrightText: 2020 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #include "waylandclient.h" #include "screens.h" #include "wayland_server.h" #include "workspace.h" #include #include #include #include #include #include #include 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 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(); } } } // namespace KWin