Commit 4f744d1b authored by Méven Car's avatar Méven Car
Browse files

Add TestOutputManagement::testOutputDeviceDisabled test

Allow VirtualBackend to supports Output changes.
parent 866dfb4e
...@@ -263,10 +263,10 @@ void AbstractWaylandOutput::initInterfaces(const QString &model, const QString & ...@@ -263,10 +263,10 @@ void AbstractWaylandOutput::initInterfaces(const QString &model, const QString &
{ {
m_waylandOutputDevice->setUuid(uuid); m_waylandOutputDevice->setUuid(uuid);
if (!manufacturer.isEmpty()) { if (manufacturer.isEmpty()) {
m_waylandOutputDevice->setManufacturer(manufacturer);
} else {
m_waylandOutputDevice->setManufacturer(i18n("unknown")); m_waylandOutputDevice->setManufacturer(i18n("unknown"));
} else {
m_waylandOutputDevice->setManufacturer(manufacturer);
} }
m_waylandOutputDevice->setEdid(edid); m_waylandOutputDevice->setEdid(edid);
......
...@@ -97,6 +97,7 @@ integrationTest(WAYLAND_ONLY NAME testPlacement SRCS placement_test.cpp) ...@@ -97,6 +97,7 @@ integrationTest(WAYLAND_ONLY NAME testPlacement SRCS placement_test.cpp)
integrationTest(WAYLAND_ONLY NAME testActivation SRCS activation_test.cpp) integrationTest(WAYLAND_ONLY NAME testActivation SRCS activation_test.cpp)
integrationTest(WAYLAND_ONLY NAME testInputMethod SRCS inputmethod_test.cpp) integrationTest(WAYLAND_ONLY NAME testInputMethod SRCS inputmethod_test.cpp)
integrationTest(WAYLAND_ONLY NAME testScreens SRCS screens_test.cpp) integrationTest(WAYLAND_ONLY NAME testScreens SRCS screens_test.cpp)
integrationTest(WAYLAND_ONLY NAME testOutputManagement SRCS outputmanagement_test.cpp)
if (KWIN_BUILD_CMS) if (KWIN_BUILD_CMS)
integrationTest(WAYLAND_ONLY NAME testNightColor SRCS nightcolor_test.cpp LIBS KWinNightColorPlugin) integrationTest(WAYLAND_ONLY NAME testNightColor SRCS nightcolor_test.cpp LIBS KWinNightColorPlugin)
......
...@@ -42,6 +42,7 @@ class Surface; ...@@ -42,6 +42,7 @@ class Surface;
class XdgDecorationManager; class XdgDecorationManager;
class OutputManagement; class OutputManagement;
class TextInputManager; class TextInputManager;
class OutputDevice;
} }
} }
...@@ -236,7 +237,8 @@ enum class AdditionalWaylandInterface { ...@@ -236,7 +237,8 @@ enum class AdditionalWaylandInterface {
TextInputManagerV2 = 1 << 10, TextInputManagerV2 = 1 << 10,
InputMethodV1 = 1 << 11, InputMethodV1 = 1 << 11,
LayerShellV1 = 1 << 12, LayerShellV1 = 1 << 12,
TextInputManagerV3 = 1 << 13 TextInputManagerV3 = 1 << 13,
OutputDevice = 1 << 14
}; };
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
/** /**
...@@ -271,6 +273,7 @@ KWayland::Client::XdgDecorationManager *xdgDecorationManager(); ...@@ -271,6 +273,7 @@ KWayland::Client::XdgDecorationManager *xdgDecorationManager();
KWayland::Client::OutputManagement *waylandOutputManagement(); KWayland::Client::OutputManagement *waylandOutputManagement();
KWayland::Client::TextInputManager *waylandTextInputManager(); KWayland::Client::TextInputManager *waylandTextInputManager();
QVector<KWayland::Client::Output *> waylandOutputs(); QVector<KWayland::Client::Output *> waylandOutputs();
QVector<KWayland::Client::OutputDevice *> waylandOutputDevices();
bool waitForWaylandPointer(); bool waitForWaylandPointer();
bool waitForWaylandTouch(); bool waitForWaylandTouch();
......
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 Méven Car <meven.car@enioka.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "abstract_client.h"
#include "abstract_wayland_output.h"
#include "deleted.h"
#include "platform.h"
#include "screens.h"
#include "wayland_server.h"
#include <KWayland/Client/outputmanagement.h>
#include <KWayland/Client/outputconfiguration.h>
#include <KWayland/Client/outputdevice.h>
#include <KWaylandServer/outputmanagement_interface.h>
#include <KWaylandServer/outputconfiguration_interface.h>
#include <KWaylandServer/outputdevice_interface.h>
#include <KWayland/Client/output.h>
#include <KWayland/Client/outputdevice.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/xdgshell.h>
#include <KWaylandServer/display.h>
using namespace KWin;
using namespace KWayland::Client;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_outputmanagement-0");
class TestOutputManagement : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testOutputDeviceDisabled();
};
void TestOutputManagement::initTestCase()
{
qRegisterMetaType<KWin::Deleted*>();
qRegisterMetaType<KWin::AbstractClient*>();
qRegisterMetaType<KWin::AbstractOutput*>();
qRegisterMetaType<KWin::AbstractOutput*>("AbstractOutput *");
qRegisterMetaType<KWayland::Client::Output*>();
qRegisterMetaType<KWayland::Client::OutputDevice::Enablement>();
qRegisterMetaType<OutputDevice::Enablement>("OutputDevice::Enablement");
qRegisterMetaType<KWaylandServer::OutputDeviceInterface::Enablement>();
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(applicationStartedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
QVERIFY(waylandServer()->init(s_socketName));
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
kwinApp()->start();
QVERIFY(applicationStartedSpy.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();
}
void TestOutputManagement::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::OutputManagement |
Test::AdditionalWaylandInterface::OutputDevice));
screens()->setCurrent(0);
//put mouse in the middle of screen one
KWin::Cursors::self()->mouse()->setPos(QPoint(512, 512));
}
void TestOutputManagement::cleanup()
{
Test::destroyWaylandConnection();
}
void TestOutputManagement::testOutputDeviceDisabled()
{
// This tests checks that OutputConfiguration::apply aka Platform::requestOutputsChange works as expected
// when disabling and enabling virtual OutputDevice
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
auto size = QSize(200,200);
QSignalSpy outputEnteredSpy(surface.data(), &Surface::outputEntered);
QSignalSpy outputLeftSpy(surface.data(), &Surface::outputLeft);
QSignalSpy outputEnabledSpy(kwinApp()->platform(), &Platform::outputEnabled);
QSignalSpy outputDisabledSpy(kwinApp()->platform(), &Platform::outputDisabled);
auto c = Test::renderAndWaitForShown(surface.data(), size, Qt::blue);
//move to be in the first screen
c->setFrameGeometry(QRect(QPoint(100,100), size));
//we don't don't know where the compositor first placed this window,
//this might fire, it might not
outputEnteredSpy.wait(5);
outputEnteredSpy.clear();
QCOMPARE(waylandServer()->display()->outputs().count(), 2);
QCOMPARE(surface->outputs().count(), 1);
Output *firstOutput = surface->outputs().first();
QCOMPARE(firstOutput->globalPosition(), QPoint(0,0));
QSignalSpy modesChangedSpy(firstOutput, &Output::modeChanged);
QSignalSpy screenChangedSpy(screens(), &KWin::Screens::changed);
OutputManagement *outManagement = Test::waylandOutputManagement();
auto outputDevices = Test::waylandOutputDevices();
QCOMPARE(outputDevices.count(), 2);
OutputDevice *device = outputDevices.first();
QCOMPARE(device->enabled(), OutputDevice::Enablement::Enabled);
QSignalSpy outputDeviceEnabledChangedSpy(device, &OutputDevice::enabledChanged);
OutputConfiguration *config;
// Disables an output
config = outManagement->createConfiguration();
QSignalSpy configAppliedSpy (config, &OutputConfiguration::applied);
config->setEnabled(device, OutputDevice::Enablement::Disabled);
config->apply();
QVERIFY(configAppliedSpy.wait());
QCOMPARE(outputDeviceEnabledChangedSpy.count(), 1);
QCOMPARE(device->enabled(), OutputDevice::Enablement::Disabled);
QCOMPARE(screenChangedSpy.count(), 3);
QCOMPARE(outputLeftSpy.count(), 1);
QCOMPARE(outputEnteredSpy.count(), 1); // surface was moved to other screen
QCOMPARE(surface->outputs().count(), 1);
QCOMPARE(screens()->count(), 1);
QCOMPARE(modesChangedSpy.count(), 0);
QCOMPARE(outputEnabledSpy.count(), 0);
QCOMPARE(outputDisabledSpy.count(), 1);
screenChangedSpy.clear();
outputLeftSpy.clear();
outputEnteredSpy.clear();
outputDeviceEnabledChangedSpy.clear();
outputEnabledSpy.clear();
outputDisabledSpy.clear();
// Enable the disabled output
config = outManagement->createConfiguration();
QSignalSpy configAppliedSpy2 (config, &OutputConfiguration::applied);
config->setEnabled(device, OutputDevice::Enablement::Enabled);
config->apply();
QVERIFY(configAppliedSpy2.wait());
QVERIFY(outputEnteredSpy.wait());
QCOMPARE(outputDeviceEnabledChangedSpy.count(), 1);
QCOMPARE(device->enabled(), OutputDevice::Enablement::Enabled);
QCOMPARE(screenChangedSpy.count(), 3);
QCOMPARE(outputLeftSpy.count(), 1);
QCOMPARE(outputEnteredSpy.count(), 1); // surface moved back to first screen
QCOMPARE(surface->outputs().count(), 1);
QCOMPARE(screens()->count(), 2);
QCOMPARE(modesChangedSpy.count(), 0);
QCOMPARE(outputEnabledSpy.count(), 1);
QCOMPARE(outputDisabledSpy.count(), 0);
}
WAYLANDTEST_MAIN(TestOutputManagement)
#include "outputmanagement_test.moc"
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <KWayland/Client/shadow.h> #include <KWayland/Client/shadow.h>
#include <KWayland/Client/shm_pool.h> #include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/output.h> #include <KWayland/Client/output.h>
#include <KWayland/Client/outputdevice.h>
#include <KWayland/Client/subcompositor.h> #include <KWayland/Client/subcompositor.h>
#include <KWayland/Client/subsurface.h> #include <KWayland/Client/subsurface.h>
#include <KWayland/Client/surface.h> #include <KWayland/Client/surface.h>
...@@ -200,6 +201,7 @@ static struct { ...@@ -200,6 +201,7 @@ static struct {
OutputManagement* outputManagement = nullptr; OutputManagement* outputManagement = nullptr;
QThread *thread = nullptr; QThread *thread = nullptr;
QVector<Output*> outputs; QVector<Output*> outputs;
QVector<OutputDevice*> outputDevices;
IdleInhibitManager *idleInhibit = nullptr; IdleInhibitManager *idleInhibit = nullptr;
AppMenuManager *appMenu = nullptr; AppMenuManager *appMenu = nullptr;
XdgDecorationManager *xdgDecoration = nullptr; XdgDecorationManager *xdgDecoration = nullptr;
...@@ -299,7 +301,7 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags) ...@@ -299,7 +301,7 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
registry->setEventQueue(s_waylandConnection.queue); registry->setEventQueue(s_waylandConnection.queue);
QObject::connect(registry, &Registry::outputAnnounced, [=](quint32 name, quint32 version) { QObject::connect(registry, &Registry::outputAnnounced, [=](quint32 name, quint32 version) {
auto output = registry->createOutput(name, version, s_waylandConnection.registry); Output* output = registry->createOutput(name, version, s_waylandConnection.registry);
s_waylandConnection.outputs << output; s_waylandConnection.outputs << output;
QObject::connect(output, &Output::removed, [=]() { QObject::connect(output, &Output::removed, [=]() {
output->deleteLater(); output->deleteLater();
...@@ -310,6 +312,22 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags) ...@@ -310,6 +312,22 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
}); });
}); });
if (flags.testFlag(AdditionalWaylandInterface::OutputDevice)) {
QObject::connect(registry, &KWayland::Client::Registry::outputDeviceAnnounced,
[=](quint32 name, quint32 version) {
OutputDevice *device = registry->createOutputDevice(name, version);
s_waylandConnection.outputDevices << device;
QObject::connect(device, &OutputDevice::removed, [=]() {
s_waylandConnection.outputDevices.removeOne(device);
});
QObject::connect(device, &OutputDevice::destroyed, [=]() {
s_waylandConnection.outputDevices.removeOne(device);
});
});
}
QObject::connect(registry, &Registry::interfaceAnnounced, [=](const QByteArray &interface, quint32 name, quint32 version) { QObject::connect(registry, &Registry::interfaceAnnounced, [=](const QByteArray &interface, quint32 name, quint32 version) {
if (flags & AdditionalWaylandInterface::InputMethodV1) { if (flags & AdditionalWaylandInterface::InputMethodV1) {
if (interface == QByteArrayLiteral("zwp_input_method_v1")) { if (interface == QByteArrayLiteral("zwp_input_method_v1")) {
...@@ -497,6 +515,8 @@ void destroyWaylandConnection() ...@@ -497,6 +515,8 @@ void destroyWaylandConnection()
s_waylandConnection.thread = nullptr; s_waylandConnection.thread = nullptr;
s_waylandConnection.connection = nullptr; s_waylandConnection.connection = nullptr;
} }
s_waylandConnection.outputs.clear();
s_waylandConnection.outputDevices.clear();
} }
ConnectionThread *waylandConnection() ConnectionThread *waylandConnection()
...@@ -584,6 +604,11 @@ QVector<KWayland::Client::Output *> waylandOutputs() ...@@ -584,6 +604,11 @@ QVector<KWayland::Client::Output *> waylandOutputs()
return s_waylandConnection.outputs; return s_waylandConnection.outputs;
} }
QVector<OutputDevice *> waylandOutputDevices()
{
return s_waylandConnection.outputDevices;
}
bool waitForWaylandPointer() bool waitForWaylandPointer()
{ {
if (!s_waylandConnection.seat) { if (!s_waylandConnection.seat) {
......
...@@ -122,16 +122,19 @@ void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationInterface ...@@ -122,16 +122,19 @@ void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationInterface
for (auto it = changes.begin(); it != changes.end(); it++) { for (auto it = changes.begin(); it != changes.end(); it++) {
const KWaylandServer::OutputChangeSet *changeset = it.value(); const KWaylandServer::OutputChangeSet *changeset = it.value();
auto output = findOutput(it.key()->uuid()); AbstractOutput* output = findOutput(it.key()->uuid());
if (!output) { if (!output) {
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid(); qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
continue; continue;
} }
qDebug(KWIN_CORE) << "Platform::requestOutputsChange enabling" << changeset << it.key()->uuid() << changeset->enabledChanged() << (changeset->enabled() == Enablement::Enabled);
if (changeset->enabledChanged() && if (changeset->enabledChanged() &&
changeset->enabled() == Enablement::Enabled) { changeset->enabled() == Enablement::Enabled) {
output->setEnabled(true); output->setEnabled(true);
} }
output->applyChanges(changeset); output->applyChanges(changeset);
} }
...@@ -152,9 +155,11 @@ void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationInterface ...@@ -152,9 +155,11 @@ void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationInterface
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid(); qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
continue; continue;
} }
qDebug(KWIN_CORE) << "Platform::requestOutputsChange disabling false" << it.key()->uuid();
output->setEnabled(false); output->setEnabled(false);
} }
} }
emit screens()->changed(); emit screens()->changed();
config->setApplied(); config->setApplied();
} }
......
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "screens_virtual.h"
#include "virtual_backend.h"
#include "virtual_output.h"
namespace KWin
{
VirtualScreens::VirtualScreens(VirtualBackend *backend, QObject *parent)
: OutputScreens(backend, parent)
, m_backend(backend)
{
connect(backend, &VirtualBackend::screensQueried, this, &VirtualScreens::updateCount);
connect(backend, &VirtualBackend::screensQueried, this, &VirtualScreens::changed);
}
VirtualScreens::~VirtualScreens() = default;
void VirtualScreens::init()
{
updateCount();
KWin::Screens::init();
connect(m_backend, &VirtualBackend::virtualOutputsSet, this,
[this] (bool countChanged) {
if (countChanged) {
setCount(m_backend->outputs().size());
} else {
emit changed();
}
}
);
emit changed();
}
}
...@@ -35,6 +35,8 @@ VirtualBackend::VirtualBackend(QObject *parent) ...@@ -35,6 +35,8 @@ VirtualBackend::VirtualBackend(QObject *parent)
qDebug() << "Screenshots saved to: " << m_screenshotDir->path(); qDebug() << "Screenshots saved to: " << m_screenshotDir->path();
} }
} }
supportsOutputChanges();
setSupportsPointerWarping(true); setSupportsPointerWarping(true);
setSupportsGammaControl(true); setSupportsGammaControl(true);
setPerScreenRenderingEnabled(true); setPerScreenRenderingEnabled(true);
...@@ -97,7 +99,7 @@ Outputs VirtualBackend::outputs() const ...@@ -97,7 +99,7 @@ Outputs VirtualBackend::outputs() const
Outputs VirtualBackend::enabledOutputs() const Outputs VirtualBackend::enabledOutputs() const
{ {
return m_outputs; return m_outputsEnabled;
} }
void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVector<int> scales) void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVector<int> scales)
...@@ -105,9 +107,15 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe ...@@ -105,9 +107,15 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe
Q_ASSERT(geometries.size() == 0 || geometries.size() == count); Q_ASSERT(geometries.size() == 0 || geometries.size() == count);
Q_ASSERT(scales.size() == 0 || scales.size() == count); Q_ASSERT(scales.size() == 0 || scales.size() == count);
bool countChanged = m_outputs.size() != count;
while (!m_outputsEnabled.isEmpty()) {
VirtualOutput *output = m_outputsEnabled.takeLast();
emit outputDisabled(output);
}
while (!m_outputs.isEmpty()) { while (!m_outputs.isEmpty()) {
VirtualOutput *output = m_outputs.takeLast(); VirtualOutput *output = m_outputs.takeLast();
emit outputDisabled(output);
emit outputRemoved(output); emit outputRemoved(output);
delete output; delete output;
} }
...@@ -126,6 +134,7 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe ...@@ -126,6 +134,7 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe
vo->setScale(scales.at(i)); vo->setScale(scales.at(i));
} }
m_outputs.append(vo); m_outputs.append(vo);
m_outputsEnabled.append(vo);
emit outputAdded(vo); emit outputAdded(vo);
emit outputEnabled(vo); emit outputEnabled(vo);
} }
...@@ -133,4 +142,37 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe ...@@ -133,4 +142,37 @@ void VirtualBackend::setVirtualOutputs(int count, QVector<QRect> geometries, QVe
emit screensQueried(); emit screensQueried();
} }
void VirtualBackend::enableOutput(VirtualOutput *output, bool enable)
{
if (enable) {
Q_ASSERT(!m_outputsEnabled.contains(output));
m_outputsEnabled << output;
emit outputEnabled(output);
} else {
Q_ASSERT(m_outputsEnabled.contains(output));
m_outputsEnabled.removeOne(output);
emit outputDisabled(output);
}
emit screensQueried();
}
void VirtualBackend::removeOutput(AbstractOutput *output)
{
VirtualOutput* virtualOutput = static_cast<VirtualOutput *>(output);
if (m_outputsEnabled.removeOne(virtualOutput)) {
emit outputDisabled(virtualOutput);
}
emit outputRemoved(virtualOutput);
m_outputsEnabled.removeOne(virtualOutput);
delete virtualOutput;
emit screensQueried();
}
} }
...@@ -52,11 +52,16 @@ public: ...@@ -52,11 +52,16 @@ public:
return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing}; return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing};
} }
void enableOutput(VirtualOutput *output, bool enable);
void removeOutput(AbstractOutput *output);
Q_SIGNALS: Q_SIGNALS:
void virtualOutputsSet(bool countChanged); void virtualOutputsSet(bool countChanged);
private: private:
QVector<VirtualOutput*> m_outputs; QVector<VirtualOutput*> m_outputs;
QVector<VirtualOutput*> m_outputsEnabled;
QScopedPointer<QTemporaryDir> m_screenshotDir; QScopedPointer<QTemporaryDir> m_screenshotDir;
}; };
......
...@@ -7,23 +7,24 @@ ...@@ -7,23 +7,24 @@
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later
*/ */
#include "virtual_output.h" #include "virtual_output.h"
#include "virtual_backend.h"
#include "renderloop_p.h" #include "renderloop_p.h"
#include "softwarevsyncmonitor.h" #include "softwarevsyncmonitor.h"
namespace KWin namespace KWin
{ {
VirtualOutput::VirtualOutput(QObject *parent) VirtualOutput::VirtualOutput(VirtualBackend *parent)
: AbstractWaylandOutput() : AbstractWaylandOutput(parent)
, m_backend(parent)
, m_renderLoop(new RenderLoop(this)) , m_renderLoop(new RenderLoop(this))
, m_vsyncMonitor(SoftwareVsyncMonitor::create(this)) , m_vsyncMonitor(SoftwareVsyncMonitor::create(this))