File indexing completed on 2024-09-15 12:58:51
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/clientbuffer.h" 0012 #include "wayland/clientconnection.h" 0013 #include "wayland/display.h" 0014 #include "wayland/surface_interface.h" 0015 #include "wayland_server.h" 0016 #include "workspace.h" 0017 0018 #include <QFileInfo> 0019 0020 #include <csignal> 0021 0022 #include <sys/types.h> 0023 #include <unistd.h> 0024 0025 using namespace KWaylandServer; 0026 0027 namespace KWin 0028 { 0029 0030 enum WaylandGeometryType { 0031 WaylandGeometryClient = 0x1, 0032 WaylandGeometryFrame = 0x2, 0033 WaylandGeometryBuffer = 0x4, 0034 }; 0035 Q_DECLARE_FLAGS(WaylandGeometryTypes, WaylandGeometryType) 0036 0037 WaylandWindow::WaylandWindow(SurfaceInterface *surface) 0038 { 0039 setSurface(surface); 0040 setupCompositing(); 0041 0042 connect(surface, &SurfaceInterface::shadowChanged, 0043 this, &WaylandWindow::updateShadow); 0044 connect(this, &WaylandWindow::frameGeometryChanged, 0045 this, &WaylandWindow::updateClientOutputs); 0046 connect(this, &WaylandWindow::desktopFileNameChanged, 0047 this, &WaylandWindow::updateIcon); 0048 connect(workspace(), &Workspace::outputsChanged, this, &WaylandWindow::updateClientOutputs); 0049 connect(surface->client(), &ClientConnection::aboutToBeDestroyed, 0050 this, &WaylandWindow::destroyWindow); 0051 0052 updateResourceName(); 0053 updateIcon(); 0054 } 0055 0056 std::unique_ptr<WindowItem> WaylandWindow::createItem(Scene *scene) 0057 { 0058 return std::make_unique<WindowItemWayland>(this, scene); 0059 } 0060 0061 QString WaylandWindow::captionNormal() const 0062 { 0063 return m_captionNormal; 0064 } 0065 0066 QString WaylandWindow::captionSuffix() const 0067 { 0068 return m_captionSuffix; 0069 } 0070 0071 pid_t WaylandWindow::pid() const 0072 { 0073 return surface()->client()->processId(); 0074 } 0075 0076 bool WaylandWindow::isClient() const 0077 { 0078 return true; 0079 } 0080 0081 bool WaylandWindow::isLockScreen() const 0082 { 0083 return surface()->client() == waylandServer()->screenLockerClientConnection(); 0084 } 0085 0086 bool WaylandWindow::isLocalhost() const 0087 { 0088 return true; 0089 } 0090 0091 Window *WaylandWindow::findModal(bool allow_itself) 0092 { 0093 return nullptr; 0094 } 0095 0096 QRectF WaylandWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size) 0097 { 0098 const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center()); 0099 0100 qreal width = size.width(); 0101 qreal height = size.height(); 0102 0103 // don't allow growing larger than workarea 0104 if (width > area.width()) { 0105 width = area.width(); 0106 } 0107 if (height > area.height()) { 0108 height = area.height(); 0109 } 0110 return QRectF(geometry.topLeft(), QSizeF(width, height)); 0111 } 0112 0113 void WaylandWindow::killWindow() 0114 { 0115 if (!surface()) { 0116 return; 0117 } 0118 auto c = surface()->client(); 0119 if (c->processId() == getpid() || c->processId() == 0) { 0120 c->destroy(); 0121 return; 0122 } 0123 ::kill(c->processId(), SIGTERM); 0124 // give it time to terminate and only if terminate fails, try destroy Wayland connection 0125 QTimer::singleShot(5000, c, &ClientConnection::destroy); 0126 } 0127 0128 QString WaylandWindow::windowRole() const 0129 { 0130 return QString(); 0131 } 0132 0133 bool WaylandWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const 0134 { 0135 if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) { 0136 if (other->desktopFileName() == desktopFileName()) { 0137 return true; 0138 } 0139 } 0140 if (auto s = other->surface()) { 0141 return s->client() == surface()->client(); 0142 } 0143 return false; 0144 } 0145 0146 bool WaylandWindow::belongsToDesktop() const 0147 { 0148 const auto clients = waylandServer()->windows(); 0149 0150 return std::any_of(clients.constBegin(), clients.constEnd(), 0151 [this](const Window *client) { 0152 if (belongsToSameApplication(client, SameApplicationChecks())) { 0153 return client->isDesktop(); 0154 } 0155 return false; 0156 }); 0157 } 0158 0159 void WaylandWindow::updateClientOutputs() 0160 { 0161 surface()->setOutputs(waylandServer()->display()->outputsIntersecting(frameGeometry().toAlignedRect())); 0162 if (output()) { 0163 surface()->setPreferredScale(output()->scale()); 0164 } 0165 } 0166 0167 void WaylandWindow::updateIcon() 0168 { 0169 const QString waylandIconName = QStringLiteral("wayland"); 0170 const QString dfIconName = iconFromDesktopFile(); 0171 const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName; 0172 if (iconName == icon().name()) { 0173 return; 0174 } 0175 setIcon(QIcon::fromTheme(iconName)); 0176 } 0177 0178 void WaylandWindow::updateResourceName() 0179 { 0180 const QFileInfo fileInfo(surface()->client()->executablePath()); 0181 if (fileInfo.exists()) { 0182 const QByteArray executableFileName = fileInfo.fileName().toUtf8(); 0183 setResourceClass(executableFileName, executableFileName); 0184 } 0185 } 0186 0187 void WaylandWindow::updateCaption() 0188 { 0189 const QString oldSuffix = m_captionSuffix; 0190 const auto shortcut = shortcutCaptionSuffix(); 0191 m_captionSuffix = shortcut; 0192 if ((!isSpecialWindow() || isToolbar()) && findWindowWithSameCaption()) { 0193 int i = 2; 0194 do { 0195 m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>'); 0196 i++; 0197 } while (findWindowWithSameCaption()); 0198 } 0199 if (m_captionSuffix != oldSuffix) { 0200 Q_EMIT captionChanged(); 0201 } 0202 } 0203 0204 void WaylandWindow::setCaption(const QString &caption) 0205 { 0206 const QString oldSuffix = m_captionSuffix; 0207 m_captionNormal = caption.simplified(); 0208 updateCaption(); 0209 if (m_captionSuffix == oldSuffix) { 0210 // Don't emit caption change twice it already got emitted by the changing suffix. 0211 Q_EMIT captionChanged(); 0212 } 0213 } 0214 0215 void WaylandWindow::doSetActive() 0216 { 0217 if (isActive()) { // TODO: Xwayland clients must be unfocused somewhere else. 0218 StackingUpdatesBlocker blocker(workspace()); 0219 workspace()->focusToNull(); 0220 } 0221 } 0222 0223 void WaylandWindow::updateDepth() 0224 { 0225 if (surface()->buffer()->hasAlphaChannel()) { 0226 setDepth(32); 0227 } else { 0228 setDepth(24); 0229 } 0230 } 0231 0232 void WaylandWindow::cleanGrouping() 0233 { 0234 if (transientFor()) { 0235 transientFor()->removeTransient(this); 0236 } 0237 for (auto it = transients().constBegin(); it != transients().constEnd();) { 0238 if ((*it)->transientFor() == this) { 0239 removeTransient(*it); 0240 it = transients().constBegin(); // restart, just in case something more has changed with the list 0241 } else { 0242 ++it; 0243 } 0244 } 0245 } 0246 0247 bool WaylandWindow::isShown() const 0248 { 0249 return !isZombie() && !isHidden() && !isMinimized(); 0250 } 0251 0252 bool WaylandWindow::isHiddenInternal() const 0253 { 0254 return isHidden(); 0255 } 0256 0257 bool WaylandWindow::isHidden() const 0258 { 0259 return m_isHidden; 0260 } 0261 0262 void WaylandWindow::showClient() 0263 { 0264 if (!isHidden()) { 0265 return; 0266 } 0267 m_isHidden = false; 0268 Q_EMIT windowShown(this); 0269 } 0270 0271 void WaylandWindow::hideClient() 0272 { 0273 if (isHidden()) { 0274 return; 0275 } 0276 if (isInteractiveMoveResize()) { 0277 leaveInteractiveMoveResize(); 0278 } 0279 m_isHidden = true; 0280 workspace()->windowHidden(this); 0281 Q_EMIT windowHidden(this); 0282 } 0283 0284 QRectF WaylandWindow::frameRectToBufferRect(const QRectF &rect) const 0285 { 0286 return QRectF(rect.topLeft(), surface()->size()); 0287 } 0288 0289 void WaylandWindow::updateGeometry(const QRectF &rect) 0290 { 0291 const QRectF oldClientGeometry = m_clientGeometry; 0292 const QRectF oldFrameGeometry = m_frameGeometry; 0293 const QRectF oldBufferGeometry = m_bufferGeometry; 0294 const Output *oldOutput = m_output; 0295 0296 m_clientGeometry = frameRectToClientRect(rect); 0297 m_frameGeometry = rect; 0298 m_bufferGeometry = frameRectToBufferRect(rect); 0299 0300 WaylandGeometryTypes changedGeometries; 0301 0302 if (m_clientGeometry != oldClientGeometry) { 0303 changedGeometries |= WaylandGeometryClient; 0304 } 0305 if (m_frameGeometry != oldFrameGeometry) { 0306 changedGeometries |= WaylandGeometryFrame; 0307 } 0308 if (m_bufferGeometry != oldBufferGeometry) { 0309 changedGeometries |= WaylandGeometryBuffer; 0310 } 0311 0312 if (!changedGeometries) { 0313 return; 0314 } 0315 0316 m_output = workspace()->outputAt(rect.center()); 0317 updateWindowRules(Rules::Position | Rules::Size); 0318 0319 if (changedGeometries & WaylandGeometryBuffer) { 0320 Q_EMIT bufferGeometryChanged(this, oldBufferGeometry); 0321 } 0322 if (changedGeometries & WaylandGeometryClient) { 0323 Q_EMIT clientGeometryChanged(this, oldClientGeometry); 0324 } 0325 if (changedGeometries & WaylandGeometryFrame) { 0326 Q_EMIT frameGeometryChanged(this, oldFrameGeometry); 0327 } 0328 if (oldOutput != m_output) { 0329 Q_EMIT screenChanged(); 0330 } 0331 Q_EMIT geometryShapeChanged(this, oldFrameGeometry); 0332 } 0333 0334 } // namespace KWin