Blob Blame History Raw
commit 716a12529ec92a2ed03fcf00940a8ce890d1b6e8
Author: Dan Vrátil <dvratil@redhat.com>
Date:   Wed Apr 15 22:38:16 2015 +0200

    Serializer: calculate screen size needed for the loaded config
    
    When the configuration we apply from Serializer needs bigger XRandR Screen then
    the currently set, we need to update the screen size as well. This can happen for
    example when one of the screens in the loaded config is rotated by 90 degrees,
    which means that it requires higher XRandR screen then currently available.
    Without this the content of the output would be weirdly cropped.
    
    BUG: 346239
    FIXED-IN: 5.3.0
    
    Conflicts:
    	kded/serializer.cpp
    	kded/serializer.h
    	tests/kded/CMakeLists.txt

diff --git a/kded/serializer.cpp b/kded/serializer.cpp
index 325c90a..5d96891 100644
--- a/kded/serializer.cpp
+++ b/kded/serializer.cpp
@@ -27,6 +27,8 @@
 #include <QtCore/QVariantList>
 #include <QtCore/QVariantMap>
 #include <QtCore/QStandardPaths>
+#include <QtCore/QRect>
+#include <QtCore/QStringBuilder>
 #include <QJsonDocument>
 #include <QDir>
 #include <QLoggingCategory>
@@ -35,6 +37,16 @@
 #include <kscreen/output.h>
 #include <kscreen/edid.h>
 
+QString Serializer::sConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/");
+
+void Serializer::setConfigPath(const QString &path)
+{
+    sConfigPath = path;
+    if (!sConfigPath.endsWith(QLatin1Char('/'))) {
+        sConfigPath += QLatin1Char('/');
+    }
+}
+
 QString Serializer::configId(const KScreen::ConfigPtr &currentConfig)
 {
     KScreen::OutputList outputs = currentConfig->outputs();
@@ -65,15 +77,14 @@ bool Serializer::configExists(const KScreen::ConfigPtr &config)
 
 bool Serializer::configExists(const QString& id)
 {
-    QString path(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kscreen/") + id);
-    return QFile::exists(path);
+    return QFile::exists(sConfigPath % id);
 }
 
 KScreen::ConfigPtr Serializer::config(const KScreen::ConfigPtr &currentConfig, const QString& id)
 {
     KScreen::ConfigPtr config = currentConfig->clone();
 
-    QFile file(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kscreen/") + id);
+    QFile file(sConfigPath % id);
     if (!file.open(QIODevice::ReadOnly))
         return KScreen::ConfigPtr();
 
@@ -86,16 +97,29 @@ KScreen::ConfigPtr Serializer::config(const KScreen::ConfigPtr &currentConfig, c
         }
     }
 
+    QSize screenSize;
     Q_FOREACH(const QVariant &info, outputs) {
         KScreen::OutputPtr output = Serializer::findOutput(config, info.toMap());
         if (!output) {
             continue;
         }
 
+        if (output->isEnabled()) {
+            const QRect geom = output->geometry();
+            if (geom.x() + geom.width() > screenSize.width()) {
+                screenSize.setWidth(geom.x() + geom.width());
+            }
+            if (geom.y() + geom.height() > screenSize.height()) {
+                screenSize.setHeight(geom.y() + geom.height());
+            }
+        }
+
         outputList.remove(output->id());
         outputList.insert(output->id(), output);
     }
     config->setOutputs(outputList);
+    config->screen()->setCurrentSize(screenSize);
+
 
     return config;
 }
@@ -145,10 +169,9 @@ bool Serializer::saveConfig(const KScreen::ConfigPtr &config)
         outputList.append(info);
     }
 
-    const QString directory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kscreen/");
-    bool b = QDir().mkpath(directory);
+    bool b = QDir().mkpath(sConfigPath);
     Q_ASSERT(b);
-    QString filePath = directory + Serializer::configId(config);
+    QString filePath = sConfigPath + Serializer::configId(config);
     QFile file(filePath);
     b = file.open(QIODevice::WriteOnly);
     Q_ASSERT(b);
diff --git a/kded/serializer.h b/kded/serializer.h
index ac590af..7e58862 100644
--- a/kded/serializer.h
+++ b/kded/serializer.h
@@ -36,6 +36,10 @@ class Serializer
         static KScreen::OutputPtr findOutput(const KScreen::ConfigPtr &config, const QVariantMap &info);
         static QString outputId(const KScreen::OutputPtr &output);
         static QVariantMap metadata(const KScreen::OutputPtr &output);
+
+        static void setConfigPath(const QString &path);
+    private:
+        static QString sConfigPath;
 };
 
 #endif //KDED_SERIALIZER_H
diff --git a/tests/kded/CMakeLists.txt b/tests/kded/CMakeLists.txt
index 28cb003..e2ff0f0 100644
--- a/tests/kded/CMakeLists.txt
+++ b/tests/kded/CMakeLists.txt
@@ -9,6 +9,7 @@ macro(ADD_KDED_TEST testname)
         ${CMAKE_SOURCE_DIR}/kded/generator.cpp
         ${CMAKE_SOURCE_DIR}/kded/device.cpp
         ${CMAKE_SOURCE_DIR}/kded/debug.cpp
+        ${CMAKE_SOURCE_DIR}/kded/serializer.cpp
         #${CMAKE_SOURCE_DIR}/kded/daemon.cpp
     )
 
@@ -18,11 +19,13 @@ macro(ADD_KDED_TEST testname)
     )
 
     add_executable(${testname} ${test_SRCS})
-    target_compile_definitions(${testname} PRIVATE "-DTEST_DATA=\"${CMAKE_CURRENT_SOURCE_DIR}/configs/\"")
+    add_dependencies(${testname} kded_kscreen) # make sure the dbus interfaces are generated
+    target_compile_definitions(${testname} PRIVATE "-DTEST_DATA=\"${CMAKE_CURRENT_SOURCE_DIR}/\"")
     target_link_libraries(${testname} Qt5::Test Qt5::DBus Qt5::Gui KF5::Screen)
     add_test(kscreen-kded-${testname} ${testname})
     ecm_mark_as_test(${testname})
 endmacro()
 
 add_kded_test(testgenerator)
+add_kded_test(serializertest)
 #add_kded_test(testdaemon)
diff --git a/tests/kded/serializerdata/disabledScreenConfig.json b/tests/kded/serializerdata/disabledScreenConfig.json
new file mode 100644
index 0000000..ca6504b
--- /dev/null
+++ b/tests/kded/serializerdata/disabledScreenConfig.json
@@ -0,0 +1,36 @@
+[
+    {
+        "enabled": true,
+        "id": "OUTPUT-1",
+        "metadata": {
+            "name": "OUTPUT-1"
+        },
+        "mode": {
+            "refresh": 60.0,
+            "size": {
+                "height": 1080,
+                "width": 1920
+            }
+        },
+        "pos": {
+            "x": 0,
+            "y": 0
+        },
+        "primary": true,
+        "rotation": 1
+    },
+    {
+        "enabled": false,
+        "id": "OUTPUT-2",
+        "metadata": {
+            "name": "OUTPUT-2"
+        },
+        "pos": {
+            "x": 0,
+            "y": 0
+        },
+        "primary": false,
+        "rotation": 1
+    }
+]
+
diff --git a/tests/kded/serializerdata/rotatedScreenConfig.json b/tests/kded/serializerdata/rotatedScreenConfig.json
new file mode 100644
index 0000000..a063080
--- /dev/null
+++ b/tests/kded/serializerdata/rotatedScreenConfig.json
@@ -0,0 +1,43 @@
+[
+    {
+        "enabled": true,
+        "id": "OUTPUT-1",
+        "metadata": {
+            "name": "OUTPUT-1"
+        },
+        "mode": {
+            "refresh": 60.0,
+            "size": {
+                "height": 1080,
+                "width": 1920
+            }
+        },
+        "pos": {
+            "x": 0,
+            "y": 0
+        },
+        "primary": true,
+        "rotation": 1
+    },
+    {
+        "enabled": true,
+        "id": "OUTPUT-2",
+        "metadata": {
+            "name": "OUTPUT-2"
+        },
+        "mode": {
+            "refresh": 60,
+            "size": {
+                "height": 1024,
+                "width": 1280
+            }
+        },
+        "pos": {
+            "x": 1920,
+            "y": 0
+        },
+        "primary": false,
+        "rotation": 2
+    }
+]
+
diff --git a/tests/kded/serializerdata/simpleConfig.json b/tests/kded/serializerdata/simpleConfig.json
new file mode 100644
index 0000000..54503bb
--- /dev/null
+++ b/tests/kded/serializerdata/simpleConfig.json
@@ -0,0 +1,22 @@
+[
+    {
+        "enabled": true,
+        "id": "OUTPUT-1",
+        "metadata": {
+            "name": "OUTPUT-1"
+        },
+        "mode": {
+            "refresh": 60.0,
+            "size": {
+                "height": 1280,
+                "width": 1920
+            }
+        },
+        "pos": {
+            "x": 0,
+            "y": 0
+        },
+        "primary": true,
+        "rotation": 1
+    }
+]
diff --git a/tests/kded/serializerdata/twoScreenConfig.json b/tests/kded/serializerdata/twoScreenConfig.json
new file mode 100644
index 0000000..b80a710
--- /dev/null
+++ b/tests/kded/serializerdata/twoScreenConfig.json
@@ -0,0 +1,42 @@
+[
+    {
+        "enabled": true,
+        "id": "OUTPUT-1",
+        "metadata": {
+            "name": "OUTPUT-1"
+        },
+        "mode": {
+            "refresh": 60.0,
+            "size": {
+                "height": 1080,
+                "width": 1920
+            }
+        },
+        "pos": {
+            "x": 0,
+            "y": 0
+        },
+        "primary": true,
+        "rotation": 1
+    },
+    {
+        "enabled": true,
+        "id": "OUTPUT-2",
+        "metadata": {
+            "name": "OUTPUT-2"
+        },
+        "mode": {
+            "refresh": 60,
+            "size": {
+                "height": 1024,
+                "width": 1280
+            }
+        },
+        "pos": {
+            "x": 1920,
+            "y": 0
+        },
+        "primary": false,
+        "rotation": 1
+    }
+]
diff --git a/tests/kded/serializertest.cpp b/tests/kded/serializertest.cpp
new file mode 100644
index 0000000..e90ddcf
--- /dev/null
+++ b/tests/kded/serializertest.cpp
@@ -0,0 +1,206 @@
+/*************************************************************************************
+ *  Copyright (C) 2015 by Daniel Vrátil <dvratil@redhat.com>                         *
+ *                                                                                   *
+ *  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, write to the Free Software                      *
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
+ *************************************************************************************/
+
+#include "../../kded/serializer.h"
+
+#include <QtTest>
+#include <QtCore/QObject>
+
+#include <KScreen/Config>
+#include <KScreen/Screen>
+#include <KScreen/Mode>
+#include <KScreen/Output>
+
+using namespace KScreen;
+
+class TestSerializer : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+
+    void testSimpleConfig();
+    void testTwoScreenConfig();
+    void testRotatedScreenConfig();
+    void testDisabledScreenConfig();
+
+private:
+    KScreen::ConfigPtr createConfig(bool output1Connected, bool output2Conected);
+};
+
+ConfigPtr TestSerializer::createConfig(bool output1Connected, bool output2Connected)
+{
+    KScreen::ScreenPtr screen = KScreen::ScreenPtr::create();
+    screen->setCurrentSize(QSize(1920, 1080));
+    screen->setMaxSize(QSize(32768, 32768));
+    screen->setMinSize(QSize(8, 8));
+
+    QList<QSize> sizes({ QSize(320, 240), QSize(640, 480), QSize(1024, 768), QSize(1280, 1024), QSize(1920, 1280) });
+    KScreen::ModeList modes;
+    for (int i = 0; i < sizes.count(); ++i) {
+        const QSize &size = sizes[i];
+        KScreen::ModePtr mode = KScreen::ModePtr::create();
+        mode->setId(QString::fromLatin1("MODE-%1").arg(i));
+        mode->setName(QString::fromLatin1("%1x%2").arg(size.width()).arg(size.height()));
+        mode->setSize(size);
+        mode->setRefreshRate(60.0);
+        modes.insert(mode->id(), mode);
+    }
+
+    KScreen::OutputPtr output1 = KScreen::OutputPtr::create();
+    output1->setId(1);
+    output1->setName(QLatin1String("OUTPUT-1"));
+    output1->setPos(QPoint(0, 0));
+    output1->setConnected(output1Connected);
+    output1->setEnabled(output1Connected);
+    if (output1Connected) {
+        output1->setModes(modes);
+    }
+
+    KScreen::OutputPtr output2 = KScreen::OutputPtr::create();
+    output2->setId(2);
+    output2->setName(QLatin1String("OUTPUT-2"));
+    output2->setPos(QPoint(0, 0));
+    output2->setConnected(output2Connected);
+    if (output2Connected) {
+        output2->setModes(modes);
+    }
+
+    KScreen::ConfigPtr config = KScreen::ConfigPtr::create();
+    config->setScreen(screen);
+    config->addOutput(output1);
+    config->addOutput(output2);
+
+    return config;
+}
+
+void TestSerializer::initTestCase()
+{
+    Serializer::setConfigPath(QLatin1String(TEST_DATA "/serializerdata/"));
+}
+
+void TestSerializer::testSimpleConfig()
+{
+    KScreen::ConfigPtr config = createConfig(true, false);
+    config = Serializer::config(config, QLatin1String("simpleConfig.json"));
+    QVERIFY(config);
+
+    QCOMPARE(config->connectedOutputs().count(), 1);
+
+    auto output = config->connectedOutputs().first();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-1"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-4"));
+    QCOMPARE(output->currentMode()->size(), QSize(1920, 1280));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::None);
+    QCOMPARE(output->pos(), QPoint(0, 0));
+    QCOMPARE(output->isPrimary(), true);
+
+    auto screen = config->screen();
+    QCOMPARE(screen->currentSize(), QSize(1920, 1280));
+}
+
+void TestSerializer::testTwoScreenConfig()
+{
+    KScreen::ConfigPtr config = createConfig(true, true);
+    config = Serializer::config(config, QLatin1String("twoScreenConfig.json"));
+    QVERIFY(config);
+
+    QCOMPARE(config->connectedOutputs().count(), 2);
+
+    auto output = config->connectedOutputs().first();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-1"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-4"));
+    QCOMPARE(output->currentMode()->size(), QSize(1920, 1280));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::None);
+    QCOMPARE(output->pos(), QPoint(0, 0));
+    QCOMPARE(output->isPrimary(), true);
+
+    output = config->connectedOutputs().last();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-2"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-3"));
+    QCOMPARE(output->currentMode()->size(), QSize(1280, 1024));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::None);
+    QCOMPARE(output->pos(), QPoint(1920, 0));
+    QCOMPARE(output->isPrimary(), false);
+
+    auto screen = config->screen();
+    QCOMPARE(screen->currentSize(), QSize(3200, 1280));
+}
+
+void TestSerializer::testRotatedScreenConfig()
+{
+    KScreen::ConfigPtr config = createConfig(true, true);
+    config = Serializer::config(config, QLatin1String("rotatedScreenConfig.json"));
+    QVERIFY(config);
+
+    QCOMPARE(config->connectedOutputs().count(), 2);
+
+    auto output = config->connectedOutputs().first();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-1"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-4"));
+    QCOMPARE(output->currentMode()->size(), QSize(1920, 1280));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::None);
+    QCOMPARE(output->pos(), QPoint(0, 0));
+    QCOMPARE(output->isPrimary(), true);
+
+    output = config->connectedOutputs().last();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-2"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-3"));
+    QCOMPARE(output->currentMode()->size(), QSize(1280, 1024));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::Left);
+    QCOMPARE(output->pos(), QPoint(1920, 0));
+    QCOMPARE(output->isPrimary(), false);
+
+    auto screen = config->screen();
+    QCOMPARE(screen->currentSize(), QSize(2944, 1280));
+}
+
+void TestSerializer::testDisabledScreenConfig()
+{
+    KScreen::ConfigPtr config = createConfig(true, true);
+    config = Serializer::config(config, QLatin1String("disabledScreenConfig.json"));
+    QVERIFY(config);
+
+    QCOMPARE(config->connectedOutputs().count(), 2);
+
+    auto output = config->connectedOutputs().first();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-1"));
+    QCOMPARE(output->currentModeId(), QLatin1String("MODE-4"));
+    QCOMPARE(output->currentMode()->size(), QSize(1920, 1280));
+    QCOMPARE(output->isEnabled(), true);
+    QCOMPARE(output->rotation(), KScreen::Output::None);
+    QCOMPARE(output->pos(), QPoint(0, 0));
+    QCOMPARE(output->isPrimary(), true);
+
+    output = config->connectedOutputs().last();
+    QCOMPARE(output->name(), QLatin1String("OUTPUT-2"));
+    QCOMPARE(output->isEnabled(), false);
+
+    auto screen = config->screen();
+    QCOMPARE(screen->currentSize(), QSize(1920, 1280));
+}
+
+QTEST_MAIN(TestSerializer)
+
+#include "serializertest.moc"
\ No newline at end of file
diff --git a/tests/kded/testgenerator.cpp b/tests/kded/testgenerator.cpp
index 3eaad78..a796c70 100644
--- a/tests/kded/testgenerator.cpp
+++ b/tests/kded/testgenerator.cpp
@@ -59,9 +59,8 @@ KScreen::ConfigPtr testScreenConfig::loadConfig(const QByteArray& fileName)
 {
     KScreen::BackendManager::instance()->shutdownBackend();
 
-    QByteArray path(TEST_DATA);
-    path.append("/" + fileName);
-    setenv("TEST_DATA", path, 1);
+    QByteArray path(TEST_DATA "configs/" + fileName);
+    qputenv("TEST_DATA", path);
     qDebug() << path;
 
     KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation;