[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
parent
31ea780d79
commit
df9e36ee68
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
13
effects.cpp
13
effects.cpp
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue