diff --git a/kcm/config_handler.cpp b/kcm/config_handler.cpp --- a/kcm/config_handler.cpp +++ b/kcm/config_handler.cpp @@ -44,19 +44,19 @@ this, &ConfigHandler::checkScreenNormalization); connect(m_outputs, &OutputModel::sizeChanged, this, &ConfigHandler::checkScreenNormalization); - connect(m_outputs, &OutputModel::changed, - this, [this]() { - checkNeedsSave(); - Q_EMIT changed(); - }); for (const KScreen::OutputPtr &output : config->outputs()) { initOutput(output); } m_lastNormalizedScreenSize = screenSize(); m_initialRetention = getRetention(); Q_EMIT retentionChanged(); + connect(m_outputs, &OutputModel::changed, + this, [this]() { + checkNeedsSave(); + Q_EMIT changed(); + }); connect(m_config.data(), &KScreen::Config::outputAdded, this, [this]() { Q_EMIT outputConnect(true); }); connect(m_config.data(), &KScreen::Config::outputRemoved, @@ -95,16 +95,23 @@ { if (m_config->supportedFeatures() & KScreen::Config::Feature::PrimaryDisplay) { - if (m_config->primaryOutput()->hashMd5() != - m_initialConfig->primaryOutput()->hashMd5() ) { + if (m_config->primaryOutput() && m_initialConfig->primaryOutput()) { + if (m_config->primaryOutput()->hashMd5() != + m_initialConfig->primaryOutput()->hashMd5() ) { + Q_EMIT needsSaveChecked(true); + return; + } + } else if ((bool)m_config->primaryOutput() != (bool)m_initialConfig->primaryOutput()) { Q_EMIT needsSaveChecked(true); return; } } + if (m_initialRetention != getRetention()) { Q_EMIT needsSaveChecked(true); return; } + for (const auto &output : m_config->connectedOutputs()) { const QString hash = output->hashMd5(); for (const auto &initialOutput : m_initialConfig->outputs()) { diff --git a/kcm/output_model.h b/kcm/output_model.h --- a/kcm/output_model.h +++ b/kcm/output_model.h @@ -90,10 +90,12 @@ {} KScreen::OutputPtr ptr; QPoint pos; - QPoint replicaReset; + QPoint posReset = QPoint(-1, -1); }; void roleChanged(int outputId, OutputRoles role); + + void resetPosition(const Output &output); void reposition(); void updatePositions(); void updateOrder(); @@ -106,9 +108,12 @@ */ void snap(const Output &output, QPoint &dest); + bool setEnabled(int outputIndex, bool enable); + bool setResolution(int outputIndex, int resIndex); bool setRefreshRate(int outputIndex, int refIndex); bool setRotation(int outputIndex, KScreen::Output::Rotation rotation); + int resolutionIndex(const KScreen::OutputPtr &output) const; int refreshRateIndex(const KScreen::OutputPtr &output) const; QVariantList resolutionsStrings(const KScreen::OutputPtr &output) const; diff --git a/kcm/output_model.cpp b/kcm/output_model.cpp --- a/kcm/output_model.cpp +++ b/kcm/output_model.cpp @@ -109,14 +109,7 @@ break; case EnabledRole: if (value.canConvert()) { - bool enable = value.toBool(); - if (output.ptr->isEnabled() == enable) { - return false; - } - output.ptr->setEnabled(enable); - reposition(); - Q_EMIT dataChanged(index, index, {role}); - return true; + return setEnabled(index.row(), value.toBool()); } break; case PrimaryRole: @@ -243,6 +236,47 @@ } } +void OutputModel::resetPosition(const Output &output) +{ + if (output.posReset.x() < 0) { + // KCM was closed in between. + for (const Output &out : m_outputs) { + if (out.ptr->id() == output.ptr->id()) { + continue; + } + if (out.ptr->geometry().right() > output.ptr->pos().x()) { + output.ptr->setPos(out.ptr->geometry().topRight()); + } + } + } else { + output.ptr->setPos(/*output.ptr->pos() - */output.posReset); + } +} + +bool OutputModel::setEnabled(int outputIndex, bool enable) +{ + Output &output = m_outputs[outputIndex]; + + if (output.ptr->isEnabled() == enable) { + return false; + } + + output.ptr->setEnabled(enable); + + if (enable) { + resetPosition(output); + + setResolution(outputIndex, resolutionIndex(output.ptr)); + reposition(); + } else { + output.posReset = output.ptr->pos(); + } + + QModelIndex index = createIndex(outputIndex, 0); + Q_EMIT dataChanged(index, index, {EnabledRole}); + return true; +} + inline bool refreshRateCompare(float rate1, float rate2) { @@ -258,15 +292,16 @@ } const QSize size = resolutionList[resIndex]; - const float oldRefreshRate = output.ptr->currentMode()->refreshRate(); + const float oldRate = output.ptr->currentMode() ? output.ptr->currentMode()->refreshRate() : + -1; const auto modes = output.ptr->modes(); auto modeIt = std::find_if(modes.begin(), modes.end(), - [size, oldRefreshRate](const KScreen::ModePtr &mode) { + [size, oldRate](const KScreen::ModePtr &mode) { // TODO: we don't want to compare against old refresh rate if // refresh rate selection is auto. return mode->size() == size && - refreshRateCompare(mode->refreshRate(), oldRefreshRate); + refreshRateCompare(mode->refreshRate(), oldRate); }); if (modeIt == modes.end()) { @@ -354,11 +389,13 @@ int OutputModel::resolutionIndex(const KScreen::OutputPtr &output) const { - if (!output->currentMode()) { + const QSize currentResolution = output->enforcedModeSize(); + + if (!currentResolution.isValid()) { return 0; } + const auto sizes = resolutions(output); - const QSize currentResolution = output->currentMode()->size(); const auto it = std::find_if(sizes.begin(), sizes.end(), @@ -427,10 +464,16 @@ &output) const { QVector hits; - if (!output->currentMode()) { + + QSize baseSize; + if (output->currentMode()) { + baseSize = output->currentMode()->size(); + } else if (output->preferredMode()) { + baseSize = output->preferredMode()->size(); + } + if (!baseSize.isValid()) { return hits; } - const auto baseSize = output->currentMode()->size(); for (const auto &mode : output->modes()) { if (mode->size() != baseSize) { @@ -486,28 +529,15 @@ return false; } output.ptr->setReplicationSource(0); - - if (output.replicaReset.isNull()) { - // KCM was closed in between. - for (const Output &out : m_outputs) { - if (out.ptr->id() == output.ptr->id()) { - continue; - } - if (out.ptr->geometry().right() > output.ptr->pos().x()) { - output.ptr->setPos(out.ptr->geometry().topRight()); - } - } - } else { - output.ptr->setPos(output.ptr->pos() - output.replicaReset); - } + resetPosition(output); } else { const int sourceId = m_outputs[sourceIndex].ptr->id(); if (oldSourceId == sourceId) { // no change return false; } output.ptr->setReplicationSource(sourceId); - output.replicaReset = m_outputs[sourceIndex].ptr->pos() - output.ptr->pos(); + output.posReset = output.ptr->pos(); output.ptr->setPos(m_outputs[sourceIndex].ptr->pos()); } diff --git a/kded/config.cpp b/kded/config.cpp --- a/kded/config.cpp +++ b/kded/config.cpp @@ -117,7 +117,7 @@ QSize screenSize; for (const auto &output : config->data()->outputs()) { - if (!output->isConnected() || !output->isEnabled()) { + if (!output->isPositionable()) { continue; } @@ -171,32 +171,52 @@ const auto control = ControlConfig(m_data); + const auto oldConfig = readFile(); + KScreen::OutputList oldOutputs; + if (oldConfig) { + oldOutputs = oldConfig->data()->outputs(); + } + QVariantList outputList; - Q_FOREACH(const KScreen::OutputPtr &output, outputs) { + for (const KScreen::OutputPtr &output : outputs) { QVariantMap info; + const auto oldOutputIt = std::find_if(oldOutputs.constBegin(), oldOutputs.constEnd(), + [output](const KScreen::OutputPtr &out) { + return out->hashMd5() == output->hashMd5(); + } + ); + const KScreen::OutputPtr oldOutput = oldOutputIt != oldOutputs.constEnd() ? *oldOutputIt : + nullptr; + if (!output->isConnected()) { continue; } - if (!Output::writeGlobalPart(output, info)) { - continue; - } + Output::writeGlobalPart(output, info, oldOutput); info[QStringLiteral("primary")] = output->isPrimary(); info[QStringLiteral("enabled")] = output->isEnabled(); - QString replicationSourceHash; - if (int sourceId = output->replicationSource()) { - replicationSourceHash = m_data->output(sourceId)->hashMd5(); - } - info[QStringLiteral("replicate")] = replicationSourceHash; - - QVariantMap pos; - pos[QStringLiteral("x")] = output->pos().x(); - pos[QStringLiteral("y")] = output->pos().y(); - info[QStringLiteral("pos")] = pos; - - if (control.getOutputRetention(output->hash(), output->name()) != Control::OutputRetention::Individual) { + auto setOutputConfigInfo = [this, &info](const KScreen::OutputPtr &out) { + if (!out) { + return; + } + QString replicationSourceHash; + if (int sourceId = out->replicationSource()) { + replicationSourceHash = m_data->output(sourceId)->hashMd5(); + } + info[QStringLiteral("replicate")] = replicationSourceHash; + + QVariantMap pos; + pos[QStringLiteral("x")] = out->pos().x(); + pos[QStringLiteral("y")] = out->pos().y(); + info[QStringLiteral("pos")] = pos; + }; + setOutputConfigInfo(output->isEnabled() ? output : oldOutput); + + if (output->isEnabled() && + control.getOutputRetention(output->hash(), output->name()) != + Control::OutputRetention::Individual) { // try to update global output data Output::writeGlobal(output); } diff --git a/kded/output.h b/kded/output.h --- a/kded/output.h +++ b/kded/output.h @@ -29,7 +29,8 @@ static void readInOutputs(KScreen::ConfigPtr config, const QVariantList &outputsInfo); static void writeGlobal(const KScreen::OutputPtr &output); - static bool writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info); + static bool writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info, + const KScreen::OutputPtr &fallback); static QString dirPath(); diff --git a/kded/output.cpp b/kded/output.cpp --- a/kded/output.cpp +++ b/kded/output.cpp @@ -351,32 +351,38 @@ return metadata; } -bool Output::writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info) +bool Output::writeGlobalPart(const KScreen::OutputPtr &output, QVariantMap &info, + const KScreen::OutputPtr &fallback) { - if (!output->isEnabled()) { - return false; - } - const KScreen::ModePtr mode = output->currentMode(); - if (!mode) { - qWarning() << "CurrentMode is null" << output->name(); - return false; - } info[QStringLiteral("id")] = output->hash(); + info[QStringLiteral("metadata")] = metadata(output); info[QStringLiteral("rotation")] = output->rotation(); // Round scale to one digit. info[QStringLiteral("scale")] = int(output->scale() * 10 + 0.5) / 10.; - info[QStringLiteral("metadata")] = metadata(output); - QVariantMap modeInfo; - modeInfo[QStringLiteral("refresh")] = mode->refreshRate(); + float refreshRate = -1.; + QSize modeSize; + if (output->currentMode() && output->isEnabled()) { + refreshRate = output->currentMode()->refreshRate(); + modeSize = output->currentMode()->size(); + } else if (fallback && fallback->currentMode()) { + refreshRate = fallback->currentMode()->refreshRate(); + modeSize = fallback->currentMode()->size(); + } + + if (refreshRate < 0 || !modeSize.isValid()) { + return false; + } + + modeInfo[QStringLiteral("refresh")] = refreshRate; - QVariantMap modeSize; - modeSize[QStringLiteral("width")] = mode->size().width(); - modeSize[QStringLiteral("height")] = mode->size().height(); - modeInfo[QStringLiteral("size")] = modeSize; + QVariantMap modeSizeMap; + modeSizeMap[QStringLiteral("width")] = modeSize.width(); + modeSizeMap[QStringLiteral("height")] = modeSize.height(); + modeInfo[QStringLiteral("size")] = modeSizeMap; info[QStringLiteral("mode")] = modeInfo; @@ -387,7 +393,7 @@ { // get old values and subsequently override QVariantMap info = getGlobalData(output); - if (!writeGlobalPart(output, info)) { + if (!writeGlobalPart(output, info, nullptr)) { return; }