diff --git a/kded/daemon.cpp b/kded/daemon.cpp --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -153,11 +153,6 @@ 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 + QLatin1String("_lidOpened"), configId); - } - KScreen::ConfigPtr config = Serializer::loadConfig(m_monitoredConfig, configId); // It's possible that the Serializer returned a nullptr if (!config || !KScreen::Config::canBeApplied(config, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen)) { @@ -213,14 +208,44 @@ void KScreenDaemon::applyIdealConfig() { + m_osdManager->hideOsd(); + if (m_monitoredConfig->connectedOutputs().count() < 2) { - m_osdManager->hideOsd(); doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); } else { - qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; - auto action = m_osdManager->showActionSelector(); - connect(action, &KScreen::OsdAction::selected, - this, &KScreenDaemon::applyOsdAction); + bool lidIsOpen = !Device::self()->isLaptop() + || (Device::self()->isLaptop() && !Device::self()->isLidClosed()); + + if (!lidIsOpen) { + for (KScreen::OutputPtr &output : m_monitoredConfig->outputs()) { + if (output->type() == KScreen::Output::Panel) { + if (output->isConnected() && output->isEnabled()) { + disableOutput(m_monitoredConfig, output); + doApplyConfig(m_monitoredConfig); + break; + } + } + } + } + else { + for (KScreen::OutputPtr &output : m_monitoredConfig->outputs()) { + if (output->type() == KScreen::Output::Panel) { + if (output->isConnected() && !output->isEnabled()) { + output->setEnabled(true); + doApplyConfig(m_monitoredConfig); + break; + } + } + } + } + + if ( (m_monitoredConfig->connectedOutputs().count() > 2) + || (m_monitoredConfig->connectedOutputs().count() == 2 && lidIsOpen) ) { + qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; + auto action = m_osdManager->showActionSelector(lidIsOpen); + connect(action, &KScreen::OsdAction::selected, + this, &KScreenDaemon::applyOsdAction); + } } } @@ -301,7 +326,10 @@ { qCDebug(KSCREEN_KDED) << "displayBtn triggered"; - auto action = m_osdManager->showActionSelector(); + bool lidIsOpen = !Device::self()->isLaptop() + || (Device::self()->isLaptop() && !Device::self()->isLidClosed()); + + auto action = m_osdManager->showActionSelector(lidIsOpen); connect(action, &KScreen::OsdAction::selected, this, &KScreenDaemon::applyOsdAction); } @@ -330,10 +358,12 @@ const QString openConfigId = Serializer::configId(m_monitoredConfig) + QLatin1String("_lidOpened"); if (Serializer::configExists(openConfigId)) { const KScreen::ConfigPtr openedConfig = Serializer::loadConfig(m_monitoredConfig, openConfigId); - Serializer::removeConfig(openConfigId); doApplyConfig(openedConfig); } + else { + applyConfig(); + } } } @@ -358,9 +388,12 @@ // Save the current config with opened lid, just so that we know // how to restore it later const QString configId = Serializer::configId(m_monitoredConfig) + QLatin1String("_lidOpened"); - Serializer::saveConfig(m_monitoredConfig, configId); + if (!Serializer::configExists(configId)) { + Serializer::saveConfig(m_monitoredConfig, configId); + } disableOutput(m_monitoredConfig, output); doApplyConfig(m_monitoredConfig); + applyConfig(); return; } } @@ -429,6 +462,9 @@ void KScreenDaemon::disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output) { const QRect geom = output->geometry(); + const bool disabledWasPrimary = output->isPrimary(); + bool primaryFound = false; + KScreen::OutputPtr firstOther = KScreen::OutputPtr(); qCDebug(KSCREEN_KDED) << "Laptop geometry:" << geom << output->pos() << (output->currentMode() ? output->currentMode()->size() : QSize()); // Move all outputs right from the @p output to left @@ -443,6 +479,18 @@ } qCDebug(KSCREEN_KDED) << "Moving" << otherOutput->name() << "from" << otherOutput->pos() << "to" << otherPos; otherOutput->setPos(otherPos); + + if (otherOutput->isPrimary()) { + primaryFound = true; + } + + if (!firstOther) { + firstOther = otherOutput; + } + } + + if (!primaryFound && disabledWasPrimary && firstOther) { + firstOther->setPrimary(true); } // Disable the output diff --git a/kded/generator.h b/kded/generator.h --- a/kded/generator.h +++ b/kded/generator.h @@ -61,6 +61,11 @@ void ready(); private: + enum LeftOrRight { + Left = 0, + Right + }; + explicit Generator(); ~Generator() override; @@ -70,6 +75,8 @@ void laptop(KScreen::OutputList &connectedOutputs); void singleOutput(KScreen::OutputList &connectedOutputs); void extendToRight(KScreen::OutputList &connectedOutputs); + void extendToLeft(KScreen::OutputList &connectedOutputs); + void extendToLeftOrRight(KScreen::OutputList &connectedOutputs, LeftOrRight leftOrRight); KScreen::ModePtr bestModeForSize(const KScreen::ModeList& modes, const QSize &size); KScreen::ModePtr bestModeForOutput(const KScreen::OutputPtr &output); diff --git a/kded/generator.cpp b/kded/generator.cpp --- a/kded/generator.cpp +++ b/kded/generator.cpp @@ -177,18 +177,31 @@ return config; } - // We cannot try all possible combinations with two and more outputs - if (connectedOutputs.count() > 2) { + KScreen::OutputPtr embedded, external; + embedded = embeddedOutput(connectedOutputs); + + // If the lid is closed, the embedded panel is not available + if (embedded && isLaptop() && isLidClosed()) { + connectedOutputs.remove(embedded->id()); + embedded = KScreen::OutputPtr(); + } + + // Use extendToRight / extendToLeft functions for more than two outputs + if (action == Generator::ExtendToRight && connectedOutputs.count() > 2) { extendToRight(connectedOutputs); return config; } + else if (action == Generator::ExtendToLeft && connectedOutputs.count() > 2) { + extendToLeft(connectedOutputs); + return config; + } - KScreen::OutputPtr embedded, external; - embedded = embeddedOutput(connectedOutputs); + bool embeddedIsExternal = false; // If we don't have an embedded output (desktop with two external screens // for instance), then pretend one of them is embedded if (!embedded) { embedded = connectedOutputs.value(connectedOutputs.keys().first()); + embeddedIsExternal = true; } // Just to be sure if (embedded->modes().isEmpty()) { @@ -204,6 +217,7 @@ connectedOutputs.remove(embedded->id()); external = connectedOutputs.value(connectedOutputs.keys().first()); + // Just to be sure if (external->modes().isEmpty()) { return config; @@ -231,18 +245,39 @@ } case Generator::TurnOffEmbedded: { qCDebug(KSCREEN_KDED) << "Turn off embedded (laptop)"; + if (embeddedIsExternal) { + qCDebug(KSCREEN_KDED) << "embeddedIsExternal: don't turn off embedded (laptop)"; + return config; + } embedded->setEnabled(false); embedded->setPrimary(false); external->setEnabled(true); external->setPrimary(true); const KScreen::ModePtr extMode = bestModeForOutput(external); Q_ASSERT(extMode); external->setCurrentModeId(extMode->id()); + + if (connectedOutputs.count() > 1) { + connectedOutputs.remove(external->id()); + QMapIterator i(connectedOutputs); + while (i.hasNext()) { + i.next(); + i.value()->setEnabled(true); + const KScreen::ModePtr extMode = bestModeForOutput(i.value()); + Q_ASSERT(extMode); + i.value()->setCurrentModeId(extMode->id()); + } + } + return config; } case Generator::TurnOffExternal: { qCDebug(KSCREEN_KDED) << "Turn off external screen"; + if (embeddedIsExternal) { + qCDebug(KSCREEN_KDED) << "embeddedIsExternal: don't turn off external screen"; + return config; + } embedded->setPos(QPoint(0,0)); embedded->setEnabled(true); embedded->setPrimary(true); @@ -252,6 +287,17 @@ external->setEnabled(false); external->setPrimary(false); + + if (connectedOutputs.count() > 1) { + connectedOutputs.remove(external->id()); + QMapIterator i(connectedOutputs); + while (i.hasNext()) { + i.next(); + i.value()->setEnabled(false); + external->setPrimary(false); + } + } + return config; } case Generator::ExtendToRight: { @@ -263,7 +309,6 @@ Q_ASSERT(embeddedMode); embedded->setCurrentModeId(embeddedMode->id()); - Q_ASSERT(embedded->currentMode()); // we must have a mode now const QSize size = embedded->geometry().size(); external->setPos(QPoint(size.width(), 0)); @@ -370,6 +415,7 @@ Q_ASSERT(bestMode); output->setCurrentModeId(bestMode->id()); output->setEnabled(true); + output->setPrimary(true); output->setPos(QPoint(0,0)); } @@ -470,38 +516,61 @@ } } -void Generator::extendToRight(KScreen::OutputList &connectedOutputs) +void Generator::extendToLeftOrRight(KScreen::OutputList &connectedOutputs, LeftOrRight leftOrRight) { ASSERT_OUTPUTS(connectedOutputs); if (connectedOutputs.isEmpty()) { return; } - qCDebug(KSCREEN_KDED) << "Extending to the right"; + if (leftOrRight == Left) + qCDebug(KSCREEN_KDED) << "Extending to the left"; + else + qCDebug(KSCREEN_KDED) << "Extending to the right"; + KScreen::OutputPtr biggest = biggestOutput(connectedOutputs); Q_ASSERT(biggest); connectedOutputs.remove(biggest->id()); biggest->setEnabled(true); biggest->setPrimary(true); - biggest->setPos(QPoint(0,0)); - const KScreen::ModePtr mode = bestModeForOutput(biggest); - Q_ASSERT(mode); - biggest->setCurrentModeId(mode->id()); - int globalWidth = biggest->geometry().width(); + if (leftOrRight == Right) + biggest->setPos(QPoint(0,0)); + + const KScreen::ModePtr biggestMode = bestModeForOutput(biggest); + Q_ASSERT(biggestMode); + biggest->setCurrentModeId(biggestMode->id()); + + int globalWidth = 0; + + if (leftOrRight == Right) + globalWidth = biggest->geometry().width(); Q_FOREACH(KScreen::OutputPtr output, connectedOutputs) { output->setEnabled(true); output->setPrimary(false); output->setPos(QPoint(globalWidth, 0)); - const KScreen::ModePtr mode = bestModeForOutput(output); - Q_ASSERT(mode); - output->setCurrentModeId(mode->id()); + const KScreen::ModePtr otherMode = bestModeForOutput(output); + Q_ASSERT(otherMode); + output->setCurrentModeId(otherMode->id()); globalWidth += output->geometry().width(); } + + if (leftOrRight == Left) + biggest->setPos(QPoint(globalWidth,0)); +} + +void Generator::extendToRight(KScreen::OutputList &connectedOutputs) +{ + extendToLeftOrRight(connectedOutputs, Right); +} + +void Generator::extendToLeft(KScreen::OutputList &connectedOutputs) +{ + extendToLeftOrRight(connectedOutputs, Left); } KScreen::ModePtr Generator::biggestMode(const KScreen::ModeList &modes) diff --git a/kded/osdmanager.h b/kded/osdmanager.h --- a/kded/osdmanager.h +++ b/kded/osdmanager.h @@ -46,7 +46,7 @@ void showOutputIdentifiers(); void showOsd(const QString &icon, const QString &text); void hideOsd(); - OsdAction *showActionSelector(); + OsdAction *showActionSelector(bool lidIsOpen); private: void slotIdentifyOutputs(KScreen::ConfigOperation *op); diff --git a/kded/osdmanager.cpp b/kded/osdmanager.cpp --- a/kded/osdmanager.cpp +++ b/kded/osdmanager.cpp @@ -133,7 +133,7 @@ ); } -OsdAction *OsdManager::showActionSelector() +OsdAction *OsdManager::showActionSelector(bool lidIsOpen) { hideOsd(); @@ -145,7 +145,7 @@ } }); connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, - this, [this, action](const KScreen::ConfigOperation *op) { + this, [this, action, lidIsOpen](const KScreen::ConfigOperation *op) { if (op->hasError()) { qCWarning(KSCREEN_KDED) << op->errorString(); return; @@ -160,18 +160,33 @@ } // Prefer laptop screen - if (output->type() == KScreen::Output::Panel) { + if ((output->type() == KScreen::Output::Panel) && lidIsOpen) { osdOutput = output; break; } // Fallback to primary - if (output->isPrimary()) { + if (output->isPrimary() && lidIsOpen) { osdOutput = output; break; } } - // no laptop or primary screen, just take the first usable one + + // no laptop (with open lid) or primary screen (with open lid), just take the first usable one (with open lid) + if (!osdOutput) { + for (const auto &output : outputs) { + if ( (output->isConnected() && output->isEnabled() && output->currentMode()) + && ( ( (output->type() == KScreen::Output::Panel) && lidIsOpen) + || (output->type() != KScreen::Output::Panel) + ) + ) { + osdOutput = output; + break; + } + } + } + + // nothing up to now, just take the first usable one, even with closed lid if (!osdOutput) { for (const auto &output : outputs) { if (output->isConnected() && output->isEnabled() && output->currentMode()) { diff --git a/kded/serializer.cpp b/kded/serializer.cpp --- a/kded/serializer.cpp +++ b/kded/serializer.cpp @@ -19,6 +19,7 @@ #include "serializer.h" #include "kscreen_daemon_debug.h" #include "generator.h" +#include "device.h" #include #include @@ -57,7 +58,11 @@ if (!config) { return QString(); } - return config->connectedOutputsHash(); + if (Device::self()->isLaptop() && !Device::self()->isLidClosed()) { + return config->connectedOutputsHash() + QLatin1String("_lidOpened"); + } else { + return config->connectedOutputsHash(); + } } bool Serializer::configExists(const KScreen::ConfigPtr &config) diff --git a/tests/osd/osdtest.cpp b/tests/osd/osdtest.cpp --- a/tests/osd/osdtest.cpp +++ b/tests/osd/osdtest.cpp @@ -86,7 +86,7 @@ void OsdTest::showActionSelector() { if (!m_useDBus) { - auto action = getOsdManager()->showActionSelector(); + auto action = getOsdManager()->showActionSelector(true); connect(action, &KScreen::OsdAction::selected, [](KScreen::OsdAction::Action action) { qCDebug(KSCREEN_KDED) << "Selected action:" << action;