File indexing completed on 2024-11-10 04:57:59
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"