diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ce8635399..3f0df8b26c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -128,13 +128,14 @@ set_package_properties(epoxy PROPERTIES DESCRIPTION "libepoxy"
PURPOSE "OpenGL dispatch library"
)
-find_package(Wayland 1.2 COMPONENTS Client Egl Cursor)
+find_package(Wayland 1.2 COMPONENTS Client Egl Cursor Server)
set_package_properties(Wayland PROPERTIES
TYPE OPTIONAL
PURPOSE "Required for building KWin with Wayland support"
)
add_feature_info("Wayland-Client" Wayland_Client_FOUND "Required for building the Wayland backend in KWin")
add_feature_info("Wayland-EGL" Wayland_Egl_FOUND "Required for building the Wayland EGL compositing backend in KWin")
+add_feature_info("Wayland-Server" Wayland_Server_FOUND "Required for building Wayland server backend in KWin")
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
add_feature_info("wayland-scanner" WAYLAND_SCANNER_EXECUTABLE "Required for generating Wayland protocols")
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index 4bf1cac8a5..74fbb7cc2f 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -1,7 +1,8 @@
add_definitions(-DKWIN_UNIT_TEST)
-if(Wayland_Client_FOUND AND XKB_FOUND)
+if(Wayland_Client_FOUND AND XKB_FOUND AND Wayland_Server_FOUND)
add_subdirectory(wayland_client)
+ add_subdirectory(wayland_server)
endif()
########################################################
diff --git a/autotests/wayland_client/CMakeLists.txt b/autotests/wayland_client/CMakeLists.txt
index 130cd4eaa5..cd6ef85c71 100644
--- a/autotests/wayland_client/CMakeLists.txt
+++ b/autotests/wayland_client/CMakeLists.txt
@@ -3,9 +3,14 @@ set_source_files_properties(${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client
########################################################
# Test WaylandConnectionThread
########################################################
-set( testWaylandConnectionThread_SRCS test_wayland_connection_thread.cpp ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp )
+set( testWaylandConnectionThread_SRCS
+ test_wayland_connection_thread.cpp
+ ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp
+ ${KWIN_SOURCE_DIR}/wayland_server/display.cpp
+ ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp
+ )
add_executable(testWaylandConnectionThread ${testWaylandConnectionThread_SRCS})
-target_link_libraries( testWaylandConnectionThread Qt5::Test Wayland::Client)
+target_link_libraries( testWaylandConnectionThread Qt5::Test Wayland::Client Wayland::Server)
add_test(kwin-testWaylandConnectionThread testWaylandConnectionThread)
ecm_mark_as_test(testWaylandConnectionThread)
@@ -52,10 +57,12 @@ set( testWaylandOutput_SRCS
${KWIN_SOURCE_DIR}/wayland_client/fullscreen_shell.cpp
${KWIN_SOURCE_DIR}/wayland_client/output.cpp
${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c
+ ${KWIN_SOURCE_DIR}/wayland_server/display.cpp
+ ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp
)
add_executable(testWaylandOutput ${testWaylandOutput_SRCS})
add_dependencies(testWaylandOutput wayland-client-fullscreen-shell)
-target_link_libraries( testWaylandOutput Qt5::Test Wayland::Client)
+target_link_libraries( testWaylandOutput Qt5::Test Wayland::Client Wayland::Server)
add_test(kwin-testWaylandOutput testWaylandOutput)
ecm_mark_as_test(testWaylandOutput)
diff --git a/autotests/wayland_client/test_wayland_connection_thread.cpp b/autotests/wayland_client/test_wayland_connection_thread.cpp
index c045e5cfcd..c60b63c282 100644
--- a/autotests/wayland_client/test_wayland_connection_thread.cpp
+++ b/autotests/wayland_client/test_wayland_connection_thread.cpp
@@ -21,6 +21,7 @@ along with this program. If not, see .
#include
// KWin
#include "../../wayland_client/connection_thread.h"
+#include "../../wayland_server/display.h"
// Wayland
#include
@@ -39,60 +40,36 @@ private Q_SLOTS:
void testConnectionThread();
private:
- QProcess *m_westonProcess;
+ KWin::WaylandServer::Display *m_display;
};
static const QString s_socketName = QStringLiteral("kwin-test-wayland-connection-0");
TestWaylandConnectionThread::TestWaylandConnectionThread(QObject *parent)
: QObject(parent)
- , m_westonProcess(nullptr)
+ , m_display(nullptr)
{
}
void TestWaylandConnectionThread::init()
{
- QVERIFY(!m_westonProcess);
- // starts weston
- m_westonProcess = new QProcess(this);
- m_westonProcess->setProgram(QStringLiteral("weston"));
-
- m_westonProcess->setArguments(QStringList({QStringLiteral("--socket=%1").arg(s_socketName), QStringLiteral("--use-pixman")}));
- m_westonProcess->start();
- QVERIFY(m_westonProcess->waitForStarted());
-
- // wait for the socket to appear
- QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR"));
- if (runtimeDir.exists(s_socketName)) {
- return;
- }
- QFileSystemWatcher *socketWatcher = new QFileSystemWatcher(QStringList({runtimeDir.absolutePath()}), this);
- QSignalSpy socketSpy(socketWatcher, SIGNAL(directoryChanged(QString)));
-
- // limit to maximum of 10 waits
- for (int i = 0; i < 10; ++i) {
- QVERIFY(socketSpy.wait());
- if (runtimeDir.exists(s_socketName)) {
- delete socketWatcher;
- return;
- }
- }
+ using namespace KWin::WaylandServer;
+ delete m_display;
+ m_display = new Display(this);
+ QSignalSpy displayRunning(m_display, SIGNAL(runningChanged(bool)));
+ m_display->setSocketName(s_socketName);
+ m_display->start();
+ QVERIFY(m_display->isRunning());
}
void TestWaylandConnectionThread::cleanup()
{
- // terminates weston
- m_westonProcess->terminate();
- QVERIFY(m_westonProcess->waitForFinished());
- delete m_westonProcess;
- m_westonProcess = nullptr;
+ delete m_display;
+ m_display = nullptr;
}
void TestWaylandConnectionThread::testInitConnectionNoThread()
{
- if (m_westonProcess->state() != QProcess::Running) {
- QSKIP("This test requires a running wayland server");
- }
QScopedPointer connection(new KWin::Wayland::ConnectionThread);
QCOMPARE(connection->socketName(), QStringLiteral("wayland-0"));
connection->setSocketName(s_socketName);
@@ -109,9 +86,6 @@ void TestWaylandConnectionThread::testInitConnectionNoThread()
void TestWaylandConnectionThread::testConnectionFailure()
{
- if (m_westonProcess->state() != QProcess::Running) {
- QSKIP("This test requires a running wayland server");
- }
QScopedPointer connection(new KWin::Wayland::ConnectionThread);
connection->setSocketName(QStringLiteral("kwin-test-socket-does-not-exist"));
@@ -148,9 +122,6 @@ static const struct wl_registry_listener s_registryListener = {
void TestWaylandConnectionThread::testConnectionThread()
{
- if (m_westonProcess->state() != QProcess::Running) {
- QSKIP("This test requires a running wayland server");
- }
QScopedPointer connection(new KWin::Wayland::ConnectionThread);
connection->setSocketName(s_socketName);
@@ -177,7 +148,10 @@ void TestWaylandConnectionThread::testConnectionThread()
wl_registry_add_listener(registry, &s_registryListener, this);
wl_display_flush(display);
- QVERIFY(eventsSpy.wait());
+ if (eventsSpy.isEmpty()) {
+ QVERIFY(eventsSpy.wait());
+ }
+ QVERIFY(!eventsSpy.isEmpty());
wl_registry_destroy(registry);
wl_event_queue_destroy(queue);
@@ -189,9 +163,6 @@ void TestWaylandConnectionThread::testConnectionThread()
void TestWaylandConnectionThread::testConnectionDieing()
{
- if (m_westonProcess->state() != QProcess::Running) {
- QSKIP("This test requires a running wayland server");
- }
QScopedPointer connection(new KWin::Wayland::ConnectionThread);
QSignalSpy connectedSpy(connection.data(), SIGNAL(connected()));
connection->setSocketName(s_socketName);
@@ -200,8 +171,8 @@ void TestWaylandConnectionThread::testConnectionDieing()
QVERIFY(connection->display());
QSignalSpy diedSpy(connection.data(), SIGNAL(connectionDied()));
- m_westonProcess->terminate();
- QVERIFY(m_westonProcess->waitForFinished());
+ m_display->terminate();
+ QVERIFY(!m_display->isRunning());
QVERIFY(diedSpy.wait());
QCOMPARE(diedSpy.count(), 1);
QVERIFY(!connection->display());
@@ -209,9 +180,8 @@ void TestWaylandConnectionThread::testConnectionDieing()
connectedSpy.clear();
QVERIFY(connectedSpy.isEmpty());
// restarts the server
- delete m_westonProcess;
- m_westonProcess = nullptr;
- init();
+ m_display->start();
+ QVERIFY(m_display->isRunning());
if (connectedSpy.count() == 0) {
QVERIFY(connectedSpy.wait());
}
diff --git a/autotests/wayland_client/test_wayland_output.cpp b/autotests/wayland_client/test_wayland_output.cpp
index 1f9fe959b4..854512bbcc 100644
--- a/autotests/wayland_client/test_wayland_output.cpp
+++ b/autotests/wayland_client/test_wayland_output.cpp
@@ -23,6 +23,8 @@ along with this program. If not, see .
#include "../../wayland_client/connection_thread.h"
#include "../../wayland_client/output.h"
#include "../../wayland_client/registry.h"
+#include "../../wayland_server/display.h"
+#include "../../wayland_server/output_interface.h"
// Wayland
#include
@@ -36,79 +38,91 @@ private Q_SLOTS:
void cleanup();
void testRegistry();
+ void testModeChanges();
+ void testScaleChange();
- // TODO: add tests for removal - requires more control over the compositor
+ void testSubPixel_data();
+ void testSubPixel();
+
+ void testTransform_data();
+ void testTransform();
private:
- QProcess *m_westonProcess;
+ KWin::WaylandServer::Display *m_display;
+ KWin::WaylandServer::OutputInterface *m_serverOutput;
+ KWin::Wayland::ConnectionThread *m_connection;
+ QThread *m_thread;
};
static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0");
TestWaylandOutput::TestWaylandOutput(QObject *parent)
: QObject(parent)
- , m_westonProcess(nullptr)
+ , m_display(nullptr)
+ , m_serverOutput(nullptr)
+ , m_connection(nullptr)
+ , m_thread(nullptr)
{
}
void TestWaylandOutput::init()
{
- QVERIFY(!m_westonProcess);
- // starts weston
- m_westonProcess = new QProcess(this);
- m_westonProcess->setProgram(QStringLiteral("weston"));
+ using namespace KWin::WaylandServer;
+ delete m_display;
+ m_display = new Display(this);
+ m_display->setSocketName(s_socketName);
+ m_display->start();
+ QVERIFY(m_display->isRunning());
- m_westonProcess->setArguments(QStringList({QStringLiteral("--socket=%1").arg(s_socketName),
- QStringLiteral("--use-pixman"),
- QStringLiteral("--width=1024"),
- QStringLiteral("--height=768")}));
- m_westonProcess->start();
- QVERIFY(m_westonProcess->waitForStarted());
+ m_serverOutput = m_display->createOutput(this);
+ m_serverOutput->addMode(QSize(800, 600));
+ m_serverOutput->addMode(QSize(1024, 768));
+ m_serverOutput->addMode(QSize(1280, 1024));
+ m_serverOutput->setCurrentMode(QSize(1024, 768));
+ m_serverOutput->create();
- // wait for the socket to appear
- QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR"));
- if (runtimeDir.exists(s_socketName)) {
- return;
- }
- QFileSystemWatcher *socketWatcher = new QFileSystemWatcher(QStringList({runtimeDir.absolutePath()}), this);
- QSignalSpy socketSpy(socketWatcher, SIGNAL(directoryChanged(QString)));
+ // setup connection
+ m_connection = new KWin::Wayland::ConnectionThread;
+ QSignalSpy connectedSpy(m_connection, SIGNAL(connected()));
+ m_connection->setSocketName(s_socketName);
- // limit to maximum of 10 waits
- for (int i = 0; i < 10; ++i) {
- QVERIFY(socketSpy.wait());
- if (runtimeDir.exists(s_socketName)) {
- delete socketWatcher;
- return;
- }
- }
+ m_thread = new QThread(this);
+ m_connection->moveToThread(m_thread);
+ m_thread->start();
+
+ m_connection->initConnection();
+ QVERIFY(connectedSpy.wait());
}
void TestWaylandOutput::cleanup()
{
- // terminates weston
- m_westonProcess->terminate();
- QVERIFY(m_westonProcess->waitForFinished());
- delete m_westonProcess;
- m_westonProcess = nullptr;
+ if (m_thread) {
+ m_thread->quit();
+ m_thread->wait();
+ delete m_thread;
+ m_thread = nullptr;
+ }
+ delete m_connection;
+ m_connection = nullptr;
+
+ delete m_serverOutput;
+ m_serverOutput = nullptr;
+
+ delete m_display;
+ m_display = nullptr;
}
void TestWaylandOutput::testRegistry()
{
- if (m_westonProcess->state() != QProcess::Running) {
- QSKIP("This test requires a running wayland server");
- }
- KWin::Wayland::ConnectionThread connection;
- QSignalSpy connectedSpy(&connection, SIGNAL(connected()));
- connection.setSocketName(s_socketName);
- connection.initConnection();
- QVERIFY(connectedSpy.wait());
+ m_serverOutput->setGlobalPosition(QPoint(100, 50));
+ m_serverOutput->setPhysicalSize(QSize(200, 100));
KWin::Wayland::Registry registry;
QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32)));
- registry.create(connection.display());
+ registry.create(m_connection->display());
QVERIFY(registry.isValid());
registry.setup();
- wl_display_flush(connection.display());
+ wl_display_flush(m_connection->display());
QVERIFY(announced.wait());
KWin::Wayland::Output output;
@@ -128,14 +142,14 @@ void TestWaylandOutput::testRegistry()
QVERIFY(outputChanged.isValid());
output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value()));
- wl_display_flush(connection.display());
+ wl_display_flush(m_connection->display());
QVERIFY(outputChanged.wait());
- QCOMPARE(output.geometry(), QRect(0, 0, 1024, 768));
- QCOMPARE(output.globalPosition(), QPoint(0, 0));
- QCOMPARE(output.manufacturer(), QStringLiteral("xwayland"));
+ QCOMPARE(output.geometry(), QRect(100, 50, 1024, 768));
+ QCOMPARE(output.globalPosition(), QPoint(100, 50));
+ QCOMPARE(output.manufacturer(), QStringLiteral("org.kde.kwin"));
QCOMPARE(output.model(), QStringLiteral("none"));
- // TODO: add test for physicalSize
+ QCOMPARE(output.physicalSize(), QSize(200, 100));
QCOMPARE(output.pixelSize(), QSize(1024, 768));
QCOMPARE(output.refreshRate(), 60000);
QCOMPARE(output.scale(), 1);
@@ -145,5 +159,168 @@ void TestWaylandOutput::testRegistry()
QCOMPARE(output.transform(), KWin::Wayland::Output::Transform::Normal);
}
+void TestWaylandOutput::testModeChanges()
+{
+ KWin::Wayland::Registry registry;
+ QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32)));
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+ wl_display_flush(m_connection->display());
+ QVERIFY(announced.wait());
+
+ KWin::Wayland::Output output;
+ QSignalSpy outputChanged(&output, SIGNAL(changed()));
+ QVERIFY(outputChanged.isValid());
+ output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value()));
+ wl_display_flush(m_connection->display());
+ QVERIFY(outputChanged.wait());
+
+ QCOMPARE(output.pixelSize(), QSize(1024, 768));
+
+ // change the current mode
+ outputChanged.clear();
+ m_serverOutput->setCurrentMode(QSize(800, 600));
+ QVERIFY(outputChanged.wait());
+ QCOMPARE(output.pixelSize(), QSize(800, 600));
+
+ // change once more
+ outputChanged.clear();
+ m_serverOutput->setCurrentMode(QSize(1280, 1024));
+ QVERIFY(outputChanged.wait());
+ QCOMPARE(output.pixelSize(), QSize(1280, 1024));
+}
+
+void TestWaylandOutput::testScaleChange()
+{
+ KWin::Wayland::Registry registry;
+ QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32)));
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+ wl_display_flush(m_connection->display());
+ QVERIFY(announced.wait());
+
+ KWin::Wayland::Output output;
+ QSignalSpy outputChanged(&output, SIGNAL(changed()));
+ QVERIFY(outputChanged.isValid());
+ output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value()));
+ wl_display_flush(m_connection->display());
+ QVERIFY(outputChanged.wait());
+ QCOMPARE(output.scale(), 1);
+
+ // change the scale
+ outputChanged.clear();
+ m_serverOutput->setScale(2);
+ QVERIFY(outputChanged.wait());
+ QCOMPARE(output.scale(), 2);
+
+ // change once more
+ outputChanged.clear();
+ m_serverOutput->setScale(4);
+ QVERIFY(outputChanged.wait());
+ QCOMPARE(output.scale(), 4);
+}
+
+void TestWaylandOutput::testSubPixel_data()
+{
+ using namespace KWin::Wayland;
+ using namespace KWin::WaylandServer;
+ QTest::addColumn("expected");
+ QTest::addColumn("actual");
+
+ QTest::newRow("none") << Output::SubPixel::None << OutputInterface::SubPixel::None;
+ QTest::newRow("horizontal/rgb") << Output::SubPixel::HorizontalRGB << OutputInterface::SubPixel::HorizontalRGB;
+ QTest::newRow("horizontal/bgr") << Output::SubPixel::HorizontalBGR << OutputInterface::SubPixel::HorizontalBGR;
+ QTest::newRow("vertical/rgb") << Output::SubPixel::VerticalRGB << OutputInterface::SubPixel::VerticalRGB;
+ QTest::newRow("vertical/bgr") << Output::SubPixel::VerticalBGR << OutputInterface::SubPixel::VerticalBGR;
+}
+
+void TestWaylandOutput::testSubPixel()
+{
+ using namespace KWin::Wayland;
+ using namespace KWin::WaylandServer;
+ QFETCH(OutputInterface::SubPixel, actual);
+ m_serverOutput->setSubPixel(actual);
+
+ KWin::Wayland::Registry registry;
+ QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32)));
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+ wl_display_flush(m_connection->display());
+ QVERIFY(announced.wait());
+
+ KWin::Wayland::Output output;
+ QSignalSpy outputChanged(&output, SIGNAL(changed()));
+ QVERIFY(outputChanged.isValid());
+ output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value()));
+ wl_display_flush(m_connection->display());
+ if (outputChanged.isEmpty()) {
+ QVERIFY(outputChanged.wait());
+ }
+
+ QTEST(output.subPixel(), "expected");
+
+ // change back to unknown
+ outputChanged.clear();
+ m_serverOutput->setSubPixel(OutputInterface::SubPixel::Unknown);
+ if (outputChanged.isEmpty()) {
+ QVERIFY(outputChanged.wait());
+ }
+ QCOMPARE(output.subPixel(), Output::SubPixel::Unknown);
+}
+
+void TestWaylandOutput::testTransform_data()
+{
+ using namespace KWin::Wayland;
+ using namespace KWin::WaylandServer;
+ QTest::addColumn("expected");
+ QTest::addColumn("actual");
+
+ QTest::newRow("90") << Output::Transform::Rotated90 << OutputInterface::Transform::Rotated90;
+ QTest::newRow("180") << Output::Transform::Rotated180 << OutputInterface::Transform::Rotated180;
+ QTest::newRow("270") << Output::Transform::Rotated270 << OutputInterface::Transform::Rotated270;
+ QTest::newRow("Flipped") << Output::Transform::Flipped << OutputInterface::Transform::Flipped;
+ QTest::newRow("Flipped 90") << Output::Transform::Flipped90 << OutputInterface::Transform::Flipped90;
+ QTest::newRow("Flipped 180") << Output::Transform::Flipped180 << OutputInterface::Transform::Flipped180;
+ QTest::newRow("Flipped 280") << Output::Transform::Flipped270 << OutputInterface::Transform::Flipped270;
+}
+
+void TestWaylandOutput::testTransform()
+{
+ using namespace KWin::Wayland;
+ using namespace KWin::WaylandServer;
+ QFETCH(OutputInterface::Transform, actual);
+ m_serverOutput->setTransform(actual);
+
+ KWin::Wayland::Registry registry;
+ QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32)));
+ registry.create(m_connection->display());
+ QVERIFY(registry.isValid());
+ registry.setup();
+ wl_display_flush(m_connection->display());
+ QVERIFY(announced.wait());
+
+ KWin::Wayland::Output output;
+ QSignalSpy outputChanged(&output, SIGNAL(changed()));
+ QVERIFY(outputChanged.isValid());
+ output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value()));
+ wl_display_flush(m_connection->display());
+ if (outputChanged.isEmpty()) {
+ QVERIFY(outputChanged.wait());
+ }
+
+ QTEST(output.transform(), "expected");
+
+ // change back to normal
+ outputChanged.clear();
+ m_serverOutput->setTransform(OutputInterface::Transform::Normal);
+ if (outputChanged.isEmpty()) {
+ QVERIFY(outputChanged.wait());
+ }
+ QCOMPARE(output.transform(), Output::Transform::Normal);
+}
+
QTEST_MAIN(TestWaylandOutput)
#include "test_wayland_output.moc"
diff --git a/autotests/wayland_server/CMakeLists.txt b/autotests/wayland_server/CMakeLists.txt
new file mode 100644
index 0000000000..48897d0133
--- /dev/null
+++ b/autotests/wayland_server/CMakeLists.txt
@@ -0,0 +1,12 @@
+########################################################
+# Test WaylandServerDisplay
+########################################################
+set( testWaylandServerDisplay_SRCS
+ test_display.cpp
+ ${KWIN_SOURCE_DIR}/wayland_server/display.cpp
+ ${KWIN_SOURCE_DIR}/wayland_server/output_interface.cpp
+ )
+add_executable(testWaylandServerDisplay ${testWaylandServerDisplay_SRCS})
+target_link_libraries( testWaylandServerDisplay Qt5::Test Wayland::Server)
+add_test(kwin-testWaylandServerDisplay testWaylandServerDisplay)
+ecm_mark_as_test(testWaylandServerDisplay)
diff --git a/autotests/wayland_server/test_display.cpp b/autotests/wayland_server/test_display.cpp
new file mode 100644
index 0000000000..d9a739cfa6
--- /dev/null
+++ b/autotests/wayland_server/test_display.cpp
@@ -0,0 +1,80 @@
+/********************************************************************
+KWin - the KDE window manager
+This file is part of the KDE project.
+
+Copyright (C) 2014 Martin Gräßlin
+
+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 .
+*********************************************************************/
+// Qt
+#include
+// WaylandServer
+#include "../../wayland_server/display.h"
+
+using namespace KWin::WaylandServer;
+
+class TestWaylandServerDisplay : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void testSocketName();
+ void testStartStop();
+};
+
+void TestWaylandServerDisplay::testSocketName()
+{
+ Display display;
+ QSignalSpy changedSpy(&display, SIGNAL(socketNameChanged(QString)));
+ QVERIFY(changedSpy.isValid());
+ QCOMPARE(display.socketName(), QStringLiteral("wayland-0"));
+ const QString testSName = QStringLiteral("fooBar");
+ display.setSocketName(testSName);
+ QCOMPARE(display.socketName(), testSName);
+ QCOMPARE(changedSpy.count(), 1);
+ QCOMPARE(changedSpy.first().first().toString(), testSName);
+
+ // changing to same name again should not emit signal
+ display.setSocketName(testSName);
+ QCOMPARE(changedSpy.count(), 1);
+}
+
+void TestWaylandServerDisplay::testStartStop()
+{
+ const QString testSocketName = QStringLiteral("kwin-wayland-server-display-test-0");
+ QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR"));
+ QVERIFY(runtimeDir.exists());
+ QVERIFY(!runtimeDir.exists(testSocketName));
+
+ Display display;
+ QSignalSpy runningSpy(&display, SIGNAL(runningChanged(bool)));
+ QVERIFY(runningSpy.isValid());
+ display.setSocketName(testSocketName);
+ QVERIFY(!display.isRunning());
+ display.start();
+// QVERIFY(runningSpy.wait());
+ QCOMPARE(runningSpy.count(), 1);
+ QVERIFY(runningSpy.first().first().toBool());
+ QVERIFY(display.isRunning());
+ QVERIFY(runtimeDir.exists(testSocketName));
+
+ display.terminate();
+ QVERIFY(!display.isRunning());
+ QCOMPARE(runningSpy.count(), 2);
+ QVERIFY(runningSpy.first().first().toBool());
+ QVERIFY(!runningSpy.last().first().toBool());
+ QVERIFY(!runtimeDir.exists(testSocketName));
+}
+
+QTEST_MAIN(TestWaylandServerDisplay)
+#include "test_display.moc"
diff --git a/wayland_client/output.h b/wayland_client/output.h
index 905351c429..15dcb43b49 100644
--- a/wayland_client/output.h
+++ b/wayland_client/output.h
@@ -179,4 +179,7 @@ Output::Transform Output::transform() const
}
}
+Q_DECLARE_METATYPE(KWin::Wayland::Output::SubPixel)
+Q_DECLARE_METATYPE(KWin::Wayland::Output::Transform)
+
#endif
diff --git a/wayland_server/display.cpp b/wayland_server/display.cpp
new file mode 100644
index 0000000000..af4b410ffe
--- /dev/null
+++ b/wayland_server/display.cpp
@@ -0,0 +1,133 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2014 Martin Gräßlin
+
+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 "display.h"
+#include "output_interface.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace KWin
+{
+namespace WaylandServer
+{
+
+Display::Display(QObject *parent)
+ : QObject(parent)
+ , m_display(nullptr)
+ , m_loop(nullptr)
+ , m_socketName(QStringLiteral("wayland-0"))
+ , m_running(false)
+{
+ connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &Display::flush);
+}
+
+Display::~Display()
+{
+ terminate();
+}
+
+void Display::flush()
+{
+ if (!m_display || !m_loop) {
+ return;
+ }
+ if (wl_event_loop_dispatch(m_loop, 0) != 0) {
+ qWarning() << "Error on dispatching Wayland event loop";
+ }
+ wl_display_flush_clients(m_display);
+}
+
+void Display::setSocketName(const QString &name)
+{
+ if (m_socketName == name) {
+ return;
+ }
+ m_socketName = name;
+ emit socketNameChanged(m_socketName);
+}
+
+QString Display::socketName() const
+{
+ return m_socketName;
+}
+
+void Display::start()
+{
+ Q_ASSERT(!m_running);
+ Q_ASSERT(!m_display);
+ m_display = wl_display_create();
+ if (wl_display_add_socket(m_display, qPrintable(m_socketName)) != 0) {
+ return;
+ }
+
+ m_loop = wl_display_get_event_loop(m_display);
+ int fd = wl_event_loop_get_fd(m_loop);
+ if (fd == -1) {
+ qWarning() << "Did not get the file descriptor for the event loop";
+ return;
+ }
+ QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ connect(m_notifier, &QSocketNotifier::activated, this, &Display::flush);
+ setRunning(true);
+}
+
+void Display::terminate()
+{
+ if (!m_running) {
+ return;
+ }
+ emit aboutToTerminate();
+ wl_display_terminate(m_display);
+ wl_display_destroy(m_display);
+ m_display = nullptr;
+ m_loop = nullptr;
+ setRunning(false);
+}
+
+void Display::setRunning(bool running)
+{
+ if (m_running == running) {
+ return;
+ }
+ m_running = running;
+ emit runningChanged(m_running);
+}
+
+OutputInterface *Display::createOutput(QObject *parent)
+{
+ OutputInterface *output = new OutputInterface(this, parent);
+ connect(output, &QObject::destroyed, this, [this,output] { m_outputs.removeAll(output); });
+ connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); });
+ m_outputs << output;
+ return output;
+}
+
+void Display::removeOutput(OutputInterface *output)
+{
+ m_outputs.removeAll(output);
+ delete output;
+}
+
+}
+}
diff --git a/wayland_server/display.h b/wayland_server/display.h
new file mode 100644
index 0000000000..c63dbe2a18
--- /dev/null
+++ b/wayland_server/display.h
@@ -0,0 +1,85 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2014 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#ifndef KWIN_WAYLAND_SERVER_DISPLAY_H
+#define KWIN_WAYLAND_SERVER_DISPLAY_H
+
+#include
+#include
+
+struct wl_display;
+struct wl_event_loop;
+
+namespace KWin
+{
+namespace WaylandServer
+{
+
+class OutputInterface;
+
+class Display : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged)
+ Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
+public:
+ explicit Display(QObject *parent = nullptr);
+ virtual ~Display();
+
+ void setSocketName(const QString &name);
+ QString socketName() const;
+
+ void start();
+ void terminate();
+
+ operator wl_display*() {
+ return m_display;
+ }
+ operator wl_display*() const {
+ return m_display;
+ }
+ bool isRunning() const {
+ return m_running;
+ }
+
+ OutputInterface *createOutput(QObject *parent = nullptr);
+ void removeOutput(OutputInterface *output);
+ const QList outputs() const {
+ return m_outputs;
+ }
+
+Q_SIGNALS:
+ void socketNameChanged(const QString&);
+ void runningChanged(bool);
+ void aboutToTerminate();
+
+private:
+ void flush();
+ void setRunning(bool running);
+ wl_display *m_display;
+ wl_event_loop *m_loop;
+ QString m_socketName;
+ bool m_running;
+ QList m_outputs;
+};
+
+}
+}
+
+#endif
diff --git a/wayland_server/output_interface.cpp b/wayland_server/output_interface.cpp
new file mode 100644
index 0000000000..4614503e6c
--- /dev/null
+++ b/wayland_server/output_interface.cpp
@@ -0,0 +1,371 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2014 Martin Gräßlin
+
+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 "output_interface.h"
+#include "display.h"
+
+#include
+
+namespace KWin
+{
+namespace WaylandServer
+{
+
+static const quint32 s_version = 2;
+
+OutputInterface::OutputInterface(Display *display, QObject *parent)
+ : QObject(parent)
+ , m_display(display)
+ , m_output(nullptr)
+ , m_physicalSize(QSize())
+ , m_globalPosition(QPoint())
+ , m_manufacturer(QStringLiteral("org.kde.kwin"))
+ , m_model(QStringLiteral("none"))
+ , m_scale(1)
+ , m_subPixel(SubPixel::Unknown)
+ , m_transform(Transform::Normal)
+{
+ connect(this, &OutputInterface::currentModeChanged, this,
+ [this] {
+ auto currentModeIt = std::find_if(m_modes.constBegin(), m_modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); });
+ if (currentModeIt == m_modes.constEnd()) {
+ return;
+ }
+ for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) {
+ sendMode((*it).resource, *currentModeIt);
+ sendDone(*it);
+ }
+ }
+ );
+ connect(this, &OutputInterface::subPixelChanged, this, &OutputInterface::updateGeometry);
+ connect(this, &OutputInterface::transformChanged, this, &OutputInterface::updateGeometry);
+ connect(this, &OutputInterface::globalPositionChanged, this, &OutputInterface::updateGeometry);
+ connect(this, &OutputInterface::modelChanged, this, &OutputInterface::updateGeometry);
+ connect(this, &OutputInterface::manufacturerChanged, this, &OutputInterface::updateGeometry);
+ connect(this, &OutputInterface::scaleChanged, this, &OutputInterface::updateScale);
+}
+
+OutputInterface::~OutputInterface()
+{
+ destroy();
+}
+
+void OutputInterface::create()
+{
+ Q_ASSERT(!m_output);
+ m_output = wl_global_create(*m_display, &wl_output_interface, s_version, this, OutputInterface::bind);
+}
+
+void OutputInterface::destroy()
+{
+ if (!m_output) {
+ return;
+ }
+ wl_global_destroy(m_output);
+ m_output = nullptr;
+}
+
+QSize OutputInterface::pixelSize() const
+{
+ auto it = std::find_if(m_modes.begin(), m_modes.end(),
+ [](const Mode &mode) {
+ return mode.flags.testFlag(ModeFlag::Current);
+ }
+ );
+ if (it == m_modes.end()) {
+ return QSize();
+ }
+ return (*it).size;
+}
+
+int OutputInterface::refreshRate() const
+{
+ auto it = std::find_if(m_modes.begin(), m_modes.end(),
+ [](const Mode &mode) {
+ return mode.flags.testFlag(ModeFlag::Current);
+ }
+ );
+ if (it == m_modes.end()) {
+ return 60000;
+ }
+ return (*it).refreshRate;
+}
+
+void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate)
+{
+ Q_ASSERT(!isValid());
+
+ auto currentModeIt = std::find_if(m_modes.begin(), m_modes.end(),
+ [](const Mode &mode) {
+ return mode.flags.testFlag(ModeFlag::Current);
+ }
+ );
+ if (currentModeIt == m_modes.end() && !flags.testFlag(ModeFlag::Current)) {
+ // no mode with current flag - enforce
+ flags |= ModeFlag::Current;
+ }
+ if (currentModeIt != m_modes.end() && flags.testFlag(ModeFlag::Current)) {
+ // another mode has the current flag - remove
+ (*currentModeIt).flags &= ~uint(ModeFlag::Current);
+ }
+
+ if (flags.testFlag(ModeFlag::Preferred)) {
+ // remove from existing Preferred mode
+ auto preferredIt = std::find_if(m_modes.begin(), m_modes.end(),
+ [](const Mode &mode) {
+ return mode.flags.testFlag(ModeFlag::Preferred);
+ }
+ );
+ if (preferredIt != m_modes.end()) {
+ (*preferredIt).flags &= ~uint(ModeFlag::Preferred);
+ }
+ }
+
+ auto existingModeIt = std::find_if(m_modes.begin(), m_modes.end(),
+ [size,refreshRate](const Mode &mode) {
+ return mode.size == size && mode.refreshRate == refreshRate;
+ }
+ );
+ auto emitChanges = [this,flags,size,refreshRate] {
+ emit modesChanged();
+ if (flags.testFlag(ModeFlag::Current)) {
+ emit refreshRateChanged(refreshRate);
+ emit pixelSizeChanged(size);
+ emit currentModeChanged();
+ }
+ };
+ if (existingModeIt != m_modes.end()) {
+ if ((*existingModeIt).flags == flags) {
+ // nothing to do
+ return;
+ }
+ (*existingModeIt).flags = flags;
+ emitChanges();
+ return;
+ }
+ Mode mode;
+ mode.size = size;
+ mode.refreshRate = refreshRate;
+ mode.flags = flags;
+ m_modes << mode;
+ emitChanges();
+}
+
+void OutputInterface::setCurrentMode(const QSize &size, int refreshRate)
+{
+ auto currentModeIt = std::find_if(m_modes.begin(), m_modes.end(),
+ [](const Mode &mode) {
+ return mode.flags.testFlag(ModeFlag::Current);
+ }
+ );
+ if (currentModeIt != m_modes.end()) {
+ // another mode has the current flag - remove
+ (*currentModeIt).flags &= ~uint(ModeFlag::Current);
+ }
+
+ auto existingModeIt = std::find_if(m_modes.begin(), m_modes.end(),
+ [size,refreshRate](const Mode &mode) {
+ return mode.size == size && mode.refreshRate == refreshRate;
+ }
+ );
+
+ Q_ASSERT(existingModeIt != m_modes.end());
+ (*existingModeIt).flags |= ModeFlag::Current;
+ emit modesChanged();
+ emit refreshRateChanged((*existingModeIt).refreshRate);
+ emit pixelSizeChanged((*existingModeIt).size);
+ emit currentModeChanged();
+}
+
+void OutputInterface::bind(wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ OutputInterface *output = reinterpret_cast(data);
+ output->bind(client, version, id);
+}
+
+int32_t OutputInterface::toTransform() const
+{
+ switch (m_transform) {
+ case Transform::Normal:
+ return WL_OUTPUT_TRANSFORM_NORMAL;
+ case Transform::Rotated90:
+ return WL_OUTPUT_TRANSFORM_90;
+ case Transform::Rotated180:
+ return WL_OUTPUT_TRANSFORM_180;
+ case Transform::Rotated270:
+ return WL_OUTPUT_TRANSFORM_270;
+ case Transform::Flipped:
+ return WL_OUTPUT_TRANSFORM_FLIPPED;
+ case Transform::Flipped90:
+ return WL_OUTPUT_TRANSFORM_FLIPPED_90;
+ case Transform::Flipped180:
+ return WL_OUTPUT_TRANSFORM_FLIPPED_180;
+ case Transform::Flipped270:
+ return WL_OUTPUT_TRANSFORM_FLIPPED_270;
+ }
+ abort();
+}
+
+int32_t OutputInterface::toSubPixel() const
+{
+ switch (m_subPixel) {
+ case SubPixel::Unknown:
+ return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ case SubPixel::None:
+ return WL_OUTPUT_SUBPIXEL_NONE;
+ case SubPixel::HorizontalRGB:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+ case SubPixel::HorizontalBGR:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+ case SubPixel::VerticalRGB:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+ case SubPixel::VerticalBGR:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+ }
+ abort();
+}
+
+void OutputInterface::bind(wl_client *client, uint32_t version, uint32_t id)
+{
+ wl_resource *resource = wl_resource_create(client, &wl_output_interface, qMin(version, s_version), id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_user_data(resource, this);
+ wl_resource_set_destructor(resource, OutputInterface::unbind);
+ ResourceData r;
+ r.resource = resource;
+ r.version = version;
+ m_resources << r;
+
+ sendGeometry(resource);
+ sendScale(r);
+
+ auto currentModeIt = m_modes.constEnd();
+ for (auto it = m_modes.constBegin(); it != m_modes.constEnd(); ++it) {
+ const Mode &mode = *it;
+ if (mode.flags.testFlag(ModeFlag::Current)) {
+ // needs to be sent as last mode
+ currentModeIt = it;
+ continue;
+ }
+ sendMode(resource, mode);
+ }
+
+ if (currentModeIt != m_modes.constEnd()) {
+ sendMode(resource, *currentModeIt);
+ }
+
+ sendDone(r);
+}
+
+void OutputInterface::unbind(wl_resource *resource)
+{
+ OutputInterface *o = reinterpret_cast(wl_resource_get_user_data(resource));
+ auto it = std::find_if(o->m_resources.begin(), o->m_resources.end(), [resource](const ResourceData &r) { return r.resource == resource; });
+ if (it != o->m_resources.end()) {
+ o->m_resources.erase(it);
+ }
+}
+
+void OutputInterface::sendMode(wl_resource *resource, const Mode &mode)
+{
+ int32_t flags = 0;
+ if (mode.flags.testFlag(ModeFlag::Current)) {
+ flags |= WL_OUTPUT_MODE_CURRENT;
+ }
+ if (mode.flags.testFlag(ModeFlag::Preferred)) {
+ flags |= WL_OUTPUT_MODE_PREFERRED;
+ }
+ wl_output_send_mode(resource,
+ flags,
+ mode.size.width(),
+ mode.size.height(),
+ mode.refreshRate);
+
+}
+
+void OutputInterface::sendGeometry(wl_resource *resource)
+{
+ wl_output_send_geometry(resource,
+ m_globalPosition.x(),
+ m_globalPosition.y(),
+ m_physicalSize.width(),
+ m_physicalSize.height(),
+ toSubPixel(),
+ qPrintable(m_manufacturer),
+ qPrintable(m_model),
+ toTransform());
+}
+
+void OutputInterface::sendScale(const OutputInterface::ResourceData &data)
+{
+ if (data.version < 2) {
+ return;
+ }
+ wl_output_send_scale(data.resource, m_scale);
+}
+
+void OutputInterface::sendDone(const OutputInterface::ResourceData &data)
+{
+ if (data.version < 2) {
+ return;
+ }
+ wl_output_send_done(data.resource);
+}
+
+void OutputInterface::updateGeometry()
+{
+ for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) {
+ sendGeometry((*it).resource);
+ sendDone(*it);
+ }
+}
+
+void OutputInterface::updateScale()
+{
+ for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) {
+ sendScale(*it);
+ sendDone(*it);
+ }
+}
+
+#define SETTER(setterName, type, argumentName) \
+ void OutputInterface::setterName(type arg) \
+ { \
+ if (m_##argumentName == arg) { \
+ return; \
+ } \
+ m_##argumentName = arg; \
+ emit argumentName##Changed(m_##argumentName); \
+ }
+
+SETTER(setPhysicalSize, const QSize&, physicalSize)
+SETTER(setGlobalPosition, const QPoint&, globalPosition)
+SETTER(setManufacturer, const QString&, manufacturer)
+SETTER(setModel, const QString&, model)
+SETTER(setScale, int, scale)
+SETTER(setSubPixel, SubPixel, subPixel)
+SETTER(setTransform, Transform, transform)
+
+#undef SETTER
+
+}
+}
diff --git a/wayland_server/output_interface.h b/wayland_server/output_interface.h
new file mode 100644
index 0000000000..b13410de51
--- /dev/null
+++ b/wayland_server/output_interface.h
@@ -0,0 +1,179 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2014 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#ifndef KWIN_WAYLAND_SERVER_OUTPUT_INTERFACE_H
+#define KWIN_WAYLAND_SERVER_OUTPUT_INTERFACE_H
+
+#include
+#include
+#include
+
+struct wl_global;
+struct wl_client;
+struct wl_resource;
+
+namespace KWin
+{
+namespace WaylandServer
+{
+
+class Display;
+
+class OutputInterface : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged)
+ Q_PROPERTY(QPoint globalPosition READ globalPosition WRITE setGlobalPosition NOTIFY globalPositionChanged)
+ Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged)
+ Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged)
+ Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged)
+ Q_PROPERTY(int scale READ scale WRITE setScale NOTIFY scaleChanged)
+public:
+ enum class SubPixel {
+ Unknown,
+ None,
+ HorizontalRGB,
+ HorizontalBGR,
+ VerticalRGB,
+ VerticalBGR
+ };
+ enum class Transform {
+ Normal,
+ Rotated90,
+ Rotated180,
+ Rotated270,
+ Flipped,
+ Flipped90,
+ Flipped180,
+ Flipped270
+ };
+ enum class ModeFlag {
+ Current = 1,
+ Preferred = 2
+ };
+ Q_DECLARE_FLAGS(ModeFlags, ModeFlag)
+ struct Mode {
+ QSize size = QSize();
+ int refreshRate = 60000;
+ ModeFlags flags;
+ };
+ virtual ~OutputInterface();
+ void create();
+ void destroy();
+ bool isValid() const {
+ return m_output != nullptr;
+ }
+
+ const QSize &physicalSize() const {
+ return m_physicalSize;
+ }
+ const QPoint &globalPosition() const {
+ return m_globalPosition;
+ }
+ const QString &manufacturer() const {
+ return m_manufacturer;
+ }
+ const QString &model() const {
+ return m_model;
+ }
+ QSize pixelSize() const;
+ int refreshRate() const;
+ int scale() const {
+ return m_scale;
+ }
+ SubPixel subPixel() const {
+ return m_subPixel;
+ }
+ Transform transform() const {
+ return m_transform;
+ }
+ QList modes() const {
+ return m_modes;
+ }
+
+ void setPhysicalSize(const QSize &size);
+ void setGlobalPosition(const QPoint &pos);
+ void setManufacturer(const QString &manufacturer);
+ void setModel(const QString &model);
+ void setScale(int scale);
+ void setSubPixel(SubPixel subPixel);
+ void setTransform(Transform transform);
+ void addMode(const QSize &size, ModeFlags flags = ModeFlags(), int refreshRate = 60000);
+ void setCurrentMode(const QSize &size, int refreshRate = 60000);
+
+ operator wl_global*() {
+ return m_output;
+ }
+ operator wl_global*() const {
+ return m_output;
+ }
+
+Q_SIGNALS:
+ void physicalSizeChanged(const QSize&);
+ void globalPositionChanged(const QPoint&);
+ void manufacturerChanged(const QString&);
+ void modelChanged(const QString&);
+ void pixelSizeChanged(const QSize&);
+ void refreshRateChanged(int);
+ void scaleChanged(int);
+ void subPixelChanged(SubPixel);
+ void transformChanged(Transform);
+ void modesChanged();
+ void currentModeChanged();
+
+private:
+ struct ResourceData {
+ wl_resource *resource;
+ uint32_t version;
+ };
+ friend class Display;
+ explicit OutputInterface(Display *display, QObject *parent = nullptr);
+ static void bind(wl_client *client, void *data, uint32_t version, uint32_t id);
+ static void unbind(wl_resource *resource);
+ void bind(wl_client *client, uint32_t version, uint32_t id);
+ int32_t toTransform() const;
+ int32_t toSubPixel() const;
+ void sendMode(wl_resource *resource, const Mode &mode);
+ void sendDone(const ResourceData &data);
+ void updateGeometry();
+ void sendGeometry(wl_resource *resource);
+ void sendScale(const ResourceData &data);
+ void updateScale();
+ Display *m_display;
+ wl_global *m_output;
+ QSize m_physicalSize;
+ QPoint m_globalPosition;
+ QString m_manufacturer;
+ QString m_model;
+ int m_scale;
+ SubPixel m_subPixel;
+ Transform m_transform;
+ QList m_modes;
+ QList m_resources;
+};
+
+}
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::WaylandServer::OutputInterface::ModeFlags)
+Q_DECLARE_METATYPE(KWin::WaylandServer::OutputInterface::SubPixel)
+Q_DECLARE_METATYPE(KWin::WaylandServer::OutputInterface::Transform)
+
+#endif