[wayland] Destroy XdgToplevelClient and XdgPopupClient on unmap

There are several ways to handle unmapping of a wl_surface. The first
one is to destroy the associated AbstractClient instance. The second one
is to transition the AbstractClient in a special state.

The problem with the second approach is that it makes animations such as
fade out more difficult to handle since effects in kwin are geared more
towards the first approach (destroying AbstractClient).
master
Vlad Zahorodnii 2020-05-07 17:29:41 +03:00
parent 31ea780d79
commit df9e36ee68
18 changed files with 331 additions and 532 deletions

View File

@ -519,6 +519,8 @@ set(kwin_SRCS
wayland_cursor_theme.cpp
wayland_server.cpp
waylandclient.cpp
waylandshellintegration.cpp
waylandxdgshellintegration.cpp
window_property_notify_x11_filter.cpp
workspace.cpp
x11client.cpp

View File

@ -388,16 +388,8 @@ void DebugConsoleTest::testWaylandClient()
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
shellSurface.reset();
Test::flushWaylandConnection();
qDebug() << rowsRemovedSpy.count();
QEXPECT_FAIL("wlShell", "Deleting a ShellSurface does not result in the server removing the XdgShellClient", Continue);
QVERIFY(rowsRemovedSpy.wait(500));
surface.reset();
QVERIFY(rowsRemovedSpy.wait());
if (rowsRemovedSpy.isEmpty()) {
QVERIFY(rowsRemovedSpy.wait());
}
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.first().first().value<QModelIndex>(), waylandTopLevelIndex);
QCOMPARE(rowsRemovedSpy.first().at(1).value<int>(), 0);

View File

@ -3,7 +3,6 @@ if (XCB_ICCCM_FOUND)
integrationTest(NAME testSlidingPopups SRCS slidingpopups_test.cpp LIBS XCB::ICCCM)
integrationTest(NAME testShadeWobblyWindows SRCS wobbly_shade_test.cpp LIBS XCB::ICCCM)
endif()
integrationTest(NAME testFade SRCS fade_test.cpp)
integrationTest(WAYLAND_ONLY NAME testEffectWindowGeometry SRCS windowgeometry_test.cpp)
integrationTest(NAME testScriptedEffects SRCS scripted_effects_test.cpp)
integrationTest(WAYLAND_ONLY NAME testToplevelOpenCloseAnimation SRCS toplevel_open_close_animation_test.cpp)

View File

@ -1,179 +0,0 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "kwin_wayland_test.h"
#include "abstract_client.h"
#include "composite.h"
#include "effects.h"
#include "effectloader.h"
#include "cursor.h"
#include "platform.h"
#include "wayland_server.h"
#include "workspace.h"
#include "effect_builtins.h"
#include <KConfigGroup>
#include <KWayland/Client/buffer.h>
#include <KWayland/Client/surface.h>
using namespace KWin;
using namespace KWayland::Client;
static const QString s_socketName = QStringLiteral("wayland_test_effects_translucency-0");
class FadeTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testWindowCloseAfterWindowHidden_data();
void testWindowCloseAfterWindowHidden();
private:
Effect *m_fadeEffect = nullptr;
};
void FadeTest::initTestCase()
{
qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
qRegisterMetaType<KWin::AbstractClient*>();
qRegisterMetaType<KWin::Effect*>();
QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
QVERIFY(workspaceCreatedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
// disable all effects - we don't want to have it interact with the rendering
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
KConfigGroup plugins(config, QStringLiteral("Plugins"));
ScriptedEffectLoader loader;
const auto builtinNames = BuiltInEffects::availableEffectNames() << loader.listOfKnownEffects();
for (QString name : builtinNames) {
plugins.writeEntry(name + QStringLiteral("Enabled"), false);
}
config->sync();
kwinApp()->setConfig(config);
qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", "1");
kwinApp()->start();
QVERIFY(workspaceCreatedSpy.wait());
QVERIFY(KWin::Compositor::self());
}
void FadeTest::init()
{
QVERIFY(Test::setupWaylandConnection());
// load the translucency effect
EffectsHandlerImpl *e = static_cast<EffectsHandlerImpl*>(effects);
// find the effectsloader
auto effectloader = e->findChild<AbstractEffectLoader*>();
QVERIFY(effectloader);
QSignalSpy effectLoadedSpy(effectloader, &AbstractEffectLoader::effectLoaded);
QVERIFY(effectLoadedSpy.isValid());
QVERIFY(!e->isEffectLoaded(QStringLiteral("kwin4_effect_fade")));
QVERIFY(e->loadEffect(QStringLiteral("kwin4_effect_fade")));
QVERIFY(e->isEffectLoaded(QStringLiteral("kwin4_effect_fade")));
QCOMPARE(effectLoadedSpy.count(), 1);
m_fadeEffect = effectLoadedSpy.first().first().value<Effect*>();
QVERIFY(m_fadeEffect);
}
void FadeTest::cleanup()
{
Test::destroyWaylandConnection();
EffectsHandlerImpl *e = static_cast<EffectsHandlerImpl*>(effects);
if (e->isEffectLoaded(QStringLiteral("kwin4_effect_fade"))) {
e->unloadEffect(QStringLiteral("kwin4_effect_fade"));
}
QVERIFY(!e->isEffectLoaded(QStringLiteral("kwin4_effect_fade")));
m_fadeEffect = nullptr;
}
void FadeTest::testWindowCloseAfterWindowHidden_data()
{
QTest::addColumn<Test::XdgShellSurfaceType>("type");
QTest::newRow("xdgWmBase") << Test::XdgShellSurfaceType::XdgShellStable;
}
void FadeTest::testWindowCloseAfterWindowHidden()
{
// this test simulates the showing/hiding/closing of a Wayland window
// especially the situation that a window got unmapped and destroyed way later
QVERIFY(!m_fadeEffect->isActive());
QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
QVERIFY(windowAddedSpy.isValid());
QSignalSpy windowHiddenSpy(effects, &EffectsHandler::windowHidden);
QVERIFY(windowHiddenSpy.isValid());
QSignalSpy windowShownSpy(effects, &EffectsHandler::windowShown);
QVERIFY(windowShownSpy.isValid());
QSignalSpy windowClosedSpy(effects, &EffectsHandler::windowClosed);
QVERIFY(windowClosedSpy.isValid());
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::XdgShellSurfaceType, type);
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellSurface(type, surface.data()));
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
QTRY_COMPARE(windowAddedSpy.count(), 1);
QTRY_COMPARE(m_fadeEffect->isActive(), true);
QTest::qWait(500);
QTRY_COMPARE(m_fadeEffect->isActive(), false);
// now unmap the surface
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(windowHiddenSpy.wait());
QCOMPARE(m_fadeEffect->isActive(), true);
QTest::qWait(500);
QTRY_COMPARE(m_fadeEffect->isActive(), false);
// and map again
Test::render(surface.data(), QSize(100, 50), Qt::red);
QVERIFY(windowShownSpy.wait());
QTRY_COMPARE(m_fadeEffect->isActive(), true);
QTest::qWait(500);
QTRY_COMPARE(m_fadeEffect->isActive(), false);
// and unmap once more
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(windowHiddenSpy.wait());
QCOMPARE(m_fadeEffect->isActive(), true);
QTest::qWait(500);
QTRY_COMPARE(m_fadeEffect->isActive(), false);
// and now destroy
shellSurface.reset();
surface.reset();
QVERIFY(windowClosedSpy.wait());
QCOMPARE(m_fadeEffect->isActive(), false);
}
WAYLANDTEST_MAIN(FadeTest)
#include "fade_test.moc"

View File

@ -240,7 +240,7 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped()
// This test verifies that the idle inhibitor object is not honored by KWin
// when the associated client is unmapped.
// Get reference to the idle interface.
// Get reference to the idle interface.
auto idle = waylandServer()->display()->findChild<IdleInterface *>();
QVERIFY(idle);
QVERIFY(!idle->isInhibited());
@ -252,36 +252,56 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped()
QVERIFY(!surface.isNull());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
QVERIFY(!shellSurface.isNull());
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
QVERIFY(configureRequestedSpy.isValid());
// Create the inhibitor object.
QScopedPointer<IdleInhibitor> inhibitor(Test::waylandIdleInhibitManager()->createInhibitor(surface.data()));
QVERIFY(inhibitor->isValid());
// Render the client.
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
// Map the client.
QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
QVERIFY(clientAddedSpy.isValid());
Test::render(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(clientAddedSpy.isEmpty());
QVERIFY(clientAddedSpy.wait());
QCOMPARE(clientAddedSpy.count(), 1);
AbstractClient *client = clientAddedSpy.last().first().value<AbstractClient *>();
QVERIFY(client);
QCOMPARE(client->readyForPainting(), true);
// The compositor will respond with a configure event when the surface becomes active.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 1);
// This should inhibit our server object.
QVERIFY(idle->isInhibited());
QCOMPARE(inhibitedSpy.count(), 1);
// Unmap the client.
QSignalSpy hiddenSpy(c, &AbstractClient::windowHidden);
QVERIFY(hiddenSpy.isValid());
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(hiddenSpy.wait());
QVERIFY(Test::waitForWindowDestroyed(client));
// The surface is no longer visible, so the compositor don't have to honor the
// The surface is no longer visible, so the compositor doesn't have to honor the
// idle inhibitor object.
QVERIFY(!idle->isInhibited());
QCOMPARE(inhibitedSpy.count(), 2);
// Tell the compositor that we want to map the surface.
surface->commit(Surface::CommitFlag::None);
// The compositor will respond with a configure event.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 2);
// Map the client.
QSignalSpy windowShownSpy(c, &AbstractClient::windowShown);
QVERIFY(windowShownSpy.isValid());
Test::render(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(windowShownSpy.wait());
QVERIFY(clientAddedSpy.wait());
QCOMPARE(clientAddedSpy.count(), 2);
client = clientAddedSpy.last().first().value<AbstractClient *>();
QVERIFY(client);
QCOMPARE(client->readyForPainting(), true);
// The test client became visible again, so the compositor has to honor the idle
// inhibitor object back again.
@ -290,7 +310,7 @@ void TestIdleInhibition::testDontInhibitWhenUnmapped()
// Destroy the test client.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(c));
QVERIFY(Test::waitForWindowDestroyed(client));
QTRY_VERIFY(!idle->isInhibited());
QCOMPARE(inhibitedSpy.count(), 4);
}

View File

@ -80,8 +80,6 @@ private Q_SLOTS:
void testResizeForVirtualKeyboardWithFullScreen();
void testDestroyMoveClient();
void testDestroyResizeClient();
void testUnmapMoveClient();
void testUnmapResizeClient();
private:
KWayland::Client::ConnectionThread *m_connection = nullptr;
@ -1111,98 +1109,6 @@ void MoveResizeWindowTest::testDestroyResizeClient()
QCOMPARE(workspace()->moveResizeClient(), nullptr);
}
void MoveResizeWindowTest::testUnmapMoveClient()
{
// This test verifies that active move operation gets cancelled when
// the associated client is unmapped.
// Create the test client.
using namespace KWayland::Client;
QScopedPointer<Surface> surface(Test::createSurface());
QVERIFY(!surface.isNull());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
QVERIFY(!shellSurface.isNull());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(client);
// Start resizing the client.
QSignalSpy clientStartMoveResizedSpy(client, &AbstractClient::clientStartUserMovedResized);
QVERIFY(clientStartMoveResizedSpy.isValid());
QSignalSpy clientFinishUserMovedResizedSpy(client, &AbstractClient::clientFinishUserMovedResized);
QVERIFY(clientFinishUserMovedResizedSpy.isValid());
QCOMPARE(workspace()->moveResizeClient(), nullptr);
QCOMPARE(client->isMove(), false);
QCOMPARE(client->isResize(), false);
workspace()->slotWindowMove();
QCOMPARE(clientStartMoveResizedSpy.count(), 1);
QCOMPARE(workspace()->moveResizeClient(), client);
QCOMPARE(client->isMove(), true);
QCOMPARE(client->isResize(), false);
// Unmap the client while we're moving it.
QSignalSpy hiddenSpy(client, &AbstractClient::windowHidden);
QVERIFY(hiddenSpy.isValid());
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(hiddenSpy.wait());
QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0);
QCOMPARE(workspace()->moveResizeClient(), nullptr);
QCOMPARE(client->isMove(), false);
QCOMPARE(client->isResize(), false);
// Destroy the client.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(client));
QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0);
}
void MoveResizeWindowTest::testUnmapResizeClient()
{
// This test verifies that active resize operation gets cancelled when
// the associated client is unmapped.
// Create the test client.
using namespace KWayland::Client;
QScopedPointer<Surface> surface(Test::createSurface());
QVERIFY(!surface.isNull());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
QVERIFY(!shellSurface.isNull());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(client);
// Start resizing the client.
QSignalSpy clientStartMoveResizedSpy(client, &AbstractClient::clientStartUserMovedResized);
QVERIFY(clientStartMoveResizedSpy.isValid());
QSignalSpy clientFinishUserMovedResizedSpy(client, &AbstractClient::clientFinishUserMovedResized);
QVERIFY(clientFinishUserMovedResizedSpy.isValid());
QCOMPARE(workspace()->moveResizeClient(), nullptr);
QCOMPARE(client->isMove(), false);
QCOMPARE(client->isResize(), false);
workspace()->slotWindowResize();
QCOMPARE(clientStartMoveResizedSpy.count(), 1);
QCOMPARE(workspace()->moveResizeClient(), client);
QCOMPARE(client->isMove(), false);
QCOMPARE(client->isResize(), true);
// Unmap the client while we're resizing it.
QSignalSpy hiddenSpy(client, &AbstractClient::windowHidden);
QVERIFY(hiddenSpy.isValid());
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(hiddenSpy.wait());
QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0);
QCOMPARE(workspace()->moveResizeClient(), nullptr);
QCOMPARE(client->isMove(), false);
QCOMPARE(client->isResize(), false);
// Destroy the client.
shellSurface.reset();
QVERIFY(Test::waitForWindowDestroyed(client));
QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0);
}
}
WAYLANDTEST_MAIN(KWin::MoveResizeWindowTest)

View File

@ -70,10 +70,8 @@ private Q_SLOTS:
void init();
void cleanup();
void testMapUnmapMap_data();
void testMapUnmapMap();
void testMapUnmap();
void testDesktopPresenceChanged();
void testTransientPositionAfterRemap();
void testWindowOutputs_data();
void testWindowOutputs();
void testMinimizeActiveWindow_data();
@ -158,109 +156,68 @@ void TestXdgShellClient::cleanup()
Test::destroyWaylandConnection();
}
void TestXdgShellClient::testMapUnmapMap_data()
void TestXdgShellClient::testMapUnmap()
{
QTest::addColumn<Test::XdgShellSurfaceType>("type");
// This test verifies that the compositor destroys XdgToplevelClient when the
// associated xdg_toplevel surface is unmapped.
QTest::newRow("xdgWmBase") << Test::XdgShellSurfaceType::XdgShellStable;
}
// Create a wl_surface and an xdg_toplevel, but don't commit them yet!
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface(
Test::createXdgShellStableSurface(surface.data(), nullptr, Test::CreationSetup::CreateOnly));
void TestXdgShellClient::testMapUnmapMap()
{
// this test verifies that mapping a previously mapped window works correctly
QSignalSpy clientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
QVERIFY(clientAddedSpy.isValid());
QSignalSpy effectsWindowShownSpy(effects, &EffectsHandler::windowShown);
QVERIFY(effectsWindowShownSpy.isValid());
QSignalSpy effectsWindowHiddenSpy(effects, &EffectsHandler::windowHidden);
QVERIFY(effectsWindowHiddenSpy.isValid());
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::XdgShellSurfaceType, type);
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellSurface(type, surface.data()));
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
QVERIFY(configureRequestedSpy.isValid());
// now let's render
// Tell the compositor that we want to map the surface.
surface->commit(Surface::CommitFlag::None);
// The compositor will respond with a configure event.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 1);
// Now we can attach a buffer with actual data to the surface.
Test::render(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(clientAddedSpy.isEmpty());
QVERIFY(clientAddedSpy.wait());
auto client = clientAddedSpy.first().first().value<AbstractClient *>();
QCOMPARE(clientAddedSpy.count(), 1);
AbstractClient *client = clientAddedSpy.last().first().value<AbstractClient *>();
QVERIFY(client);
QVERIFY(client->isShown(true));
QCOMPARE(client->isHiddenInternal(), false);
QCOMPARE(client->readyForPainting(), true);
QCOMPARE(client->depth(), 32);
QVERIFY(client->hasAlpha());
QCOMPARE(client->icon().name(), QStringLiteral("wayland"));
QCOMPARE(workspace()->activeClient(), client);
QVERIFY(effectsWindowShownSpy.isEmpty());
QVERIFY(client->isMaximizable());
QVERIFY(client->isMovable());
QVERIFY(client->isMovableAcrossScreens());
QVERIFY(client->isResizable());
QVERIFY(client->property("maximizable").toBool());
QVERIFY(client->property("moveable").toBool());
QVERIFY(client->property("moveableAcrossScreens").toBool());
QVERIFY(client->property("resizeable").toBool());
QCOMPARE(client->isInternal(), false);
QVERIFY(client->effectWindow());
QVERIFY(!client->effectWindow()->internalWindow());
QCOMPARE(client->internalId().isNull(), false);
const auto uuid = client->internalId();
QUuid deletedUuid;
QCOMPARE(deletedUuid.isNull(), true);
connect(client, &AbstractClient::windowClosed, this, [&deletedUuid] (Toplevel *, Deleted *d) { deletedUuid = d->internalId(); });
// When the client becomes active, the compositor will send another configure event.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 2);
// now unmap
QSignalSpy hiddenSpy(client, &AbstractClient::windowHidden);
QVERIFY(hiddenSpy.isValid());
QSignalSpy windowClosedSpy(client, &AbstractClient::windowClosed);
QVERIFY(windowClosedSpy.isValid());
// Unmap the xdg_toplevel surface by committing a null buffer.
surface->attachBuffer(Buffer::Ptr());
surface->commit(Surface::CommitFlag::None);
QVERIFY(hiddenSpy.wait());
QCOMPARE(client->readyForPainting(), true);
QCOMPARE(client->isHiddenInternal(), true);
QVERIFY(windowClosedSpy.isEmpty());
QVERIFY(!workspace()->activeClient());
QCOMPARE(effectsWindowHiddenSpy.count(), 1);
QCOMPARE(effectsWindowHiddenSpy.first().first().value<EffectWindow*>(), client->effectWindow());
QVERIFY(Test::waitForWindowDestroyed(client));
QSignalSpy windowShownSpy(client, &AbstractClient::windowShown);
QVERIFY(windowShownSpy.isValid());
Test::render(surface.data(), QSize(100, 50), Qt::blue, QImage::Format_RGB32);
QCOMPARE(clientAddedSpy.count(), 1);
QVERIFY(windowShownSpy.wait());
QCOMPARE(windowShownSpy.count(), 1);
QCOMPARE(clientAddedSpy.count(), 1);
QCOMPARE(client->readyForPainting(), true);
QCOMPARE(client->isHiddenInternal(), false);
QCOMPARE(client->depth(), 24);
QVERIFY(!client->hasAlpha());
QCOMPARE(workspace()->activeClient(), client);
QCOMPARE(effectsWindowShownSpy.count(), 1);
QCOMPARE(effectsWindowShownSpy.first().first().value<EffectWindow*>(), client->effectWindow());
// let's unmap again
surface->attachBuffer(Buffer::Ptr());
// Tell the compositor that we want to re-map the xdg_toplevel surface.
surface->commit(Surface::CommitFlag::None);
QVERIFY(hiddenSpy.wait());
QCOMPARE(hiddenSpy.count(), 2);
QCOMPARE(client->readyForPainting(), true);
QCOMPARE(client->isHiddenInternal(), true);
QCOMPARE(client->internalId(), uuid);
QVERIFY(windowClosedSpy.isEmpty());
QCOMPARE(effectsWindowHiddenSpy.count(), 2);
QCOMPARE(effectsWindowHiddenSpy.last().first().value<EffectWindow*>(), client->effectWindow());
// The compositor will respond with a configure event.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 3);
// Now we can attach a buffer with actual data to the surface.
Test::render(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(clientAddedSpy.wait());
QCOMPARE(clientAddedSpy.count(), 2);
client = clientAddedSpy.last().first().value<AbstractClient *>();
QVERIFY(client);
QCOMPARE(client->readyForPainting(), true);
// The compositor will respond with a configure event.
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 4);
// Destroy the test client.
shellSurface.reset();
surface.reset();
QVERIFY(windowClosedSpy.wait());
QCOMPARE(windowClosedSpy.count(), 1);
QCOMPARE(effectsWindowHiddenSpy.count(), 2);
QCOMPARE(deletedUuid.isNull(), false);
QCOMPARE(deletedUuid, uuid);
QVERIFY(Test::waitForWindowDestroyed(client));
}
void TestXdgShellClient::testDesktopPresenceChanged()
@ -296,43 +253,6 @@ void TestXdgShellClient::testDesktopPresenceChanged()
QCOMPARE(desktopPresenceChangedEffectsSpy.first().at(2).toInt(), 2);
}
void TestXdgShellClient::testTransientPositionAfterRemap()
{
// this test simulates the situation that a transient window gets reused and the parent window
// moved between the two usages
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
// create the Transient window
XdgPositioner positioner(QSize(50, 40), QRect(0, 0, 5, 10));
positioner.setAnchorEdge(Qt::BottomEdge | Qt::RightEdge);
positioner.setGravity(Qt::BottomEdge | Qt::RightEdge);
QScopedPointer<Surface> transientSurface(Test::createSurface());
QScopedPointer<XdgShellPopup> transientShellSurface(Test::createXdgShellStablePopup(transientSurface.data(), shellSurface.data(), positioner));
auto transient = Test::renderAndWaitForShown(transientSurface.data(), positioner.initialSize(), Qt::blue);
QVERIFY(transient);
QCOMPARE(transient->frameGeometry(), QRect(c->frameGeometry().topLeft() + QPoint(5, 10), QSize(50, 40)));
// unmap the transient
QSignalSpy windowHiddenSpy(transient, &AbstractClient::windowHidden);
QVERIFY(windowHiddenSpy.isValid());
transientSurface->attachBuffer(Buffer::Ptr());
transientSurface->commit(Surface::CommitFlag::None);
QVERIFY(windowHiddenSpy.wait());
// now move the parent surface
c->setFrameGeometry(c->frameGeometry().translated(5, 10));
// now map the transient again
QSignalSpy windowShownSpy(transient, &AbstractClient::windowShown);
QVERIFY(windowShownSpy.isValid());
Test::render(transientSurface.data(), QSize(50, 40), Qt::blue);
QVERIFY(windowShownSpy.wait());
QCOMPARE(transient->frameGeometry(), QRect(c->frameGeometry().topLeft() + QPoint(5, 10), QSize(50, 40)));
}
void TestXdgShellClient::testWindowOutputs_data()
{
QTest::addColumn<Test::XdgShellSurfaceType>("type");

View File

@ -260,16 +260,16 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene)
if (auto w = waylandServer()) {
connect(w, &WaylandServer::shellClientAdded, this, [this](AbstractClient *c) {
if (c->readyForPainting())
slotWaylandClientShown(c);
slotClientShown(c);
else
connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotWaylandClientShown);
connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotClientShown);
});
const auto clients = waylandServer()->clients();
for (AbstractClient *c : clients) {
if (c->readyForPainting()) {
setupClientConnections(c);
} else {
connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotWaylandClientShown);
connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotClientShown);
}
}
}
@ -571,13 +571,6 @@ void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t)
emit windowAdded(c->effectWindow());
}
void EffectsHandlerImpl::slotWaylandClientShown(Toplevel *toplevel)
{
AbstractClient *client = static_cast<AbstractClient *>(toplevel);
setupClientConnections(client);
emit windowAdded(toplevel->effectWindow());
}
void EffectsHandlerImpl::slotUnmanagedShown(KWin::Toplevel *t)
{ // regardless, unmanaged windows are -yet?- not synced anyway
Q_ASSERT(qobject_cast<Unmanaged *>(t));

View File

@ -292,7 +292,6 @@ public Q_SLOTS:
protected Q_SLOTS:
void slotClientShown(KWin::Toplevel*);
void slotWaylandClientShown(KWin::Toplevel*);
void slotUnmanagedShown(KWin::Toplevel*);
void slotWindowClosed(KWin::Toplevel *c, KWin::Deleted *d);
void slotClientMaximized(KWin::AbstractClient *c, MaximizeMode maxMode);

View File

@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "composite.h"
#include "idle_inhibition.h"
#include "screens.h"
#include "waylandxdgshellintegration.h"
#include "workspace.h"
#include "xdgshellclient.h"
@ -144,7 +145,7 @@ void WaylandServer::terminateClientConnections()
}
}
void WaylandServer::registerClient(AbstractClient *client)
void WaylandServer::registerShellClient(AbstractClient *client)
{
if (client->readyForPainting()) {
emit shellClientAdded(client);
@ -154,13 +155,11 @@ void WaylandServer::registerClient(AbstractClient *client)
m_clients << client;
}
void WaylandServer::createXdgToplevelClient(XdgToplevelInterface *shellSurface)
void WaylandServer::registerXdgToplevelClient(XdgToplevelClient *client)
{
if (!workspace()) {
return;
}
// TODO: Find a better way and more generic to install extensions.
SurfaceInterface *surface = shellSurface->surface();
SurfaceInterface *surface = client->surface();
if (surface->client() == m_xwayland.client) {
return;
}
@ -168,8 +167,7 @@ void WaylandServer::createXdgToplevelClient(XdgToplevelInterface *shellSurface)
ScreenLocker::KSldApp::self()->lockScreenShown();
}
XdgToplevelClient *client = new XdgToplevelClient(shellSurface);
registerClient(client);
registerShellClient(client);
auto it = std::find_if(m_plasmaShellSurfaces.begin(), m_plasmaShellSurfaces.end(),
[surface] (PlasmaShellSurfaceInterface *plasmaSurface) {
@ -183,6 +181,9 @@ void WaylandServer::createXdgToplevelClient(XdgToplevelInterface *shellSurface)
if (auto decoration = ServerSideDecorationInterface::get(surface)) {
client->installServerDecoration(decoration);
}
if (auto decoration = XdgToplevelDecorationV1Interface::get(client->shellSurface())) {
client->installXdgDecoration(decoration);
}
if (auto menu = m_appMenuManager->appMenuForSurface(surface)) {
client->installAppMenu(menu);
}
@ -195,14 +196,19 @@ void WaylandServer::createXdgToplevelClient(XdgToplevelInterface *shellSurface)
});
}
void WaylandServer::createXdgPopupClient(XdgPopupInterface *shellSurface)
void WaylandServer::registerXdgGenericClient(AbstractClient *client)
{
if (!workspace()) {
XdgToplevelClient *toplevelClient = qobject_cast<XdgToplevelClient *>(client);
if (toplevelClient) {
registerXdgToplevelClient(toplevelClient);
return;
}
XdgPopupClient *client = new XdgPopupClient(shellSurface);
registerClient(client);
XdgPopupClient *popupClient = qobject_cast<XdgPopupClient *>(client);
if (popupClient) {
registerShellClient(popupClient);
return;
}
qCDebug(KWIN_CORE) << "Received invalid xdg client:" << client->surface();
}
class KWinDisplay : public KWaylandServer::FilteredDisplay
@ -340,9 +346,9 @@ bool WaylandServer::init(const QByteArray &socketName, InitializationFlags flags
m_tabletManager = m_display->createTabletManagerInterface(m_display);
m_keyboardShortcutsInhibitManager = m_display->createKeyboardShortcutsInhibitManagerV1(m_display);
m_xdgShell = m_display->createXdgShell(m_display);
connect(m_xdgShell, &XdgShellInterface::toplevelCreated, this, &WaylandServer::createXdgToplevelClient);
connect(m_xdgShell, &XdgShellInterface::popupCreated, this, &WaylandServer::createXdgPopupClient);
auto shellIntegration = new WaylandXdgShellIntegration(this);
connect(shellIntegration, &WaylandXdgShellIntegration::clientCreated,
this, &WaylandServer::registerXdgGenericClient);
m_xdgDecorationManagerV1 = m_display->createXdgDecorationManagerV1(m_display);
connect(m_xdgDecorationManagerV1, &XdgDecorationManagerV1Interface::decorationCreated, this,

View File

@ -70,9 +70,6 @@ class LinuxDmabufUnstableV1Buffer;
class TabletManagerInterface;
class KeyboardShortcutsInhibitManagerV1Interface;
class XdgDecorationManagerV1Interface;
class XdgShellInterface;
class XdgToplevelInterface;
class XdgPopupInterface;
}
@ -81,6 +78,7 @@ namespace KWin
class AbstractClient;
class Toplevel;
class XdgPopupClient;
class XdgToplevelClient;
class KWIN_EXPORT WaylandServer : public QObject
@ -264,15 +262,15 @@ private:
quint16 createClientId(KWaylandServer::ClientConnection *c);
void destroyInternalConnection();
void initScreenLocker();
void createXdgToplevelClient(KWaylandServer::XdgToplevelInterface *shellSurface);
void createXdgPopupClient(KWaylandServer::XdgPopupInterface *shellSurface);
void registerClient(AbstractClient *client);
void registerXdgGenericClient(AbstractClient *client);
void registerXdgToplevelClient(XdgToplevelClient *client);
void registerXdgPopupClient(XdgPopupClient *client);
void registerShellClient(AbstractClient *client);
KWaylandServer::Display *m_display = nullptr;
KWaylandServer::CompositorInterface *m_compositor = nullptr;
KWaylandServer::SeatInterface *m_seat = nullptr;
KWaylandServer::TabletManagerInterface *m_tabletManager = nullptr;
KWaylandServer::DataDeviceManagerInterface *m_dataDeviceManager = nullptr;
KWaylandServer::XdgShellInterface *m_xdgShell = nullptr;
KWaylandServer::PlasmaShellInterface *m_plasmaShell = nullptr;
KWaylandServer::PlasmaWindowManagementInterface *m_windowManagement = nullptr;
KWaylandServer::PlasmaVirtualDesktopManagementInterface *m_virtualDesktopManagement = nullptr;

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "waylandshellintegration.h"
namespace KWin
{
WaylandShellIntegration::WaylandShellIntegration(QObject *parent)
: QObject(parent)
{
}
} // namespace KWin

36
waylandshellintegration.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "abstract_client.h"
namespace KWin
{
class WaylandShellIntegration : public QObject
{
Q_OBJECT
public:
explicit WaylandShellIntegration(QObject *parent = nullptr);
Q_SIGNALS:
void clientCreated(AbstractClient *client);
};
} // namespace KWin

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "waylandxdgshellintegration.h"
#include "wayland_server.h"
#include "workspace.h"
#include "xdgshellclient.h"
#include <KWaylandServer/display.h>
#include <KWaylandServer/xdgshell_interface.h>
using namespace KWaylandServer;
namespace KWin
{
/**
* The WaylandXdgShellIntegration class is a factory class for xdg-shell clients.
*
* The xdg-shell protocol defines two surface roles - xdg_toplevel and xdg_popup. On the
* compositor side, those roles are represented by XdgToplevelClient and XdgPopupClient,
* respectively.
*
* WaylandXdgShellIntegration monitors for new xdg_toplevel and xdg_popup objects. If it
* detects one, it will create an XdgToplevelClient or XdgPopupClient based on the current
* surface role of the underlying xdg_surface object.
*/
WaylandXdgShellIntegration::WaylandXdgShellIntegration(QObject *parent)
: WaylandShellIntegration(parent)
{
XdgShellInterface *shell = waylandServer()->display()->createXdgShell(this);
connect(shell, &XdgShellInterface::toplevelCreated,
this, &WaylandXdgShellIntegration::registerXdgToplevel);
connect(shell, &XdgShellInterface::popupCreated,
this, &WaylandXdgShellIntegration::registerXdgPopup);
}
void WaylandXdgShellIntegration::registerXdgToplevel(XdgToplevelInterface *toplevel)
{
// Note that the client is going to be destroyed and immediately re-created when the
// underlying surface is unmapped. XdgToplevelClient is re-created right away since
// we don't want too loose any client requests that are allowed to be sent prior to
// the first initial commit, e.g. set_maximized or set_fullscreen.
connect(toplevel, &XdgToplevelInterface::resetOccurred,
this, [this, toplevel] { createXdgToplevelClient(toplevel); });
createXdgToplevelClient(toplevel);
}
void WaylandXdgShellIntegration::createXdgToplevelClient(XdgToplevelInterface *toplevel)
{
if (!workspace()) {
return; // TODO: Shouldn't we create the client when workspace is initialized?
}
emit clientCreated(new XdgToplevelClient(toplevel));
}
void WaylandXdgShellIntegration::registerXdgPopup(XdgPopupInterface *popup)
{
if (!workspace()) {
return; // TODO: Shouldn't we create the client when workspace is initialized?
}
emit clientCreated(new XdgPopupClient(popup));
}
} // namespace KWin

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "waylandshellintegration.h"
namespace KWaylandServer
{
class XdgToplevelInterface;
class XdgPopupInterface;
}
namespace KWin
{
class WaylandXdgShellIntegration : public WaylandShellIntegration
{
Q_OBJECT
public:
explicit WaylandXdgShellIntegration(QObject *parent = nullptr);
private:
void registerXdgToplevel(KWaylandServer::XdgToplevelInterface *toplevel);
void registerXdgPopup(KWaylandServer::XdgPopupInterface *popup);
void createXdgToplevelClient(KWaylandServer::XdgToplevelInterface *surface);
};
} // namespace KWin

View File

@ -737,11 +737,6 @@ void Workspace::addShellClient(AbstractClient *client)
updateTabbox();
connect(client, &AbstractClient::windowShown, this, [this, client] {
updateClientLayer(client);
// TODO: when else should we send the client through placement?
if (client->hasTransientPlacementHint()) {
const QRect area = clientArea(PlacementArea, Screens::self()->current(), client->desktop());
client->placeIn(area);
}
markXStackingOrderAsDirty();
updateStackingOrder(true);
updateClientArea();

View File

@ -58,12 +58,16 @@ XdgSurfaceClient::XdgSurfaceClient(XdgSurfaceInterface *shellSurface)
connect(shellSurface, &XdgSurfaceInterface::configureAcknowledged,
this, &XdgSurfaceClient::handleConfigureAcknowledged);
connect(shellSurface, &XdgSurfaceInterface::resetOccurred,
this, &XdgSurfaceClient::destroyClient);
connect(shellSurface->surface(), &SurfaceInterface::committed,
this, &XdgSurfaceClient::handleCommit);
connect(shellSurface->surface(), &SurfaceInterface::shadowChanged,
this, &XdgSurfaceClient::updateShadow);
connect(shellSurface->surface(), &SurfaceInterface::unmapped,
this, &XdgSurfaceClient::internalUnmap);
#if 0 // TODO: Refactor kwin core in order to uncomment this code.
connect(shellSurface->surface(), &SurfaceInterface::mapped,
this, &XdgSurfaceClient::setReadyForPainting);
#endif
connect(shellSurface->surface(), &SurfaceInterface::unbound,
this, &XdgSurfaceClient::destroyClient);
connect(shellSurface->surface(), &SurfaceInterface::destroyed,
@ -234,7 +238,7 @@ void XdgSurfaceClient::handleCommit()
handleRoleCommit();
m_lastAcknowledgedConfigure.reset();
internalMap();
setReadyForPainting();
updateDepth();
}
@ -464,7 +468,7 @@ void XdgSurfaceClient::updateGeometry(const QRect &rect)
*/
void XdgSurfaceClient::updateGeometryRestoreHack()
{
if (isUnmapped() && geometryRestore().isEmpty() && !frameGeometry().isEmpty()) {
if (geometryRestore().isEmpty() && !frameGeometry().isEmpty()) {
setGeometryRestore(frameGeometry());
}
}
@ -496,12 +500,12 @@ void XdgSurfaceClient::addDamage(const QRegion &damage)
bool XdgSurfaceClient::isShown(bool shaded_is_shown) const
{
Q_UNUSED(shaded_is_shown)
return !isClosing() && !isHidden() && !isMinimized() && !isUnmapped();
return !isClosing() && !isHidden() && !isMinimized();
}
bool XdgSurfaceClient::isHiddenInternal() const
{
return isHidden() || isUnmapped();
return isHidden();
}
void XdgSurfaceClient::hideClient(bool hide)
@ -542,55 +546,6 @@ void XdgSurfaceClient::internalHide()
emit windowHidden(this);
}
/**
* \todo We just need to destroy XdgSurfaceClient when the xdg-surface is unmapped.
*/
bool XdgSurfaceClient::isUnmapped() const
{
return m_isUnmapped;
}
/**
* \todo We just need to destroy XdgSurfaceClient when the xdg-surface is unmapped.
*/
void XdgSurfaceClient::internalMap()
{
if (!isUnmapped()) {
return;
}
m_isUnmapped = false;
if (readyForPainting()) {
addRepaintFull();
emit windowShown(this);
} else {
setReadyForPainting();
}
emit windowMapped();
}
/**
* \todo We just need to destroy XdgSurfaceClient when the xdg-surface is unmapped.
*/
void XdgSurfaceClient::internalUnmap()
{
if (isUnmapped()) {
return;
}
if (isMoveResize()) {
leaveMoveResize();
}
m_isUnmapped = true;
m_requestedClientGeometry = QRect();
m_lastAcknowledgedConfigure = nullptr;
m_configureTimer->stop();
qDeleteAll(m_configureEvents);
m_configureEvents.clear();
addWorkspaceRepaint(visibleRect());
workspace()->clientHidden(this);
emit windowHidden(this);
emit windowUnmapped();
}
bool XdgSurfaceClient::isClosing() const
{
return m_isClosing;
@ -694,6 +649,11 @@ XdgToplevelClient::~XdgToplevelClient()
{
}
XdgToplevelInterface *XdgToplevelClient::shellSurface() const
{
return m_shellSurface;
}
void XdgToplevelClient::debug(QDebug &stream) const
{
stream << this;
@ -933,7 +893,7 @@ bool XdgToplevelClient::hasStrut() const
void XdgToplevelClient::showOnScreenEdge()
{
if (!m_plasmaShellSurface || isUnmapped()) {
if (!m_plasmaShellSurface) {
return;
}
hideClient(false);
@ -1098,7 +1058,7 @@ bool XdgToplevelClient::acceptsFocus() const
return m_plasmaShellSurface->panelTakesFocus();
}
}
return !isClosing() && !isUnmapped();
return !isClosing() && readyForPainting();
}
Layer XdgToplevelClient::layerForDock() const
@ -1412,19 +1372,19 @@ void XdgToplevelClient::installServerDecoration(ServerSideDecorationInterface *d
m_serverDecoration = decoration;
connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed, this, [this] {
if (!isClosing() && !isUnmapped()) {
if (!isClosing() && readyForPainting()) {
updateDecoration(/* check_workspace_pos */ true);
}
});
connect(m_serverDecoration, &ServerSideDecorationInterface::modeRequested, this,
[this] (ServerSideDecorationManagerInterface::Mode mode) {
const bool changed = mode != m_serverDecoration->mode();
if (changed && !isUnmapped()) {
if (changed && readyForPainting()) {
updateDecoration(/* check_workspace_pos */ false);
}
}
);
if (!isUnmapped()) {
if (readyForPainting()) {
updateDecoration(/* check_workspace_pos */ true);
}
}
@ -1562,7 +1522,7 @@ void XdgToplevelClient::updateShowOnScreenEdge()
if (!ScreenEdges::self()) {
return;
}
if (isUnmapped() || !m_plasmaShellSurface ||
if (!readyForPainting() || !m_plasmaShellSurface ||
m_plasmaShellSurface->role() != PlasmaShellSurfaceInterface::Role::Panel) {
ScreenEdges::self()->reserve(this, ElectricNone);
return;
@ -1639,15 +1599,13 @@ void XdgToplevelClient::setupWindowManagementIntegration()
if (isLockScreen()) {
return;
}
connect(this, &XdgToplevelClient::windowMapped,
connect(surface(), &SurfaceInterface::mapped,
this, &XdgToplevelClient::setupWindowManagementInterface);
connect(this, &XdgToplevelClient::windowUnmapped,
this, &XdgToplevelClient::destroyWindowManagementInterface);
}
void XdgToplevelClient::setupPlasmaShellIntegration()
{
connect(this, &XdgToplevelClient::windowMapped,
connect(surface(), &SurfaceInterface::mapped,
this, &XdgToplevelClient::updateShowOnScreenEdge);
}
@ -1945,7 +1903,11 @@ static QPoint popupOffset(const QRect &anchorRect, const Qt::Edges anchorEdge,
QRect XdgPopupClient::transientPlacement(const QRect &bounds) const
{
const XdgPositioner positioner = m_shellSurface->positioner();
const QSize desiredSize = isUnmapped() ? positioner.size() : size();
QSize desiredSize = size();
if (desiredSize.isEmpty()) {
desiredSize = positioner.size();
}
const QPoint parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());

View File

@ -85,11 +85,6 @@ public:
QRect clientGeometry() const;
bool isClosing() const;
bool isHidden() const;
bool isUnmapped() const;
Q_SIGNALS:
void windowMapped();
void windowUnmapped();
protected:
void addDamage(const QRegion &damage) override;
@ -116,8 +111,6 @@ private:
void updateDepth();
void internalShow();
void internalHide();
void internalMap();
void internalUnmap();
void cleanGrouping();
void cleanTabBox();
@ -132,7 +125,6 @@ private:
QRect m_clientGeometry;
bool m_isClosing = false;
bool m_isHidden = false;
bool m_isUnmapped = true;
bool m_haveNextWindowGeometry = false;
};
@ -152,6 +144,8 @@ public:
explicit XdgToplevelClient(KWaylandServer::XdgToplevelInterface *shellSurface);
~XdgToplevelClient() override;
KWaylandServer::XdgToplevelInterface *shellSurface() const;
void debug(QDebug &stream) const override;
NET::WindowType windowType(bool direct = false, int supported_types = 0) const override;
MaximizeMode maximizeMode() const override;