File indexing completed on 2024-04-28 05:30:34

0001 /*
0002     SPDX-FileCopyrightText: 2015 Martin Flöser <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
0004     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "waylandwindow.h"
0010 #include "scene/windowitem.h"
0011 #include "wayland/clientconnection.h"
0012 #include "wayland/display.h"
0013 #include "wayland/surface.h"
0014 #include "wayland_server.h"
0015 #include "workspace.h"
0016 
0017 #include <QFileInfo>
0018 
0019 #include <csignal>
0020 
0021 #include <sys/types.h>
0022 #include <unistd.h>
0023 
0024 namespace KWin
0025 {
0026 
0027 enum WaylandGeometryType {
0028     WaylandGeometryClient = 0x1,
0029     WaylandGeometryFrame = 0x2,
0030     WaylandGeometryBuffer = 0x4,
0031 };
0032 Q_DECLARE_FLAGS(WaylandGeometryTypes, WaylandGeometryType)
0033 
0034 WaylandWindow::WaylandWindow(SurfaceInterface *surface)
0035     : m_isScreenLocker(surface->client() == waylandServer()->screenLockerClientConnection())
0036 {
0037     setSurface(surface);
0038 
0039     connect(surface, &SurfaceInterface::shadowChanged,
0040             this, &WaylandWindow::updateShadow);
0041     connect(this, &WaylandWindow::frameGeometryChanged,
0042             this, &WaylandWindow::updateClientOutputs);
0043     connect(this, &WaylandWindow::desktopFileNameChanged,
0044             this, &WaylandWindow::updateIcon);
0045     connect(workspace(), &Workspace::outputsChanged, this, &WaylandWindow::updateClientOutputs);
0046 
0047     updateResourceName();
0048     updateIcon();
0049 }
0050 
0051 std::unique_ptr<WindowItem> WaylandWindow::createItem(Scene *scene)
0052 {
0053     return std::make_unique<WindowItemWayland>(this, scene);
0054 }
0055 
0056 QString WaylandWindow::captionNormal() const
0057 {
0058     return m_captionNormal;
0059 }
0060 
0061 QString WaylandWindow::captionSuffix() const
0062 {
0063     return m_captionSuffix;
0064 }
0065 
0066 pid_t WaylandWindow::pid() const
0067 {
0068     return surface() ? surface()->client()->processId() : -1;
0069 }
0070 
0071 bool WaylandWindow::isClient() const
0072 {
0073     return true;
0074 }
0075 
0076 bool WaylandWindow::isLockScreen() const
0077 {
0078     return m_isScreenLocker;
0079 }
0080 
0081 bool WaylandWindow::isLocalhost() const
0082 {
0083     return true;
0084 }
0085 
0086 Window *WaylandWindow::findModal(bool allow_itself)
0087 {
0088     return nullptr;
0089 }
0090 
0091 QRectF WaylandWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size)
0092 {
0093     const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center());
0094 
0095     qreal width = size.width();
0096     qreal height = size.height();
0097 
0098     // don't allow growing larger than workarea
0099     if (width > area.width()) {
0100         width = area.width();
0101     }
0102     if (height > area.height()) {
0103         height = area.height();
0104     }
0105     return QRectF(geometry.topLeft(), QSizeF(width, height));
0106 }
0107 
0108 void WaylandWindow::killWindow()
0109 {
0110     if (!surface()) {
0111         return;
0112     }
0113     auto c = surface()->client();
0114     if (c->processId() == getpid() || c->processId() == 0) {
0115         c->destroy();
0116         return;
0117     }
0118     ::kill(c->processId(), SIGTERM);
0119     // give it time to terminate and only if terminate fails, try destroy Wayland connection
0120     QTimer::singleShot(5000, c, &ClientConnection::destroy);
0121 }
0122 
0123 QString WaylandWindow::windowRole() const
0124 {
0125     return QString();
0126 }
0127 
0128 bool WaylandWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const
0129 {
0130     if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) {
0131         if (other->desktopFileName() == desktopFileName()) {
0132             return true;
0133         }
0134     }
0135     if (auto s = other->surface()) {
0136         return s->client() == surface()->client();
0137     }
0138     return false;
0139 }
0140 
0141 bool WaylandWindow::belongsToDesktop() const
0142 {
0143     const auto clients = waylandServer()->windows();
0144 
0145     return std::any_of(clients.constBegin(), clients.constEnd(),
0146                        [this](const Window *client) {
0147                            if (belongsToSameApplication(client, SameApplicationChecks())) {
0148                                return client->isDesktop();
0149                            }
0150                            return false;
0151                        });
0152 }
0153 
0154 void WaylandWindow::updateClientOutputs()
0155 {
0156     if (isDeleted()) {
0157         return;
0158     }
0159     surface()->setOutputs(waylandServer()->display()->outputsIntersecting(frameGeometry().toAlignedRect()),
0160                           waylandServer()->display()->largestIntersectingOutput(frameGeometry().toAlignedRect()));
0161     if (output()) {
0162         surface()->setPreferredBufferScale(output()->scale());
0163         surface()->setPreferredBufferTransform(output()->transform());
0164         surface()->setPreferredColorDescription(output()->colorDescription());
0165     }
0166 }
0167 
0168 void WaylandWindow::updateIcon()
0169 {
0170     const QString waylandIconName = QStringLiteral("wayland");
0171     const QString dfIconName = iconFromDesktopFile();
0172     const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName;
0173     if (iconName == icon().name()) {
0174         return;
0175     }
0176     setIcon(QIcon::fromTheme(iconName));
0177 }
0178 
0179 void WaylandWindow::updateResourceName()
0180 {
0181     const QFileInfo fileInfo(surface()->client()->executablePath());
0182     if (fileInfo.exists()) {
0183         const QByteArray executableFileName = fileInfo.fileName().toUtf8();
0184         setResourceClass(executableFileName, executableFileName);
0185     }
0186 }
0187 
0188 void WaylandWindow::updateCaption()
0189 {
0190     const QString suffix = shortcutCaptionSuffix();
0191     if (m_captionSuffix != suffix) {
0192         m_captionSuffix = suffix;
0193         Q_EMIT captionChanged();
0194     }
0195 }
0196 
0197 void WaylandWindow::setCaption(const QString &caption)
0198 {
0199     const QString simplified = caption.simplified();
0200     if (m_captionNormal != simplified) {
0201         m_captionNormal = simplified;
0202         Q_EMIT captionNormalChanged();
0203         Q_EMIT captionChanged();
0204     }
0205 }
0206 
0207 void WaylandWindow::doSetActive()
0208 {
0209     if (isActive()) { // TODO: Xwayland clients must be unfocused somewhere else.
0210         StackingUpdatesBlocker blocker(workspace());
0211         workspace()->focusToNull();
0212     }
0213 }
0214 
0215 void WaylandWindow::cleanGrouping()
0216 {
0217     // We want to break parent-child relationships, but preserve stacking
0218     // order constraints at the same time for window closing animations.
0219 
0220     if (transientFor()) {
0221         transientFor()->removeTransientFromList(this);
0222         setTransientFor(nullptr);
0223     }
0224 
0225     const auto children = transients();
0226     for (Window *transient : children) {
0227         removeTransientFromList(transient);
0228         transient->setTransientFor(nullptr);
0229     }
0230 }
0231 
0232 QRectF WaylandWindow::frameRectToBufferRect(const QRectF &rect) const
0233 {
0234     return QRectF(rect.topLeft(), surface()->size());
0235 }
0236 
0237 void WaylandWindow::updateGeometry(const QRectF &rect)
0238 {
0239     const QRectF oldClientGeometry = m_clientGeometry;
0240     const QRectF oldFrameGeometry = m_frameGeometry;
0241     const QRectF oldBufferGeometry = m_bufferGeometry;
0242     const Output *oldOutput = m_output;
0243 
0244     m_clientGeometry = frameRectToClientRect(rect);
0245     m_frameGeometry = rect;
0246     m_bufferGeometry = frameRectToBufferRect(rect);
0247 
0248     WaylandGeometryTypes changedGeometries;
0249 
0250     if (m_clientGeometry != oldClientGeometry) {
0251         changedGeometries |= WaylandGeometryClient;
0252     }
0253     if (m_frameGeometry != oldFrameGeometry) {
0254         changedGeometries |= WaylandGeometryFrame;
0255     }
0256     if (m_bufferGeometry != oldBufferGeometry) {
0257         changedGeometries |= WaylandGeometryBuffer;
0258     }
0259 
0260     if (!changedGeometries) {
0261         return;
0262     }
0263 
0264     m_output = workspace()->outputAt(rect.center());
0265     updateWindowRules(Rules::Position | Rules::Size);
0266 
0267     if (changedGeometries & WaylandGeometryBuffer) {
0268         Q_EMIT bufferGeometryChanged(oldBufferGeometry);
0269     }
0270     if (changedGeometries & WaylandGeometryClient) {
0271         Q_EMIT clientGeometryChanged(oldClientGeometry);
0272     }
0273     if (changedGeometries & WaylandGeometryFrame) {
0274         Q_EMIT frameGeometryChanged(oldFrameGeometry);
0275     }
0276     if (oldOutput != m_output) {
0277         Q_EMIT outputChanged();
0278     }
0279 }
0280 
0281 void WaylandWindow::markAsMapped()
0282 {
0283     if (Q_UNLIKELY(!ready_for_painting)) {
0284         setupCompositing();
0285         setReadyForPainting();
0286     }
0287 }
0288 
0289 } // namespace KWin
0290 
0291 #include "moc_waylandwindow.cpp"