From 754b72d155820a6c8a9ba94b2c0960da1b2f86ce Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 28 Nov 2019 11:46:06 +0200 Subject: [PATCH] [x11] Name client pixmap instead of frame pixmap Summary: Since KDE 4.2 - 4.3 times, KWin doesn't paint window decorations on real X11 windows, except when compositing is turned off. This leaves us with a problem. The actual client contents is inside a larger texture with no useful pixel data around it. This and decoration texture bleeding are the main factors that contribute to 1px gap between the server-side decoration and client contents with effects such as wobbly windows, and zoom. Another problem with naming frame pixmap instead of client pixmap is that it doesn't quite go along with wayland. It only makes more difficult to abstract window quad generation in the scene. Since we don't actually need the frame window when compositing is on, there is nothing that holds us from redirecting client windows instead of frame windows. This will help us to fix the texture bleeding issue and also help us with the ongoing redesign of the scene. Test Plan: X11 clients are still composited. Reviewers: #kwin, davidedmundson Reviewed By: #kwin, davidedmundson Subscribers: davidedmundson, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D25610 --- composite.cpp | 6 -- deleted.cpp | 7 +- deleted.h | 3 +- effects.cpp | 5 +- events.cpp | 2 +- geometry.cpp | 104 ++++++++++++------------- plugins/scenes/opengl/scene_opengl.cpp | 8 +- scene.cpp | 20 ++--- scene.h | 14 +--- toplevel.cpp | 17 ++-- toplevel.h | 11 +-- x11client.cpp | 64 ++------------- x11client.h | 28 +++++-- 13 files changed, 104 insertions(+), 185 deletions(-) diff --git a/composite.cpp b/composite.cpp index 2fbf3e269c..99960b4af6 100644 --- a/composite.cpp +++ b/composite.cpp @@ -319,8 +319,6 @@ void Compositor::setupX11Support() return; } claimCompositorSelection(); - xcb_composite_redirect_subwindows(con, kwinApp()->x11RootWindow(), - XCB_COMPOSITE_REDIRECT_MANUAL); } void Compositor::startupWithWorkspace() @@ -429,10 +427,6 @@ 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/deleted.cpp b/deleted.cpp index 2dbced2739..76c3bccd6f 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -3,6 +3,7 @@ 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 @@ -95,7 +96,6 @@ 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,11 +171,6 @@ 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 bb7fe2f94c..d69e497664 100644 --- a/deleted.h +++ b/deleted.h @@ -3,6 +3,7 @@ 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 @@ -44,7 +45,6 @@ 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,7 +202,6 @@ 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 a64099dd0c..f9a604867a 100644 --- a/effects.cpp +++ b/effects.cpp @@ -4,6 +4,7 @@ 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 @@ -1951,8 +1952,8 @@ void EffectWindowImpl::setSceneWindow(Scene::Window* w) QRegion EffectWindowImpl::shape() const { - if (isX11Client() && sceneWindow()) { - return sceneWindow()->bufferShape(); + if (sw) { + return sw->decorationShape() | sw->bufferShape().translated(toplevel->clientPos()); } return geometry(); } diff --git a/events.cpp b/events.cpp index cb30d74020..a3658de56d 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 == frameId()) + if (eventType == Xcb::Extensions::self()->damageNotifyEvent() && reinterpret_cast(e)->drawable == windowId()) damageNotifyEvent(); break; } diff --git a/geometry.cpp b/geometry.cpp index a106e93a27..ad84c04b40 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 bufferGeometry; + QRect serverGeometry; 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()) { - bufferGeometry = frameGeometry; + serverGeometry = frameGeometry; } else { - bufferGeometry = m_clientGeometry; + serverGeometry = m_clientGeometry; } + geom = frameGeometry; if (!areGeometryUpdatesBlocked() && frameGeometry != rules()->checkGeometry(frameGeometry)) { qCDebug(KWIN_CORE) << "forced geometry fail:" << frameGeometry << ":" << rules()->checkGeometry(frameGeometry); } - if (!canUpdateGeometry(frameGeometry, bufferGeometry, force)) { + if (force == NormalGeometrySet && m_serverGeometry == serverGeometry && pendingGeometryUpdate() == PendingGeometryNone) { return; } - m_bufferGeometry = bufferGeometry; - geom = frameGeometry; + m_serverGeometry = serverGeometry; if (areGeometryUpdatesBlocked()) { if (pendingGeometryUpdate() == PendingGeometryForced) {} // maximum, nothing needed @@ -1982,35 +1982,7 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for setPendingGeometryUpdate(PendingGeometryNormal); return; } - const QRect oldBufferGeometry = bufferGeometryBeforeUpdateBlocking(); - bool resized = (oldBufferGeometry.size() != m_bufferGeometry.size() || pendingGeometryUpdate() == PendingGeometryForced); - if (resized) { - resizeDecoration(); - m_frame.setGeometry(m_bufferGeometry); - if (!isShade()) { - QSize cs = clientSize(); - m_wrapper.setGeometry(QRect(clientPos(), cs)); - if (!isResize() || syncRequest.counter == XCB_NONE) - m_client.setGeometry(0, 0, cs.width(), cs.height()); - // SELI - won't this be too expensive? - // THOMAS - yes, but gtk+ clients will not resize without ... - sendSyntheticConfigureNotify(); - } - updateShape(); - } else { - if (isMoveResize()) { - if (compositing()) // Defer the X update until we leave this mode - needsXWindowMove = true; - else - m_frame.move(m_bufferGeometry.topLeft()); // sendSyntheticConfigureNotify() on finish shall be sufficient - } else { - m_frame.move(m_bufferGeometry.topLeft()); - sendSyntheticConfigureNotify(); - } - - // Unconditionally move the input window: it won't affect rendering - m_decoInputExtent.move(QPoint(x, y) + inputPos()); - } + updateServerGeometry(); updateWindowRules(Rules::Position|Rules::Size); // keep track of old maximize mode @@ -2019,7 +1991,7 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for workspace()->updateStackingOrder(); // Need to regenerate decoration pixmaps when the buffer size is changed. - if (oldBufferGeometry.size() != m_bufferGeometry.size()) { + if (bufferGeometryBeforeUpdateBlocking().size() != bufferGeometry().size()) { discardWindowPixmap(); } emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); @@ -2032,7 +2004,7 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for void X11Client::plainResize(int w, int h, ForceGeometry_t force) { QSize frameSize(w, h); - QSize bufferSize; + QSize serverSize; // this code is also duplicated in X11Client::setGeometry(), and it's also commented there if (shade_geometry_change) @@ -2048,20 +2020,20 @@ void X11Client::plainResize(int w, int h, ForceGeometry_t force) m_clientGeometry.setSize(frameSizeToClientSize(frameSize)); } if (isDecorated()) { - bufferSize = frameSize; + serverSize = frameSize; } else { - bufferSize = m_clientGeometry.size(); + 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 (!canUpdateSize(frameSize, bufferSize, force)) { + if (force == NormalGeometrySet && m_serverGeometry.size() == serverSize) { return; } - m_bufferGeometry.setSize(bufferSize); - geom.setSize(frameSize); + m_serverGeometry.setSize(serverSize); if (areGeometryUpdatesBlocked()) { if (pendingGeometryUpdate() == PendingGeometryForced) {} // maximum, nothing needed @@ -2071,20 +2043,11 @@ void X11Client::plainResize(int w, int h, ForceGeometry_t force) 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(); + updateServerGeometry(); updateWindowRules(Rules::Position|Rules::Size); screens()->setCurrent(this); workspace()->updateStackingOrder(); - if (bufferGeometryBeforeUpdateBlocking().size() != m_bufferGeometry.size()) { + if (bufferGeometryBeforeUpdateBlocking().size() != bufferGeometry().size()) { discardWindowPixmap(); } emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking()); @@ -2094,6 +2057,39 @@ void X11Client::plainResize(int w, int h, ForceGeometry_t force) emit geometryChanged(); } +void X11Client::updateServerGeometry() +{ + const QRect previousServerGeometry = m_frame.geometry(); + const bool resized = (previousServerGeometry.size() != m_serverGeometry.size() || pendingGeometryUpdate() == PendingGeometryForced); + if (resized) { + resizeDecoration(); + m_frame.setGeometry(m_serverGeometry); + if (!isShade()) { + QSize cs = clientSize(); + m_wrapper.setGeometry(QRect(clientPos(), cs)); + if (!isResize() || syncRequest.counter == XCB_NONE) + m_client.setGeometry(0, 0, cs.width(), cs.height()); + // SELI - won't this be too expensive? + // THOMAS - yes, but gtk+ clients will not resize without ... + sendSyntheticConfigureNotify(); + } + updateShape(); + } else { + if (isMoveResize()) { + 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 + } else { + m_frame.move(m_serverGeometry.topLeft()); + sendSyntheticConfigureNotify(); + } + + // Unconditionally move the input window: it won't affect rendering + m_decoInputExtent.move(pos() + inputPos()); + } +} + /** * Reimplemented to inform the client about the new window position. */ @@ -2673,7 +2669,7 @@ void X11Client::leaveMoveResize() { if (needsXWindowMove) { // Do the deferred move - m_frame.move(m_bufferGeometry.topLeft()); + m_frame.move(m_serverGeometry.topLeft()); needsXWindowMove = false; } if (!isResize()) diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp index 1953ab4b38..f6f5d9c6c6 100644 --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -1433,7 +1433,6 @@ 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,12 +1441,7 @@ void SceneOpenGL2Window::performPaint(int mask, QRegion region, WindowPaintData // the previous Client's content space. WindowQuad newQuad(WindowQuadContents); for (int i = 0; i < 4; ++i) { - 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; + newQuad[i] = WindowVertex(quad[i].x(), quad[i].y(), quad[i].u(), quad[i].v()); } quads[PreviousContentLeaf].append(newQuad); } diff --git a/scene.cpp b/scene.cpp index b466046b54..1c65625ef0 100644 --- a/scene.cpp +++ b/scene.cpp @@ -3,6 +3,7 @@ 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 @@ -735,7 +736,7 @@ QRegion Scene::Window::bufferShape() const const QRect bufferGeometry = toplevel->bufferGeometry(); if (toplevel->shape()) { - auto cookie = xcb_shape_get_rectangles_unchecked(connection(), toplevel->frameId(), XCB_SHAPE_SK_BOUNDING); + auto cookie = xcb_shape_get_rectangles_unchecked(connection(), toplevel->windowId(), XCB_SHAPE_SK_BOUNDING); ScopedCPointer reply(xcb_shape_get_rectangles_reply(connection(), cookie, nullptr)); if (!reply.isNull()) { m_bufferShape = QRegion(); @@ -765,15 +766,7 @@ QRegion Scene::Window::clientShape() const return QRegion(); } } - - 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; + return bufferShape(); } QRegion Scene::Window::decorationShape() const @@ -1039,9 +1032,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()->frameId(), pix); - Xcb::WindowAttributes windowAttributes(toplevel()->frameId()); - Xcb::WindowGeometry windowGeometry(toplevel()->frameId()); + 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()); if (xcb_generic_error_t *error = xcb_request_check(connection(), namePixmapCookie)) { qCDebug(KWIN_CORE) << "Creating window pixmap failed: " << error->error_code; free(error); @@ -1062,7 +1055,6 @@ 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 7e7aee5847..7444cdafca 100644 --- a/scene.h +++ b/scene.h @@ -3,6 +3,7 @@ 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 @@ -448,12 +449,6 @@ 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. @@ -513,7 +508,6 @@ private: xcb_pixmap_t m_pixmap; QSize m_pixmapSize; bool m_discarded; - QRect m_contentsRect; QPointer m_buffer; QSharedPointer m_fbo; QImage m_internalImage; @@ -679,12 +673,6 @@ 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 3a11f30bd7..5666c8b3b4 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -3,6 +3,7 @@ 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 @@ -256,12 +257,13 @@ bool Toplevel::setupCompositing() if (damage_handle != XCB_NONE) return false; - if (kwinApp()->operationMode() == Application::OperationModeX11 && !surface()) { + if (kwinApp()->operationMode() == Application::OperationModeX11) { damage_handle = xcb_generate_id(connection()); - xcb_damage_create(connection(), damage_handle, frameId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); + xcb_damage_create(connection(), damage_handle, windowId(), XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); + xcb_composite_redirect_window(connection(), windowId(), XCB_COMPOSITE_REDIRECT_MANUAL); } - damage_region = QRegion(0, 0, width(), height()); + damage_region = QRegion(QRect(QPoint(), bufferGeometry().size())); effect_window = new EffectWindowImpl(this); Compositor::self()->scene()->addToplevel(this); @@ -278,6 +280,10 @@ 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); @@ -791,11 +797,6 @@ 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 a99556108d..3aa4960210 100644 --- a/toplevel.h +++ b/toplevel.h @@ -3,6 +3,7 @@ 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 @@ -315,16 +316,6 @@ 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 76a504c75e..d19967ced4 100644 --- a/x11client.cpp +++ b/x11client.cpp @@ -4,6 +4,7 @@ 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 @@ -255,7 +256,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_bufferGeometry.x(), m_bufferGeometry.y()); + m_client.reparent(rootWindow(), m_serverGeometry.x(), m_serverGeometry.y()); xcb_change_save_set(c, XCB_SET_MODE_DELETE, m_client); m_client.selectInput(XCB_EVENT_MASK_NO_EVENT); if (on_shutdown) @@ -1985,12 +1986,7 @@ xcb_window_t X11Client::frameId() const QRect X11Client::bufferGeometry() const { - return m_bufferGeometry; -} - -QMargins X11Client::bufferMargins() const -{ - return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom()); + return m_clientGeometry; } QPoint X11Client::framePosToClientPos(const QPoint &point) const @@ -2216,64 +2212,21 @@ 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 bufferPosition = isDecorated() ? framePosition : m_clientGeometry.topLeft(); + const QPoint serverPosition = 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); } - if (!canUpdatePosition(framePosition, bufferPosition, force)) { + geom.moveTopLeft(framePosition); + if (force == NormalGeometrySet && m_serverGeometry.topLeft() == serverPosition) { return; } - m_bufferGeometry.moveTopLeft(bufferPosition); - geom.moveTopLeft(framePosition); + m_serverGeometry.moveTopLeft(serverPosition); if (areGeometryUpdatesBlocked()) { if (pendingGeometryUpdate() == PendingGeometryForced) { // Maximum, nothing needed. @@ -2284,8 +2237,7 @@ void X11Client::move(int x, int y, ForceGeometry_t force) } return; } - m_frame.move(m_bufferGeometry.topLeft()); - sendSyntheticConfigureNotify(); + updateServerGeometry(); updateWindowRules(Rules::Position); screens()->setCurrent(this); workspace()->updateStackingOrder(); diff --git a/x11client.h b/x11client.h index 34bb1371d7..df06e05d64 100644 --- a/x11client.h +++ b/x11client.h @@ -4,6 +4,7 @@ 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 @@ -57,6 +58,25 @@ 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 @@ -90,7 +110,6 @@ 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; @@ -446,9 +465,6 @@ 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(); @@ -456,7 +472,7 @@ private: void map(); void unmap(); void updateHiddenPreview(); - + void updateServerGeometry(); void updateInputShape(); xcb_timestamp_t readUserTimeMapTimestamp(const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, @@ -522,7 +538,7 @@ private: } m_fullscreenMode; MaximizeMode max_mode; - QRect m_bufferGeometry = QRect(0, 0, 100, 100); + QRect m_serverGeometry = QRect(0, 0, 100, 100); QRect m_clientGeometry = QRect(0, 0, 100, 100); QRect geom_restore; QRect geom_fs_restore;