10#include "compositor.h"
25#include <QOpenGLContext>
26#include <QQuickWindow>
28#include <QSGImageNode>
29#include <QSGTextureProvider>
34static bool useGlThumbnails()
36 static bool qtQuickIsSoftware = QStringList({QStringLiteral(
"software"), QStringLiteral(
"softwarecontext")}).contains(QQuickWindow::sceneGraphBackend());
58 if (!m_offscreenTexture) {
61 if (!QOpenGLContext::currentContext()) {
64 m_offscreenTarget.reset();
65 m_offscreenTexture.reset();
68 glDeleteSync(m_acquireFence);
75 using WindowThumbnailSourceKey = std::pair<QQuickWindow *, Window *>;
76 const WindowThumbnailSourceKey key{window, handle};
78 static std::map<WindowThumbnailSourceKey, std::weak_ptr<WindowThumbnailSource>> sources;
79 auto &source = sources[key];
80 if (!source.expired()) {
84 auto s = std::make_shared<WindowThumbnailSource>(window, handle);
87 QObject::connect(handle, &Window::destroyed, [key]() {
90 QObject::connect(window, &QQuickWindow::destroyed, [key]() {
100 .fence = std::exchange(m_acquireFence,
nullptr),
104void WindowThumbnailSource::update()
106 if (m_acquireFence || !m_dirty || !m_handle) {
111 const QRectF geometry = m_handle->visibleGeometry();
112 const qreal devicePixelRatio = m_view->devicePixelRatio();
113 const QSize textureSize = geometry.toAlignedRect().size() * devicePixelRatio;
115 if (!m_offscreenTexture || m_offscreenTexture->size() != textureSize) {
117 if (!m_offscreenTexture) {
120 m_offscreenTexture->setFilter(GL_LINEAR);
121 m_offscreenTexture->setWrapMode(GL_CLAMP_TO_EDGE);
122 m_offscreenTarget = std::make_unique<GLFramebuffer>(m_offscreenTexture.get());
125 RenderTarget offscreenRenderTarget(m_offscreenTarget.get());
126 RenderViewport offscreenViewport(geometry, devicePixelRatio, offscreenRenderTarget);
128 glClearColor(0.0, 0.0, 0.0, 0.0);
129 glClear(GL_COLOR_BUFFER_BIT);
131 QMatrix4x4 projectionMatrix;
132 projectionMatrix.ortho(geometry.x() * devicePixelRatio, (geometry.x() + geometry.width()) * devicePixelRatio,
133 geometry.y() * devicePixelRatio, (geometry.y() + geometry.height()) * devicePixelRatio, -1, 1);
135 WindowPaintData data;
136 data.setProjectionMatrix(projectionMatrix);
148 m_acquireFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
158 QSGTexture *
texture()
const override;
159 void setTexture(
const std::shared_ptr<GLTexture> &nativeTexture);
163 QQuickWindow *m_window;
164 std::shared_ptr<GLTexture> m_nativeTexture;
165 std::unique_ptr<QSGTexture> m_texture;
175 return m_texture.get();
180 if (m_nativeTexture != nativeTexture) {
181 const GLuint textureId = nativeTexture->texture();
182 m_nativeTexture = nativeTexture;
183 m_texture.reset(QNativeInterface::QSGOpenGLTexture::fromNative(textureId, m_window,
184 nativeTexture->size(),
185 QQuickWindow::TextureHasAlphaChannel));
186 m_texture->setFiltering(QSGTexture::Linear);
187 m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
188 m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
192 Q_EMIT textureChanged();
197 m_nativeTexture =
nullptr;
199 Q_EMIT textureChanged();
206 : m_provider(provider)
216 std::unique_ptr<ThumbnailTextureProvider> m_provider;
222 setFlag(ItemHasContents);
225 this, &WindowThumbnailItem::resetSource);
227 this, &WindowThumbnailItem::updateSource);
234 window()->scheduleRenderJob(
new ThumbnailTextureProviderCleanupJob(m_provider),
235 QQuickWindow::AfterSynchronizingStage);
237 qCCritical(KWIN_SCRIPTING) <<
"Can't destroy thumbnail texture provider because window is null";
246 QQuickWindow::AfterSynchronizingStage);
247 m_provider =
nullptr;
253 if (change == QQuickItem::ItemSceneChange) {
256 QQuickItem::itemChange(change, value);
266 if (QQuickItem::isTextureProvider()) {
267 return QQuickItem::textureProvider();
275void WindowThumbnailItem::resetSource()
280void WindowThumbnailItem::updateSource()
282 if (useGlThumbnails() && window() && m_client) {
297 auto [texture, acquireFence] = m_source->acquire();
304 glWaitSync(acquireFence, 0, GL_TIMEOUT_IGNORED);
305 glDeleteSync(acquireFence);
317 const QImage placeholderImage = fallbackImage();
318 m_provider->
setTexture(window()->createTextureFromImage(placeholderImage));
321 QSGImageNode *node =
static_cast<QSGImageNode *
>(oldNode);
323 node = window()->createImageNode();
324 node->setFiltering(QSGTexture::Linear);
326 node->setTexture(m_provider->
texture());
327 node->setTextureCoordinatesTransform(QSGImageNode::NoTransform);
328 node->setRect(paintedRect());
344 if (!m_wId.isNull()) {
346 }
else if (m_client) {
349 updateImplicitSize();
368 this, &WindowThumbnailItem::updateImplicitSize);
373 this, &WindowThumbnailItem::updateImplicitSize);
374 setWId(m_client->internalId());
379 updateImplicitSize();
383void WindowThumbnailItem::updateImplicitSize()
387 frameSize = m_client->frameGeometry().toAlignedRect().size();
389 setImplicitSize(frameSize.width(), frameSize.height());
392QImage WindowThumbnailItem::fallbackImage()
const
395 return m_client->icon().pixmap(window(), boundingRect().size().toSize()).toImage();
400static QRectF centeredSize(
const QRectF &boundingRect,
const QSizeF &size)
402 const QSizeF scaled = size.scaled(boundingRect.size(), Qt::KeepAspectRatio);
403 const qreal x = boundingRect.x() + (boundingRect.width() - scaled.width()) / 2;
404 const qreal y = boundingRect.y() + (boundingRect.height() - scaled.height()) / 2;
405 return QRectF(QPointF(x, y), scaled);
408QRectF WindowThumbnailItem::paintedRect()
const
414 const QSizeF iconSize = m_client->icon().actualSize(window(), boundingRect().size().toSize());
415 return centeredSize(boundingRect(), iconSize);
418 const QRectF visibleGeometry = m_client->visibleGeometry();
419 const QRectF frameGeometry = m_client->frameGeometry();
420 const QSizeF scaled = QSizeF(frameGeometry.size()).scaled(boundingRect().size(), Qt::KeepAspectRatio);
422 const qreal xScale = scaled.width() / frameGeometry.width();
423 const qreal yScale = scaled.height() / frameGeometry.height();
425 QRectF paintedRect(boundingRect().x() + (boundingRect().width() - scaled.width()) / 2,
426 boundingRect().y() + (boundingRect().height() - scaled.height()) / 2,
427 visibleGeometry.width() * xScale,
428 visibleGeometry.height() * yScale);
430 paintedRect.moveLeft(paintedRect.x() + (visibleGeometry.x() - frameGeometry.x()) * xScale);
431 paintedRect.moveTop(paintedRect.y() + (visibleGeometry.y() - frameGeometry.y()) * yScale);
438#include "moc_windowthumbnailitem.cpp"
void compositingToggled(bool active)
void aboutToToggleCompositing()
WorkspaceScene * scene() const
static bool compositing()
Static check to test whether the Compositor is available and active.
RenderBackend * backend() const
static Compositor * self()
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
virtual void renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const QRegion ®ion, const WindowPaintData &data)=0
virtual CompositingType compositingType() const =0
@ PAINT_WINDOW_TRANSFORMED
ItemRenderer * renderer() const
ThumbnailTextureProviderCleanupJob(ThumbnailTextureProvider *provider)
void setTexture(const std::shared_ptr< GLTexture > &nativeTexture)
QSGTexture * texture() const override
ThumbnailTextureProvider(QQuickWindow *window)
void frameGeometryChanged(const QRectF &oldGeometry)
void damaged(KWin::Window *window)
void setWId(qulonglong wId)
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
QSGTextureProvider * textureProvider() const override
bool isTextureProvider() const override
~WindowThumbnailItem() override
void releaseResources() override
WindowThumbnailItem(QQuickItem *parent=nullptr)
void setClient(Window *client)
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override
~WindowThumbnailSource() override
WindowThumbnailSource(QQuickWindow *view, Window *handle)
static std::shared_ptr< WindowThumbnailSource > getOrCreate(QQuickWindow *window, Window *handle)
virtual bool makeOpenGLContextCurrent()
KWIN_EXPORT QRect infiniteRegion()
std::shared_ptr< GLTexture > texture