File indexing completed on 2024-11-10 04:55:56

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "compositor.h"
0012 #include "effect/effecthandler.h"
0013 #include "effect/effectloader.h"
0014 #include "pointer_input.h"
0015 #include "virtualdesktops.h"
0016 #include "wayland_server.h"
0017 #include "workspace.h"
0018 #include "x11window.h"
0019 
0020 #include <KConfigGroup>
0021 
0022 #include <netwm.h>
0023 #include <xcb/xcb_icccm.h>
0024 
0025 using namespace KWin;
0026 static const QString s_socketName = QStringLiteral("wayland_test_effects_translucency-0");
0027 
0028 class TranslucencyTest : public QObject
0029 {
0030     Q_OBJECT
0031 private Q_SLOTS:
0032     void initTestCase();
0033     void init();
0034     void cleanup();
0035 
0036     void testMoveAfterDesktopChange();
0037     void testDialogClose();
0038 
0039 private:
0040     Effect *m_translucencyEffect = nullptr;
0041 };
0042 
0043 void TranslucencyTest::initTestCase()
0044 {
0045     qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
0046     qRegisterMetaType<KWin::Window *>();
0047     qRegisterMetaType<KWin::Effect *>();
0048     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0049     QVERIFY(waylandServer()->init(s_socketName));
0050     Test::setOutputConfig({
0051         QRect(0, 0, 1280, 1024),
0052         QRect(1280, 0, 1280, 1024),
0053     });
0054 
0055     // disable all effects - we don't want to have it interact with the rendering
0056     auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0057     KConfigGroup plugins(config, QStringLiteral("Plugins"));
0058     const auto builtinNames = EffectLoader().listOfKnownEffects();
0059     for (QString name : builtinNames) {
0060         plugins.writeEntry(name + QStringLiteral("Enabled"), false);
0061     }
0062     config->group(QStringLiteral("Outline")).writeEntry(QStringLiteral("QmlPath"), QString("/does/not/exist.qml"));
0063     config->group(QStringLiteral("Effect-translucency")).writeEntry(QStringLiteral("Dialogs"), 90);
0064 
0065     config->sync();
0066     kwinApp()->setConfig(config);
0067 
0068     qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", "1");
0069     kwinApp()->start();
0070     QVERIFY(applicationStartedSpy.wait());
0071     QVERIFY(Compositor::self());
0072 }
0073 
0074 void TranslucencyTest::init()
0075 {
0076     // find the effectsloader
0077     auto effectloader = effects->findChild<AbstractEffectLoader *>();
0078     QVERIFY(effectloader);
0079     QSignalSpy effectLoadedSpy(effectloader, &AbstractEffectLoader::effectLoaded);
0080 
0081     QVERIFY(!effects->isEffectLoaded(QStringLiteral("translucency")));
0082     QVERIFY(effects->loadEffect(QStringLiteral("translucency")));
0083     QVERIFY(effects->isEffectLoaded(QStringLiteral("translucency")));
0084 
0085     QCOMPARE(effectLoadedSpy.count(), 1);
0086     m_translucencyEffect = effectLoadedSpy.first().first().value<Effect *>();
0087     QVERIFY(m_translucencyEffect);
0088 }
0089 
0090 void TranslucencyTest::cleanup()
0091 {
0092     if (effects->isEffectLoaded(QStringLiteral("translucency"))) {
0093         effects->unloadEffect(QStringLiteral("translucency"));
0094     }
0095     QVERIFY(!effects->isEffectLoaded(QStringLiteral("translucency")));
0096     m_translucencyEffect = nullptr;
0097 }
0098 
0099 void TranslucencyTest::testMoveAfterDesktopChange()
0100 {
0101     // test tries to simulate the condition of bug 366081
0102     QVERIFY(!m_translucencyEffect->isActive());
0103 
0104     QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
0105 
0106     // create an xcb window
0107     Test::XcbConnectionPtr c = Test::createX11Connection();
0108     QVERIFY(!xcb_connection_has_error(c.get()));
0109     const QRect windowGeometry(0, 0, 100, 200);
0110     xcb_window_t windowId = xcb_generate_id(c.get());
0111     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0112                       windowGeometry.x(),
0113                       windowGeometry.y(),
0114                       windowGeometry.width(),
0115                       windowGeometry.height(),
0116                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0117     xcb_size_hints_t hints;
0118     memset(&hints, 0, sizeof(hints));
0119     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0120     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0121     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0122     xcb_map_window(c.get(), windowId);
0123     xcb_flush(c.get());
0124 
0125     // we should get a window for it
0126     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0127     QVERIFY(windowCreatedSpy.wait());
0128     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0129     QVERIFY(window);
0130     QCOMPARE(window->window(), windowId);
0131     QVERIFY(window->isDecorated());
0132 
0133     QCOMPARE(windowAddedSpy.count(), 1);
0134     QVERIFY(!m_translucencyEffect->isActive());
0135     // let's send the window to desktop 2
0136     VirtualDesktopManager *vds = VirtualDesktopManager::self();
0137     vds->setCount(2);
0138     const QList<VirtualDesktop *> desktops = vds->desktops();
0139     workspace()->sendWindowToDesktops(window, {desktops[1]}, false);
0140     vds->setCurrent(desktops[1]);
0141     QVERIFY(!m_translucencyEffect->isActive());
0142     KWin::input()->pointer()->warp(window->frameGeometry().center());
0143     workspace()->performWindowOperation(window, Options::MoveOp);
0144     QVERIFY(m_translucencyEffect->isActive());
0145     QTest::qWait(200);
0146     QVERIFY(m_translucencyEffect->isActive());
0147     // now end move resize
0148     window->endInteractiveMoveResize();
0149     QVERIFY(m_translucencyEffect->isActive());
0150     QTest::qWait(500);
0151     QTRY_VERIFY(!m_translucencyEffect->isActive());
0152 
0153     // and destroy the window again
0154     xcb_unmap_window(c.get(), windowId);
0155     xcb_flush(c.get());
0156 
0157     QSignalSpy windowClosedSpy(window, &X11Window::closed);
0158     QVERIFY(windowClosedSpy.wait());
0159     xcb_destroy_window(c.get(), windowId);
0160     c.reset();
0161 }
0162 
0163 void TranslucencyTest::testDialogClose()
0164 {
0165     // this test simulates the condition of BUG 342716
0166     // with translucency settings for window type dialog the effect never ends when the window gets destroyed
0167     QVERIFY(!m_translucencyEffect->isActive());
0168     QSignalSpy windowAddedSpy(effects, &EffectsHandler::windowAdded);
0169 
0170     // create an xcb window
0171     Test::XcbConnectionPtr c = Test::createX11Connection();
0172     QVERIFY(!xcb_connection_has_error(c.get()));
0173     const QRect windowGeometry(0, 0, 100, 200);
0174     xcb_window_t windowId = xcb_generate_id(c.get());
0175     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0176                       windowGeometry.x(),
0177                       windowGeometry.y(),
0178                       windowGeometry.width(),
0179                       windowGeometry.height(),
0180                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0181     xcb_size_hints_t hints;
0182     memset(&hints, 0, sizeof(hints));
0183     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0184     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0185     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0186     NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::Properties(), NET::Properties2());
0187     winInfo.setWindowType(NET::Dialog);
0188     xcb_map_window(c.get(), windowId);
0189     xcb_flush(c.get());
0190 
0191     // we should get a window for it
0192     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0193     QVERIFY(windowCreatedSpy.wait());
0194     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0195     QVERIFY(window);
0196     QCOMPARE(window->window(), windowId);
0197     QVERIFY(window->isDecorated());
0198     QVERIFY(window->isDialog());
0199 
0200     QCOMPARE(windowAddedSpy.count(), 1);
0201     QTRY_VERIFY(m_translucencyEffect->isActive());
0202     // and destroy the window again
0203     xcb_unmap_window(c.get(), windowId);
0204     xcb_flush(c.get());
0205 
0206     QSignalSpy windowClosedSpy(window, &X11Window::closed);
0207 
0208     QSignalSpy windowDeletedSpy(effects, &EffectsHandler::windowDeleted);
0209     QVERIFY(windowClosedSpy.wait());
0210     if (windowDeletedSpy.isEmpty()) {
0211         QVERIFY(windowDeletedSpy.wait());
0212     }
0213     QCOMPARE(windowDeletedSpy.count(), 1);
0214     QTRY_VERIFY(!m_translucencyEffect->isActive());
0215     xcb_destroy_window(c.get(), windowId);
0216     c.reset();
0217 }
0218 
0219 WAYLANDTEST_MAIN(TranslucencyTest)
0220 #include "translucency_test.moc"