diff --git a/composite.cpp b/composite.cpp index 99960b4af6..2fbf3e269c 100644 --- a/composite.cpp +++ b/composite.cpp @@ -319,6 +319,8 @@ void Compositor::setupX11Support() return; } claimCompositorSelection(); + xcb_composite_redirect_subwindows(con, kwinApp()->x11RootWindow(), + XCB_COMPOSITE_REDIRECT_MANUAL); } void Compositor::startupWithWorkspace() @@ -427,6 +429,10 @@ void Compositor::stop() for (InternalClient *client : workspace()->internalClients()) { client->finishCompositing(); } + if (auto *con = kwinApp()->x11Connection()) { + xcb_composite_unredirect_subwindows(con, kwinApp()->x11RootWindow(), + XCB_COMPOSITE_REDIRECT_MANUAL); + } while (!workspace()->deletedList().isEmpty()) { workspace()->deletedList().first()->discard(); } diff --git a/decorations/decorationrenderer.cpp b/decorations/decorationrenderer.cpp index ef753f27c4..3efbceba30 100644 --- a/decorations/decorationrenderer.cpp +++ b/decorations/decorationrenderer.cpp @@ -74,15 +74,10 @@ QImage Renderer::renderToImage(const QRect &geo) p.setRenderHint(QPainter::Antialiasing); p.setWindow(QRect(geo.topLeft(), geo.size() * dpr)); p.setClipRect(geo); - renderToPainter(&p, geo); + client()->decoration()->paint(&p, geo); return image; } -void Renderer::renderToPainter(QPainter *painter, const QRect &rect) -{ - client()->decoration()->paint(painter, rect); -} - void Renderer::reparent(Deleted *deleted) { setParent(deleted); diff --git a/decorations/decorationrenderer.h b/decorations/decorationrenderer.h index 86651f403d..ac3aa7e83f 100644 --- a/decorations/decorationrenderer.h +++ b/decorations/decorationrenderer.h @@ -73,7 +73,6 @@ protected: m_imageSizesDirty = false; } QImage renderToImage(const QRect &geo); - void renderToPainter(QPainter *painter, const QRect &rect); private: DecoratedClientImpl *m_client; diff --git a/deleted.cpp b/deleted.cpp index 76c3bccd6f..2dbced2739 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -96,6 +95,7 @@ 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(); @@ -171,6 +171,11 @@ QRect Deleted::bufferGeometry() const return m_bufferGeometry; } +QMargins Deleted::bufferMargins() const +{ + return m_bufferMargins; +} + QMargins Deleted::frameMargins() const { return m_frameMargins; diff --git a/deleted.h b/deleted.h index d69e497664..bb7fe2f94c 100644 --- a/deleted.h +++ b/deleted.h @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,6 +44,7 @@ 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; @@ -202,6 +202,7 @@ private: void removeTransientFor(Deleted *parent); QRect m_bufferGeometry; + QMargins m_bufferMargins; QMargins m_frameMargins; int delete_refcount; diff --git a/effects.cpp b/effects.cpp index f9a604867a..a64099dd0c 100644 --- a/effects.cpp +++ b/effects.cpp @@ -4,7 +4,6 @@ Copyright (C) 2006 Lubos Lunak Copyright (C) 2010, 2011 Martin Gräßlin -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1952,8 +1951,8 @@ void EffectWindowImpl::setSceneWindow(Scene::Window* w) QRegion EffectWindowImpl::shape() const { - if (sw) { - return sw->decorationShape() | sw->bufferShape().translated(toplevel->clientPos()); + if (isX11Client() && sceneWindow()) { + return sceneWindow()->bufferShape(); } return geometry(); } diff --git a/events.cpp b/events.cpp index a3658de56d..cb30d74020 100644 --- a/events.cpp +++ b/events.cpp @@ -581,7 +581,7 @@ bool X11Client::windowEvent(xcb_generic_event_t *e) detectShape(window()); // workaround for #19644 updateShape(); } - if (eventType == Xcb::Extensions::self()->damageNotifyEvent() && reinterpret_cast(e)->drawable == windowId()) + if (eventType == Xcb::Extensions::self()->damageNotifyEvent() && reinterpret_cast(e)->drawable == frameId()) damageNotifyEvent(); break; } diff --git a/geometry.cpp b/geometry.cpp index ad84c04b40..a106e93a27 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1946,7 +1946,7 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for // for example using X11Client::clientSize() QRect frameGeometry(x, y, w, h); - QRect serverGeometry; + QRect bufferGeometry; if (shade_geometry_change) ; // nothing @@ -1961,18 +1961,18 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for m_clientGeometry = frameRectToClientRect(frameGeometry); } if (isDecorated()) { - serverGeometry = frameGeometry; + bufferGeometry = frameGeometry; } else { - serverGeometry = m_clientGeometry; + bufferGeometry = m_clientGeometry; } - geom = frameGeometry; if (!areGeometryUpdatesBlocked() && frameGeometry != rules()->checkGeometry(frameGeometry)) { qCDebug(KWIN_CORE) << "forced geometry fail:" << frameGeometry << ":" << rules()->checkGeometry(frameGeometry); } - if (force == NormalGeometrySet && m_serverGeometry == serverGeometry && pendingGeometryUpdate() == PendingGeometryNone) { + if (!canUpdateGeometry(frameGeometry, bufferGeometry, force)) { return; } - m_serverGeometry = serverGeometry; + m_bufferGeometry = bufferGeometry; + geom = frameGeometry; if (areGeometryUpdatesBlocked()) { if (pendingGeometryUpdate() == PendingGeometryForced) {} // maximum, nothing needed @@ -1982,88 +1982,11 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for setPendingGeometryUpdate(PendingGeometryNormal); return; } - updateServerGeometry(); - updateWindowRules(Rules::Position|Rules::Size); - - // keep track of old maximize mode - // to detect changes - screens()->setCurrent(this); - workspace()->updateStackingOrder(); - - // Need to regenerate decoration pixmaps when the buffer size is changed. - if (bufferGeometryBeforeUpdateBlocking().size() != bufferGeometry().size()) { - discardWindowPixmap(); - } - emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); - addRepaintDuringGeometryUpdates(); - updateGeometryBeforeUpdateBlocking(); - // TODO: this signal is emitted too often - emit geometryChanged(); -} - -void X11Client::plainResize(int w, int h, ForceGeometry_t force) -{ - QSize frameSize(w, h); - QSize serverSize; - - // this code is also duplicated in X11Client::setGeometry(), and it's also commented there - if (shade_geometry_change) - ; // nothing - else if (isShade()) { - if (frameSize.height() == borderTop() + borderBottom()) { - qCDebug(KWIN_CORE) << "Shaded geometry passed for size:"; - } else { - m_clientGeometry.setSize(frameSizeToClientSize(frameSize)); - frameSize.setHeight(borderTop() + borderBottom()); - } - } else { - m_clientGeometry.setSize(frameSizeToClientSize(frameSize)); - } - if (isDecorated()) { - serverSize = frameSize; - } else { - serverSize = m_clientGeometry.size(); - } - if (!areGeometryUpdatesBlocked() && frameSize != rules()->checkSize(frameSize)) { - qCDebug(KWIN_CORE) << "forced size fail:" << frameSize << ":" << rules()->checkSize(frameSize); - } - geom.setSize(frameSize); - // resuming geometry updates is handled only in setGeometry() - Q_ASSERT(pendingGeometryUpdate() == PendingGeometryNone || areGeometryUpdatesBlocked()); - if (force == NormalGeometrySet && m_serverGeometry.size() == serverSize) { - return; - } - m_serverGeometry.setSize(serverSize); - if (areGeometryUpdatesBlocked()) { - if (pendingGeometryUpdate() == PendingGeometryForced) - {} // maximum, nothing needed - else if (force == ForceGeometrySet) - setPendingGeometryUpdate(PendingGeometryForced); - else - setPendingGeometryUpdate(PendingGeometryNormal); - return; - } - updateServerGeometry(); - updateWindowRules(Rules::Position|Rules::Size); - screens()->setCurrent(this); - workspace()->updateStackingOrder(); - if (bufferGeometryBeforeUpdateBlocking().size() != bufferGeometry().size()) { - discardWindowPixmap(); - } - emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); - addRepaintDuringGeometryUpdates(); - updateGeometryBeforeUpdateBlocking(); - // TODO: this signal is emitted too often - emit geometryChanged(); -} - -void X11Client::updateServerGeometry() -{ - const QRect previousServerGeometry = m_frame.geometry(); - const bool resized = (previousServerGeometry.size() != m_serverGeometry.size() || pendingGeometryUpdate() == PendingGeometryForced); + const QRect oldBufferGeometry = bufferGeometryBeforeUpdateBlocking(); + bool resized = (oldBufferGeometry.size() != m_bufferGeometry.size() || pendingGeometryUpdate() == PendingGeometryForced); if (resized) { resizeDecoration(); - m_frame.setGeometry(m_serverGeometry); + m_frame.setGeometry(m_bufferGeometry); if (!isShade()) { QSize cs = clientSize(); m_wrapper.setGeometry(QRect(clientPos(), cs)); @@ -2079,15 +2002,96 @@ void X11Client::updateServerGeometry() if (compositing()) // Defer the X update until we leave this mode needsXWindowMove = true; else - m_frame.move(m_serverGeometry.topLeft()); // sendSyntheticConfigureNotify() on finish shall be sufficient + m_frame.move(m_bufferGeometry.topLeft()); // sendSyntheticConfigureNotify() on finish shall be sufficient } else { - m_frame.move(m_serverGeometry.topLeft()); + m_frame.move(m_bufferGeometry.topLeft()); sendSyntheticConfigureNotify(); } // Unconditionally move the input window: it won't affect rendering - m_decoInputExtent.move(pos() + inputPos()); + m_decoInputExtent.move(QPoint(x, y) + inputPos()); } + updateWindowRules(Rules::Position|Rules::Size); + + // keep track of old maximize mode + // to detect changes + screens()->setCurrent(this); + workspace()->updateStackingOrder(); + + // Need to regenerate decoration pixmaps when the buffer size is changed. + if (oldBufferGeometry.size() != m_bufferGeometry.size()) { + discardWindowPixmap(); + } + emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); + addRepaintDuringGeometryUpdates(); + updateGeometryBeforeUpdateBlocking(); + // TODO: this signal is emitted too often + emit geometryChanged(); +} + +void X11Client::plainResize(int w, int h, ForceGeometry_t force) +{ + QSize frameSize(w, h); + QSize bufferSize; + + // this code is also duplicated in X11Client::setGeometry(), and it's also commented there + if (shade_geometry_change) + ; // nothing + else if (isShade()) { + if (frameSize.height() == borderTop() + borderBottom()) { + qCDebug(KWIN_CORE) << "Shaded geometry passed for size:"; + } else { + m_clientGeometry.setSize(frameSizeToClientSize(frameSize)); + frameSize.setHeight(borderTop() + borderBottom()); + } + } else { + m_clientGeometry.setSize(frameSizeToClientSize(frameSize)); + } + if (isDecorated()) { + bufferSize = frameSize; + } else { + bufferSize = m_clientGeometry.size(); + } + if (!areGeometryUpdatesBlocked() && frameSize != rules()->checkSize(frameSize)) { + qCDebug(KWIN_CORE) << "forced size fail:" << frameSize << ":" << rules()->checkSize(frameSize); + } + // resuming geometry updates is handled only in setGeometry() + Q_ASSERT(pendingGeometryUpdate() == PendingGeometryNone || areGeometryUpdatesBlocked()); + if (!canUpdateSize(frameSize, bufferSize, force)) { + return; + } + m_bufferGeometry.setSize(bufferSize); + geom.setSize(frameSize); + if (areGeometryUpdatesBlocked()) { + if (pendingGeometryUpdate() == PendingGeometryForced) + {} // maximum, nothing needed + else if (force == ForceGeometrySet) + setPendingGeometryUpdate(PendingGeometryForced); + else + setPendingGeometryUpdate(PendingGeometryNormal); + return; + } + resizeDecoration(); + m_frame.resize(m_bufferGeometry.size()); + if (!isShade()) { + QSize cs = clientSize(); + m_wrapper.setGeometry(QRect(clientPos(), cs)); + m_client.setGeometry(0, 0, cs.width(), cs.height()); + } + updateShape(); + + sendSyntheticConfigureNotify(); + updateWindowRules(Rules::Position|Rules::Size); + screens()->setCurrent(this); + workspace()->updateStackingOrder(); + if (bufferGeometryBeforeUpdateBlocking().size() != m_bufferGeometry.size()) { + discardWindowPixmap(); + } + emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); + addRepaintDuringGeometryUpdates(); + updateGeometryBeforeUpdateBlocking(); + // TODO: this signal is emitted too often + emit geometryChanged(); } /** @@ -2669,7 +2673,7 @@ void X11Client::leaveMoveResize() { if (needsXWindowMove) { // Do the deferred move - m_frame.move(m_serverGeometry.topLeft()); + m_frame.move(m_bufferGeometry.topLeft()); needsXWindowMove = false; } if (!isResize()) diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp index fee2a11e06..1953ab4b38 100644 --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -4,7 +4,6 @@ Copyright (C) 2006 Lubos Lunak Copyright (C) 2009, 2010, 2011 Martin Gräßlin -Copyright (C) 2019 Vlad Zahorodnii Based on glcompmgr code by Felix Bellaby. Using code from Compiz and Beryl. @@ -1434,6 +1433,7 @@ void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData if (data.crossFadeProgress() != 1.0) { OpenGLWindowPixmap *previous = previousWindowPixmap(); if (previous) { + const QRect &oldGeometry = previous->contentsRect(); for (const WindowQuad &quad : quads[ContentLeaf]) { // we need to create new window quads with normalize texture coordinates // normal quads divide the x/y position by width/height. This would not work as the texture @@ -1442,7 +1442,12 @@ void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData // the previous Client's content space. WindowQuad newQuad(WindowQuadContents); for (int i = 0; i < 4; ++i) { - newQuad[i] = WindowVertex(quad[i].x(), quad[i].y(), quad[i].u(), quad[i].v()); + const qreal xFactor = qreal(quad[i].textureX() - toplevel->clientPos().x())/qreal(toplevel->clientSize().width()); + const qreal yFactor = qreal(quad[i].textureY() - toplevel->clientPos().y())/qreal(toplevel->clientSize().height()); + WindowVertex vertex(quad[i].x(), quad[i].y(), + (xFactor * oldGeometry.width() + oldGeometry.x())/qreal(previous->size().width()), + (yFactor * oldGeometry.height() + oldGeometry.y())/qreal(previous->size().height())); + newQuad[i] = vertex; } quads[PreviousContentLeaf].append(newQuad); } @@ -2519,52 +2524,6 @@ static QImage rotate(const QImage &srcImage, const QRect &srcRect) return image; } -static void clamp_row(int left, int width, int right, const uint32_t *src, uint32_t *dest) -{ - std::fill_n(dest, left, *src); - std::copy(src, src + width, dest + left); - std::fill_n(dest + left + width, right, *(src + width - 1)); -} - -static void clamp_sides(int left, int width, int right, const uint32_t *src, uint32_t *dest) -{ - std::fill_n(dest, left, *src); - std::fill_n(dest + left + width, right, *(src + width - 1)); -} - -static void clamp(QImage &image, const QRect &viewport) -{ - Q_ASSERT(image.depth() == 32); - - const QRect rect = image.rect(); - - const int left = viewport.left() - rect.left(); - const int top = viewport.top() - rect.top(); - const int right = rect.right() - viewport.right(); - const int bottom = rect.bottom() - viewport.bottom(); - - const int width = rect.width() - left - right; - const int height = rect.height() - top - bottom; - - const uint32_t *firstRow = reinterpret_cast(image.scanLine(top)); - const uint32_t *lastRow = reinterpret_cast(image.scanLine(top + height - 1)); - - for (int i = 0; i < top; ++i) { - uint32_t *dest = reinterpret_cast(image.scanLine(i)); - clamp_row(left, width, right, firstRow + left, dest); - } - - for (int i = 0; i < height; ++i) { - uint32_t *dest = reinterpret_cast(image.scanLine(top + i)); - clamp_sides(left, width, right, dest + left, dest); - } - - for (int i = 0; i < bottom; ++i) { - uint32_t *dest = reinterpret_cast(image.scanLine(top + height + i)); - clamp_row(left, width, right, lastRow + left, dest); - } -} - void SceneOpenGLDecorationRenderer::render() { const QRegion scheduled = getScheduled(); @@ -2587,68 +2546,21 @@ void SceneOpenGLDecorationRenderer::render() const QRect geometry = dirty ? QRect(QPoint(0, 0), client()->client()->size()) : scheduled.boundingRect(); - // We pad each part in the decoration atlas in order to avoid texture bleeding. - const int padding = 1; - - auto renderPart = [=](const QRect &geo, const QRect &partRect, const QPoint &position, bool rotated = false) { + auto renderPart = [this](const QRect &geo, const QRect &partRect, const QPoint &offset, bool rotated = false) { if (!geo.isValid()) { return; } - - QRect rect = geo; - - // We allow partial decoration updates and it might just so happen that the dirty region - // is completely contained inside the decoration part, i.e. the dirty region doesn't touch - // any of the decoration's edges. In that case, we should **not** pad the dirty region. - if (rect.left() == partRect.left()) { - rect.setLeft(rect.left() - padding); - } - if (rect.top() == partRect.top()) { - rect.setTop(rect.top() - padding); - } - if (rect.right() == partRect.right()) { - rect.setRight(rect.right() + padding); - } - if (rect.bottom() == partRect.bottom()) { - rect.setBottom(rect.bottom() + padding); - } - - QRect viewport = geo.translated(-rect.x(), -rect.y()); - const qreal devicePixelRatio = client()->client()->screenScale(); - - QImage image(rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); - image.setDevicePixelRatio(devicePixelRatio); - image.fill(Qt::transparent); - - QPainter painter(&image); - painter.setRenderHint(QPainter::Antialiasing); - painter.setViewport(QRect(viewport.topLeft(), viewport.size() * devicePixelRatio)); - painter.setWindow(QRect(geo.topLeft(), geo.size() * devicePixelRatio)); - painter.setClipRect(geo); - renderToPainter(&painter, geo); - painter.end(); - - clamp(image, QRect(viewport.topLeft() * devicePixelRatio, viewport.size() * devicePixelRatio)); - + QImage image = renderToImage(geo); if (rotated) { // TODO: get this done directly when rendering to the image - image = rotate(image, QRect(QPoint(), rect.size())); - viewport = QRect(viewport.y(), viewport.x(), viewport.height(), viewport.width()); + image = rotate(image, QRect(geo.topLeft() - partRect.topLeft(), geo.size())); } - - const QPoint dirtyOffset = geo.topLeft() - partRect.topLeft(); - m_texture->update(image, (position + dirtyOffset - viewport.topLeft()) * image.devicePixelRatio()); + m_texture->update(image, (geo.topLeft() - partRect.topLeft() + offset) * image.devicePixelRatio()); }; - - const QPoint topPosition(padding, padding); - const QPoint bottomPosition(padding, topPosition.y() + top.height() + 2 * padding); - const QPoint leftPosition(padding, bottomPosition.y() + bottom.height() + 2 * padding); - const QPoint rightPosition(padding, leftPosition.y() + left.width() + 2 * padding); - - renderPart(left.intersected(geometry), left, leftPosition, true); - renderPart(top.intersected(geometry), top, topPosition); - renderPart(right.intersected(geometry), right, rightPosition, true); - renderPart(bottom.intersected(geometry), bottom, bottomPosition); + renderPart(left.intersected(geometry), left, QPoint(0, top.height() + bottom.height() + 2), true); + renderPart(top.intersected(geometry), top, QPoint(0, 0)); + renderPart(right.intersected(geometry), right, QPoint(0, top.height() + bottom.height() + left.width() + 3), true); + renderPart(bottom.intersected(geometry), bottom, QPoint(0, top.height() + 1)); } static int align(int value, int align) @@ -2665,12 +2577,7 @@ void SceneOpenGLDecorationRenderer::resizeTexture() size.rwidth() = qMax(qMax(top.width(), bottom.width()), qMax(left.height(), right.height())); size.rheight() = top.height() + bottom.height() + - left.width() + right.width(); - - // Reserve some space for padding. We pad decoration parts to avoid texture bleeding. - const int padding = 1; - size.rwidth() += 2 * padding; - size.rheight() += 4 * 2 * padding; + left.width() + right.width() + 3; size.rwidth() = align(size.width(), 128); diff --git a/scene.cpp b/scene.cpp index b9fb7aa755..b466046b54 100644 --- a/scene.cpp +++ b/scene.cpp @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -736,7 +735,7 @@ QRegion Scene::Window::bufferShape() const const QRect bufferGeometry = toplevel->bufferGeometry(); if (toplevel->shape()) { - auto cookie = xcb_shape_get_rectangles_unchecked(connection(), toplevel->windowId(), XCB_SHAPE_SK_BOUNDING); + 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(); @@ -766,7 +765,15 @@ QRegion Scene::Window::clientShape() const return QRegion(); } } - return bufferShape(); + + 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 @@ -880,18 +887,11 @@ WindowQuadList Scene::Window::makeDecorationQuads(const QRect *rects, const QReg { WindowQuadList list; - const int padding = 1; - - const QPoint topSpritePosition(padding, padding); - const QPoint bottomSpritePosition(padding, topSpritePosition.y() + rects[1].height() + 2 * padding); - const QPoint leftSpritePosition(bottomSpritePosition.y() + rects[3].height() + 2 * padding, padding); - const QPoint rightSpritePosition(leftSpritePosition.x() + rects[0].width() + 2 * padding, padding); - const QPoint offsets[4] = { - QPoint(-rects[0].x(), -rects[0].y()) + leftSpritePosition, - QPoint(-rects[1].x(), -rects[1].y()) + topSpritePosition, - QPoint(-rects[2].x(), -rects[2].y()) + rightSpritePosition, - QPoint(-rects[3].x(), -rects[3].y()) + bottomSpritePosition, + QPoint(-rects[0].x() + rects[1].height() + rects[3].height() + 2, -rects[0].y()), // Left + QPoint(-rects[1].x(), -rects[1].y()), // Top + QPoint(-rects[2].x() + rects[1].height() + rects[3].height() + rects[0].width() + 3, -rects[2].y()), // Right + QPoint(-rects[3].x(), -rects[3].y() + rects[1].height() + 1) // Bottom }; const Qt::Orientation orientations[4] = { @@ -1039,9 +1039,9 @@ void WindowPixmap::create() } XServerGrabber grabber; xcb_pixmap_t pix = xcb_generate_id(connection()); - xcb_void_cookie_t namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection(), toplevel()->windowId(), pix); - Xcb::WindowAttributes windowAttributes(toplevel()->windowId()); - Xcb::WindowGeometry windowGeometry(toplevel()->windowId()); + xcb_void_cookie_t namePixmapCookie = xcb_composite_name_window_pixmap_checked(connection(), toplevel()->frameId(), pix); + Xcb::WindowAttributes windowAttributes(toplevel()->frameId()); + Xcb::WindowGeometry windowGeometry(toplevel()->frameId()); if (xcb_generic_error_t *error = xcb_request_check(connection(), namePixmapCookie)) { qCDebug(KWIN_CORE) << "Creating window pixmap failed: " << error->error_code; free(error); @@ -1062,6 +1062,7 @@ void WindowPixmap::create() } m_pixmap = pix; m_pixmapSize = bufferGeometry.size(); + m_contentsRect = QRect(toplevel()->clientPos(), toplevel()->clientSize()); m_window->unreferencePreviousPixmap(); } diff --git a/scene.h b/scene.h index 7444cdafca..7e7aee5847 100644 --- a/scene.h +++ b/scene.h @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -449,6 +448,12 @@ public: * The size of the pixmap. */ const QSize &size() const; + /** + * The geometry of the Client's content inside the pixmap. In case of a decorated Client the + * pixmap also contains the decoration which is not rendered into this pixmap, though. This + * contentsRect tells where inside the complete pixmap the real content is. + */ + const QRect &contentsRect() const; /** * @brief Returns the Toplevel this WindowPixmap belongs to. * Note: the Toplevel can change over the lifetime of the WindowPixmap in case the Toplevel is copied to Deleted. @@ -508,6 +513,7 @@ private: xcb_pixmap_t m_pixmap; QSize m_pixmapSize; bool m_discarded; + QRect m_contentsRect; QPointer m_buffer; QSharedPointer m_fbo; QImage m_internalImage; @@ -673,6 +679,12 @@ void WindowPixmap::markAsDiscarded() m_window->referencePreviousPixmap(); } +inline +const QRect &WindowPixmap::contentsRect() const +{ + return m_contentsRect; +} + inline const QSize &WindowPixmap::size() const { diff --git a/toplevel.cpp b/toplevel.cpp index 5666c8b3b4..3a11f30bd7 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -257,13 +256,12 @@ bool Toplevel::setupCompositing() if (damage_handle != XCB_NONE) return false; - if (kwinApp()->operationMode() == Application::OperationModeX11) { + if (kwinApp()->operationMode() == Application::OperationModeX11 && !surface()) { damage_handle = xcb_generate_id(connection()); - xcb_damage_create(connection(), damage_handle, windowId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); - xcb_composite_redirect_window(connection(), windowId(), XCB_COMPOSITE_REDIRECT_MANUAL); + xcb_damage_create(connection(), damage_handle, frameId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); } - damage_region = QRegion(QRect(QPoint(), bufferGeometry().size())); + damage_region = QRegion(0, 0, width(), height()); effect_window = new EffectWindowImpl(this); Compositor::self()->scene()->addToplevel(this); @@ -280,10 +278,6 @@ void Toplevel::finishCompositing(ReleaseReason releaseReason) delete effect_window; } - if (kwinApp()->operationMode() == Application::OperationModeX11) { - xcb_composite_unredirect_window(connection(), windowId(), XCB_COMPOSITE_REDIRECT_MANUAL); - } - if (damage_handle != XCB_NONE && releaseReason != ReleaseReason::Destroyed) { xcb_damage_destroy(connection(), damage_handle); @@ -797,6 +791,11 @@ bool Toplevel::isLocalhost() const return m_clientMachine->isLocal(); } +QMargins Toplevel::bufferMargins() const +{ + return QMargins(); +} + QMargins Toplevel::frameMargins() const { return QMargins(); diff --git a/toplevel.h b/toplevel.h index 3aa4960210..a99556108d 100644 --- a/toplevel.h +++ b/toplevel.h @@ -3,7 +3,6 @@ This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -316,6 +315,16 @@ 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. diff --git a/x11client.cpp b/x11client.cpp index d19967ced4..76a504c75e 100644 --- a/x11client.cpp +++ b/x11client.cpp @@ -4,7 +4,6 @@ Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -256,7 +255,7 @@ void X11Client::releaseWindow(bool on_shutdown) m_client.deleteProperty(atoms->kde_net_wm_user_creation_time); m_client.deleteProperty(atoms->net_frame_extents); m_client.deleteProperty(atoms->kde_net_wm_frame_strut); - m_client.reparent(rootWindow(), m_serverGeometry.x(), m_serverGeometry.y()); + m_client.reparent(rootWindow(), m_bufferGeometry.x(), m_bufferGeometry.y()); xcb_change_save_set(c, XCB_SET_MODE_DELETE, m_client); m_client.selectInput(XCB_EVENT_MASK_NO_EVENT); if (on_shutdown) @@ -1986,7 +1985,12 @@ xcb_window_t X11Client::frameId() const QRect X11Client::bufferGeometry() const { - return m_clientGeometry; + return m_bufferGeometry; +} + +QMargins X11Client::bufferMargins() const +{ + return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom()); } QPoint X11Client::framePosToClientPos(const QPoint &point) const @@ -2212,21 +2216,64 @@ void X11Client::handleSync() addRepaintFull(); } +bool X11Client::canUpdatePosition(const QPoint &frame, const QPoint &buffer, ForceGeometry_t force) const +{ + // Obey forced geometry updates. + if (force != NormalGeometrySet) { + return true; + } + // Server-side geometry and our geometry are out of sync. + if (bufferGeometry().topLeft() != buffer) { + return true; + } + if (frameGeometry().topLeft() != frame) { + return true; + } + return false; +} + +bool X11Client::canUpdateSize(const QSize &frame, const QSize &buffer, ForceGeometry_t force) const +{ + // Obey forced geometry updates. + if (force != NormalGeometrySet) { + return true; + } + // Server-side geometry and our geometry are out of sync. + if (bufferGeometry().size() != buffer) { + return true; + } + if (frameGeometry().size() != frame) { + return true; + } + return false; +} + +bool X11Client::canUpdateGeometry(const QRect &frame, const QRect &buffer, ForceGeometry_t force) const +{ + if (canUpdatePosition(frame.topLeft(), buffer.topLeft(), force)) { + return true; + } + if (canUpdateSize(frame.size(), buffer.size(), force)) { + return true; + } + return pendingGeometryUpdate() != PendingGeometryNone; +} + void X11Client::move(int x, int y, ForceGeometry_t force) { const QPoint framePosition(x, y); m_clientGeometry.moveTopLeft(framePosToClientPos(framePosition)); - const QPoint serverPosition = isDecorated() ? framePosition : m_clientGeometry.topLeft(); + const QPoint bufferPosition = isDecorated() ? framePosition : m_clientGeometry.topLeft(); // resuming geometry updates is handled only in setGeometry() Q_ASSERT(pendingGeometryUpdate() == PendingGeometryNone || areGeometryUpdatesBlocked()); if (!areGeometryUpdatesBlocked() && framePosition != rules()->checkPosition(framePosition)) { qCDebug(KWIN_CORE) << "forced position fail:" << framePosition << ":" << rules()->checkPosition(framePosition); } - geom.moveTopLeft(framePosition); - if (force == NormalGeometrySet && m_serverGeometry.topLeft() == serverPosition) { + if (!canUpdatePosition(framePosition, bufferPosition, force)) { return; } - m_serverGeometry.moveTopLeft(serverPosition); + m_bufferGeometry.moveTopLeft(bufferPosition); + geom.moveTopLeft(framePosition); if (areGeometryUpdatesBlocked()) { if (pendingGeometryUpdate() == PendingGeometryForced) { // Maximum, nothing needed. @@ -2237,7 +2284,8 @@ void X11Client::move(int x, int y, ForceGeometry_t force) } return; } - updateServerGeometry(); + m_frame.move(m_bufferGeometry.topLeft()); + sendSyntheticConfigureNotify(); updateWindowRules(Rules::Position); screens()->setCurrent(this); workspace()->updateStackingOrder(); diff --git a/x11client.h b/x11client.h index df06e05d64..34bb1371d7 100644 --- a/x11client.h +++ b/x11client.h @@ -4,7 +4,6 @@ Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak -Copyright (C) 2019 Vlad Zahorodnii This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,25 +57,6 @@ enum class Predicate { InputIdMatch }; -/** - * The X11Client class represents a managed X11 client. - * - * Note that override-redirect clients are represented by instances of the Unmanaged class. - * - * Each X11 client has three geometries associated with it - frame, client, and server. The frame - * geometry is the most easiest one to understand, it specifies visible bounds of the window from - * the user's perspective. The frame geometry doesn't include server-side and client-side drop - * shadows. Window operations such resizing, snapping, tiling and so on must operate on this kind - * of geometry. The client geometry specifies a rectangle on the screen occupied by the client - * window. The server-side decoration, if any, must be put around the client geometry. The server - * geometry specifies the server-side geometry of the frame window. - * - * There is no strict order between the frame and the client geometry. Either one of them can be - * inside the other one. However, it's always guaranteed that both of them are inside the server - * geometry. - * - * The buffer geometry is an alias for the client geometry. - */ class KWIN_EXPORT X11Client : public AbstractClient { Q_OBJECT @@ -110,6 +90,7 @@ public: xcb_window_t frameId() const override; QRect bufferGeometry() const override; + QMargins bufferMargins() const override; QPoint framePosToClientPos(const QPoint &point) const override; QPoint clientPosToFramePos(const QPoint &point) const override; @@ -465,6 +446,9 @@ private: void destroyDecoration() override; void updateFrameExtents(); void setClientFrameExtents(const NETStrut &strut); + bool canUpdatePosition(const QPoint &frame, const QPoint &buffer, ForceGeometry_t force) const; + bool canUpdateSize(const QSize &frame, const QSize &buffer, ForceGeometry_t force) const; + bool canUpdateGeometry(const QRect &frame, const QRect &buffer, ForceGeometry_t force) const; void internalShow(); void internalHide(); @@ -472,7 +456,7 @@ private: void map(); void unmap(); void updateHiddenPreview(); - void updateServerGeometry(); + void updateInputShape(); xcb_timestamp_t readUserTimeMapTimestamp(const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, @@ -538,7 +522,7 @@ private: } m_fullscreenMode; MaximizeMode max_mode; - QRect m_serverGeometry = QRect(0, 0, 100, 100); + QRect m_bufferGeometry = QRect(0, 0, 100, 100); QRect m_clientGeometry = QRect(0, 0, 100, 100); QRect geom_restore; QRect geom_fs_restore;