Add TestOutputManagement::testOutputDeviceDisabled test
Allow VirtualBackend to supports Output changes.
This commit is contained in:
parent
866dfb4e89
commit
4f744d1bb6
11 changed files with 329 additions and 15 deletions
|
@ -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)
|
||||||
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;
|
||||||
class XdgDecorationManager;
|
class XdgDecorationManager;
|
||||||
class OutputManagement;
|
class OutputManagement;
|
||||||
class TextInputManager;
|
class TextInputManager;
|
||||||
|
class OutputDevice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
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();
|
||||||
|
|
176
autotests/integration/outputmanagement_test.cpp
Normal file
176
autotests/integration/outputmanagement_test.cpp
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
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 @@
|
||||||
#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 {
|
||||||
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)
|
||||||
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)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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()
|
||||||
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()
|
||||||
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
|
||||||
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
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
44
plugins/platforms/virtual/screens_virtual.cpp
Normal file
44
plugins/platforms/virtual/screens_virtual.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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)
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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:
|
||||||
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 @@
|
||||||
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))
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
|
||||||
|
|
||||||
connect(m_vsyncMonitor, &VsyncMonitor::vblankOccurred, this, &VirtualOutput::vblank);
|
connect(m_vsyncMonitor, &VsyncMonitor::vblankOccurred, this, &VirtualOutput::vblank);
|
||||||
|
|
||||||
static int identifier = -1;
|
static int identifier = -1;
|
||||||
identifier++;
|
m_identifier = ++identifier;
|
||||||
setName("Virtual-" + QString::number(identifier));
|
setName("Virtual-" + QString::number(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +53,10 @@ void VirtualOutput::init(const QPoint &logicalPosition, const QSize &pixelSize)
|
||||||
mode.size = pixelSize;
|
mode.size = pixelSize;
|
||||||
mode.flags = KWaylandServer::OutputDeviceInterface::ModeFlag::Current;
|
mode.flags = KWaylandServer::OutputDeviceInterface::ModeFlag::Current;
|
||||||
mode.refreshRate = refreshRate;
|
mode.refreshRate = refreshRate;
|
||||||
initInterfaces("model_TODO", "manufacturer_TODO", "UUID_TODO", pixelSize, { mode }, {});
|
initInterfaces(QByteArray("model_").append(QString::number(m_identifier)),
|
||||||
|
QByteArray("manufacturer_").append(QString::number(m_identifier)),
|
||||||
|
QByteArray("UUID_").append(QString::number(m_identifier)),
|
||||||
|
pixelSize, { mode }, QByteArray("EDID_").append(QString::number(m_identifier)));
|
||||||
setGeometry(QRect(logicalPosition, pixelSize));
|
setGeometry(QRect(logicalPosition, pixelSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,4 +72,9 @@ void VirtualOutput::vblank(std::chrono::nanoseconds timestamp)
|
||||||
renderLoopPrivate->notifyFrameCompleted(timestamp);
|
renderLoopPrivate->notifyFrameCompleted(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VirtualOutput::updateEnablement(bool enable)
|
||||||
|
{
|
||||||
|
m_backend->enableOutput(this, enable);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class VirtualOutput : public AbstractWaylandOutput
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VirtualOutput(QObject *parent = nullptr);
|
VirtualOutput(VirtualBackend *parent = nullptr);
|
||||||
~VirtualOutput() override;
|
~VirtualOutput() override;
|
||||||
|
|
||||||
RenderLoop *renderLoop() const override;
|
RenderLoop *renderLoop() const override;
|
||||||
|
@ -43,16 +43,20 @@ public:
|
||||||
return m_gammaResult;
|
return m_gammaResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateEnablement(bool enable) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void vblank(std::chrono::nanoseconds timestamp);
|
void vblank(std::chrono::nanoseconds timestamp);
|
||||||
|
|
||||||
Q_DISABLE_COPY(VirtualOutput);
|
Q_DISABLE_COPY(VirtualOutput);
|
||||||
friend class VirtualBackend;
|
friend class VirtualBackend;
|
||||||
|
|
||||||
|
VirtualBackend *m_backend;
|
||||||
RenderLoop *m_renderLoop;
|
RenderLoop *m_renderLoop;
|
||||||
SoftwareVsyncMonitor *m_vsyncMonitor;
|
SoftwareVsyncMonitor *m_vsyncMonitor;
|
||||||
int m_gammaSize = 200;
|
int m_gammaSize = 200;
|
||||||
bool m_gammaResult = true;
|
bool m_gammaResult = true;
|
||||||
|
int m_identifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue