diff --git a/kded/daemon.cpp b/kded/daemon.cpp --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -163,6 +163,11 @@ const QString configId = Serializer::configId(m_monitoredConfig); qCDebug(KSCREEN_KDED) << "Applying known config" << configId; + // We may look for a config that has been set when the lid was closed, Bug: 353029 + if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { + Serializer::moveConfig(configId + QStringLiteral("_lidOpened"), configId); + } + KScreen::ConfigPtr config = Serializer::config(m_monitoredConfig, configId); // It's possible that the Serializer returned a nullptr if (!config || !KScreen::Config::canBeApplied(config, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen)) { diff --git a/kded/serializer.h b/kded/serializer.h --- a/kded/serializer.h +++ b/kded/serializer.h @@ -33,13 +33,15 @@ static KScreen::ConfigPtr config(const KScreen::ConfigPtr ¤tConfig, const QString& id); static bool saveConfig(const KScreen::ConfigPtr &config, const QString &configId); static void removeConfig(const QString &id); + static bool moveConfig(const QString &srcId, const QString &destId); 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: + friend class TestSerializer; static QString configFileName(const QString &configId); static QString sConfigPath; diff --git a/kded/serializer.cpp b/kded/serializer.cpp --- a/kded/serializer.cpp +++ b/kded/serializer.cpp @@ -200,6 +200,20 @@ QFile::remove(configFileName(id)); } +bool Serializer::moveConfig(const QString &srcId, const QString &destId) +{ + const QFile srcFile(configFileName(srcId)); + if (srcFile.exists()) { + removeConfig(destId); + if (QFile::copy(configFileName(srcId), configFileName(destId))) { + removeConfig(srcId); + qCDebug(KSCREEN_KDED) << "Restored config" << srcId << "to" << destId; + return true; + } + } + return false; +} + KScreen::OutputPtr Serializer::findOutput(const KScreen::ConfigPtr &config, const QVariantMap& info) { KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, diff --git a/tests/kded/serializertest.cpp b/tests/kded/serializertest.cpp --- a/tests/kded/serializertest.cpp +++ b/tests/kded/serializertest.cpp @@ -46,6 +46,7 @@ void testCorruptUselessConfig(); void testNullConfig(); void testIdenticalOutputs(); + void testMoveConfig(); private: KScreen::ConfigPtr createConfig(bool output1Connected, bool output2Conected); @@ -372,6 +373,92 @@ QCOMPARE(config->screen()->currentSize(), QSize(5940, 2160)); } +void TestSerializer::testMoveConfig() +{ + // Test if restoring a config using Serializer::moveConfig(src, dest) works + // https://bugs.kde.org/show_bug.cgi?id=353029 + + // Load a dualhead config + KScreen::ConfigPtr config = createConfig(true, true); + config = Serializer::config(config, QStringLiteral("twoScreenConfig.json")); + QVERIFY(config); + + // Make sure we don't write into TEST_DATA + QStandardPaths::setTestModeEnabled(true); + Serializer::setConfigPath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/")); + + // Basic assumptions for the remainder of our tests, this is the situation where the lid is opened + QCOMPARE(config->connectedOutputs().count(), 2); + + auto output = config->connectedOutputs().first(); + QCOMPARE(output->name(), QLatin1String("OUTPUT-1")); + QCOMPARE(output->isEnabled(), true); + QCOMPARE(output->isPrimary(), true); + + auto output2 = config->connectedOutputs().last(); + QCOMPARE(output2->name(), QLatin1String("OUTPUT-2")); + QCOMPARE(output2->isEnabled(), true); + QCOMPARE(output2->isPrimary(), false); + + // we fake the lid being closed, first save our current config to _lidOpened + Serializer::saveConfig(config, "0xdeadbeef_lidOpened"); + + // ... then switch off the panel, set primary to the other output + output->setEnabled(false); + output->setPrimary(false); + output2->setPrimary(true); + + // save config as the current one, this is the config we don't want restored, and which we'll overwrite + Serializer::saveConfig(config, "0xdeadbeef"); + + QCOMPARE(output->isEnabled(), false); + QCOMPARE(output->isPrimary(), false); + QCOMPARE(output2->isPrimary(), true); + + // Check if both files exist + QFile openCfg(Serializer::configFileName("0xdeadbeef_lidOpened")); + QFile closedCfg(Serializer::configFileName("0xdeadbeef")); + QVERIFY(openCfg.exists()); + QVERIFY(closedCfg.exists()); + + // Switcheroo... + QVERIFY(Serializer::moveConfig("0xdeadbeef_lidOpened", "0xdeadbeef")); + + // Check actual files, src should be gone, dest must exist + QVERIFY(!openCfg.exists()); + QVERIFY(closedCfg.exists()); + + // Now load the resulting config and make sure the laptop panel is enabled and primary again + config = Serializer::config(config, "0xdeadbeef"); + + output = config->connectedOutputs().first(); + QCOMPARE(output->name(), QLatin1String("OUTPUT-1")); + QCOMPARE(output->isEnabled(), true); + QCOMPARE(output->isPrimary(), true); + + output2 = config->connectedOutputs().last(); + QCOMPARE(output2->name(), QLatin1String("OUTPUT-2")); + QCOMPARE(output2->isEnabled(), true); + QCOMPARE(output2->isPrimary(), false); + + // Make sure we don't screw up when there's no _lidOpened config + QVERIFY(!Serializer::moveConfig("0xdeadbeef_lidOpened", "0xdeadbeef")); + + config = Serializer::config(config, "0xdeadbeef"); + + output = config->connectedOutputs().first(); + QCOMPARE(output->name(), QLatin1String("OUTPUT-1")); + QCOMPARE(output->isEnabled(), true); + QCOMPARE(output->isPrimary(), true); + + output2 = config->connectedOutputs().last(); + QCOMPARE(output2->name(), QLatin1String("OUTPUT-2")); + QCOMPARE(output2->isEnabled(), true); + QCOMPARE(output2->isPrimary(), false); + + Serializer::setConfigPath(QStringLiteral(TEST_DATA "/serializerdata/")); +} + QTEST_MAIN(TestSerializer) #include "serializertest.moc" \ No newline at end of file