/* SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #include "scene/windowitem.h" #include "effect/effecthandler.h" #include "internalwindow.h" #include "scene/decorationitem.h" #include "scene/shadowitem.h" #include "scene/surfaceitem_internal.h" #include "scene/surfaceitem_wayland.h" #include "scene/surfaceitem_x11.h" #include "virtualdesktops.h" #include "wayland_server.h" #include "window.h" #include "workspace.h" #if KWIN_BUILD_X11 #include "x11window.h" #endif #include namespace KWin { WindowItem::WindowItem(Window *window, Item *parent) : Item(parent) , m_window(window) { connect(window, &Window::decorationChanged, this, &WindowItem::updateDecorationItem); updateDecorationItem(); connect(window, &Window::shadowChanged, this, &WindowItem::updateShadowItem); updateShadowItem(); connect(window, &Window::frameGeometryChanged, this, &WindowItem::updatePosition); updatePosition(); if (waylandServer()) { connect(waylandServer(), &WaylandServer::lockStateChanged, this, &WindowItem::updateVisibility); } if (!window->readyForPainting()) { connect(window, &Window::readyForPaintingChanged, this, &WindowItem::updateVisibility); } connect(window, &Window::lockScreenOverlayChanged, this, &WindowItem::updateVisibility); connect(window, &Window::minimizedChanged, this, &WindowItem::updateVisibility); connect(window, &Window::hiddenChanged, this, &WindowItem::updateVisibility); connect(window, &Window::hiddenByShowDesktopChanged, this, &WindowItem::updateVisibility); connect(window, &Window::activitiesChanged, this, &WindowItem::updateVisibility); connect(window, &Window::desktopsChanged, this, &WindowItem::updateVisibility); connect(window, &Window::offscreenRenderingChanged, this, &WindowItem::updateVisibility); connect(workspace(), &Workspace::currentActivityChanged, this, &WindowItem::updateVisibility); connect(workspace(), &Workspace::currentDesktopChanged, this, &WindowItem::updateVisibility); updateVisibility(); connect(window, &Window::opacityChanged, this, &WindowItem::updateOpacity); updateOpacity(); connect(window, &Window::stackingOrderChanged, this, &WindowItem::updateStackingOrder); updateStackingOrder(); connect(window, &Window::closed, this, &WindowItem::freeze); m_effectWindow = std::make_unique(this); } WindowItem::~WindowItem() { } SurfaceItem *WindowItem::surfaceItem() const { return m_surfaceItem.get(); } DecorationItem *WindowItem::decorationItem() const { return m_decorationItem.get(); } ShadowItem *WindowItem::shadowItem() const { return m_shadowItem.get(); } Window *WindowItem::window() const { return m_window; } EffectWindow *WindowItem::effectWindow() const { return m_effectWindow.get(); } void WindowItem::refVisible(int reason) { if (reason & PAINT_DISABLED_BY_HIDDEN) { m_forceVisibleByHiddenCount++; } if (reason & PAINT_DISABLED_BY_DESKTOP) { m_forceVisibleByDesktopCount++; } if (reason & PAINT_DISABLED_BY_MINIMIZE) { m_forceVisibleByMinimizeCount++; } if (reason & PAINT_DISABLED_BY_ACTIVITY) { m_forceVisibleByActivityCount++; } updateVisibility(); } void WindowItem::unrefVisible(int reason) { if (reason & PAINT_DISABLED_BY_HIDDEN) { Q_ASSERT(m_forceVisibleByHiddenCount > 0); m_forceVisibleByHiddenCount--; } if (reason & PAINT_DISABLED_BY_DESKTOP) { Q_ASSERT(m_forceVisibleByDesktopCount > 0); m_forceVisibleByDesktopCount--; } if (reason & PAINT_DISABLED_BY_MINIMIZE) { Q_ASSERT(m_forceVisibleByMinimizeCount > 0); m_forceVisibleByMinimizeCount--; } if (reason & PAINT_DISABLED_BY_ACTIVITY) { Q_ASSERT(m_forceVisibleByActivityCount > 0); m_forceVisibleByActivityCount--; } updateVisibility(); } void WindowItem::elevate() { // Not ideal, but it's also highly unlikely that there are more than 1000 windows. The // elevation constantly increases so it's possible to force specific stacking order. It // can potentially overflow, but it's unlikely to happen because windows are elevated // rarely. static int elevation = 1000; m_elevation = elevation++; updateStackingOrder(); } void WindowItem::deelevate() { m_elevation.reset(); updateStackingOrder(); } bool WindowItem::computeVisibility() const { if (!m_window->readyForPainting()) { return false; } if (waylandServer() && waylandServer()->isScreenLocked()) { return m_window->isLockScreen() || m_window->isInputMethod() || m_window->isLockScreenOverlay(); } if (!m_window->isOnCurrentDesktop()) { if (m_forceVisibleByDesktopCount == 0) { return false; } } if (!m_window->isOnCurrentActivity()) { if (m_forceVisibleByActivityCount == 0) { return false; } } if (m_window->isMinimized()) { if (m_forceVisibleByMinimizeCount == 0) { return false; } } if (m_window->isHidden() || m_window->isHiddenByShowDesktop()) { if (m_forceVisibleByHiddenCount == 0) { return false; } } return true; } void WindowItem::updateVisibility() { const bool visible = computeVisibility(); setVisible(visible); if (m_window->readyForPainting()) { m_window->setSuspended(!visible && !m_window->isOffscreenRendering()); } } void WindowItem::updatePosition() { setPosition(m_window->pos()); } void WindowItem::addSurfaceItemDamageConnects(Item *item) { auto surfaceItem = static_cast(item); connect(surfaceItem, &SurfaceItem::damaged, this, &WindowItem::markDamaged); connect(surfaceItem, &SurfaceItem::childAdded, this, &WindowItem::addSurfaceItemDamageConnects); const auto childItems = item->childItems(); for (const auto &child : childItems) { addSurfaceItemDamageConnects(child); } } void WindowItem::updateSurfaceItem(std::unique_ptr &&surfaceItem) { m_surfaceItem = std::move(surfaceItem); if (m_surfaceItem) { connect(m_window, &Window::shadeChanged, this, &WindowItem::updateSurfaceVisibility); connect(m_window, &Window::bufferGeometryChanged, this, &WindowItem::updateSurfacePosition); connect(m_window, &Window::frameGeometryChanged, this, &WindowItem::updateSurfacePosition); addSurfaceItemDamageConnects(m_surfaceItem.get()); updateSurfacePosition(); updateSurfaceVisibility(); } else { disconnect(m_window, &Window::shadeChanged, this, &WindowItem::updateSurfaceVisibility); disconnect(m_window, &Window::bufferGeometryChanged, this, &WindowItem::updateSurfacePosition); disconnect(m_window, &Window::frameGeometryChanged, this, &WindowItem::updateSurfacePosition); } } void WindowItem::updateSurfacePosition() { const QRectF bufferGeometry = m_window->bufferGeometry(); const QRectF frameGeometry = m_window->frameGeometry(); m_surfaceItem->setPosition(bufferGeometry.topLeft() - frameGeometry.topLeft()); } void WindowItem::updateSurfaceVisibility() { m_surfaceItem->setVisible(!m_window->isShade()); } void WindowItem::updateShadowItem() { Shadow *shadow = m_window->shadow(); if (shadow) { if (!m_shadowItem || m_shadowItem->shadow() != shadow) { m_shadowItem = std::make_unique(shadow, m_window, this); } if (m_decorationItem) { m_shadowItem->stackBefore(m_decorationItem.get()); } else if (m_surfaceItem) { m_shadowItem->stackBefore(m_surfaceItem.get()); } markDamaged(); } else { m_shadowItem.reset(); } } void WindowItem::updateDecorationItem() { if (m_window->isDeleted()) { return; } if (m_window->decoration()) { m_decorationItem = std::make_unique(m_window->decoration(), m_window, this); if (m_shadowItem) { m_decorationItem->stackAfter(m_shadowItem.get()); } else if (m_surfaceItem) { m_decorationItem->stackBefore(m_surfaceItem.get()); } connect(m_window->decoration(), &KDecoration2::Decoration::damaged, this, &WindowItem::markDamaged); markDamaged(); } else { m_decorationItem.reset(); } } void WindowItem::updateOpacity() { setOpacity(m_window->opacity()); } void WindowItem::updateStackingOrder() { if (m_elevation.has_value()) { setZ(m_elevation.value()); } else { setZ(m_window->stackingOrder()); } } void WindowItem::markDamaged() { Q_EMIT m_window->damaged(m_window); } void WindowItem::freeze() { if (m_surfaceItem) { m_surfaceItem->freeze(); } } #if KWIN_BUILD_X11 WindowItemX11::WindowItemX11(X11Window *window, Item *parent) : WindowItem(window, parent) { initialize(); // Xwayland windows and Wayland surfaces are associated asynchronously. connect(window, &Window::surfaceChanged, this, &WindowItemX11::initialize); } void WindowItemX11::initialize() { switch (kwinApp()->operationMode()) { case Application::OperationModeX11: updateSurfaceItem(std::make_unique(static_cast(window()), this)); break; case Application::OperationModeXwayland: if (!window()->surface()) { updateSurfaceItem(nullptr); } else { updateSurfaceItem(std::make_unique(static_cast(window()), this)); } break; case Application::OperationModeWaylandOnly: Q_UNREACHABLE(); } } #endif WindowItemWayland::WindowItemWayland(Window *window, Item *parent) : WindowItem(window, parent) { updateSurfaceItem(std::make_unique(window->surface(), this)); } WindowItemInternal::WindowItemInternal(InternalWindow *window, Item *parent) : WindowItem(window, parent) { updateSurfaceItem(std::make_unique(window, this)); } } // namespace KWin #include "moc_windowitem.cpp"