diff --git a/CMakeLists.txt b/CMakeLists.txt index 199feb7..07082f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,51 +1,60 @@ cmake_minimum_required(VERSION 3.0) project(KScreen) set(PROJECT_VERSION "5.13.80") set(KSCREEN_VERSION ${PROJECT_VERSION}) add_definitions("-DKSCREEN_VERSION=\"${KSCREEN_VERSION}\"") +add_definitions( + -DQT_USE_QSTRINGBUILDER + -DQT_NO_CAST_TO_ASCII + -DQT_NO_CAST_FROM_ASCII + -DQT_STRICT_ITERATORS + -DQT_NO_URL_CAST_FROM_STRING + -DQT_NO_CAST_FROM_BYTEARRAY +) + set(QT_MIN_VERSION "5.9.0") set(KF5_MIN_VERSION "5.42.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMMarkAsTest) include(ECMQtDeclareLoggingCategory) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS QuickWidgets Test) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS DBusAddons Config ConfigWidgets I18n XmlGui GlobalAccel WidgetsAddons Declarative IconThemes Plasma ) # WARNING PlasmaQuick provides unversioned CMake config find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) set(MIN_LIBKSCREEN_VERSION "5.2.91") find_package(KF5Screen ${MIN_LIBKSCREEN_VERSION} REQUIRED) add_subdirectory(icons) add_subdirectory(kcm) add_subdirectory(kded) add_subdirectory(tests) add_subdirectory(console) install( FILES kscreen.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/console/console.cpp b/console/console.cpp index add36cb..238646b 100644 --- a/console/console.cpp +++ b/console/console.cpp @@ -1,224 +1,224 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * 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 "console.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QTextStream cout(stdout); namespace KScreen { namespace ConfigSerializer { // Exported private symbol in configserializer_p.h in KScreen extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); } } using namespace KScreen; Console::Console(const ConfigPtr &config) : QObject() , m_config(config) { } Console::~Console() { } #include void Console::printConfig() { if (!m_config) { qDebug() << "Config is invalid, probably backend couldn't load"; return; } if (!m_config->screen()) { qDebug() << "No screen in the configuration, broken backend"; return; } connect(m_config.data(), &Config::primaryOutputChanged, [&](const OutputPtr &output) { if (output) { qDebug() << "New primary output: " << output->id() << output->name(); } else { qDebug() << "No primary output."; } }); qDebug() << "Screen:"; qDebug() << "\tmaxSize:" << m_config->screen()->maxSize(); qDebug() << "\tminSize:" << m_config->screen()->minSize(); qDebug() << "\tcurrentSize:" << m_config->screen()->currentSize(); OutputList outputs = m_config->outputs(); Q_FOREACH(const OutputPtr &output, outputs) { qDebug() << "\n-----------------------------------------------------\n"; qDebug() << "Id: " << output->id(); qDebug() << "Name: " << output->name(); qDebug() << "Type: " << typetoString(output->type()); qDebug() << "Connected: " << output->isConnected(); if (!output->isConnected()) { continue; } qDebug() << "Enabled: " << output->isEnabled(); qDebug() << "Primary: " << output->isPrimary(); qDebug() << "Rotation: " << output->rotation(); qDebug() << "Pos: " << output->pos(); qDebug() << "MMSize: " << output->sizeMm(); if (output->currentMode()) { qDebug() << "Size: " << output->size(); } qDebug() << "Scale: " << output->scale(); if (output->clones().isEmpty()) { qDebug() << "Clones: " << "None"; } else { qDebug() << "Clones: " << output->clones().count(); } qDebug() << "Mode: " << output->currentModeId(); qDebug() << "Preferred Mode: " << output->preferredModeId(); qDebug() << "Preferred modes: " << output->preferredModes(); qDebug() << "Modes: "; ModeList modes = output->modes(); Q_FOREACH(const ModePtr &mode, modes) { qDebug() << "\t" << mode->id() << " " << mode->name() << " " << mode->size() << " " << mode->refreshRate(); } Edid* edid = output->edid(); qDebug() << "EDID Info: "; if (edid && edid->isValid()) { qDebug() << "\tDevice ID: " << edid->deviceId(); qDebug() << "\tName: " << edid->name(); qDebug() << "\tVendor: " << edid->vendor(); qDebug() << "\tSerial: " << edid->serial(); qDebug() << "\tEISA ID: " << edid->eisaId(); qDebug() << "\tHash: " << edid->hash(); qDebug() << "\tWidth: " << edid->width(); qDebug() << "\tHeight: " << edid->height(); qDebug() << "\tGamma: " << edid->gamma(); qDebug() << "\tRed: " << edid->red(); qDebug() << "\tGreen: " << edid->green(); qDebug() << "\tBlue: " << edid->blue(); qDebug() << "\tWhite: " << edid->white(); } else { qDebug() << "\tUnavailable"; } } } QString Console::typetoString(const Output::Type& type) const { switch (type) { case Output::Unknown: return QStringLiteral("Unknown"); case Output::Panel: return QStringLiteral("Panel (Laptop)"); case Output::VGA: return QStringLiteral("VGA"); case Output::DVI: return QStringLiteral("DVI"); case Output::DVII: return QStringLiteral("DVI-I"); case Output::DVIA: return QStringLiteral("DVI-A"); case Output::DVID: return QStringLiteral("DVI-D"); case Output::HDMI: return QStringLiteral("HDMI"); case Output::TV: return QStringLiteral("TV"); case Output::TVComposite: return QStringLiteral("TV-Composite"); case Output::TVSVideo: return QStringLiteral("TV-SVideo"); case Output::TVComponent: return QStringLiteral("TV-Component"); case Output::TVSCART: return QStringLiteral("TV-SCART"); case Output::TVC4: return QStringLiteral("TV-C4"); case Output::DisplayPort: return QStringLiteral("DisplayPort"); }; return QStringLiteral("Invalid Type") + QString::number(type); } void Console::printJSONConfig() { QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); cout << doc.toJson(QJsonDocument::Indented); } void Console::printSerializations() { - QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kscreen/"; + QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kscreen/"); qDebug() << "Configs in: " << path; QDir dir(path); QStringList files = dir.entryList(QDir::Files); qDebug() << "Number of files: " << files.count() << endl; QJsonDocument parser; Q_FOREACH(const QString fileName, files) { QJsonParseError error; qDebug() << fileName; - QFile file(path + "/" + fileName); + QFile file(path + QLatin1Char('/') + fileName); file.open(QFile::ReadOnly); QVariant data = parser.fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError) { qDebug() << " " << "can't parse file"; qDebug() << " " << error.errorString(); continue; } qDebug() << parser.toJson(QJsonDocument::Indented) << endl; } } void Console::monitor() { ConfigMonitor::instance()->addConfig(m_config); } void Console::monitorAndPrint() { monitor(); connect(ConfigMonitor::instance(), &ConfigMonitor::configurationChanged, this, &Console::printConfig); } diff --git a/kcm/src/utils.cpp b/kcm/src/utils.cpp index 89a4295..472dc8e 100644 --- a/kcm/src/utils.cpp +++ b/kcm/src/utils.cpp @@ -1,61 +1,61 @@ /* * Copyright 2013 Daniel Vrátil * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "utils.h" #include #include #include QString Utils::outputName(const KScreen::OutputPtr& output) { return outputName(output.data()); } QString Utils::outputName(const KScreen::Output *output) { if (output->type() == KScreen::Output::Panel) { return i18n("Laptop Screen"); } if (output->edid()) { // The name will be "VendorName ModelName (ConnectorName)", // but some components may be empty. QString name; if (!(output->edid()->vendor().isEmpty())) { name = output->edid()->vendor() + QLatin1Char(' '); } if (!output->edid()->name().isEmpty()) { name += output->edid()->name() + QLatin1Char(' '); } if (!name.trimmed().isEmpty()) { - return name + QStringLiteral("(%1)").arg(output->name()); + return name + QLatin1Char('(') + output->name() + QLatin1Char(')'); } } return output->name(); } QString Utils::sizeToString(const QSize &size) { return QStringLiteral("%1x%2").arg(size.width()).arg(size.height()); } diff --git a/kded/daemon.cpp b/kded/daemon.cpp index e05592b..85abd3a 100644 --- a/kded/daemon.cpp +++ b/kded/daemon.cpp @@ -1,451 +1,451 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright 2016 by Sebastian Kügler * * * * 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 "daemon.h" #include "serializer.h" #include "generator.h" #include "device.h" #include "kscreenadaptor.h" #include "kscreen_daemon_debug.h" #include "osdmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KScreenDaemonFactory, "kscreen.json", registerPlugin();) KScreenDaemon::KScreenDaemon(QObject* parent, const QList< QVariant >& ) : KDEDModule(parent) , m_monitoredConfig(nullptr) , m_monitoring(false) , m_changeCompressor(new QTimer(this)) , m_saveTimer(new QTimer(this)) , m_lidClosedTimer(new QTimer(this)) { KScreen::Log::instance(); QMetaObject::invokeMethod(this, "requestConfig", Qt::QueuedConnection); } void KScreenDaemon::requestConfig() { connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, &KScreenDaemon::configReady); } void KScreenDaemon::configReady(KScreen::ConfigOperation* op) { if (op->hasError()) { return; } m_monitoredConfig = qobject_cast(op)->config(); qCDebug(KSCREEN_KDED) << "Config" << m_monitoredConfig.data() << "is ready"; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); init(); } KScreenDaemon::~KScreenDaemon() { Generator::destroy(); Device::destroy(); } void KScreenDaemon::init() { KActionCollection *coll = new KActionCollection(this); QAction* action = coll->addAction(QStringLiteral("display")); action->setText(i18n("Switch Display" )); QList switchDisplayShortcuts({Qt::Key_Display, Qt::MetaModifier + Qt::Key_P}); KGlobalAccel::self()->setGlobalShortcut(action, switchDisplayShortcuts); connect(action, &QAction::triggered, [&](bool) { displayButton(); }); new KScreenAdaptor(this); // Initialize OSD manager to register its dbus interface KScreen::OsdManager::self(); m_saveTimer->setInterval(300); m_saveTimer->setSingleShot(true); connect(m_saveTimer, &QTimer::timeout, this, &KScreenDaemon::saveCurrentConfig); m_changeCompressor->setInterval(10); m_changeCompressor->setSingleShot(true); connect(m_changeCompressor, &QTimer::timeout, this, &KScreenDaemon::applyConfig); m_lidClosedTimer->setInterval(1000); m_lidClosedTimer->setSingleShot(true); connect(m_lidClosedTimer, &QTimer::timeout, this, &KScreenDaemon::lidClosedTimeout); connect(Device::self(), &Device::lidClosedChanged, this, &KScreenDaemon::lidClosedChanged); connect(Device::self(), &Device::resumingFromSuspend, this, [&]() { - KScreen::Log::instance()->setContext("resuming"); + KScreen::Log::instance()->setContext(QLatin1String("resuming")); qCDebug(KSCREEN_KDED) << "Resumed from suspend, checking for screen changes"; // We don't care about the result, we just want to force the backend // to query XRandR so that it will detect possible changes that happened // while the computer was suspended, and will emit the change events. new KScreen::GetConfigOperation(KScreen::GetConfigOperation::NoEDID, this); }); connect(Device::self(), &Device::aboutToSuspend, this, [&]() { qCDebug(KSCREEN_KDED) << "System is going to suspend, won't be changing config (waited for " << (m_lidClosedTimer->interval() - m_lidClosedTimer->remainingTime()) << "ms)"; m_lidClosedTimer->stop(); }); connect(Generator::self(), &Generator::ready, this, &KScreenDaemon::applyConfig); Generator::self()->setCurrentConfig(m_monitoredConfig); monitorConnectedChange(); } void KScreenDaemon::doApplyConfig(const KScreen::ConfigPtr& config) { qCDebug(KSCREEN_KDED) << "doApplyConfig()"; setMonitorForChanges(false); m_monitoredConfig = config; KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig); connect(new KScreen::SetConfigOperation(config), &KScreen::SetConfigOperation::finished, this, [&]() { qCDebug(KSCREEN_KDED) << "Config applied"; setMonitorForChanges(true); }); } void KScreenDaemon::applyConfig() { qCDebug(KSCREEN_KDED) << "Applying config"; if (Serializer::configExists(m_monitoredConfig)) { applyKnownConfig(); return; } applyIdealConfig(); } void KScreenDaemon::applyKnownConfig() { 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); + Serializer::moveConfig(configId + QLatin1String("_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)) { return applyIdealConfig(); } doApplyConfig(config); } void KScreenDaemon::applyOsdAction(KScreen::OsdAction::Action action) { switch (action) { case KScreen::OsdAction::NoAction: qCDebug(KSCREEN_KDED) << "OSD: no action"; return; case KScreen::OsdAction::SwitchToInternal: qCDebug(KSCREEN_KDED) << "OSD: switch to internal"; doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffExternal)); return; case KScreen::OsdAction::SwitchToExternal: qCDebug(KSCREEN_KDED) << "OSD: switch to external"; doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffEmbedded)); return; case KScreen::OsdAction::ExtendLeft: qCDebug(KSCREEN_KDED) << "OSD: extend left"; doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToLeft)); return; case KScreen::OsdAction::ExtendRight: qCDebug(KSCREEN_KDED) << "OSD: extend right"; doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToRight)); return; case KScreen::OsdAction::Clone: qCDebug(KSCREEN_KDED) << "OSD: clone"; doApplyConfig(Generator::self()->displaySwitch(Generator::Clone)); return; } Q_UNREACHABLE(); } void KScreenDaemon::applyIdealConfig() { if (m_monitoredConfig->connectedOutputs().count() < 2) { KScreen::OsdManager::self()->hideOsd(); doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig)); } else { qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; auto action = KScreen::OsdManager::self()->showActionSelector(); connect(action, &KScreen::OsdAction::selected, this, &KScreenDaemon::applyOsdAction); } } void logConfig(const KScreen::ConfigPtr &config) { if (config) { foreach (auto o, config->outputs()) { if (o->isConnected()) { qCDebug(KSCREEN_KDED) << o; } } } } void KScreenDaemon::configChanged() { qCDebug(KSCREEN_KDED) << "Change detected"; logConfig(m_monitoredConfig); // Modes may have changed, fix-up current mode id Q_FOREACH(const KScreen::OutputPtr &output, m_monitoredConfig->outputs()) { if (output->isConnected() && output->isEnabled() && output->currentMode().isNull()) { qCDebug(KSCREEN_KDED) << "Current mode" << output->currentModeId() << "invalid, setting preferred mode" << output->preferredModeId(); output->setCurrentModeId(output->preferredModeId()); doApplyConfig(m_monitoredConfig); } } // Reset timer, delay the writeback m_saveTimer->start(); } void KScreenDaemon::saveCurrentConfig() { qCDebug(KSCREEN_KDED) << "Saving current config to file"; // We assume the config is valid, since it's what we got, but we are interested // in the "at least one enabled screen" check const bool valid = KScreen::Config::canBeApplied(m_monitoredConfig, KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); if (valid) { Serializer::saveConfig(m_monitoredConfig, Serializer::configId(m_monitoredConfig)); logConfig(m_monitoredConfig); } else { qCWarning(KSCREEN_KDED) << "Config does not have at least one screen enabled, WILL NOT save this config, this is not what user wants."; logConfig(m_monitoredConfig); } } void KScreenDaemon::showOsd(const QString &icon, const QString &text) { QDBusMessage msg = QDBusMessage::createMethodCall( QLatin1Literal("org.kde.plasmashell"), QLatin1Literal("/org/kde/osdService"), QLatin1Literal("org.kde.osdService"), QLatin1Literal("showText") ); msg << icon << text; QDBusConnection::sessionBus().asyncCall(msg); } void KScreenDaemon::showOutputIdentifier() { KScreen::OsdManager::self()->showOutputIdentifiers(); } void KScreenDaemon::displayButton() { qCDebug(KSCREEN_KDED) << "displayBtn triggered"; auto action = KScreen::OsdManager::self()->showActionSelector(); connect(action, &KScreen::OsdAction::selected, this, &KScreenDaemon::applyOsdAction); } void KScreenDaemon::lidClosedChanged(bool lidIsClosed) { // Ignore this when we don't have any external monitors, we can't turn off our // only screen if (m_monitoredConfig->connectedOutputs().count() == 1) { return; } if (lidIsClosed) { // Lid is closed, now we wait for couple seconds to find out whether it // will trigger a suspend (see Device::aboutToSuspend), or whether we should // turn off the screen qCDebug(KSCREEN_KDED) << "Lid closed, waiting to see if the computer goes to sleep..."; m_lidClosedTimer->start(); return; } else { qCDebug(KSCREEN_KDED) << "Lid opened!"; // We should have a config with "_lidOpened" suffix lying around. If not, // then the configuration has changed while the lid was closed and we just // use applyConfig() and see what we can do ... - const QString openConfigId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); + const QString openConfigId = Serializer::configId(m_monitoredConfig) + QLatin1String("_lidOpened"); if (Serializer::configExists(openConfigId)) { const KScreen::ConfigPtr openedConfig = Serializer::config(m_monitoredConfig, openConfigId); Serializer::removeConfig(openConfigId); doApplyConfig(openedConfig); } } } void KScreenDaemon::lidClosedTimeout() { // Make sure nothing has changed in the past second... :-) if (!Device::self()->isLidClosed()) { return; } // If we are here, it means that closing the lid did not result in suspend // action. // FIXME: This could be simply because the suspend took longer than m_lidClosedTimer // timeout. Ideally we need to be able to look into PowerDevil config to see // what's the configured action for lid events, but there's no API to do that // and I'm no parsing PowerDevil's configs... qCDebug(KSCREEN_KDED) << "Lid closed without system going to suspend -> turning off the screen"; for (KScreen::OutputPtr &output : m_monitoredConfig->outputs()) { if (output->type() == KScreen::Output::Panel) { if (output->isConnected() && output->isEnabled()) { // Save the current config with opened lid, just so that we know // how to restore it later - const QString configId = Serializer::configId(m_monitoredConfig) + QStringLiteral("_lidOpened"); + const QString configId = Serializer::configId(m_monitoredConfig) + QLatin1String("_lidOpened"); Serializer::saveConfig(m_monitoredConfig, configId); disableOutput(m_monitoredConfig, output); doApplyConfig(m_monitoredConfig); return; } } } } void KScreenDaemon::outputConnectedChanged() { if (!m_changeCompressor->isActive()) { m_changeCompressor->start(); } KScreen::Output *output = qobject_cast(sender()); qCDebug(KSCREEN_KDED) << "outputConnectedChanged():" << output->name(); if (output->isConnected()) { Q_EMIT outputConnected(output->name()); if (!Serializer::configExists(m_monitoredConfig)) { Q_EMIT unknownOutputConnected(output->name()); } } } void KScreenDaemon::monitorConnectedChange() { KScreen::OutputList outputs = m_monitoredConfig->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); } connect(m_monitoredConfig.data(), &KScreen::Config::outputAdded, this, [this] (const KScreen::OutputPtr output) { if (output->isConnected()) { m_changeCompressor->start(); } connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); }, Qt::UniqueConnection ); connect(m_monitoredConfig.data(), &KScreen::Config::outputRemoved, this, &KScreenDaemon::applyConfig, static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); } void KScreenDaemon::setMonitorForChanges(bool enabled) { if (m_monitoring == enabled) { return; } qCDebug(KSCREEN_KDED) << "Monitor for changes: " << enabled; m_monitoring = enabled; if (m_monitoring) { connect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged, Qt::UniqueConnection); } else { disconnect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged); } } void KScreenDaemon::disableOutput(KScreen::ConfigPtr &config, KScreen::OutputPtr &output) { const QRect geom = output->geometry(); qCDebug(KSCREEN_KDED) << "Laptop geometry:" << geom << output->pos() << (output->currentMode() ? output->currentMode()->size() : QSize()); // Move all outputs right from the @p output to left for (KScreen::OutputPtr &otherOutput : config->outputs()) { if (otherOutput == output || !otherOutput->isConnected() || !otherOutput->isEnabled()) { continue; } QPoint otherPos = otherOutput->pos(); if (otherPos.x() >= geom.right() && otherPos.y() >= geom.top() && otherPos.y() <= geom.bottom()) { otherPos.setX(otherPos.x() - geom.width()); } qCDebug(KSCREEN_KDED) << "Moving" << otherOutput->name() << "from" << otherOutput->pos() << "to" << otherPos; otherOutput->setPos(otherPos); } // Disable the output output->setEnabled(false); } KScreen::OutputPtr KScreenDaemon::findEmbeddedOutput(const KScreen::ConfigPtr &config) { Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (output->type() == KScreen::Output::Panel) { return output; } } return KScreen::OutputPtr(); } #include "daemon.moc" diff --git a/kded/osdmanager.cpp b/kded/osdmanager.cpp index 4982c3f..e293937 100644 --- a/kded/osdmanager.cpp +++ b/kded/osdmanager.cpp @@ -1,221 +1,221 @@ /* * Copyright 2016 Sebastian Kügler * * 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 "osdmanager.h" #include "osd.h" #include "kscreen_daemon_debug.h" #include #include #include #include #include namespace KScreen { OsdManager* OsdManager::s_instance = nullptr; OsdAction::OsdAction(QObject *parent) : QObject(parent) { } class OsdActionImpl : public OsdAction { Q_OBJECT public: OsdActionImpl(QObject *parent = nullptr) : OsdAction(parent) {} void setOsd(Osd *osd) { connect(osd, &Osd::osdActionSelected, this, [this](Action action) { Q_EMIT selected(action); deleteLater(); }); } }; OsdManager::OsdManager(QObject *parent) : QObject(parent) , m_cleanupTimer(new QTimer(this)) { - qmlRegisterUncreatableType("org.kde.KScreen", 1, 0, "OsdAction", "You cannot create OsdAction"); + qmlRegisterUncreatableType("org.kde.KScreen", 1, 0, "OsdAction", QStringLiteral("You cannot create OsdAction")); // free up memory when the osd hasn't been used for more than 1 minute m_cleanupTimer->setInterval(60000); m_cleanupTimer->setSingleShot(true); connect(m_cleanupTimer, &QTimer::timeout, this, [this]() { hideOsd(); }); QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.kscreen.osdService")); if (!QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/kscreen/osdService"), this, QDBusConnection::ExportAllSlots)) { qCWarning(KSCREEN_KDED) << "Failed to registerObject"; } } void OsdManager::hideOsd() { qDeleteAll(m_osds); m_osds.clear(); } OsdManager::~OsdManager() { } OsdManager* OsdManager::self() { if (!OsdManager::s_instance) { s_instance = new OsdManager(); } return s_instance; } void OsdManager::showOutputIdentifiers() { connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, &OsdManager::slotIdentifyOutputs); } void OsdManager::slotIdentifyOutputs(KScreen::ConfigOperation *op) { if (op->hasError()) { return; } const KScreen::ConfigPtr config = qobject_cast(op)->config(); Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { continue; } auto osd = m_osds.value(output->name()); if (!osd) { osd = new KScreen::Osd(output, this); m_osds.insert(output->name(), osd); } osd->showOutputIdentifier(output); } m_cleanupTimer->start(); } void OsdManager::showOsd(const QString& icon, const QString& text) { qDeleteAll(m_osds); m_osds.clear(); connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this, icon, text] (KScreen::ConfigOperation *op) { if (op->hasError()) { return; } const KScreen::ConfigPtr config = qobject_cast(op)->config(); Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { continue; } auto osd = m_osds.value(output->name()); if (!osd) { osd = new KScreen::Osd(output, this); m_osds.insert(output->name(), osd); } osd->showGenericOsd(icon, text); } m_cleanupTimer->start(); } ); } OsdAction *OsdManager::showActionSelector() { hideOsd(); OsdActionImpl *action = new OsdActionImpl(this); connect(action, &OsdActionImpl::selected, this, [this]() { for (auto osd : qAsConst(m_osds)) { osd->hideOsd(); } }); connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, [this, action](const KScreen::ConfigOperation *op) { if (op->hasError()) { qCWarning(KSCREEN_KDED) << op->errorString(); return; } // Show selector on alll enabled screens const auto outputs = op->config()->outputs(); KScreen::OutputPtr osdOutput; for (const auto &output : outputs) { if (!output->isConnected() || !output->isEnabled() || !output->currentMode()) { continue; } // Prefer laptop screen if (output->type() == KScreen::Output::Panel) { osdOutput = output; break; } // Fallback to primary if (output->isPrimary()) { osdOutput = output; break; } } // no laptop or primary screen, just take the first usable one if (!osdOutput) { for (const auto &output : outputs) { if (output->isConnected() && output->isEnabled() && output->currentMode()) { osdOutput = output; break; } } } if (!osdOutput) { // huh!? return; } KScreen::Osd* osd = nullptr; if (m_osds.contains(osdOutput->name())) { osd = m_osds.value(osdOutput->name()); } else { osd = new KScreen::Osd(osdOutput, this); m_osds.insert(osdOutput->name(), osd); } action->setOsd(osd); osd->showActionSelector(); m_cleanupTimer->start(); } ); return action; } } #include "osdmanager.moc" diff --git a/kded/serializer.cpp b/kded/serializer.cpp index 47776cf..065e2dd 100644 --- a/kded/serializer.cpp +++ b/kded/serializer.cpp @@ -1,333 +1,333 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * 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 "serializer.h" #include "kscreen_daemon_debug.h" #include "generator.h" #include #include #include #include #include #include #include #include #include #include #include #include QString Serializer::sConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QStringLiteral("/kscreen/"); QString Serializer::sFixedConfig = QStringLiteral("fixed-config"); void Serializer::setConfigPath(const QString &path) { sConfigPath = path; if (!sConfigPath.endsWith(QLatin1Char('/'))) { sConfigPath += QLatin1Char('/'); } } QString Serializer::configFileName(const QString &configId) { if (!QDir().mkpath(sConfigPath)) { return QString(); } return sConfigPath % configId; } QString Serializer::configId(const KScreen::ConfigPtr ¤tConfig) { if (!currentConfig) { return QString(); } KScreen::OutputList outputs = currentConfig->outputs(); QStringList hashList; //qCDebug(KSCREEN_KDED) << "Calculating config ID for" << currentConfig.data(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } //qCDebug(KSCREEN_KDED) << "\tPart of the Id: " << Serializer::outputId(output); hashList.insert(0, Serializer::outputId(output)); } qSort(hashList.begin(), hashList.end()); const QByteArray hash = QCryptographicHash::hash(hashList.join(QString()).toLatin1(), QCryptographicHash::Md5).toHex(); //qCDebug(KSCREEN_KDED) << "\tConfig ID:" << hash; - return hash; + return QString::fromLatin1(hash); } bool Serializer::configExists(const KScreen::ConfigPtr &config) { return Serializer::configExists(Serializer::configId(config)); } bool Serializer::configExists(const QString &id) { return (QFile::exists(sConfigPath % id) || QFile::exists(sConfigPath % sFixedConfig)); } KScreen::ConfigPtr Serializer::config(const KScreen::ConfigPtr ¤tConfig, const QString &id) { KScreen::ConfigPtr config = currentConfig->clone(); QFile file; if (QFile::exists(sConfigPath % sFixedConfig)) { file.setFileName(sConfigPath % sFixedConfig); qCDebug(KSCREEN_KDED) << "found a fixed config, will use " << file.fileName(); } else { file.setFileName(configFileName(id)); } if (!file.open(QIODevice::ReadOnly)) { qCDebug(KSCREEN_KDED) << "failed to open file" << file.fileName(); return KScreen::ConfigPtr(); } KScreen::OutputList outputList = config->outputs(); QJsonDocument parser; QVariantList outputs = parser.fromJson(file.readAll()).toVariant().toList(); Q_FOREACH(KScreen::OutputPtr output, outputList) { if (!output->isConnected() && output->isEnabled()) { output->setEnabled(false); } } 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; } bool Serializer::saveConfig(const KScreen::ConfigPtr &config, const QString &configId) { if (!config || configId.isEmpty()) { return false; } const KScreen::OutputList outputs = config->outputs(); QVariantList outputList; Q_FOREACH(const KScreen::OutputPtr &output, outputs) { if (!output->isConnected()) { continue; } QVariantMap info; info[QStringLiteral("id")] = Serializer::outputId(output); info[QStringLiteral("primary")] = output->isPrimary(); info[QStringLiteral("enabled")] = output->isEnabled(); info[QStringLiteral("rotation")] = output->rotation(); info[QStringLiteral("scale")] = output->scale(); QVariantMap pos; pos[QStringLiteral("x")] = output->pos().x(); pos[QStringLiteral("y")] = output->pos().y(); info[QStringLiteral("pos")] = pos; if (output->isEnabled()) { const KScreen::ModePtr mode = output->currentMode(); if (!mode) { qWarning() << "CurrentMode is null" << output->name(); return false; } QVariantMap modeInfo; modeInfo[QStringLiteral("refresh")] = mode->refreshRate(); QVariantMap modeSize; modeSize[QStringLiteral("width")] = mode->size().width(); modeSize[QStringLiteral("height")] = mode->size().height(); modeInfo[QStringLiteral("size")] = modeSize; info[QStringLiteral("mode")] = modeInfo; } info[QStringLiteral("metadata")] = Serializer::metadata(output); outputList.append(info); } QFile file(configFileName(configId)); if (!file.open(QIODevice::WriteOnly)) { qCWarning(KSCREEN_KDED) << "Failed to open config file for writing! " << file.errorString(); return false; } file.write(QJsonDocument::fromVariant(outputList).toJson()); qCDebug(KSCREEN_KDED) << "Config saved on: " << file.fileName(); return true; } void Serializer::removeConfig(const QString &id) { 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) { const KScreen::OutputList outputs = config->outputs(); // As individual outputs are indexed by a hash of their edid, which is not unique, // to be able to tell apart multiple identical outputs, these need special treatment QStringList duplicateIds; QStringList allIds; allIds.reserve(outputs.count()); Q_FOREACH (const KScreen::OutputPtr &output, outputs) { const auto outputId = Serializer::outputId(output); if (allIds.contains(outputId) && !duplicateIds.contains(outputId)) { duplicateIds << outputId; } allIds << outputId; } allIds.clear(); Q_FOREACH(KScreen::OutputPtr output, outputs) { if (!output->isConnected()) { continue; } const auto outputId = Serializer::outputId(output); if (outputId != info[QStringLiteral("id")].toString()) { continue; } // We may have identical outputs connected, these will have the same id in the config // in order to find the right one, also check the output's name (usually the connector) if (!output->name().isEmpty() && duplicateIds.contains(outputId)) { const auto metadata = info[QStringLiteral("metadata")].toMap(); const auto outputName = metadata[QStringLiteral("name")].toString(); if (output->name() != outputName) { continue; } } const QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); output->setPos(point); output->setPrimary(info[QStringLiteral("primary")].toBool()); output->setEnabled(info[QStringLiteral("enabled")].toBool()); output->setRotation(static_cast(info[QStringLiteral("rotation")].toInt())); output->setScale(info.value(QStringLiteral("scale"), 1).toInt()); const QVariantMap modeInfo = info[QStringLiteral("mode")].toMap(); const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); const QSize size = QSize(modeSize[QStringLiteral("width")].toInt(), modeSize[QStringLiteral("height")].toInt()); qCDebug(KSCREEN_KDED) << "Finding a mode for" << size << "@" << modeInfo[QStringLiteral("refresh")].toFloat(); KScreen::ModeList modes = output->modes(); KScreen::ModePtr matchingMode; Q_FOREACH(const KScreen::ModePtr &mode, modes) { if (mode->size() != size) { continue; } if (!qFuzzyCompare(mode->refreshRate(), modeInfo[QStringLiteral("refresh")].toFloat())) { continue; } qCDebug(KSCREEN_KDED) << "\tFound: " << mode->id() << " " << mode->size() << "@" << mode->refreshRate(); matchingMode = mode; break; } if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to find a matching mode - this means that our config is corrupted" "or a different device with the same serial number has been connected (very unlikely)." "Falling back to preferred modes."; matchingMode = output->preferredMode(); if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to get a preferred mode, falling back to biggest mode."; matchingMode = Generator::biggestMode(modes); if (!matchingMode) { qCWarning(KSCREEN_KDED) << "\tFailed to get biggest mode. Which means there are no modes. Turning off the screen."; output->setEnabled(false); return output; } } } output->setCurrentModeId(matchingMode->id()); return output; } qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current config - this means that our config is corrupted" "or a different device with the same serial number has been connected (very unlikely)."; return KScreen::OutputPtr(); } QString Serializer::outputId(const KScreen::OutputPtr &output) { if (output->edid() && output->edid()->isValid()) { return output->edid()->hash(); } return output->name(); } QVariantMap Serializer::metadata(const KScreen::OutputPtr &output) { QVariantMap metadata; metadata[QStringLiteral("name")] = output->name(); if (!output->edid() || !output->edid()->isValid()) { return metadata; } metadata[QStringLiteral("fullname")] = output->edid()->deviceId(); return metadata; } diff --git a/tests/kded/serializertest.cpp b/tests/kded/serializertest.cpp index 7a61f6a..0366774 100644 --- a/tests/kded/serializertest.cpp +++ b/tests/kded/serializertest.cpp @@ -1,484 +1,484 @@ /************************************************************************************* * Copyright (C) 2015 by Daniel Vrátil * * * * 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 #include #include #include #include #include #include using namespace KScreen; class TestSerializer : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testSimpleConfig(); void testTwoScreenConfig(); void testRotatedScreenConfig(); void testDisabledScreenConfig(); void testConfig404(); void testCorruptConfig(); void testCorruptEmptyConfig(); void testCorruptUselessConfig(); void testNullConfig(); void testIdenticalOutputs(); void testMoveConfig(); void testFixedConfig(); 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 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(QStringLiteral("MODE-%1").arg(i)); mode->setName(QStringLiteral("%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(QStringLiteral("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(QStringLiteral("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() { qputenv("KSCREEN_LOGGING", "false"); Serializer::setConfigPath(QStringLiteral(TEST_DATA "/serializerdata/")); } void TestSerializer::testSimpleConfig() { KScreen::ConfigPtr config = createConfig(true, false); config = Serializer::config(config, QStringLiteral("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, QStringLiteral("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, QStringLiteral("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, QStringLiteral("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)); } void TestSerializer::testConfig404() { KScreen::ConfigPtr config = createConfig(true, true); config = Serializer::config(config, QStringLiteral("filenotfoundConfig.json")); QVERIFY(!config); QVERIFY(config.isNull()); } void TestSerializer::testCorruptConfig() { KScreen::ConfigPtr config = createConfig(true, true); config = Serializer::config(config, QStringLiteral("corruptConfig.json")); QVERIFY(config); QCOMPARE(config->outputs().count(), 2); QVERIFY(config->isValid()); } void TestSerializer::testCorruptEmptyConfig() { KScreen::ConfigPtr config = createConfig(true, true); config = Serializer::config(config, QStringLiteral("corruptEmptyConfig.json")); QVERIFY(config); QCOMPARE(config->outputs().count(), 2); QVERIFY(config->isValid()); } void TestSerializer::testCorruptUselessConfig() { KScreen::ConfigPtr config = createConfig(true, true); config = Serializer::config(config, QStringLiteral("corruptUselessConfig.json")); QVERIFY(config); QCOMPARE(config->outputs().count(), 2); QVERIFY(config->isValid()); } void TestSerializer::testNullConfig() { KScreen::ConfigPtr nullConfig; QVERIFY(!nullConfig); // Null configs have empty configIds QVERIFY(Serializer::configId(nullConfig).isEmpty()); // Load config from a file not found results in a nullptr KScreen::ConfigPtr config = createConfig(true, true); QVERIFY(!Serializer::config(config, QString())); // Wrong config file name should fail to save QCOMPARE(Serializer::saveConfig(config, QString()), false); } void TestSerializer::testIdenticalOutputs() { // Test configuration of a video wall with 6 identical outputs connected // this is the autotest for https://bugs.kde.org/show_bug.cgi?id=325277 KScreen::ScreenPtr screen = KScreen::ScreenPtr::create(); screen->setCurrentSize(QSize(1920, 1080)); screen->setMaxSize(QSize(32768, 32768)); screen->setMinSize(QSize(8, 8)); QList sizes({ QSize(640, 480), QSize(1024, 768), QSize(1920, 1080), 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(QStringLiteral("MODE-%1").arg(i)); mode->setName(QStringLiteral("%1x%2").arg(size.width()).arg(size.height())); mode->setSize(size); mode->setRefreshRate(60.0); modes.insert(mode->id(), mode); } // This one is important, the output id in the config file is a hash of it QByteArray data = QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); // When setting up the outputs, make sure they're not added in alphabetical order // or in the same order of the config file, as that makes the tests accidentally pass KScreen::OutputPtr output1 = KScreen::OutputPtr::create(); output1->setId(1); output1->setEdid(data); output1->setName(QStringLiteral("DisplayPort-0")); output1->setPos(QPoint(0, 0)); output1->setConnected(true); output1->setEnabled(false); output1->setModes(modes); KScreen::OutputPtr output2 = KScreen::OutputPtr::create(); output2->setId(2); output2->setEdid(data); output2->setName(QStringLiteral("DisplayPort-1")); output2->setPos(QPoint(0, 0)); output2->setConnected(true); output2->setEnabled(false); output2->setModes(modes); KScreen::OutputPtr output3 = KScreen::OutputPtr::create(); output3->setId(3); output3->setEdid(data); output3->setName(QStringLiteral("DisplayPort-2")); output3->setPos(QPoint(0, 0)); output3->setConnected(true); output3->setEnabled(false); output3->setModes(modes); KScreen::OutputPtr output6 = KScreen::OutputPtr::create(); output6->setId(6); output6->setEdid(data); output6->setName(QStringLiteral("DVI-0")); output6->setPos(QPoint(0, 0)); output6->setConnected(true); output6->setEnabled(false); output6->setModes(modes); KScreen::OutputPtr output4 = KScreen::OutputPtr::create(); output4->setId(4); output4->setEdid(data); output4->setName(QStringLiteral("DisplayPort-3")); output4->setPos(QPoint(0, 0)); output4->setConnected(true); output4->setEnabled(false); output4->setModes(modes); KScreen::OutputPtr output5 = KScreen::OutputPtr::create(); output5->setId(5); output5->setEdid(data); output5->setName(QStringLiteral("DVI-1")); output5->setPos(QPoint(0, 0)); output5->setConnected(true); output5->setEnabled(false); output5->setModes(modes); KScreen::ConfigPtr config = KScreen::ConfigPtr::create(); config->setScreen(screen); config->addOutput(output6); config->addOutput(output2); config->addOutput(output5); config->addOutput(output4); config->addOutput(output3); config->addOutput(output1); QHash positions; - positions["DisplayPort-0"] = QPoint(0, 1080); - positions["DisplayPort-1"] = QPoint(2100, 30); - positions["DisplayPort-2"] = QPoint(2100, 1080); - positions["DisplayPort-3"] = QPoint(4020, 0); - positions["DVI-0"] = QPoint(4020, 1080); - positions["DVI-1"] = QPoint(0, 0); + positions[QStringLiteral("DisplayPort-0")] = QPoint(0, 1080); + positions[QStringLiteral("DisplayPort-1")] = QPoint(2100, 30); + positions[QStringLiteral("DisplayPort-2")] = QPoint(2100, 1080); + positions[QStringLiteral("DisplayPort-3")] = QPoint(4020, 0); + positions[QStringLiteral("DVI-0")] = QPoint(4020, 1080); + positions[QStringLiteral("DVI-1")] = QPoint(0, 0); config = Serializer::config(config, QStringLiteral("outputgrid_2x3.json")); QVERIFY(config); QCOMPARE(config->connectedOutputs().count(), 6); Q_FOREACH (auto output, config->connectedOutputs()) { QVERIFY(positions.keys().contains(output->name())); QVERIFY(output->name() != Serializer::outputId(output)); QCOMPARE(positions[output->name()], output->pos()); QCOMPARE(output->currentMode()->size(), QSize(1920, 1080)); QCOMPARE(output->currentMode()->refreshRate(), 60.0); QVERIFY(output->isEnabled()); } 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"); + Serializer::saveConfig(config, QStringLiteral("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"); + Serializer::saveConfig(config, QStringLiteral("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")); + QFile openCfg(Serializer::configFileName(QStringLiteral("0xdeadbeef_lidOpened"))); + QFile closedCfg(Serializer::configFileName(QStringLiteral("0xdeadbeef"))); QVERIFY(openCfg.exists()); QVERIFY(closedCfg.exists()); // Switcheroo... - QVERIFY(Serializer::moveConfig("0xdeadbeef_lidOpened", "0xdeadbeef")); + QVERIFY(Serializer::moveConfig(QStringLiteral("0xdeadbeef_lidOpened"), QStringLiteral("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"); + config = Serializer::config(config, QStringLiteral("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")); + QVERIFY(!Serializer::moveConfig(QStringLiteral("0xdeadbeef_lidOpened"), QStringLiteral("0xdeadbeef"))); - config = Serializer::config(config, "0xdeadbeef"); + config = Serializer::config(config, QStringLiteral("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/")); } void TestSerializer::testFixedConfig() { // 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/")); // save config as the current one, this is the config we don't want restored, and which we'll overwrite Serializer::saveConfig(config, Serializer::sFixedConfig); // Check if both files exist QFile fixedCfg(Serializer::configFileName(Serializer::sFixedConfig)); QVERIFY(fixedCfg.exists()); } QTEST_MAIN(TestSerializer) #include "serializertest.moc" diff --git a/tests/osd/main.cpp b/tests/osd/main.cpp index 4bbff93..35216e5 100644 --- a/tests/osd/main.cpp +++ b/tests/osd/main.cpp @@ -1,64 +1,64 @@ /************************************************************************************* * Copyright 2014-2016 by Sebastian Kügler * * * * 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 "osdtest.h" #include #include int main(int argc, char **argv) { QGuiApplication app(argc, argv); - QCommandLineOption dbus = QCommandLineOption(QStringList() << QStringLiteral("d") << "dbus", + QCommandLineOption dbus = QCommandLineOption(QStringList() << QStringLiteral("d") << QStringLiteral("dbus"), QStringLiteral("Call over dbus")); - QCommandLineOption outputid = QCommandLineOption(QStringList() << QStringLiteral("o") << "outputidentifiers", + QCommandLineOption outputid = QCommandLineOption(QStringList() << QStringLiteral("o") << QStringLiteral("outputidentifiers"), QStringLiteral("Show output identifier")); - QCommandLineOption icon = QCommandLineOption(QStringList() << QStringLiteral("i") << "icon", + QCommandLineOption icon = QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("icon"), QStringLiteral("Icon to use for OSD"), QStringLiteral("preferences-desktop-display-randr")); - QCommandLineOption message = QCommandLineOption(QStringList() << QStringLiteral("m") << "message", + QCommandLineOption message = QCommandLineOption(QStringList() << QStringLiteral("m") << QStringLiteral("message"), QStringLiteral("Icon to use for OSD"), QStringLiteral("OSD Test")); QCommandLineOption selector = QCommandLineOption({ QStringLiteral("s"), QStringLiteral("selector") }, QStringLiteral("Show new screen action selector")); KScreen::OsdTest osdtest; QCommandLineParser parser; parser.addHelpOption(); parser.addOption(dbus); parser.addOption(outputid); parser.addOption(icon); parser.addOption(message); parser.addOption(selector); parser.process(app); if (parser.isSet(dbus)) { osdtest.setUseDBus(true); } if (parser.isSet(outputid)) { osdtest.showOutputIdentifiers(); } else if (parser.isSet(selector)) { osdtest.showActionSelector(); } else { osdtest.showGenericOsd(parser.value(icon), parser.value(message)); } if (parser.isSet(outputid)) { } return app.exec(); }