diff --git a/autotests/integration/scripting/CMakeLists.txt b/autotests/integration/scripting/CMakeLists.txt --- a/autotests/integration/scripting/CMakeLists.txt +++ b/autotests/integration/scripting/CMakeLists.txt @@ -1 +1,2 @@ integrationTest(NAME testScriptingScreenEdge SRCS screenedge_test.cpp) +integrationTest(WAYLAND_ONLY NAME testMinimizeAllScript SRCS minimizeall_test.cpp) diff --git a/autotests/integration/scripting/minimizeall_test.cpp b/autotests/integration/scripting/minimizeall_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/integration/scripting/minimizeall_test.cpp @@ -0,0 +1,170 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zahorodnii + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +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 . +*********************************************************************/ + +#include "kwin_wayland_test.h" + +#include "platform.h" +#include "screens.h" +#include "scripting/scripting.h" +#include "shell_client.h" +#include "wayland_server.h" +#include "workspace.h" + +#include +#include + +#include + +namespace KWin +{ + +static const QString s_socketName = QStringLiteral("wayland_test_minimizeall-0"); +static const QString s_scriptName = QStringLiteral("minimizeall"); + +class MinimizeAllScriptTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testMinimizeUnminimize(); +}; + +void MinimizeAllScriptTest::initTestCase() +{ + qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8()); + + qRegisterMetaType(); + qRegisterMetaType(); + + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2)); + + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + QCOMPARE(screens()->count(), 2); + QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); + QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); + waylandServer()->initWorkspace(); +} + +static QString locateMainScript(const QString &pluginName) +{ + const QList offers = KPackage::PackageLoader::self()->findPackages( + QStringLiteral("KWin/Script"), + QStringLiteral("kwin/scripts"), + [&](const KPluginMetaData &metaData) { + return metaData.pluginId() == pluginName; + } + ); + if (offers.isEmpty()) { + return QString(); + } + const KPluginMetaData &metaData = offers.first(); + const QString mainScriptFileName = metaData.value(QStringLiteral("X-Plasma-MainScript")); + const QFileInfo metaDataFileInfo(metaData.fileName()); + return metaDataFileInfo.path() + QLatin1String("/contents/") + mainScriptFileName; +} + +void MinimizeAllScriptTest::init() +{ + QVERIFY(Test::setupWaylandConnection()); + + Scripting::self()->loadScript(locateMainScript(s_scriptName), s_scriptName); + QTRY_VERIFY(Scripting::self()->isScriptLoaded(s_scriptName)); + + AbstractScript *script = Scripting::self()->findScript(s_scriptName); + QVERIFY(script); + QSignalSpy runningChangedSpy(script, &AbstractScript::runningChanged); + QVERIFY(runningChangedSpy.isValid()); + script->run(); + QTRY_COMPARE(runningChangedSpy.count(), 1); +} + +void MinimizeAllScriptTest::cleanup() +{ + Test::destroyWaylandConnection(); + + Scripting::self()->unloadScript(s_scriptName); + QTRY_VERIFY(!Scripting::self()->isScriptLoaded(s_scriptName)); +} + +void MinimizeAllScriptTest::testMinimizeUnminimize() +{ + // This test verifies that all windows are minimized when Meta+Shift+D + // is pressed, and unminimized when the shortcut is pressed once again. + + using namespace KWayland::Client; + + // Create a couple of test clients. + QScopedPointer surface1(Test::createSurface()); + QScopedPointer shellSurface1(Test::createXdgShellStableSurface(surface1.data())); + ShellClient *client1 = Test::renderAndWaitForShown(surface1.data(), QSize(100, 50), Qt::blue); + QVERIFY(client1); + QVERIFY(client1->isActive()); + QVERIFY(client1->isMinimizable()); + + QScopedPointer surface2(Test::createSurface()); + QScopedPointer shellSurface2(Test::createXdgShellStableSurface(surface2.data())); + ShellClient *client2 = Test::renderAndWaitForShown(surface2.data(), QSize(100, 50), Qt::red); + QVERIFY(client2); + QVERIFY(client2->isActive()); + QVERIFY(client2->isMinimizable()); + + // Minimize the windows. + quint32 timestamp = 1; + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_D, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_D, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); + + QTRY_VERIFY(client1->isMinimized()); + QTRY_VERIFY(client2->isMinimized()); + + // Unminimize the windows. + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_D, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_D, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); + + QTRY_VERIFY(!client1->isMinimized()); + QTRY_VERIFY(!client2->isMinimized()); + + // Destroy test clients. + shellSurface2.reset(); + QVERIFY(Test::waitForWindowDestroyed(client2)); + shellSurface1.reset(); + QVERIFY(Test::waitForWindowDestroyed(client1)); +} + +} + +WAYLANDTEST_MAIN(KWin::MinimizeAllScriptTest) +#include "minimizeall_test.moc" diff --git a/scripting/meta.h b/scripting/meta.h --- a/scripting/meta.h +++ b/scripting/meta.h @@ -30,10 +30,12 @@ class QSize; namespace KWin { +class AbstractClient; class Client; class Toplevel; } +typedef KWin::AbstractClient *KAbstractClientRef; typedef KWin::Client* KClientRef; typedef KWin::Toplevel* KToplevelRef; @@ -75,6 +77,12 @@ void fromScriptValue(const QScriptValue&, QRect&); } +namespace AbstractClient +{ +QScriptValue toScriptValue(QScriptEngine *engine, const KAbstractClientRef &client); +void fromScriptValue(const QScriptValue &value, KAbstractClientRef &client); +} + namespace Client { QScriptValue toScriptValue(QScriptEngine *eng, const KClientRef &client); diff --git a/scripting/meta.cpp b/scripting/meta.cpp --- a/scripting/meta.cpp +++ b/scripting/meta.cpp @@ -96,6 +96,20 @@ } // End of meta for QRect object +QScriptValue AbstractClient::toScriptValue(QScriptEngine *engine, const KAbstractClientRef &client) +{ + return engine->newQObject(client, QScriptEngine::QtOwnership, + QScriptEngine::ExcludeChildObjects | + QScriptEngine::ExcludeDeleteLater | + QScriptEngine::PreferExistingWrapperObject | + QScriptEngine::AutoCreateDynamicProperties); +} + +void AbstractClient::fromScriptValue(const QScriptValue &value, KWin::AbstractClient *&client) +{ + client = qobject_cast(value.toQObject()); +} + QScriptValue Client::toScriptValue(QScriptEngine *eng, const KClientRef &client) { return eng->newQObject(client, QScriptEngine::QtOwnership, @@ -130,6 +144,7 @@ qScriptRegisterMetaType(eng, Point::toScriptValue, Point::fromScriptValue); qScriptRegisterMetaType(eng, Size::toScriptValue, Size::fromScriptValue); qScriptRegisterMetaType(eng, Rect::toScriptValue, Rect::fromScriptValue); + qScriptRegisterMetaType(eng, AbstractClient::toScriptValue, AbstractClient::fromScriptValue); qScriptRegisterMetaType(eng, Client::toScriptValue, Client::fromScriptValue); qScriptRegisterMetaType(eng, Toplevel::toScriptValue, Toplevel::fromScriptValue); diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -1,4 +1,12 @@ -kpackage_install_package(videowall videowall scripts kwin) -kpackage_install_package(synchronizeskipswitcher synchronizeskipswitcher scripts kwin) -kpackage_install_package(desktopchangeosd desktopchangeosd scripts kwin) -kpackage_install_package(minimizeall minimizeall scripts kwin) +function(add_kwin_script name) + kpackage_install_package(${name} ${name} scripts kwin) + + # Copy the script to the build directory so one can run tests without prior + # make install. FIXME: use add_custom_command. + file(COPY ${name} DESTINATION ${CMAKE_BINARY_DIR}/bin/kwin/scripts/) +endfunction() + +add_kwin_script(videowall) +add_kwin_script(synchronizeskipswitcher) +add_kwin_script(desktopchangeosd) +add_kwin_script(minimizeall)