diff --git a/decorations/decorationrenderer.cpp b/decorations/decorationrenderer.cpp --- a/decorations/decorationrenderer.cpp +++ b/decorations/decorationrenderer.cpp @@ -21,6 +21,7 @@ #include "decoratedclient.h" #include "deleted.h" #include "abstract_client.h" +#include "screens.h" #include #include @@ -38,10 +39,10 @@ , m_client(client) , m_imageSizesDirty(true) { - auto markImageSizesDirty = [this]{ m_imageSizesDirty = true; }; - if (kwinApp()->operationMode() != Application::OperationModeX11) { - connect(client->client(), &AbstractClient::screenChanged, this, markImageSizesDirty); - } + auto markImageSizesDirty = [this]{ + m_imageSizesDirty = true; + }; + connect(client->client(), &AbstractClient::screenScaleChanged, this, markImageSizesDirty); connect(client->decoration(), &KDecoration2::Decoration::bordersChanged, this, markImageSizesDirty); connect(client->decoratedClient(), &KDecoration2::DecoratedClient::widthChanged, this, markImageSizesDirty); connect(client->decoratedClient(), &KDecoration2::DecoratedClient::heightChanged, this, markImageSizesDirty); @@ -65,11 +66,13 @@ QImage Renderer::renderToImage(const QRect &geo) { Q_ASSERT(m_client); - QImage image(geo.width(), geo.height(), QImage::Format_ARGB32_Premultiplied); + auto dpr = client()->client()->screenScale(); + QImage image(geo.width() * dpr, geo.height() * dpr, QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(dpr); image.fill(Qt::transparent); QPainter p(&image); p.setRenderHint(QPainter::Antialiasing); - p.setWindow(geo); + p.setWindow(QRect(geo.topLeft(), geo.size() * dpr)); p.setClipRect(geo); client()->decoration()->paint(&p, geo); return image; diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -2336,13 +2336,16 @@ // and flips it vertically static QImage rotate(const QImage &srcImage, const QRect &srcRect) { - QImage image(srcRect.height(), srcRect.width(), srcImage.format()); + auto dpr = srcImage.devicePixelRatio(); + QImage image(srcRect.height() * dpr, srcRect.width() * dpr, srcImage.format()); + image.setDevicePixelRatio(dpr); + const QPoint srcPoint(srcRect.x() * dpr, srcRect.y() * dpr); const uint32_t *src = reinterpret_cast(srcImage.bits()); uint32_t *dst = reinterpret_cast(image.bits()); for (int x = 0; x < image.width(); x++) { - const uint32_t *s = src + (srcRect.y() + x) * srcImage.width() + srcRect.x(); + const uint32_t *s = src + (srcPoint.y() + x) * srcImage.width() + srcPoint.x(); uint32_t *d = dst + x; for (int y = 0; y < image.height(); y++) { @@ -2357,10 +2360,10 @@ void SceneOpenGLDecorationRenderer::render() { const QRegion scheduled = getScheduled(); - if (scheduled.isEmpty()) { + const bool dirty = areImageSizesDirty(); + if (scheduled.isEmpty() && !dirty) { return; } - const bool dirty = areImageSizesDirty(); if (dirty) { resizeTexture(); resetImageSizesDirty(); @@ -2385,7 +2388,7 @@ // TODO: get this done directly when rendering to the image image = rotate(image, QRect(geo.topLeft() - partRect.topLeft(), geo.size())); } - m_texture->update(image, geo.topLeft() - partRect.topLeft() + offset); + m_texture->update(image, (geo.topLeft() - partRect.topLeft() + offset) * image.devicePixelRatio()); }; renderPart(left.intersected(geometry), left, QPoint(0, top.height() + bottom.height() + 2), true); renderPart(top.intersected(geometry), top, QPoint(0, 0)); @@ -2411,6 +2414,7 @@ size.rwidth() = align(size.width(), 128); + size *= client()->client()->screenScale(); if (m_texture && m_texture->size() == size) return; diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -632,7 +632,7 @@ client()->client()->layoutDecorationRects(left, top, right, bottom); auto checkAndCreate = [this](int index, const QSize &size) { - auto dpr = screens()->scale(client()->client()->screen()); + auto dpr = client()->client()->screenScale(); if (m_images[index].size() != size * dpr || m_images[index].devicePixelRatio() != dpr) { diff --git a/plugins/scenes/xrender/scene_xrender.cpp b/plugins/scenes/xrender/scene_xrender.cpp --- a/plugins/scenes/xrender/scene_xrender.cpp +++ b/plugins/scenes/xrender/scene_xrender.cpp @@ -1230,6 +1230,7 @@ return; } QImage image = renderToImage(geo); + Q_ASSERT(image.devicePixelRatio() == 1); xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, m_pixmaps[index], m_gc, image.width(), image.height(), geo.x() - offset.x(), geo.y() - offset.y(), 0, 32, image.byteCount(), image.constBits()); diff --git a/scene.h b/scene.h --- a/scene.h +++ b/scene.h @@ -338,7 +338,7 @@ void unreferencePreviousPixmap(); protected: WindowQuadList makeQuads(WindowQuadType type, const QRegion& reg, const QPoint &textureOffset = QPoint(0, 0), qreal textureScale = 1.0) const; - WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion) const; + WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion, qreal textureScale = 1.0) const; /** * @brief Returns the WindowPixmap for this Window. * diff --git a/scene.cpp b/scene.cpp --- a/scene.cpp +++ b/scene.cpp @@ -403,6 +403,7 @@ if (c->surface()) { connect(c->surface(), &KWayland::Server::SurfaceInterface::scaleChanged, this, std::bind(&Scene::windowGeometryShapeChanged, this, c)); } + connect(c, &Toplevel::screenScaleChanged, std::bind(&Scene::windowGeometryShapeChanged, this, c)); c->effectWindow()->setSceneWindow(w); c->getShadow(); w->updateShadow(c->shadow()); @@ -851,22 +852,23 @@ QRegion contents = clientShape(); QRegion center = toplevel->transparentRect(); QRegion decoration = (client ? QRegion(client->decorationRect()) : shape()) - center; + qreal decorationScale = 1.0; ret = makeQuads(WindowQuadContents, contents, toplevel->clientContentPos(), scale); - QRect rects[4]; bool isShadedClient = false; if (client) { client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]); + decorationScale = client->screenScale(); isShadedClient = client->isShade() || center.isEmpty(); } if (isShadedClient) { const QRect bounding = rects[0] | rects[1] | rects[2] | rects[3]; - ret += makeDecorationQuads(rects, bounding); + ret += makeDecorationQuads(rects, bounding, decorationScale); } else { - ret += makeDecorationQuads(rects, decoration); + ret += makeDecorationQuads(rects, decoration, decorationScale); } } @@ -878,7 +880,7 @@ return ret; } -WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion) const +WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QRegion ®ion, qreal textureScale) const { WindowQuadList list; @@ -908,10 +910,10 @@ const int x1 = r.x() + r.width(); const int y1 = r.y() + r.height(); - const int u0 = x0 + offsets[i].x(); - const int v0 = y0 + offsets[i].y(); - const int u1 = x1 + offsets[i].x(); - const int v1 = y1 + offsets[i].y(); + const int u0 = (x0 + offsets[i].x()) * textureScale; + const int v0 = (y0 + offsets[i].y()) * textureScale; + const int u1 = (x1 + offsets[i].x()) * textureScale; + const int v1 = (y1 + offsets[i].y()) * textureScale; WindowQuad quad(WindowQuadDecoration); quad.setUVAxisSwapped(swap); diff --git a/toplevel.h b/toplevel.h --- a/toplevel.h +++ b/toplevel.h @@ -236,6 +236,12 @@ bool isOnScreen(int screen) const; // true if it's at least partially there bool isOnActiveScreen() const; int screen() const; // the screen where the center is + /** + * The scale of the screen this window is currently on + * @Note: The buffer scale can be different. + * @since 5.12 + */ + qreal screenScale() const; // virtual QPoint clientPos() const = 0; // inside of geometry() /** * Describes how the client's content maps to the window geometry including the frame. @@ -490,6 +496,13 @@ **/ void surfaceChanged(); + /* + * Emitted when the client's screen changes onto a screen of a different scale + * or the screen we're on changes + * @since 5.12 + */ + void screenScaleChanged(); + protected Q_SLOTS: /** * Checks whether the screen number for this Toplevel changed and updates if needed. @@ -570,6 +583,7 @@ **/ QSharedPointer m_internalFBO; // when adding new data members, check also copyToDeleted() + qreal m_screenScale = 1.0; }; inline xcb_window_t Toplevel::window() const diff --git a/toplevel.cpp b/toplevel.cpp --- a/toplevel.cpp +++ b/toplevel.cpp @@ -281,12 +281,17 @@ m_screen = 0; emit screenChanged(); } - return; + } else { + const int s = screens()->number(geometry().center()); + if (s != m_screen) { + m_screen = s; + emit screenChanged(); + } } - const int s = screens()->number(geometry().center()); - if (s != m_screen) { - m_screen = s; - emit screenChanged(); + qreal newScale = screens()->scale(m_screen); + if (newScale != m_screenScale) { + m_screenScale = newScale; + emit screenScaleChanged(); } } @@ -308,6 +313,11 @@ return m_screen; } +qreal Toplevel::screenScale() const +{ + return m_screenScale; +} + bool Toplevel::isOnScreen(int screen) const { return screens()->geometry(screen).intersects(geometry());