From e327cce4bc92aa028c7aefcec13f6239b08aa5bf Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 5 Oct 2018 17:36:02 +0300 Subject: [PATCH] [wayland] Asyncronously update maximise flags Summary: A window maximising is an async operation. We work out what size we want the client to be, then request the client to update. The window isn't really maximised until we get that new buffer with the new size. This patch splits the requested, pending and current state, updating as appropriate. Things are a bit complex with things like borders. Technically we shouldn't update them till we get a response, but we also need to have the correct geometry of the full size window in our request. For now they behave as before, updating when we request the change. X code is untouched. This hopefully fixes maximise animations on wayland as now we update the geometry before emitting maximisedChanged. Test Plan: Maximised a window with the button and double clicking title bar. I get only the following events on maximise/restore: 19:51:39.156 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry shape changed QRect(47,24 640x509) QRect(0,0 716x573) 19:51:39.157 KWin::EffectsHandlerImpl::slotClientMaximized slot client maximised true true 19:51:40.522 KWin::EffectsHandlerImpl::slotGeometryShapeChanged geometry shape changed QRect(0,0 716x573) QRect(47,24 640x509) 19:51:40.522 KWin::EffectsHandlerImpl::slotClientMaximized slot client maximised false false BUG: 382698 Reviewers: #kwin, romangg Reviewed By: #kwin, romangg Subscribers: romangg, zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D15150 --- autotests/integration/shell_client_test.cpp | 11 ++++- shell_client.cpp | 52 ++++++++++++++------- shell_client.h | 9 +++- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp index b15a1f3119..e369f88516 100644 --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -671,6 +671,15 @@ void TestShellClient::testMaximizedToFullscreen() } QVERIFY(sizeChangeRequestedSpy.wait()); QCOMPARE(sizeChangeRequestedSpy.count(), 1); + + if (xdgShellSurface) { + for (const auto &it: configureRequestedSpy) { + xdgShellSurface->ackConfigure(it[2].toInt()); + } + } + Test::render(surface.data(), sizeChangeRequestedSpy.last().first().toSize(), Qt::red); + QVERIFY(geometryChangedSpy.wait()); + QCOMPARE(c->maximizeMode(), MaximizeFull); QCOMPARE(geometryChangedSpy.isEmpty(), false); geometryChangedSpy.clear(); @@ -690,7 +699,6 @@ void TestShellClient::testMaximizedToFullscreen() QCOMPARE(sizeChangeRequestedSpy.last().first().toSize(), QSize(screens()->size(0))); // TODO: should switch to fullscreen once it's updated QVERIFY(c->isFullScreen()); - QCOMPARE(c->clientSize(), QSize(100, 50)); QVERIFY(geometryChangedSpy.isEmpty()); if (xdgShellSurface) { @@ -698,7 +706,6 @@ void TestShellClient::testMaximizedToFullscreen() xdgShellSurface->ackConfigure(it[2].toInt()); } } - // render at the new size Test::render(surface.data(), sizeChangeRequestedSpy.last().first().toSize(), Qt::red); QVERIFY(geometryChangedSpy.wait()); diff --git a/shell_client.cpp b/shell_client.cpp index fe8bf0f253..12f53d57a9 100644 --- a/shell_client.cpp +++ b/shell_client.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2018 David Edmundson 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 @@ -606,6 +607,7 @@ void ShellClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force) if (QSize(w, h) == geom.size() && !isWaitingForMoveResizeSync()) { // size didn't change, update directly doSetGeometry(QRect(x, y, w, h)); + updateMaximizeMode(m_requestedMaximizeMode); } else { // size did change, Client needs to provide a new buffer requestGeometry(QRect(x, y, w, h)); @@ -826,30 +828,30 @@ void ShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) workspace()->clientArea(MaximizeArea, Cursor::pos(), desktop()) : workspace()->clientArea(MaximizeArea, this); - MaximizeMode oldMode = m_maximizeMode; + MaximizeMode oldMode = m_requestedMaximizeMode; StackingUpdatesBlocker blocker(workspace()); RequestGeometryBlocker geometryBlocker(this); // 'adjust == true' means to update the size only, e.g. after changing workspace size if (!adjust) { if (vertical) - m_maximizeMode = MaximizeMode(m_maximizeMode ^ MaximizeVertical); + m_requestedMaximizeMode = MaximizeMode(m_requestedMaximizeMode ^ MaximizeVertical); if (horizontal) - m_maximizeMode = MaximizeMode(m_maximizeMode ^ MaximizeHorizontal); + m_requestedMaximizeMode = MaximizeMode(m_requestedMaximizeMode ^ MaximizeHorizontal); } // TODO: add more checks as in Client // call into decoration update borders - if (isDecorated() && decoration()->client() && !(options->borderlessMaximizedWindows() && m_maximizeMode == KWin::MaximizeFull)) { + if (isDecorated() && decoration()->client() && !(options->borderlessMaximizedWindows() && m_requestedMaximizeMode == KWin::MaximizeFull)) { changeMaximizeRecursion = true; const auto c = decoration()->client().data(); - if ((m_maximizeMode & MaximizeVertical) != (oldMode & MaximizeVertical)) { - emit c->maximizedVerticallyChanged(m_maximizeMode & MaximizeVertical); + if ((m_requestedMaximizeMode & MaximizeVertical) != (oldMode & MaximizeVertical)) { + emit c->maximizedVerticallyChanged(m_requestedMaximizeMode & MaximizeVertical); } - if ((m_maximizeMode & MaximizeHorizontal) != (oldMode & MaximizeHorizontal)) { - emit c->maximizedHorizontallyChanged(m_maximizeMode & MaximizeHorizontal); + if ((m_requestedMaximizeMode & MaximizeHorizontal) != (oldMode & MaximizeHorizontal)) { + emit c->maximizedHorizontallyChanged(m_requestedMaximizeMode & MaximizeHorizontal); } - if ((m_maximizeMode == MaximizeFull) != (oldMode == MaximizeFull)) { - emit c->maximizedChanged(m_maximizeMode & MaximizeFull); + if ((m_requestedMaximizeMode == MaximizeFull) != (oldMode == MaximizeFull)) { + emit c->maximizedChanged(m_requestedMaximizeMode & MaximizeFull); } changeMaximizeRecursion = false; } @@ -858,7 +860,7 @@ void ShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) // triggers a maximize change. // The next setNoBorder interation will exit since there's no change but the first recursion pullutes the restore geometry changeMaximizeRecursion = true; - setNoBorder(rules()->checkNoBorder(m_maximizeMode == MaximizeFull)); + setNoBorder(rules()->checkNoBorder(m_requestedMaximizeMode == MaximizeFull)); changeMaximizeRecursion = false; } @@ -870,15 +872,15 @@ void ShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) // Not restoring on the same screen // TODO: The following doesn't work for some reason //quick_tile_mode = QuickTileNone; // And exit quick tile mode manually - } else if ((oldMode == MaximizeVertical && m_maximizeMode == MaximizeRestore) || - (oldMode == MaximizeFull && m_maximizeMode == MaximizeHorizontal)) { + } else if ((oldMode == MaximizeVertical && m_requestedMaximizeMode == MaximizeRestore) || + (oldMode == MaximizeFull && m_requestedMaximizeMode == MaximizeHorizontal)) { // Modifying geometry of a tiled window updateQuickTileMode(QuickTileFlag::None); // Exit quick tile mode without restoring geometry } } // TODO: check rules - if (m_maximizeMode == MaximizeFull) { + if (m_requestedMaximizeMode == MaximizeFull) { m_geomMaximizeRestore = geometry(); // TODO: Client has more checks if (options->electricBorderMaximize()) { @@ -892,7 +894,7 @@ void ShellClient::changeMaximize(bool horizontal, bool vertical, bool adjust) setGeometry(workspace()->clientArea(MaximizeArea, this)); workspace()->raiseClient(this); } else { - if (m_maximizeMode == MaximizeRestore) { + if (m_requestedMaximizeMode == MaximizeRestore) { updateQuickTileMode(QuickTileFlag::None); } if (quickTileMode() != oldQuickTileMode) { @@ -1174,6 +1176,7 @@ void ShellClient::requestGeometry(const QRect &rect) } PendingConfigureRequest configureRequest; configureRequest.positionAfterResize = rect.topLeft(); + configureRequest.maximizeMode = m_requestedMaximizeMode; const QSize size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); if (m_shellSurface) { @@ -1202,6 +1205,7 @@ void ShellClient::requestGeometry(const QRect &rect) void ShellClient::updatePendingGeometry() { QPoint position = geom.topLeft(); + MaximizeMode maximizeMode = m_maximizeMode; for (auto it = m_pendingConfigureRequests.begin(); it != m_pendingConfigureRequests.end(); it++) { if (it->serialId > m_lastAckedConfigureRequest) { //this serial is not acked yet, therefore we know all future serials are not @@ -1212,6 +1216,7 @@ void ShellClient::updatePendingGeometry() addLayerRepaint(geometry()); } position = it->positionAfterResize; + maximizeMode = it->maximizeMode; m_pendingConfigureRequests.erase(m_pendingConfigureRequests.begin(), ++it); break; @@ -1219,6 +1224,7 @@ void ShellClient::updatePendingGeometry() //else serialId < m_lastAckedConfigureRequest and the state is now irrelevant and can be ignored } doSetGeometry(QRect(position, m_clientSize + QSize(borderLeft() + borderRight(), borderTop() + borderBottom()))); + updateMaximizeMode(maximizeMode); } void ShellClient::clientFullScreenChanged(bool fullScreen) @@ -1484,6 +1490,20 @@ void ShellClient::updateColorScheme() } } +void ShellClient::updateMaximizeMode(MaximizeMode maximizeMode) +{ + if (maximizeMode == m_maximizeMode) { + return; + } + + bool horizontalChanged = (maximizeMode & MaximizeHorizontal) != (m_maximizeMode & MaximizeHorizontal); + bool verticalChanged = (maximizeMode & MaximizeVertical) != (m_maximizeMode & MaximizeVertical); + m_maximizeMode = maximizeMode; + + emit clientMaximizedStateChanged(this, m_maximizeMode); + emit clientMaximizedStateChanged(this, horizontalChanged, verticalChanged); +} + bool ShellClient::hasStrut() const { if (!isShown(true)) { @@ -1636,7 +1656,7 @@ KWayland::Server::XdgShellSurfaceInterface::States ShellClient::xdgSurfaceStates if (isFullScreen()) { states |= XdgShellSurfaceInterface::State::Fullscreen; } - if (maximizeMode() == MaximizeMode::MaximizeFull) { + if (m_requestedMaximizeMode == MaximizeMode::MaximizeFull) { states |= XdgShellSurfaceInterface::State::Maximized; } if (isResize()) { diff --git a/shell_client.h b/shell_client.h index 46145678cc..0232770a9a 100644 --- a/shell_client.h +++ b/shell_client.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin +Copyright (C) 2018 David Edmundson 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 @@ -202,6 +203,7 @@ private: void updateClientOutputs(); KWayland::Server::XdgShellSurfaceInterface::States xdgSurfaceStates() const; void updateShowOnScreenEdge(); + void updateMaximizeMode(MaximizeMode maximizeMode); // called on surface commit and processes all m_pendingConfigureRequests up to m_lastAckedConfigureReqest void updatePendingGeometry(); static void deleteClient(ShellClient *c); @@ -217,17 +219,22 @@ private: quint32 serialId = 0; // position to apply after a resize operation has been completed QPoint positionAfterResize; + MaximizeMode maximizeMode; }; QVector m_pendingConfigureRequests; quint32 m_lastAckedConfigureRequest = 0; + //mode in use by the current buffer + MaximizeMode m_maximizeMode = MaximizeRestore; + //mode we currently want to be, could be pending on client updating, could be not sent yet + MaximizeMode m_requestedMaximizeMode = MaximizeRestore; + QRect m_geomFsRestore; //size and position of the window before it was set to fullscreen bool m_closing = false; quint32 m_windowId = 0; QWindow *m_internalWindow = nullptr; Qt::WindowFlags m_internalWindowFlags = Qt::WindowFlags(); bool m_unmapped = true; - MaximizeMode m_maximizeMode = MaximizeRestore; QRect m_geomMaximizeRestore; // size and position of the window before it was set to maximize NET::WindowType m_windowType = NET::Normal; QPointer m_plasmaShellSurface;