diff --git a/abstract_client.h b/abstract_client.h index f1122a3e08..a80067e560 100644 --- a/abstract_client.h +++ b/abstract_client.h @@ -385,6 +385,7 @@ public: bool wantsTabFocus() const; + QMargins frameMargins() const override; QPoint clientPos() const override { return QPoint(borderLeft(), borderTop()); } @@ -835,14 +836,6 @@ public: */ virtual bool supportsWindowRules() const; - /** - * Returns the extents of the server-side decoration. - * - * Note that the returned margins object will have all margins set to 0 if - * the client doesn't have a server-side decoration. - */ - QMargins frameMargins() const; - public Q_SLOTS: virtual void closeWindow() = 0; diff --git a/deleted.cpp b/deleted.cpp index c03b7b3466..2dbced2739 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -95,6 +95,8 @@ void Deleted::copyToDeleted(Toplevel* c) Q_ASSERT(dynamic_cast< Deleted* >(c) == nullptr); Toplevel::copyToDeleted(c); m_bufferGeometry = c->bufferGeometry(); + m_bufferMargins = c->bufferMargins(); + m_frameMargins = c->frameMargins(); m_bufferScale = c->bufferScale(); desk = c->desktop(); m_desktops = c->desktops(); @@ -169,6 +171,16 @@ QRect Deleted::bufferGeometry() const return m_bufferGeometry; } +QMargins Deleted::bufferMargins() const +{ + return m_bufferMargins; +} + +QMargins Deleted::frameMargins() const +{ + return m_frameMargins; +} + qreal Deleted::bufferScale() const { return m_bufferScale; diff --git a/deleted.h b/deleted.h index 86ee54f409..4681d61ed8 100644 --- a/deleted.h +++ b/deleted.h @@ -44,6 +44,8 @@ public: void unrefWindow(); void discard(); QRect bufferGeometry() const override; + QMargins bufferMargins() const override; + QMargins frameMargins() const override; qreal bufferScale() const override; int desktop() const override; QStringList activities() const override; @@ -200,6 +202,8 @@ private: void removeTransientFor(Deleted *parent); QRect m_bufferGeometry; + QMargins m_bufferMargins; + QMargins m_frameMargins; int delete_refcount; int desk; diff --git a/effects.cpp b/effects.cpp index a559a5b3e8..b8a3c4312d 100644 --- a/effects.cpp +++ b/effects.cpp @@ -1951,7 +1951,10 @@ void EffectWindowImpl::setSceneWindow(Scene::Window* w) QRegion EffectWindowImpl::shape() const { - return sw ? sw->shape() : geometry(); + if (isX11Client() && sceneWindow()) { + return sceneWindow()->bufferShape(); + } + return geometry(); } QRect EffectWindowImpl::decorationInnerRect() const diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp index 1b0974d1e5..8693bdc2dc 100644 --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -1512,7 +1512,8 @@ void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData // render sub-surfaces auto wp = windowPixmap(); const auto &children = wp ? wp->children() : QVector(); - windowMatrix.translate(toplevel->clientPos().x(), toplevel->clientPos().y()); + const QPoint mainSurfaceOffset = bufferOffset(); + windowMatrix.translate(mainSurfaceOffset.x(), mainSurfaceOffset.y()); for (auto pixmap : children) { if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) { continue; diff --git a/plugins/scenes/qpainter/scene_qpainter.cpp b/plugins/scenes/qpainter/scene_qpainter.cpp index d0913135ca..3ae749df3a 100644 --- a/plugins/scenes/qpainter/scene_qpainter.cpp +++ b/plugins/scenes/qpainter/scene_qpainter.cpp @@ -287,14 +287,17 @@ void SceneQPainter::Window::performPaint(int mask, QRegion region, WindowPaintDa renderWindowDecorations(painter); // render content - const QRect target = QRect(toplevel->clientPos(), toplevel->clientSize()); - QSize srcSize = pixmap->image().size(); - if (pixmap->surface() && pixmap->surface()->scale() == 1 && srcSize != toplevel->clientSize()) { + QRect source; + QRect target; + if (toplevel->isClient()) { // special case for XWayland windows - srcSize = toplevel->clientSize(); + source = QRect(toplevel->clientPos(), toplevel->clientSize()); + target = source; + } else { + source = pixmap->image().rect(); + target = toplevel->bufferGeometry().translated(-pos()); } - const QRect src = QRect(toplevel->clientPos() + toplevel->clientContentPos(), srcSize); - painter->drawImage(target, pixmap->image(), src); + painter->drawImage(target, pixmap->image(), source); // render subsurfaces const auto &children = pixmap->children(); @@ -302,7 +305,7 @@ void SceneQPainter::Window::performPaint(int mask, QRegion region, WindowPaintDa if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) { continue; } - paintSubSurface(painter, toplevel->clientPos(), static_cast(pixmap)); + paintSubSurface(painter, bufferOffset(), static_cast(pixmap)); } if (!opaque) { diff --git a/plugins/scenes/xrender/scene_xrender.cpp b/plugins/scenes/xrender/scene_xrender.cpp index 506b8ec675..d9aaa1ecf5 100644 --- a/plugins/scenes/xrender/scene_xrender.cpp +++ b/plugins/scenes/xrender/scene_xrender.cpp @@ -462,10 +462,10 @@ void SceneXrender::Window::performPaint(int mask, QRegion region, WindowPaintDat if (toplevel->shape()) { // "xeyes" + decoration transformed_shape -= cr; - transformed_shape += shape(); + transformed_shape += bufferShape(); } } else { - transformed_shape = shape(); + transformed_shape = bufferShape(); } if (toplevel->shadow()) { transformed_shape |= toplevel->shadow()->shadowRegion(); diff --git a/scene.cpp b/scene.cpp index c8f449031e..2920a4a71d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -278,22 +278,14 @@ void Scene::paintSimpleScreen(int orig_mask, QRegion region) if (client) { opaqueFullscreen = client->isFullScreen(); } - X11Client *cc = dynamic_cast(client); - // the window is fully opaque - if (cc && cc->decorationHasAlpha()) { - // decoration uses alpha channel, so we may not exclude it in clipping - data.clip = window->clientShape().translated(window->x(), window->y()); - } else { - // decoration is fully opaque - if (client && client->isShade()) { - data.clip = QRegion(); - } else { - data.clip = window->shape().translated(window->x(), window->y()); - } + if (!(client && client->decorationHasAlpha())) { + data.clip = window->decorationShape().translated(window->pos()); } + data.clip |= window->clientShape().translated(window->pos() + window->bufferOffset()); } else if (toplevel->hasAlpha() && toplevel->opacity() == 1.0) { - // the window is partially opaque - data.clip = (window->clientShape() & toplevel->opaqueRegion().translated(toplevel->clientPos())).translated(window->x(), window->y()); + const QRegion clientShape = window->clientShape().translated(window->pos() + window->bufferOffset()); + const QRegion opaqueShape = toplevel->opaqueRegion().translated(window->pos() + toplevel->clientPos()); + data.clip = clientShape & opaqueShape; } else { data.clip = QRegion(); } @@ -687,7 +679,6 @@ Scene::Window::Window(Toplevel * c) , m_previousPixmap() , m_referencePixmapCounter(0) , disable_painting(0) - , shape_valid(false) , cached_quad_list(nullptr) { } @@ -731,47 +722,70 @@ void Scene::Window::discardShape() { // it is created on-demand and cached, simply // reset the flag - shape_valid = false; + m_bufferShapeIsValid = false; invalidateQuadsCache(); } -// Find out the shape of the window using the XShape extension -// or if shape is not set then simply it's the window geometry. -const QRegion &Scene::Window::shape() const +QRegion Scene::Window::bufferShape() const { - if (!shape_valid) { - if (toplevel->shape()) { - auto cookie = xcb_shape_get_rectangles_unchecked(connection(), toplevel->frameId(), XCB_SHAPE_SK_BOUNDING); - ScopedCPointer reply(xcb_shape_get_rectangles_reply(connection(), cookie, nullptr)); - if (!reply.isNull()) { - shape_region = QRegion(); - auto *rects = xcb_shape_get_rectangles_rectangles(reply.data()); - for (int i = 0; - i < xcb_shape_get_rectangles_rectangles_length(reply.data()); - ++i) - shape_region += QRegion(rects[ i ].x, rects[ i ].y, - rects[ i ].width, rects[ i ].height); - // make sure the shape is sane (X is async, maybe even XShape is broken) - shape_region &= QRegion(0, 0, width(), height()); - } else - shape_region = QRegion(); - } else - shape_region = QRegion(0, 0, width(), height()); - shape_valid = true; + if (m_bufferShapeIsValid) { + return m_bufferShape; } - return shape_region; + + const QRect bufferGeometry = toplevel->bufferGeometry(); + + if (toplevel->shape()) { + auto cookie = xcb_shape_get_rectangles_unchecked(connection(), toplevel->frameId(), XCB_SHAPE_SK_BOUNDING); + ScopedCPointer reply(xcb_shape_get_rectangles_reply(connection(), cookie, nullptr)); + if (!reply.isNull()) { + m_bufferShape = QRegion(); + const xcb_rectangle_t *rects = xcb_shape_get_rectangles_rectangles(reply.data()); + const int rectCount = xcb_shape_get_rectangles_rectangles_length(reply.data()); + for (int i = 0; i < rectCount; ++i) { + m_bufferShape += QRegion(rects[i].x, rects[i].y, rects[i].width, rects[i].height); + } + // make sure the shape is sane (X is async, maybe even XShape is broken) + m_bufferShape &= QRegion(0, 0, bufferGeometry.width(), bufferGeometry.height()); + } else { + m_bufferShape = QRegion(); + } + } else { + m_bufferShape = QRegion(0, 0, bufferGeometry.width(), bufferGeometry.height()); + } + + m_bufferShapeIsValid = true; + + return m_bufferShape; } QRegion Scene::Window::clientShape() const { - if (AbstractClient *c = dynamic_cast< AbstractClient * > (toplevel)) { - if (c->isShade()) + if (AbstractClient *client = qobject_cast(toplevel)) { + if (client->isShade()) { return QRegion(); + } } - // TODO: cache - const QRegion r = shape() & QRect(toplevel->clientPos(), toplevel->clientSize()); - return r.isEmpty() ? QRegion() : r; + const QRegion shape = bufferShape(); + const QMargins bufferMargins = toplevel->bufferMargins(); + if (bufferMargins.isNull()) { + return shape; + } + + const QRect clippingRect = QRect(QPoint(0, 0), toplevel->bufferGeometry().size()) - toplevel->bufferMargins(); + return shape & clippingRect; +} + +QRegion Scene::Window::decorationShape() const +{ + return QRegion(toplevel->decorationRect()) - toplevel->transparentRect(); +} + +QPoint Scene::Window::bufferOffset() const +{ + const QRect bufferGeometry = toplevel->bufferGeometry(); + const QRect frameGeometry = toplevel->frameGeometry(); + return bufferGeometry.topLeft() - frameGeometry.topLeft(); } bool Scene::Window::isVisible() const @@ -835,19 +849,14 @@ WindowQuadList Scene::Window::buildQuads(bool force) const { if (cached_quad_list != nullptr && !force) return *cached_quad_list; - WindowQuadList ret; - const qreal scale = toplevel->bufferScale(); + WindowQuadList ret = makeContentsQuads(); - if (toplevel->clientPos() == QPoint(0, 0) && toplevel->clientSize() == toplevel->decorationRect().size()) - ret = makeQuads(WindowQuadContents, shape(), QPoint(0,0), scale); // has no decoration - else { + if (!toplevel->frameMargins().isNull()) { AbstractClient *client = dynamic_cast(toplevel); - QRegion contents = clientShape(); QRegion center = toplevel->transparentRect(); - QRegion decoration = (client ? QRegion(client->decorationRect()) : shape()) - center; + const QRegion decoration = decorationShape(); qreal decorationScale = 1.0; - ret = makeQuads(WindowQuadContents, contents, toplevel->clientContentPos(), scale); QRect rects[4]; bool isShadedClient = false; @@ -932,32 +941,48 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg return list; } +WindowQuadList Scene::Window::makeContentsQuads() const +{ + const QRegion contentsRegion = clientShape(); + if (contentsRegion.isEmpty()) { + return WindowQuadList(); + } + + const QPointF geometryOffset = bufferOffset(); + const qreal textureScale = toplevel->bufferScale(); + + WindowQuadList quads; + quads.reserve(contentsRegion.rectCount()); + + for (const QRectF &rect : contentsRegion) { + WindowQuad quad(WindowQuadContents); + + const qreal x0 = rect.left() + geometryOffset.x(); + const qreal y0 = rect.top() + geometryOffset.y(); + const qreal x1 = rect.right() + geometryOffset.x(); + const qreal y1 = rect.bottom() + geometryOffset.y(); + + const qreal u0 = rect.left() * textureScale; + const qreal v0 = rect.top() * textureScale; + const qreal u1 = rect.right() * textureScale; + const qreal v1 = rect.bottom() * textureScale; + + quad[0] = WindowVertex(QPointF(x0, y0), QPointF(u0, v0)); + quad[1] = WindowVertex(QPointF(x1, y0), QPointF(u1, v0)); + quad[2] = WindowVertex(QPointF(x1, y1), QPointF(u1, v1)); + quad[3] = WindowVertex(QPointF(x0, y1), QPointF(u0, v1)); + + quads << quad; + } + + return quads; +} + void Scene::Window::invalidateQuadsCache() { cached_quad_list.reset(); } -WindowQuadList Scene::Window::makeQuads(WindowQuadType type, const QRegion& reg, const QPoint &textureOffset, qreal scale) const -{ - WindowQuadList ret; - ret.reserve(reg.rectCount()); - for (const QRect &r : reg) { - WindowQuad quad(type); - // TODO asi mam spatne pravy dolni roh - bud tady, nebo v jinych castech - quad[ 0 ] = WindowVertex(QPointF(r.x(), r.y()), - QPointF(r.x() + textureOffset.x(), r.y() + textureOffset.y()) * scale); - quad[ 1 ] = WindowVertex(QPointF(r.x() + r.width(), r.y()), - QPointF(r.x() + r.width() + textureOffset.x(), r.y() + textureOffset.y()) * scale); - quad[ 2 ] = WindowVertex(QPointF(r.x() + r.width(), r.y() + r.height()), - QPointF(r.x() + r.width() + textureOffset.x(), r.y() + r.height() + textureOffset.y()) * scale); - quad[ 3 ] = WindowVertex(QPointF(r.x(), r.y() + r.height()), - QPointF(r.x() + textureOffset.x(), r.y() + r.height() + textureOffset.y()) * scale); - - ret.append(quad); - } - return ret; -} - void Scene::Window::updateShadow(Shadow* shadow) { if (m_shadow == shadow) { @@ -1029,14 +1054,14 @@ void WindowPixmap::create() xcb_free_pixmap(connection(), pix); return; } - if (!windowGeometry || - windowGeometry->width != toplevel()->width() || windowGeometry->height != toplevel()->height()) { + const QRect bufferGeometry = toplevel()->bufferGeometry(); + if (windowGeometry.size() != bufferGeometry.size()) { qCDebug(KWIN_CORE) << "Creating window pixmap failed: " << this; xcb_free_pixmap(connection(), pix); return; } m_pixmap = pix; - m_pixmapSize = QSize(toplevel()->width(), toplevel()->height()); + m_pixmapSize = bufferGeometry.size(); m_contentsRect = QRect(toplevel()->clientPos(), toplevel()->clientSize()); m_window->unreferencePreviousPixmap(); } diff --git a/scene.h b/scene.h index 7e539193bc..2430970ac6 100644 --- a/scene.h +++ b/scene.h @@ -330,8 +330,10 @@ public: // is the window fully opaque bool isOpaque() const; // shape of the window - const QRegion &shape() const; + QRegion bufferShape() const; QRegion clientShape() const; + QRegion decorationShape() const; + QPoint bufferOffset() const; void discardShape(); void updateToplevel(Toplevel* c); // creates initial quad list for the window @@ -343,8 +345,8 @@ public: void unreferencePreviousPixmap(); void invalidateQuadsCache(); 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, qreal textureScale = 1.0) const; + WindowQuadList makeContentsQuads() const; /** * @brief Returns the WindowPixmap for this Window. * @@ -377,8 +379,8 @@ private: QScopedPointer m_previousPixmap; int m_referencePixmapCounter; int disable_painting; - mutable QRegion shape_region; - mutable bool shape_valid; + mutable QRegion m_bufferShape; + mutable bool m_bufferShapeIsValid = false; mutable QScopedPointer cached_quad_list; Q_DISABLE_COPY(Window) }; diff --git a/toplevel.cpp b/toplevel.cpp index 18000445d6..811b6b14ca 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -804,5 +804,15 @@ bool Toplevel::isLocalhost() const return m_clientMachine->isLocal(); } +QMargins Toplevel::bufferMargins() const +{ + return QMargins(); +} + +QMargins Toplevel::frameMargins() const +{ + return QMargins(); +} + } // namespace diff --git a/toplevel.h b/toplevel.h index 190b6c8cb7..772f5e30f1 100644 --- a/toplevel.h +++ b/toplevel.h @@ -314,11 +314,30 @@ public: * occupies on the screen, in global screen coordinates. */ virtual QRect bufferGeometry() const = 0; + /** + * Returns the extents of invisible portions in the pixmap. + * + * An X11 pixmap may contain invisible space around the actual contents of the + * client. That space is reserved for server-side decoration, which we usually + * want to skip when building contents window quads. + * + * Default implementation returns a margins object with all margins set to 0. + */ + virtual QMargins bufferMargins() const; /** * Returns the geometry of the Toplevel, excluding invisible portions, e.g. * server-side and client-side drop shadows, etc. */ QRect frameGeometry() const; + /** + * Returns the extents of the server-side decoration. + * + * Note that the returned margins object will have all margins set to 0 if + * the client doesn't have a server-side decoration. + * + * Default implementation returns a margins object with all margins set to 0. + */ + virtual QMargins frameMargins() const; /** * The geometry of the Toplevel which accepts input events. This might be larger * than the actual geometry, e.g. to support resizing outside the window. diff --git a/x11client.cpp b/x11client.cpp index dff94c6d29..f4ce2278b8 100644 --- a/x11client.cpp +++ b/x11client.cpp @@ -1976,6 +1976,11 @@ QRect X11Client::bufferGeometry() const return geom; } +QMargins X11Client::bufferMargins() const +{ + return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom()); +} + Xcb::Property X11Client::fetchShowOnScreenEdge() const { return Xcb::Property(false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1); diff --git a/x11client.h b/x11client.h index c2d7a6f8e6..3107e2d986 100644 --- a/x11client.h +++ b/x11client.h @@ -90,6 +90,7 @@ public: xcb_window_t frameId() const override; QRect bufferGeometry() const override; + QMargins bufferMargins() const override; bool isTransient() const override; bool groupTransient() const override; diff --git a/xcbutils.h b/xcbutils.h index 827d92498a..6c9c0879ae 100644 --- a/xcbutils.h +++ b/xcbutils.h @@ -566,6 +566,14 @@ public: } return QRect(geometry->x, geometry->y, geometry->width, geometry->height); } + + inline QSize size() { + const xcb_get_geometry_reply_t *geometry = data(); + if (!geometry) { + return QSize(); + } + return QSize(geometry->width, geometry->height); + } }; XCB_WRAPPER_DATA(TreeData, xcb_query_tree, xcb_window_t)