diff --git a/CMakeLists.txt b/CMakeLists.txt index d01b716..d7e17d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,104 +1,115 @@ cmake_minimum_required(VERSION 3.0) project(libkscreen) set(PROJECT_VERSION "5.13.80") set(REQUIRED_QT_VERSION "5.9.0") set(KF5_MIN_VERSION "5.42.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMSetupVersion) include(ECMMarkAsTest) include(ECMGenerateHeaders) include(ECMQtDeclareLoggingCategory) include(ECMAddQch) include(FeatureSummary) include(CheckCXXCompilerFlag) include(CMakePackageConfigHelpers) include(GenerateExportHeader) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core DBus Gui Test X11Extras) # Wayland backend find_package(KF5Wayland ${KF5_MIN_VERSION} CONFIG REQUIRED) add_feature_info("KF5Wayland" KF5Wayland_FOUND "Required for building libkscreen's KWayland backend") # xrandr backend find_package(XCB COMPONENTS XCB RANDR) set_package_properties(XCB PROPERTIES TYPE OPTIONAL PURPOSE "Required for building XRandR backends" ) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") # library setup +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 + -DQT_NO_SIGNALS_SLOTS_KEYWORDS + -DQT_USE_FAST_OPERATOR_PLUS +) + set(KF5_VERSION ${PROJECT_VERSION}) #When we are happy with the api, we can sync with frameworks ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KSCREEN VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kscreen_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfigVersion.cmake" SOVERSION 7) check_cxx_compiler_flag(-fvisibility=hidden _HAVE_VISIBILITY) if (_HAVE_VISIBILITY AND NOT WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") check_cxx_compiler_flag(-fvisibility-inlines-hidden _HAVE_VISIBILITY_INLINES) if (_HAVE_VISIBILITY_INLINES) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") endif (_HAVE_VISIBILITY_INLINES) endif (_HAVE_VISIBILITY AND NOT WIN32) add_subdirectory(src) add_subdirectory(backends) if(BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5Screen") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Screen_QCH FILE KF5ScreenQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5ScreenQchTargets.cmake\")") endif() configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5ScreenConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5ScreenTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5ScreenTargets.cmake NAMESPACE KF5:: COMPONENT Devel ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kscreen_version.h" DESTINATION "${KF5_INCLUDE_INSTALL_DIR}" COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/testkwaylandbackend.cpp b/autotests/testkwaylandbackend.cpp index 65c56e0..13f2794 100644 --- a/autotests/testkwaylandbackend.cpp +++ b/autotests/testkwaylandbackend.cpp @@ -1,316 +1,316 @@ /************************************************************************************* * Copyright 2014-2015 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include #include #include #include #include "../src/backendmanager_p.h" #include "../src/getconfigoperation.h" #include "../src/setconfigoperation.h" #include "../src/config.h" #include "../src/configmonitor.h" #include "../src/output.h" #include "../src/mode.h" #include "../src/edid.h" // KWayland #include #include #include "waylandtestserver.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND, "kscreen.kwayland") using namespace KScreen; class testWaylandBackend : public QObject { Q_OBJECT public: explicit testWaylandBackend(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void loadConfig(); void verifyConfig(); void verifyOutputs(); void verifyModes(); void verifyScreen(); void verifyIds(); void verifyFeatures(); void simpleWrite(); void addOutput(); void removeOutput(); void testEdid(); private: ConfigPtr m_config; WaylandTestServer *m_server; KWayland::Server::OutputDeviceInterface *m_serverOutputDevice; }; testWaylandBackend::testWaylandBackend(QObject *parent) : QObject(parent) , m_config(nullptr) { qputenv("KSCREEN_LOGGING", "false"); m_server = new WaylandTestServer(this); m_server->setConfig(TEST_DATA + QStringLiteral("multipleoutput.json")); } void testWaylandBackend::initTestCase() { qputenv("KSCREEN_BACKEND", "kwayland"); KScreen::BackendManager::instance()->shutdownBackend(); // This is how KWayland will pick up the right socket, // and thus connect to our internal test server. - setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit(), 1); + setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit().constData(), true); m_server->start(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); } void testWaylandBackend::loadConfig() { GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); QVERIFY(m_config->isValid()); qCDebug(KSCREEN_WAYLAND) << "ops" << m_config->outputs(); } void testWaylandBackend::verifyConfig() { QVERIFY(m_config != nullptr); if (!m_config) { QSKIP("Wayland backend invalid", SkipAll); } } void testWaylandBackend::verifyScreen() { ScreenPtr screen = m_config->screen(); QVERIFY(screen->minSize().width() <= screen->maxSize().width()); QVERIFY(screen->minSize().height() <= screen->maxSize().height()); QVERIFY(screen->minSize().width() <= screen->currentSize().width()); QVERIFY(screen->minSize().height() <= screen->currentSize().height()); QVERIFY(screen->maxSize().width() >= screen->currentSize().width()); QVERIFY(screen->maxSize().height() >= screen->currentSize().height()); QVERIFY(m_config->screen()->maxActiveOutputsCount() > 0); } void testWaylandBackend::verifyOutputs() { bool primaryFound = false; Q_FOREACH (const KScreen::OutputPtr op, m_config->outputs()) { if (op->isPrimary()) { primaryFound = true; } } //qCDebug(KSCREEN_WAYLAND) << "Primary found? " << primaryFound << m_config->outputs(); QVERIFY(primaryFound); QVERIFY(m_config->outputs().count()); QCOMPARE(m_server->outputCount(), m_config->outputs().count()); KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary->isEnabled()); QVERIFY(primary->isConnected()); QList ids; Q_FOREACH (const auto &output, m_config->outputs()) { QVERIFY(!output->name().isEmpty()); QVERIFY(output->id() > -1); QVERIFY(output->isConnected()); QVERIFY(output->geometry() != QRectF(1,1,1,1)); QVERIFY(output->geometry() != QRectF()); QVERIFY(output->sizeMm() != QSize()); QVERIFY(output->edid() != nullptr); QVERIFY(output->preferredModes().size() == 1); QCOMPARE(output->rotation(), Output::None); QVERIFY(!ids.contains(output->id())); ids << output->id(); } } void testWaylandBackend::verifyModes() { KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary); QVERIFY(primary->modes().count() > 0); Q_FOREACH (const auto &output, m_config->outputs()) { Q_FOREACH (auto mode, output->modes()) { QVERIFY(!mode->name().isEmpty()); QVERIFY(mode->refreshRate() > 0); QVERIFY(mode->size().isValid()); } } } void testWaylandBackend::verifyIds() { QList ids; Q_FOREACH (const auto &output, m_config->outputs()) { QVERIFY(ids.contains(output->id()) == false); QVERIFY(output->id() > 0); ids << output->id(); } } void testWaylandBackend::simpleWrite() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); auto output = m_config->output(1); auto n_mode = QStringLiteral("800x600@60"); auto o_mode = output->currentModeId(); output->setCurrentModeId(n_mode); auto setop = new SetConfigOperation(m_config); QVERIFY(setop->exec()); } void testWaylandBackend::cleanupTestCase() { m_config->deleteLater(); KScreen::BackendManager::instance()->shutdownBackend(); } void testWaylandBackend::addOutput() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QCOMPARE(config->outputs().count(), 2); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); // Now add an outputdevice on the server side m_serverOutputDevice = m_server->display()->createOutputDevice(this); m_serverOutputDevice->setUuid("1337"); OutputDeviceInterface::Mode m0; m0.id = 0; m0.size = QSize(800, 600); m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); m_serverOutputDevice->addMode(m0); OutputDeviceInterface::Mode m1; m1.id = 1; m1.size = QSize(1024, 768); m_serverOutputDevice->addMode(m1); OutputDeviceInterface::Mode m2; m2.id = 2; m2.size = QSize(1280, 1024); m2.refreshRate = 90000; m_serverOutputDevice->addMode(m2); m_serverOutputDevice->setCurrentMode(1); m_serverOutputDevice->create(); QVERIFY(configSpy.wait()); //QTRY_VERIFY(configSpy.count()); GetConfigOperation *op2 = new GetConfigOperation(); op2->exec(); auto newconfig = op2->config(); QCOMPARE(newconfig->outputs().count(), 3); } void testWaylandBackend::removeOutput() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QCOMPARE(config->outputs().count(), 3); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); delete m_serverOutputDevice; QVERIFY(configSpy.wait()); GetConfigOperation *op2 = new GetConfigOperation(); op2->exec(); auto newconfig = op2->config(); QCOMPARE(newconfig->outputs().count(), 2); } void testWaylandBackend::testEdid() { m_server->showOutputs(); QByteArray data = QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); auto edid = new Edid(data); QVERIFY(edid->isValid()); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QVERIFY(config->outputs().count() > 0); auto o = config->outputs().last(); qCDebug(KSCREEN_WAYLAND) << "Edid: " << o->edid()->isValid(); QVERIFY(o->edid()->isValid()); QCOMPARE(o->edid()->deviceId(), edid->deviceId()); QCOMPARE(o->edid()->name(), edid->name()); QCOMPARE(o->edid()->vendor(), edid->vendor()); QCOMPARE(o->edid()->eisaId(), edid->eisaId()); QCOMPARE(o->edid()->serial(), edid->serial()); QCOMPARE(o->edid()->hash(), edid->hash()); QCOMPARE(o->edid()->width(), edid->width()); QCOMPARE(o->edid()->height(), edid->height()); QCOMPARE(o->edid()->gamma(), edid->gamma()); QCOMPARE(o->edid()->red(), edid->red()); QCOMPARE(o->edid()->green(), edid->green()); QCOMPARE(o->edid()->blue(), edid->blue()); QCOMPARE(o->edid()->white(), edid->white()); } void testWaylandBackend::verifyFeatures() { GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(Config::Feature::Writable)); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::PrimaryDisplay)); } QTEST_GUILESS_MAIN(testWaylandBackend) #include "testkwaylandbackend.moc" diff --git a/autotests/testkwaylanddpms.cpp b/autotests/testkwaylanddpms.cpp index 3e263d5..069e543 100644 --- a/autotests/testkwaylanddpms.cpp +++ b/autotests/testkwaylanddpms.cpp @@ -1,126 +1,126 @@ /************************************************************************************* * Copyright 2016 by Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include #include #include #include #include #include #include #include "waylandtestserver.h" static const QString s_socketName = QStringLiteral("libkscreen-test-wayland-backend-0"); // static const QString s_socketName = QStringLiteral("wayland-0"); Q_LOGGING_CATEGORY(KSCREEN, "kscreen") using namespace KWayland::Client; class TestDpmsClient : public QObject { Q_OBJECT public: explicit TestDpmsClient(QObject *parent = nullptr); Q_SIGNALS: void dpmsAnnounced(); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testDpmsConnect(); private: ConnectionThread *m_connection; QThread *m_thread; Registry *m_registry; KScreen::WaylandTestServer *m_server; }; TestDpmsClient::TestDpmsClient(QObject *parent) : QObject(parent) , m_server(nullptr) { - setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit(), 1); + setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit().constData(), true); m_server = new KScreen::WaylandTestServer(this); m_server->start(); } void TestDpmsClient::initTestCase() { // setup connection m_connection = new KWayland::Client::ConnectionThread; m_connection->setSocketName(s_socketName); QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); QSignalSpy dpmsSpy(this, &TestDpmsClient::dpmsAnnounced); m_connection->initConnection(); QVERIFY(connectedSpy.wait(100)); m_registry = new KWayland::Client::Registry; m_registry->create(m_connection); QObject::connect(m_registry, &Registry::interfacesAnnounced, this, [this] { const bool hasDpms = m_registry->hasInterface(Registry::Interface::Dpms); if (hasDpms) { qDebug() << QStringLiteral("Compositor provides a DpmsManager"); } else { qDebug() << QStringLiteral("Compositor does not provid a DpmsManager"); } emit this->dpmsAnnounced(); }); m_registry->setup(); QVERIFY(dpmsSpy.wait(100)); } void TestDpmsClient::cleanupTestCase() { m_thread->exit(); m_thread->wait(); delete m_registry; delete m_thread; delete m_connection; } void TestDpmsClient::testDpmsConnect() { QVERIFY(m_registry->isValid()); } QTEST_GUILESS_MAIN(TestDpmsClient) #include "testkwaylanddpms.moc" diff --git a/autotests/testlog.cpp b/autotests/testlog.cpp index 556d18c..4fb0415 100644 --- a/autotests/testlog.cpp +++ b/autotests/testlog.cpp @@ -1,140 +1,140 @@ /************************************************************************************* * Copyright 2016 by Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include #include #include #include "../src/log.h" Q_DECLARE_LOGGING_CATEGORY(KSCREEN_TESTLOG) Q_LOGGING_CATEGORY(KSCREEN_TESTLOG, "kscreen.testlog") using namespace KScreen; auto KSCREEN_LOGGING = "KSCREEN_LOGGING"; class TestLog : public QObject { Q_OBJECT private Q_SLOTS: void init(); void initTestCase(); void cleanupTestCase(); void testContext(); void testEnabled(); void testLog(); private: QString m_defaultLogFile; }; void TestLog::init() { QStandardPaths::setTestModeEnabled(true); - m_defaultLogFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kscreen/kscreen.log"; + m_defaultLogFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kscreen/kscreen.log"); } void TestLog::initTestCase() { qputenv(KSCREEN_LOGGING, QByteArray("true")); } void TestLog::cleanupTestCase() { qunsetenv(KSCREEN_LOGGING); } void TestLog::testContext() { auto log = Log::instance(); - QString ctx("context text"); + QString ctx = QStringLiteral("context text"); QVERIFY(log != nullptr); log->setContext(ctx); QCOMPARE(log->context(), ctx); delete log; } void TestLog::testEnabled() { qputenv(KSCREEN_LOGGING, QByteArray("faLSe")); auto log = Log::instance(); QCOMPARE(log->enabled(), false); QCOMPARE(log->logFile(), QString()); delete log; qunsetenv(KSCREEN_LOGGING); log = Log::instance(); QCOMPARE(log->enabled(), false); QCOMPARE(log->logFile(), QString()); delete log; qputenv(KSCREEN_LOGGING, QByteArray("truE")); log = Log::instance(); QCOMPARE(log->enabled(), true); QCOMPARE(log->logFile(), m_defaultLogFile); delete log; } void TestLog::testLog() { auto log = Log::instance(); Q_UNUSED(log); QFile lf(m_defaultLogFile); lf.remove(); QVERIFY(!lf.exists()); - QString logmsg("This is a log message. ♥"); + QString logmsg = QStringLiteral("This is a log message. ♥"); Log::log(logmsg); QVERIFY(lf.exists()); QVERIFY(lf.remove()); qCDebug(KSCREEN_TESTLOG) << "qCDebug message from testlog"; QVERIFY(lf.exists()); QVERIFY(lf.remove()); delete Log::instance(); // Make sure on log file gets written when disabled qputenv(KSCREEN_LOGGING, "false"); qCDebug(KSCREEN_TESTLOG) << logmsg; QCOMPARE(Log::instance()->enabled(), false); QVERIFY(!lf.exists()); Log::log(logmsg); QVERIFY(!lf.exists()); // Make sure we don't crash on cleanup delete Log::instance(); delete Log::instance(); } QTEST_MAIN(TestLog) #include "testlog.moc" diff --git a/autotests/testscreenconfig.cpp b/autotests/testscreenconfig.cpp index 27d50b4..6d42c31 100644 --- a/autotests/testscreenconfig.cpp +++ b/autotests/testscreenconfig.cpp @@ -1,326 +1,326 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include #include #include "../src/screen.h" #include "../src/config.h" #include "../src/output.h" #include "../src/mode.h" #include "../src/getconfigoperation.h" #include "../src/setconfigoperation.h" #include "../src/backendmanager_p.h" using namespace KScreen; class testScreenConfig : public QObject { Q_OBJECT private: KScreen::ConfigPtr getConfig(); private Q_SLOTS: void initTestCase(); void singleOutput(); void singleOutputWithoutPreferred(); void multiOutput(); void clonesOutput(); void configCanBeApplied(); void supportedFeatures(); void testInvalidMode(); void cleanupTestCase(); void testOutputPositionNormalization(); }; ConfigPtr testScreenConfig::getConfig() { qputenv("KSCREEN_BACKEND_INPROCESS", "1"); auto *op = new GetConfigOperation(); if (!op->exec()) { qWarning("ConfigOperation error: %s", qPrintable(op->errorString())); BackendManager::instance()->shutdownBackend(); return ConfigPtr(); } BackendManager::instance()->shutdownBackend(); return op->config(); } void testScreenConfig::initTestCase() { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND", "Fake"); } void testScreenConfig::cleanupTestCase() { BackendManager::instance()->shutdownBackend(); } void testScreenConfig::singleOutput() { //json file for the fake backend qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleoutput.json"); // QVERIFY2(kscreen, KScreen::errorString().toLatin1()); // QVERIFY2(!kscreen->backend().isEmpty(), "No backend loaded"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(1280, 800)); QCOMPARE(config->outputs().count(), 1); const OutputPtr output = config->outputs().take(1); QVERIFY(!output.isNull()); - QCOMPARE(output->name(), QString("LVDS1")); + QCOMPARE(output->name(), QStringLiteral("LVDS1")); QCOMPARE(output->type(), Output::Panel); QCOMPARE(output->modes().count(), 3); QCOMPARE(output->pos(), QPoint(0, 0)); QCOMPARE(output->geometry(), QRect(0,0, 1280, 800)); QCOMPARE(output->currentModeId(), QLatin1String("3")); QCOMPARE(output->preferredModeId(), QLatin1String("3")); QCOMPARE(output->rotation(), Output::None); QCOMPARE(output->scale(), 1.0); QCOMPARE(output->isConnected(), true); QCOMPARE(output->isEnabled(), true); QCOMPARE(output->isPrimary(), true); //QCOMPARE(output->isEmbedded(), true); QVERIFY2(output->clones().isEmpty(), "In singleOutput is impossible to have clones"); const ModePtr mode = output->currentMode(); QCOMPARE(mode->size(), QSize(1280, 800)); QCOMPARE(mode->refreshRate(), (float)59.9); } void testScreenConfig::singleOutputWithoutPreferred() { qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleOutputWithoutPreferred.json"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const OutputPtr output = config->outputs().take(1); QVERIFY(!output.isNull()); QVERIFY(output->preferredModes().isEmpty()); QCOMPARE(output->preferredModeId(), QLatin1String("3")); } void testScreenConfig::multiOutput() { qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "multipleoutput.json"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(3200, 1880)); QCOMPARE(config->outputs().count(), 2); const OutputPtr output = config->outputs().take(2); QVERIFY(!output.isNull()); - QCOMPARE(output->name(), QString("HDMI1")); + QCOMPARE(output->name(), QStringLiteral("HDMI1")); QCOMPARE(output->type(), Output::HDMI); QCOMPARE(output->modes().count(), 4); QCOMPARE(output->pos(), QPoint(1280, 0)); QCOMPARE(output->geometry(), QRect(1280, 0, 1920 / 1.4, 1080 / 1.4)); QCOMPARE(output->currentModeId(), QLatin1String("4")); QCOMPARE(output->preferredModeId(), QLatin1String("4")); QCOMPARE(output->rotation(), Output::None); QCOMPARE(output->scale(), 1.4); QCOMPARE(output->isConnected(), true); QCOMPARE(output->isEnabled(), true); QCOMPARE(output->isPrimary(), false); QVERIFY2(output->clones().isEmpty(), "This simulates extended output, no clones"); const ModePtr mode = output->currentMode(); QVERIFY(!mode.isNull()); QCOMPARE(mode->size(), QSize(1920, 1080)); QCOMPARE(mode->refreshRate(), (float)60.0); } void testScreenConfig::clonesOutput() { qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "multipleclone.json"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(1024, 768)); const OutputPtr one = config->outputs()[1]; const OutputPtr two = config->outputs()[2]; QCOMPARE(one->currentMode()->size(), two->currentMode()->size()); QCOMPARE(one->clones().count(), 1); QCOMPARE(one->clones().first(), two->id()); QVERIFY2(two->clones().isEmpty(), "Output two should have no clones"); } void testScreenConfig::configCanBeApplied() { qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleoutputBroken.json"); const ConfigPtr brokenConfig = getConfig(); qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleoutput.json"); const ConfigPtr currentConfig = getConfig(); QVERIFY(!currentConfig.isNull()); const OutputPtr primaryBroken = brokenConfig->outputs()[2]; QVERIFY(!primaryBroken.isNull()); const OutputPtr currentPrimary = currentConfig->outputs()[1]; QVERIFY(!currentPrimary.isNull()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setId(currentPrimary->id()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setConnected(currentPrimary->isConnected()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setCurrentModeId(QLatin1String("42")); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setCurrentModeId(currentPrimary->currentModeId()); QVERIFY(!Config::canBeApplied(brokenConfig)); - qDebug() << "brokenConfig.modes" << primaryBroken->mode("3"); + qDebug() << "brokenConfig.modes" << primaryBroken->mode(QStringLiteral("3")); primaryBroken->mode(QLatin1String("3"))->setSize(QSize(1280, 800)); - qDebug() << "brokenConfig.modes" << primaryBroken->mode("3"); + qDebug() << "brokenConfig.modes" << primaryBroken->mode(QStringLiteral("3")); QVERIFY(Config::canBeApplied(brokenConfig)); qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "tooManyOutputs.json"); const ConfigPtr brokenConfig2 = getConfig(); QVERIFY(!brokenConfig2.isNull()); int enabledOutputsCount = 0; Q_FOREACH (const OutputPtr &output, brokenConfig2->outputs()) { if (output->isEnabled()) { ++enabledOutputsCount; } } QVERIFY(brokenConfig2->screen()->maxActiveOutputsCount() < enabledOutputsCount); QVERIFY(!Config::canBeApplied(brokenConfig2)); const ConfigPtr nulllConfig; QVERIFY(!Config::canBeApplied(nulllConfig)); } void testScreenConfig::supportedFeatures() { ConfigPtr config = getConfig(); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); config->setSupportedFeatures(KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); config->setSupportedFeatures(KScreen::Config::Feature::None); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); config->setSupportedFeatures(KScreen::Config::Feature::PerOutputScaling | KScreen::Config::Feature::Writable); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); config->setSupportedFeatures(KScreen::Config::Feature::PerOutputScaling | KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); } void testScreenConfig::testInvalidMode() { ModeList modes; - ModePtr invalidMode = modes.value("99"); + ModePtr invalidMode = modes.value(QStringLiteral("99")); QVERIFY(invalidMode.isNull()); auto output = new KScreen::Output(); auto currentMode = output->currentMode(); QVERIFY(currentMode.isNull()); QVERIFY(!currentMode); delete output; } void testScreenConfig::testOutputPositionNormalization() { qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "multipleoutput.json"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); auto left = config->outputs().first(); auto right = config->outputs().last(); QVERIFY(!left.isNull()); QVERIFY(!right.isNull()); left->setPos(QPoint(-5000, 700)); right->setPos(QPoint(-3720, 666)); QCOMPARE(left->pos(), QPoint(-5000, 700)); QCOMPARE(right->pos(), QPoint(-3720, 666)); // start a set operation to fix up the positions { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(left->pos(), QPoint(0, 34)); QCOMPARE(right->pos(), QPoint(1280, 0)); // make sure it doesn't touch a valid config { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(left->pos(), QPoint(0, 34)); QCOMPARE(right->pos(), QPoint(1280, 0)); // positions of single outputs should be at 0, 0 left->setEnabled(false); { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(right->pos(), QPoint()); } QTEST_MAIN(testScreenConfig) #include "testscreenconfig.moc" diff --git a/backends/fake/fake.cpp b/backends/fake/fake.cpp index 0d19d1b..07d8185 100644 --- a/backends/fake/fake.cpp +++ b/backends/fake/fake.cpp @@ -1,203 +1,203 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "fake.h" #include "parser.h" #include "config.h" #include "edid.h" #include #include #include #include #include #include #include #include #include "fakebackendadaptor.h" using namespace KScreen; Q_LOGGING_CATEGORY(KSCREEN_FAKE, "kscreen.fake") Fake::Fake() : KScreen::AbstractBackend() { QLoggingCategory::setFilterRules(QStringLiteral("kscreen.fake.debug = true")); if (qgetenv("KSCREEN_BACKEND_INPROCESS") != QByteArray("1")) { QTimer::singleShot(0, this, SLOT(delayedInit())); } } void Fake::init(const QVariantMap &arguments) { if (!mConfig.isNull()) { mConfig.clear(); } mConfigFile = arguments[QStringLiteral("TEST_DATA")].toString(); qCDebug(KSCREEN_FAKE) << "Fake profile file:" << mConfigFile; } void Fake::delayedInit() { new FakeBackendAdaptor(this); QDBusConnection::sessionBus().registerObject(QLatin1String("/fake"), this); } Fake::~Fake() { } QString Fake::name() const { - return QString("Fake"); + return QStringLiteral("Fake"); } QString Fake::serviceName() const { return QLatin1Literal("org.kde.KScreen.Backend.Fake"); } ConfigPtr Fake::config() const { if (mConfig.isNull()) { mConfig = Parser::fromJson(mConfigFile); } return mConfig; } void Fake::setConfig(const ConfigPtr &config) { qCDebug(KSCREEN_FAKE) << "set config" << config->outputs(); mConfig = config->clone(); emit configChanged(mConfig); } bool Fake::isValid() const { return true; } QByteArray Fake::edid(int outputId) const { Q_UNUSED(outputId); QFile file(mConfigFile); file.open(QIODevice::ReadOnly); const QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll()); const QJsonObject json = jsonDoc.object(); - const QJsonArray outputs = json["outputs"].toArray(); + const QJsonArray outputs = json[QStringLiteral("outputs")].toArray(); Q_FOREACH(const QJsonValue &value, outputs) { const QVariantMap output = value.toObject().toVariantMap(); - if (output["id"].toInt() != outputId) { + if (output[QStringLiteral("id")].toInt() != outputId) { continue; } - return QByteArray::fromBase64(output["edid"].toByteArray()); + return QByteArray::fromBase64(output[QStringLiteral("edid")].toByteArray()); } return QByteArray(); } void Fake::setConnected(int outputId, bool connected) { KScreen::OutputPtr output = config()->output(outputId); if (output->isConnected() == connected) { return; } output->setConnected(connected); qCDebug(KSCREEN_FAKE) << "emitting configChanged in Fake"; Q_EMIT configChanged(mConfig); } void Fake::setEnabled(int outputId, bool enabled) { KScreen::OutputPtr output = config()->output(outputId); if (output->isEnabled() == enabled) { return; } output->setEnabled(enabled); Q_EMIT configChanged(mConfig); } void Fake::setPrimary(int outputId, bool primary) { KScreen::OutputPtr output = config()->output(outputId); if (output->isPrimary() == primary) { return; } Q_FOREACH (KScreen::OutputPtr output, config()->outputs()) { if (output->id() == outputId) { output->setPrimary(primary); } else { output->setPrimary(false); } } Q_EMIT configChanged(mConfig); } void Fake::setCurrentModeId(int outputId, const QString &modeId) { KScreen::OutputPtr output = config()->output(outputId); if (output->currentModeId() == modeId) { return; } output->setCurrentModeId(modeId); Q_EMIT configChanged(mConfig); } void Fake::setRotation(int outputId, int rotation) { KScreen::OutputPtr output = config()->output(outputId); const KScreen::Output::Rotation rot = static_cast(rotation); if (output->rotation() == rot) { return; } output->setRotation(rot); Q_EMIT configChanged(mConfig); } void Fake::addOutput(int outputId, const QString &name) { KScreen::OutputPtr output(new KScreen::Output); output->setId(outputId); output->setName(name); mConfig->addOutput(output); Q_EMIT configChanged(mConfig); } void Fake::removeOutput(int outputId) { mConfig->removeOutput(outputId); Q_EMIT configChanged(mConfig); } diff --git a/backends/fake/parser.cpp b/backends/fake/parser.cpp index efaa7c4..3d0c751 100644 --- a/backends/fake/parser.cpp +++ b/backends/fake/parser.cpp @@ -1,257 +1,258 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "parser.h" #include "fake.h" #include "config.h" #include "output.h" #include #include #include #include #include #include #include using namespace KScreen; ConfigPtr Parser::fromJson(const QByteArray& data) { ConfigPtr config(new Config); const QJsonObject json = QJsonDocument::fromJson(data).object(); - ScreenPtr screen = Parser::screenFromJson(json["screen"].toObject().toVariantMap()); + ScreenPtr screen = Parser::screenFromJson(json[QStringLiteral("screen")].toObject().toVariantMap()); config->setScreen(screen); - const QVariantList outputs = json["outputs"].toArray().toVariantList(); + const QVariantList outputs = json[QStringLiteral("outputs")].toArray().toVariantList(); if (outputs.isEmpty()) { return config; } OutputList outputList; Q_FOREACH(const QVariant &value, outputs) { const OutputPtr output = Parser::outputFromJson(value.toMap()); outputList.insert(output->id(), output); } config->setOutputs(outputList); return config; } ConfigPtr Parser::fromJson(const QString& path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qWarning() << file.errorString(); qWarning() << "File: " << path; return ConfigPtr(); } return Parser::fromJson(file.readAll()); } ScreenPtr Parser::screenFromJson(const QVariantMap &data) { ScreenPtr screen(new Screen); - screen->setId(data["id"].toInt()); - screen->setMinSize(Parser::sizeFromJson(data["minSize"].toMap())); - screen->setMaxSize(Parser::sizeFromJson(data["maxSize"].toMap())); - screen->setCurrentSize(Parser::sizeFromJson(data["currentSize"].toMap())); - screen->setMaxActiveOutputsCount(data["maxActiveOutputsCount"].toInt()); + screen->setId(data[QStringLiteral("id")].toInt()); + screen->setMinSize(Parser::sizeFromJson(data[QStringLiteral("minSize")].toMap())); + screen->setMaxSize(Parser::sizeFromJson(data[QStringLiteral("maxSize")].toMap())); + screen->setCurrentSize(Parser::sizeFromJson(data[QStringLiteral("currentSize")].toMap())); + screen->setMaxActiveOutputsCount(data[QStringLiteral("maxActiveOutputsCount")].toInt()); return screen; } void Parser::qvariant2qobject(const QVariantMap &variant, QObject *object) { const QMetaObject *metaObject = object->metaObject(); for (QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter) { const int propertyIndex = metaObject->indexOfProperty(qPrintable(iter.key())); if (propertyIndex == -1) { //qWarning() << "Skipping non-existent property" << iter.key(); continue; } const QMetaProperty metaProperty = metaObject->property(propertyIndex); if (!metaProperty.isWritable()) { //qWarning() << "Skipping read-only property" << iter.key(); continue; } - const QVariant property = object->property(iter.key().toLatin1()); + const QVariant property = object->property(iter.key().toLatin1().constData()); Q_ASSERT(property.isValid()); if (property.isValid()) { QVariant value = iter.value(); if (value.canConvert(property.type())) { value.convert(property.type()); - object->setProperty(iter.key().toLatin1(), value); + object->setProperty(iter.key().toLatin1().constData(), value); } else if (QString(QLatin1String("QVariant")).compare(QLatin1String(property.typeName())) == 0) { - object->setProperty(iter.key().toLatin1(), value); + object->setProperty(iter.key().toLatin1().constData(), value); } } } } OutputPtr Parser::outputFromJson(QMap< QString, QVariant > map) { OutputPtr output(new Output); - output->setId(map["id"].toInt()); + output->setId(map[QStringLiteral("id")].toInt()); QStringList preferredModes; - const QVariantList prefModes = map["preferredModes"].toList(); + const QVariantList prefModes = map[QStringLiteral("preferredModes")].toList(); Q_FOREACH(const QVariant &mode, prefModes) { preferredModes.append(mode.toString()); } output->setPreferredModes(preferredModes); - map.remove(QLatin1Literal("preferredModes")); + map.remove(QStringLiteral("preferredModes")); ModeList modelist; - const QVariantList modes = map["modes"].toList(); + const QVariantList modes = map[QStringLiteral("modes")].toList(); Q_FOREACH(const QVariant &modeValue, modes) { const ModePtr mode = Parser::modeFromJson(modeValue); modelist.insert(mode->id(), mode); } output->setModes(modelist); - map.remove(QLatin1Literal("modes")); + map.remove(QStringLiteral("modes")); - if(map.contains("clones")) { + if(map.contains(QLatin1String("clones"))) { QList clones; - Q_FOREACH(const QVariant &id, map["clones"].toList()) { + Q_FOREACH(const QVariant &id, map[QStringLiteral("clones")].toList()) { clones.append(id.toInt()); } output->setClones(clones); - map.remove(QLatin1Literal("clones")); + map.remove(QStringLiteral("clones")); } - const QString type = map["type"].toByteArray().toUpper(); + const QByteArray type = map[QStringLiteral("type")].toByteArray().toUpper(); if (type.contains("LVDS") || type.contains("EDP") || type.contains("IDP") || type.contains("7")) { output->setType(Output::Panel); } else if (type.contains("VGA")) { output->setType(Output::VGA); } else if (type.contains("DVI")) { output->setType(Output::DVI); } else if (type.contains("DVI-I")) { output->setType(Output::DVII); } else if (type.contains("DVI-A")) { output->setType(Output::DVIA); } else if (type.contains("DVI-D")) { output->setType(Output::DVID); } else if (type.contains("HDMI") || type.contains("6")) { output->setType(Output::HDMI); } else if (type.contains("Panel")) { output->setType(Output::Panel); } else if (type.contains("TV")) { output->setType(Output::TV); } else if (type.contains("TV-Composite")) { output->setType(Output::TVComposite); } else if (type.contains("TV-SVideo")) { output->setType(Output::TVSVideo); } else if (type.contains("TV-Component")) { output->setType(Output::TVComponent); } else if (type.contains("TV-SCART")) { output->setType(Output::TVSCART); } else if (type.contains("TV-C4")) { output->setType(Output::TVC4); } else if (type.contains("DisplayPort") || type.contains("14")) { output->setType(Output::DisplayPort); } else if (type.contains("Unknown")) { output->setType(Output::Unknown); } else { qCWarning(KSCREEN_FAKE) << "Output Type not translated:" << type; } - map.remove(QLatin1Literal("type")); + map.remove(QStringLiteral("type")); - if (map.contains("pos")) { - output->setPos(Parser::pointFromJson(map["pos"].toMap())); - map.remove(QLatin1Literal("pos")); + if (map.contains(QStringLiteral("pos"))) { + output->setPos(Parser::pointFromJson(map[QStringLiteral("pos")].toMap())); + map.remove(QStringLiteral("pos")); } - if (map.contains("size")) { - output->setSize(Parser::sizeFromJson(map["size"].toMap())); - map.remove(QLatin1Literal("size")); + if (map.contains(QStringLiteral("size"))) { + output->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap())); + map.remove(QStringLiteral("size")); } - if (map.contains("scale")) { - qDebug() << "Scale found:" << map["scale"].toReal(); - output->setScale(map["scale"].toReal()); - map.remove(QLatin1Literal("scale")); + auto scale = QStringLiteral("scale"); + if (map.contains(scale)) { + qDebug() << "Scale found:" << map[scale].toReal(); + output->setScale(map[scale].toReal()); + map.remove(scale); } //Remove some extra properties that we do not want or need special treatment - map.remove(QLatin1Literal("edid")); + map.remove(QStringLiteral("edid")); Parser::qvariant2qobject(map, output.data()); return output; } ModePtr Parser::modeFromJson(const QVariant& data) { const QVariantMap map = data.toMap(); ModePtr mode(new Mode); Parser::qvariant2qobject(map, mode.data()); - mode->setSize(Parser::sizeFromJson(map["size"].toMap())); + mode->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap())); return mode; } QSize Parser::sizeFromJson(const QVariant& data) { const QVariantMap map = data.toMap(); QSize size; - size.setWidth(map["width"].toInt()); - size.setHeight(map["height"].toInt()); + size.setWidth(map[QStringLiteral("width")].toInt()); + size.setHeight(map[QStringLiteral("height")].toInt()); return size; } QPoint Parser::pointFromJson(const QVariant& data) { const QVariantMap map = data.toMap(); QPoint point; - point.setX(map["x"].toInt()); - point.setY(map["y"].toInt()); + point.setX(map[QStringLiteral("x")].toInt()); + point.setY(map[QStringLiteral("y")].toInt()); return point; } QRect Parser::rectFromJson(const QVariant& data) { QRect rect; rect.setSize(Parser::sizeFromJson(data)); rect.setBottomLeft(Parser::pointFromJson(data)); return rect; } bool Parser::validate(const QByteArray& data) { Q_UNUSED(data); return true; } bool Parser::validate(const QString& data) { Q_UNUSED(data); return true; } diff --git a/backends/qscreen/qscreenbackend.cpp b/backends/qscreen/qscreenbackend.cpp index b3ed545..0de6fc5 100644 --- a/backends/qscreen/qscreenbackend.cpp +++ b/backends/qscreen/qscreenbackend.cpp @@ -1,76 +1,76 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2012, 2013 by Daniel Vrátil * * Copyright 2014 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "qscreenbackend.h" #include "qscreenconfig.h" #include "qscreenoutput.h" using namespace KScreen; Q_LOGGING_CATEGORY(KSCREEN_QSCREEN, "kscreen.qscreen") QScreenConfig *QScreenBackend::s_internalConfig = nullptr; QScreenBackend::QScreenBackend() : KScreen::AbstractBackend() , m_isValid(true) { if (s_internalConfig == nullptr) { s_internalConfig = new QScreenConfig(); connect(s_internalConfig, &QScreenConfig::configChanged, this, &QScreenBackend::configChanged); } } QScreenBackend::~QScreenBackend() { } QString QScreenBackend::name() const { - return QString("QScreen"); + return QStringLiteral("QScreen"); } QString QScreenBackend::serviceName() const { - return QLatin1Literal("org.kde.KScreen.Backend.QScreen"); + return QStringLiteral("org.kde.KScreen.Backend.QScreen"); } ConfigPtr QScreenBackend::config() const { return s_internalConfig->toKScreenConfig(); } void QScreenBackend::setConfig(const ConfigPtr &config) { if (!config) { return; } qWarning() << "The QScreen backend for libkscreen is read-only,"; qWarning() << "setting a configuration is not supported."; qWarning() << "You can force another backend using the KSCREEN_BACKEND env var."; } bool QScreenBackend::isValid() const { return m_isValid; } diff --git a/backends/utils.cpp b/backends/utils.cpp index 7d7d62a..a9a26b5 100644 --- a/backends/utils.cpp +++ b/backends/utils.cpp @@ -1,70 +1,70 @@ /************************************************************************************* * Copyright 2018 Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "utils.h" #include KScreen::Output::Type Utils::guessOutputType(const QString &type, const QString &name) { static const auto embedded = { QLatin1String("LVDS"), QLatin1String("IDP"), QLatin1String("EDP"), QLatin1String("LCD") }; for (const QLatin1String &pre : embedded) { if (name.toUpper().startsWith(pre)) { return KScreen::Output::Panel; } } - if (type.contains("VGA")) { + if (type.contains(QLatin1String("VGA"))) { return KScreen::Output::VGA; - } else if (type.contains("DVI")) { + } else if (type.contains(QLatin1String("DVI"))) { return KScreen::Output::DVI; - } else if (type.contains("DVI-I")) { + } else if (type.contains(QLatin1String("DVI-I"))) { return KScreen::Output::DVII; - } else if (type.contains("DVI-A")) { + } else if (type.contains(QLatin1String("DVI-A"))) { return KScreen::Output::DVIA; - } else if (type.contains("DVI-D")) { + } else if (type.contains(QLatin1String("DVI-D"))) { return KScreen::Output::DVID; - } else if (type.contains("HDMI")) { + } else if (type.contains(QLatin1String("HDMI"))) { return KScreen::Output::HDMI; - } else if (type.contains("Panel")) { + } else if (type.contains(QLatin1String("Panel"))) { return KScreen::Output::Panel; - } else if (type.contains("TV-Composite")) { + } else if (type.contains(QLatin1String("TV-Composite"))) { return KScreen::Output::TVComposite; - } else if (type.contains("TV-SVideo")) { + } else if (type.contains(QLatin1String("TV-SVideo"))) { return KScreen::Output::TVSVideo; - } else if (type.contains("TV-Component")) { + } else if (type.contains(QLatin1String("TV-Component"))) { return KScreen::Output::TVComponent; - } else if (type.contains("TV-SCART")) { + } else if (type.contains(QLatin1String("TV-SCART"))) { return KScreen::Output::TVSCART; - } else if (type.contains("TV-C4")) { + } else if (type.contains(QLatin1String("TV-C4"))) { return KScreen::Output::TVC4; - } else if (type.contains("TV")) { + } else if (type.contains(QLatin1String("TV"))) { return KScreen::Output::TV; - } else if (type.contains("DisplayPort") || type.startsWith("DP")) { + } else if (type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP"))) { return KScreen::Output::DisplayPort; - } else if (type.contains("unknown")) { + } else if (type.contains(QLatin1String("unknown"))) { return KScreen::Output::Unknown; } else { return KScreen::Output::Unknown; } } diff --git a/backends/xcbeventlistener.cpp b/backends/xcbeventlistener.cpp index 536e7ee..c451426 100644 --- a/backends/xcbeventlistener.cpp +++ b/backends/xcbeventlistener.cpp @@ -1,211 +1,211 @@ /************************************************************************************* * Copyright 2012, 2013 Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "xcbeventlistener.h" #include #include #include Q_LOGGING_CATEGORY(KSCREEN_XCB_HELPER, "kscreen.xcb.helper") XCBEventListener::XCBEventListener(): QObject(), m_isRandrPresent(false), m_randrBase(0), m_randrErrorBase(0), m_majorOpcode(0), m_eventType(0), m_versionMajor(0), m_versionMinor(0), m_window(0) { xcb_connection_t* c = QX11Info::connection(); xcb_prefetch_extension_data(c, &xcb_randr_id); auto cookie = xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); const auto *queryExtension = xcb_get_extension_data(c, &xcb_randr_id); if (!queryExtension) { qCDebug(KSCREEN_XCB_HELPER) << "Fail to query for xrandr extension"; return; } if (!queryExtension->present) { qCDebug(KSCREEN_XCB_HELPER) << "XRandR extension is not present at all"; return; } m_isRandrPresent = queryExtension->present; m_randrBase = queryExtension->first_event; m_randrErrorBase = queryExtension->first_error; m_majorOpcode = queryExtension->major_opcode; xcb_generic_error_t *error = nullptr; auto* versionReply = xcb_randr_query_version_reply(c, cookie, &error); Q_ASSERT_X(versionReply, "xrandrxcbhelper", "Query to fetch xrandr version failed"); if (error) { qFatal("Error while querying for xrandr version: %d", error->error_code); } m_versionMajor = versionReply->major_version; m_versionMinor = versionReply->minor_version; free(versionReply); qCDebug(KSCREEN_XCB_HELPER).nospace() << "Detected XRandR " << m_versionMajor << "." << m_versionMinor; qCDebug(KSCREEN_XCB_HELPER) << "Event Base: " << m_randrBase; qCDebug(KSCREEN_XCB_HELPER) << "Event Error: "<< m_randrErrorBase; uint32_t rWindow = QX11Info::appRootWindow(); m_window = xcb_generate_id(c); xcb_create_window(c, XCB_COPY_FROM_PARENT, m_window, rWindow, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_randr_select_input(c, m_window, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY ); qApp->installNativeEventFilter(this); } XCBEventListener::~XCBEventListener() { if (m_window && QX11Info::connection()) { xcb_destroy_window(QX11Info::connection(), m_window); } } QString XCBEventListener::rotationToString(xcb_randr_rotation_t rotation) { switch (rotation) { case XCB_RANDR_ROTATION_ROTATE_0: - return "Rotate_0"; + return QStringLiteral("Rotate_0"); case XCB_RANDR_ROTATION_ROTATE_90: - return "Rotate_90"; + return QStringLiteral("Rotate_90"); case XCB_RANDR_ROTATION_ROTATE_180: - return "Rotate_180"; + return QStringLiteral("Rotate_180"); case XCB_RANDR_ROTATION_ROTATE_270: - return "Rotate_270"; + return QStringLiteral("Rotate_270"); case XCB_RANDR_ROTATION_REFLECT_X: - return "Reflect_X"; + return QStringLiteral("Reflect_X"); case XCB_RANDR_ROTATION_REFLECT_Y: - return "REflect_Y"; + return QStringLiteral("REflect_Y"); } - return QString("invalid value (%1)").arg(rotation); + return QStringLiteral("invalid value (%1)").arg(rotation); } QString XCBEventListener::connectionToString(xcb_randr_connection_t connection) { switch (connection) { case XCB_RANDR_CONNECTION_CONNECTED: - return "Connected"; + return QStringLiteral("Connected"); case XCB_RANDR_CONNECTION_DISCONNECTED: - return "Disconnected"; + return QStringLiteral("Disconnected"); case XCB_RANDR_CONNECTION_UNKNOWN: - return "UnknownConnection"; + return QStringLiteral("UnknownConnection"); } - return QString("invalid value (%1)").arg(connection); + return QStringLiteral("invalid value (%1)").arg(connection); } bool XCBEventListener::nativeEventFilter(const QByteArray& eventType, void* message, long int* result) { Q_UNUSED(result); if (eventType != "xcb_generic_event_t") { return false; } xcb_generic_event_t* e = static_cast(message); const uint8_t xEventType = e->response_type & ~0x80; //If this event is not xcb_randr_notify, we don't want it if (xEventType == m_randrBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { handleScreenChange(e); } if (xEventType == m_randrBase + XCB_RANDR_NOTIFY) { handleXRandRNotify(e); } return false; } void XCBEventListener::handleScreenChange(xcb_generic_event_t* e) { xcb_randr_screen_change_notify_event_t *e2 = (xcb_randr_screen_change_notify_event_t *) e; // Only accept notifications for our window if (e2->request_window != m_window) { return; } qCDebug(KSCREEN_XCB_HELPER) << "RRScreenChangeNotify"; qCDebug(KSCREEN_XCB_HELPER) << "\tWindow:" << e2->request_window; qCDebug(KSCREEN_XCB_HELPER) << "\tRoot:" << e2->root; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t) e2->rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tSize ID:" << e2->sizeID; qCDebug(KSCREEN_XCB_HELPER) << "\tSize: " << e2->width << e2->height; qCDebug(KSCREEN_XCB_HELPER) << "\tSizeMM: " << e2->mwidth << e2->mheight; Q_EMIT screenChanged((xcb_randr_rotation_t) e2->rotation, QSize(e2->width, e2->height), QSize(e2->mwidth, e2->mheight)); Q_EMIT outputsChanged(); } void XCBEventListener::handleXRandRNotify(xcb_generic_event_t* e) { xcb_randr_notify_event_t* randrEvent = reinterpret_cast(e); if (randrEvent->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) { xcb_randr_crtc_change_t crtc = randrEvent->u.cc; qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_CrtcChange"; qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << crtc.crtc; qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << crtc.mode; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t) crtc.rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tGeometry: " << crtc.x << crtc.y << crtc.width << crtc.height; Q_EMIT crtcChanged(crtc.crtc, crtc.mode, (xcb_randr_rotation_t) crtc.rotation, QRect(crtc.x, crtc.y, crtc.width, crtc.height)); } else if(randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) { xcb_randr_output_change_t output = randrEvent->u.oc; qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputChange"; qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << output.output; qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << output.crtc; qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << output.mode; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t) output.rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tConnection: " << connectionToString((xcb_randr_connection_t) output.connection); qCDebug(KSCREEN_XCB_HELPER) << "\tSubpixel Order: " << output.subpixel_order; Q_EMIT outputChanged(output.output, output.crtc, output.mode, (xcb_randr_connection_t) output.connection); } else if(randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_PROPERTY) { xcb_randr_output_property_t property = randrEvent->u.op; XCB::ScopedPointer reply(xcb_get_atom_name_reply(QX11Info::connection(), xcb_get_atom_name(QX11Info::connection(), property.atom), nullptr)); qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputProperty (ignored)"; qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << property.output; qCDebug(KSCREEN_XCB_HELPER) << "\tProperty: " << xcb_get_atom_name_name(reply.data()); qCDebug(KSCREEN_XCB_HELPER) << "\tState (newValue, Deleted): " << property.status; } } diff --git a/backends/xrandr/xrandroutput.cpp b/backends/xrandr/xrandroutput.cpp index 0048296..39ad9dd 100644 --- a/backends/xrandr/xrandroutput.cpp +++ b/backends/xrandr/xrandroutput.cpp @@ -1,337 +1,337 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2012, 2013 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "xrandroutput.h" #include "xrandrmode.h" #include "xrandrconfig.h" #include "xrandr.h" #include "output.h" #include "config.h" #include "../utils.h" #include Q_DECLARE_METATYPE(QList) XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config) : QObject(config) , m_config(config) , m_id(id) , m_type(KScreen::Output::Unknown) , m_primary(false) , m_crtc(nullptr) { init(); } XRandROutput::~XRandROutput() { } xcb_randr_output_t XRandROutput::id() const { return m_id; } bool XRandROutput::isConnected() const { return m_connected == XCB_RANDR_CONNECTION_CONNECTED; } bool XRandROutput::isEnabled() const { return m_crtc != nullptr && m_crtc->mode() != XCB_NONE; } bool XRandROutput::isPrimary() const { return m_primary; } QPoint XRandROutput::position() const { return m_crtc ? m_crtc->geometry().topLeft() : QPoint(); } QSize XRandROutput::size() const { return m_crtc ? m_crtc->geometry().size() : QSize(); } XRandRMode::Map XRandROutput::modes() const { return m_modes; } QString XRandROutput::currentModeId() const { return m_crtc ? QString::number(m_crtc->mode()) : QString(); } XRandRMode* XRandROutput::currentMode() const { if (!m_crtc) { return nullptr; } unsigned int modeId = m_crtc->mode(); if (!m_modes.contains(modeId)) { return nullptr; } return m_modes[modeId]; } KScreen::Output::Rotation XRandROutput::rotation() const { return static_cast(m_crtc ? m_crtc->rotation() : XCB_RANDR_ROTATION_ROTATE_0); } QByteArray XRandROutput::edid() const { if (m_edid.isNull()) { m_edid = XRandR::outputEdid(m_id); } return m_edid; } XRandRCrtc* XRandROutput::crtc() const { return m_crtc; } void XRandROutput::update() { init(); } void XRandROutput::update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn, bool primary) { qCDebug(KSCREEN_XRANDR) << "XRandROutput" << m_id << "update"; qCDebug(KSCREEN_XRANDR) << "\tm_connected:" << m_connected; qCDebug(KSCREEN_XRANDR) << "\tm_crtc" << m_crtc; qCDebug(KSCREEN_XRANDR) << "\tCRTC:" << crtc; qCDebug(KSCREEN_XRANDR) << "\tMODE:" << mode; qCDebug(KSCREEN_XRANDR) << "\tConnection:" << conn; qCDebug(KSCREEN_XRANDR) << "\tPrimary:" << primary; // Connected or disconnected if (isConnected() != (conn == XCB_RANDR_CONNECTION_CONNECTED)) { if (conn == XCB_RANDR_CONNECTION_CONNECTED) { // New monitor has been connected, refresh everything init(); } else { // Disconnected m_connected = conn; m_clones.clear(); m_heightMm = 0; m_widthMm = 0; m_type = KScreen::Output::Unknown; qDeleteAll(m_modes); m_modes.clear(); m_preferredModes.clear(); m_edid.clear(); } } else if (conn == XCB_RANDR_CONNECTION_CONNECTED) { // the output changed in some way, let's update the internal // list of modes, as it may have changed XCB::OutputInfo outputInfo(m_id, XCB_TIME_CURRENT_TIME); if (outputInfo) { updateModes(outputInfo); } } // A monitor has been enabled or disabled // We don't use isEnabled(), because it checks for crtc && crtc->mode(), however // crtc->mode may already be unset due to xcb_randr_crtc_tChangeNotify coming before // xcb_randr_output_tChangeNotify and reseting the CRTC mode if ((m_crtc == nullptr) != (crtc == XCB_NONE)) { if (crtc == XCB_NONE && mode == XCB_NONE) { // Monitor has been disabled m_crtc->disconectOutput(m_id); m_crtc = nullptr; } else { m_crtc = m_config->crtc(crtc); m_crtc->connectOutput(m_id); } } // Primary has changed m_primary = primary; } void XRandROutput::setIsPrimary(bool primary) { m_primary = primary; } void XRandROutput::init() { XCB::OutputInfo outputInfo(m_id, XCB_TIME_CURRENT_TIME); Q_ASSERT(outputInfo); if (!outputInfo) { return; } XCB::PrimaryOutput primary(XRandR::rootWindow()); m_name = QString::fromUtf8((const char *) xcb_randr_get_output_info_name(outputInfo.data()), outputInfo->name_len); m_type = fetchOutputType(m_id, m_name); m_icon = QString(); m_connected = (xcb_randr_connection_t) outputInfo->connection; m_primary = (primary->output == m_id); xcb_randr_output_t *clones = xcb_randr_get_output_info_clones(outputInfo.data()); for (int i = 0; i < outputInfo->num_clones; ++i) { m_clones.append(clones[i]); } m_widthMm = outputInfo->mm_width; m_heightMm = outputInfo->mm_height; m_crtc = m_config->crtc(outputInfo->crtc); if (m_crtc) { m_crtc->connectOutput(m_id); } updateModes(outputInfo); } void XRandROutput::updateModes(const XCB::OutputInfo &outputInfo) { /* Init modes */ XCB::ScopedPointer screenResources(XRandR::screenResources()); Q_ASSERT(screenResources); if (!screenResources) { return; } xcb_randr_mode_info_t *modes = xcb_randr_get_screen_resources_modes(screenResources.data()); xcb_randr_mode_t *outputModes = xcb_randr_get_output_info_modes(outputInfo.data()); m_preferredModes.clear(); qDeleteAll(m_modes); m_modes.clear(); for (int i = 0; i < outputInfo->num_modes; ++i) { /* Resources->modes contains all possible modes, we are only interested * in those listed in outputInfo->modes. */ for (int j = 0; j < screenResources->num_modes; ++j) { if (modes[j].id != outputModes[i]) { continue; } XRandRMode *mode = new XRandRMode(modes[j], this); m_modes.insert(mode->id(), mode); if (i < outputInfo->num_preferred) { m_preferredModes.append(QString::number(mode->id())); } break; } } } KScreen::Output::Type XRandROutput::fetchOutputType(xcb_randr_output_t outputId, const QString &name) { - QByteArray type = typeFromProperty(outputId); + QString type = QString::fromUtf8(typeFromProperty(outputId)); if (type.isEmpty()) { - type = name.toLocal8Bit(); + type = name; } return Utils::guessOutputType(type, name); } QByteArray XRandROutput::typeFromProperty(xcb_randr_output_t outputId) { QByteArray type; XCB::InternAtom atomType(true, 13, "ConnectorType"); if (!atomType) { return type; } char *connectorType; auto cookie = xcb_randr_get_output_property(XCB::connection(), outputId, atomType->atom, XCB_ATOM_ANY, 0, 100, false, false); XCB::ScopedPointer reply(xcb_randr_get_output_property_reply(XCB::connection(), cookie, nullptr)); if (!reply) { return type; } if (!(reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->num_items == 1)) { return type; } const uint8_t *prop = xcb_randr_get_output_property_data(reply.data()); XCB::AtomName atomName(*reinterpret_cast(prop)); if (!atomName) { return type; } connectorType = xcb_get_atom_name_name(atomName); if (!connectorType) { return type; } type = connectorType; return type; } KScreen::OutputPtr XRandROutput::toKScreenOutput() const { KScreen::OutputPtr kscreenOutput(new KScreen::Output); const bool signalsBlocked = kscreenOutput->signalsBlocked(); kscreenOutput->blockSignals(true); kscreenOutput->setId(m_id); kscreenOutput->setType(m_type); kscreenOutput->setSizeMm(QSize(m_widthMm, m_heightMm)); kscreenOutput->setName(m_name); kscreenOutput->setIcon(m_icon); kscreenOutput->setConnected(isConnected()); if (isConnected()) { KScreen::ModeList kscreenModes; for (auto iter = m_modes.constBegin(), end = m_modes.constEnd(); iter != end; ++iter) { XRandRMode *mode = iter.value(); kscreenModes.insert(QString::number(iter.key()), mode->toKScreenMode()); } kscreenOutput->setModes(kscreenModes); kscreenOutput->setPreferredModes(m_preferredModes); kscreenOutput->setPrimary(m_primary); kscreenOutput->setClones([](const QList &clones) { QList kclones; kclones.reserve(clones.size()); for (xcb_randr_output_t o : clones) { kclones.append(static_cast(o)); } return kclones; }(m_clones)); kscreenOutput->setEnabled(isEnabled()); if (isEnabled()) { kscreenOutput->setSize(size()); kscreenOutput->setPos(position()); kscreenOutput->setRotation(rotation()); kscreenOutput->setCurrentModeId(currentModeId()); } } kscreenOutput->blockSignals(signalsBlocked); return kscreenOutput; } diff --git a/backends/xrandr1.1/xrandr11.cpp b/backends/xrandr1.1/xrandr11.cpp index 1af6a5b..95f13ed 100644 --- a/backends/xrandr1.1/xrandr11.cpp +++ b/backends/xrandr1.1/xrandr11.cpp @@ -1,189 +1,189 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "xrandr11.h" #include "../xcbeventlistener.h" #include "config.h" #include "edid.h" #include "output.h" #include #include #include #include Q_LOGGING_CATEGORY(KSCREEN_XRANDR11, "kscreen.xrandr11") XRandR11::XRandR11() : KScreen::AbstractBackend() , m_valid(false) , m_x11Helper(nullptr) , m_currentConfig(new KScreen::Config) , m_currentTimestamp(0) { xcb_generic_error_t *error = nullptr; xcb_randr_query_version_reply_t* version; version = xcb_randr_query_version_reply(XCB::connection(), xcb_randr_query_version(XCB::connection(), XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &error); if (!version || error) { free(error); XCB::closeConnection(); qCDebug(KSCREEN_XRANDR11) << "Can't get XRandR version"; return; } if (version->major_version != 1 || version->minor_version != 1) { XCB::closeConnection(); qCDebug(KSCREEN_XRANDR11) << "This backend is only for XRandR 1.1, your version is: " << version->major_version << "." << version->minor_version; return; } m_x11Helper = new XCBEventListener(); connect(m_x11Helper, &XCBEventListener::outputsChanged, this, &XRandR11::updateConfig); m_valid = true; } XRandR11::~XRandR11() { XCB::closeConnection(); delete m_x11Helper; } QString XRandR11::name() const { return QLatin1Literal("XRandR 1.1"); } QString XRandR11::serviceName() const { return QLatin1Literal("org.kde.KScreen.Backend.XRandR11"); } bool XRandR11::isValid() const { return m_valid; } KScreen::ConfigPtr XRandR11::config() const { KScreen::ConfigPtr config(new KScreen::Config); auto features = KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay; config->setSupportedFeatures(features); const int screenId = QX11Info::appScreen(); xcb_screen_t* xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId); const XCB::ScreenInfo info(xcbScreen->root); const XCB::ScreenSize size(xcbScreen->root); if (info->config_timestamp == m_currentTimestamp) { return m_currentConfig; } KScreen::ScreenPtr screen(new KScreen::Screen); screen->setId(screenId); screen->setCurrentSize(QSize(xcbScreen->width_in_pixels, xcbScreen->height_in_pixels)); if (size) { // RRGetScreenSize may file on VNC/RDP connections screen->setMaxSize(QSize(size->max_width, size->max_height)); screen->setMinSize(QSize(size->min_width, size->min_height)); } else { screen->setMaxSize(screen->currentSize()); screen->setMinSize(screen->currentSize()); } screen->setMaxActiveOutputsCount(1); config->setScreen(screen); KScreen::OutputList outputs; KScreen::OutputPtr output(new KScreen::Output); output->setId(1); output->setConnected(true); output->setEnabled(true); output->setName(QLatin1String("Default")); output->setPos(QPoint(0,0)); output->setPrimary(true); output->setRotation((KScreen::Output::Rotation) info->rotation); output->setSizeMm(QSize(xcbScreen->width_in_millimeters, xcbScreen->height_in_millimeters)); outputs.insert(1, output); config->setOutputs(outputs); KScreen::ModePtr mode; KScreen::ModeList modes; auto iter = xcb_randr_get_screen_info_rates_iterator(info); xcb_randr_screen_size_t* sizes = xcb_randr_get_screen_info_sizes(info); for (int x = 0; x < info->nSizes; x++) { const xcb_randr_screen_size_t size = sizes[x]; const uint16_t* rates = xcb_randr_refresh_rates_rates(iter.data); const int nrates = xcb_randr_refresh_rates_rates_length(iter.data); for (int j = 0; j < nrates; j++) { float rate = rates[j]; mode = KScreen::ModePtr(new KScreen::Mode); mode->setId(QString::fromLatin1("%1-%2").arg(x).arg(j)); mode->setSize(QSize(size.width, size.height)); mode->setRefreshRate(rate); mode->setName(QString::fromLatin1("%1x%2").arg(size.width).arg(size.height)); if (x == info->sizeID && rate == info->rate) { output->setCurrentModeId(mode->id()); output->setSize(mode->size()); } modes.insert(mode->id(), mode); } xcb_randr_refresh_rates_next(&iter); } output->setModes(modes); return config; } void XRandR11::setConfig(const KScreen::ConfigPtr &config) { const KScreen::OutputPtr output = config->outputs().take(1); const KScreen::ModePtr mode = output->currentMode(); const int screenId = QX11Info::appScreen(); xcb_screen_t* xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId); const XCB::ScreenInfo info(xcbScreen->root); xcb_generic_error_t *err; - const int sizeId = mode->id().split("-").first().toInt(); + const int sizeId = mode->id().split(QStringLiteral("-")).first().toInt(); auto cookie = xcb_randr_set_screen_config(XCB::connection(), xcbScreen->root, XCB_CURRENT_TIME, info->config_timestamp, sizeId, (short) output->rotation(), mode->refreshRate()); XCB::ScopedPointer reply( xcb_randr_set_screen_config_reply(XCB::connection(), cookie, &err)); if (err) { free(err); } } void XRandR11::updateConfig() { m_currentConfig = config(); Q_EMIT configChanged(m_currentConfig); } diff --git a/src/backendmanager.cpp b/src/backendmanager.cpp index d9ec055..6785eb9 100644 --- a/src/backendmanager.cpp +++ b/src/backendmanager.cpp @@ -1,429 +1,430 @@ /* * Copyright (C) 2014 Daniel Vratil * Copyright 2015 Sebastian Kügler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "backendmanager_p.h" #include "abstractbackend.h" #include "config.h" #include "configmonitor.h" #include "backendinterface.h" #include "debug_p.h" #include "getconfigoperation.h" #include "configserializer_p.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include using namespace KScreen; Q_DECLARE_METATYPE(org::kde::kscreen::Backend*) const int BackendManager::sMaxCrashCount = 4; BackendManager *BackendManager::sInstance = nullptr; BackendManager *BackendManager::instance() { if (!sInstance) { sInstance = new BackendManager(); } return sInstance; } BackendManager::BackendManager() : mInterface(nullptr) , mCrashCount(0) , mShuttingDown(false) , mRequestsCounter(0) , mLoader(nullptr) , mMethod(OutOfProcess) { Log::instance(); // Decide whether to run in, or out-of-process // if KSCREEN_BACKEND_INPROCESS is set explicitly, we respect that const auto _inprocess = qgetenv("KSCREEN_BACKEND_INPROCESS"); if (!_inprocess.isEmpty()) { const QByteArrayList falses({QByteArray("0"), QByteArray("false")}); if (!falses.contains(_inprocess.toLower())) { mMethod = InProcess; } else { mMethod = OutOfProcess; } } else { // For XRandR backends, use out of process if (preferredBackend().fileName().startsWith(QLatin1String("KSC_XRandR"))) { mMethod = OutOfProcess; } else { mMethod = InProcess; } } initMethod(); } void BackendManager::initMethod() { if (mMethod == OutOfProcess) { qRegisterMetaType("OrgKdeKscreenBackendInterface"); mServiceWatcher.setConnection(QDBusConnection::sessionBus()); connect(&mServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &BackendManager::backendServiceUnregistered); mResetCrashCountTimer.setSingleShot(true); mResetCrashCountTimer.setInterval(60000); connect(&mResetCrashCountTimer, &QTimer::timeout, this, [=]() { mCrashCount = 0; }); } } void BackendManager::setMethod(BackendManager::Method m) { if (mMethod == m) { return; } shutdownBackend(); mMethod = m; initMethod(); } BackendManager::Method BackendManager::method() const { return mMethod; } BackendManager::~BackendManager() { if (mMethod == InProcess) { shutdownBackend(); } } QFileInfo BackendManager::preferredBackend(const QString &backend) { /** this is the logic to pick a backend, in order of priority * * - backend argument is used if not empty * - env var KSCREEN_BACKEND is considered * - if platform is X11, the XRandR backend is picked * - if platform is wayland, KWayland backend is picked * - if neither is the case, QScreen backend is picked * - the QScreen backend is also used as fallback * */ QString backendFilter; - const auto env_kscreen_backend = qgetenv("KSCREEN_BACKEND"); + const auto env_kscreen_backend = QString::fromUtf8(qgetenv("KSCREEN_BACKEND")); if (!backend.isEmpty()) { backendFilter = backend; } else if (!env_kscreen_backend.isEmpty()) { backendFilter = env_kscreen_backend; } else { if (QX11Info::isPlatformX11()) { backendFilter = QStringLiteral("XRandR"); } else if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { backendFilter = QStringLiteral("KWayland"); } else { backendFilter = QStringLiteral("QScreen"); } } QFileInfo fallback; Q_FOREACH (const QFileInfo &f, listBackends()) { // Here's the part where we do the match case-insensitive if (f.baseName().toLower() == QStringLiteral("ksc_%1").arg(backendFilter.toLower())) { return f; } if (f.baseName() == QLatin1String("KSC_QScreen")) { fallback = f; } } // qCWarning(KSCREEN) << "No preferred backend found. KSCREEN_BACKEND is set to " << env_kscreen_backend; // qCWarning(KSCREEN) << "falling back to " << fallback.fileName(); return fallback; } QFileInfoList BackendManager::listBackends() { // Compile a list of installed backends first const QString backendFilter = QStringLiteral("KSC_*"); const QStringList paths = QCoreApplication::libraryPaths(); QFileInfoList finfos; for (const QString &path : paths) { const QDir dir(path + QLatin1String("/kf5/kscreen/"), backendFilter, QDir::SortFlags(QDir::QDir::Name), QDir::NoDotAndDotDot | QDir::Files); finfos.append(dir.entryInfoList()); } return finfos; } KScreen::AbstractBackend *BackendManager::loadBackendPlugin(QPluginLoader *loader, const QString &name, const QVariantMap &arguments) { const auto finfo = preferredBackend(name); loader->setFileName(finfo.filePath()); QObject *instance = loader->instance(); if (!instance) { qCDebug(KSCREEN) << loader->errorString(); return nullptr; } auto backend = qobject_cast(instance); if (backend) { backend->init(arguments); if (!backend->isValid()) { qCDebug(KSCREEN) << "Skipping" << backend->name() << "backend"; delete backend; return nullptr; } //qCDebug(KSCREEN) << "Loaded" << backend->name() << "backend"; return backend; } else { qCDebug(KSCREEN) << finfo.fileName() << "does not provide valid KScreen backend"; } return nullptr; } KScreen::AbstractBackend *BackendManager::loadBackendInProcess(const QString &name) { Q_ASSERT(mMethod == InProcess); if (mMethod == OutOfProcess) { qCWarning(KSCREEN) << "You are trying to load a backend in process, while the BackendManager is set to use OutOfProcess communication. Use loadBackendPlugin() instead."; return nullptr; } if (m_inProcessBackend.first != nullptr && (name.isEmpty() || m_inProcessBackend.first->name() == name)) { return m_inProcessBackend.first; } else if (m_inProcessBackend.first != nullptr && m_inProcessBackend.first->name() != name) { shutdownBackend(); } if (mLoader == nullptr) { mLoader = new QPluginLoader(this); } + auto test_data_equals = QStringLiteral("TEST_DATA="); QVariantMap arguments; auto beargs = QString::fromLocal8Bit(qgetenv("KSCREEN_BACKEND_ARGS")); - if (beargs.startsWith("TEST_DATA=")) { - arguments["TEST_DATA"] = beargs.remove("TEST_DATA="); + if (beargs.startsWith(test_data_equals)) { + arguments[QStringLiteral("TEST_DATA")] = beargs.remove(test_data_equals); } auto backend = BackendManager::loadBackendPlugin(mLoader, name, arguments); //qCDebug(KSCREEN) << "Connecting ConfigMonitor to backend."; ConfigMonitor::instance()->connectInProcessBackend(backend); m_inProcessBackend = qMakePair(backend, arguments); setConfig(backend->config()); return backend; } void BackendManager::requestBackend() { Q_ASSERT(mMethod == OutOfProcess); if (mInterface && mInterface->isValid()) { ++mRequestsCounter; QMetaObject::invokeMethod(this, "emitBackendReady", Qt::QueuedConnection); return; } // Another request already pending if (mRequestsCounter > 0) { return; } ++mRequestsCounter; const QByteArray args = qgetenv("KSCREEN_BACKEND_ARGS"); QVariantMap arguments; if (!args.isEmpty()) { QList arglist = args.split(';'); Q_FOREACH (const QByteArray &arg, arglist) { const int pos = arg.indexOf('='); if (pos == -1) { continue; } - arguments.insert(arg.left(pos), arg.mid(pos + 1)); + arguments.insert(QString::fromUtf8(arg.left(pos)), arg.mid(pos + 1)); } } startBackend(QString::fromLatin1(qgetenv("KSCREEN_BACKEND")), arguments); } void BackendManager::emitBackendReady() { Q_ASSERT(mMethod == OutOfProcess); Q_EMIT backendReady(mInterface); --mRequestsCounter; if (mShutdownLoop.isRunning()) { mShutdownLoop.quit(); } } void BackendManager::startBackend(const QString &backend, const QVariantMap &arguments) { // This will autostart the launcher if it's not running already, calling // requestBackend(backend) will: // a) if the launcher is started it will force it to load the correct backend, // b) if the launcher is already running it will make sure it's running with // the same backend as the one we requested and send an error otherwise QDBusConnection conn = QDBusConnection::sessionBus(); QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("requestBackend")); call.setArguments({ backend, arguments }); QDBusPendingCall pending = conn.asyncCall(call); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending); connect(watcher, &QDBusPendingCallWatcher::finished, this, &BackendManager::onBackendRequestDone); } void BackendManager::onBackendRequestDone(QDBusPendingCallWatcher *watcher) { Q_ASSERT(mMethod == OutOfProcess); watcher->deleteLater(); QDBusPendingReply reply = *watcher; // Most probably we requested an explicit backend that is different than the // one already loaded in the launcher if (reply.isError()) { qCWarning(KSCREEN) << "Failed to request backend:" << reply.error().name() << ":" << reply.error().message(); invalidateInterface(); emitBackendReady(); return; } // Most probably request and explicit backend which is not available or failed // to initialize, or the launcher did not find any suitable backend for the // current platform. if (!reply.value()) { qCWarning(KSCREEN) << "Failed to request backend: unknown error"; invalidateInterface(); emitBackendReady(); return; } // The launcher has successfully loaded the backend we wanted and registered // it to DBus (hopefuly), let's try to get an interface for the backend. if (mInterface) { invalidateInterface(); } mInterface = new org::kde::kscreen::Backend(QStringLiteral("org.kde.KScreen"), QStringLiteral("/backend"), QDBusConnection::sessionBus()); if (!mInterface->isValid()) { qCWarning(KSCREEN) << "Backend successfully requested, but we failed to obtain a valid DBus interface for it"; invalidateInterface(); emitBackendReady(); return; } // The backend is GO, so let's watch for it's possible disappearance, so we // can invalidate the interface mServiceWatcher.addWatchedService(mBackendService); // Immediatelly request config connect(new GetConfigOperation(GetConfigOperation::NoEDID), &GetConfigOperation::finished, [&](ConfigOperation *op) { mConfig = qobject_cast(op)->config(); emitBackendReady(); }); // And listen for its change. connect(mInterface, &org::kde::kscreen::Backend::configChanged, [&](const QVariantMap &newConfig) { mConfig = KScreen::ConfigSerializer::deserializeConfig(newConfig); }); } void BackendManager::backendServiceUnregistered(const QString &serviceName) { Q_ASSERT(mMethod == OutOfProcess); mServiceWatcher.removeWatchedService(serviceName); invalidateInterface(); requestBackend(); } void BackendManager::invalidateInterface() { Q_ASSERT(mMethod == OutOfProcess); delete mInterface; mInterface = nullptr; mBackendService.clear(); } ConfigPtr BackendManager::config() const { return mConfig; } void BackendManager::setConfig(ConfigPtr c) { //qCDebug(KSCREEN) << "BackendManager::setConfig, outputs:" << c->outputs().count(); mConfig = c; } void BackendManager::shutdownBackend() { if (mMethod == InProcess) { delete mLoader; mLoader = nullptr; m_inProcessBackend.second.clear(); delete m_inProcessBackend.first; m_inProcessBackend.first = nullptr; } else { if (mBackendService.isEmpty() && !mInterface) { return; } // If there are some currently pending requests, then wait for them to // finish before quitting while (mRequestsCounter > 0) { mShutdownLoop.exec(); } mServiceWatcher.removeWatchedService(mBackendService); mShuttingDown = true; QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("quit")); // Call synchronously QDBusConnection::sessionBus().call(call); invalidateInterface(); while (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.KScreen"))) { QThread::msleep(100); } } } diff --git a/src/configoperation.cpp b/src/configoperation.cpp index 7feae82..7755904 100644 --- a/src/configoperation.cpp +++ b/src/configoperation.cpp @@ -1,146 +1,146 @@ /* * Copyright (C) 2014 Daniel Vratil * Copyright 2015 Sebastian Kügler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "configoperation.h" #include "configoperation_p.h" #include "backendmanager_p.h" #include "debug_p.h" using namespace KScreen; ConfigOperationPrivate::ConfigOperationPrivate(ConfigOperation* qq) : QObject() , isExec(false) , q_ptr(qq) { } ConfigOperationPrivate::~ConfigOperationPrivate() { } void ConfigOperationPrivate::requestBackend() { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); connect(BackendManager::instance(), &BackendManager::backendReady, this, &ConfigOperationPrivate::backendReady); BackendManager::instance()->requestBackend(); } void ConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_UNUSED(backend); disconnect(BackendManager::instance(), &BackendManager::backendReady, this, &ConfigOperationPrivate::backendReady); } void ConfigOperationPrivate::doEmitResult() { Q_Q(ConfigOperation); Q_EMIT q->finished(q); // Don't call deleteLater() when this operation is running from exec() // because then the operation will be deleted when we return control to // the nested QEventLoop in exec() (i.e. before loop.exec() returns) // and subsequent hasError() call references deleted "this". Instead we // shedule the operation for deletion manually in exec(), so that it will // be deleted when control returns to parent event loop (or QApplication). if (!isExec) { q->deleteLater(); } } ConfigOperation::ConfigOperation(ConfigOperationPrivate* dd, QObject* parent) : QObject(parent) , d_ptr(dd) { const bool ok = QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); Q_ASSERT(ok); Q_UNUSED(ok); } ConfigOperation::~ConfigOperation() { delete d_ptr; } bool ConfigOperation::hasError() const { Q_D(const ConfigOperation); return !d->error.isEmpty(); } QString ConfigOperation::errorString() const { Q_D(const ConfigOperation); return d->error; } void ConfigOperation::setError(const QString& error) { Q_D(ConfigOperation); d->error = error; } void ConfigOperation::emitResult() { Q_D(ConfigOperation); const bool ok = QMetaObject::invokeMethod(d, "doEmitResult", Qt::QueuedConnection); Q_ASSERT(ok); Q_UNUSED(ok); } bool ConfigOperation::exec() { Q_D(ConfigOperation); QEventLoop loop; - connect(this, &ConfigOperation::finished, + connect(this, &ConfigOperation::finished, this, [&](ConfigOperation *op) { Q_UNUSED(op); loop.quit(); }); d->isExec = true; loop.exec(QEventLoop::ExcludeUserInputEvents); // Schedule the operation for deletion, see doEmitResult() deleteLater(); return !hasError(); } KScreen::AbstractBackend* ConfigOperationPrivate::loadBackend() { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); Q_Q(ConfigOperation); - const QString &name = qgetenv("KSCREEN_BACKEND").constData(); + const QString &name = QString::fromUtf8(qgetenv("KSCREEN_BACKEND")); auto backend = KScreen::BackendManager::instance()->loadBackendInProcess(name); if (backend == nullptr) { const QString &e = QStringLiteral("Plugin does not provide valid KScreen backend"); qCDebug(KSCREEN) << e; q->setError(e); q->emitResult(); } return backend; } diff --git a/src/doctor/doctor.cpp b/src/doctor/doctor.cpp index 02da982..8f4135b 100644 --- a/src/doctor/doctor.cpp +++ b/src/doctor/doctor.cpp @@ -1,494 +1,494 @@ /************************************************************************************* * Copyright 2014-2016 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "doctor.h" #include "dpmsclient.h" #include #include #include #include #include #include #include #include #include #include #include #include "../backendmanager_p.h" #include "../config.h" #include "../configoperation.h" #include "../getconfigoperation.h" #include "../setconfigoperation.h" #include "../edid.h" #include "../log.h" #include "../output.h" Q_LOGGING_CATEGORY(KSCREEN_DOCTOR, "kscreen.doctor") static QTextStream cout(stdout); static QTextStream cerr(stderr); const static QString green = QStringLiteral("\033[01;32m"); const static QString red = QStringLiteral("\033[01;31m"); const static QString yellow = QStringLiteral("\033[01;33m"); const static QString blue = QStringLiteral("\033[01;34m"); const static QString bold = QStringLiteral("\033[01;39m"); const static QString cr = QStringLiteral("\033[0;0m"); namespace KScreen { namespace ConfigSerializer { // Exported private symbol in configserializer_p.h in KScreen extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); } } using namespace KScreen; Doctor::Doctor(QObject *parent) : QObject(parent) , m_config(nullptr) , m_changed(false) , m_dpmsClient(nullptr) { } Doctor::~Doctor() { } void Doctor::start(QCommandLineParser *parser) { m_parser = parser; if (m_parser->isSet("info")) { showBackends(); } if (parser->isSet("json") || parser->isSet("outputs") || !m_positionalArgs.isEmpty()) { KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation(); connect(op, &KScreen::GetConfigOperation::finished, this, [this](KScreen::ConfigOperation *op) { configReceived(op); }); return; } if (m_parser->isSet("dpms")) { if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { cerr << "DPMS is only supported on Wayland." << endl; // We need to kick the event loop, otherwise .quit() hangs QTimer::singleShot(0, qApp->quit); return; } m_dpmsClient = new DpmsClient(this); connect(m_dpmsClient, &DpmsClient::finished, qApp, &QCoreApplication::quit); const QString dpmsArg = m_parser->value(QStringLiteral("dpms")); if (dpmsArg == QStringLiteral("show")) { showDpms(); } else { setDpms(dpmsArg); } return; } if (m_parser->isSet("log")) { const QString logmsg = m_parser->value(QStringLiteral("log")); if (!Log::instance()->enabled()) { qCWarning(KSCREEN_DOCTOR) << "Logging is disabled, unset KSCREEN_LOGGING in your environment."; } else { Log::log(logmsg); } } // We need to kick the event loop, otherwise .quit() hangs QTimer::singleShot(0, qApp->quit); } void KScreen::Doctor::setDpms(const QString& dpmsArg) { qDebug() << "SetDpms: " << dpmsArg; connect(m_dpmsClient, &DpmsClient::ready, this, [this, dpmsArg]() { cout << "DPMS.ready()"; if (dpmsArg == QStringLiteral("off")) { m_dpmsClient->off(); } else if (dpmsArg == QStringLiteral("on")) { m_dpmsClient->on(); } else { cout << "--dpms argument not understood (" << dpmsArg << ")"; } }); m_dpmsClient->connect(); } void Doctor::showDpms() { m_dpmsClient = new DpmsClient(this); connect(m_dpmsClient, &DpmsClient::ready, this, []() { cout << "DPMS.ready()"; }); m_dpmsClient->connect(); } void Doctor::showBackends() const { cout << "Environment: " << endl; auto env_kscreen_backend = (qgetenv("KSCREEN_BACKEND").isEmpty()) ? QStringLiteral("[not set]") : qgetenv("KSCREEN_BACKEND"); cout << " * KSCREEN_BACKEND : " << env_kscreen_backend << endl; auto env_kscreen_backend_inprocess = (qgetenv("KSCREEN_BACKEND_INPROCESS").isEmpty()) ? QStringLiteral("[not set]") : qgetenv("KSCREEN_BACKEND_INPROCESS"); cout << " * KSCREEN_BACKEND_INPROCESS : " << env_kscreen_backend_inprocess << endl; auto env_kscreen_logging = (qgetenv("KSCREEN_LOGGING").isEmpty()) ? QStringLiteral("[not set]") : qgetenv("KSCREEN_LOGGING"); cout << " * KSCREEN_LOGGING : " << env_kscreen_logging << endl; cout << "Logging to : " << (Log::instance()->enabled() ? Log::instance()->logFile() : "[logging disabled]") << endl; auto backends = BackendManager::instance()->listBackends(); auto preferred = BackendManager::instance()->preferredBackend(); cout << "Preferred KScreen backend : " << green << preferred.fileName() << cr << endl; cout << "Available KScreen backends:" << endl; Q_FOREACH(const QFileInfo f, backends) { auto c = blue; if (preferred == f) { c = green; } cout << " * " << c << f.fileName() << cr << ": " << f.absoluteFilePath() << endl; } cout << endl; } void Doctor::setOptionList(const QStringList &positionalArgs) { m_positionalArgs = positionalArgs; } void Doctor::parsePositionalArgs() { //qCDebug(KSCREEN_DOCTOR) << "POSARGS" << m_positionalArgs; Q_FOREACH(const QString &op, m_positionalArgs) { auto ops = op.split('.'); if (ops.count() > 2) { bool ok; int output_id = -1; if (ops[0] == QStringLiteral("output")) { Q_FOREACH (const auto &output, m_config->outputs()) { if (output->name() == ops[1]) { output_id = output->id(); } } if (output_id == -1) { output_id = ops[1].toInt(&ok); if (!ok) { cerr << "Unable to parse output id" << ops[1] << endl; qApp->exit(3); return; } } if (ops.count() == 3 && ops[2] == QStringLiteral("enable")) { if (!setEnabled(output_id, true)) { qApp->exit(1); return; }; } else if (ops.count() == 3 && ops[2] == QStringLiteral("disable")) { if (!setEnabled(output_id, false)) { qApp->exit(1); return; }; } else if (ops.count() == 4 && ops[2] == QStringLiteral("mode")) { QString mode_id = ops[3]; // set mode if (!setMode(output_id, mode_id)) { qApp->exit(9); return; } qCDebug(KSCREEN_DOCTOR) << "Output" << output_id << "set mode" << mode_id; } else if (ops.count() == 4 && ops[2] == QStringLiteral("position")) { QStringList _pos = ops[3].split(','); if (_pos.count() != 2) { qCWarning(KSCREEN_DOCTOR) << "Invalid position:" << ops[3]; qApp->exit(5); return; } int x = _pos[0].toInt(&ok); int y = _pos[1].toInt(&ok); if (!ok) { cerr << "Unable to parse position" << ops[3] << endl; qApp->exit(5); return; } QPoint p(x, y); qCDebug(KSCREEN_DOCTOR) << "Output position" << p; if (!setPosition(output_id, p)) { qApp->exit(1); return; } } else if ((ops.count() == 4 || ops.count() == 5) && ops[2] == QStringLiteral("scale")) { // be lenient about . vs. comma as separator qreal scale = ops[3].replace(QStringLiteral(","), QStringLiteral(".")).toDouble(&ok); if (ops.count() == 5) { const auto dbl = ops[3] + QStringLiteral(".") + ops[4]; scale = dbl.toDouble(&ok); }; // set scale if (!ok || qFuzzyCompare(scale, 0.0) || !setScale(output_id, scale)) { qCDebug(KSCREEN_DOCTOR) << "Could not set scale " << scale << " to output " << output_id; qApp->exit(9); return; } } else if ((ops.count() == 4) && (ops[2] == QStringLiteral("orientation") || ops[2] == QStringLiteral("rotation"))) { const QString _rotation = ops[3].toLower(); bool ok = false; const QHash rotationMap({ {QStringLiteral("none"), KScreen::Output::None}, {QStringLiteral("normal"), KScreen::Output::None}, {QStringLiteral("left"), KScreen::Output::Left}, {QStringLiteral("right"), KScreen::Output::Right}, {QStringLiteral("inverted"), KScreen::Output::Inverted} }); KScreen::Output::Rotation rot = KScreen::Output::None; // set orientation if (rotationMap.contains(_rotation)) { ok = true; rot = rotationMap[_rotation]; } if (!ok || !setRotation(output_id, rot)) { qCDebug(KSCREEN_DOCTOR) << "Could not set orientation " << _rotation << " to output " << output_id; qApp->exit(9); return; } } else { cerr << "Unable to parse arguments" << op << endl; qApp->exit(2); return; } } } } } void Doctor::configReceived(KScreen::ConfigOperation *op) { m_config = op->config(); - if (m_parser->isSet("json")) { + if (m_parser->isSet(QStringLiteral("json"))) { showJson(); qApp->quit(); } - if (m_parser->isSet("outputs")) { + if (m_parser->isSet(QStringLiteral("outputs"))) { showOutputs(); qApp->quit(); } parsePositionalArgs(); if (m_changed) { applyConfig(); m_changed = false; } } int Doctor::outputCount() const { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return 0; } return m_config->outputs().count(); } void Doctor::showOutputs() const { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return; } QHash typeString; typeString[KScreen::Output::Unknown] = QStringLiteral("Unknown"); typeString[KScreen::Output::VGA] = QStringLiteral("VGA"); typeString[KScreen::Output::DVI] = QStringLiteral("DVI"); typeString[KScreen::Output::DVII] = QStringLiteral("DVII"); typeString[KScreen::Output::DVIA] = QStringLiteral("DVIA"); typeString[KScreen::Output::DVID] = QStringLiteral("DVID"); typeString[KScreen::Output::HDMI] = QStringLiteral("HDMI"); typeString[KScreen::Output::Panel] = QStringLiteral("Panel"); typeString[KScreen::Output::TV] = QStringLiteral("TV"); typeString[KScreen::Output::TVComposite] = QStringLiteral("TVComposite"); typeString[KScreen::Output::TVSVideo] = QStringLiteral("TVSVideo"); typeString[KScreen::Output::TVComponent] = QStringLiteral("TVComponent"); typeString[KScreen::Output::TVSCART] = QStringLiteral("TVSCART"); typeString[KScreen::Output::TVC4] = QStringLiteral("TVC4"); typeString[KScreen::Output::DisplayPort] = QStringLiteral("DisplayPort"); Q_FOREACH (const auto &output, m_config->outputs()) { cout << green << "Output: " << cr << output->id() << " " << output->name(); - cout << " " << (output->isEnabled() ? green + "enabled" : red + "disabled"); - cout << " " << (output->isConnected() ? green + "connected" : red + "disconnected"); - cout << " " << (output->isPrimary() ? green + "primary" : QString()); + cout << " " << (output->isEnabled() ? green + QStringLiteral("enabled") : red + QStringLiteral("disabled")); + cout << " " << (output->isConnected() ? green + QStringLiteral("connected") : red + QStringLiteral("disconnected")); + cout << " " << (output->isPrimary() ? green + QStringLiteral("primary") : QString()); auto _type = typeString[output->type()]; - cout << " " << yellow << (_type.isEmpty() ? "UnmappedOutputType" : _type); + cout << " " << yellow << (_type.isEmpty() ? QStringLiteral("UnmappedOutputType") : _type); cout << blue << " Modes: " << cr; Q_FOREACH (auto mode, output->modes()) { - auto name = QString("%1x%2@%3").arg(QString::number(mode->size().width()), + auto name = QStringLiteral("%1x%2@%3").arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); if (mode == output->currentMode()) { name = green + name + "*" + cr; } if (mode == output->preferredMode()) { name = name + "!"; } cout << mode->id() << ":" << name << " "; } const auto g = output->geometry(); cout << yellow << "Geometry: " << cr << g.x() << "," << g.y() << " " << g.width() << "x" << g.height() << " "; cout << yellow << "Scale: " << cr << output->scale() << " "; cout << yellow << "Rotation: " << cr << output->rotation() << " "; if (output->isPrimary()) { cout << blue << "primary"; } cout << endl; } } void Doctor::showJson() const { QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); cout << doc.toJson(QJsonDocument::Indented); } bool Doctor::setEnabled(int id, bool enabled = true) { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return false; } Q_FOREACH (const auto &output, m_config->outputs()) { if (output->id() == id) { cout << (enabled ? "Enabling " : "Disabling ") << "output " << id << endl; output->setEnabled(enabled); m_changed = true; return true; } } cerr << "Output with id " << id << " not found." << endl; qApp->exit(8); return false; } bool Doctor::setPosition(int id, const QPoint &pos) { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return false; } Q_FOREACH (const auto &output, m_config->outputs()) { if (output->id() == id) { qCDebug(KSCREEN_DOCTOR) << "Set output position" << pos; output->setPos(pos); m_changed = true; return true; } } cout << "Output with id " << id << " not found." << endl; return false; } bool Doctor::setMode(int id, const QString &mode_id) { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return false; } Q_FOREACH (const auto &output, m_config->outputs()) { if (output->id() == id) { // find mode Q_FOREACH (const KScreen::ModePtr mode, output->modes()) { - auto name = QString("%1x%2@%3").arg(QString::number(mode->size().width()), + auto name = QStringLiteral("%1x%2@%3").arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); if (mode->id() == mode_id || name == mode_id) { qCDebug(KSCREEN_DOCTOR) << "Taddaaa! Found mode" << mode->id() << name; output->setCurrentModeId(mode->id()); m_changed = true; return true; } } } } cout << "Output mode " << mode_id << " not found." << endl; return false; } bool Doctor::setScale(int id, qreal scale) { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return false; } Q_FOREACH (const auto &output, m_config->outputs()) { if (output->id() == id) { output->setScale(scale); m_changed = true; return true; } } cout << "Output scale " << id << " invalid." << endl; return false; } bool Doctor::setRotation(int id, KScreen::Output::Rotation rot) { if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return false; } Q_FOREACH (const auto &output, m_config->outputs()) { if (output->id() == id) { output->setRotation(rot); m_changed = true; return true; } } cout << "Output rotation " << id << " invalid." << endl; return false; } void Doctor::applyConfig() { if (!m_changed) { return; } auto setop = new SetConfigOperation(m_config, this); setop->exec(); qCDebug(KSCREEN_DOCTOR) << "setop exec returned" << m_config; qApp->exit(0); } diff --git a/src/doctor/main.cpp b/src/doctor/main.cpp index ecb3c1c..c5cd2e8 100644 --- a/src/doctor/main.cpp +++ b/src/doctor/main.cpp @@ -1,115 +1,115 @@ /************************************************************************************* * Copyright 2014-2015 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 "doctor.h" #include #include #include /** Usage example: * kscreen-doctor --set output.0.disable output.1.mode.1 output.1.enable" * * Error codes: * 2 : general parse error * 3 : output id parse error * 4 : mode id parse error * 5 : position parse error * * 8 : invalid output id * 9 : invalid mode id * */ int main(int argc, char **argv) { - const QString desc = "kscreen-doctor allows to change the screen setup from the command-line.\n" + const QString desc = QStringLiteral("kscreen-doctor allows to change the screen setup from the command-line.\n" "\n" "Setting the output configuration is done in an atomic fashion, all settings\n" "are applied in a single command.\n" "kscreen-doctor can be used to enable and disable outputs, to position screens,\n" "change resolution (mode setting), etc.. You should put all your options into \n" "a single invocation of kscreen-doctor, so they can all be applied at once.\n" "\n" "Usage examples:\n\n" " Show output information:\n" " $ kscreen-doctor -o\n" " Output: 1 eDP-1 enabled connected Panel Modes: Modes: 1:800x600@60 [...] Geometry: 0,0 1280x800\n" " Output: 70 HDMI-2 enabled connected HDMI Modes: 1:800x600@60 [...] Geometry: 1280,0 1920x1080\n" "\n Disable the hdmi output, enable the laptop panel and set it to a specific mode\n" " $ kscreen-doctor output.HDMI-2.disable output.eDP-1.mode.1 output.eDP-1.enable\n" "\n Position the hdmi monitor on the right of the laptop panel\n" " $ kscreen-doctor output.HDMI-2.position.0,1280 output.eDP-1.position.0,0\n" "\n Set resolution mode\n" " $ kscreen-doctor output.HDMI-2.mode.1920x1080@60 \n" "\n Set scale (note: fractional scaling is only supported on wayland)\n" " $ kscreen-doctor output.HDMI-2.scale.2 \n" "\n Set rotation (possible values: none, left, right, inverted)\n" - " $ kscreen-doctor output.HDMI-2.rotation.left \n"; + " $ kscreen-doctor output.HDMI-2.rotation.left \n"); /* "\nError codes:\n" " 2 : general parse error\n" " 3 : output id parse error\n" " 4 : mode id parse error\n" " 5 : position parse error\n" " 8 : invalid output id\n" " 9 : invalid mode id\n"; */ - const QString syntax = "Specific output settings are separated by spaces, each setting is in the form of\n" + const QString syntax = QStringLiteral("Specific output settings are separated by spaces, each setting is in the form of\n" "output..[.]\n" "For example:\n" "$ kscreen-doctor output.HDMI-2.enable \\ \n" " output.eDP-1.mode.4 \\ \n" " output.eDP-1.position.1280,0\n" - "Multiple settings are passed in order to have kscreen-doctor apply these settings in one go.\n"; + "Multiple settings are passed in order to have kscreen-doctor apply these settings in one go.\n"); QGuiApplication app(argc, argv); KScreen::Doctor server; - QCommandLineOption info = QCommandLineOption(QStringList() << QStringLiteral("i") << "info", + QCommandLineOption info = QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("info"), QStringLiteral("Show runtime information: backends, logging, etc.")); - QCommandLineOption outputs = QCommandLineOption(QStringList() << QStringLiteral("o") << "outputs", + QCommandLineOption outputs = QCommandLineOption(QStringList() << QStringLiteral("o") << QStringLiteral("outputs"), QStringLiteral("Show outputs")); - QCommandLineOption json = QCommandLineOption(QStringList() << QStringLiteral("j") << "json", + QCommandLineOption json = QCommandLineOption(QStringList() << QStringLiteral("j") << QStringLiteral("json"), QStringLiteral("Show configuration in JSON format")); - QCommandLineOption dpms = QCommandLineOption(QStringList() << QStringLiteral("d") << "dpms", + QCommandLineOption dpms = QCommandLineOption(QStringList() << QStringLiteral("d") << QStringLiteral("dpms"), QStringLiteral("Display power management (wayland only)"), QStringLiteral("off")); - QCommandLineOption log = QCommandLineOption(QStringList() << QStringLiteral("l") << "log", + QCommandLineOption log = QCommandLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("log"), QStringLiteral("Write a comment to the log file"), QStringLiteral("comment")); QCommandLineParser parser; parser.setApplicationDescription(desc); - parser.addPositionalArgument("config", syntax, QStringLiteral("[output.. output..setting [...]]")); + parser.addPositionalArgument(QStringLiteral("config"), syntax, QStringLiteral("[output.. output..setting [...]]")); parser.addHelpOption(); parser.addOption(info); parser.addOption(json); parser.addOption(outputs); parser.addOption(dpms); parser.addOption(log); parser.process(app); if (!parser.positionalArguments().isEmpty()) { server.setOptionList(parser.positionalArguments()); } server.start(&parser); return app.exec(); } diff --git a/src/edid.cpp b/src/edid.cpp index 30dfb6e..2760993 100644 --- a/src/edid.cpp +++ b/src/edid.cpp @@ -1,419 +1,419 @@ /************************************************************************************* * Copyright (C) 2012 by Daniel Nicoletti * * (C) 2012 - 2014 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "edid.h" #include "debug_p.h" #include #include #include #include #include #define GCM_EDID_OFFSET_PNPID 0x08 #define GCM_EDID_OFFSET_SERIAL 0x0c #define GCM_EDID_OFFSET_SIZE 0x15 #define GCM_EDID_OFFSET_GAMMA 0x17 #define GCM_EDID_OFFSET_DATA_BLOCKS 0x36 #define GCM_EDID_OFFSET_LAST_BLOCK 0x6c #define GCM_EDID_OFFSET_EXTENSION_BLOCK_COUNT 0x7e #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff #define GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA 0xf9 #define GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe #define GCM_DESCRIPTOR_COLOR_POINT 0xfb #define PNP_IDS "/usr/share/hwdata/pnp.ids" using namespace KScreen; class Q_DECL_HIDDEN Edid::Private { public: Private(): valid(false), width(0), height(0), gamma(0) { } Private(const Private &other): valid(other.valid), monitorName(other.monitorName), vendorName(other.vendorName), serialNumber(other.serialNumber), eisaId(other.eisaId), checksum(other.checksum), pnpId(other.pnpId), width(other.width), height(other.height), gamma(other.gamma), red(other.red), green(other.green), blue(other.blue), white(other.white) { } bool parse(const QByteArray &data); int edidGetBit(int in, int bit) const; int edidGetBits(int in, int begin, int end) const; double edidDecodeFraction(int high, int low) const; QString edidParseString(const quint8 *data) const; bool valid; QString monitorName; QString vendorName; QString serialNumber; QString eisaId; QString checksum; QString pnpId; uint width; uint height; qreal gamma; QQuaternion red; QQuaternion green; QQuaternion blue; QQuaternion white; }; Edid::Edid() : QObject() , d(new Private()) { } Edid::Edid(const QByteArray &data, QObject *parent) : QObject(parent) , d(new Private()) { d->parse(data); } Edid::Edid(Edid::Private *dd) : QObject() , d(dd) { } Edid::~Edid() { delete d; } Edid *Edid::clone() const { return new Edid(new Private(*d)); } bool Edid::isValid() const { return d->valid; } QString Edid::deviceId(const QString &fallbackName) const { QString id = QLatin1String("xrandr"); // if no info was added check if the fallbacName is provided if (vendor().isNull() && name().isNull() && serial().isNull()) { if (!fallbackName.isEmpty()) { id.append(QLatin1Char('-') % fallbackName); } else { // all info we have are empty strings id.append(QLatin1String("-unknown")); } } else if (d->valid) { if (!vendor().isNull()) { id.append(QLatin1Char('-') % vendor()); } if (!name().isNull()) { id.append(QLatin1Char('-') % name()); } if (!serial().isNull()) { id.append(QLatin1Char('-') % serial()); } } return id; } QString Edid::name() const { if (d->valid) { return d->monitorName; } return QString(); } QString Edid::vendor() const { if (d->valid) { return d->vendorName; } return QString(); } QString Edid::serial() const { if (d->valid) { return d->serialNumber; } return QString(); } QString Edid::eisaId() const { if (d->valid) { return d->eisaId; } return QString(); } QString Edid::hash() const { if (d->valid) { return d->checksum; } return QString(); } QString Edid::pnpId() const { if (d->valid) { return d->pnpId; } return QString(); } uint Edid::width() const { return d->width; } uint Edid::height() const { return d->height; } qreal Edid::gamma() const { return d->gamma; } QQuaternion Edid::red() const { return d->red; } QQuaternion Edid::green() const { return d->green; } QQuaternion Edid::blue() const { return d->blue; } QQuaternion Edid::white() const { return d->white; } bool Edid::Private::parse(const QByteArray &rawData) { quint32 serial; const quint8 *data = (quint8*) rawData.constData(); size_t length = rawData.length(); /* check header */ if (length < 128) { if (length > 0) { qCWarning(KSCREEN_EDID) << "Invalid EDID length (" << length << " bytes)"; } valid = false; return valid; } if (data[0] != 0x00 || data[1] != 0xff) { qCWarning(KSCREEN_EDID) << "Failed to parse EDID header"; valid = false; return valid; } /* decode the PNP ID from three 5 bit words packed into 2 bytes * /--08--\/--09--\ * 7654321076543210 * |\---/\---/\---/ * R C1 C2 C3 */ pnpId[0] = 'A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1; pnpId[1] = 'A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[GCM_EDID_OFFSET_PNPID+1] & 0xe0) / 32) - 1; pnpId[2] = 'A' + (data[GCM_EDID_OFFSET_PNPID + 1] & 0x1f) - 1; // load the PNP_IDS file and load the vendor name if (!pnpId.isEmpty()) { - QFile pnpIds(PNP_IDS); + QFile pnpIds(QStringLiteral(PNP_IDS)); if (pnpIds.open(QIODevice::ReadOnly)) { while (!pnpIds.atEnd()) { - QString line = pnpIds.readLine(); + QString line = QString::fromUtf8(pnpIds.readLine()); if (line.startsWith(pnpId)) { QStringList parts = line.split(QLatin1Char('\t')); if (parts.size() == 2) { vendorName = line.split(QLatin1Char('\t')).at(1).simplified(); } break; } } } } /* maybe there isn't a ASCII serial number descriptor, so use this instead */ serial = static_cast(data[GCM_EDID_OFFSET_SERIAL + 0]); serial += static_cast(data[GCM_EDID_OFFSET_SERIAL + 1] * 0x100); serial += static_cast(data[GCM_EDID_OFFSET_SERIAL + 2] * 0x10000); serial += static_cast(data[GCM_EDID_OFFSET_SERIAL + 3] * 0x1000000); if (serial > 0) { serialNumber = QString::number(serial); } /* get the size */ width = data[GCM_EDID_OFFSET_SIZE + 0]; height = data[GCM_EDID_OFFSET_SIZE + 1]; /* we don't care about aspect */ if (width == 0 || height == 0) { width = 0; height = 0; } /* get gamma */ if (data[GCM_EDID_OFFSET_GAMMA] == 0xff) { gamma = 1.0f; } else { gamma = (static_cast(data[GCM_EDID_OFFSET_GAMMA] / 100) + 1); } /* get color red */ red.setX(edidDecodeFraction(data[0x1b], edidGetBits(data[0x19], 6, 7))); red.setY(edidDecodeFraction(data[0x1c], edidGetBits(data[0x19], 5, 4))); /* get color green */ green.setX(edidDecodeFraction(data[0x1d], edidGetBits(data[0x19], 2, 3))); green.setY(edidDecodeFraction(data[0x1e], edidGetBits(data[0x19], 0, 1))); /* get color blue */ blue.setX(edidDecodeFraction(data[0x1f], edidGetBits(data[0x1a], 6, 7))); blue.setY(edidDecodeFraction(data[0x20], edidGetBits(data[0x1a], 4, 5))); /* get color white */ white.setX(edidDecodeFraction(data[0x21], edidGetBits(data[0x1a], 2, 3))); white.setY(edidDecodeFraction(data[0x22], edidGetBits(data[0x1a], 0, 1))); /* parse EDID data */ for (uint i = GCM_EDID_OFFSET_DATA_BLOCKS; i <= GCM_EDID_OFFSET_LAST_BLOCK; i += 18) { /* ignore pixel clock data */ if (data[i] != 0) { continue; } if (data[i+2] != 0) { continue; } /* any useful blocks? */ if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME) { QString tmp = edidParseString(&data[i+5]); if (!tmp.isEmpty()) { monitorName = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) { QString tmp = edidParseString(&data[i+5]); if (!tmp.isEmpty()) { serialNumber = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA) { qCWarning(KSCREEN_EDID) << "failing to parse color management data"; } else if (data[i+3] == GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) { QString tmp = edidParseString(&data[i+5]); if (!tmp.isEmpty()) { eisaId = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_POINT) { if (data[i+3+9] != 0xff) { /* extended EDID block(1) which contains * a better gamma value */ gamma = ((float) data[i+3+9] / 100) + 1; } if (data[i+3+14] != 0xff) { /* extended EDID block(2) which contains * a better gamma value */ gamma = ((float) data[i+3+9] / 100) + 1; } } } // calculate checksum QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(reinterpret_cast(data), length); - checksum = hash.result().toHex(); + checksum = QString::fromLatin1(hash.result().toHex()); valid = true; return valid; } int Edid::Private::edidGetBit(int in, int bit) const { return (in & (1 << bit)) >> bit; } int Edid::Private::edidGetBits(int in, int begin, int end) const { int mask = (1 << (end - begin + 1)) - 1; return (in >> begin) & mask; } double Edid::Private::edidDecodeFraction(int high, int low) const { double result = 0.0; int i; high = (high << 2) | low; for (i = 0; i < 10; ++i) { result += edidGetBit(high, i) * pow(2, i - 10); } return result; } QString Edid::Private::edidParseString(const quint8 *data) const { QString text; /* this is always 12 bytes, but we can't guarantee it's null * terminated or not junk. */ text = QString::fromLocal8Bit((const char*) data, 12); // Remove newlines, extra spaces and stuff text = text.simplified(); return text; } diff --git a/src/log.cpp b/src/log.cpp index 91d6fa0..0662327 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,138 +1,140 @@ /************************************************************************************* * Copyright 2016 by Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "log.h" +#include + #include #include #include #include #include namespace KScreen { Log* Log::sInstance = nullptr; QtMessageHandler sDefaultMessageHandler = nullptr; void kscreenLogOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - QByteArray localMsg = msg.toLocal8Bit(); - if (QString::fromLocal8Bit(context.category).startsWith(QLatin1String("kscreen"))) { - Log::log(localMsg.constData(), context.category); + auto category = QString::fromLocal8Bit(context.category); + if (category.startsWith(QLatin1String("kscreen"))) { + Log::log(msg, category); } sDefaultMessageHandler(type, context, msg); } void log(const QString& msg) { Log::log(msg); } Log* Log::instance() { if (!sInstance) { sInstance = new Log(); } return sInstance; } using namespace KScreen; class Q_DECL_HIDDEN Log::Private { public: QString context; bool enabled = false; QString logFile; }; Log::Log() : d(new Private) { const char* logging_env = "KSCREEN_LOGGING"; if (qEnvironmentVariableIsSet(logging_env)) { - const QString logging_env_value = qgetenv(logging_env).constData(); + const QString logging_env_value = QString::fromUtf8(qgetenv(logging_env)); if (logging_env_value != QStringLiteral("0") && logging_env_value.toLower() != QStringLiteral("false")) { d->enabled = true; } } if (!d->enabled) { return; } - d->logFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kscreen/kscreen.log"; + d->logFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kscreen/kscreen.log"); - QLoggingCategory::setFilterRules("kscreen.*=true"); + QLoggingCategory::setFilterRules(QStringLiteral("kscreen.*=true")); QFileInfo fi(d->logFile); if (!QDir().mkpath(fi.absolutePath())) { qWarning() << "Failed to create logging dir" << fi.absolutePath(); } if (!sDefaultMessageHandler) { sDefaultMessageHandler = qInstallMessageHandler(kscreenLogOutput); } } Log::Log(Log::Private *dd) : d(dd) { } Log::~Log() { delete d; sInstance = nullptr; } QString Log::context() const { return d->context; } void Log::setContext(const QString& context) { d->context = context; } bool Log::enabled() const { return d->enabled; } QString Log::logFile() const { return d->logFile; } void Log::log(const QString &msg, const QString &category) { if (!instance()->enabled()) { return; } auto _cat = category; - _cat.remove("kscreen."); - const QString timestamp = QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss.zzz"); - QString logMessage = QString("\n%1 ; %2 ; %3 : %4").arg(timestamp, _cat, instance()->context(), msg); + _cat.remove(QStringLiteral("kscreen.")); + const QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss.zzz")); + QString logMessage = QStringLiteral("\n%1 ; %2 ; %3 : %4").arg(timestamp, _cat, instance()->context(), msg); QFile file(instance()->logFile()); if (!file.open(QIODevice::Append | QIODevice::Text)) { return; } file.write(logMessage.toUtf8()); } } // ns diff --git a/src/output.cpp b/src/output.cpp index 4de6e8f..767d28c 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,596 +1,596 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2014 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "output.h" #include "mode.h" #include "edid.h" #include "abstractbackend.h" #include "backendmanager_p.h" #include "debug_p.h" #include #include #include using namespace KScreen; class Q_DECL_HIDDEN Output::Private { public: Private(): id(0), type(Unknown), rotation(None), scale(1.0), connected(false), enabled(false), primary(false), edid(nullptr) {} Private(const Private &other): id(other.id), name(other.name), type(other.type), icon(other.icon), clones(other.clones), currentMode(other.currentMode), preferredMode(other.preferredMode), preferredModes(other.preferredModes), sizeMm(other.sizeMm), pos(other.pos), size(other.size), rotation(other.rotation), scale(other.scale), connected(other.connected), enabled(other.enabled), primary(other.primary) { Q_FOREACH (const ModePtr &otherMode, other.modeList) { modeList.insert(otherMode->id(), otherMode->clone()); } if (other.edid) { edid = other.edid->clone(); } } QString biggestMode(const ModeList& modes) const; bool compareModeList(const ModeList& before, const ModeList& after); int id; QString name; Type type; QString icon; ModeList modeList; QList clones; QString currentMode; QString preferredMode; QStringList preferredModes; QSize sizeMm; QPoint pos; QSize size; Rotation rotation; qreal scale; bool connected; bool enabled; bool primary; mutable QPointer edid; }; bool Output::Private::compareModeList(const ModeList& before, const ModeList &after) { if (before.keys() != after.keys()) { return false; } for (const QString &key : before.keys()) { const auto mb = before.value(key); const auto ma = after.value(key); if (mb->id() != ma->id()) { return false; } if (mb->size() != ma->size()) { return false; } if (mb->refreshRate() != ma->refreshRate()) { return false; } if (mb->name() != ma->name()) { return false; } } // They're the same return true; } QString Output::Private::biggestMode(const ModeList& modes) const { int area, total = 0; KScreen::ModePtr biggest; Q_FOREACH(const KScreen::ModePtr &mode, modes) { area = mode->size().width() * mode->size().height(); if (area < total) { continue; } if (area == total && mode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && mode->refreshRate() > biggest->refreshRate()) { biggest = mode; continue; } total = area; biggest = mode; } if (!biggest) { - return nullptr; + return QString(); } return biggest->id(); } Output::Output() : QObject(nullptr) , d(new Private()) { } Output::Output(Output::Private *dd) : QObject() , d(dd) { } Output::~Output() { delete d; } OutputPtr Output::clone() const { return OutputPtr(new Output(new Private(*d))); } int Output::id() const { return d->id; } void Output::setId(int id) { if (d->id == id) { return; } d->id = id; Q_EMIT outputChanged(); } QString Output::name() const { return d->name; } void Output::setName(const QString& name) { if (d->name == name) { return; } d->name = name; Q_EMIT outputChanged(); } Output::Type Output::type() const { return d->type; } void Output::setType(Type type) { if (d->type == type) { return; } d->type = type; Q_EMIT outputChanged(); } QString Output::icon() const { return d->icon; } void Output::setIcon(const QString& icon) { if (d->icon == icon) { return; } d->icon = icon; Q_EMIT outputChanged(); } ModePtr Output::mode(const QString& id) const { if (!d->modeList.contains(id)) { return ModePtr(); } return d->modeList[id]; } ModeList Output::modes() const { return d->modeList; } void Output::setModes(const ModeList &modes) { bool changed = !d->compareModeList(d->modeList, modes); d->modeList = modes; if (changed) { emit modesChanged(); emit outputChanged(); } } QString Output::currentModeId() const { return d->currentMode; } void Output::setCurrentModeId(const QString& mode) { if (d->currentMode == mode) { return; } d->currentMode = mode; Q_EMIT currentModeIdChanged(); } ModePtr Output::currentMode() const { return d->modeList.value(d->currentMode); } void Output::setPreferredModes(const QStringList &modes) { d->preferredMode = QString(); d->preferredModes = modes; } QStringList Output::preferredModes() const { return d->preferredModes; } QString Output::preferredModeId() const { if (!d->preferredMode.isEmpty()) { return d->preferredMode; } if (d->preferredModes.isEmpty()) { return d->biggestMode(modes()); } int area, total = 0; KScreen::ModePtr biggest; KScreen::ModePtr candidateMode; Q_FOREACH(const QString &modeId, d->preferredModes) { candidateMode = mode(modeId); area = candidateMode->size().width() * candidateMode->size().height(); if (area < total) { continue; } if (area == total && biggest && candidateMode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && biggest && candidateMode->refreshRate() > biggest->refreshRate()) { biggest = candidateMode; continue; } total = area; biggest = candidateMode; } Q_ASSERT_X(biggest, "preferredModeId", "biggest mode must exist"); d->preferredMode = biggest->id(); return d->preferredMode; } ModePtr Output::preferredMode() const { return d->modeList.value(preferredModeId()); } QPoint Output::pos() const { return d->pos; } void Output::setPos(const QPoint& pos) { if (d->pos == pos) { return; } d->pos = pos; Q_EMIT posChanged(); } QSize Output::size() const { return d->size; } void Output::setSize(const QSize& size) { if (d->size == size) { return; } d->size = size; Q_EMIT sizeChanged(); } Output::Rotation Output::rotation() const { return d->rotation; } void Output::setRotation(Output::Rotation rotation) { if (d->rotation == rotation) { return; } d->rotation = rotation; Q_EMIT rotationChanged(); } qreal Output::scale() const { return d->scale; } void Output::setScale(qreal factor) { if (d->scale == factor) { return; } d->scale = factor; emit scaleChanged(); } bool Output::isConnected() const { return d->connected; } void Output::setConnected(bool connected) { if (d->connected == connected) { return; } d->connected = connected; Q_EMIT isConnectedChanged(); } bool Output::isEnabled() const { return d->enabled; } void Output::setEnabled(bool enabled) { if (d->enabled == enabled) { return; } d->enabled = enabled; Q_EMIT isEnabledChanged(); } bool Output::isPrimary() const { return d->primary; } void Output::setPrimary(bool primary) { if (d->primary == primary) { return; } d->primary = primary; Q_EMIT isPrimaryChanged(); } QList Output::clones() const { return d->clones; } void Output::setClones(QList outputlist) { if (d->clones == outputlist) { return; } d->clones = outputlist; Q_EMIT clonesChanged(); } void Output::setEdid(const QByteArray& rawData) { Q_ASSERT(d->edid.isNull()); d->edid = new Edid(rawData); } Edid *Output::edid() const { return d->edid; } QSize Output::sizeMm() const { return d->sizeMm; } void Output::setSizeMm(const QSize &size) { d->sizeMm = size; } QRect Output::geometry() const { if (!currentMode()) { return QRect(); } // We can't use QRect(d->pos, d->size), because d->size does not reflect the // actual rotation() set by caller, it's only updated when we get update from // KScreen, but not when user changes mode or rotation manually QSize size = currentMode()->size() / d->scale; if (!isHorizontal()) { size = size.transposed(); } return QRect(d->pos, size); } void Output::apply(const OutputPtr& other) { typedef void (KScreen::Output::*ChangeSignal)(); QList changes; // We block all signals, and emit them only after we have set up everything // This is necessary in order to prevent clients from accessing inconsistent // outputs from intermediate change signals const bool keepBlocked = signalsBlocked(); blockSignals(true); if (d->name != other->d->name) { changes << &Output::outputChanged; setName(other->d->name); } if (d->type != other->d->type) { changes << &Output::outputChanged; setType(other->d->type); } if (d->icon != other->d->icon) { changes << &Output::outputChanged; setIcon(other->d->icon); } if (d->pos != other->d->pos) { changes << &Output::posChanged; setPos(other->pos()); } if (d->rotation != other->d->rotation) { changes << &Output::rotationChanged; setRotation(other->d->rotation); } if (d->scale != other->d->scale) { changes << &Output::scaleChanged; setScale(other->d->scale); } if (d->currentMode != other->d->currentMode) { changes << &Output::currentModeIdChanged; setCurrentModeId(other->d->currentMode); } if (d->connected != other->d->connected) { changes << &Output::isConnectedChanged; setConnected(other->d->connected); } if (d->enabled != other->d->enabled) { changes << &Output::isEnabledChanged; setEnabled(other->d->enabled); } if (d->primary != other->d->primary) { changes << &Output::isPrimaryChanged; setPrimary(other->d->primary); } if (d->clones != other->d->clones) { changes << &Output::clonesChanged; setClones(other->d->clones);; } if (!d->compareModeList(d->modeList, other->d->modeList)) { changes << &Output::outputChanged; } setPreferredModes(other->d->preferredModes); ModeList modes; Q_FOREACH (const ModePtr &otherMode, other->modes()) { modes.insert(otherMode->id(), otherMode->clone()); } setModes(modes); // Non-notifyable changes if (other->d->edid) { delete d->edid; d->edid = other->d->edid->clone(); } blockSignals(keepBlocked); while (!changes.isEmpty()) { const ChangeSignal &sig = changes.first(); Q_EMIT (this->*sig)(); changes.removeAll(sig); } } QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output) { if(output) { dbg << "KScreen::Output(" << output->id() << " " << output->name() << (output->isConnected() ? "connected" : "disconnected") << (output->isEnabled() ? "enabled" : "disabled") << (output->isPrimary() ? "primary" : "") << "pos:" << output->pos() << "res:" << output->size() << "modeId:" << output->currentModeId() << "scale:" << output->scale() << ")"; } else { dbg << "KScreen::Output(NULL)"; } return dbg; } diff --git a/tests/kwayland/main.cpp b/tests/kwayland/main.cpp index 5853184..57240c6 100644 --- a/tests/kwayland/main.cpp +++ b/tests/kwayland/main.cpp @@ -1,44 +1,44 @@ /************************************************************************************* * 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 "waylandtestserver.h" #include #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); KScreen::WaylandTestServer server; - QCommandLineOption config = QCommandLineOption(QStringList() << QStringLiteral("c") << "config", - QStringLiteral("Config file"), "config"); + QCommandLineOption config = QCommandLineOption(QStringList() << QStringLiteral("c") << QLatin1String("config"), + QStringLiteral("Config file"), QLatin1String("config")); QCommandLineParser parser; parser.addHelpOption(); parser.addOption(config); parser.process(app); if (parser.isSet(config)) { server.setConfig(parser.value(config)); } else { server.setConfig(QString::fromLocal8Bit(TEST_DATA)+"/multipleoutput.json"); } server.start(); return app.exec(); } diff --git a/tests/kwayland/waylandtestserver.cpp b/tests/kwayland/waylandtestserver.cpp index d22b5bb..e689eed 100644 --- a/tests/kwayland/waylandtestserver.cpp +++ b/tests/kwayland/waylandtestserver.cpp @@ -1,200 +1,200 @@ /************************************************************************************* * Copyright 2014-2015 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "waylandtestserver.h" #include "waylandconfigreader.h" #include #include #include #include #include #include #include "../src/edid.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND_TESTSERVER, "kscreen.kwayland.testserver") using namespace KScreen; using namespace KWayland::Server; WaylandTestServer::WaylandTestServer(QObject *parent) : QObject(parent) , m_configFile(TEST_DATA + QStringLiteral("default.json")) , m_display(nullptr) , m_outputManagement(nullptr) , m_dpmsManager(nullptr) , m_suspendChanges(false) , m_waiting(nullptr) { } WaylandTestServer::~WaylandTestServer() { stop(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Wayland server shut down."; } void WaylandTestServer::start() { using namespace KWayland::Server; delete m_display; m_display = new KWayland::Server::Display(this); if (qgetenv("WAYLAND_DISPLAY").isEmpty()) { m_display->setSocketName(s_socketName); } else { - m_display->setSocketName(qgetenv("WAYLAND_DISPLAY").constData()); + m_display->setSocketName(QString::fromLatin1(qgetenv("WAYLAND_DISPLAY"))); } m_display->start(); auto manager = m_display->createDpmsManager(); manager->create(); m_outputManagement = m_display->createOutputManagement(); m_outputManagement->create(); connect(m_outputManagement, &OutputManagementInterface::configurationChangeRequested, this, &WaylandTestServer::configurationChangeRequested); KScreen::WaylandConfigReader::outputsFromConfig(m_configFile, m_display, m_outputs); - qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QString("export WAYLAND_DISPLAY="+m_display->socketName()); - qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QString("You can specify the WAYLAND_DISPLAY for this server by exporting it in the environment"); + qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("export WAYLAND_DISPLAY=") + m_display->socketName(); + qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("You can specify the WAYLAND_DISPLAY for this server by exporting it in the environment"); //showOutputs(); } void WaylandTestServer::stop() { Q_FOREACH (const auto &o, m_outputs) { delete o; } m_outputs.clear(); // actually stop the Wayland server delete m_display; m_display = nullptr; } KWayland::Server::Display* WaylandTestServer::display() { return m_display; } void WaylandTestServer::setConfig(const QString& configfile) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Creating Wayland server from " << configfile; m_configFile = configfile; } int WaylandTestServer::outputCount() const { return m_outputs.count(); } QList WaylandTestServer::outputs() const { return m_outputs; } void WaylandTestServer::suspendChanges(bool suspend) { if (m_suspendChanges == suspend) { return; } m_suspendChanges = suspend; if (!suspend && m_waiting) { m_waiting->setApplied(); m_waiting = nullptr; Q_EMIT configChanged(); } } void WaylandTestServer::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface* configurationInterface) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server received change request, changes:" << configurationInterface->changes().count(); Q_EMIT configReceived(); auto changes = configurationInterface->changes(); Q_FOREACH (const auto &outputdevice, changes.keys()) { auto c = changes[outputdevice]; if (c->enabledChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting enabled:"; outputdevice->setEnabled(c->enabled()); } if (c->modeChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting new mode:" << c->mode() << modeString(outputdevice, c->mode()); outputdevice->setCurrentMode(c->mode()); } if (c->transformChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting transform: " << (int)(c->transform()); outputdevice->setTransform(c->transform()); } if (c->positionChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting position: " << c->position(); outputdevice->setGlobalPosition(c->position()); } if (c->scaleChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting scale:" << c->scale(); outputdevice->setScale(c->scale()); } } if (m_suspendChanges) { Q_ASSERT(!m_waiting); m_waiting = configurationInterface; return; } configurationInterface->setApplied(); //showOutputs(); Q_EMIT configChanged(); } void WaylandTestServer::showOutputs() { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******** Wayland server running: " << m_outputs.count() << " outputs. ********"; foreach (auto o, m_outputs) { bool enabled = (o->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " * Output id: " << o->uuid(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Enabled: " << (enabled ? "enabled" : "disabled"); - qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Name: " << QString("%2-%3").arg(o->manufacturer(), o->model()); + qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Name: " << QStringLiteral("%2-%3").arg(o->manufacturer(), o->model()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Mode: " << modeString(o, o->currentModeId()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Pos: " << o->globalPosition(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Edid: " << o->edid(); // << o->currentMode().size(); } qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******************************************************"; } QString WaylandTestServer::modeString(KWayland::Server::OutputDeviceInterface* outputdevice, int mid) { QString s; QString ids; int _i = 0; Q_FOREACH (const auto &_m, outputdevice->modes()) { _i++; if (_i < 6) { ids.append(QString::number(_m.id) + ", "); } else { - ids.append("."); + ids.append(QStringLiteral(".")); } if (_m.id == mid) { - s = QString("%1x%2 @%3").arg(QString::number(_m.size.width()), \ + s = QStringLiteral("%1x%2 @%3").arg(QString::number(_m.size.width()), \ QString::number(_m.size.height()), QString::number(_m.refreshRate)); } } - return QString("[%1] %2 (%4 modes: %3)").arg(QString::number(mid), s, ids, QString::number(outputdevice->modes().count())); + return QStringLiteral("[%1] %2 (%4 modes: %3)").arg(QString::number(mid), s, ids, QString::number(outputdevice->modes().count())); }