File indexing completed on 2024-11-10 04:57:48
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Martin Flöser <mgraesslin@kde.org> 0006 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "internalwindow.h" 0011 #include "decorations/decorationbridge.h" 0012 #include "scene/surfaceitem.h" 0013 #include "scene/windowitem.h" 0014 #include "workspace.h" 0015 0016 #include <KDecoration2/Decoration> 0017 0018 #include <QMouseEvent> 0019 #include <QWindow> 0020 0021 Q_DECLARE_METATYPE(NET::WindowType) 0022 0023 static const QByteArray s_skipClosePropertyName = QByteArrayLiteral("KWIN_SKIP_CLOSE_ANIMATION"); 0024 static const QByteArray s_shadowEnabledPropertyName = QByteArrayLiteral("kwin_shadow_enabled"); 0025 0026 namespace KWin 0027 { 0028 0029 InternalWindow::InternalWindow(QWindow *handle) 0030 : m_handle(handle) 0031 , m_internalWindowFlags(handle->flags()) 0032 { 0033 connect(m_handle, &QWindow::xChanged, this, &InternalWindow::updateInternalWindowGeometry); 0034 connect(m_handle, &QWindow::yChanged, this, &InternalWindow::updateInternalWindowGeometry); 0035 connect(m_handle, &QWindow::widthChanged, this, &InternalWindow::updateInternalWindowGeometry); 0036 connect(m_handle, &QWindow::heightChanged, this, &InternalWindow::updateInternalWindowGeometry); 0037 connect(m_handle, &QWindow::windowTitleChanged, this, &InternalWindow::setCaption); 0038 connect(m_handle, &QWindow::opacityChanged, this, &InternalWindow::setOpacity); 0039 connect(m_handle, &QWindow::destroyed, this, &InternalWindow::destroyWindow); 0040 0041 setCaption(m_handle->title()); 0042 setIcon(QIcon::fromTheme(QStringLiteral("kwin"))); 0043 setOnAllDesktops(true); 0044 setOpacity(m_handle->opacity()); 0045 setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); 0046 updateColorScheme(); 0047 updateShadow(); 0048 0049 setMoveResizeGeometry(m_handle->geometry()); 0050 commitGeometry(m_handle->geometry()); 0051 0052 updateDecoration(true); 0053 0054 m_handle->installEventFilter(this); 0055 } 0056 0057 InternalWindow::~InternalWindow() 0058 { 0059 } 0060 0061 std::unique_ptr<WindowItem> InternalWindow::createItem(Scene *scene) 0062 { 0063 return std::make_unique<WindowItemInternal>(this, scene); 0064 } 0065 0066 bool InternalWindow::isClient() const 0067 { 0068 return true; 0069 } 0070 0071 bool InternalWindow::hitTest(const QPointF &point) const 0072 { 0073 if (!Window::hitTest(point)) { 0074 return false; 0075 } 0076 0077 const QRegion mask = m_handle->mask(); 0078 if (!mask.isEmpty() && !mask.contains(mapToLocal(point).toPoint())) { 0079 return false; 0080 } else if (m_handle->property("outputOnly").toBool()) { 0081 return false; 0082 } 0083 0084 return true; 0085 } 0086 0087 void InternalWindow::pointerEnterEvent(const QPointF &globalPos) 0088 { 0089 Window::pointerEnterEvent(globalPos); 0090 0091 QEnterEvent enterEvent(pos(), pos(), globalPos); 0092 QCoreApplication::sendEvent(m_handle, &enterEvent); 0093 } 0094 0095 void InternalWindow::pointerLeaveEvent() 0096 { 0097 if (!m_handle) { 0098 return; 0099 } 0100 Window::pointerLeaveEvent(); 0101 0102 QEvent event(QEvent::Leave); 0103 QCoreApplication::sendEvent(m_handle, &event); 0104 } 0105 0106 bool InternalWindow::eventFilter(QObject *watched, QEvent *event) 0107 { 0108 if (watched == m_handle && event->type() == QEvent::DynamicPropertyChange) { 0109 QDynamicPropertyChangeEvent *pe = static_cast<QDynamicPropertyChangeEvent *>(event); 0110 if (pe->propertyName() == s_skipClosePropertyName) { 0111 setSkipCloseAnimation(m_handle->property(s_skipClosePropertyName).toBool()); 0112 } 0113 if (pe->propertyName() == s_shadowEnabledPropertyName) { 0114 updateShadow(); 0115 } 0116 } 0117 return false; 0118 } 0119 0120 qreal InternalWindow::bufferScale() const 0121 { 0122 if (m_handle) { 0123 return m_handle->devicePixelRatio(); 0124 } 0125 return 1; 0126 } 0127 0128 QString InternalWindow::captionNormal() const 0129 { 0130 return m_captionNormal; 0131 } 0132 0133 QString InternalWindow::captionSuffix() const 0134 { 0135 return m_captionSuffix; 0136 } 0137 0138 QSizeF InternalWindow::minSize() const 0139 { 0140 return m_handle->minimumSize(); 0141 } 0142 0143 QSizeF InternalWindow::maxSize() const 0144 { 0145 return m_handle->maximumSize(); 0146 } 0147 0148 NET::WindowType InternalWindow::windowType() const 0149 { 0150 return NET::Normal; 0151 } 0152 0153 void InternalWindow::killWindow() 0154 { 0155 // We don't kill our internal windows. 0156 } 0157 0158 bool InternalWindow::isPopupWindow() const 0159 { 0160 if (Window::isPopupWindow()) { 0161 return true; 0162 } 0163 return m_internalWindowFlags.testFlag(Qt::Popup); 0164 } 0165 0166 QString InternalWindow::windowRole() const 0167 { 0168 return QString(); 0169 } 0170 0171 void InternalWindow::closeWindow() 0172 { 0173 if (m_handle) { 0174 m_handle->hide(); 0175 } 0176 } 0177 0178 bool InternalWindow::isCloseable() const 0179 { 0180 return true; 0181 } 0182 0183 bool InternalWindow::isMovable() const 0184 { 0185 return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); 0186 } 0187 0188 bool InternalWindow::isMovableAcrossScreens() const 0189 { 0190 return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); 0191 } 0192 0193 bool InternalWindow::isResizable() const 0194 { 0195 return true; 0196 } 0197 0198 bool InternalWindow::isPlaceable() const 0199 { 0200 return !m_internalWindowFlags.testFlag(Qt::BypassWindowManagerHint) && !m_internalWindowFlags.testFlag(Qt::Popup); 0201 } 0202 0203 bool InternalWindow::noBorder() const 0204 { 0205 return m_userNoBorder || m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); 0206 } 0207 0208 bool InternalWindow::userCanSetNoBorder() const 0209 { 0210 return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); 0211 } 0212 0213 bool InternalWindow::wantsInput() const 0214 { 0215 return false; 0216 } 0217 0218 bool InternalWindow::isInternal() const 0219 { 0220 return true; 0221 } 0222 0223 bool InternalWindow::isLockScreen() const 0224 { 0225 if (m_handle) { 0226 return m_handle->property("org_kde_ksld_emergency").toBool(); 0227 } 0228 return false; 0229 } 0230 0231 bool InternalWindow::isOutline() const 0232 { 0233 if (m_handle) { 0234 return m_handle->property("__kwin_outline").toBool(); 0235 } 0236 return false; 0237 } 0238 0239 QRectF InternalWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size) 0240 { 0241 if (!m_handle) { 0242 return geometry; 0243 } 0244 const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center()); 0245 return QRectF(moveResizeGeometry().topLeft(), size.boundedTo(area.size())); 0246 } 0247 0248 void InternalWindow::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) 0249 { 0250 if (areGeometryUpdatesBlocked()) { 0251 setPendingMoveResizeMode(mode); 0252 return; 0253 } 0254 0255 const QSizeF requestedClientSize = frameSizeToClientSize(rect.size()); 0256 if (clientSize() == requestedClientSize) { 0257 commitGeometry(rect); 0258 } else { 0259 requestGeometry(rect); 0260 } 0261 } 0262 0263 Window *InternalWindow::findModal(bool allow_itself) 0264 { 0265 return nullptr; 0266 } 0267 0268 bool InternalWindow::takeFocus() 0269 { 0270 return false; 0271 } 0272 0273 void InternalWindow::setNoBorder(bool set) 0274 { 0275 if (!userCanSetNoBorder()) { 0276 return; 0277 } 0278 if (m_userNoBorder == set) { 0279 return; 0280 } 0281 m_userNoBorder = set; 0282 updateDecoration(true); 0283 } 0284 0285 void InternalWindow::createDecoration(const QRectF &oldGeometry) 0286 { 0287 setDecoration(std::shared_ptr<KDecoration2::Decoration>(Workspace::self()->decorationBridge()->createDecoration(this))); 0288 moveResize(QRectF(oldGeometry.topLeft(), clientSizeToFrameSize(clientSize()))); 0289 } 0290 0291 void InternalWindow::destroyDecoration() 0292 { 0293 const QSizeF clientSize = frameSizeToClientSize(moveResizeGeometry().size()); 0294 setDecoration(nullptr); 0295 resize(clientSize); 0296 } 0297 0298 void InternalWindow::updateDecoration(bool check_workspace_pos, bool force) 0299 { 0300 if (!force && isDecorated() == !noBorder()) { 0301 return; 0302 } 0303 0304 GeometryUpdatesBlocker blocker(this); 0305 0306 const QRectF oldFrameGeometry = frameGeometry(); 0307 if (force) { 0308 destroyDecoration(); 0309 } 0310 0311 if (!noBorder()) { 0312 createDecoration(oldFrameGeometry); 0313 } else { 0314 destroyDecoration(); 0315 } 0316 0317 updateShadow(); 0318 0319 if (check_workspace_pos) { 0320 checkWorkspacePosition(oldFrameGeometry); 0321 } 0322 } 0323 0324 void InternalWindow::invalidateDecoration() 0325 { 0326 updateDecoration(true, true); 0327 } 0328 0329 void InternalWindow::destroyWindow() 0330 { 0331 m_handle->removeEventFilter(this); 0332 m_handle->disconnect(this); 0333 0334 markAsDeleted(); 0335 if (isInteractiveMoveResize()) { 0336 leaveInteractiveMoveResize(); 0337 Q_EMIT interactiveMoveResizeFinished(); 0338 } 0339 0340 Q_EMIT closed(); 0341 0342 workspace()->removeInternalWindow(this); 0343 m_handle = nullptr; 0344 0345 unref(); 0346 } 0347 0348 bool InternalWindow::hasPopupGrab() const 0349 { 0350 return !m_handle->flags().testFlag(Qt::WindowTransparentForInput) && m_handle->flags().testFlag(Qt::Popup) && !m_handle->flags().testFlag(Qt::ToolTip); 0351 } 0352 0353 void InternalWindow::popupDone() 0354 { 0355 m_handle->close(); 0356 } 0357 0358 GraphicsBuffer *InternalWindow::graphicsBuffer() const 0359 { 0360 return m_graphicsBufferRef.buffer(); 0361 } 0362 0363 GraphicsBufferOrigin InternalWindow::graphicsBufferOrigin() const 0364 { 0365 return m_graphicsBufferOrigin; 0366 } 0367 0368 void InternalWindow::present(const InternalWindowFrame &frame) 0369 { 0370 const QSize bufferSize = frame.buffer->size() / bufferScale(); 0371 QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); 0372 if (isInteractiveResize()) { 0373 geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity()); 0374 } 0375 0376 commitGeometry(geometry); 0377 markAsMapped(); 0378 0379 m_graphicsBufferRef = frame.buffer; 0380 m_graphicsBufferOrigin = frame.bufferOrigin; 0381 0382 surfaceItem()->addDamage(frame.bufferDamage); 0383 } 0384 0385 QWindow *InternalWindow::handle() const 0386 { 0387 return m_handle; 0388 } 0389 0390 bool InternalWindow::acceptsFocus() const 0391 { 0392 return false; 0393 } 0394 0395 bool InternalWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const 0396 { 0397 const InternalWindow *otherInternal = qobject_cast<const InternalWindow *>(other); 0398 if (!otherInternal) { 0399 return false; 0400 } 0401 if (otherInternal == this) { 0402 return true; 0403 } 0404 return otherInternal->handle()->isAncestorOf(handle()) || handle()->isAncestorOf(otherInternal->handle()); 0405 } 0406 0407 void InternalWindow::doInteractiveResizeSync(const QRectF &rect) 0408 { 0409 moveResize(rect); 0410 } 0411 0412 void InternalWindow::updateCaption() 0413 { 0414 const QString suffix = shortcutCaptionSuffix(); 0415 if (m_captionSuffix != suffix) { 0416 m_captionSuffix = suffix; 0417 Q_EMIT captionChanged(); 0418 } 0419 } 0420 0421 void InternalWindow::requestGeometry(const QRectF &rect) 0422 { 0423 if (m_handle) { 0424 m_handle->setGeometry(frameRectToClientRect(rect).toRect()); 0425 } 0426 } 0427 0428 void InternalWindow::commitGeometry(const QRectF &rect) 0429 { 0430 // The client geometry and the buffer geometry are the same. 0431 const QRectF oldClientGeometry = m_clientGeometry; 0432 const QRectF oldFrameGeometry = m_frameGeometry; 0433 const Output *oldOutput = m_output; 0434 0435 Q_EMIT frameGeometryAboutToChange(); 0436 0437 m_clientGeometry = frameRectToClientRect(rect); 0438 m_frameGeometry = rect; 0439 m_bufferGeometry = m_clientGeometry; 0440 0441 if (oldClientGeometry == m_clientGeometry && oldFrameGeometry == m_frameGeometry) { 0442 return; 0443 } 0444 0445 m_output = workspace()->outputAt(rect.center()); 0446 syncGeometryToInternalWindow(); 0447 0448 if (oldClientGeometry != m_clientGeometry) { 0449 Q_EMIT bufferGeometryChanged(oldClientGeometry); 0450 Q_EMIT clientGeometryChanged(oldClientGeometry); 0451 } 0452 if (oldFrameGeometry != m_frameGeometry) { 0453 Q_EMIT frameGeometryChanged(oldFrameGeometry); 0454 } 0455 if (oldOutput != m_output) { 0456 Q_EMIT outputChanged(); 0457 } 0458 } 0459 0460 void InternalWindow::setCaption(const QString &caption) 0461 { 0462 if (m_captionNormal == caption) { 0463 return; 0464 } 0465 0466 m_captionNormal = caption; 0467 Q_EMIT captionNormalChanged(); 0468 Q_EMIT captionChanged(); 0469 } 0470 0471 void InternalWindow::markAsMapped() 0472 { 0473 if (!ready_for_painting) { 0474 setupCompositing(); 0475 setReadyForPainting(); 0476 workspace()->addInternalWindow(this); 0477 } 0478 } 0479 0480 void InternalWindow::syncGeometryToInternalWindow() 0481 { 0482 if (m_handle->geometry() == frameRectToClientRect(frameGeometry())) { 0483 return; 0484 } 0485 0486 QTimer::singleShot(0, this, [this] { 0487 requestGeometry(frameGeometry()); 0488 }); 0489 } 0490 0491 void InternalWindow::updateInternalWindowGeometry() 0492 { 0493 if (!isInteractiveMoveResize()) { 0494 const QRectF rect = clientRectToFrameRect(m_handle->geometry()); 0495 setMoveResizeGeometry(rect); 0496 commitGeometry(rect); 0497 } 0498 } 0499 0500 } 0501 0502 #include "moc_internalwindow.cpp"